Skip to content

Add scale_voltage_current_power to ModelChain.pvwatts_dc #1138

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 10 commits into from
Jan 21, 2021
4 changes: 4 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ Enhancements
automatically switch to using ``'effective_irradiance'`` (if available) for
cell temperature models, when ``'poa_global'`` is not provided in input
weather or calculated from input weather data.
* :py:meth:`~pvlib.modelchain.ModelChain.pvwatts_dc` now scales the DC power
by ``pvsystem.PVSystem.modules_per_strings`` and
``pvsystem.PVSystem.strings_per_inverter``. Note that both attributes still
default to 1. (:pull:`1138`)

Bug fixes
~~~~~~~~~
Expand Down
25 changes: 25 additions & 0 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,33 @@ def pvsyst(self):
return self._singlediode(self.system.calcparams_pvsyst)

def pvwatts_dc(self):
"""Calculate DC power using the PVWatts model.

Results are stored in ModelChain.results.dc. DC power is computed
from PVSystem.module_parameters['pdc0'] and then scaled by
PVSystem.modules_per_string and PVSystem.strings_per_inverter.

Returns
-------
self

See also
--------
pvlib.pvsystem.PVSystem.pvwatts_dc
pvlib.pvsystem.PVSystem.scale_voltage_current_power
"""
self.results.dc = self.system.pvwatts_dc(
self.results.effective_irradiance, self.results.cell_temperature)
if isinstance(self.results.dc, tuple):
temp = tuple(
pd.DataFrame(s, columns=['p_mp']) for s in self.results.dc)
else:
temp = pd.DataFrame(self.results.dc, columns=['p_mp'])
scaled = self.system.scale_voltage_current_power(temp)
if isinstance(scaled, tuple):
self.results.dc = tuple(s['p_mp'] for s in scaled)
else:
self.results.dc = scaled['p_mp']
return self

@property
Expand Down
39 changes: 20 additions & 19 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ def scale_voltage_current_power(self, data):
Parameters
----------
data: DataFrame or tuple of DataFrame
Must contain columns `'v_mp', 'v_oc', 'i_mp' ,'i_x', 'i_xx',
May contain columns `'v_mp', 'v_oc', 'i_mp' ,'i_x', 'i_xx',
'i_sc', 'p_mp'`.

Returns
Expand Down Expand Up @@ -2626,13 +2626,13 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,

def scale_voltage_current_power(data, voltage=1, current=1):
"""
Scales the voltage, current, and power of the DataFrames
returned by :py:func:`singlediode` and :py:func:`sapm`.
Scales the voltage, current, and power in data by the voltage
and current factors.

Parameters
----------
data: DataFrame
Must contain columns `'v_mp', 'v_oc', 'i_mp' ,'i_x', 'i_xx',
May contain columns `'v_mp', 'v_oc', 'i_mp' ,'i_x', 'i_xx',
'i_sc', 'p_mp'`.
voltage: numeric, default 1
The amount by which to multiply the voltages.
Expand All @@ -2648,14 +2648,15 @@ def scale_voltage_current_power(data, voltage=1, current=1):

# as written, only works with a DataFrame
# could make it work with a dict, but it would be more verbose
data = data.copy()
voltages = ['v_mp', 'v_oc']
currents = ['i_mp', 'i_x', 'i_xx', 'i_sc']
data[voltages] *= voltage
data[currents] *= current
data['p_mp'] *= voltage * current

return data
voltage_keys = ['v_mp', 'v_oc']
current_keys = ['i_mp', 'i_x', 'i_xx', 'i_sc']
power_keys = ['p_mp']
voltage_df = data.filter(voltage_keys, axis=1) * voltage
current_df = data.filter(current_keys, axis=1) * current
power_df = data.filter(power_keys, axis=1) * voltage * current
df = pd.concat([voltage_df, current_df, power_df], axis=1)
df_sorted = df[data.columns] # retain original column order
return df_sorted


def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
Expand All @@ -2675,20 +2676,20 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
Parameters
----------
g_poa_effective: numeric
Irradiance transmitted to the PV cells in units of W/m**2. To be
Irradiance transmitted to the PV cells. To be
fully consistent with PVWatts, the user must have already
applied angle of incidence losses, but not soiling, spectral,
etc.
etc. [W/m^2]
temp_cell: numeric
Cell temperature in degrees C.
Cell temperature [C].
pdc0: numeric
Power of the modules at 1000 W/m2 and cell reference temperature.
Power of the modules at 1000 W/m^2 and cell reference temperature. [W]
gamma_pdc: numeric
The temperature coefficient in units of 1/C. Typically -0.002 to
-0.005 per degree C.
The temperature coefficient of power. Typically -0.002 to
-0.005 per degree C. [1/C]
temp_ref: numeric, default 25.0
Cell reference temperature. PVWatts defines it to be 25 C and
is included here for flexibility.
is included here for flexibility. [C]

Returns
-------
Expand Down
19 changes: 19 additions & 0 deletions pvlib/tests/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,25 @@ def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather,
assert not mc.results.ac.empty


def test_pvwatts_dc_multiple_strings(pvwatts_dc_pvwatts_ac_system, location,
weather, mocker):
system = pvwatts_dc_pvwatts_ac_system
m = mocker.spy(system, 'scale_voltage_current_power')
mc1 = ModelChain(system, location,
aoi_model='no_loss', spectral_model='no_loss')
mc1.run_model(weather)
assert m.call_count == 1
system.arrays[0].modules_per_string = 2
mc2 = ModelChain(system, location,
aoi_model='no_loss', spectral_model='no_loss')
mc2.run_model(weather)
assert isinstance(mc2.results.ac, (pd.Series, pd.DataFrame))
assert not mc2.results.ac.empty
expected = pd.Series(data=[2., np.nan], index=mc2.results.dc.index,
name='p_mp')
assert_series_equal(mc2.results.dc / mc1.results.dc, expected)


def acdc(mc):
mc.results.ac = mc.results.dc

Expand Down