-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add support for diffuse IAM in the Array and PVSystem classes
#2845
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
base: main
Are you sure you want to change the base?
Changes from 34 commits
1c459d3
4148c53
f3e69b3
fdde55c
97a34f6
dcd202c
722f6fd
2eb64ca
8d698e3
fbf1e30
4be6430
41c3ff0
a3b9391
9455e83
51c08b3
6eebc16
d360853
eddb1c3
1795658
0c5552d
4871948
6cbb1aa
8772a7c
720c8ea
b4270a2
f09a971
e3a5a29
11375de
7911c17
d9bf100
ae7d017
515ff98
a52ff3f
3f76a90
52255f2
e93b7c1
1fc0e1d
4935587
508139b
478f02c
cae647e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -307,7 +307,7 @@ def get_aoi(self, solar_zenith, solar_azimuth): | |||
| @_unwrap_single_value | ||||
| def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | ||||
| dni_extra=None, airmass=None, albedo=None, | ||||
| model='haydavies', **kwargs): | ||||
| model='haydavies', diffuse_components=False, **kwargs): | ||||
| """ | ||||
| Uses :py:func:`pvlib.irradiance.get_total_irradiance` to | ||||
| calculate the plane of array irradiance components on the tilted | ||||
|
|
@@ -334,6 +334,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | |||
| Ground surface albedo. [unitless] | ||||
| model : String, default 'haydavies' | ||||
| Irradiance model. | ||||
| diffuse_components : bool, default False | ||||
| If `True`, returns values for the different diffuse irradiance | ||||
| components available from the selected model | ||||
| (e.g., isotropic, circumsolar, horizon brightening). | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| If `False`, only the total diffuse irradiance is returned. | ||||
|
|
||||
| kwargs | ||||
| Extra parameters passed to | ||||
|
|
@@ -373,7 +378,9 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | |||
| array.get_irradiance(solar_zenith, solar_azimuth, | ||||
| dni, ghi, dhi, | ||||
| dni_extra=dni_extra, airmass=airmass, | ||||
| albedo=albedo, model=model, **kwargs) | ||||
| albedo=albedo, model=model, | ||||
| diffuse_components=diffuse_components, | ||||
| **kwargs) | ||||
| for array, dni, ghi, dhi, albedo in zip( | ||||
| self.arrays, dni, ghi, dhi, albedo | ||||
| ) | ||||
|
|
@@ -382,8 +389,8 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | |||
| @_unwrap_single_value | ||||
| def get_iam(self, aoi, iam_model='physical'): | ||||
| """ | ||||
| Determine the incidence angle modifier using the method specified by | ||||
| ``iam_model``. | ||||
| Determine the incidence angle modifier for direct irradiance | ||||
| using the method specified by ``iam_model``. | ||||
|
|
||||
| Parameters for the selected IAM model are expected to be in | ||||
| ``PVSystem.module_parameters``. Default parameters are available for | ||||
|
|
@@ -411,6 +418,48 @@ def get_iam(self, aoi, iam_model='physical'): | |||
| return tuple(array.get_iam(aoi, iam_model) | ||||
| for array, aoi in zip(self.arrays, aoi)) | ||||
|
|
||||
| @_unwrap_single_value | ||||
| def get_iam_diffuse(self, surface_tilt, iam_model='marion_diffuse', | ||||
| marion_model=None, **kwargs): | ||||
| """ | ||||
| Determine the incidence angle modifier for diffuse irradiance using the | ||||
| method specified by ``iam_model``. | ||||
|
|
||||
| Parameters for the selected IAM model are expected to be in | ||||
| ``Array.module_parameters``. Default parameters are available for | ||||
| the 'marion_diffuse' and 'martin_ruiz_diffuse' models. | ||||
|
|
||||
| Parameters | ||||
| ---------- | ||||
| surface_tilt : float or Series | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The tilt angle of the surface in degrees. | ||||
| iam_model : string, default 'marion_diffuse' | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I worry that having Maybe switch to martin-ruiz, which is the more common choice anyway? Maybe leave it with no default? Not sure...
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I went with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe have no default. Nonetheless, #2824 would address this risk. So maybe this can be revisited at a later point in time 👀
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with default
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems we have a certain quorum here. The only downside I see is that it requires users to be aware of this new functionality, but I have been thinking that maybe a small technical note could be made to showcase this. A side topic to be discussed by e-mail maybe :-)
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The IAM model to be used. Valid strings are 'marion_diffuse', | ||||
| 'martin_ruiz_diffuse', and 'schlick_diffuse'. | ||||
| marion_model : string, default None | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The IAM function to evaluate across a solid angle. Only used when | ||||
| ``iam_model='marion_diffuse'``. Must be one of 'ashrae', | ||||
| 'physical', 'martin_ruiz', 'sapm', and 'schlick'. | ||||
|
|
||||
| kwargs : dict, optional | ||||
| Additional keyword arguments passed to the IAM model function. | ||||
|
|
||||
| Returns | ||||
| ------- | ||||
| iam_diffuse : dict or DataFrame | ||||
| The AOI modifiers for different diffuse irradiance components. | ||||
| Included components depend on the selected ``iam_model``. | ||||
|
|
||||
| Raises | ||||
| ------ | ||||
| ValueError | ||||
| if `iam_model` is not a valid model name. | ||||
| """ | ||||
| surface_tilt = self._validate_per_array(surface_tilt) | ||||
| return tuple(array.get_iam_diffuse(tilt, iam_model=iam_model, | ||||
| marion_model=marion_model, **kwargs) | ||||
| for array, tilt in zip(self.arrays, surface_tilt)) | ||||
|
|
||||
| @_unwrap_single_value | ||||
| def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, | ||||
| effective_irradiance=None, longwave_down=None): | ||||
|
|
@@ -1093,7 +1142,7 @@ def get_aoi(self, solar_zenith, solar_azimuth): | |||
|
|
||||
| def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | ||||
| dni_extra=None, airmass=None, albedo=None, | ||||
| model='haydavies', **kwargs): | ||||
| model='haydavies', diffuse_components=False, **kwargs): | ||||
| """ | ||||
| Get plane of array irradiance components. | ||||
|
|
||||
|
|
@@ -1121,6 +1170,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | |||
| Ground surface albedo. [unitless] | ||||
| model : String, default 'haydavies' | ||||
| Irradiance model. | ||||
| diffuse_components : bool, default False | ||||
| If `True`, returns values for the different diffuse irradiance | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| components available from the selected model | ||||
| (e.g., isotropic, circumsolar, horizon brightening). | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| If `False`, only the total diffuse irradiance is returned. | ||||
|
|
||||
| kwargs | ||||
| Extra parameters passed to | ||||
|
|
@@ -1161,20 +1215,23 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi, | |||
| airmass = atmosphere.get_relative_airmass(solar_zenith) | ||||
|
|
||||
| orientation = self.mount.get_orientation(solar_zenith, solar_azimuth) | ||||
| return irradiance.get_total_irradiance(orientation['surface_tilt'], | ||||
| orientation['surface_azimuth'], | ||||
| solar_zenith, solar_azimuth, | ||||
| dni, ghi, dhi, | ||||
| dni_extra=dni_extra, | ||||
| airmass=airmass, | ||||
| albedo=albedo, | ||||
| model=model, | ||||
| **kwargs) | ||||
| return irradiance.get_total_irradiance( | ||||
| orientation['surface_tilt'], | ||||
| orientation['surface_azimuth'], | ||||
| solar_zenith, solar_azimuth, | ||||
| dni, ghi, dhi, | ||||
| dni_extra=dni_extra, | ||||
| airmass=airmass, | ||||
| albedo=albedo, | ||||
| model=model, | ||||
| diffuse_components=diffuse_components, | ||||
| **kwargs | ||||
| ) | ||||
|
|
||||
| def get_iam(self, aoi, iam_model='physical'): | ||||
| """ | ||||
| Determine the incidence angle modifier using the method specified by | ||||
| ``iam_model``. | ||||
| Determine the incidence angle modifier for direct irradiance | ||||
| using the method specified by ``iam_model``. | ||||
|
|
||||
| Parameters for the selected IAM model are expected to be in | ||||
| ``Array.module_parameters``. Default parameters are available for | ||||
|
|
@@ -1213,6 +1270,79 @@ def get_iam(self, aoi, iam_model='physical'): | |||
| else: | ||||
| raise ValueError(model + ' is not a valid IAM model') | ||||
|
|
||||
| def get_iam_diffuse(self, surface_tilt, iam_model='marion_diffuse', | ||||
| marion_model=None): | ||||
| """ | ||||
| Determine the incidence angle modifier for various diffuse irradiance | ||||
| components using the method specified by ``iam_model``. | ||||
|
|
||||
| Parameters for the selected IAM model are expected to be in | ||||
| ``Array.module_parameters``. Default parameters are available for | ||||
| the 'marion_diffuse' and 'martin_ruiz_diffuse' models. | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
|
|
||||
| Parameters | ||||
| ---------- | ||||
| surface_tilt : float or Series | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The tilt angle of the surface in degrees. | ||||
| iam_model : string, default 'marion_diffuse' | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The IAM model to be used. Valid strings are 'marion_diffuse', | ||||
| 'martin_ruiz_diffuse' and 'schlick_diffuse'. | ||||
| marion_model : string, default None | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| The IAM function to evaluate across a solid angle. Only used when | ||||
| ``iam_model='marion_diffuse'``. Must be one of 'ashrae', | ||||
| 'physical', 'martin_ruiz', 'sapm', and 'schlick'. | ||||
|
|
||||
| Returns | ||||
| ------- | ||||
| iam_diffuse : dict or DataFrame | ||||
| The AOI modifiers for different diffuse irradiance components. | ||||
| Included components depend on the selected ``iam_model``. | ||||
|
|
||||
| Raises | ||||
| ------ | ||||
| ValueError | ||||
| if `iam_model` is not a valid model name. | ||||
| ValueError | ||||
| if `iam_model` is 'marion_diffuse' and `marion_model` is None. | ||||
|
cbcrespo marked this conversation as resolved.
Outdated
|
||||
| """ | ||||
| model = iam_model.lower() | ||||
| if model == 'marion_diffuse' and marion_model is None: | ||||
| raise ValueError('marion_model must be specified when ' | ||||
| 'iam_model="marion_diffuse"') | ||||
| if model == 'marion_diffuse': | ||||
| if marion_model in ['ashrae', 'physical', 'martin_ruiz', | ||||
| 'schlick']: | ||||
| func = getattr(iam, marion_model) | ||||
| params = set(inspect.signature(func).parameters.keys()) | ||||
| params.discard('aoi') | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I see that this is copied over from Line 21 in 7dde893
I suggest we go ahead with this as-is, but file an issue to eventually come back and switch to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went ahead and changed this line to use |
||||
| kwargs = _build_kwargs(params, self.module_parameters) | ||||
| iams = iam.marion_diffuse(model=marion_model, | ||||
| surface_tilt=surface_tilt, | ||||
| **kwargs) | ||||
| elif marion_model == 'sapm': | ||||
| iams = iam.marion_diffuse(model='sapm', | ||||
| surface_tilt=surface_tilt, | ||||
| module=self.module_parameters) | ||||
| else: | ||||
| raise ValueError(marion_model + ' is not a valid IAM model') | ||||
| elif model == 'martin_ruiz_diffuse': | ||||
| func = getattr(iam, model) # get function at pvlib.iam | ||||
| # get all parameters from function signature to retrieve them from | ||||
| # module_parameters if present | ||||
| params = set(inspect.signature(func).parameters.keys()) | ||||
| params.discard('aoi') | ||||
| kwargs = _build_kwargs(params, self.module_parameters) | ||||
| iams = iam.martin_ruiz_diffuse(surface_tilt=surface_tilt, **kwargs) | ||||
| elif model == 'schlick_diffuse': | ||||
| iams = iam.schlick_diffuse(surface_tilt=surface_tilt) | ||||
| else: | ||||
| raise ValueError(model + ' is not a valid diffuse IAM model') | ||||
|
|
||||
| if isinstance(surface_tilt, pd.Series): | ||||
| iams = pd.DataFrame(iams, index=surface_tilt.index) | ||||
|
|
||||
| return iams | ||||
|
|
||||
| def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, | ||||
| effective_irradiance=None, longwave_down=None): | ||||
| """ | ||||
|
|
||||
Uh oh!
There was an error while loading. Please reload this page.