Skip to content
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