Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 7 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.5.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ Enhancements
* Added the optional `string_factor` parameter to
:py:func:`pvlib.snow.loss_townsend` (:issue:`1636`, :pull:`1653`)

* Added `haydavies` sky-diffuse transposition model to
:py:func:`pvlib.bifacial.infinite_sheds.get_irradiance` and
:py:func:`pvlib.bifacial.infinite_sheds.get_irradiance_poa`
(:pull:`1668`)

Bug fixes
~~~~~~~~~
* Added a limit to :py:func:`pvlib.snow.loss_townsend` to guard against
Expand Down Expand Up @@ -69,3 +74,5 @@ Contributors
* Anton Driesse (:ghuser:`adriesse`)
* Adam R. Jensen (:ghuser:`AdamRJensen`)
* Michael Deceglie (:ghuser:`mdeceglie`)
* Saurabh Aneja (:ghuser:`spaneja`)
* John Moseley (:ghuser:`johnMoseleyArray`)
51 changes: 44 additions & 7 deletions pvlib/bifacial/infinite_sheds.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
from pvlib.tools import cosd, sind, tand
from pvlib.bifacial import utils
from pvlib.shading import masking_angle
from pvlib.irradiance import beam_component, aoi

from pvlib.irradiance import beam_component, aoi, haydavies

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

def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
solar_azimuth, gcr, height, pitch, ghi, dhi, dni,
albedo, iam=1.0, npoints=100):
albedo, dni_extra=None, model='isotropic', iam=1.0,
npoints=100):
r"""
Calculate plane-of-array (POA) irradiance on one side of a row of modules.

Expand Down Expand Up @@ -457,6 +457,13 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
albedo : numeric
Surface albedo. [unitless]

dni_extra : numeric, optional
Extraterrestrial direct normal irradiance. Required when
``model='haydavies'``. [W/m2]

model : str, default 'isotropic'
Irradiance model - can be one of 'isotropic' or 'haydavies'.

iam : numeric, default 1.0
Incidence angle modifier, the fraction of direct irradiance incident
on the surface that is not reflected away. [unitless]
Expand Down Expand Up @@ -495,6 +502,27 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
--------
get_irradiance
"""
if model == 'haydavies':
if dni_extra is None:
raise ValueError(f'must supply dni_extra for {model} model')
# Call haydavies first time within the horizontal plane - to subtract
# circumsolar_horizontal from DHI
sky_diffuse_comps_horizontal = haydavies(0, 180, dhi, dni, dni_extra,
solar_zenith, solar_azimuth,
return_components=True)
circumsolar_horizontal = sky_diffuse_comps_horizontal['circumsolar']

# Call haydavies a second time where circumsolar_normal is facing
# directly towards sun, and can be added to DNI
sky_diffuse_comps_normal = haydavies(solar_zenith, solar_azimuth, dhi,
dni, dni_extra, solar_zenith,
solar_azimuth,
return_components=True)
circumsolar_normal = sky_diffuse_comps_normal['circumsolar']

Copy link
Member

Choose a reason for hiding this comment

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

I think that instead of calling haydavies twice, you should just calculate the anisotropy index here and use the relevant cosines of angles/projection ratios.

Copy link
Member

Choose a reason for hiding this comment

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

My 2 cents: maybe it's a bit unthrifty to call haydavies 4x for each get_irradiance call, but being able to do this was part of the motivation for #1553, and I think the simplicity of calling functions in pvlib.irradiance is worth the slight hit to efficiency (which I bet is tiny compared with other parts of infinite_sheds anyway). Also, if we add more options for model in the future, are we going to reimplement all of them here?

Copy link
Member

@adriesse adriesse Feb 23, 2023

Choose a reason for hiding this comment

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

I don't know what the future holds. I do know that in the past and again very recently I have found the functions in pvlib irradiance not suitable for my purposes in a similar way to this example. Each offers a little bundle of calculations, but sometimes I just need them bundled differently.

dhi = dhi - circumsolar_horizontal
dni = dni + circumsolar_normal

