Skip to content

Add polo-smm #2491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,3 +710,86 @@
+ coeff[2] * (airmass - 1.5)
)
return mismatch


def spectral_factor_polo(precipitable_water, airmass_absolute, aod500, aoi,
altitude, module_type=None, coefficients=None,
albedo=0.2):
"""
Estimation of spectral mismatch for BIPV application in vertical facades.
Copy link
Contributor

@RDaxini RDaxini Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Estimation of spectral mismatch for BIPV application in vertical facades.
Estimate the spectral mismatch for BIPV application in vertical facades.

I think the imperative form is standard practice for the summary line

Parameters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Parameters
Parameters

I think it won't render well in readthedocs without the margin.

----------
precipitable_water : numeric
atmospheric precipitable water. [cm]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

For consistency.

airmass_absolute : numeric
absolute (pressure-adjusted) airmass. [unitless]
aod500 : numeric
atmospheric aerosol optical depth at 500 nm. [unitless]
aoi : numeric
angle of incidence. [degrees]
Comment on lines +725 to +730
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to link these terms to their respective definitions on the nomenclature page. Not all definitions on that page have been fully developed, but we can link over to them anyway and they will soon be updated.

Suggested change
airmass_absolute : numeric
absolute (pressure-adjusted) airmass. [unitless]
aod500 : numeric
atmospheric aerosol optical depth at 500 nm. [unitless]
aoi : numeric
angle of incidence. [degrees]
airmass_absolute : numeric
absolute (pressure-adjusted) airmass. See :term:`airmass_absolute`. [unitless]
aod500 : numeric
atmospheric aerosol optical depth at 500 nm. [unitless]
aoi : numeric
angle of incidence. See :term:`aoi`. [degrees]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For aoi, I think the definition here should be angle of incidence on the vertical surface, and maybe we don't link to the generic aoi term.

altitude: numeric
altitude over sea level. [m]
module_type : str, optional
One of the following PV technology strings from [1]_:
* ``'cdte'`` - anonymous CdTe module.
* ``'monosi'`` - anonymous sc-si module.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* ``'monosi'`` - anonymous sc-si module.
* ``'monosi'`` - anonymous sc-si module.

What is "sc-si"? Is this a typo and should be "monocrystalline Si"?

* ``'cigs'`` - anonymous copper indium gallium selenide module.
* ``'asi'`` - anonymous amorphous silicon module.
albedo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can albedo both be a float and an array?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, I always thought it as a float, since it refers to a total albedo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was more wondering if the code allows for albedo being passed in as a time series?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was more wondering if the code allows for albedo being passed in as a time series?

+1 to making this possible if not already

Ground albedo (default value if 0.2). [unitless]
Comment on lines +740 to +741
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
albedo
Ground albedo (default value if 0.2). [unitless]
albedo
Ground albedo (default value if 0.2). See :term:`albedo`. [unitless]

coefficients : array-like, optional
user-defined coefficients, if not using one of the default coefficient
set via the ``module_type`` parameter.
Comment on lines +744 to +745
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
user-defined coefficients, if not using one of the default coefficient
set via the ``module_type`` parameter.
user-defined coefficients, if not using one of the coefficient
sets via the ``module_type`` parameter.

Returns
-------
modifier: numeric
spectral mismatch factor (unitless) which is multiplied
with broadband irradiance reaching a module's cells to estimate
effective irradiance, i.e., the irradiance that is converted to
electrical current.
References
----------
[1] J. Polo and C. Sanz-Saiz, 'Development of spectral mismatch models
for BIPV applications in building façades', Renewable Energy, vol. 245,
p. 122820, Jun. 2025,:doi:`10.1016/j.renene.2025.122820`
"""
if module_type is None and coefficients is None:
raise ValueError('Must provide either `module_type` or `coefficients`')
if module_type is not None and coefficients is not None:
raise ValueError('Only one of `module_type` and `coefficients` should '

Check warning on line 764 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L761-L764

Added lines #L761 - L764 were not covered by tests
'be provided')
am_aoi = pvlib.atmosphere.get_relative_airmass(aoi)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct that aoi = 90 - zenith? I think so, because the surface is assumed to be vertical. If that's the case, then I think requesting zenith rather than aoi as input would be significantly easier for a user to understand.

pressure = pvlib.atmosphere.alt2pres(altitude)
am90 = pvlib.atmosphere.get_absolute_airmass(am_aoi, pressure)
Ram = am90/airmass_absolute
_coefficients = {}
_coefficients = {

Check warning on line 771 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L766-L771

Added lines #L766 - L771 were not covered by tests
'cdte': (-0.0009, 46.80, 49.20, -0.87, 0.00041, 0.053),
'monosi': (0.0027, 10.34, 9.48, 0.307, 0.00077, 0.006),
'cigs': (0.0017, 2.33, 1.30, 0.11, 0.00098, -0.0177),
'asi': (0.0024, 7.32, 7.09, -0.72, -0.0013, 0.089),
}
c = {

Check warning on line 777 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L777

Added line #L777 was not covered by tests
'asi': (0.0056, -0.020, 1.014),
'cigs': (-0.0009, -0.0003, 1),
'cdte': (0.0021, -0.01, 1.01),
'monosi': (0, -0.003, 1.0),
}
if module_type is not None:
coeff = _coefficients[module_type]
c_albedo = c[module_type]

Check warning on line 785 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L783-L785

Added lines #L783 - L785 were not covered by tests
else:
coeff = coefficients
c_albedo = (0.0, 0.0, 1.0) # 0.2 albedo assumed
albedo = 0.2
Comment on lines +788 to +789
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is intentional, but this will ignore whatever value the user provides. If it's intentional, the docstring does not reflect that albedo is only used when the type is given, and assumed 0.2 when given the coefficients.

smm = coeff[0] * Ram + coeff[1] / (coeff[2] + Ram**coeff[3]) \

Check warning on line 790 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L787-L790

Added lines #L787 - L790 were not covered by tests
+ coeff[4] / aod500 + coeff[5]*np.sqrt(precipitable_water)
# Ground albedo correction
g = c_albedo[0] * (albedo/0.2)**2 \

Check warning on line 793 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L793

Added line #L793 was not covered by tests
+ c_albedo[1] * (albedo/0.2) + c_albedo[2]
return g*smm

Check warning on line 795 in pvlib/spectrum/mismatch.py

View check run for this annotation

Codecov / codecov/patch

pvlib/spectrum/mismatch.py#L795

Added line #L795 was not covered by tests
Loading