Skip to content

preconfigured ModelChain for pvwatts, sapm #1022

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 13 commits into from
Aug 25, 2020
2 changes: 2 additions & 0 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ Creating a ModelChain object.
:toctree: generated/

modelchain.ModelChain
modelchain.ModelChain.with_pvwatts
modelchain.ModelChain.with_sapm

Running
-------
Expand Down
5 changes: 5 additions & 0 deletions docs/sphinx/source/whatsnew/v0.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ Enhancements
(:pull:`1017`)
* Add :py:func:`pvlib.inverter.fit_sandia` that fits the Sandia inverter model
to a set of inverter efficiency curves. (:pull:`1011`)
* Add factory methods :py:meth:`~pvlib.modelchain.ModelChain.with_pvwatts`
:py:meth:`~pvlib.modelchain.ModelChain.with_sapm` to create ``ModelChain``
objects configured for the respective modeling paradigms. The
configurations are defined in ``modelchain.PVWATTS_CONFIG``, and
``modelchain.SAPM_CONFIG``. (:issue:`1013`, :pull:`1022`)
* Added *racking_model*, *module_type*, and *temperature_model_parameters* to
PVSystem, LocalizedPVSystem, SingleAxisTracker, and
LocalizedSingleAxisTracker repr methods. (:issue:`1027`)
Expand Down
189 changes: 189 additions & 0 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@
from pvlib._deprecation import pvlibDeprecationWarning
from pvlib.tools import _build_kwargs


# these dictionaries contain the default configuration for following
# established modeling sequences. They can be used in combination with
# basic_chain and ModelChain. They are used by the ModelChain methods
# ModelChain.with_pvwatts, ModelChain.with_sapm, etc.

# pvwatts documentation states that it uses the following reference for
# a temperature model: Fuentes, M. K. (1987). A Simplified Thermal Model
# for Flat-Plate Photovoltaic Arrays. SAND85-0330. Albuquerque, NM:
# Sandia National Laboratories. Accessed September 3, 2013:
# http://prod.sandia.gov/techlib/access-control.cgi/1985/850330.pdf
# pvlib python does not implement that model, so use the SAPM instead.
PVWATTS_CONFIG = dict(
dc_model='pvwatts', ac_model='pvwatts', losses_model='pvwatts',
transposition_model='perez', aoi_model='physical',
spectral_model='no_loss', temperature_model='sapm'
)

SAPM_CONFIG = dict(
dc_model='sapm', ac_model='sandia', losses_model='no_loss',
aoi_model='sapm', spectral_model='sapm', temperature_model='sapm'
)


