Skip to content

Commit 7bb30ad

Browse files
authored
Extend infinite_sheds to optionally use haydavies transposition (#1668)
* Updating infinite_sheds irradiance engine Updating the infinite_sheds irradiance engine to use the haydavies sky diffuse model. This will help the sensitivity issue with circumsolar on the backside of bifacial systems. * update use solar_zenith and solar_azimuth, instead of projection_ratio, in the haydavies() call. * update removed unused functions from imports * adding test and updating reference Added test and updating documentation. * updates Corrected white-space errors and added test for when haydavies is selected, but dni_extra is not supplied. * updates Updated docstrings and implemented a second call to haydavies to avoid 1/cos(zenith) issues with DNI where zenith nears 90 deg. * Update reorganize order of function inputs. * Update Updated tests give have correct ordering of inputs. * update Whitespace errors - these will be the end of me! * Update Added detail to docstring in infinite_sheds.py and note on haydavies implementation in bifacial.rst (based on comments from PR). * Update bifacial.rst Updated bifacial.rst file for formatting and ordering. Had added note detailing to user what occurs when 'haydavies' model is selected.
1 parent f041355 commit 7bb30ad

File tree

4 files changed

+99
-7
lines changed

4 files changed

+99
-7
lines changed

docs/sphinx/source/user_guide/bifacial.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,16 @@ considered to be towards the "front" of the array. Array height differs in this
9191
code from the description in [1], where array height is described at the row's
9292
lower edge.
9393

94+
If ``model='isotropic'`` (the default), ``dhi`` is assumed to be isotropically
95+
distributed across the sky dome as in [1]_. This implementation provides an
96+
optional extension to [1]_ to model sky anisotropy: if ``model='haydavies'``,
97+
the input ``dhi`` is decomposed into circumsolar and isotropic components using
98+
:py:func:`~pvlib.irradiance.haydavies`, with the circumsolar component treated
99+
as additional ``dni`` for transposition and shading purposes.
100+
94101
This model is influenced by the 2D model published by Marion, *et al.* in [2].
95102

103+
96104
References
97105
----------
98106
.. [1] Mikofski, M., Darawali, R., Hamer, M., Neubert, A., and Newmiller,

docs/sphinx/source/whatsnew/v0.9.5.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ Enhancements
2121
* Added optional ``n_ar`` parameter to :py:func:`pvlib.iam.physical` to
2222
support an anti-reflective coating. (:issue:`1501`, :pull:`1616`)
2323

24+
* Added an optional ``model`` parameter to
25+
:py:func:`pvlib.bifacial.infinite_sheds.get_irradiance` and
26+
:py:func:`pvlib.bifacial.infinite_sheds.get_irradiance_poa`
27+
to enable use of the hay-davies sky diffuse irradiance model
28+
instead of the default isotropic model. (:pull:`1668`)
29+
2430
Bug fixes
2531
~~~~~~~~~
2632
* Added a limit to :py:func:`pvlib.snow.loss_townsend` to guard against
@@ -66,3 +72,5 @@ Contributors
6672
* Mark Mikofski (:ghuser:`mikofski`)
6773
* Anton Driesse (:ghuser:`adriesse`)
6874
* Michael Deceglie (:ghuser:`mdeceglie`)
75+
* Saurabh Aneja (:ghuser:`spaneja`)
76+
* John Moseley (:ghuser:`johnMoseleyArray`)

pvlib/bifacial/infinite_sheds.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
from pvlib.tools import cosd, sind, tand
88
from pvlib.bifacial import utils
99
from pvlib.shading import masking_angle
10-
from pvlib.irradiance import beam_component, aoi
11-
10+
from pvlib.irradiance import beam_component, aoi, haydavies
1211

1312
def _vf_ground_sky_integ(surface_tilt, surface_azimuth, gcr, height,
1413
pitch, max_rows=10, npoints=100):
@@ -401,7 +400,8 @@ def _shaded_fraction(solar_zenith, solar_azimuth, surface_tilt,
401400

402401
def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
403402
solar_azimuth, gcr, height, pitch, ghi, dhi, dni,
404-
albedo, iam=1.0, npoints=100):
403+
albedo, model='isotropic', dni_extra=None, iam=1.0,
404+
npoints=100):
405405
r"""
406406
Calculate plane-of-array (POA) irradiance on one side of a row of modules.
407407
@@ -457,6 +457,13 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
457457
albedo : numeric
458458
Surface albedo. [unitless]
459459
460+
model : str, default 'isotropic'
461+
Irradiance model - can be one of 'isotropic' or 'haydavies'.
462+
463+
dni_extra : numeric, optional
464+
Extraterrestrial direct normal irradiance. Required when
465+
``model='haydavies'``. [W/m2]
466+
460467
iam : numeric, default 1.0
461468
Incidence angle modifier, the fraction of direct irradiance incident
462469
on the surface that is not reflected away. [unitless]
@@ -495,6 +502,27 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
495502
--------
496503
get_irradiance
497504
"""
505+
if model == 'haydavies':
506+
if dni_extra is None:
507+
raise ValueError(f'must supply dni_extra for {model} model')
508+
# Call haydavies first time within the horizontal plane - to subtract
509+
# circumsolar_horizontal from DHI
510+
sky_diffuse_comps_horizontal = haydavies(0, 180, dhi, dni, dni_extra,
511+
solar_zenith, solar_azimuth,
512+
return_components=True)
513+
circumsolar_horizontal = sky_diffuse_comps_horizontal['circumsolar']
514+
515+
# Call haydavies a second time where circumsolar_normal is facing
516+
# directly towards sun, and can be added to DNI
517+
sky_diffuse_comps_normal = haydavies(solar_zenith, solar_azimuth, dhi,
518+
dni, dni_extra, solar_zenith,
519+
solar_azimuth,
520+
return_components=True)
521+
circumsolar_normal = sky_diffuse_comps_normal['circumsolar']
522+
523+
dhi = dhi - circumsolar_horizontal
524+
dni = dni + circumsolar_normal
525+
498526
# Calculate some geometric quantities
499527
# rows to consider in front and behind current row
500528
# ensures that view factors to the sky are computed to within 5 degrees
@@ -580,8 +608,8 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
580608

