Skip to content

Change sapm to expect effective_irradiance in W/m2 #609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
43 changes: 14 additions & 29 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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):

Expand Down Expand Up @@ -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
Expand All @@ -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).
Expand All @@ -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:
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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.
Expand All @@ -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

Expand Down
41 changes: 17 additions & 24 deletions pvlib/test/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down