From 6bee9fefbebbb47efbe13dc79980102ef7c5b322 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 15 Apr 2016 16:43:09 -0700 Subject: [PATCH 1/9] refactor ModelChain. add SingleDiode modelchain --- pvlib/modelchain.py | 84 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 69832611b9..1652c512b3 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -211,7 +211,7 @@ 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. @@ -284,7 +284,7 @@ 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. @@ -307,8 +307,9 @@ 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) @@ -316,6 +317,9 @@ def run_model(self, times, irradiance=None, weather=None): 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, @@ -358,13 +362,39 @@ 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): + """ + Run the model. + + Parameters + ---------- + + Returns + ------- + self + + Assigns attributes: temps, dc, ac + """ + + 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'], @@ -376,3 +406,45 @@ 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): + """ + Run the model. + + Parameters + ---------- + + Returns + ------- + self + + Assigns attributes: temps, dc, ac + """ + + + self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], + 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'], + 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.ac = self.system.snlinverter(self.dc['v_mp'], self.dc['p_mp']) + + return self From e173d8b921973efd2743c11187e0e279c2c26249 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Mon, 18 Apr 2016 11:16:18 -0700 Subject: [PATCH 2/9] call prepare_inputs from run_model --- pvlib/modelchain.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 1652c512b3..280873acb9 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -286,7 +286,8 @@ def orientation_strategy(self, strategy): def prepare_inputs(self, times, irradiance=None, weather=None): """ - Run the model. + Prepare the solar position, irradiance, and weather inputs to + the model. Parameters ---------- @@ -368,6 +369,7 @@ def run_model(self): """ A stub function meant to be subclassed. """ + raise NotImplementedError( 'you must subclass ModelChain and implement this method') @@ -376,12 +378,23 @@ class SAPM(ModelChain): """ Uses the SAPM to calculate cell temperature, DC power and AC power. """ - def run_model(self): + 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 ------- @@ -389,7 +402,7 @@ def run_model(self): Assigns attributes: temps, dc, ac """ - + self.prepare_inputs(times, irradiance, weather) self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], self.weather['wind_speed'], @@ -414,12 +427,23 @@ class SingleDiode(ModelChain): and the SAPM models to calculate cell temperature and AC power. """ - def run_model(self): + 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 ------- @@ -428,6 +452,7 @@ def run_model(self): Assigns attributes: temps, dc, ac """ + self.prepare_inputs(times, irradiance, weather) self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], self.weather['wind_speed'], From 932cff0fbe63a1391e6a7c95f58cc797d07ef548 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Thu, 21 Apr 2016 16:29:42 -0700 Subject: [PATCH 3/9] update mc, sapm, singlediode, mc tests --- pvlib/test/test_modelchain.py | 135 ++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 30 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index e590325113..a30d1b33ac 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -3,7 +3,7 @@ from numpy import nan from pvlib import modelchain, pvsystem -from pvlib.modelchain import ModelChain +from pvlib.modelchain import ModelChain, SAPM, SingleDiode from pvlib.pvsystem import PVSystem from pvlib.tracking import SingleAxisTracker from pvlib.location import Location @@ -19,7 +19,7 @@ def retrieve_sam_network(): sam_data['cecinverter'] = pvsystem.retrieve_sam('cecinverter') -def mc_setup(): +def get_sapm_module_parameters(): # limit network usage try: modules = sam_data['sandiamod'] @@ -27,20 +27,72 @@ def mc_setup(): retrieve_sam_network() modules = sam_data['sandiamod'] - module = modules.Canadian_Solar_CS5P_220M___2009_.copy() + module = 'Canadian_Solar_CS5P_220M___2009_' + module_parameters = modules[module].copy() + + return module_parameters + + +def get_cec_module_parameters(): + # limit network usage + try: + modules = sam_data['cecmod'] + except KeyError: + retrieve_sam_network() + modules = sam_data['cecmod'] + + module = 'Canadian_Solar_CS5P_220M' + module_parameters = modules[module].copy() + module_parameters['EgRef'] = 1.121 + module_parameters['dEgdT'] = -0.0002677 + + return module_parameters + + +def get_cec_inverter_parameters(): + inverters = sam_data['cecinverter'] - inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy() + inverter = 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_' + inverter_parameters = inverters[inverter].copy() + + return inverter_parameters + + +def sapm_setup(): + + module_parameters = get_sapm_module_parameters() + + inverter_parameters = get_cec_inverter_parameters() + + system = PVSystem(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) + + location = Location(32.2, -111, altitude=700) + + mc = SAPM(system, location) + + return mc + + +def singlediode_setup(): + + module_parameters = get_cec_module_parameters() + + inverter_parameters = get_cec_inverter_parameters() - system = PVSystem(module_parameters=module, - inverter_parameters=inverter) + system = PVSystem(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) location = Location(32.2, -111, altitude=700) - return system, location + mc = SingleDiode(system, location) + + return mc def test_ModelChain_creation(): - system, location = mc_setup() + system = PVSystem() + location = Location(32.2, -111, altitude=700) mc = ModelChain(system, location) @@ -66,40 +118,63 @@ def run_orientation_strategy(strategy, expected): assert system.surface_azimuth == expected[1] -def test_run_model(): - system, location = mc_setup() +@raises(NotImplementedError) +def test_run_model_ModelChain(): + system = PVSystem() + location = Location(32.2, -111, altitude=700) mc = ModelChain(system, location) + mc.run_model() + + +def test_run_model(): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') - ac = mc.run_model(times).ac + expected = {} + expected[SAPM] = \ + pd.Series(np.array([ 1.82033564e+02, -2.00000000e-02]), index=times) + expected[SingleDiode] = \ + pd.Series(np.array([179.720353871, np.nan]), index=times) - expected = pd.Series(np.array([ 1.82033564e+02, -2.00000000e-02]), - index=times) - assert_series_equal(ac, expected) + def run_test(mc): + ac = mc.run_model(times).ac + assert_series_equal(ac, expected[type(mc)]) + + for mc in (sapm_setup(), singlediode_setup()): + yield run_test, mc def test_run_model_with_irradiance(): - system, location = mc_setup() - mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') - irradiance = pd.DataFrame({'dni':900, 'ghi':600, 'dhi':150}, - index=times) - ac = mc.run_model(times, irradiance=irradiance).ac + irradiance = pd.DataFrame( + {'dni': [900, 0], 'ghi': [600, 50], 'dhi': [150, 50]}, index=times) + expected = {} + expected[SAPM] = \ + pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), index=times) + expected[SingleDiode] = \ + pd.Series(np.array([186.979595403, 7.89417460232]), index=times) - expected = pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), - index=times) - assert_series_equal(ac, expected) + def run_test(mc): + ac = mc.run_model(times, irradiance=irradiance).ac + assert_series_equal(ac, expected[type(mc)]) + + for mc in (sapm_setup(), singlediode_setup()): + yield run_test, mc def test_run_model_with_weather(): - system, location = mc_setup() - mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') - weather = pd.DataFrame({'wind_speed':5, 'temp_air':10}, index=times) - ac = mc.run_model(times, weather=weather).ac - - expected = pd.Series(np.array([ 1.99952400e+02, -2.00000000e-02]), - index=times) - assert_series_equal(ac, expected) + weather = pd.DataFrame({'wind_speed': 5, 'temp_air': 10}, index=times) + expected = {} + expected[SAPM] = \ + pd.Series(np.array([ 1.99952400e+02, -2.00000000e-02]), index=times) + expected[SingleDiode] = \ + pd.Series(np.array([198.13564009, np.nan]), index=times) + + def run_test(mc): + ac = mc.run_model(times, weather=weather).ac + assert_series_equal(ac, expected[type(mc)]) + + for mc in (sapm_setup(), singlediode_setup()): + yield run_test, mc def test_run_model_tracker(): From abd1984e273a544f9b99c7e581d2d0436f564d23 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Thu, 21 Apr 2016 16:40:15 -0700 Subject: [PATCH 4/9] allow py3 conda failures. pep8 --- pvlib/test/test_modelchain.py | 63 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index a30d1b33ac..ad15defe2a 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -8,8 +8,10 @@ from pvlib.tracking import SingleAxisTracker from pvlib.location import Location -from pandas.util.testing import assert_series_equal, assert_frame_equal -from nose.tools import with_setup, raises +from pandas.util.testing import assert_series_equal +from nose.tools import raises + +from . import incompatible_conda_linux_py3 # should store this test data locally, but for now... sam_data = {} @@ -93,7 +95,7 @@ def singlediode_setup(): def test_ModelChain_creation(): system = PVSystem() location = Location(32.2, -111, altitude=700) - mc = ModelChain(system, location) + ModelChain(system, location) def test_orientation_strategy(): @@ -113,7 +115,7 @@ def run_orientation_strategy(strategy, expected): # the || accounts for the coercion of 'None' to None assert (mc.orientation_strategy == strategy or - mc.orientation_strategy == None) + mc.orientation_strategy is None) assert system.surface_tilt == expected[0] assert system.surface_azimuth == expected[1] @@ -126,11 +128,12 @@ def test_run_model_ModelChain(): mc.run_model() +@incompatible_conda_linux_py3 def test_run_model(): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') expected = {} expected[SAPM] = \ - pd.Series(np.array([ 1.82033564e+02, -2.00000000e-02]), index=times) + pd.Series(np.array([1.82033564e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ pd.Series(np.array([179.720353871, np.nan]), index=times) @@ -142,13 +145,14 @@ def run_test(mc): yield run_test, mc +@incompatible_conda_linux_py3 def test_run_model_with_irradiance(): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame( {'dni': [900, 0], 'ghi': [600, 50], 'dhi': [150, 50]}, index=times) expected = {} expected[SAPM] = \ - pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), index=times) + pd.Series(np.array([1.90054749e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ pd.Series(np.array([186.979595403, 7.89417460232]), index=times) @@ -160,12 +164,13 @@ def run_test(mc): yield run_test, mc +@incompatible_conda_linux_py3 def test_run_model_with_weather(): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') weather = pd.DataFrame({'wind_speed': 5, 'temp_air': 10}, index=times) expected = {} expected[SAPM] = \ - pd.Series(np.array([ 1.99952400e+02, -2.00000000e-02]), index=times) + pd.Series(np.array([1.99952400e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ pd.Series(np.array([198.13564009, np.nan]), index=times) @@ -209,10 +214,9 @@ def test_basic_chain_required(): latitude = 32 longitude = -111 altitude = 700 - modules = sam_data['sandiamod'] - module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - inverters = sam_data['cecinverter'] - inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] + + module_parameters = get_sapm_module_parameters() + inverter_parameters = get_cec_inverter_parameters() dc, ac = modelchain.basic_chain(times, latitude, longitude, module_parameters, inverter_parameters, @@ -224,20 +228,18 @@ def test_basic_chain_alt_az(): end='20160101 1800-0700', freq='6H') latitude = 32.2 longitude = -111 - altitude = 700 surface_tilt = 0 surface_azimuth = 0 - modules = sam_data['sandiamod'] - module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - inverters = sam_data['cecinverter'] - inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] + + module_parameters = get_sapm_module_parameters() + inverter_parameters = get_cec_inverter_parameters() dc, ac = modelchain.basic_chain(times, latitude, longitude, module_parameters, inverter_parameters, surface_tilt=surface_tilt, surface_azimuth=surface_azimuth) - expected = pd.Series(np.array([ 1.14490928477e+02, -2.00000000e-02]), + expected = pd.Series(np.array([1.14490928477e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -248,17 +250,15 @@ def test_basic_chain_strategy(): latitude = 32.2 longitude = -111 altitude = 700 - modules = sam_data['sandiamod'] - module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - inverters = sam_data['cecinverter'] - inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] - dc, ac = modelchain.basic_chain(times, latitude, longitude, - module_parameters, inverter_parameters, - orientation_strategy='south_at_latitude_tilt', - altitude=altitude) + module_parameters = get_sapm_module_parameters() + inverter_parameters = get_cec_inverter_parameters() - expected = pd.Series(np.array([ 1.82033563543e+02, -2.00000000e-02]), + dc, ac = modelchain.basic_chain( + times, latitude, longitude, module_parameters, inverter_parameters, + orientation_strategy='south_at_latitude_tilt', altitude=altitude) + + expected = pd.Series(np.array([1.82033563543e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -271,10 +271,9 @@ def test_basic_chain_altitude_pressure(): altitude = 700 surface_tilt = 0 surface_azimuth = 0 - modules = sam_data['sandiamod'] - module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - inverters = sam_data['cecinverter'] - inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] + + module_parameters = get_sapm_module_parameters() + inverter_parameters = get_cec_inverter_parameters() dc, ac = modelchain.basic_chain(times, latitude, longitude, module_parameters, inverter_parameters, @@ -282,7 +281,7 @@ def test_basic_chain_altitude_pressure(): surface_azimuth=surface_azimuth, pressure=93194) - expected = pd.Series(np.array([ 1.15771428788e+02, -2.00000000e-02]), + expected = pd.Series(np.array([1.15771428788e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -292,6 +291,6 @@ def test_basic_chain_altitude_pressure(): surface_azimuth=surface_azimuth, altitude=altitude) - expected = pd.Series(np.array([ 1.15771428788e+02, -2.00000000e-02]), + expected = pd.Series(np.array([1.15771428788e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) From d5852c56456185a93916a79f81bfa3deb70201d5 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Thu, 21 Apr 2016 16:57:02 -0700 Subject: [PATCH 5/9] update doc strings --- pvlib/modelchain.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 280873acb9..1aef50eb03 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -400,7 +400,8 @@ def run_model(self, times, irradiance=None, weather=None): ------- self - Assigns attributes: temps, dc, ac + Assigns attributes: times, solar_position, airmass, irradiance, + total_irrad, weather, aoi, temps, dc, ac. """ self.prepare_inputs(times, irradiance, weather) @@ -449,7 +450,8 @@ def run_model(self, times, irradiance=None, weather=None): ------- self - Assigns attributes: temps, dc, ac + Assigns attributes: times, solar_position, airmass, irradiance, + total_irrad, weather, aoi, temps, dc, ac. """ self.prepare_inputs(times, irradiance, weather) From 685825027e2e53694b1b02352cb2dfbb324a9b51 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 13 May 2016 15:53:01 -0700 Subject: [PATCH 6/9] add series and parallel multipliers, aoi modifier --- pvlib/modelchain.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 1aef50eb03..dc4457a81d 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -44,7 +44,7 @@ def basic_chain(times, latitude, longitude, Use decimal degrees notation. module_parameters : None, dict or Series - Module parameters as defined by the SAPM. + Module parameters as defined by the SAPM, CEC, or other. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. @@ -213,10 +213,7 @@ class ModelChain(object): """ 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 ---------- @@ -456,13 +453,19 @@ def run_model(self, times, irradiance=None, weather=None): 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_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'], + self.system.calcparams_desoto(self.total_irrad['poa_global_aoi'], self.temps['temp_cell'])) self.desoto = (photocurrent, saturation_current, resistance_series, @@ -472,6 +475,14 @@ def run_model(self, times, irradiance=None, weather=None): photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) + self.dc = self.dc.fillna(0) + + voltages = ['v_mp', 'v_oc'] + self.dc[voltages] *= self.system.series_modules + currents = ['i_mp', 'i_sc', 'i_x', 'i_xx'] + self.dc[currents] *= self.system.parallel_modules + self.dc['p_mp'] = self.dc['v_mp'] * self.dc['i_mp'] + self.ac = self.system.snlinverter(self.dc['v_mp'], self.dc['p_mp']) return self From 0e9ba3e050e59d87df1408b851201d6b2f995366 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 22 May 2016 10:35:30 -0700 Subject: [PATCH 7/9] use scaling method in SingleDiode --- pvlib/modelchain.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index dc4457a81d..0b4059cf14 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -44,7 +44,7 @@ def basic_chain(times, latitude, longitude, Use decimal degrees notation. module_parameters : None, dict or Series - Module parameters as defined by the SAPM, CEC, or other. + Module parameters as defined by the SAPM. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. @@ -375,6 +375,7 @@ 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. @@ -477,11 +478,7 @@ def run_model(self, times, irradiance=None, weather=None): self.dc = self.dc.fillna(0) - voltages = ['v_mp', 'v_oc'] - self.dc[voltages] *= self.system.series_modules - currents = ['i_mp', 'i_sc', 'i_x', 'i_xx'] - self.dc[currents] *= self.system.parallel_modules - self.dc['p_mp'] = self.dc['v_mp'] * self.dc['i_mp'] + self.dc = self.system.scale_voltage_current_power(self.dc) self.ac = self.system.snlinverter(self.dc['v_mp'], self.dc['p_mp']) From 15f5444b46e66c8b4092721468d51b70d4bdafca Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 22 May 2016 10:35:57 -0700 Subject: [PATCH 8/9] add 040 whatsnew --- docs/sphinx/source/whatsnew.rst | 1 + docs/sphinx/source/whatsnew/v0.4.0.txt | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 docs/sphinx/source/whatsnew/v0.4.0.txt diff --git a/docs/sphinx/source/whatsnew.rst b/docs/sphinx/source/whatsnew.rst index 1afae70ab0..677a13f3c5 100644 --- a/docs/sphinx/source/whatsnew.rst +++ b/docs/sphinx/source/whatsnew.rst @@ -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 diff --git a/docs/sphinx/source/whatsnew/v0.4.0.txt b/docs/sphinx/source/whatsnew/v0.4.0.txt new file mode 100644 index 0000000000..67451e99ca --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.4.0.txt @@ -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 From 17432e0ef6c1c925ccf57b54428e1a208eb8a212 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 22 May 2016 11:09:04 -0700 Subject: [PATCH 9/9] fix failing tests --- pvlib/test/test_modelchain.py | 53 ++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index ad15defe2a..2b4e285258 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -8,7 +8,7 @@ from pvlib.tracking import SingleAxisTracker from pvlib.location import Location -from pandas.util.testing import assert_series_equal +from pandas.util.testing import assert_series_equal, assert_frame_equal from nose.tools import raises from . import incompatible_conda_linux_py3 @@ -45,6 +45,7 @@ def get_cec_module_parameters(): module = 'Canadian_Solar_CS5P_220M' module_parameters = modules[module].copy() + module_parameters['b'] = 0.05 module_parameters['EgRef'] = 1.121 module_parameters['dEgdT'] = -0.0002677 @@ -60,14 +61,18 @@ def get_cec_inverter_parameters(): return inverter_parameters -def sapm_setup(): +def sapm_setup(tracker=False): module_parameters = get_sapm_module_parameters() inverter_parameters = get_cec_inverter_parameters() - system = PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + if tracker: + system = SingleAxisTracker(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) + else: + system = PVSystem(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) location = Location(32.2, -111, altitude=700) @@ -76,14 +81,18 @@ def sapm_setup(): return mc -def singlediode_setup(): +def singlediode_setup(tracker=False): module_parameters = get_cec_module_parameters() inverter_parameters = get_cec_inverter_parameters() - system = PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + if tracker: + system = SingleAxisTracker(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) + else: + system = PVSystem(module_parameters=module_parameters, + inverter_parameters=inverter_parameters) location = Location(32.2, -111, altitude=700) @@ -135,7 +144,7 @@ def test_run_model(): expected[SAPM] = \ pd.Series(np.array([1.82033564e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ - pd.Series(np.array([179.720353871, np.nan]), index=times) + pd.Series(np.array([179.15260, -2.00000000e-02]), index=times) def run_test(mc): ac = mc.run_model(times).ac @@ -154,7 +163,7 @@ def test_run_model_with_irradiance(): expected[SAPM] = \ pd.Series(np.array([1.90054749e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ - pd.Series(np.array([186.979595403, 7.89417460232]), index=times) + pd.Series(np.array([ 186.46774125, 7.8941746 ]), index=times) def run_test(mc): ac = mc.run_model(times, irradiance=irradiance).ac @@ -172,7 +181,7 @@ def test_run_model_with_weather(): expected[SAPM] = \ pd.Series(np.array([1.99952400e+02, -2.00000000e-02]), index=times) expected[SingleDiode] = \ - pd.Series(np.array([198.13564009, np.nan]), index=times) + pd.Series(np.array([ 1.97456536e+02, -2.00000000e-02]), index=times) def run_test(mc): ac = mc.run_model(times, weather=weather).ac @@ -183,23 +192,27 @@ def run_test(mc): def test_run_model_tracker(): - system, location = mc_setup() - system = SingleAxisTracker(module_parameters=system.module_parameters, - inverter_parameters=system.inverter_parameters) - mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') - ac = mc.run_model(times).ac - expected = pd.Series(np.array([ 121.421719, -2.00000000e-02]), - index=times) - assert_series_equal(ac, expected) + expected_ac = {} + expected_ac[SAPM] = \ + pd.Series(np.array([ 121.421719, -2.00000000e-02]), index=times) + expected_ac[SingleDiode] = \ + pd.Series(np.array([ 1.22736286e+02, -2.00000000e-02]), index=times) - expected = pd.DataFrame(np. + expected_tracking = pd.DataFrame(np. array([[ 54.82513187, 90. , 11.0039221 , 11.0039221 ], [ nan, 0. , 0. , nan]]), columns=['aoi', 'surface_azimuth', 'surface_tilt', 'tracker_theta'], index=times) - assert_frame_equal(mc.tracking, expected) + + def run_test(mc): + ac = mc.run_model(times).ac + assert_series_equal(ac, expected_ac[type(mc)]) + assert_frame_equal(mc.tracking, expected_tracking) + + for mc in (sapm_setup(tracker=True), singlediode_setup(tracker=True)): + yield run_test, mc @raises(ValueError)