Skip to content

Create inverter.py #886

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 36 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e9fe39c
create inverter.py, move code, add adrinverter fixture
cwhanse Feb 11, 2020
4be25ba
stickler, adjust PVSystem methods
cwhanse Feb 11, 2020
904595b
update modelchain, test fixes
cwhanse Feb 11, 2020
1769295
sticker
cwhanse Feb 11, 2020
1dd3e7b
Merge branch 'master' of https://github.com/pvlib/pvlib-python into i…
cwhanse May 21, 2020
06cb965
docstring improvements
cwhanse May 21, 2020
d13d162
more docstring work
cwhanse May 21, 2020
592d33b
lint, kludge inverter.test_deprecated_09
cwhanse May 21, 2020
1bb1567
lint, kludge test_deprecated_09 x2
cwhanse May 21, 2020
a27bbaf
Merge branch 'master' of https://github.com/pvlib/pvlib-python into i…
cwhanse Jun 10, 2020
d371713
use new conftest capability
cwhanse Jun 10, 2020
f9f8ae7
correct use of fixture
cwhanse Jun 10, 2020
f6d6f77
add test for invalid ac model string
cwhanse Jun 10, 2020
d0e811f
update whatsnew
cwhanse Jun 10, 2020
f630f05
update api.rst
cwhanse Jun 10, 2020
78a7611
extend test_ac_models to new key values
cwhanse Jun 10, 2020
4a19d67
more for whatsnew
cwhanse Jun 10, 2020
ea90bec
correct modelchain.ac_model test
cwhanse Jun 10, 2020
1eb75cd
test correction
cwhanse Jun 10, 2020
e471ec2
edits from review
cwhanse Jun 14, 2020
4759394
more edits from review
cwhanse Jun 15, 2020
7183c95
fix thedocs, error
cwhanse Jun 16, 2020
7e9532f
fix indents
cwhanse Jun 16, 2020
6dc4dae
Merge branch 'master' of https://github.com/pvlib/pvlib-python into i…
cwhanse Jun 25, 2020
c85ab82
improvements from review
cwhanse Jun 26, 2020
dd40006
more improvements from review
cwhanse Jun 26, 2020
89c3460
Merge branch 'master' of https://github.com/pvlib/pvlib-python into i…
cwhanse Jul 1, 2020
7663321
improvements from review
cwhanse Jul 6, 2020
ff13e7e
improvements from review
cwhanse Jul 6, 2020
da5dc15
Merge branch 'master' into inverter
cwhanse Jul 6, 2020
75d818b
last cleanup
cwhanse Jul 7, 2020
9a9f105
last cleanup part 2
cwhanse Jul 7, 2020
9be21e4
last cleanup part 3
cwhanse Jul 7, 2020
33cddd2
py:attribute in whatsnew
cwhanse Jul 7, 2020
1e07351
docstring improvements
cwhanse Jul 7, 2020
6ba55aa
one more
cwhanse Jul 7, 2020
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
305 changes: 305 additions & 0 deletions pvlib/inverter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
# -*- coding: utf-8 -*-
"""
This module contains functions for inverter modeling, primarily conversion of
DC to AC power.
"""

import numpy as np
import pandas as pd


def sandia(v_dc, p_dc, inverter):
r'''
Convert DC power and voltage to AC power using Sandia's
Grid-Connected PV Inverter model.

Parameters
----------
v_dc : numeric
DC voltage input to the inverter. [V]

p_dc : numeric
DC power input to the inverter. [W]

inverter : dict-like
Defines parameters for the inverter model in [1]_. See Notes for
required model parameters. A copy of the parameter database from the
System Advisor Model (SAM) [2]_ is provided with pvlib and may be read
using :py:func:`pvlib.pvsystem.retrieve_sam.

Returns
-------
ac_power : numeric
AC power output. [W]

Notes
-----

Determines the AC power output of an inverter given the DC voltage and DC
power. Output AC power is bounded above by the parameter ``Paco``, to
represent inverter "clipping". When `ac_power` would be less than
parameter ``Pso`` (startup power required), then `ac_power` is set to
``-Pnt``, representing self-consumption. `ac_power` is not adjusted for
maximum power point tracking (MPPT) voltage windows or maximum current
limits of the inverter.

Required model parameters are:

====== ============================================================
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]
Vdco DC voltage at which the AC power rating is achieved
at the reference operating condition. [V]
Pso DC power required to start the inversion process, or
self-consumption by inverter, strongly influences inverter
efficiency at low power levels. [W]
C0 Parameter defining the curvature (parabolic) of the
relationship between AC power and DC power at the reference
operating condition. [1/W]
C1 Empirical coefficient allowing ``Pdco`` to vary linearly
with DC voltage input. [1/V]
C2 Empirical coefficient allowing ``Pso`` to vary linearly with
DC voltage input. [1/V]
C3 Empirical coefficient allowing Co to vary linearly with
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
C3 Empirical coefficient allowing Co to vary linearly with
C3 Empirical coefficient allowing ``C0`` to vary linearly with

DC voltage input. [1/V]
Pnt AC power consumed by the inverter at night (night tare). [W]
====== ============================================================

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

.. [2] System Advisor Model web page. https://sam.nrel.gov.

See also
--------
pvlib.pvsystem.retrieve_sam
'''

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']

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

