Skip to content

refactor ModelChain. add SingleDiode modelchain #179

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

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions docs/sphinx/source/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ What's New

These are new features and improvements of note in each release.

.. include:: whatsnew/v0.4.0.txt
.. include:: whatsnew/v0.3.3.txt
.. include:: whatsnew/v0.3.2.txt
.. include:: whatsnew/v0.3.1.txt
Expand Down
23 changes: 23 additions & 0 deletions docs/sphinx/source/whatsnew/v0.4.0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.. _whatsnew_0400:

v0.4.0 (June xx, 2016)
-----------------------

This is a major release from 0.3.3.
We recommend that all users upgrade to this version.

Enhancements
~~~~~~~~~~~~

* Splits ``ModelChain`` into a ``SingleDiode`` chain and a ``SAPM`` chain.
(:issue:`151`)


Bug fixes
~~~~~~~~~


Contributors
~~~~~~~~~~~~

* Will Holmgren
129 changes: 118 additions & 11 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,9 @@ def get_orientation(strategy, **kwargs):

class ModelChain(object):
"""
An experimental class that represents all of the modeling steps
An experimental base class that represents all of the modeling steps
necessary for calculating power or energy for a PV system at a given
location using the SAPM.

CEC module specifications and the single diode model are not yet
supported.
location.

Parameters
----------
Expand Down Expand Up @@ -284,9 +281,10 @@ def orientation_strategy(self, strategy):

self._orientation_strategy = strategy

def run_model(self, times, irradiance=None, weather=None):
def prepare_inputs(self, times, irradiance=None, weather=None):
"""
Run the model.
Prepare the solar position, irradiance, and weather inputs to
the model.

Parameters
----------
Expand All @@ -307,15 +305,19 @@ def run_model(self, times, irradiance=None, weather=None):
self

Assigns attributes: times, solar_position, airmass, irradiance,
total_irrad, weather, temps, aoi, dc, ac
total_irrad, weather, aoi
"""

self.times = times

self.solar_position = self.location.get_solarposition(self.times)

self.airmass = self.location.get_airmass(
solar_position=self.solar_position, model=self.airmass_model)

self.aoi = self.system.get_aoi(self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])

if irradiance is None:
irradiance = self.location.get_clearsky(
self.solar_position.index, self.clearsky_model,
Expand Down Expand Up @@ -358,13 +360,53 @@ def run_model(self, times, irradiance=None, weather=None):
weather = {'wind_speed': 0, 'temp_air': 20}
self.weather = weather

return self

def run_model(self):
"""
A stub function meant to be subclassed.
"""

raise NotImplementedError(
'you must subclass ModelChain and implement this method')


class SAPM(ModelChain):
"""
Uses the SAPM to calculate cell temperature, DC power and AC power.
"""

def run_model(self, times, irradiance=None, weather=None):
"""
Run the model.

Parameters
----------
times : DatetimeIndex
Times at which to evaluate the model.

irradiance : None or DataFrame
If None, calculates clear sky data.
Columns must be 'dni', 'ghi', 'dhi'.

weather : None or DataFrame
If None, assumes air temperature is 20 C and
wind speed is 0 m/s.
Columns must be 'wind_speed', 'temp_air'.

Returns
-------
self

Assigns attributes: times, solar_position, airmass, irradiance,
total_irrad, weather, aoi, temps, dc, ac.
"""
self.prepare_inputs(times, irradiance, weather)

self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'],
self.weather['wind_speed'],
self.weather['temp_air'])

self.aoi = self.system.get_aoi(self.solar_position['apparent_zenith'],
self.solar_position['azimuth'])

self.dc = self.system.sapm(self.total_irrad['poa_direct'],
self.total_irrad['poa_diffuse'],
self.temps['temp_cell'],
Expand All @@ -376,3 +418,68 @@ def run_model(self, times, irradiance=None, weather=None):
self.ac = self.system.snlinverter(self.dc['v_mp'], self.dc['p_mp'])

return self


class SingleDiode(ModelChain):
"""
Uses the DeSoto and single diode models to calculate the DC power,
and the SAPM models to calculate cell temperature and AC power.
"""

def run_model(self, times, irradiance=None, weather=None):
"""
Run the model.

Parameters
----------
times : DatetimeIndex
Times at which to evaluate the model.

irradiance : None or DataFrame
If None, calculates clear sky data.
Columns must be 'dni', 'ghi', 'dhi'.

weather : None or DataFrame
If None, assumes air temperature is 20 C and
wind speed is 0 m/s.
Columns must be 'wind_speed', 'temp_air'.

Returns
-------
self

Assigns attributes: times, solar_position, airmass, irradiance,
total_irrad, weather, aoi, temps, dc, ac.
"""

self.prepare_inputs(times, irradiance, weather)

self.aoi_mod = self.system.ashraeiam(self.aoi).fillna(0)
self.total_irrad['poa_global_aoi'] = (
self.total_irrad['poa_direct'] * self.aoi_mod +
self.total_irrad['poa_diffuse'])

self.temps = self.system.sapm_celltemp(
self.total_irrad['poa_global_aoi'],
self.weather['wind_speed'],
self.weather['temp_air'])

(photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth) = (
self.system.calcparams_desoto(self.total_irrad['poa_global_aoi'],
self.temps['temp_cell']))

self.desoto = (photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth)

self.dc = self.system.singlediode(
photocurrent, saturation_current, resistance_series,
resistance_shunt, nNsVth)

self.dc = self.dc.fillna(0)

self.dc = self.system.scale_voltage_current_power(self.dc)

self.ac = self.system.snlinverter(self.dc['v_mp'], self.dc['p_mp'])

return self
Loading