From df13672483746cca71dacb1f7d3df886f2fe6604 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 6 Nov 2019 15:20:09 -0700 Subject: [PATCH 01/12] change effective_irradiance units in sapm, change sapm_effective_irradiance function --- pvlib/pvsystem.py | 50 +++++++++++++++++++++---------------- pvlib/test/test_pvsystem.py | 34 ++++++++++++------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index feaf9c42ce..144d20e609 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1571,7 +1571,7 @@ def _parse_raw_sam_df(csvdata): return df -def sapm(effective_irradiance, temp_cell, module): +def sapm(effective_irradiance, temp_cell, module, irrad_ref=1000, temp_ref=25): ''' The Sandia PV Array Performance Model (SAPM) generates 5 points on a PV module's I-V curve (Voc, Isc, Ix, Ixx, Vmp/Imp) according to @@ -1580,15 +1580,21 @@ def sapm(effective_irradiance, temp_cell, module): Parameters ---------- effective_irradiance : numeric - Effective irradiance (suns). + Effective irradiance [W/m^2]. temp_cell : numeric - The cell temperature (degrees C). + Cell temperature [C]. module : dict-like A dict, Series, or DataFrame defining the SAPM performance parameters. See the notes section for more details. + irrad_ref : float (optional, default=1000) + Reference irradiance [W/m^2]. + + temp_ref : float (optional, default=25) + Reference cell temperature [C] + Returns ------- A DataFrame with the columns: @@ -1659,12 +1665,19 @@ def sapm(effective_irradiance, temp_cell, module): temperature.sapm_module ''' - T0 = 25 + # TODO: remove this check after deprecation period for change in effective + # irradiance units, made in v0.7 + if np.all(effective_irradiance < 2.0): + import warnings + warnings.warn('Effective irradiance input to SAPM appears low.' + ' Units changed in v0.7 from suns to W/m2', + pvlibDeprecationWarning) + q = 1.60218e-19 # Elementary charge in units of coulombs kb = 1.38066e-23 # Boltzmann's constant in units of J/K # avoid problem with integer input - Ee = np.array(effective_irradiance, dtype='float64') + Ee = np.array(effective_irradiance, dtype='float64') / irrad_ref # set up masking for 0, positive, and nan inputs Ee_gt_0 = np.full_like(Ee, False, dtype='bool') @@ -1687,32 +1700,32 @@ def sapm(effective_irradiance, temp_cell, module): out = OrderedDict() out['i_sc'] = ( - module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell - T0))) + module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell - temp_ref))) out['i_mp'] = ( module['Impo'] * (module['C0']*Ee + module['C1']*(Ee**2)) * - (1 + module['Aimp']*(temp_cell - T0))) + (1 + module['Aimp']*(temp_cell - temp_ref))) out['v_oc'] = np.maximum(0, ( module['Voco'] + cells_in_series * delta * logEe + - Bvoco*(temp_cell - T0))) + Bvoco*(temp_cell - temp_ref))) out['v_mp'] = np.maximum(0, ( module['Vmpo'] + module['C2'] * cells_in_series * delta * logEe + module['C3'] * cells_in_series * ((delta * logEe) ** 2) + - Bvmpo*(temp_cell - T0))) + Bvmpo*(temp_cell - temp_ref))) out['p_mp'] = out['i_mp'] * out['v_mp'] out['i_x'] = ( module['IXO'] * (module['C4']*Ee + module['C5']*(Ee**2)) * - (1 + module['Aisc']*(temp_cell - T0))) + (1 + module['Aisc']*(temp_cell - temp_ref))) # the Ixx calculation in King 2004 has a typo (mixes up Aisc and Aimp) out['i_xx'] = ( module['IXXO'] * (module['C6']*Ee + module['C7']*(Ee**2)) * - (1 + module['Aisc']*(temp_cell - T0))) + (1 + module['Aisc']*(temp_cell - temp_ref))) if isinstance(out['i_sc'], pd.Series): out = pd.DataFrame(out) @@ -1847,25 +1860,22 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, Parameters ---------- poa_direct : numeric - The direct irradiance incident upon the module. + The direct irradiance incident upon the module. [W/m2] poa_diffuse : numeric - The diffuse irradiance incident on module. + The diffuse irradiance incident on module. [W/m2] airmass_absolute : numeric - Absolute airmass. + Absolute airmass. [unitless] aoi : numeric - Angle of incidence in degrees. + Angle of incidence. [degrees] module : dict-like A dict, Series, or DataFrame defining the SAPM performance parameters. See the :py:func:`sapm` notes section for more details. - reference_irradiance : numeric, default 1000 - Reference irradiance by which to divide the input irradiance. - Returns ------- effective_irradiance : numeric @@ -1875,9 +1885,7 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, F1 = sapm_spectral_loss(airmass_absolute, module) F2 = iam.sapm(aoi, module) - E0 = reference_irradiance - - Ee = F1 * (poa_direct*F2 + module['FD']*poa_diffuse) / E0 + Ee = F1 * (poa_direct*F2 + module['FD']*poa_diffuse) return Ee diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index bc9f1426a7..5c89fb1343 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -195,7 +195,7 @@ def test_retrieve_sam_cecinverter(): def test_sapm(sapm_module_params): times = pd.date_range(start='2015-01-01', periods=5, freq='12H') - effective_irradiance = pd.Series([-1, 0.5, 1.1, np.nan, 1], index=times) + effective_irradiance = pd.Series([-1000, 500, 1100, np.nan, 1000], index=times) temp_cell = pd.Series([10, 25, 50, 25, np.nan], index=times) out = pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) @@ -216,7 +216,7 @@ def test_sapm(sapm_module_params): assert_frame_equal(out, expected, check_less_precise=4) - out = pvsystem.sapm(1, 25, sapm_module_params) + out = pvsystem.sapm(1000, 25, sapm_module_params) expected = OrderedDict() expected['i_sc'] = 5.09115 @@ -238,7 +238,7 @@ def test_sapm(sapm_module_params): def test_PVSystem_sapm(sapm_module_params, mocker): mocker.spy(pvsystem, 'sapm') system = pvsystem.PVSystem(module_parameters=sapm_module_params) - effective_irradiance = 0.5 + effective_irradiance = 500 temp_cell = 25 out = system.sapm(effective_irradiance, temp_cell) pvsystem.sapm.assert_called_once_with(effective_irradiance, temp_cell, @@ -295,28 +295,21 @@ def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type, @pytest.mark.parametrize('test_input,expected', [ - ([1000, 100, 5, 45, 1000], 1.1400510967821877), + ([1000, 100, 5, 45], 1140.0510967821877), ([np.array([np.nan, 1000, 1000]), np.array([100, np.nan, 100]), np.array([1.1, 1.1, 1.1]), - np.array([10, 10, 10]), - 1000], - np.array([np.nan, np.nan, 1.081157])), + np.array([10, 10, 10])], + np.array([np.nan, np.nan, 1081.157])), ([pd.Series([1000]), pd.Series([100]), pd.Series([1.1]), - pd.Series([10]), 1370], - pd.Series([0.789166])) + pd.Series([10])], + pd.Series([1081.1574])) ]) def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): - try: - kwargs = {'reference_irradiance': test_input[4]} - test_input = test_input[:-1] - except IndexError: - kwargs = {} - test_input.append(sapm_module_params) - out = pvsystem.sapm_effective_irradiance(*test_input, **kwargs) + out = pvsystem.sapm_effective_irradiance(*test_input) if isinstance(test_input, pd.Series): assert_series_equal(out, expected, check_less_precise=4) @@ -1463,7 +1456,7 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker): @fail_on_pvlib_version('0.8') -def test_deprecated_08(): +def test_deprecated_08(sapm_module_params): with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_celltemp(1000, 25, 1) with pytest.warns(pvlibDeprecationWarning): @@ -1502,6 +1495,13 @@ def test_deprecated_08(): with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_aoi_loss(45, {'B5': 0.0, 'B4': 0.0, 'B3': 0.0, 'B2': 0.0, 'B1': 0.0, 'B0': 1.0}) + # deprecation warning for change in effective_irradiance units in + # pvsystem.sapm + effective_irradiance = np.array([0.1, 0.2, 1.3]) + temp_cell = np.array([25, 25, 50]) + warn_txt = 'Effective irradiance input to SAPM' + with pytest.warns(pvlibDeprecationWarning, match=warn_txt): + pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) @fail_on_pvlib_version('0.8') From 07c9294f5f93131bc5c352f5b7f395b3f91d485c Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 7 Nov 2019 08:25:07 -0700 Subject: [PATCH 02/12] whatsnew --- docs/sphinx/source/whatsnew/v0.7.0.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 06e5130f5d..28188f5f78 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -94,6 +94,13 @@ API Changes The `weather` argument of the above methods is now the first, required positional argument and the `times` argument is kept as the second keyword argument for capability during the deprecation period. +* New kwargs `irrad_ref` and `temp_ref` added to :py:func:`pvsystem.sapm`. + Previously, the reference irradiance and cell temperature were hard-coded. +* The `effective_irradiance` argument for :py:func:`pvsystem.sapm` now requires + units of W/m^2. Previously, units for this input were suns. A deprecation + warning is raised if all `effective_irradiance` values are extremely low. +* The output of :py:func:`pvsystem.sapm_effective_irradiance` is now in units + of W/m2 rather than suns. Enhancements ~~~~~~~~~~~~ From 1c41cd741ae8552e9695e18b86a04a5107f470f3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 7 Nov 2019 08:40:21 -0700 Subject: [PATCH 03/12] docstring additions --- pvlib/pvsystem.py | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 144d20e609..57350b0a9c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1853,7 +1853,7 @@ def sapm_spectral_loss(airmass_absolute, module): def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, module, reference_irradiance=1000): - """ + r""" Calculates the SAPM effective irradiance using the SAPM spectral loss and SAPM angle of incidence loss functions. @@ -1879,7 +1879,36 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, Returns ------- effective_irradiance : numeric - The SAPM effective irradiance. + Effective irradiance accounting for reflections and spectral content. + [W/m2] + + Notes + ----- + The SAPM model for effective irradiance [1] translates broadband direct and + diffuse irradiance on the plane of array to the irradiance absorbed by a + module's cells. + + The model is + .. math:: + + Ee = f_1(AM_a) (E_b f_2(AOI) + f_d E_d) + + where :math:`Ee` is effective irradiance (W/m2), :math:`f_1` is a fourth + degree polynomial in air mass :math:`AM_a`, :math:`E_b` is beam (direct) + irradiance on the plane of array, :math:`E_d` is diffuse irradiance on the + plane of array, :math:`f_2` is a fifth degree polynomial in the angle of + incidence :math:`AOI`, and :math:`f_d` is the fraction of diffuse + irradiance on the plane of array that is not reflected away. + + References + ---------- + [1] D. King et al, "Sandia Photovoltaic Array Performance Model", + SAND2004-3535, Sandia National Laboratories, Albuquerque, NM + + See also + -------- + pvlib.iam.sapm + pvlib.pvsystem.sapm_spectral_loss """ F1 = sapm_spectral_loss(airmass_absolute, module) @@ -2000,7 +2029,7 @@ def singlediode(photocurrent, saturation_current, resistance_series, the IV curve are linearly spaced. References - ----------- + ---------- [1] S.R. Wenham, M.A. Green, M.E. Watt, "Applied Photovoltaics" ISBN 0 86758 909 4 From de6d9d1690082359749b5a38a3d0508a9ba5efd7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 7 Nov 2019 08:54:36 -0700 Subject: [PATCH 04/12] lint --- pvlib/test/test_pvsystem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 5c89fb1343..e030ab9886 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -195,7 +195,8 @@ def test_retrieve_sam_cecinverter(): def test_sapm(sapm_module_params): times = pd.date_range(start='2015-01-01', periods=5, freq='12H') - effective_irradiance = pd.Series([-1000, 500, 1100, np.nan, 1000], index=times) + effective_irradiance = pd.Series([-1000, 500, 1100, np.nan, 1000], + index=times) temp_cell = pd.Series([10, 25, 50, 25, np.nan], index=times) out = pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) From eb9be232cce5b11afbc63e3802ad4786cca03d5f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 7 Nov 2019 10:09:11 -0700 Subject: [PATCH 05/12] remove /1000 from ModelChain.sapm, fix pvsystem tests --- pvlib/modelchain.py | 2 +- pvlib/test/test_pvsystem.py | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 4786667503..1f253a25b8 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -440,7 +440,7 @@ def infer_dc_model(self): 'set the model with the dc_model kwarg.') def sapm(self): - self.dc = self.system.sapm(self.effective_irradiance/1000., + self.dc = self.system.sapm(self.effective_irradiance, self.cell_temperature) self.dc = self.system.scale_voltage_current_power(self.dc) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index e030ab9886..ece19d14fe 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -236,6 +236,17 @@ def test_sapm(sapm_module_params): pd.Series(sapm_module_params)) +def test_pvsystem_sapm_warning(sapm_module_params): + # deprecation warning for change in effective_irradiance units in + # pvsystem.sapm + # TODO: remove after deprecation period (v0.8) + effective_irradiance = np.array([0.1, 0.2, 1.3]) + temp_cell = np.array([25, 25, 50]) + warn_txt = 'Effective irradiance input to SAPM' + with pytest.warns(pvlibDeprecationWarning, match=warn_txt): + pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) + + def test_PVSystem_sapm(sapm_module_params, mocker): mocker.spy(pvsystem, 'sapm') system = pvsystem.PVSystem(module_parameters=sapm_module_params) @@ -301,7 +312,7 @@ def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type, np.array([100, np.nan, 100]), np.array([1.1, 1.1, 1.1]), np.array([10, 10, 10])], - np.array([np.nan, np.nan, 1081.157])), + np.array([np.nan, np.nan, 1081.1574])), ([pd.Series([1000]), pd.Series([100]), pd.Series([1.1]), pd.Series([10])], pd.Series([1081.1574])) @@ -315,7 +326,7 @@ def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): if isinstance(test_input, pd.Series): assert_series_equal(out, expected, check_less_precise=4) else: - assert_allclose(out, expected, atol=1e-4) + assert_allclose(out, expected, atol=1e-1) def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): @@ -334,7 +345,7 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): pvsystem.sapm_effective_irradiance.assert_called_once_with( poa_direct, poa_diffuse, airmass_absolute, aoi, sapm_module_params, reference_irradiance=reference_irradiance) - assert_allclose(out, 1, atol=0.1) + assert_allclose(out, 1000, atol=0.1) def test_PVSystem_sapm_celltemp(mocker): @@ -1457,9 +1468,11 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker): @fail_on_pvlib_version('0.8') -def test_deprecated_08(sapm_module_params): +def test_deprecated_08(): + # deprecated function pvsystem.sapm_celltemp with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_celltemp(1000, 25, 1) + # deprecated function pvsystem.pvsyst_celltemp with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) module_parameters = {'R_sh_ref': 1, 'a_ref': 1, 'I_o_ref': 1, @@ -1496,13 +1509,6 @@ def test_deprecated_08(sapm_module_params): with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_aoi_loss(45, {'B5': 0.0, 'B4': 0.0, 'B3': 0.0, 'B2': 0.0, 'B1': 0.0, 'B0': 1.0}) - # deprecation warning for change in effective_irradiance units in - # pvsystem.sapm - effective_irradiance = np.array([0.1, 0.2, 1.3]) - temp_cell = np.array([25, 25, 50]) - warn_txt = 'Effective irradiance input to SAPM' - with pytest.warns(pvlibDeprecationWarning, match=warn_txt): - pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) @fail_on_pvlib_version('0.8') From 6cf006c7b8e1e5ea7eb6c795c3a8921ab649c75b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 7 Nov 2019 11:15:16 -0700 Subject: [PATCH 06/12] test fix, docstring edits --- pvlib/pvsystem.py | 3 ++- pvlib/test/test_pvsystem.py | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 97feafa03a..15c34742e5 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1891,7 +1891,7 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, The model is .. math:: - Ee = f_1(AM_a) (E_b f_2(AOI) + f_d E_d) + `Ee = f_1(AM_a) (E_b f_2(AOI) + f_d E_d)` where :math:`Ee` is effective irradiance (W/m2), :math:`f_1` is a fourth degree polynomial in air mass :math:`AM_a`, :math:`E_b` is beam (direct) @@ -1909,6 +1909,7 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, -------- pvlib.iam.sapm pvlib.pvsystem.sapm_spectral_loss + pvlib.pvsystem.sapm """ F1 = sapm_spectral_loss(airmass_absolute, module) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index ece19d14fe..65000d9912 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -318,11 +318,8 @@ def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type, pd.Series([1081.1574])) ]) def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): - test_input.append(sapm_module_params) - out = pvsystem.sapm_effective_irradiance(*test_input) - if isinstance(test_input, pd.Series): assert_series_equal(out, expected, check_less_precise=4) else: @@ -338,14 +335,18 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): airmass_absolute = 1.5 aoi = 0 reference_irradiance = 1000 - + p = (sapm_module_params['A4'], sapm_module_params['A3'], + sapm_module_params['A2'], sapm_module_params['A1'], + sapm_module_params['A0']) + f1 = np.polyval(p, airmass_absolute) + expected = f1 * (poa_direct + sapm_module_params['FD'] * poa_diffuse) out = system.sapm_effective_irradiance( poa_direct, poa_diffuse, airmass_absolute, aoi, reference_irradiance=reference_irradiance) pvsystem.sapm_effective_irradiance.assert_called_once_with( poa_direct, poa_diffuse, airmass_absolute, aoi, sapm_module_params, reference_irradiance=reference_irradiance) - assert_allclose(out, 1000, atol=0.1) + assert_allclose(out, expected, atol=0.1) def test_PVSystem_sapm_celltemp(mocker): From 1013a3341daf16106937bd264174c4f546b358e4 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 25 Nov 2019 13:23:36 -0700 Subject: [PATCH 07/12] change irrad_ref to suns, with deprecation message --- pvlib/pvsystem.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 15c34742e5..e40ad20740 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1571,7 +1571,7 @@ def _parse_raw_sam_df(csvdata): return df -def sapm(effective_irradiance, temp_cell, module, irrad_ref=1000, temp_ref=25): +def sapm(effective_irradiance, temp_cell, module, irrad_ref=1., temp_ref=25): ''' The Sandia PV Array Performance Model (SAPM) generates 5 points on a PV module's I-V curve (Voc, Isc, Ix, Ixx, Vmp/Imp) according to @@ -1580,7 +1580,8 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1000, temp_ref=25): Parameters ---------- effective_irradiance : numeric - Effective irradiance [W/m^2]. + Effective irradiance in suns (deprecated unit) or W/m2. Unit must be + the same as for irrad_ref. temp_cell : numeric Cell temperature [C]. @@ -1589,8 +1590,9 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1000, temp_ref=25): A dict or Series defining the SAPM parameters. See the notes section for more details. - irrad_ref : float (optional, default=1000) - Reference irradiance [W/m^2]. + irrad_ref : float (optional, default=1.0) + Reference irradiance in suns (deprecated unit) or W/m2. Unit must be + the same as for effective_irradiance temp_ref : float (optional, default=25) Reference cell temperature [C] @@ -1665,12 +1667,14 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1000, temp_ref=25): temperature.sapm_module ''' - # TODO: remove this check after deprecation period for change in effective - # irradiance units, made in v0.7 - if np.all(effective_irradiance < 2.0): + # TODO: remove this warning in v0.8 after deprecation period for change in + # effective irradiance units, made in v0.7 + if irrad_ref == 1.0: import warnings - warnings.warn('Effective irradiance input to SAPM appears low.' - ' Units changed in v0.7 from suns to W/m2', + warnings.warn('Reference irradiance is 1.0 suns. Default value and ' + 'unit changing to 1000 W/m2 in pvlib v0.8. Set ' + 'irrad_ref=1000 to silence this warning and maintain ' + 'future compatibility.', pvlibDeprecationWarning) q = 1.60218e-19 # Elementary charge in units of coulombs @@ -1852,7 +1856,7 @@ def sapm_spectral_loss(airmass_absolute, module): def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, - module, reference_irradiance=1000): + module, reference_irradiance=1.): r""" Calculates the SAPM effective irradiance using the SAPM spectral loss and SAPM angle of incidence loss functions. @@ -1876,11 +1880,14 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, parameters. See the :py:func:`sapm` notes section for more details. + reference_irradiance : float (optional, default=1.0) + Reference irradiance in suns (deprecated unit) or W/m2. + Returns ------- effective_irradiance : numeric Effective irradiance accounting for reflections and spectral content. - [W/m2] + Returned in units of reference_irradiance. Notes ----- @@ -1912,10 +1919,24 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, pvlib.pvsystem.sapm """ + # TODO: remove this warning in v0.8 after deprecation period for change in + # effective irradiance units, made in v0.7 + if reference_irradiance == 1.0: + import warnings + warnings.warn('reference_irradiance is 1.0 suns, output in suns. ' + 'Output unit is changing to W/m2 in pvlib v0.8. Set ' + 'reference_irradiance=1000 to silence this warning and ' + 'maintain future compatibility.', + pvlibDeprecationWarning) + divisor = 1000 # input in W/m2, set output units to suns + else: # assume reference_irradiance in W/m2 + divisor = 1.0 + F1 = sapm_spectral_loss(airmass_absolute, module) F2 = iam.sapm(aoi, module) - Ee = F1 * (poa_direct*F2 + module['FD']*poa_diffuse) + Ee = F1 * (poa_direct * F2 + module['FD'] * poa_diffuse) / \ + divisor return Ee From ee437e175c540afefce101d6009efcfa9b996049 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 26 Nov 2019 08:33:45 -0700 Subject: [PATCH 08/12] remove suns option, new kwargs --- docs/sphinx/source/whatsnew/v0.7.0.rst | 2 -- pvlib/pvsystem.py | 46 +++++++------------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 1355753e15..5c12c3721c 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -99,8 +99,6 @@ API Changes The `weather` argument of the above methods is now the first, required positional argument and the `times` argument is kept as the second keyword argument for capability during the deprecation period. -* New kwargs `irrad_ref` and `temp_ref` added to :py:func:`pvsystem.sapm`. - Previously, the reference irradiance and cell temperature were hard-coded. * The `effective_irradiance` argument for :py:func:`pvsystem.sapm` now requires units of W/m^2. Previously, units for this input were suns. A deprecation warning is raised if all `effective_irradiance` values are extremely low. diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index c7e831a86e..3643b8cd59 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1571,7 +1571,7 @@ def _parse_raw_sam_df(csvdata): return df -def sapm(effective_irradiance, temp_cell, module, irrad_ref=1., temp_ref=25): +def sapm(effective_irradiance, temp_cell, module): ''' The Sandia PV Array Performance Model (SAPM) generates 5 points on a PV module's I-V curve (Voc, Isc, Ix, Ixx, Vmp/Imp) according to @@ -1580,8 +1580,8 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1., temp_ref=25): Parameters ---------- effective_irradiance : numeric - Effective irradiance in suns (deprecated unit) or W/m2. Unit must be - the same as for irrad_ref. + Irradiance reaching the module's cells, after reflections and + adjustment for spectrum. [W/m2] temp_cell : numeric Cell temperature [C]. @@ -1590,13 +1590,6 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1., temp_ref=25): A dict or Series defining the SAPM parameters. See the notes section for more details. - irrad_ref : float (optional, default=1.0) - Reference irradiance in suns (deprecated unit) or W/m2. Unit must be - the same as for effective_irradiance - - temp_ref : float (optional, default=25) - Reference cell temperature [C] - Returns ------- A DataFrame with the columns: @@ -1667,14 +1660,16 @@ def sapm(effective_irradiance, temp_cell, module, irrad_ref=1., temp_ref=25): temperature.sapm_module ''' + # TODO: someday, change temp_ref and irrad_ref to reference_temperature and + # reference_irradiance and expose + temp_ref = 25 + irrad_ref = 1000 # TODO: remove this warning in v0.8 after deprecation period for change in # effective irradiance units, made in v0.7 - if irrad_ref == 1.0: + if np.all(effective_irradiance) < 2.0: import warnings - warnings.warn('Reference irradiance is 1.0 suns. Default value and ' - 'unit changing to 1000 W/m2 in pvlib v0.8. Set ' - 'irrad_ref=1000 to silence this warning and maintain ' - 'future compatibility.', + warnings.warn('All effective_irradiance inputs to SAPM are low.' + ' Units changed in v0.7 from suns to W/m2', pvlibDeprecationWarning) q = 1.60218e-19 # Elementary charge in units of coulombs @@ -1856,7 +1851,7 @@ def sapm_spectral_loss(airmass_absolute, module): def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, - module, reference_irradiance=1.): + module): r""" Calculates the SAPM effective irradiance using the SAPM spectral loss and SAPM angle of incidence loss functions. @@ -1880,9 +1875,6 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, parameters. See the :py:func:`sapm` notes section for more details. - reference_irradiance : float (optional, default=1.0) - Reference irradiance in suns (deprecated unit) or W/m2. - Returns ------- effective_irradiance : numeric @@ -1919,24 +1911,10 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, pvlib.pvsystem.sapm """ - # TODO: remove this warning in v0.8 after deprecation period for change in - # effective irradiance units, made in v0.7 - if reference_irradiance == 1.0: - import warnings - warnings.warn('reference_irradiance is 1.0 suns, output in suns. ' - 'Output unit is changing to W/m2 in pvlib v0.8. Set ' - 'reference_irradiance=1000 to silence this warning and ' - 'maintain future compatibility.', - pvlibDeprecationWarning) - divisor = 1000 # input in W/m2, set output units to suns - else: # assume reference_irradiance in W/m2 - divisor = 1.0 - F1 = sapm_spectral_loss(airmass_absolute, module) F2 = iam.sapm(aoi, module) - Ee = F1 * (poa_direct * F2 + module['FD'] * poa_diffuse) / \ - divisor + Ee = F1 * (poa_direct * F2 + module['FD'] * poa_diffuse) return Ee From 370585b7ee3319b67e534e53e20fee9f585b9fd5 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 26 Nov 2019 08:47:20 -0700 Subject: [PATCH 09/12] remove reference_irradiance from tests --- pvlib/test/test_pvsystem.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 65000d9912..d3c460e25f 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -242,7 +242,7 @@ def test_pvsystem_sapm_warning(sapm_module_params): # TODO: remove after deprecation period (v0.8) effective_irradiance = np.array([0.1, 0.2, 1.3]) temp_cell = np.array([25, 25, 50]) - warn_txt = 'Effective irradiance input to SAPM' + warn_txt = 'All effective_irradiance inputs to SAPM' with pytest.warns(pvlibDeprecationWarning, match=warn_txt): pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) @@ -334,18 +334,15 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): poa_diffuse = 100 airmass_absolute = 1.5 aoi = 0 - reference_irradiance = 1000 p = (sapm_module_params['A4'], sapm_module_params['A3'], sapm_module_params['A2'], sapm_module_params['A1'], sapm_module_params['A0']) f1 = np.polyval(p, airmass_absolute) expected = f1 * (poa_direct + sapm_module_params['FD'] * poa_diffuse) out = system.sapm_effective_irradiance( - poa_direct, poa_diffuse, airmass_absolute, - aoi, reference_irradiance=reference_irradiance) + poa_direct, poa_diffuse, airmass_absolute, aoi) pvsystem.sapm_effective_irradiance.assert_called_once_with( - poa_direct, poa_diffuse, airmass_absolute, aoi, sapm_module_params, - reference_irradiance=reference_irradiance) + poa_direct, poa_diffuse, airmass_absolute, aoi, sapm_module_params) assert_allclose(out, expected, atol=0.1) From 31adf5bd7395afba7acebc65f1420f39a7dae362 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 26 Nov 2019 10:25:16 -0700 Subject: [PATCH 10/12] fix method test --- pvlib/test/test_pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index d3c460e25f..c9bec9529d 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -242,8 +242,8 @@ def test_pvsystem_sapm_warning(sapm_module_params): # TODO: remove after deprecation period (v0.8) effective_irradiance = np.array([0.1, 0.2, 1.3]) temp_cell = np.array([25, 25, 50]) - warn_txt = 'All effective_irradiance inputs to SAPM' - with pytest.warns(pvlibDeprecationWarning, match=warn_txt): + warn_txt = 'effective_irradiance inputs appear to be in suns' + with pytest.warns(RuntimeWarning, match=warn_txt): pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) From 31bb1f464410de563ca5ea35313a4ae182ef6395 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 26 Nov 2019 10:25:30 -0700 Subject: [PATCH 11/12] update warning --- pvlib/pvsystem.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 3643b8cd59..93491a5704 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -563,28 +563,25 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, Parameters ---------- poa_direct : numeric - The direct irradiance incident upon the module. + The direct irradiance incident upon the module. [W/m2] poa_diffuse : numeric - The diffuse irradiance incident on module. + The diffuse irradiance incident on module. [W/m2] airmass_absolute : numeric - Absolute airmass. + Absolute airmass. [unitless] aoi : numeric - Angle of incidence in degrees. - - reference_irradiance : numeric, default 1000 - Reference irradiance by which to divide the input irradiance. + Angle of incidence. [degrees] Returns ------- effective_irradiance : numeric - The SAPM effective irradiance. + The SAPM effective irradiance. [W/m2] """ return sapm_effective_irradiance( poa_direct, poa_diffuse, airmass_absolute, aoi, - self.module_parameters, reference_irradiance=reference_irradiance) + self.module_parameters) def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): """Uses :py:func:`temperature.pvsyst_cell` to calculate cell @@ -1668,9 +1665,9 @@ def sapm(effective_irradiance, temp_cell, module): # effective irradiance units, made in v0.7 if np.all(effective_irradiance) < 2.0: import warnings - warnings.warn('All effective_irradiance inputs to SAPM are low.' + warnings.warn('effective_irradiance inputs appear to be in suns.' ' Units changed in v0.7 from suns to W/m2', - pvlibDeprecationWarning) + RuntimeWarning) q = 1.60218e-19 # Elementary charge in units of coulombs kb = 1.38066e-23 # Boltzmann's constant in units of J/K @@ -1879,7 +1876,7 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, ------- effective_irradiance : numeric Effective irradiance accounting for reflections and spectral content. - Returned in units of reference_irradiance. + [W/m2] Notes ----- From c121f92de707ade8cb538c54f02fc3ae6e881070 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 26 Nov 2019 10:26:00 -0700 Subject: [PATCH 12/12] separate API breaking changes --- docs/sphinx/source/whatsnew/v0.7.0.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 5c12c3721c..81e02135fd 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -10,8 +10,16 @@ compatibility notes. **Python 2.7 support ended on June 1, 2019.** (:issue:`501`) **Minimum numpy version is now 1.10.4. Minimum pandas version is now 0.18.1.** -API Changes -~~~~~~~~~~~ +API Breaking Changes +~~~~~~~~~~~~~~~~~~~~ +* The `effective_irradiance` argument for :py:func:`pvsystem.sapm` now requires + units of W/m^2. Previously, units for this input were suns. A RuntimeWarning + warning is raised if all `effective_irradiance < 2.0`. +* The output of :py:func:`pvsystem.sapm_effective_irradiance` is now in units + of W/m2 rather than suns. + +API Changes with Deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Changes related to cell temperature models (:issue:`678`): * Changes to functions - Moved functions for cell temperature from `pvsystem.py` to `temperature.py`. @@ -99,11 +107,6 @@ API Changes The `weather` argument of the above methods is now the first, required positional argument and the `times` argument is kept as the second keyword argument for capability during the deprecation period. -* The `effective_irradiance` argument for :py:func:`pvsystem.sapm` now requires - units of W/m^2. Previously, units for this input were suns. A deprecation - warning is raised if all `effective_irradiance` values are extremely low. -* The output of :py:func:`pvsystem.sapm_effective_irradiance` is now in units - of W/m2 rather than suns. * Parameter `pvsystem.DC_MODEL_PARAMS` is renamed to `pvsystem._DC_MODEL_PARAMS`. Users should not rely on this dictionary's existence or structure. * :py:func:`pvlib.iotools.midc.read_midc` now passes additional keyword