ac_power = (Paco/(A-B) - C*(A-B)) * (p_dc-B) + C*((p_dc-B)**2)
ac_power = np.minimum(Paco, ac_power)
ac_power = np.where(p_dc < Pso, -1.0 * abs(Pnt), ac_power)

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

return ac_power


def adr(v_dc, p_dc, inverter, vtol=0.10):
r'''
Converts DC power and voltage to AC power using Anton Driesse's
Grid-Connected PV Inverter efficiency model
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Grid-Connected PV Inverter efficiency model
Grid-Connected PV Inverter efficiency model.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grid-connected inverter?


Parameters
----------
v_dc : numeric
DC voltage input to the inverter, should be >= 0. [V]

p_dc : numeric
DC power input to the inverter, should be >= 0. [W]

inverter : dict-like
Defines parameters for the inverter model in [1]_. See Notes for
required model parameters. A parameter database is provided with pvlib
and may be read using :py:func:`pvlib.pvsystem.retrieve_sam.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
and may be read using :py:func:`pvlib.pvsystem.retrieve_sam.
and may be read using :py:func:`pvlib.pvsystem.retrieve_sam`.

How relevant are the SAM coefficients here? The values might be useful, but the keys won't match up. Might be good to mention it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a file of coefficients for the ADR inverter model, which is also read by pvlib.pvystem.retrieve_sam. I really do think that this generic function could be improved and moved to pvlib.iotools, but not in this PR.


vtol : numeric, default 0.1
Fraction of DC voltage that determines how far the efficiency model is
extrapolated beyond the inverter's normal input voltage operating
range. 0.0 <= vtol <= 1.0. [unitless]

Returns
-------
ac_power : numeric
AC power output. [W]

Notes
-----

Determines the AC power output of an inverter given the DC voltage and DC
power. Output AC power is bounded above by the parameter ``Pacmax``, to
represent inverter "clipping". AC power is bounded below by ``-Pnt``
(negative when power is consumed rather than produced) which represents
self-consumption. `ac_power` is not adjusted for maximum power point
tracking (MPPT) voltage windows or maximum current limits of the inverter.

Required model parameters are:

======= ============================================================
Column Description
======= ============================================================
Pnom Nominal DC power, typically the DC power needed to produce
maximum AC power output. [W]

Vnom Nominal DC input voltage. Typically the level at which the
highest efficiency is achieved. [V]

Vmax Maximum DC input voltage. [V]

Vmin Minimum DC input voltage. [V]

Vdcmax . [V]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adriesse for the ADR inverter model, what is the definition of the parameter Vdcmax? The definition is missing from the function docstring. I'm having trouble distinguishing Vdcmax from Vmax, which I understand to be the maximum DC input voltage. It look like Vdcmax is an alternate upper bound on DC voltage.


MPPTHi Maximum DC voltage for MPPT range. [V]

MPPTLow Minimum DC voltage for MPPT range. [V]

Pacmax Maximum AC output power, used to clip the output power
if needed. [W]

ADRCoefficients A list of 9 coefficients that capture the influence
of input voltage and power on inverter losses, and thereby
efficiency. Corresponds to terms from [1]_ (in order): :math:
`b_{0,0}, b_{1,0}, b_{2,0}, b_{0,1}, b_{1,1}, b_{2,1}, b_{0,2},
b_{1,2}, b_{1,2}`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Units?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Difficult to describe. From Eq. 9 in the reference, b0,x is W, b1,x is 1/W, b2,x is 1/W^2 after one finds the note that "v_in" is normalized voltage so unitless. I think it is better to point to the reference. Do you agree?


Pnt AC power consumed by inverter at night (night tare) to
maintain circuitry required to sense PV array voltage. [W]

======= ============================================================
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter table doesn't show up on RTD -- maybe it's the linebreaks? The table in inverter.sandia renders correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sphinx still doesn't like this. I'm not if all of these steps are necessary, but I got it working locally for me by:

  1. Adding spacing between terms and definitions so that there is no overlap between columns (the ADRCoefficients term sticks out pretty far)
  2. Extending the left ==== strings so that they completely cover the column width
  3. Removing the empty line between Pnt and the trailing line.

Here's the text that works for me (with some text reflows to stay under 80 chars):

    ===============   ========================================================
    Column            Description
    ===============   ========================================================
    Pnom              Nominal DC power, typically the DC power needed to
                      produce maximum AC power output. [W]
    Vnom              Nominal DC input voltage. Typically the level at which
                      the highest efficiency is achieved. [V]
    Vmax              Maximum DC input voltage. [V]
    Vmin              Minimum DC input voltage. [V]
    Vdcmax            . [V]
    MPPTHi            Maximum DC voltage for MPPT range. [V]
    MPPTLow           Minimum DC voltage for MPPT range. [V]
    Pacmax            Maximum AC output power, used to clip the output power
                      if needed. [W]
    ADRCoefficients   A list of 9 coefficients that capture the influence
                      of input voltage and power on inverter losses, and
                      thereby efficiency. Corresponds to terms from [1]_
                      (in order): :math:`b_{0,0}, b_{1,0}, b_{2,0}, b_{0,1},
                      b_{1,1}, b_{2,1}, b_{0,2}, b_{1,2}, b_{1,2}`. See [1]_
                      for the use of each coefficient and the associated unit.
    Pnt               AC power consumed by inverter at night (night tare) to
                      maintain circuitry required to sense PV array voltage.
                      [W]
    ===============   ========================================================

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two more notes: Vdcmax is still undefined and b_{1,2} is repeated in ADRCoefficients, presumably should be b_{2,2}?


References
----------
.. [1] Driesse, A. "Beyond the Curves: Modeling the Electrical Efficiency
of Photovoltaic Inverters", 33rd IEEE Photovoltaic Specialist
Conference (PVSC), June 2008

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

p_nom = inverter['Pnom']
v_nom = inverter['Vnom']
pac_max = inverter['Pacmax']
p_nt = inverter['Pnt']
ce_list = inverter['ADRCoefficients']
v_max = inverter['Vmax']
v_min = inverter['Vmin']
vdc_max = inverter['Vdcmax']
mppt_hi = inverter['MPPTHi']
mppt_low = inverter['MPPTLow']

v_lim_upper = float(np.nanmax([v_max, vdc_max, mppt_hi]) * (1 + vtol))
v_lim_lower = float(np.nanmax([v_min, mppt_low]) * (1 - vtol))

pdc = p_dc / p_nom
vdc = v_dc / v_nom
# zero voltage will lead to division by zero, but since power is
# set to night time value later, these errors can be safely ignored
with np.errstate(invalid='ignore', divide='ignore'):
poly = np.array([pdc**0, # replace with np.ones_like?
pdc,
pdc**2,
vdc - 1,
pdc * (vdc - 1),
pdc**2 * (vdc - 1),
1. / vdc - 1, # divide by 0
pdc * (1. / vdc - 1), # invalid 0./0. --> nan
pdc**2 * (1. / vdc - 1)]) # divide by 0
p_loss = np.dot(np.array(ce_list), poly)
ac_power = p_nom * (pdc-p_loss)
p_nt = -1 * np.absolute(p_nt)

# set output to nan where input is outside of limits
# errstate silences case where input is nan
with np.errstate(invalid='ignore'):
invalid = (v_lim_upper < v_dc) | (v_dc < v_lim_lower)
ac_power = np.where(invalid, np.nan, ac_power)

# set night values
ac_power = np.where(vdc == 0, p_nt, ac_power)
ac_power = np.maximum(ac_power, p_nt)

# set max ac output
ac_power = np.minimum(ac_power, pac_max)

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

return ac_power


def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):
r"""
Implements NREL's PVWatts inverter model [1]_.

.. math::

\eta = \frac{\eta_{nom}}{\eta_{ref}} (-0.0162\zeta - \frac{0.0059}
{\zeta} + 0.9858)

.. math::

P_{ac} = \min(\eta P_{dc}, P_{ac0})

where :math:`\zeta=P_{dc}/P_{dc0}` and :math:`P_{dc0}=P_{ac0}/\eta_{nom}`.

Note that ``pdc0`` is also used as a symbol in
:py:func:`pvlib.pvsystem.pvwatts_dc`. ``pdc0`` in this function refers to
the DC power input limit of the inverter. ``pdc0`` in
:py:func:`pvlib.pvsystem.pvwatts_dc` refers to the DC power of the modules
at reference conditions.

Parameters
----------
pdc: numeric
DC power. Same unit as ``pdc0``.
pdc0: numeric
DC input limit of the inverter. Same unit as ``pdc``.
eta_inv_nom: numeric, default 0.96
Nominal inverter efficiency. [unitless]
eta_inv_ref: numeric, default 0.9637
Reference inverter efficiency. PVWatts defines it to be 0.9637
and is included here for flexibility. [unitless]

Returns
-------
pac: numeric
AC power. Same unit as ``pdc0``.

References
----------
.. [1] A. P. Dobos, "PVWatts Version 5 Manual,"
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
(2014).
"""

pac0 = eta_inv_nom * pdc0
zeta = pdc / pdc0

# arrays to help avoid divide by 0 for scalar and array
eta = np.zeros_like(pdc, dtype=float)
pdc_neq_0 = ~np.equal(pdc, 0)

# eta < 0 if zeta < 0.006. pac is forced to be >= 0 below. GH 541
eta = eta_inv_nom / eta_inv_ref * (
-0.0162 * zeta - np.divide(0.0059, zeta, out=eta, where=pdc_neq_0)
+ 0.9858) # noQA: W503

pac = eta * pdc
pac = np.minimum(pac0, pac)
pac = np.maximum(0, pac) # GH 541

return pac
17 changes: 9 additions & 8 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import warnings
import pandas as pd

from pvlib import (atmosphere, clearsky, pvsystem, solarposition, temperature,
tools)
from pvlib import (atmosphere, clearsky, inverter, pvsystem, solarposition,
temperature, tools)
from pvlib.tracking import SingleAxisTracker
import pvlib.irradiance # avoid name conflict with full import
from pvlib.pvsystem import _DC_MODEL_PARAMS
Expand Down Expand Up @@ -56,7 +56,7 @@ def basic_chain(times, latitude, longitude,
See temperature.sapm_cell for details.

inverter_parameters : None, dict or Series
Inverter parameters as defined by the CEC. See pvsystem.snlinverter for
Inverter parameters as defined by the CEC. See inverter.sandia for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using py:func: here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noting the open comment that may have been buried

details.

irradiance : None or DataFrame, default None
Expand Down Expand Up @@ -183,7 +183,7 @@ def basic_chain(times, latitude, longitude,
dc = pvsystem.sapm(effective_irradiance, cell_temperature,
module_parameters)

ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters)
ac = inverter.sandia(dc['v_mp'], dc['p_mp'], inverter_parameters)

return dc, ac

Expand Down Expand Up @@ -265,7 +265,7 @@ class ModelChain(object):
ac_model: None, str, or function, default None
If None, the model will be inferred from the contents of
system.inverter_parameters and system.module_parameters. Valid
strings are 'snlinverter', 'adrinverter', 'pvwatts'. The
strings are 'sandia', 'adr', 'pvwatts'. The
ModelChain instance will be passed as the first argument to a
user-defined function.

Expand Down Expand Up @@ -493,11 +493,12 @@ def ac_model(self, model):
self._ac_model = self.infer_ac_model()
elif isinstance(model, str):
model = model.lower()
if model == 'snlinverter':
# TODO in v0.9: remove 'snlinverter', 'adrinverter', 'pvwatts'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm wrong, but doesn't seem like 'pvwatts' is getting removed.

if model in ['sandia', 'snlinverter']:
self._ac_model = self.snlinverter
elif model == 'adrinverter':
elif model in ['adr', 'adrinverter']:
self._ac_model = self.adrinverter
elif model == 'pvwatts':
elif model in ['pvwatts']:
self._ac_model = self.pvwatts_inverter
else:
raise ValueError(model + ' is not a valid AC power model')
Expand Down
Loading