# Calculate some geometric quantities
# rows to consider in front and behind current row
# ensures that view factors to the sky are computed to within 5 degrees
Expand Down Expand Up @@ -580,8 +608,8 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,

def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
gcr, height, pitch, ghi, dhi, dni,
albedo, iam_front=1.0, iam_back=1.0,
bifaciality=0.8, shade_factor=-0.02,
albedo, dni_extra=None, model='isotropic', iam_front=1.0,
iam_back=1.0, bifaciality=0.8, shade_factor=-0.02,
transmission_factor=0, npoints=100):
"""
Get front and rear irradiance using the infinite sheds model.
Expand Down Expand Up @@ -643,6 +671,13 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
albedo : numeric
Surface albedo. [unitless]

dni_extra : numeric, optional
Extraterrestrial direct normal irradiance. Required when
``model='haydavies'``. [W/m2]

model : str, default 'isotropic'
Irradiance model - can be one of 'isotropic' or 'haydavies'.

iam_front : numeric, default 1.0
Incidence angle modifier, the fraction of direct irradiance incident
on the front surface that is not reflected away. [unitless]
Expand Down Expand Up @@ -720,13 +755,15 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
surface_tilt=surface_tilt, surface_azimuth=surface_azimuth,
solar_zenith=solar_zenith, solar_azimuth=solar_azimuth,
gcr=gcr, height=height, pitch=pitch, ghi=ghi, dhi=dhi, dni=dni,
albedo=albedo, iam=iam_front, npoints=npoints)
albedo=albedo, dni_extra=dni_extra, model=model, iam=iam_front,
npoints=npoints)
# back side POA irradiance
irrad_back = get_irradiance_poa(
surface_tilt=backside_tilt, surface_azimuth=backside_sysaz,
solar_zenith=solar_zenith, solar_azimuth=solar_azimuth,
gcr=gcr, height=height, pitch=pitch, ghi=ghi, dhi=dhi, dni=dni,
albedo=albedo, iam=iam_back, npoints=npoints)
albedo=albedo, dni_extra=dni_extra, model=model, iam=iam_back,
npoints=npoints)

colmap_front = {
'poa_global': 'poa_front',
Expand Down
39 changes: 39 additions & 0 deletions pvlib/tests/bifacial/test_infinite_sheds.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,42 @@ def test_get_irradiance_limiting_gcr():
result['poa_back_sky_diffuse'])
assert np.isclose(result['poa_front_ground_diffuse'],
result['poa_back_ground_diffuse'])


def test_get_irradiance_with_haydavies():
# singleton inputs
solar_zenith = 0.
solar_azimuth = 180.
surface_tilt = 0.
surface_azimuth = 180.
gcr = 0.5
height = 1.
pitch = 1.
ghi = 1000.
dhi = 300.
dni = 700.
albedo = 0.
dni_extra = 1413.
model = 'haydavies'
iam_front = 1.0
iam_back = 1.0
npoints = 100
result = infinite_sheds.get_irradiance(
surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
gcr, height, pitch, ghi, dhi, dni, albedo, dni_extra, model,
iam_front, iam_back, bifaciality=0.8, shade_factor=-0.02,
transmission_factor=0, npoints=npoints)
expected_front_diffuse = np.array([151.38])
expected_front_direct = np.array([848.62])
expected_front_global = expected_front_diffuse + expected_front_direct
assert np.isclose(result['poa_front'], expected_front_global)
assert np.isclose(result['poa_front_diffuse'], expected_front_diffuse)
assert np.isclose(result['poa_front_direct'], expected_front_direct)
assert np.isclose(result['poa_global'], result['poa_front'])
# test for when dni_extra is not supplied
with pytest.raises(ValueError, match='supply dni_extra for haydavies'):
result = infinite_sheds.get_irradiance(
surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
gcr, height, pitch, ghi, dhi, dni, albedo, None, model,
iam_front, iam_back, bifaciality=0.8, shade_factor=-0.02,
transmission_factor=0, npoints=npoints)