Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ Inverter models (DC to AC conversion)
:toctree: generated/

inverter.sandia
inverter.sandia_multi
inverter.adr
inverter.pvwatts

Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.8.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Enhancements
option to :py:class:`~pvlib.modelchain.ModelChain`. (:pull:`1042`) (:issue:`1073`)
* Added :py:func:`pvlib.temperature.ross` for cell temperature modeling using
only NOCT. (:pull:`1045`)
* Added :py:func:`pvlib.inverter.sandia_multi` for modeling inverters with
multiple MPPTs (:issue:`457`, :pull:`1085`)
* Added optional ``attributes`` parameter to :py:func:`pvlib.iotools.get_psm3`
and added the option of fetching 5- and 15-minute PSM3 data. (:pull:`1086`)

Expand Down
117 changes: 99 additions & 18 deletions pvlib/inverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,44 @@

import numpy as np
import pandas as pd

from numpy.polynomial.polynomial import polyfit # different than np.polyfit


def _sandia_eff(v_dc, p_dc, inverter):
r'''
Calculate the inverter AC power without clipping
'''
Paco = inverter['Paco']
Pdco = inverter['Pdco']
Vdco = inverter['Vdco']
C0 = inverter['C0']
C1 = inverter['C1']
C2 = inverter['C2']
C3 = inverter['C3']
Pso = inverter['Pso']

A = Pdco * (1 + C1 * (v_dc - Vdco))
B = Pso * (1 + C2 * (v_dc - Vdco))
C = C0 * (1 + C3 * (v_dc - Vdco))

return (Paco / (A - B) - C * (A - B)) * (p_dc - B) + C * (p_dc - B)**2


def _sandia_limits(power_ac, p_dc, Paco, Pnt, Pso):
r'''
Applies minimum and maximum power limits to `power_ac`
'''
power_ac = np.minimum(Paco, power_ac)
min_ac_power = -1.0 * abs(Pnt)
below_limit = p_dc < Pso
try:
power_ac[below_limit] = min_ac_power
except TypeError: # power_ac is a float
if below_limit:
power_ac = min_ac_power
return power_ac


def sandia(v_dc, p_dc, inverter):
r'''
Convert DC power and voltage to AC power using Sandia's
Expand Down Expand Up @@ -54,10 +88,10 @@ def sandia(v_dc, p_dc, inverter):
Column Description
====== ============================================================
Paco AC power rating of the inverter. [W]
Pdco DC power input to inverter, typically assumed to be equal
to the PV array maximum power. [W]
Pdco DC power input that results in Paco output at reference
voltage Vdco. [W]
Vdco DC voltage at which the AC power rating is achieved
at the reference operating condition. [V]
with Pdco power input. [V]
Pso DC power required to start the inversion process, or
self-consumption by inverter, strongly influences inverter
efficiency at low power levels. [W]
Expand Down Expand Up @@ -91,29 +125,76 @@ def sandia(v_dc, p_dc, inverter):
'''

Paco = inverter['Paco']
Pdco = inverter['Pdco']
Vdco = inverter['Vdco']
Pso = inverter['Pso']
C0 = inverter['C0']
C1 = inverter['C1']
C2 = inverter['C2']
C3 = inverter['C3']
Pnt = inverter['Pnt']
Pso = inverter['Pso']

A = Pdco * (1 + C1 * (v_dc - Vdco))
B = Pso * (1 + C2 * (v_dc - Vdco))
C = C0 * (1 + C3 * (v_dc - Vdco))

power_ac = (Paco / (A - B) - C * (A - B)) * (p_dc - B) + C * (p_dc - B)**2
power_ac = np.minimum(Paco, power_ac)
power_ac = np.where(p_dc < Pso, -1.0 * abs(Pnt), power_ac)
power_ac = _sandia_eff(v_dc, p_dc, inverter)
power_ac = _sandia_limits(power_ac, p_dc, Paco, Pnt, Pso)

if isinstance(p_dc, pd.Series):
power_ac = pd.Series(power_ac, index=p_dc.index)

return power_ac