581609
def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
582610
gcr, height, pitch, ghi, dhi, dni,
583-
albedo, iam_front=1.0, iam_back=1.0,
584-
bifaciality=0.8, shade_factor=-0.02,
611+
albedo, model='isotropic', dni_extra=None, iam_front=1.0,
612+
iam_back=1.0, bifaciality=0.8, shade_factor=-0.02,
585613
transmission_factor=0, npoints=100):
586614
"""
587615
Get front and rear irradiance using the infinite sheds model.
@@ -643,6 +671,13 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
643671
albedo : numeric
644672
Surface albedo. [unitless]
645673
674+
model : str, default 'isotropic'
675+
Irradiance model - can be one of 'isotropic' or 'haydavies'.
676+
677+
dni_extra : numeric, optional
678+
Extraterrestrial direct normal irradiance. Required when
679+
``model='haydavies'``. [W/m2]
680+
646681
iam_front : numeric, default 1.0
647682
Incidence angle modifier, the fraction of direct irradiance incident
648683
on the front surface that is not reflected away. [unitless]
@@ -720,13 +755,15 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
720755
surface_tilt=surface_tilt, surface_azimuth=surface_azimuth,
721756
solar_zenith=solar_zenith, solar_azimuth=solar_azimuth,
722757
gcr=gcr, height=height, pitch=pitch, ghi=ghi, dhi=dhi, dni=dni,
723-
albedo=albedo, iam=iam_front, npoints=npoints)
758+
albedo=albedo, model=model, dni_extra=dni_extra, iam=iam_front,
759+
npoints=npoints)
724760
# back side POA irradiance
725761
irrad_back = get_irradiance_poa(
726762
surface_tilt=backside_tilt, surface_azimuth=backside_sysaz,
727763
solar_zenith=solar_zenith, solar_azimuth=solar_azimuth,
728764
gcr=gcr, height=height, pitch=pitch, ghi=ghi, dhi=dhi, dni=dni,
729-
albedo=albedo, iam=iam_back, npoints=npoints)
765+
albedo=albedo, model=model, dni_extra=dni_extra, iam=iam_back,
766+
npoints=npoints)
730767

731768
colmap_front = {
732769
'poa_global': 'poa_front',

pvlib/tests/bifacial/test_infinite_sheds.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,42 @@ def test_get_irradiance_limiting_gcr():
353353
result['poa_back_sky_diffuse'])
354354
assert np.isclose(result['poa_front_ground_diffuse'],
355355
result['poa_back_ground_diffuse'])
356+
357+
358+
def test_get_irradiance_with_haydavies():
359+
# singleton inputs
360+
solar_zenith = 0.
361+
solar_azimuth = 180.
362+
surface_tilt = 0.
363+
surface_azimuth = 180.
364+
gcr = 0.5
365+
height = 1.
366+
pitch = 1.
367+
ghi = 1000.
368+
dhi = 300.
369+
dni = 700.
370+
albedo = 0.
371+
dni_extra = 1413.
372+
model = 'haydavies'
373+
iam_front = 1.0
374+
iam_back = 1.0
375+
npoints = 100
376+
result = infinite_sheds.get_irradiance(
377+
surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
378+
gcr, height, pitch, ghi, dhi, dni, albedo, model, dni_extra,
379+
iam_front, iam_back, bifaciality=0.8, shade_factor=-0.02,
380+
transmission_factor=0, npoints=npoints)
381+
expected_front_diffuse = np.array([151.38])
382+
expected_front_direct = np.array([848.62])
383+
expected_front_global = expected_front_diffuse + expected_front_direct
384+
assert np.isclose(result['poa_front'], expected_front_global)
385+
assert np.isclose(result['poa_front_diffuse'], expected_front_diffuse)
386+
assert np.isclose(result['poa_front_direct'], expected_front_direct)
387+
assert np.isclose(result['poa_global'], result['poa_front'])
388+
# test for when dni_extra is not supplied
389+
with pytest.raises(ValueError, match='supply dni_extra for haydavies'):
390+
result = infinite_sheds.get_irradiance(
391+
surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
392+
gcr, height, pitch, ghi, dhi, dni, albedo, model, None,
393+
iam_front, iam_back, bifaciality=0.8, shade_factor=-0.02,
394+
transmission_factor=0, npoints=npoints)

0 commit comments

Comments
 (0)