def basic_chain(times, latitude, longitude,
module_parameters, temperature_model_parameters,
inverter_parameters,
Expand Down Expand Up @@ -349,6 +373,171 @@ def __init__(self, system, location,
self.times = None
self.solar_position = None

@classmethod
def with_pvwatts(cls, system, location,
orientation_strategy=None,
clearsky_model='ineichen',
airmass_model='kastenyoung1989',
name=None,
**kwargs):
"""
ModelChain that follows the PVWatts methods.

Parameters
----------
system : PVSystem
A :py:class:`~pvlib.pvsystem.PVSystem` object that represents
the connected set of modules, inverters, etc.

location : Location
A :py:class:`~pvlib.location.Location` object that represents
the physical location at which to evaluate the model.

orientation_strategy : None or str, default None
The strategy for aligning the modules. If not None, sets the
``surface_azimuth`` and ``surface_tilt`` properties of the
``system``. Allowed strategies include 'flat',
'south_at_latitude_tilt'. Ignored for SingleAxisTracker systems.

clearsky_model : str, default 'ineichen'
Passed to location.get_clearsky.

airmass_model : str, default 'kastenyoung1989'
Passed to location.get_airmass.

name: None or str, default None
Name of ModelChain instance.

**kwargs
Parameters supplied here are passed to the ModelChain
constructor and take precedence over the default
configuration.

Examples
--------
>>> module_parameters = dict(gamma_pdc=-0.003, pdc0=4500)
>>> inverter_parameters = dict(pac0=4000)
>>> tparams = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
>>> system = PVSystem(surface_tilt=30, surface_azimuth=180,
... module_parameters=module_parameters,
... inverter_parameters=inverter_parameters,
... temperature_model_parameters=tparams)
>>> location = Location(32.2, -110.9)
>>> ModelChain.with_pvwatts(system, location)
ModelChain:
name: None
orientation_strategy: None
clearsky_model: ineichen
transposition_model: perez
solar_position_method: nrel_numpy
airmass_model: kastenyoung1989
dc_model: pvwatts_dc
ac_model: pvwatts_inverter
aoi_model: physical_aoi_loss
spectral_model: no_spectral_loss
temperature_model: sapm_temp
losses_model: pvwatts_losses
""" # noqa: E501
config = PVWATTS_CONFIG.copy()
config.update(kwargs)
return ModelChain(
system, location,
orientation_strategy=orientation_strategy,
clearsky_model=clearsky_model,
airmass_model=airmass_model,
name=name,
**config
)

@classmethod
def with_sapm(cls, system, location,
orientation_strategy=None,
clearsky_model='ineichen',
transposition_model='haydavies',
solar_position_method='nrel_numpy',
airmass_model='kastenyoung1989',
name=None,
**kwargs):
"""
ModelChain that follows the Sandia Array Performance Model
(SAPM) methods.

Parameters
----------
system : PVSystem
A :py:class:`~pvlib.pvsystem.PVSystem` object that represents
the connected set of modules, inverters, etc.

location : Location
A :py:class:`~pvlib.location.Location` object that represents
the physical location at which to evaluate the model.

orientation_strategy : None or str, default None
The strategy for aligning the modules. If not None, sets the
``surface_azimuth`` and ``surface_tilt`` properties of the
``system``. Allowed strategies include 'flat',
'south_at_latitude_tilt'. Ignored for SingleAxisTracker systems.

clearsky_model : str, default 'ineichen'
Passed to location.get_clearsky.

transposition_model : str, default 'haydavies'
Passed to system.get_irradiance.

solar_position_method : str, default 'nrel_numpy'
Passed to location.get_solarposition.

airmass_model : str, default 'kastenyoung1989'
Passed to location.get_airmass.

name: None or str, default None
Name of ModelChain instance.

**kwargs
Parameters supplied here are passed to the ModelChain
constructor and take precedence over the default
configuration.

Examples
--------
>>> mods = pvlib.pvsystem.retrieve_sam('sandiamod')
>>> invs = pvlib.pvsystem.retrieve_sam('cecinverter')
>>> module_parameters = mods['Canadian_Solar_CS5P_220M___2009_']
>>> inverter_parameters = invs['ABB__MICRO_0_25_I_OUTD_US_240__240V_']
>>> tparams = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
>>> system = PVSystem(surface_tilt=30, surface_azimuth=180,
... module_parameters=module_parameters,
... inverter_parameters=inverter_parameters,
... temperature_model_parameters=tparams)
>>> location = Location(32.2, -110.9)
>>> ModelChain.with_sapm(system, location)
ModelChain:
name: None
orientation_strategy: None
clearsky_model: ineichen
transposition_model: haydavies
solar_position_method: nrel_numpy
airmass_model: kastenyoung1989
dc_model: sapm
ac_model: snlinverter
aoi_model: sapm_aoi_loss
spectral_model: sapm_spectral_loss
temperature_model: sapm_temp
losses_model: no_extra_losses
""" # noqa: E501
config = SAPM_CONFIG.copy()
config.update(kwargs)
return ModelChain(
system, location,
orientation_strategy=orientation_strategy,
clearsky_model=clearsky_model,
transposition_model=transposition_model,
solar_position_method=solar_position_method,
airmass_model=airmass_model,
name=name,
**config
)

def __repr__(self):
attrs = [
'name', 'orientation_strategy', 'clearsky_model',
Expand Down
13 changes: 13 additions & 0 deletions pvlib/tests/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@ def test_ModelChain_creation(sapm_dc_snl_ac_system, location):
ModelChain(sapm_dc_snl_ac_system, location)


def test_with_sapm(sapm_dc_snl_ac_system, location, weather):
mc = ModelChain.with_sapm(sapm_dc_snl_ac_system, location)
assert mc.dc_model == mc.sapm
mc.run_model(weather)


def test_with_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather):
mc = ModelChain.with_pvwatts(pvwatts_dc_pvwatts_ac_system, location)
assert mc.dc_model == mc.pvwatts_dc
assert mc.temperature_model == mc.sapm_temp
mc.run_model(weather)


@pytest.mark.parametrize('strategy, expected', [
(None, (32.2, 180)), ('None', (32.2, 180)), ('flat', (0, 180)),
('south_at_latitude_tilt', (32.2, 180))
Expand Down