def sandia_multi(v_dc, p_dc, inverter):
r'''
Convert DC power and voltage to AC power for an inverter with multiple
MPPT inputs.

Uses Sandia's Grid-Connected PV Inverter model [1]_.

Parameters
----------
v_dc : tuple, list or array of numeric
DC voltage on each MPPT input of the inverter. If type is array, must
be 2d with axis 0 being the MPPT inputs. [V]

p_dc : tuple, list or array of numeric
DC power on each MPPT input of the inverter. If type is array, must
be 2d with axis 0 being the MPPT inputs. [W]

inverter : dict-like
Defines parameters for the inverter model in [1]_.

Returns
-------
power_ac : numeric
AC power output for the inverter. [W]

Raises
------
ValueError
If v_dc and p_dc have different lengths.

Notes
-----
See :py:func:`pvlib.inverter.sandia` for definition of the parameters in
`inverter`.

References
----------
.. [1] D. King, S. Gonzalez, G. Galbraith, W. Boyson, "Performance Model
for Grid-Connected Photovoltaic Inverters", SAND2007-5036, Sandia
National Laboratories.

See also
--------
pvlib.inverter.sandia
'''

if len(p_dc) != len(v_dc):
raise ValueError('p_dc and v_dc have different lengths')
power_dc = sum(p_dc)
power_ac = 0. * power_dc

for vdc, pdc in zip(v_dc, p_dc):
Comment thread
cwhanse marked this conversation as resolved.
power_ac += pdc / power_dc * _sandia_eff(vdc, power_dc, inverter)

return _sandia_limits(power_ac, power_dc, inverter['Paco'],
inverter['Pnt'], inverter['Pso'])


def adr(v_dc, p_dc, inverter, vtol=0.10):
r'''
Converts DC power and voltage to AC power using Anton Driesse's
Expand Down
41 changes: 40 additions & 1 deletion pvlib/tests/test_inverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,14 @@ def test_sandia_float(cec_inverter_parameters):
vdcs = 25.
idcs = 5.5
pdcs = idcs * vdcs

pacs = inverter.sandia(vdcs, pdcs, cec_inverter_parameters)
assert_allclose(pacs, 132.004278, 5)
# test at low power condition
vdcs = 25.
idcs = 0
pdcs = idcs * vdcs
pacs = inverter.sandia(vdcs, pdcs, cec_inverter_parameters)
assert_allclose(pacs, -1. * cec_inverter_parameters['Pnt'], 5)


def test_sandia_Pnt_micro():
Expand Down Expand Up @@ -95,6 +100,40 @@ def test_sandia_Pnt_micro():
assert_series_equal(pacs, pd.Series([-0.043, 132.545914746, 240.0]))


def test_sandia_multi(cec_inverter_parameters):
vdcs = pd.Series(np.linspace(0, 50, 3))
idcs = pd.Series(np.linspace(0, 11, 3)) / 2
Comment thread
cwhanse marked this conversation as resolved.
pdcs = idcs * vdcs
pacs = inverter.sandia_multi((vdcs, vdcs), (pdcs, pdcs),
cec_inverter_parameters)
assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000]))
# with lists instead of tuples
pacs = inverter.sandia_multi([vdcs, vdcs], [pdcs, pdcs],
cec_inverter_parameters)
assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000]))
# with arrays instead of tuples
pacs = inverter.sandia_multi(np.array([vdcs, vdcs]),
np.array([pdcs, pdcs]),
cec_inverter_parameters)
assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000]))


def test_sandia_multi_length_error(cec_inverter_parameters):
vdcs = pd.Series(np.linspace(0, 50, 3))
idcs = pd.Series(np.linspace(0, 11, 3))
pdcs = idcs * vdcs
with pytest.raises(ValueError, match='p_dc and v_dc have different'):
inverter.sandia_multi((vdcs,), (pdcs, pdcs), cec_inverter_parameters)


def test_sandia_multi_array(cec_inverter_parameters):
vdcs = np.linspace(0, 50, 3)
idcs = np.linspace(0, 11, 3)
pdcs = idcs * vdcs
pacs = inverter.sandia_multi((vdcs,), (pdcs,), cec_inverter_parameters)
assert_allclose(pacs, np.array([-0.020000, 132.004278, 250.000000]))


def test_pvwatts_scalars():
expected = 85.58556604752516
out = inverter.pvwatts(90, 100, 0.95)
Expand Down