@@ -306,7 +306,7 @@ def get_aoi(self, solar_zenith, solar_azimuth):
306306 @_unwrap_single_value
307307 def get_irradiance (self , solar_zenith , solar_azimuth , dni , ghi , dhi ,
308308 dni_extra = None , airmass = None , albedo = None ,
309- model = 'haydavies' , ** kwargs ):
309+ model = 'haydavies' , diffuse_components = False , ** kwargs ):
310310 """
311311 Uses :py:func:`pvlib.irradiance.get_total_irradiance` to
312312 calculate the plane of array irradiance components on the tilted
@@ -333,6 +333,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
333333 Ground surface albedo. [unitless]
334334 model : String, default 'haydavies'
335335 Irradiance model.
336+ diffuse_components : bool, default False
337+ If ``True``, returns the diffuse irradiance components available
338+ from the selected model (e.g., `poa_isotropic`,
339+ `poa_circumsolar`, `poa_horizon`).
340+ If ``False``, only the total diffuse irradiance is returned.
336341
337342 kwargs
338343 Extra parameters passed to
@@ -372,7 +377,9 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
372377 array .get_irradiance (solar_zenith , solar_azimuth ,
373378 dni , ghi , dhi ,
374379 dni_extra = dni_extra , airmass = airmass ,
375- albedo = albedo , model = model , ** kwargs )
380+ albedo = albedo , model = model ,
381+ diffuse_components = diffuse_components ,
382+ ** kwargs )
376383 for array , dni , ghi , dhi , albedo in zip (
377384 self .arrays , dni , ghi , dhi , albedo
378385 )
@@ -381,8 +388,8 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
381388 @_unwrap_single_value
382389 def get_iam (self , aoi , iam_model = 'physical' ):
383390 """
384- Determine the incidence angle modifier using the method specified by
385- ``iam_model``.
391+ Determine the incidence angle modifier for direct irradiance
392+ using the method specified by ``iam_model``.
386393
387394 Parameters for the selected IAM model are expected to be in
388395 ``PVSystem.module_parameters``. Default parameters are available for
@@ -410,6 +417,48 @@ def get_iam(self, aoi, iam_model='physical'):
410417 return tuple (array .get_iam (aoi , iam_model )
411418 for array , aoi in zip (self .arrays , aoi ))
412419
420+ @_unwrap_single_value
421+ def get_iam_diffuse (self , surface_tilt , iam_model ,
422+ marion_model = None , ** kwargs ):
423+ """
424+ Determine the incidence angle modifier for diffuse irradiance using the
425+ method specified by ``iam_model``.
426+
427+ Parameters for the selected IAM model are expected to be in
428+ ``Array.module_parameters``. Default parameters are available for
429+ the 'marion_diffuse' and 'martin_ruiz_diffuse' models.
430+
431+ Parameters
432+ ----------
433+ surface_tilt : numeric or tuple of numeric
434+ The tilt angle of the surface in degrees.
435+ iam_model : str
436+ The IAM model to be used. Valid strings are 'marion_diffuse',
437+ 'martin_ruiz_diffuse', and 'schlick_diffuse'.
438+ marion_model : str, optional
439+ The IAM function to evaluate across a solid angle. Only used when
440+ ``iam_model='marion_diffuse'``. Must be one of 'ashrae',
441+ 'physical', 'martin_ruiz', 'sapm', and 'schlick'.
442+
443+ kwargs : dict, optional
444+ Additional keyword arguments passed to the IAM model function.
445+
446+ Returns
447+ -------
448+ iam_diffuse : dict or DataFrame
449+ The AOI modifiers for different diffuse irradiance components.
450+ Included components depend on the selected ``iam_model``.
451+
452+ Raises
453+ ------
454+ ValueError
455+ if `iam_model` is not a valid model name.
456+ """
457+ surface_tilt = self ._validate_per_array (surface_tilt )
458+ return tuple (array .get_iam_diffuse (tilt , iam_model = iam_model ,
459+ marion_model = marion_model , ** kwargs )
460+ for array , tilt in zip (self .arrays , surface_tilt ))
461+
413462 @_unwrap_single_value
414463 def get_cell_temperature (self , poa_global , temp_air , wind_speed , model ,
415464 effective_irradiance = None , longwave_down = None ):
@@ -1092,7 +1141,7 @@ def get_aoi(self, solar_zenith, solar_azimuth):
10921141
10931142 def get_irradiance (self , solar_zenith , solar_azimuth , dni , ghi , dhi ,
10941143 dni_extra = None , airmass = None , albedo = None ,
1095- model = 'haydavies' , ** kwargs ):
1144+ model = 'haydavies' , diffuse_components = False , ** kwargs ):
10961145 """
10971146 Get plane of array irradiance components.
10981147
@@ -1120,6 +1169,11 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
11201169 Ground surface albedo. [unitless]
11211170 model : String, default 'haydavies'
11221171 Irradiance model.
1172+ diffuse_components : bool, default False
1173+ If ``True``, returns the diffuse irradiance components available
1174+ from the selected model (e.g., ``poa_isotropic``,
1175+ ``poa_circumsolar``, ``poa_horizon``).
1176+ If ``False``, only the total diffuse irradiance is returned.
11231177
11241178 kwargs
11251179 Extra parameters passed to
@@ -1160,20 +1214,23 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
11601214 airmass = atmosphere .get_relative_airmass (solar_zenith )
11611215
11621216 orientation = self .mount .get_orientation (solar_zenith , solar_azimuth )
1163- return irradiance .get_total_irradiance (orientation ['surface_tilt' ],
1164- orientation ['surface_azimuth' ],
1165- solar_zenith , solar_azimuth ,
1166- dni , ghi , dhi ,
1167- dni_extra = dni_extra ,
1168- airmass = airmass ,
1169- albedo = albedo ,
1170- model = model ,
1171- ** kwargs )
1217+ return irradiance .get_total_irradiance (
1218+ orientation ['surface_tilt' ],
1219+ orientation ['surface_azimuth' ],
1220+ solar_zenith , solar_azimuth ,
1221+ dni , ghi , dhi ,
1222+ dni_extra = dni_extra ,
1223+ airmass = airmass ,
1224+ albedo = albedo ,
1225+ model = model ,
1226+ diffuse_components = diffuse_components ,
1227+ ** kwargs
1228+ )
11721229
11731230 def get_iam (self , aoi , iam_model = 'physical' ):
11741231 """
1175- Determine the incidence angle modifier using the method specified by
1176- ``iam_model``.
1232+ Determine the incidence angle modifier for direct irradiance
1233+ using the method specified by ``iam_model``.
11771234
11781235 Parameters for the selected IAM model are expected to be in
11791236 ``Array.module_parameters``. Default parameters are available for
@@ -1212,6 +1269,81 @@ def get_iam(self, aoi, iam_model='physical'):
12121269 else :
12131270 raise ValueError (model + ' is not a valid IAM model' )
12141271
1272+ def get_iam_diffuse (self , surface_tilt , iam_model ,
1273+ marion_model = None , ** kwargs ):
1274+ """
1275+ Determine the incidence angle modifier for various diffuse irradiance
1276+ components using the method specified by ``iam_model``.
1277+
1278+ Parameters for ``iam_model`` are used from ``Array.module_parameters``
1279+ if found. If parameters are not found in ``Array.module_parameters``,
1280+ default parameters for ``iam_model`` are used.
1281+
1282+ Parameters
1283+ ----------
1284+ surface_tilt : numeric
1285+ The tilt angle of the surface in degrees.
1286+ iam_model : str
1287+ The IAM model to be used. Valid strings are 'marion_diffuse',
1288+ 'martin_ruiz_diffuse' and 'schlick_diffuse'.
1289+ marion_model : str, optional
1290+ The IAM function to evaluate across a solid angle. Only used when
1291+ ``iam_model='marion_diffuse'``. Must be one of 'ashrae',
1292+ 'physical', 'martin_ruiz', 'sapm', and 'schlick'.
1293+
1294+ Returns
1295+ -------
1296+ iam_diffuse : dict or DataFrame
1297+ The AOI modifiers for different diffuse irradiance components.
1298+ Included components depend on the selected ``iam_model``.
1299+
1300+ Raises
1301+ ------
1302+ ValueError
1303+ if ``iam_model`` is not a valid model name.
1304+ ValueError
1305+ if ``iam_model`` is 'marion_diffuse' and ``marion_model`` is not
1306+ a valid model name.
1307+ """
1308+ model = iam_model .lower ()
1309+ if model == 'marion_diffuse' and marion_model is None :
1310+ raise ValueError ('marion_model must be specified when '
1311+ 'iam_model="marion_diffuse"' )
1312+ if model == 'marion_diffuse' :
1313+ if marion_model in ['ashrae' , 'physical' , 'martin_ruiz' ,
1314+ 'schlick' ]:
1315+ func = getattr (iam , marion_model )
1316+ params = iam ._IAM_MODEL_PARAMS [marion_model ]
1317+ params .discard ('aoi' )
1318+ kwargs = _build_kwargs (params , self .module_parameters )
1319+ iams = iam .marion_diffuse (model = marion_model ,
1320+ surface_tilt = surface_tilt ,
1321+ ** kwargs )
1322+ elif marion_model == 'sapm' :
1323+ iams = iam .marion_diffuse (model = 'sapm' ,
1324+ surface_tilt = surface_tilt ,
1325+ module = self .module_parameters ,
1326+ ** kwargs )
1327+ else :
1328+ raise ValueError (marion_model + ' is not a valid IAM model' )
1329+ elif model == 'martin_ruiz_diffuse' :
1330+ func = getattr (iam , model ) # get function at pvlib.iam
1331+ # get all parameters from function signature to retrieve them from
1332+ # module_parameters if present
1333+ params = set (inspect .signature (func ).parameters .keys ())
1334+ params .discard ('aoi' )
1335+ kwargs = _build_kwargs (params , self .module_parameters )
1336+ iams = iam .martin_ruiz_diffuse (surface_tilt = surface_tilt , ** kwargs )
1337+ elif model == 'schlick_diffuse' :
1338+ iams = iam .schlick_diffuse (surface_tilt = surface_tilt )
1339+ else :
1340+ raise ValueError (model + ' is not a valid diffuse IAM model' )
1341+
1342+ if isinstance (surface_tilt , pd .Series ):
1343+ iams = pd .DataFrame (iams , index = surface_tilt .index )
1344+
1345+ return iams
1346+
12151347 def get_cell_temperature (self , poa_global , temp_air , wind_speed , model ,
12161348 effective_irradiance = None , longwave_down = None ):
12171349 """
0 commit comments