SAPM
This function evaluates the Sandia Photovoltaic Array Performance Model to generate the principal operating points of a module IV curve from effective irradiance, cell temperature, and a dictionary of SAPM module coefficients.
The result summarizes the curve with short-circuit current, maximum-power current and voltage, open-circuit voltage, and the auxiliary shape points I_x and I_{xx}. The peak electrical output is represented by:
P_{mp} = I_{mp} V_{mp}
Module coefficients must be supplied as a 2D list of key-value pairs, for example [["C0", 1.0], ["Isco", 6.0]]. The wrapper converts that list into the dict-like structure required by pvlib.
Excel Usage
=SAPM(effective_irradiance, temp_cell, module_params, temperature_ref, irradiance_ref)
effective_irradiance(float, required): Irradiance reaching the cells (W/m^2).temp_cell(float, required): Cell temperature (C).module_params(list[list], required): 2D list of SAPM parameters [[‘key’, value], …].temperature_ref(float, optional, default: 25): Reference temperature (C).irradiance_ref(float, optional, default: 1000): Reference irradiance (W/m^2).
Returns (list[list]): 2D list [[i_sc, i_mp, v_oc, v_mp, p_mp, i_x, i_xx]], or an error string.
Example 1: Standard SAPM points
Inputs:
| effective_irradiance | temp_cell | module_params | |
|---|---|---|---|
| 1000 | 25 | C0 | 1.012 |
| C1 | -0.012 | ||
| C2 | 0 | ||
| C3 | 0 | ||
| Isco | 6 | ||
| Impo | 5.5 | ||
| Voco | 36 | ||
| Vmpo | 30 | ||
| Aisc | 0.001 | ||
| Aimp | 0.0005 | ||
| Bvoco | -0.12 | ||
| Mbvoc | 0 | ||
| Bvmpo | -0.1 | ||
| Mbvmp | 0 | ||
| N | 1.2 | ||
| Cells_in_series | 60 | ||
| IXO | 5.8 | ||
| IXXO | 5.3 | ||
| C4 | 1 | ||
| C5 | 0 | ||
| C6 | 1 | ||
| C7 | 0 |
Excel formula:
=SAPM(1000, 25, {"C0",1.012;"C1",-0.012;"C2",0;"C3",0;"Isco",6;"Impo",5.5;"Voco",36;"Vmpo",30;"Aisc",0.001;"Aimp",0.0005;"Bvoco",-0.12;"Mbvoc",0;"Bvmpo",-0.1;"Mbvmp",0;"N",1.2;"Cells_in_series",60;"IXO",5.8;"IXXO",5.3;"C4",1;"C5",0;"C6",1;"C7",0})
Expected output:
| Result | ||||||
|---|---|---|---|---|---|---|
| 6 | 5.5 | 36 | 30 | 165 | 5.8 | 5.3 |
Example 2: Lower irradiance operating points
Inputs:
| effective_irradiance | temp_cell | module_params | |
|---|---|---|---|
| 800 | 25 | C0 | 1.012 |
| C1 | -0.012 | ||
| C2 | 0 | ||
| C3 | 0 | ||
| Isco | 6 | ||
| Impo | 5.5 | ||
| Voco | 36 | ||
| Vmpo | 30 | ||
| Aisc | 0.001 | ||
| Aimp | 0.0005 | ||
| Bvoco | -0.12 | ||
| Mbvoc | 0 | ||
| Bvmpo | -0.1 | ||
| Mbvmp | 0 | ||
| N | 1.2 | ||
| Cells_in_series | 60 | ||
| IXO | 5.8 | ||
| IXXO | 5.3 | ||
| C4 | 1 | ||
| C5 | 0 | ||
| C6 | 1 | ||
| C7 | 0 |
Excel formula:
=SAPM(800, 25, {"C0",1.012;"C1",-0.012;"C2",0;"C3",0;"Isco",6;"Impo",5.5;"Voco",36;"Vmpo",30;"Aisc",0.001;"Aimp",0.0005;"Bvoco",-0.12;"Mbvoc",0;"Bvmpo",-0.1;"Mbvmp",0;"N",1.2;"Cells_in_series",60;"IXO",5.8;"IXXO",5.3;"C4",1;"C5",0;"C6",1;"C7",0})
Expected output:
| Result | ||||||
|---|---|---|---|---|---|---|
| 4.8 | 4.41056 | 35.5872 | 30 | 132.317 | 4.64 | 4.24 |
Example 3: Warmer cell temperature case
Inputs:
| effective_irradiance | temp_cell | module_params | |
|---|---|---|---|
| 1000 | 45 | C0 | 1.012 |
| C1 | -0.012 | ||
| C2 | 0 | ||
| C3 | 0 | ||
| Isco | 6 | ||
| Impo | 5.5 | ||
| Voco | 36 | ||
| Vmpo | 30 | ||
| Aisc | 0.001 | ||
| Aimp | 0.0005 | ||
| Bvoco | -0.12 | ||
| Mbvoc | 0 | ||
| Bvmpo | -0.1 | ||
| Mbvmp | 0 | ||
| N | 1.2 | ||
| Cells_in_series | 60 | ||
| IXO | 5.8 | ||
| IXXO | 5.3 | ||
| C4 | 1 | ||
| C5 | 0 | ||
| C6 | 1 | ||
| C7 | 0 |
Excel formula:
=SAPM(1000, 45, {"C0",1.012;"C1",-0.012;"C2",0;"C3",0;"Isco",6;"Impo",5.5;"Voco",36;"Vmpo",30;"Aisc",0.001;"Aimp",0.0005;"Bvoco",-0.12;"Mbvoc",0;"Bvmpo",-0.1;"Mbvmp",0;"N",1.2;"Cells_in_series",60;"IXO",5.8;"IXXO",5.3;"C4",1;"C5",0;"C6",1;"C7",0})
Expected output:
| Result | ||||||
|---|---|---|---|---|---|---|
| 6.12 | 5.555 | 33.6 | 28 | 155.54 | 5.916 | 5.353 |
Example 4: Lowercase cell-series alias is accepted
Inputs:
| effective_irradiance | temp_cell | module_params | |
|---|---|---|---|
| 1000 | 25 | C0 | 1.012 |
| C1 | -0.012 | ||
| C2 | 0 | ||
| C3 | 0 | ||
| Isco | 6 | ||
| Impo | 5.5 | ||
| Voco | 36 | ||
| Vmpo | 30 | ||
| Aisc | 0.001 | ||
| Aimp | 0.0005 | ||
| Bvoco | -0.12 | ||
| Mbvoc | 0 | ||
| Bvmpo | -0.1 | ||
| Mbvmp | 0 | ||
| N | 1.2 | ||
| cells_in_series | 60 | ||
| IXO | 5.8 | ||
| IXXO | 5.3 | ||
| C4 | 1 | ||
| C5 | 0 | ||
| C6 | 1 | ||
| C7 | 0 |
Excel formula:
=SAPM(1000, 25, {"C0",1.012;"C1",-0.012;"C2",0;"C3",0;"Isco",6;"Impo",5.5;"Voco",36;"Vmpo",30;"Aisc",0.001;"Aimp",0.0005;"Bvoco",-0.12;"Mbvoc",0;"Bvmpo",-0.1;"Mbvmp",0;"N",1.2;"cells_in_series",60;"IXO",5.8;"IXXO",5.3;"C4",1;"C5",0;"C6",1;"C7",0})
Expected output:
| Result | ||||||
|---|---|---|---|---|---|---|
| 6 | 5.5 | 36 | 30 | 165 | 5.8 | 5.3 |
Python Code
Show Code
from pvlib.pvsystem import sapm as result_func
import pandas as pd
def sapm(effective_irradiance, temp_cell, module_params, temperature_ref=25, irradiance_ref=1000):
"""
Sandia Photovoltaic Array Performance Model (SAPM) solver.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.pvsystem.sapm.html
This example function is provided as-is without any representation of accuracy.
Args:
effective_irradiance (float): Irradiance reaching the cells (W/m^2).
temp_cell (float): Cell temperature (C).
module_params (list[list]): 2D list of SAPM parameters [['key', value], ...].
temperature_ref (float, optional): Reference temperature (C). Default is 25.
irradiance_ref (float, optional): Reference irradiance (W/m^2). Default is 1000.
Returns:
list[list]: 2D list [[i_sc, i_mp, v_oc, v_mp, p_mp, i_x, i_xx]], or an error string.
"""
try:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
irrad = float(effective_irradiance)
tc = float(temp_cell)
module_params = to2d(module_params)
if not isinstance(module_params, list) or not all(isinstance(row, list) for row in module_params):
return "Error: module_params must be a 2D list of key-value pairs"
key_aliases = {
"cells_in_series": "Cells_in_Series",
"cells_in_Series": "Cells_in_Series",
"Cells_in_series": "Cells_in_Series"
}
mod_dict = {}
for row in module_params:
if len(row) >= 2:
key = str(row[0]).strip()
key = key_aliases.get(key, key)
val = row[1]
try:
val = float(val)
except (TypeError, ValueError):
pass
mod_dict[key] = val
t_ref = float(temperature_ref) if temperature_ref is not None else 25.0
i_ref = float(irradiance_ref) if irradiance_ref is not None else 1000.0
res = result_func(
effective_irradiance=irrad,
temp_cell=tc,
module=mod_dict,
temperature_ref=t_ref,
irradiance_ref=i_ref
)
if isinstance(res, pd.DataFrame):
row_res = res.iloc[0].to_dict()
elif isinstance(res, dict):
row_res = dict(res)
else:
# Some versions of pvlib return an OrderedDict or other mapping-like object
# which may not implement to_dict(). Fall back to dict() conversion.
if hasattr(res, "to_dict"):
row_res = res.to_dict()
else:
try:
row_res = dict(res)
except Exception:
row_res = {}
out = [
float(row_res.get('i_sc', 0)),
float(row_res.get('i_mp', 0)),
float(row_res.get('v_oc', 0)),
float(row_res.get('v_mp', 0)),
float(row_res.get('p_mp', 0)),
float(row_res.get('i_x', 0)),
float(row_res.get('i_xx', 0))
]
return [out]
except Exception as e:
return f"Error: {str(e)}"