From 7b2259817c98aefc623797dec7aa4ba62692d6e3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 10 May 2021 10:29:43 -0600 Subject: [PATCH 1/6] edits to get_total_irradiance and get_sky_diffuse --- pvlib/irradiance.py | 89 +++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 3ec6b213f9..179822226e 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -14,7 +14,7 @@ from pvlib import atmosphere, solarposition, tools -# see References section of grounddiffuse function +# see References section of get_ground_diffuse function SURFACE_ALBEDOS = {'urban': 0.18, 'grass': 0.20, 'fresh grass': 0.26, @@ -323,38 +323,50 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Parameters ---------- surface_tilt : numeric - Panel tilt from horizontal. + Panel tilt from horizontal.[degree] surface_azimuth : numeric - Panel azimuth from north. + Panel azimuth from north. [degree] solar_zenith : numeric - Solar zenith angle. + Solar zenith angle. [degree] solar_azimuth : numeric - Solar azimuth angle. + Solar azimuth angle. [degree] dni : numeric - Direct Normal Irradiance + Direct Normal Irradiance. [W/m2] ghi : numeric - Global horizontal irradiance + Global horizontal irradiance. [W/m2] dhi : numeric - Diffuse horizontal irradiance + Diffuse horizontal irradiance. [W/m2] dni_extra : None or numeric, default None - Extraterrestrial direct normal irradiance + Extraterrestrial direct normal irradiance. [W/m2] airmass : None or numeric, default None - Airmass + Relative airmass (not adjusted for pressure). [unitless] albedo : numeric, default 0.25 - Surface albedo + Surface albedo. [unitless] surface_type : None or String, default None - Surface type. See grounddiffuse. + Surface type. See :py:func:`~pvlib.irradiance.grounddiffuse` for + the list of accepted values. model : String, default 'isotropic' - Irradiance model. + Irradiance model. Can be one of 'isotropic', 'klucher', 'haydavies', + 'reindl', 'king', 'perez'. model_perez : String, default 'allsitescomposite1990' - Used only if model='perez'. See :py:func:`perez`. + Used only if model='perez'. See :py:func:`~pvlib.irradiance.perez`. Returns ------- total_irrad : OrderedDict or DataFrame Contains keys/columns ``'poa_global', 'poa_direct', 'poa_diffuse', 'poa_sky_diffuse', 'poa_ground_diffuse'``. + + Notes + ----- + Models 'haydavies', 'reindl', or 'perez' require 'dni_extra'. Values can + be calculated using :py:func:`~pvlib.irradiance.get_extra_radiation`. + + The 'perez' model requires relative airmass ('airmass') as input. If + 'airmass' is not provided, it is calculated usign the defaults in + :py:func:`~pvlib.irradiance.get_relative_airmass`. """ + poa_sky_diffuse = get_sky_diffuse( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, ghi, dhi, dni_extra=dni_extra, airmass=airmass, model=model, @@ -387,34 +399,55 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, Parameters ---------- surface_tilt : numeric - Panel tilt from horizontal. + Panel tilt from horizontal.[degree] surface_azimuth : numeric - Panel azimuth from north. + Panel azimuth from north. [degree] solar_zenith : numeric - Solar zenith angle. + Solar zenith angle. [degree] solar_azimuth : numeric - Solar azimuth angle. + Solar azimuth angle. [degree] dni : numeric - Direct Normal Irradiance + Direct Normal Irradiance. [W/m2] ghi : numeric - Global horizontal irradiance + Global horizontal irradiance. [W/m2] dhi : numeric - Diffuse horizontal irradiance + Diffuse horizontal irradiance. [W/m2] dni_extra : None or numeric, default None - Extraterrestrial direct normal irradiance + Extraterrestrial direct normal irradiance. [W/m2] airmass : None or numeric, default None - Airmass + Relative airmass (not adjusted for pressure). [unitless] model : String, default 'isotropic' - Irradiance model. + Irradiance model. Can be one of 'isotropic', 'klucher', 'haydavies', + 'reindl', 'king', 'perez'. model_perez : String, default 'allsitescomposite1990' - See perez. + Used only if model='perez'. See :py:func:`~pvlib.irradiance.perez`. Returns ------- poa_sky_diffuse : numeric + Sky diffuse irradiance in the plane of array. [W/m2] + + Raises + ------ + ValueError + If model is one of 'haydavies', 'reindl', or 'perez' and dni_extra + is None. + + Notes + ----- + Models 'haydavies', 'reindl', or 'perez' require 'dni_extra'. Values can + be calculated using :py:func:`~pvlib.irradiance.get_extra_radiation`. + + The 'perez' model requires relative airmass ('airmass') as input. If + 'airmass' is not provided, it is calculated usign the defaults in + :py:func:`~pvlib.irradiance.get_relative_airmass`. """ model = model.lower() + + if (model in {'haydavies', 'reindl', 'perez'}) and (dni_extra is None): + raise ValueError(f'dni_extra is required for model {model}') + if model == 'isotropic': sky = isotropic(surface_tilt, dhi) elif model == 'klucher': @@ -429,6 +462,8 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, elif model == 'king': sky = king(surface_tilt, dhi, ghi, solar_zenith) elif model == 'perez': + if airmass is None: + airmass = atmosphere.get_relative_airmass(solar_zenith) sky = perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra, solar_zenith, solar_azimuth, airmass, model=model_perez) @@ -501,7 +536,7 @@ def poa_components(aoi, dni, poa_sky_diffuse, poa_ground_diffuse): def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None): ''' Estimate diffuse irradiance from ground reflections given - irradiance, albedo, and surface tilt + irradiance, albedo, and surface tilt. Function to determine the portion of irradiance on a tilted surface due to ground reflections. Any of the inputs may be DataFrames or @@ -515,7 +550,7 @@ def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None): (e.g. surface facing up = 0, surface facing horizon = 90). ghi : numeric - Global horizontal irradiance in W/m^2. + Global horizontal irradiance. [W/m^2] albedo : numeric, default 0.25 Ground reflectance, typically 0.1-0.4 for surfaces on Earth From 42b3dc731878a526029a9bc005067e3769d702fc Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 10 May 2021 14:04:31 -0600 Subject: [PATCH 2/6] add tests --- pvlib/tests/test_irradiance.py | 51 ++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index f11897d8e6..e91f5bd3cf 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -281,13 +281,35 @@ def test_sky_diffuse_zenith_close_to_90(model): assert sky_diffuse < 100 -def test_get_sky_diffuse_invalid(): +def test_get_sky_diffuse_model_invalid(): with pytest.raises(ValueError): irradiance.get_sky_diffuse( 30, 180, 0, 180, 1000, 1100, 100, dni_extra=1360, airmass=1, model='invalid') +def test_get_sky_diffuse_missing_dni_extra(): + msg = 'dni_extra is required' + with pytest.raises(ValueError, match=msg): + irradiance.get_sky_diffuse( + 30, 180, 0, 180, 1000, 1100, 100, airmass=1, + model='haydavies') + + +def test_get_sky_diffuse_missing_airmass(irrad_data, ephem_data, dni_et): + # test assumes location is Tuscon, AZ + # calculated airmass should be the equivalent to fixture airmass + dni = irrad_data['dni'].copy() + dni.iloc[2] = np.nan + out = irradiance.get_sky_diffuse( + 40, 180, ephem_data['apparent_zenith'], ephem_data['azimuth'], dni, + irrad_data['ghi'], irrad_data['dhi'], dni_et, model='perez') + expected = pd.Series(np.array( + [0., 31.46046871, np.nan, 45.45539877]), + index=irrad_data.index) + assert_series_equal(out, expected, check_less_precise=2) + + def test_campbell_norman(): expected = pd.DataFrame(np.array( [[863.859736967, 653.123094076, 220.65905025]]), @@ -299,7 +321,8 @@ def test_campbell_norman(): assert_frame_equal(out, expected) -def test_get_total_irradiance(irrad_data, ephem_data, dni_et, relative_airmass): +def test_get_total_irradiance(irrad_data, ephem_data, dni_et, + relative_airmass): models = ['isotropic', 'klucher', 'haydavies', 'reindl', 'king', 'perez'] @@ -337,6 +360,30 @@ def test_get_total_irradiance_scalars(model): assert np.isnan(np.array(list(total.values()))).sum() == 0 +def test_get_total_irradiance_missing_dni_extra(): + msg = 'dni_extra is required' + with pytest.raises(ValueError, match=msg): + irradiance.get_total_irradiance( + 32, 180, + 10, 180, + dni=1000, ghi=1100, + dhi=100, + model='haydavies') + + +def test_get_total_irradiance_missing_airmass(): + total = irradiance.get_total_irradiance( + 32, 180, + 10, 180, + dni=1000, ghi=1100, + dhi=100, + dni_extra=1400, + model='perez') + assert list(total.keys()) == ['poa_global', 'poa_direct', + 'poa_diffuse', 'poa_sky_diffuse', + 'poa_ground_diffuse'] + + def test_poa_components(irrad_data, ephem_data, dni_et, relative_airmass): aoi = irradiance.aoi(40, 180, ephem_data['apparent_zenith'], ephem_data['azimuth']) From aec5ffe590e892319db0f083d895e003e717f8ee Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 11 May 2021 10:17:23 -0600 Subject: [PATCH 3/6] whatsnew --- docs/sphinx/source/whatsnew/v0.9.0.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index a75697a67c..8c5318a6f1 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -111,6 +111,10 @@ Enhancements * Added :py:func:`~pvlib.ivtools.sdm.pvsyst_temperature_coeff` to calculate the temperature coefficient of power for the pvsyst module model. (:pull:`1190`) +* :py:func:`~pvlib.irradiance.get_total_irradiance` and + :py:func:`~pvlib.irradiance.get_total_irradiance` now fill in ``airmass`` + if required and not provided. These functions now raise a ``ValueError`` + if ``dni_extra`` is required and not provided. (:issue:`949`, :pull:`1224`) Bug fixes ~~~~~~~~~ From 3adeaabe36443c11d1e306aef5fb15aafe5b9d79 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 13 May 2021 10:21:23 -0600 Subject: [PATCH 4/6] improvements from review --- docs/sphinx/source/whatsnew/v0.9.0.rst | 4 +-- pvlib/irradiance.py | 49 +++++++++++++------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index 8c5318a6f1..947953911e 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -112,9 +112,9 @@ Enhancements the temperature coefficient of power for the pvsyst module model. (:pull:`1190`) * :py:func:`~pvlib.irradiance.get_total_irradiance` and - :py:func:`~pvlib.irradiance.get_total_irradiance` now fill in ``airmass`` + :py:func:`~pvlib.irradiance.get_sky_diffuse` now fill in ``airmass`` if required and not provided. These functions now raise a ``ValueError`` - if ``dni_extra`` is required and not provided. (:issue:`949`, :pull:`1224`) + if ``dni_extra`` is required and not provided. (:issue:`949`, :pull:`1225`) Bug fixes ~~~~~~~~~ diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 179822226e..c98b3648bb 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -323,7 +323,7 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Parameters ---------- surface_tilt : numeric - Panel tilt from horizontal.[degree] + Panel tilt from horizontal. [degree] surface_azimuth : numeric Panel azimuth from north. [degree] solar_zenith : numeric @@ -342,14 +342,14 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Relative airmass (not adjusted for pressure). [unitless] albedo : numeric, default 0.25 Surface albedo. [unitless] - surface_type : None or String, default None - Surface type. See :py:func:`~pvlib.irradiance.grounddiffuse` for + surface_type : None or str, default None + Surface type. See :py:func:`~pvlib.irradiance.get_ground_diffuse` for the list of accepted values. - model : String, default 'isotropic' - Irradiance model. Can be one of 'isotropic', 'klucher', 'haydavies', - 'reindl', 'king', 'perez'. - model_perez : String, default 'allsitescomposite1990' - Used only if model='perez'. See :py:func:`~pvlib.irradiance.perez`. + model : str, default 'isotropic' + Irradiance model. Can be one of ``'isotropic'``, ``'klucher'``, + ``'haydavies'``, ``'reindl'``, ``'king'``, ``'perez'``. + model_perez : str, default 'allsitescomposite1990' + Used only if ``model='perez'``. See :py:func:`~pvlib.irradiance.perez`. Returns ------- @@ -362,9 +362,9 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Models 'haydavies', 'reindl', or 'perez' require 'dni_extra'. Values can be calculated using :py:func:`~pvlib.irradiance.get_extra_radiation`. - The 'perez' model requires relative airmass ('airmass') as input. If - 'airmass' is not provided, it is calculated usign the defaults in - :py:func:`~pvlib.irradiance.get_relative_airmass`. + The ``'perez'`` model requires relative airmass (``airmass``) as input. If + ``airmass`` is not provided, it is calculated using the defaults in + :py:func:`~pvlib.atmosphere.get_relative_airmass`. """ poa_sky_diffuse = get_sky_diffuse( @@ -399,7 +399,7 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, Parameters ---------- surface_tilt : numeric - Panel tilt from horizontal.[degree] + Panel tilt from horizontal. [degree] surface_azimuth : numeric Panel azimuth from north. [degree] solar_zenith : numeric @@ -416,11 +416,11 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, Extraterrestrial direct normal irradiance. [W/m2] airmass : None or numeric, default None Relative airmass (not adjusted for pressure). [unitless] - model : String, default 'isotropic' - Irradiance model. Can be one of 'isotropic', 'klucher', 'haydavies', - 'reindl', 'king', 'perez'. - model_perez : String, default 'allsitescomposite1990' - Used only if model='perez'. See :py:func:`~pvlib.irradiance.perez`. + model : str, default 'isotropic' + Irradiance model. Can be one of ``'isotropic'``, ``'klucher'``, + ``'haydavies'``, ``'reindl'``, ``'king'``, ``'perez'``. + model_perez : str, default 'allsitescomposite1990' + Used only if ``model='perez'``. See :py:func:`~pvlib.irradiance.perez`. Returns ------- @@ -430,16 +430,17 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, Raises ------ ValueError - If model is one of 'haydavies', 'reindl', or 'perez' and dni_extra - is None. + If model is one of ``'haydavies'``, ``'reindl'``, or ``'perez'`` and + ``dni_extra`` is ``None``. Notes ----- - Models 'haydavies', 'reindl', or 'perez' require 'dni_extra'. Values can - be calculated using :py:func:`~pvlib.irradiance.get_extra_radiation`. + Models ``'haydavies'``, ``'reindl'``, and ``'perez``` require 'dni_extra'. + Values can be calculated using + :py:func:`~pvlib.irradiance.get_extra_radiation`. - The 'perez' model requires relative airmass ('airmass') as input. If - 'airmass' is not provided, it is calculated usign the defaults in + The ``'perez'`` model requires relative airmass (``airmass``) as input. If + ``airmass`` is not provided, it is calculated using the defaults in :py:func:`~pvlib.irradiance.get_relative_airmass`. """ @@ -566,7 +567,7 @@ def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None): Returns ------- grounddiffuse : numeric - Ground reflected irradiances in W/m^2. + Ground reflected irradiance. [W/m^2] References From a9c8f3aaac18f984460c685e8dcd3b7678e72aea Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 14 May 2021 09:36:12 -0600 Subject: [PATCH 5/6] spell Tucson --- pvlib/tests/test_irradiance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index e91f5bd3cf..c3061d2479 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -297,7 +297,7 @@ def test_get_sky_diffuse_missing_dni_extra(): def test_get_sky_diffuse_missing_airmass(irrad_data, ephem_data, dni_et): - # test assumes location is Tuscon, AZ + # test assumes location is Tucson, AZ # calculated airmass should be the equivalent to fixture airmass dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan From 62a6600217df1cedb9dd2ed390ff23e8f58836a2 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 14 May 2021 14:08:26 -0600 Subject: [PATCH 6/6] overlooked comments --- pvlib/irradiance.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index c98b3648bb..b218c06ae4 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -359,8 +359,9 @@ def get_total_irradiance(surface_tilt, surface_azimuth, Notes ----- - Models 'haydavies', 'reindl', or 'perez' require 'dni_extra'. Values can - be calculated using :py:func:`~pvlib.irradiance.get_extra_radiation`. + Models ``'haydavies'``, ``'reindl'``, or ``'perez'`` require + ``'dni_extra'``. Values can be calculated using + :py:func:`~pvlib.irradiance.get_extra_radiation`. The ``'perez'`` model requires relative airmass (``airmass``) as input. If ``airmass`` is not provided, it is calculated using the defaults in @@ -441,7 +442,7 @@ def get_sky_diffuse(surface_tilt, surface_azimuth, The ``'perez'`` model requires relative airmass (``airmass``) as input. If ``airmass`` is not provided, it is calculated using the defaults in - :py:func:`~pvlib.irradiance.get_relative_airmass`. + :py:func:`~pvlib.atmosphere.get_relative_airmass`. """ model = model.lower()