Skip to content
Open
Changes from 2 commits
Commits
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
54 changes: 52 additions & 2 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -2878,14 +2878,36 @@ def scale_voltage_current_power(data, voltage=1, current=1):

@renamed_kwarg_warning(
"0.13.0", "g_poa_effective", "effective_irradiance")
def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.,
k=0.0, capped_adjustment=False):
r"""
Implements NREL's PVWatts DC power model. The PVWatts DC model [1]_ is:
Implements NREL's PVWatts (Version 5) DC power model. The PVWatts Version
5 DC model [1]_ is:

.. math::

P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref}))

This model has also been referred to as the power temperature coefficient
model.

This function accepts an optional irradiance adjustment factor, `k`, based
on based on [2]_. This applies a piece-wise adjustment to power based on
irradiance, where `k` is the reduction in actual power at 200 W/m^2
relative to ideal power calculated linearly from standard test conditions,
normalized to nameplate power at standard test conditions.

.. math::

k=\frac{0.2P_{dc0}-P_{200}}{P_{dc0}}

For example, a 500 W module that produces 95 W at 200 W/m^2 (a 5% relative
reduction in efficiency) would have a value of `k` = 0.01.

Copy link
Member

Choose a reason for hiding this comment

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

Please include Equation from above with k, I'd place it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See my latest change. Is that what you had in mind?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that comment was not clear. I was thinking we should copy the equation for P_DC here, and show how k modifies the power. But the equation for the adjustment is not simple (the piecewise part), so I retract that thought. If someone wants to know how the adjustment works, we've provided code and the reference.

This adjustment increases relative efficiency for irradiance above 1000
W/m^2, which may not be desired. An optional input, `capped_adjustment`,
modifies the adjustment from [2]_ to only apply below 1000 W/m^2.

Note that ``pdc0`` is also used as a symbol in
:py:func:`pvlib.inverter.pvwatts`. ``pdc0`` in this function refers to the DC
power of the modules at reference conditions. ``pdc0`` in
Expand All @@ -2909,6 +2931,11 @@ def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
temp_ref: numeric, default 25.0
Cell reference temperature. PVWatts defines it to be 25 C and
is included here for flexibility. [C]
k: numeric, default 0.005
Irradiance correction factor, defined in [2]_. [unitless]
modified_marion: Boolean, default False
Optional modification to [2]_, where no adjustment is applied above
1000 W/m^2.

Returns
-------
Expand All @@ -2920,11 +2947,34 @@ def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
.. [1] A. P. Dobos, "PVWatts Version 5 Manual"
http://pvwatts.nrel.gov/downloads/pvwattsv5.pdf
(2014).
.. [2] B. Marion, "Comparison of Predictive Models for
Photovoltaic Module Performance,"
https://doi.org/10.1109/PVSC.2008.4922586,
https://docs.nrel.gov/docs/fy08osti/42511.pdf
(2008).
""" # noqa: E501

pdc = (effective_irradiance * 0.001 * pdc0 *
(1 + gamma_pdc * (temp_cell - temp_ref)))

# apply Marion's correction if k is anything but zero
if k != 0:
err_1 = (k * (1 - (1 - effective_irradiance / 200)**4) /
(effective_irradiance / 1000))
err_2 = (k * (1000 - effective_irradiance) / (1000 - 200))

pdc_marion = np.where(effective_irradiance <= 200,
Copy link
Member

Choose a reason for hiding this comment

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

np.where will break the paradigm of "return the same object type that was input" since it always returns an array. Options:

  1. keep np.where, cast output to match input
  2. switch to slicing, assume array input
  3. switch to slicing, promote scalars to arrays for compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see what you mean. I tried a few things, but can’t seem to figure out how to get your proposed solutions to work. Any pointers or examples?

pdc * (1 - err_1),
pdc * (1 - err_2))

# "cap" Marion's correction at 1000 W/m^2
if capped_adjustment is True:
pdc_marion = np.where(effective_irradiance >= 1000,
pdc,
pdc_marion)

pdc = pdc_marion

return pdc


Expand Down