diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 5f6fdd2800..2c9504a091 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -204,10 +204,10 @@ AOI modifiers pvsystem.ashraeiam pvsystem.sapm_aoi_loss -Single diode model ------------------- +Single diode models +------------------- -Functions relevant for the single diode model. +Functions relevant for single diode models. .. autosummary:: :toctree: generated/ @@ -419,7 +419,8 @@ ModelChain model definitions. :toctree: generated/ modelchain.ModelChain.sapm - modelchain.ModelChain.singlediode + modelchain.ModelChain.desoto + modelchain.ModelChain.pvsyst modelchain.ModelChain.pvwatts_dc modelchain.ModelChain.snlinverter modelchain.ModelChain.adrinverter diff --git a/docs/sphinx/source/whatsnew/v0.6.0.rst b/docs/sphinx/source/whatsnew/v0.6.0.rst index 6824952f5b..91d344c136 100644 --- a/docs/sphinx/source/whatsnew/v0.6.0.rst +++ b/docs/sphinx/source/whatsnew/v0.6.0.rst @@ -53,6 +53,8 @@ API Changes dirindex functions. (:issue:`311`, :issue:`396`) * Method ModelChain.infer_dc_model now returns a tuple (function handle, model name string) instead of only the function handle (:issue:`417`) +* Add DC model methods desoto and pvsyst to ModelChain, and deprecates DC model method singlediode + (singlediode defaults to desoto until v0.7.0) (:issue:`487`) Enhancements @@ -104,6 +106,7 @@ Enhancements * Add irradiance.clearness_index function. (:issue:`396`) * Add irradiance.clearness_index_zenith_independent function. (:issue:`396`) * Add checking for consistency between module_parameters and dc_model. (:issue:`417`) +* Add DC model methods desoto and pvsyst to ModelChain (:issue:`487`) Bug fixes diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index dece562297..b76660ad85 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -14,6 +14,7 @@ from pvlib.tracking import SingleAxisTracker import pvlib.irradiance # avoid name conflict with full import from pvlib.pvsystem import DC_MODEL_PARAMS +from pvlib._deprecation import pvlibDeprecationWarning def basic_chain(times, latitude, longitude, @@ -251,8 +252,8 @@ class ModelChain(object): dc_model: None, str, or function, default None If None, the model will be inferred from the contents of system.module_parameters. Valid strings are 'sapm', - 'singlediode', 'pvwatts'. The ModelChain instance will be passed - as the first argument to a user-defined function. + 'desoto', 'pvsyst', 'pvwatts'. The ModelChain instance will + be passed as the first argument to a user-defined function. ac_model: None, str, or function, default None If None, the model will be inferred from the contents of @@ -379,10 +380,19 @@ def dc_model(self, model): str(missing_params)) if model == 'sapm': self._dc_model = self.sapm - elif model == 'singlediode': - self._dc_model = self.singlediode + elif model == 'desoto': + self._dc_model = self.desoto + elif model == 'pvsyst': + self._dc_model = self.pvsyst elif model == 'pvwatts': self._dc_model = self.pvwatts_dc + elif model == 'singlediode': + warnings.warn('DC model keyword singlediode used for ' + 'ModelChain object. singlediode is ' + 'ambiguous, use desoto instead. singlediode ' + 'keyword will be removed in v0.7.0 and ' + 'later', pvlibDeprecationWarning) + self._dc_model = self.desoto else: raise ValueError(model + ' is not a valid DC power model') else: @@ -393,7 +403,10 @@ def infer_dc_model(self): if set(['A0', 'A1', 'C7']) <= params: return self.sapm, 'sapm' elif set(['a_ref', 'I_L_ref', 'I_o_ref', 'R_sh_ref', 'R_s']) <= params: - return self.singlediode, 'singlediode' + return self.desoto, 'desoto' + elif set(['gamma_ref', 'mu_gamma', 'I_L_ref', 'I_o_ref', + 'R_sh_ref', 'R_sh_0', 'R_sh_exp', 'R_s']) <= params: + return self.pvsyst, 'pvsyst' elif set(['pdc0', 'gamma_pdc']) <= params: return self.pvwatts_dc, 'pvwatts' else: @@ -408,7 +421,44 @@ def sapm(self): return self + def desoto(self): + (photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth) = ( + self.system.calcparams_desoto(self.effective_irradiance, + self.temps['temp_cell'])) + + self.diode_params = (photocurrent, saturation_current, + resistance_series, + resistance_shunt, nNsVth) + + self.dc = self.system.singlediode( + photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth) + + self.dc = self.system.scale_voltage_current_power(self.dc).fillna(0) + + return self + + def pvsyst(self): + (photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth) = ( + self.system.calcparams_pvsyst(self.effective_irradiance, + self.temps['temp_cell'])) + + self.diode_params = (photocurrent, saturation_current, + resistance_series, + resistance_shunt, nNsVth) + + self.dc = self.system.singlediode( + photocurrent, saturation_current, resistance_series, + resistance_shunt, nNsVth) + + self.dc = self.system.scale_voltage_current_power(self.dc).fillna(0) + + return self + def singlediode(self): + """Deprecated""" (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = ( self.system.calcparams_desoto(self.effective_irradiance, diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 2d08052056..1ecdac4f1e 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -31,6 +31,13 @@ 'C7', 'Isco', 'Impo', 'Aisc', 'Aimp', 'Bvoco', 'Mbvoc', 'Bvmpo', 'Mbvmp', 'N', 'Cells_in_Series', 'IXO', 'IXXO', 'FD']), + 'desoto' : + set(['alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', + 'R_sh_ref', 'R_s']), + 'pvsyst' : + set(['gamma_ref', 'mu_gamma', 'I_L_ref', 'I_o_ref', + 'R_sh_ref', 'R_sh_0', 'R_s', 'alpha_sc', 'EgRef', + 'cells_in_series']), 'singlediode' : set(['alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', 'R_sh_ref', 'R_s']), diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 1083cf97d3..dac2dd5fd6 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -14,12 +14,13 @@ from pvlib.pvsystem import PVSystem from pvlib.tracking import SingleAxisTracker from pvlib.location import Location +from pvlib._deprecation import pvlibDeprecationWarning from pandas.util.testing import assert_series_equal, assert_frame_equal import pytest -from test_pvsystem import sam_data -from conftest import requires_scipy +from test_pvsystem import sam_data, pvsyst_module_params +from conftest import fail_on_pvlib_version, requires_scipy @pytest.fixture @@ -53,6 +54,20 @@ def cec_dc_snl_ac_system(sam_data): return system +@pytest.fixture +def pvsyst_dc_snl_ac_system(sam_data): + module = 'PVsyst test module' + module_parameters = pvsyst_module_params() + module_parameters['b'] = 0.05 + inverters = sam_data['cecinverter'] + inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy() + system = PVSystem(surface_tilt=32.2, surface_azimuth=180, + module=module, + module_parameters=module_parameters, + inverter_parameters=inverter) + return system + + @pytest.fixture def cec_dc_adr_ac_system(sam_data): modules = sam_data['cecmod'] @@ -203,14 +218,26 @@ def poadc(mc): @pytest.mark.parametrize('dc_model', [ - 'sapm', pytest.param('singlediode', marks=requires_scipy), 'pvwatts_dc']) -def test_infer_dc_model(system, cec_dc_snl_ac_system, + 'sapm', + pytest.param('desoto', marks=requires_scipy), + pytest.param('pvsyst', marks=requires_scipy), + pytest.param('singlediode', marks=requires_scipy), + 'pvwatts_dc']) +def test_infer_dc_model(system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_system, location, dc_model, weather, mocker): - dc_systems = {'sapm': system, 'singlediode': cec_dc_snl_ac_system, + dc_systems = {'sapm': system, + 'desoto': cec_dc_snl_ac_system, + 'pvsyst': pvsyst_dc_snl_ac_system, + 'singlediode': cec_dc_snl_ac_system, 'pvwatts_dc': pvwatts_dc_pvwatts_ac_system} + dc_model_function = {'sapm': 'sapm', + 'desoto': 'calcparams_desoto', + 'pvsyst': 'calcparams_pvsyst', + 'singlediode': 'calcparams_desoto', + 'pvwatts_dc': 'pvwatts_dc'} system = dc_systems[dc_model] - m = mocker.spy(system, dc_model) + m = mocker.spy(system, dc_model_function[dc_model]) mc = ModelChain(system, location, aoi_model='no_loss', spectral_model='no_loss') mc.run_model(weather.index, weather=weather) @@ -410,6 +437,14 @@ def test_bad_get_orientation(): modelchain.get_orientation('bad value') +@fail_on_pvlib_version('0.7') +def test_deprecated_07(): + with pytest.warns(pvlibDeprecationWarning): + mc = ModelChain(cec_dc_snl_ac_system, location, + dc_model='singlediode', # this should fail after 0.7 + aoi_model='no_loss', spectral_model='no_loss') + + @requires_scipy def test_basic_chain_required(sam_data): times = pd.DatetimeIndex(start='20160101 1200-0700',