diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index b1e5f07b82..35e23a3559 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -414,7 +414,7 @@ def infer_dc_model(self): 'system.module_parameters') def sapm(self): - self.dc = self.system.sapm(self.effective_irradiance/1000., + self.dc = self.system.sapm(self.effective_irradiance, self.temps['temp_cell']) self.dc = self.system.scale_voltage_current_power(self.dc) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 332a43b9a0..6ae92869be 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -404,21 +404,12 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): Parameters ---------- - poa_direct : Series - The direct irradiance incident upon the module (W/m^2). - - poa_diffuse : Series - The diffuse irradiance incident on module. + effective_irradiance : Series + The effective irradiance incident upon the module (W/m^2). temp_cell : Series The cell temperature (degrees C). - airmass_absolute : Series - Absolute airmass. - - aoi : Series - Angle of incidence (degrees). - **kwargs See pvsystem.sapm for details @@ -478,8 +469,7 @@ def sapm_aoi_loss(self, aoi): return sapm_aoi_loss(aoi, self.module_parameters) def sapm_effective_irradiance(self, poa_direct, poa_diffuse, - airmass_absolute, aoi, - reference_irradiance=1000): + airmass_absolute, aoi): """ Use the :py:func:`sapm_effective_irradiance` function, the input parameters, and ``self.module_parameters`` to calculate @@ -499,17 +489,14 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, aoi : numeric Angle of incidence in degrees. - reference_irradiance : numeric, default 1000 - Reference irradiance by which to divide the input irradiance. - Returns ------- effective_irradiance : numeric - The SAPM effective irradiance. + The SAPM effective irradiance in W/m^2. """ return sapm_effective_irradiance( poa_direct, poa_diffuse, airmass_absolute, aoi, - self.module_parameters, reference_irradiance=reference_irradiance) + self.module_parameters) def first_solar_spectral_loss(self, pw, airmass_absolute): @@ -1649,7 +1636,7 @@ def _parse_raw_sam_df(csvdata): return df -def sapm(effective_irradiance, temp_cell, module): +def sapm(effective_irradiance, temp_cell, module, reference_irradiance=1000): ''' 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 @@ -1658,7 +1645,7 @@ 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). @@ -1667,6 +1654,9 @@ def sapm(effective_irradiance, temp_cell, module): A dict, Series, or DataFrame defining the SAPM performance parameters. See the notes section for more details. + reference_irradiance : float + Default 1000 W/m^2. + Returns ------- A DataFrame with the columns: @@ -1739,7 +1729,7 @@ def sapm(effective_irradiance, temp_cell, module): 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') / reference_irradiance # set up masking for 0, positive, and nan inputs Ee_gt_0 = np.full_like(Ee, False, dtype='bool') @@ -1986,7 +1976,7 @@ def sapm_aoi_loss(aoi, module, upper=None): def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, - module, reference_irradiance=1000): + module): """ Calculates the SAPM effective irradiance using the SAPM spectral loss and SAPM angle of incidence loss functions. @@ -2010,21 +2000,16 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, 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 - The SAPM effective irradiance. + The SAPM effective irradiance in W/m^2. """ F1 = sapm_spectral_loss(airmass_absolute, module) F2 = sapm_aoi_loss(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 c0e89c5095..210d9e3bf8 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -195,10 +195,13 @@ def pvsyst_module_params(): def test_sapm(sapm_module_params): times = pd.DatetimeIndex(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) + reference_irradiance = 1000 - out = pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) + out = pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params, + reference_irradiance) expected = pd.DataFrame(np.array( [[ -5.0608322 , -4.65037767, nan, nan, @@ -216,7 +219,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 +241,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, @@ -331,33 +334,26 @@ def test_PVSystem_sapm_aoi_loss(sapm_module_params, mocker): @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.11573])) ]) 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) else: - assert_allclose(out, expected, atol=1e-4) + assert_allclose(out, expected, atol=1) def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): @@ -368,15 +364,12 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): poa_diffuse = 100 airmass_absolute = 1.5 aoi = 0 - reference_irradiance = 1000 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) - assert_allclose(out, 1, atol=0.1) + poa_direct, poa_diffuse, airmass_absolute, aoi, sapm_module_params) + assert_allclose(out, 1000, atol=1) def test_calcparams_desoto(cec_module_params):