Skip to content

Commit 0b8f24c

Browse files
authored
refactor ModelChain inverter methods to use PVSystem.get_ac (#1150)
* deprecate methods * remove new but unreleased methods * refactor modelchain inverter methods for get_ac * update whats new * update api.rst * remove tests for methods that no longer exist * fix get_ac with sandia tests * fix get_ac with sandia tests * clean up pvsystem imports * add test of infer_ac_model * no v in deprecated args * fix pull number.... * remove _infer_ac_model_multi
1 parent 750b1fa commit 0b8f24c

File tree

6 files changed

+83
-179
lines changed

6 files changed

+83
-179
lines changed

docs/sphinx/source/api.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,8 @@ ModelChain model definitions.
630630
modelchain.ModelChain.desoto
631631
modelchain.ModelChain.pvsyst
632632
modelchain.ModelChain.pvwatts_dc
633-
modelchain.ModelChain.snlinverter
634-
modelchain.ModelChain.adrinverter
633+
modelchain.ModelChain.sandia_inverter
634+
modelchain.ModelChain.adr_inverter
635635
modelchain.ModelChain.pvwatts_inverter
636636
modelchain.ModelChain.ashrae_aoi_loss
637637
modelchain.ModelChain.physical_aoi_loss

docs/sphinx/source/whatsnew/v0.9.0.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Breaking changes
3636
* ``irradiance.liujordan`` and ``ForecastModel.cloud_cover_to_irradiance_liujordan``
3737
have been removed. (:pull:`1136`)
3838

39+
* ``ModelChain.snlinverter`` changed to ``ModelChain.sandia_inverter``.
40+
``ModelChain.adrinverter`` changed to ``ModelChain.adr_inverter``.
41+
(:pull:`1150`)
42+
3943

4044
Deprecations
4145
~~~~~~~~~~~~
@@ -76,9 +80,9 @@ Enhancements
7680
* Support for :py:func:`~pvlib.inverter.sandia_multi` and
7781
:py:func:`~pvlib.inverter.pvwatts_multi` added to
7882
:py:class:`~pvlib.pvsystem.PVSystem` and
79-
:py:class:`~pvlib.modelchain.ModelChain` (as ``ac_model='sandia_multi'``
80-
and ``ac_model='pvwatts_multi'``).
81-
(:pull:`1076`, :issue:`1067`, :pull:`1132`, :issue:`1117`)
83+
:py:class:`~pvlib.modelchain.ModelChain` (as ``ac_model='sandia'``
84+
and ``ac_model='pvwatts'``).
85+
(:pull:`1076`, :issue:`1067`, :pull:`1132`, :issue:`1117`, :pull:`1150`)
8286
* :py:class:`~pvlib.modelchain.ModelChain` 'run_model' methods now
8387
automatically switch to using ``'effective_irradiance'`` (if available) for
8488
cell temperature models, when ``'poa_global'`` is not provided in input
@@ -88,8 +92,8 @@ Enhancements
8892
``pvsystem.PVSystem.strings_per_inverter``. Note that both attributes still
8993
default to 1. (:pull:`1138`)
9094
* :py:meth:`~pvlib.pvsystem.PVSystem.get_ac` is added to calculate AC power
91-
from DC power. Use parameter 'model' to specify which inverter model to use.
92-
(:pull:`1147`, :issue:`998`)
95+
from DC power. Use parameter ``model`` to specify which inverter model to use.
96+
(:pull:`1147`, :issue:`998`, :pull:`1150`)
9397

9498
Bug fixes
9599
~~~~~~~~~

pvlib/modelchain.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -770,15 +770,11 @@ def ac_model(self, model):
770770
elif isinstance(model, str):
771771
model = model.lower()
772772
if model == 'sandia':
773-
self._ac_model = self.snlinverter
774-
elif model == 'sandia_multi':
775-
self._ac_model = self.sandia_multi_inverter
773+
self._ac_model = self.sandia_inverter
776774
elif model in 'adr':
777-
self._ac_model = self.adrinverter
775+
self._ac_model = self.adr_inverter
778776
elif model == 'pvwatts':
779777
self._ac_model = self.pvwatts_inverter
780-
elif model == 'pvwatts_multi':
781-
self._ac_model = self.pvwatts_multi_inverter
782778
else:
783779
raise ValueError(model + ' is not a valid AC power model')
784780
else:
@@ -787,53 +783,41 @@ def ac_model(self, model):
787783
def infer_ac_model(self):
788784
"""Infer AC power model from system attributes."""
789785
inverter_params = set(self.system.inverter_parameters.keys())
790-
if self.system.num_arrays > 1:
791-
return self._infer_ac_model_multi(inverter_params)
792786
if _snl_params(inverter_params):
793-
return self.snlinverter
787+
return self.sandia_inverter
794788
if _adr_params(inverter_params):
795-
return self.adrinverter
789+
if self.system.num_arrays > 1:
790+
raise ValueError(
791+
'The adr inverter function cannot be used for an inverter',
792+
' with multiple MPPT inputs')
793+
else:
794+
return self.adr_inverter
796795
if _pvwatts_params(inverter_params):
797796
return self.pvwatts_inverter
798797
raise ValueError('could not infer AC model from '
799798
'system.inverter_parameters. Check '
800799
'system.inverter_parameters or explicitly '
801800
'set the model with the ac_model kwarg.')
802801

803-
def _infer_ac_model_multi(self, inverter_params):
804-
if _snl_params(inverter_params):
805-
return self.sandia_multi_inverter
806-
elif _pvwatts_params(inverter_params):
807-
return self.pvwatts_multi_inverter
808-
raise ValueError('could not infer multi-array AC model from '
809-
'system.inverter_parameters. Only sandia and pvwatts '
810-
'inverter models support multiple '
811-
'Arrays. Check system.inverter_parameters or '
812-
'explicitly set the model with the ac_model kwarg.')
813-
814-
def sandia_multi_inverter(self):
815-
self.results.ac = self.system.sandia_multi(
816-
_tuple_from_dfs(self.results.dc, 'v_mp'),
817-
_tuple_from_dfs(self.results.dc, 'p_mp')
802+
def sandia_inverter(self):
803+
self.results.ac = self.system.get_ac(
804+
'sandia',
805+
_tuple_from_dfs(self.results.dc, 'p_mp'),
806+
v_dc=_tuple_from_dfs(self.results.dc, 'v_mp')
818807
)
819808
return self
820809

821-
def pvwatts_multi_inverter(self):
822-
self.results.ac = self.system.pvwatts_multi(self.results.dc)
823-
return self
824-
825-
def snlinverter(self):
826-
self.results.ac = self.system.snlinverter(self.results.dc['v_mp'],
827-
self.results.dc['p_mp'])
828-
return self
829-
830-
def adrinverter(self):
831-
self.results.ac = self.system.adrinverter(self.results.dc['v_mp'],
832-
self.results.dc['p_mp'])
810+
def adr_inverter(self):
811+
self.results.ac = self.system.get_ac(
812+
'adr',
813+
self.results.dc['p_mp'],
814+
v_dc=self.results.dc['v_mp']
815+
)
833816
return self
834817

835818
def pvwatts_inverter(self):
836-
self.results.ac = self.system.pvwatts_ac(self.results.dc).fillna(0)
819+
ac = self.system.get_ac('pvwatts', self.results.dc)
820+
self.results.ac = ac.fillna(0)
837821
return self
838822

839823
@property

pvlib/pvsystem.py

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import io
99
import os
1010
from urllib.request import urlopen
11-
import warnings
1211
import numpy as np
1312
import pandas as pd
1413

@@ -17,7 +16,6 @@
1716
from pvlib import (atmosphere, iam, inverter, irradiance,
1817
singlediode as _singlediode, temperature)
1918
from pvlib.tools import _build_kwargs
20-
from pvlib._deprecation import pvlibDeprecationWarning
2119

2220

2321
# a dict of required parameter names for each DC power model
@@ -921,6 +919,7 @@ def get_ac(self, model, p_dc, v_dc=None):
921919
model + ' is not a valid AC power model.',
922920
' model must be one of "sandia", "adr" or "pvwatts"')
923921

922+
@deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
924923
def snlinverter(self, v_dc, p_dc):
925924
"""Uses :py:func:`pvlib.inverter.sandia` to calculate AC power based on
926925
``self.inverter_parameters`` and the input voltage and power.
@@ -929,19 +928,7 @@ def snlinverter(self, v_dc, p_dc):
929928
"""
930929
return inverter.sandia(v_dc, p_dc, self.inverter_parameters)
931930

932-
def sandia_multi(self, v_dc, p_dc):
933-
"""Uses :py:func:`pvlib.inverter.sandia_multi` to calculate AC power
934-
based on ``self.inverter_parameters`` and the input voltage and power.
935-
936-
The parameters `v_dc` and `p_dc` must be tuples with length equal to
937-
``self.num_arrays`` if the system has more than one array.
938-
939-
See :py:func:`pvlib.inverter.sandia_multi` for details.
940-
"""
941-
v_dc = self._validate_per_array(v_dc)
942-
p_dc = self._validate_per_array(p_dc)
943-
return inverter.sandia_multi(v_dc, p_dc, self.inverter_parameters)
944-
931+
@deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
945932
def adrinverter(self, v_dc, p_dc):
946933
"""Uses :py:func:`pvlib.inverter.adr` to calculate AC power based on
947934
``self.inverter_parameters`` and the input voltage and power.
@@ -1009,6 +996,7 @@ def pvwatts_losses(self):
1009996
self.losses_parameters)
1010997
return pvwatts_losses(**kwargs)
1011998

999+
@deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
10121000
def pvwatts_ac(self, pdc):
10131001
"""
10141002
Calculates AC power according to the PVWatts model using
@@ -1023,20 +1011,6 @@ def pvwatts_ac(self, pdc):
10231011
return inverter.pvwatts(pdc, self.inverter_parameters['pdc0'],
10241012
**kwargs)
10251013

1026-
def pvwatts_multi(self, p_dc):
1027-
"""Uses :py:func:`pvlib.inverter.pvwatts_multi` to calculate AC power
1028-
based on ``self.inverter_parameters`` and the input voltage and power.
1029-
1030-
The parameter `p_dc` must be a tuple with length equal to
1031-
``self.num_arrays`` if the system has more than one array.
1032-
1033-
See :py:func:`pvlib.inverter.pvwatts_multi` for details.
1034-
"""
1035-
p_dc = self._validate_per_array(p_dc)
1036-
kwargs = _build_kwargs(['eta_inv_nom', 'eta_inv_ref'],
1037-
self.inverter_parameters)
1038-
return inverter.pvwatts_multi(p_dc, self.inverter_parameters['pdc0'],
1039-
**kwargs)
10401014
@property
10411015
@_unwrap_single_value
10421016
def module_parameters(self):

pvlib/tests/test_modelchain.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import numpy as np
44
import pandas as pd
55

6-
from pvlib import iam, modelchain, pvsystem, temperature
6+
from pvlib import iam, modelchain, pvsystem, temperature, inverter
77
from pvlib.modelchain import ModelChain
88
from pvlib.pvsystem import PVSystem
99
from pvlib.tracking import SingleAxisTracker
@@ -480,7 +480,7 @@ def test_ModelChain_invalid_inverter_params_arrays(
480480
sapm_dc_snl_ac_system_same_arrays.inverter_parameters = \
481481
inverter_params[inverter]
482482
with pytest.raises(ValueError,
483-
match=r'Only sandia and pvwatts inverter models'):
483+
match=r'adr inverter function cannot'):
484484
ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
485485

486486

@@ -1235,27 +1235,36 @@ def acdc(mc):
12351235
mc.results.ac = mc.results.dc
12361236

12371237

1238-
@pytest.mark.parametrize('ac_model', ['sandia', 'adr',
1239-
'pvwatts', 'sandia_multi',
1240-
'pvwatts_multi'])
1238+
@pytest.mark.parametrize('inverter_model', ['sandia', 'adr',
1239+
'pvwatts', 'sandia_multi',
1240+
'pvwatts_multi'])
12411241
def test_ac_models(sapm_dc_snl_ac_system, cec_dc_adr_ac_system,
1242-
pvwatts_dc_pvwatts_ac_system, location, ac_model,
1243-
weather, mocker):
1242+
pvwatts_dc_pvwatts_ac_system, cec_dc_snl_ac_arrays,
1243+
pvwatts_dc_pvwatts_ac_system_arrays,
1244+
location, inverter_model, weather, mocker):
12441245
ac_systems = {'sandia': sapm_dc_snl_ac_system,
1245-
'sandia_multi': sapm_dc_snl_ac_system,
1246+
'sandia_multi': cec_dc_snl_ac_arrays,
12461247
'adr': cec_dc_adr_ac_system,
12471248
'pvwatts': pvwatts_dc_pvwatts_ac_system,
1248-
'pvwatts_multi': pvwatts_dc_pvwatts_ac_system}
1249-
ac_method_name = {'sandia': 'snlinverter',
1250-
'sandia_multi': 'sandia_multi',
1251-
'adr': 'adrinverter',
1252-
'pvwatts': 'pvwatts_ac',
1253-
'pvwatts_multi': 'pvwatts_multi'}
1254-
system = ac_systems[ac_model]
1255-
1249+
'pvwatts_multi': pvwatts_dc_pvwatts_ac_system_arrays}
1250+
inverter_to_ac_model = {
1251+
'sandia': 'sandia',
1252+
'sandia_multi': 'sandia',
1253+
'adr': 'adr',
1254+
'pvwatts': 'pvwatts',
1255+
'pvwatts_multi': 'pvwatts'}
1256+
ac_model = inverter_to_ac_model[inverter_model]
1257+
system = ac_systems[inverter_model]
1258+
1259+
mc_inferred = ModelChain(system, location,
1260+
aoi_model='no_loss', spectral_model='no_loss')
12561261
mc = ModelChain(system, location, ac_model=ac_model,
12571262
aoi_model='no_loss', spectral_model='no_loss')
1258-
m = mocker.spy(system, ac_method_name[ac_model])
1263+
1264+
# tests ModelChain.infer_ac_model
1265+
assert mc_inferred.ac_model.__name__ == mc.ac_model.__name__
1266+
1267+
m = mocker.spy(inverter, inverter_model)
12591268
mc.run_model(weather)
12601269
assert m.call_count == 1
12611270
assert isinstance(mc.results.ac, pd.Series)
@@ -1447,7 +1456,7 @@ def test_losses_models_no_loss(pvwatts_dc_pvwatts_ac_system, location, weather,
14471456

14481457
def test_invalid_dc_model_params(sapm_dc_snl_ac_system, cec_dc_snl_ac_system,
14491458
pvwatts_dc_pvwatts_ac_system, location):
1450-
kwargs = {'dc_model': 'sapm', 'ac_model': 'snlinverter',
1459+
kwargs = {'dc_model': 'sapm', 'ac_model': 'sandia',
14511460
'aoi_model': 'no_loss', 'spectral_model': 'no_loss',
14521461
'temperature_model': 'sapm', 'losses_model': 'no_loss'}
14531462
sapm_dc_snl_ac_system.module_parameters.pop('A0') # remove a parameter
@@ -1488,9 +1497,9 @@ def test_bad_get_orientation():
14881497
def test_with_sapm_pvsystem_arrays(sapm_dc_snl_ac_system_Array, location,
14891498
weather):
14901499
mc = ModelChain.with_sapm(sapm_dc_snl_ac_system_Array, location,
1491-
ac_model='sandia_multi')
1500+
ac_model='sandia')
14921501
assert mc.dc_model == mc.sapm
1493-
assert mc.ac_model == mc.sandia_multi_inverter
1502+
assert mc.ac_model == mc.sandia_inverter
14941503
mc.run_model(weather)
14951504
assert mc.results
14961505

@@ -1625,7 +1634,7 @@ def test_ModelChain___repr__(sapm_dc_snl_ac_system, location, strategy,
16251634
' solar_position_method: nrel_numpy',
16261635
' airmass_model: kastenyoung1989',
16271636
' dc_model: sapm',
1628-
' ac_model: snlinverter',
1637+
' ac_model: sandia_inverter',
16291638
' aoi_model: sapm_aoi_loss',
16301639
' spectral_model: sapm_spectral_loss',
16311640
' temperature_model: sapm_temp',
@@ -1778,7 +1787,7 @@ def test_inconsistent_array_params(location,
17781787
)
17791788
with pytest.raises(ValueError, match=temperature_error):
17801789
ModelChain(different_temp_system, location,
1781-
ac_model='sandia_multi',
1790+
ac_model='sandia',
17821791
aoi_model='no_loss', spectral_model='no_loss',
17831792
temperature_model='sapm')
17841793

0 commit comments

Comments
 (0)