1212import pandas as pd
1313from pvlib .tools import cosd , sind , tand , asind
1414
15-
1615# a dict of required parameter names for each IAM model
1716# keys are the function names for the IAM models
1817IAM_MODEL_PARAMS = {
@@ -220,8 +219,8 @@ def martin_ruiz(aoi, a_r=0.16):
220219 -----
221220 `martin_ruiz` calculates the incidence angle modifier (IAM) as described in
222221 [1]. The information required is the incident angle (AOI) and the angular
223- losses coefficient (a_r). Note that [1] has a corrigendum [2] which makes
224- the document much simpler to understand .
222+ losses coefficient (a_r). Note that [1] has a corrigendum [2] which
223+ clarifies a mix-up of 'alpha's and 'a's in the former .
225224
226225 The incident angle modifier is defined as
227226
@@ -249,6 +248,7 @@ def martin_ruiz(aoi, a_r=0.16):
249248
250249 See Also
251250 --------
251+ iam.martin_ruiz_diffuse
252252 iam.physical
253253 iam.ashrae
254254 iam.interp
@@ -262,7 +262,7 @@ def martin_ruiz(aoi, a_r=0.16):
262262 a_r = np .asanyarray (a_r )
263263
264264 if np .any (np .less_equal (a_r , 0 )):
265- raise RuntimeError ("The parameter 'a_r' cannot be zero or negative." )
265+ raise ValueError ("The parameter 'a_r' cannot be zero or negative." )
266266
267267 with np .errstate (invalid = 'ignore' ):
268268 iam = (1 - np .exp (- cosd (aoi ) / a_r )) / (1 - np .exp (- 1 / a_r ))
@@ -274,6 +274,111 @@ def martin_ruiz(aoi, a_r=0.16):
274274 return iam
275275
276276
277+ def martin_ruiz_diffuse (surface_tilt , a_r = 0.16 , c1 = 0.4244 , c2 = None ):
278+ '''
279+ Determine the incidence angle modifiers (iam) for diffuse sky and
280+ ground-reflected irradiance using the Martin and Ruiz incident angle model.
281+
282+ Parameters
283+ ----------
284+ surface_tilt: float or array-like, default 0
285+ Surface tilt angles in decimal degrees.
286+ The tilt angle is defined as degrees from horizontal
287+ (e.g. surface facing up = 0, surface facing horizon = 90)
288+ surface_tilt must be in the range [0, 180]
289+
290+ a_r : numeric
291+ The angular losses coefficient described in equation 3 of [1].
292+ This is an empirical dimensionless parameter. Values of a_r are
293+ generally on the order of 0.08 to 0.25 for flat-plate PV modules.
294+ a_r must be greater than zero.
295+
296+ c1 : float
297+ First fitting parameter for the expressions that approximate the
298+ integral of diffuse irradiance coming from different directions.
299+ c1 is given as the constant 4 / 3 / pi (0.4244) in [1].
300+
301+ c2 : float
302+ Second fitting parameter for the expressions that approximate the
303+ integral of diffuse irradiance coming from different directions.
304+ If c2 is None, it will be calculated according to the linear
305+ relationship given in [3].
306+
307+ Returns
308+ -------
309+ iam_sky : numeric
310+ The incident angle modifier for sky diffuse
311+
312+ iam_ground : numeric
313+ The incident angle modifier for ground-reflected diffuse
314+
315+ Notes
316+ -----
317+ Sky and ground modifiers are complementary: iam_sky for tilt = 30 is
318+ equal to iam_ground for tilt = 180 - 30. For vertical surfaces,
319+ tilt = 90, the two factors are equal.
320+
321+ References
322+ ----------
323+ [1] N. Martin and J. M. Ruiz, "Calculation of the PV modules angular
324+ losses under field conditions by means of an analytical model", Solar
325+ Energy Materials & Solar Cells, vol. 70, pp. 25-38, 2001.
326+
327+ [2] N. Martin and J. M. Ruiz, "Corrigendum to 'Calculation of the PV
328+ modules angular losses under field conditions by means of an
329+ analytical model'", Solar Energy Materials & Solar Cells, vol. 110,
330+ pp. 154, 2013.
331+
332+ [3] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy
333+ rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018.
334+
335+ See Also
336+ --------
337+ iam.martin_ruiz
338+ iam.physical
339+ iam.ashrae
340+ iam.interp
341+ iam.sapm
342+ '''
343+ # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Oct. 2019
344+
345+ if isinstance (surface_tilt , pd .Series ):
346+ out_index = surface_tilt .index
347+ else :
348+ out_index = None
349+
350+ surface_tilt = np .asanyarray (surface_tilt )
351+
352+ # avoid undefined results for horizontal or upside-down surfaces
353+ zeroang = 1e-06
354+
355+ surface_tilt = np .where (surface_tilt == 0 , zeroang , surface_tilt )
356+ surface_tilt = np .where (surface_tilt == 180 , 180 - zeroang , surface_tilt )
357+
358+ if c2 is None :
359+ # This equation is from [3] Sect. 7.2
360+ c2 = 0.5 * a_r - 0.154
361+
362+ beta = np .radians (surface_tilt )
363+
364+ from numpy import pi , sin , cos , exp
365+
366+ # because sin(pi) isn't exactly zero
367+ sin_beta = np .where (surface_tilt < 90 , sin (beta ), sin (pi - beta ))
368+
369+ trig_term_sky = sin_beta + (pi - beta - sin_beta ) / (1 + cos (beta ))
370+ trig_term_gnd = sin_beta + (beta - sin_beta ) / (1 - cos (beta )) # noqa: E222 E261 E501
371+
372+ iam_sky = 1 - exp (- (c1 + c2 * trig_term_sky ) * trig_term_sky / a_r )
373+ iam_gnd = 1 - exp (- (c1 + c2 * trig_term_gnd ) * trig_term_gnd / a_r )
374+
375+ if out_index is not None :
376+ iam_sky = pd .Series (iam_sky , index = out_index , name = 'iam_sky' )
377+ iam_gnd = pd .Series (iam_gnd , index = out_index , name = 'iam_ground' )
378+
379+ return iam_sky , iam_gnd
380+
381+
277382def interp (aoi , theta_ref , iam_ref , method = 'linear' , normalize = True ):
278383 r'''
279384 Determine the incidence angle modifier (IAM) by interpolating a set of
0 commit comments