Skip to content

Add optional arguments temperature_ref and irradiance_ref to pvsystem.sapm #2434

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

Merged
merged 16 commits into from
Apr 21, 2025
Merged
2 changes: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.12.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Enhancements
* :py:mod:`pvlib.ivtools.sdm` is now a subpackage. (:issue:`2252`, :pull:`2256`)
* Add a function for estimating PVsyst SDM parameters from IEC 61853-1 matrix
data (:py:func:`~pvlib.ivtools.sdm.fit_pvsyst_iec61853_sandia_2025`). (:issue:`2185`, :pull:`2429`)
* Add optional arguments ``temperature_ref`` and ``irradiance_ref`` to
:py:func:`~pvlib.pvsystem.sapm`(:issue:`2432`, :pull:`2434`)

Documentation
~~~~~~~~~~~~~
Expand Down
47 changes: 24 additions & 23 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from abc import ABC, abstractmethod
from typing import Optional, Union

from pvlib._deprecation import deprecated

import pvlib # used to avoid albedo name collision in the Array class
from pvlib import (atmosphere, iam, inverter, irradiance,
singlediode as _singlediode, spectrum, temperature)
Expand Down Expand Up @@ -2195,25 +2193,32 @@ def _parse_raw_sam_df(csvdata):
return df


def sapm(effective_irradiance, temp_cell, module):
def sapm(effective_irradiance, temp_cell, module, *, temperature_ref=25,
irradiance_ref=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
SAND2004-3535. Assumes a reference cell temperature of 25 C.
SAND2004-3535. Assumes a reference cell temperature of 25°C.

Parameters
----------
effective_irradiance : numeric
Irradiance reaching the module's cells, after reflections and
adjustment for spectrum. [W/m2]
adjustment for spectrum. [Wm⁻²]

temp_cell : numeric
Cell temperature [C].
Cell temperature [°C].

module : dict-like
A dict or Series defining the SAPM parameters. See the notes section
for more details.

temperature_ref : numeric, optional
Reference temperature [°C]

irradiance_ref : numeric, optional
Reference irradiance [Wm⁻²]

Returns
-------
A DataFrame with the columns:
Expand Down Expand Up @@ -2251,19 +2256,19 @@ def sapm(effective_irradiance, temp_cell, module):
Voco Open circuit voltage at reference condition (amps)
Vmpo Maximum power voltage at reference condition (amps)
Aisc Short circuit current temperature coefficient at
reference condition (1/C)
reference condition (1/°C)
Aimp Maximum power current temperature coefficient at
reference condition (1/C)
reference condition (1/°C)
Bvoco Open circuit voltage temperature coefficient at
reference condition (V/C)
reference condition (V/°C)
Mbvoc Coefficient providing the irradiance dependence for the
BetaVoc temperature coefficient at reference irradiance
(V/C)
(V/°C)
Bvmpo Maximum power voltage temperature coefficient at
reference condition
Mbvmp Coefficient providing the irradiance dependence for the
BetaVmp temperature coefficient at reference irradiance
(V/C)
(V/°C)
N Empirically determined "diode factor" (dimensionless)
Cells_in_Series Number of cells in series in a module's cell string(s)
IXO Ix at reference conditions
Expand All @@ -2284,16 +2289,11 @@ def sapm(effective_irradiance, temp_cell, module):
pvlib.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

q = constants.e # Elementary charge in units of coulombs
kb = constants.k # Boltzmann's constant in units of J/K

# avoid problem with integer input
Ee = np.array(effective_irradiance, dtype='float64') / irrad_ref
Ee = np.array(effective_irradiance, dtype='float64') / irradiance_ref

# set up masking for 0, positive, and nan inputs
Ee_gt_0 = np.full_like(Ee, False, dtype='bool')
Expand All @@ -2316,31 +2316,32 @@ def sapm(effective_irradiance, temp_cell, module):
out = OrderedDict()

out['i_sc'] = (
module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell - temp_ref)))
module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell -
temperature_ref)))

out['i_mp'] = (
module['Impo'] * (module['C0']*Ee + module['C1']*(Ee**2)) *
(1 + module['Aimp']*(temp_cell - temp_ref)))
(1 + module['Aimp']*(temp_cell - temperature_ref)))

out['v_oc'] = np.maximum(0, (
module['Voco'] + cells_in_series * delta * logEe +
Bvoco*(temp_cell - temp_ref)))
Bvoco*(temp_cell - temperature_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 - temp_ref)))
Bvmpo*(temp_cell - temperature_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 - temp_ref)))
(1 + module['Aisc']*(temp_cell - temperature_ref)))

out['i_xx'] = (
module['IXXO'] * (module['C6']*Ee + module['C7']*(Ee**2)) *
(1 + module['Aimp']*(temp_cell - temp_ref)))
(1 + module['Aimp']*(temp_cell - temperature_ref)))

if isinstance(out['i_sc'], pd.Series):
out = pd.DataFrame(out)
Expand Down
Loading