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)}"

Online Calculator

Irradiance reaching the cells (W/m^2).
Cell temperature (C).
2D list of SAPM parameters [['key', value], ...].
Reference temperature (C).
Reference irradiance (W/m^2).