From d121ee43cf94054a5b2863d6d114e68036b80c5e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 29 Mar 2019 10:17:10 -0600 Subject: [PATCH 01/98] move celltemp functions to celltemp.py, fix docstring for pvsystem.PVSystem.sapm --- pvlib/celltemp.py | 189 +++++++++++++++++++++++++++ pvlib/pvsystem.py | 252 ++++++++---------------------------- pvlib/test/test_celltemp.py | 132 +++++++++++++++++++ pvlib/test/test_pvsystem.py | 108 ---------------- 4 files changed, 374 insertions(+), 307 deletions(-) create mode 100644 pvlib/celltemp.py create mode 100644 pvlib/test/test_celltemp.py diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py new file mode 100644 index 0000000000..8442d68428 --- /dev/null +++ b/pvlib/celltemp.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 29 09:40:42 2019 + +@author: cwhanse +""" + +import numpy as np +import pandas as pd + + +TEMP_MODEL_PARAMS = { + 'sapm': {'open_rack_cell_glassback': (-3.47, -.0594, 3), + 'roof_mount_cell_glassback': (-2.98, -.0471, 1), + 'open_rack_cell_polymerback': (-3.56, -.0750, 3), + 'insulated_back_polymerback': (-2.81, -.0455, 0), + 'open_rack_polymer_thinfilm_steel': (-3.58, -.113, 3), + '22x_concentrator_tracker': (-3.23, -.130, 13)}, + 'pvsyst': {'freestanding': (29.0, 0), 'insulated': (15.0, 0)} +} + + +def sapm(poa_global, wind_speed, temp_air, model='open_rack_cell_glassback'): + ''' + Estimate cell and module temperatures per the Sandia PV Array + Performance Model (SAPM, SAND2004-3535), from the incident + irradiance, wind speed, ambient temperature, and SAPM module + parameters. + + Parameters + ---------- + poa_global : float or Series + Total incident irradiance in W/m^2. + + wind_speed : float or Series + Wind speed in m/s at a height of 10 meters. + + temp_air : float or Series + Ambient dry bulb temperature in degrees C. + + model : string, list, or dict, default 'open_rack_cell_glassback' + Model to be used. + + If string, can be: + + * 'open_rack_cell_glassback' (default) + * 'roof_mount_cell_glassback' + * 'open_rack_cell_polymerback' + * 'insulated_back_polymerback' + * 'open_rack_polymer_thinfilm_steel' + * '22x_concentrator_tracker' + + If dict, supply the following parameters + (if list, in the following order): + + * a : float + SAPM module parameter for establishing the upper + limit for module temperature at low wind speeds and + high solar irradiance. + + * b : float + SAPM module parameter for establishing the rate at + which the module temperature drops as wind speed increases + (see SAPM eqn. 11). + + * deltaT : float + SAPM module parameter giving the temperature difference + between the cell and module back surface at the + reference irradiance, E0. + + Returns + -------- + DataFrame with columns 'temp_cell' and 'temp_module'. + Values in degrees C. + + References + ---------- + [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance + Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, + NM. + + See Also + -------- + sapm + ''' + + temp_models = TEMP_MODEL_PARAMS['sapm'] + + if isinstance(model, str): + model = temp_models[model.lower()] + + elif isinstance(model, (dict, pd.Series)): + model = [model['a'], model['b'], model['deltaT']] + + a = model[0] + b = model[1] + deltaT = model[2] + + E0 = 1000. # Reference irradiance + + temp_module = pd.Series(poa_global * np.exp(a + b * wind_speed) + temp_air) + + temp_cell = temp_module + (poa_global / E0) * (deltaT) + + return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) + + +def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, + alpha_absorption=0.9, model_params='freestanding'): + """ + Calculate cell temperature using an emperical heat loss factor model + as implemented in PVsyst. + + The heat loss factors provided through the 'model_params' argument + represent the combined effect of convection, radiation and conduction, + and their values are experimentally determined. + + Parameters + ---------- + poa_global : numeric + Total incident irradiance in W/m^2. + + temp_air : numeric + Ambient dry bulb temperature in degrees C. + + wind_speed : numeric, default 1.0 + Wind speed in m/s measured at the same height for which the wind loss + factor was determined. The default value is 1.0, which is the wind + speed at module height used to determine NOCT. + + eta_m : numeric, default 0.1 + Module external efficiency as a fraction, i.e., DC power / poa_global. + + alpha_absorption : numeric, default 0.9 + Absorption coefficient + + model_params : string, tuple, or list (no dict), default 'freestanding' + Heat loss factors to be used. + + If string, can be: + + * 'freestanding' (default) + Modules with rear surfaces exposed to open air (e.g. rack + mounted). + * 'insulated' + Modules with rear surfaces in close proximity to another + surface (e.g. roof mounted). + + If tuple/list, supply parameters in the following order: + + * constant_loss_factor : float + Combined heat loss factor coefficient. Freestanding + default is 29, fully insulated arrays is 15. + + * wind_loss_factor : float + Combined heat loss factor influenced by wind. Default is 0. + + Returns + ------- + temp_cell : numeric or Series + Cell temperature in degrees Celsius + + References + ---------- + [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: + http://files.pvsyst.com/help/index.html. [Accessed: 10- Dec- 2018]. + + [2] Faiman, D. (2008). "Assessing the outdoor operating temperature of + photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. + """ + + pvsyst_presets = TEMP_MODEL_PARAMS['pvsyst'] + + if isinstance(model_params, str): + model_params = model_params.lower() + constant_loss_factor, wind_loss_factor = pvsyst_presets[model_params] + elif isinstance(model_params, (tuple, list)): + constant_loss_factor, wind_loss_factor = model_params + else: + raise TypeError( + "Please provide model_params as a str, or tuple/list." + ) + + total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor + heat_input = poa_global * alpha_absorption * (1 - eta_m) + temp_difference = heat_input / total_loss_factor + temp_cell = temp_air + temp_difference + + return temp_cell \ No newline at end of file diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 5ee22debd3..896cf57089 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -16,9 +16,12 @@ import numpy as np import pandas as pd +from pvlib._deprecation import deprecated + from pvlib import atmosphere, irradiance, tools, singlediode as _singlediode from pvlib.tools import _build_kwargs from pvlib.location import Location +from pvlib import celltemp # a dict of required parameter names for each DC power model @@ -46,16 +49,6 @@ } -TEMP_MODEL_PARAMS = { - 'sapm': {'open_rack_cell_glassback': (-3.47, -.0594, 3), - 'roof_mount_cell_glassback': (-2.98, -.0471, 1), - 'open_rack_cell_polymerback': (-3.56, -.0750, 3), - 'insulated_back_polymerback': (-2.81, -.0455, 0), - 'open_rack_polymer_thinfilm_steel': (-3.58, -.113, 3), - '22x_concentrator_tracker': (-3.23, -.130, 13)}, - 'pvsyst': {'freestanding': (29.0, 0), 'insulated': (15.0, 0)} -} - # not sure if this belongs in the pvsystem module. # maybe something more like core.py? It may eventually grow to # import a lot more functionality from other modules. @@ -410,24 +403,15 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ Use the :py:func:`sapm` function, the input parameters, and ``self.module_parameters`` to calculate - Voc, Isc, Ix, Ixx, Vmp/Imp. + Voc, Isc, Ix, Ixx, Vmp, and Imp. Parameters ---------- - poa_direct : Series - The direct irradiance incident upon the module (W/m^2). - - poa_diffuse : Series - The diffuse irradiance incident on module. - - temp_cell : Series - The cell temperature (degrees C). - - airmass_absolute : Series - Absolute airmass. + effective_irradiance : numeric + The irradiance (W/m2) that is converted to photocurrent. - aoi : Series - Angle of incidence (degrees). + temp_cell : float or Series + The average cell temperature of cells within a module in C. **kwargs See pvsystem.sapm for details @@ -438,20 +422,32 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, irrad, wind, temp): - """Uses :py:func:`sapm_celltemp` to calculate module and cell + def sapm_celltemp(self, poa_global, wind_speed, temp_air): + """Uses :py:func:`celltemp.sapm` to calculate module and cell temperatures based on ``self.racking_model`` and the input parameters. Parameters ---------- - See pvsystem.sapm_celltemp for details + poa_global : float or Series + Total incident irradiance in W/m^2. + + wind_speed : float or Series + Wind speed in m/s at a height of 10 meters. + + temp_air : float or Series + Ambient dry bulb temperature in degrees C. + + model : string, list, or dict, default 'open_rack_cell_glassback' + Model to be used. See celltemp.sapm for details Returns ------- - See pvsystem.sapm_celltemp for details + DataFrame with columns 'temp_cell' and 'temp_module'. + Values in degrees C. """ - return sapm_celltemp(irrad, wind, temp, self.racking_model) + return celltemp.sapm(poa_global, wind_speed, temp_air, + self.racking_model) def sapm_spectral_loss(self, airmass_absolute): """ @@ -522,20 +518,39 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, self.module_parameters, reference_irradiance=reference_irradiance) def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): - """Uses :py:func:`pvsyst_celltemp` to calculate module temperatures + """Uses :py:func:`celltemp.pvsyst` to calculate module temperatures based on ``self.racking_model`` and the input parameters. Parameters ---------- - See pvsystem.pvsyst_celltemp for details + poa_global : numeric + Total incident irradiance in W/m^2. + + temp_air : numeric + Ambient dry bulb temperature in degrees C. + + wind_speed : numeric, default 1.0 + Wind speed in m/s measured at the same height for which the wind loss + factor was determined. The default value is 1.0, which is the wind + speed at module height used to determine NOCT. + + eta_m : numeric, default 0.1 + Module external efficiency as a fraction, i.e., DC power / poa_global. + + alpha_absorption : numeric, default 0.9 + Absorption coefficient + + model_params : string, tuple, or list (no dict), default 'freestanding' + Heat loss factors to be used. See celltemp.pvsyst for details. Returns ------- - See pvsystem.pvsyst_celltemp for details + temp_cell : numeric or Series + Cell temperature in degrees C. """ kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) - return pvsyst_celltemp(poa_global, temp_air, wind_speed, + return celltemp.pvsyst(poa_global, temp_air, wind_speed, model_params=self.racking_model, **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): @@ -1822,174 +1837,13 @@ def sapm(effective_irradiance, temp_cell, module): return out -def sapm_celltemp(poa_global, wind_speed, temp_air, - model='open_rack_cell_glassback'): - ''' - Estimate cell and module temperatures per the Sandia PV Array - Performance Model (SAPM, SAND2004-3535), from the incident - irradiance, wind speed, ambient temperature, and SAPM module - parameters. - - Parameters - ---------- - poa_global : float or Series - Total incident irradiance in W/m^2. - - wind_speed : float or Series - Wind speed in m/s at a height of 10 meters. - - temp_air : float or Series - Ambient dry bulb temperature in degrees C. - - model : string, list, or dict, default 'open_rack_cell_glassback' - Model to be used. - - If string, can be: - - * 'open_rack_cell_glassback' (default) - * 'roof_mount_cell_glassback' - * 'open_rack_cell_polymerback' - * 'insulated_back_polymerback' - * 'open_rack_polymer_thinfilm_steel' - * '22x_concentrator_tracker' - - If dict, supply the following parameters - (if list, in the following order): - - * a : float - SAPM module parameter for establishing the upper - limit for module temperature at low wind speeds and - high solar irradiance. - - * b : float - SAPM module parameter for establishing the rate at - which the module temperature drops as wind speed increases - (see SAPM eqn. 11). - - * deltaT : float - SAPM module parameter giving the temperature difference - between the cell and module back surface at the - reference irradiance, E0. - - Returns - -------- - DataFrame with columns 'temp_cell' and 'temp_module'. - Values in degrees C. - - References - ---------- - [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance - Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, - NM. - - See Also - -------- - sapm - ''' - - temp_models = TEMP_MODEL_PARAMS['sapm'] - - if isinstance(model, str): - model = temp_models[model.lower()] - - elif isinstance(model, (dict, pd.Series)): - model = [model['a'], model['b'], model['deltaT']] - - a = model[0] - b = model[1] - deltaT = model[2] - - E0 = 1000. # Reference irradiance - - temp_module = pd.Series(poa_global * np.exp(a + b * wind_speed) + temp_air) - - temp_cell = temp_module + (poa_global / E0) * (deltaT) - - return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) - - -def pvsyst_celltemp(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, - alpha_absorption=0.9, model_params='freestanding'): - """ - Calculate cell temperature using an emperical heat loss factor model - as implemented in PVsyst. - - The heat loss factors provided through the 'model_params' argument - represent the combined effect of convection, radiation and conduction, - and their values are experimentally determined. - - Parameters - ---------- - poa_global : numeric - Total incident irradiance in W/m^2. - - temp_air : numeric - Ambient dry bulb temperature in degrees C. - - wind_speed : numeric, default 1.0 - Wind speed in m/s measured at the same height for which the wind loss - factor was determined. The default value is 1.0, which is the wind - speed at module height used to determine NOCT. - - eta_m : numeric, default 0.1 - Module external efficiency as a fraction, i.e., DC power / poa_global. - - alpha_absorption : numeric, default 0.9 - Absorption coefficient - - model_params : string, tuple, or list (no dict), default 'freestanding' - Heat loss factors to be used. - - If string, can be: - - * 'freestanding' (default) - Modules with rear surfaces exposed to open air (e.g. rack - mounted). - * 'insulated' - Modules with rear surfaces in close proximity to another - surface (e.g. roof mounted). - - If tuple/list, supply parameters in the following order: - - * constant_loss_factor : float - Combined heat loss factor coefficient. Freestanding - default is 29, fully insulated arrays is 15. - - * wind_loss_factor : float - Combined heat loss factor influenced by wind. Default is 0. - - Returns - ------- - temp_cell : numeric or Series - Cell temperature in degrees Celsius - - References - ---------- - [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: - http://files.pvsyst.com/help/index.html. [Accessed: 10- Dec- 2018]. - - [2] Faiman, D. (2008). "Assessing the outdoor operating temperature of - photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. - """ - - pvsyst_presets = TEMP_MODEL_PARAMS['pvsyst'] - - if isinstance(model_params, str): - model_params = model_params.lower() - constant_loss_factor, wind_loss_factor = pvsyst_presets[model_params] - elif isinstance(model_params, (tuple, list)): - constant_loss_factor, wind_loss_factor = model_params - else: - raise TypeError( - "Please provide model_params as a str, or tuple/list." - ) +sapm_celltemp = deprecated('0.6.2', alternative='celltemp.sapm', + name='sapm_celltemp', removal='0.7')(celltemp.sapm) - total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor - heat_input = poa_global * alpha_absorption * (1 - eta_m) - temp_difference = heat_input / total_loss_factor - temp_cell = temp_air + temp_difference - return temp_cell +pvsyst_celltemp = deprecated('0.6.2', alternative='celltemp.pvsyst', + name='pvsyst_celltemp', + removal='0.7')(celltemp.pvsyst) def sapm_spectral_loss(airmass_absolute, module): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py new file mode 100644 index 0000000000..f17d102eea --- /dev/null +++ b/pvlib/test/test_celltemp.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 29 10:07:25 2019 + +@author: cwhanse +""" +import pandas as pd + +import pytest +from pandas.util.testing import assert_series_equal, assert_frame_equal +from numpy.testing import assert_allclose + +from pvlib import celltemp, pvsystem +from pvlib._deprecation import pvlibDeprecationWarning + +from conftest import fail_on_pvlib_version + + +def test_sapm_celltemp(): + default = celltemp.sapm(900, 5, 20) + assert_allclose(default['temp_cell'], 43.509, 3) + assert_allclose(default['temp_module'], 40.809, 3) + assert_frame_equal(default, celltemp.sapm(900, 5, 20, + [-3.47, -.0594, 3])) + + +def test_sapm_celltemp_dict_like(): + default = celltemp.sapm(900, 5, 20) + assert_allclose(default['temp_cell'], 43.509, 3) + assert_allclose(default['temp_module'], 40.809, 3) + model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} + assert_frame_equal(default, celltemp.sapm(900, 5, 20, model)) + model = pd.Series(model) + assert_frame_equal(default, celltemp.sapm(900, 5, 20, model)) + + +def test_sapm_celltemp_with_index(): + times = pd.date_range(start='2015-01-01', end='2015-01-02', freq='12H') + temps = pd.Series([0, 10, 5], index=times) + irrads = pd.Series([0, 500, 0], index=times) + winds = pd.Series([10, 5, 0], index=times) + + pvtemps = celltemp.sapm(irrads, winds, temps) + + expected = pd.DataFrame({'temp_cell':[0., 23.06066166, 5.], + 'temp_module':[0., 21.56066166, 5.]}, + index=times) + + assert_frame_equal(expected, pvtemps) + + +def test_PVSystem_sapm_celltemp(mocker): + racking_model = 'roof_mount_cell_glassback' + + system = pvsystem.PVSystem(racking_model=racking_model) + mocker.spy(pvsystem, 'sapm_celltemp') + temps = 25 + irrads = 1000 + winds = 1 + out = system.sapm_celltemp(irrads, winds, temps) + celltemp.sapm.assert_called_once_with( + irrads, winds, temps, model=racking_model) + assert isinstance(out, pd.DataFrame) + assert out.shape == (1, 2) + + +def test_pvsyst_celltemp_default(): + default = celltemp.pvsyst(900, 20, 5) + assert_allclose(default, 45.137, 0.001) + + +def test_pvsyst_celltemp_non_model(): + tup_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, + model_params=(23.5, 6.25)) + assert_allclose(tup_non_model, 33.315, 0.001) + + list_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, + model_params=[26.5, 7.68]) + assert_allclose(list_non_model, 31.233, 0.001) + + +def test_pvsyst_celltemp_model_wrong_type(): + with pytest.raises(TypeError): + pvsystem.pvsyst_celltemp( + 900, 20, 5, 0.1, + model_params={"won't": 23.5, "work": 7.68}) + + +def test_pvsyst_celltemp_model_non_option(): + with pytest.raises(KeyError): + pvsystem.pvsyst_celltemp( + 900, 20, 5, 0.1, + model_params="not_an_option") + + +def test_pvsyst_celltemp_with_index(): + times = pd.date_range(start="2015-01-01", end="2015-01-02", freq="12H") + temps = pd.Series([0, 10, 5], index=times) + irrads = pd.Series([0, 500, 0], index=times) + winds = pd.Series([10, 5, 0], index=times) + + pvtemps = pvsystem.pvsyst_celltemp(irrads, temps, wind_speed=winds) + expected = pd.Series([0.0, 23.96551, 5.0], index=times) + assert_series_equal(expected, pvtemps) + + +def test_PVSystem_pvsyst_celltemp(mocker): + racking_model = 'insulated' + alpha_absorption = 0.85 + eta_m = 0.17 + module_parameters = {} + module_parameters['alpha_absorption'] = alpha_absorption + module_parameters['eta_m'] = eta_m + system = pvsystem.PVSystem(racking_model=racking_model, + module_parameters=module_parameters) + mocker.spy(pvsystem, 'pvsyst_celltemp') + irrad = 800 + temp = 45 + wind = 0.5 + out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) + pvsystem.pvsyst_celltemp.assert_called_once_with( + irrad, temp, wind, eta_m, alpha_absorption, racking_model) + assert isinstance(out, float) + assert out < 90 and out > 70 + + +@fail_on_pvlib_version('0.7') +def test_deprecated_07(): + with pytest.warns(pvlibDeprecationWarning): + pvsystem.sapm_celltemp(1000, 1, 25) + with pytest.warns(pvlibDeprecationWarning): + pvsystem.pvsyst_celltemp(1000, 25) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index cd7679f323..60be5edf28 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1030,114 +1030,6 @@ def test_PVSystem_scale_voltage_current_power(mocker): m.assert_called_once_with(data, voltage=2, current=3) -def test_sapm_celltemp(): - default = pvsystem.sapm_celltemp(900, 5, 20) - assert_allclose(default['temp_cell'], 43.509, 3) - assert_allclose(default['temp_module'], 40.809, 3) - assert_frame_equal(default, pvsystem.sapm_celltemp(900, 5, 20, - [-3.47, -.0594, 3])) - - -def test_sapm_celltemp_dict_like(): - default = pvsystem.sapm_celltemp(900, 5, 20) - assert_allclose(default['temp_cell'], 43.509, 3) - assert_allclose(default['temp_module'], 40.809, 3) - model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} - assert_frame_equal(default, pvsystem.sapm_celltemp(900, 5, 20, model)) - model = pd.Series(model) - assert_frame_equal(default, pvsystem.sapm_celltemp(900, 5, 20, model)) - - -def test_sapm_celltemp_with_index(): - times = pd.date_range(start='2015-01-01', end='2015-01-02', freq='12H') - temps = pd.Series([0, 10, 5], index=times) - irrads = pd.Series([0, 500, 0], index=times) - winds = pd.Series([10, 5, 0], index=times) - - pvtemps = pvsystem.sapm_celltemp(irrads, winds, temps) - - expected = pd.DataFrame({'temp_cell':[0., 23.06066166, 5.], - 'temp_module':[0., 21.56066166, 5.]}, - index=times) - - assert_frame_equal(expected, pvtemps) - - -def test_PVSystem_sapm_celltemp(mocker): - racking_model = 'roof_mount_cell_glassback' - - system = pvsystem.PVSystem(racking_model=racking_model) - mocker.spy(pvsystem, 'sapm_celltemp') - temps = 25 - irrads = 1000 - winds = 1 - out = system.sapm_celltemp(irrads, winds, temps) - pvsystem.sapm_celltemp.assert_called_once_with( - irrads, winds, temps, model=racking_model) - assert isinstance(out, pd.DataFrame) - assert out.shape == (1, 2) - - -def test_pvsyst_celltemp_default(): - default = pvsystem.pvsyst_celltemp(900, 20, 5) - assert_allclose(default, 45.137, 0.001) - - -def test_pvsyst_celltemp_non_model(): - tup_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model_params=(23.5, 6.25)) - assert_allclose(tup_non_model, 33.315, 0.001) - - list_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model_params=[26.5, 7.68]) - assert_allclose(list_non_model, 31.233, 0.001) - - -def test_pvsyst_celltemp_model_wrong_type(): - with pytest.raises(TypeError): - pvsystem.pvsyst_celltemp( - 900, 20, 5, 0.1, - model_params={"won't": 23.5, "work": 7.68}) - - -def test_pvsyst_celltemp_model_non_option(): - with pytest.raises(KeyError): - pvsystem.pvsyst_celltemp( - 900, 20, 5, 0.1, - model_params="not_an_option") - - -def test_pvsyst_celltemp_with_index(): - times = pd.date_range(start="2015-01-01", end="2015-01-02", freq="12H") - temps = pd.Series([0, 10, 5], index=times) - irrads = pd.Series([0, 500, 0], index=times) - winds = pd.Series([10, 5, 0], index=times) - - pvtemps = pvsystem.pvsyst_celltemp(irrads, temps, wind_speed=winds) - expected = pd.Series([0.0, 23.96551, 5.0], index=times) - assert_series_equal(expected, pvtemps) - - -def test_PVSystem_pvsyst_celltemp(mocker): - racking_model = 'insulated' - alpha_absorption = 0.85 - eta_m = 0.17 - module_parameters = {} - module_parameters['alpha_absorption'] = alpha_absorption - module_parameters['eta_m'] = eta_m - system = pvsystem.PVSystem(racking_model=racking_model, - module_parameters=module_parameters) - mocker.spy(pvsystem, 'pvsyst_celltemp') - irrad = 800 - temp = 45 - wind = 0.5 - out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) - pvsystem.pvsyst_celltemp.assert_called_once_with( - irrad, temp, wind, eta_m, alpha_absorption, racking_model) - assert isinstance(out, float) - assert out < 90 and out > 70 - - def test_adrinverter(sam_data): inverters = sam_data['adrinverter'] testinv = 'Ablerex_Electronics_Co___Ltd___' \ From c7ef41927ec8b13c63a544d8001d2dab11559610 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 29 Mar 2019 10:32:56 -0600 Subject: [PATCH 02/98] formatting --- pvlib/celltemp.py | 2 +- pvlib/test/test_celltemp.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 8442d68428..cd10054e7d 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -186,4 +186,4 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, temp_difference = heat_input / total_loss_factor temp_cell = temp_air + temp_difference - return temp_cell \ No newline at end of file + return temp_cell diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index f17d102eea..c6d33b2c10 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -20,8 +20,7 @@ def test_sapm_celltemp(): default = celltemp.sapm(900, 5, 20) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) - assert_frame_equal(default, celltemp.sapm(900, 5, 20, - [-3.47, -.0594, 3])) + assert_frame_equal(default, celltemp.sapm(900, 5, 20, [-3.47, -.0594, 3])) def test_sapm_celltemp_dict_like(): @@ -42,8 +41,8 @@ def test_sapm_celltemp_with_index(): pvtemps = celltemp.sapm(irrads, winds, temps) - expected = pd.DataFrame({'temp_cell':[0., 23.06066166, 5.], - 'temp_module':[0., 21.56066166, 5.]}, + expected = pd.DataFrame({'temp_cell': [0., 23.06066166, 5.], + 'temp_module': [0., 21.56066166, 5.]}, index=times) assert_frame_equal(expected, pvtemps) From 2d807eb30098a7921e77178197cc4e1fe3252789 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 29 Mar 2019 10:33:12 -0600 Subject: [PATCH 03/98] formatting --- pvlib/pvsystem.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 896cf57089..66834b188c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -431,13 +431,13 @@ def sapm_celltemp(self, poa_global, wind_speed, temp_air): ---------- poa_global : float or Series Total incident irradiance in W/m^2. - + wind_speed : float or Series Wind speed in m/s at a height of 10 meters. - + temp_air : float or Series Ambient dry bulb temperature in degrees C. - + model : string, list, or dict, default 'open_rack_cell_glassback' Model to be used. See celltemp.sapm for details @@ -525,21 +525,22 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): ---------- poa_global : numeric Total incident irradiance in W/m^2. - + temp_air : numeric Ambient dry bulb temperature in degrees C. - + wind_speed : numeric, default 1.0 - Wind speed in m/s measured at the same height for which the wind loss - factor was determined. The default value is 1.0, which is the wind - speed at module height used to determine NOCT. - + Wind speed in m/s measured at the same height for which the wind + loss factor was determined. The default value is 1.0, which is + the wind speed at module height used to determine NOCT. + eta_m : numeric, default 0.1 - Module external efficiency as a fraction, i.e., DC power / poa_global. - + Module external efficiency as a fraction, i.e., + DC power / poa_global. + alpha_absorption : numeric, default 0.9 Absorption coefficient - + model_params : string, tuple, or list (no dict), default 'freestanding' Heat loss factors to be used. See celltemp.pvsyst for details. From f4751203bbf194bbf55a970df0ef02e31f45f6b7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 3 May 2019 11:46:24 -0600 Subject: [PATCH 04/98] initial commit fit_cec_using_sam --- pvlib/ivtools/PySSC.py | 248 ++++++++++++++++++++++++ pvlib/ivtools/fit_cec_model_with_sam.py | 103 ++++++++++ 2 files changed, 351 insertions(+) create mode 100644 pvlib/ivtools/PySSC.py create mode 100644 pvlib/ivtools/fit_cec_model_with_sam.py diff --git a/pvlib/ivtools/PySSC.py b/pvlib/ivtools/PySSC.py new file mode 100644 index 0000000000..c638a11b74 --- /dev/null +++ b/pvlib/ivtools/PySSC.py @@ -0,0 +1,248 @@ +#Created with SAM version 2018.11.11 +# Modified by C. W. Hansen for PEP8 compliance +import sys +import os +import ctypes +ctypes.c_number = ctypes.c_float # must be c_double or c_float depending on how defined in sscapi.h +class PySSC(): + def __init__(self, sam_dir): + if sys.platform in ['win32', 'cygwin']: + self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.dll")) + elif sys.platform == 'darwin': + self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.dylib")) + elif sys.platform == 'linux2': + self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.so")) # instead of relative path, require user to have on LD_LIBRARY_PATH + else: + print ('Platform not supported ', sys.platform) + INVALID=0 + STRING=1 + NUMBER=2 + ARRAY=3 + MATRIX=4 + INPUT=1 + OUTPUT=2 + INOUT=3 + + def version(self): + self.pdll.ssc_version.restype = ctypes.c_int + return self.pdll.ssc_version() + + def build_info(self): + self.pdll.ssc_build_info.restype = ctypes.c_char_p + return self.pdll.ssc_build_info() + + def data_create(self): + self.pdll.ssc_data_create.restype = ctypes.c_void_p + return self.pdll.ssc_data_create() + + def data_free(self, p_data): + self.pdll.ssc_data_free(ctypes.c_void_p(p_data)) + + def data_clear(self, p_data): + self.pdll.ssc_data_clear(ctypes.c_void_p(p_data)) + + def data_unassign(self, p_data, name): + self.pdll.ssc_data_unassign(ctypes.c_void_p(p_data), + ctypes.c_char_p(name)) + + def data_query(self, p_data, name): + self.pdll.ssc_data_query.restype = ctypes.c_int + return self.pdll.ssc_data_query(ctypes.c_void_p(p_data), + ctypes.c_char_p(name)) + + def data_first(self, p_data): + self.pdll.ssc_data_first.restype = ctypes.c_char_p + return self.pdll.ssc_data_first(ctypes.c_void_p(p_data)) + + def data_next(self, p_data): + self.pdll.ssc_data_next.restype = ctypes.c_char_p + return self.pdll.ssc_data_next(ctypes.c_void_p(p_data)) + + def data_set_string(self, p_data, name, value): + self.pdll.ssc_data_set_string(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.c_char_p(value)) + + def data_set_number(self, p_data, name, value): + self.pdll.ssc_data_set_number(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.c_number(value)) + + def data_set_array(self,p_data,name,parr): + count = len(parr) + arr = (ctypes.c_number * count)() + arr[:] = parr # set all at once instead of looping + return self.pdll.ssc_data_set_array(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.pointer(arr), + ctypes.c_int(count)) + + def data_set_array_from_csv(self, p_data, name, fn): + f = open(fn, 'rb'); + data = []; + for line in f : + data.extend([n for n in map(float, line.split(b','))]) + f.close() + return self.data_set_array(p_data, name, data) + + def data_set_matrix(self,p_data,name,mat): + nrows = len(mat) + ncols = len(mat[0]) + size = nrows * ncols + arr = (ctypes.c_number * size)() + idx=0 + for r in range(nrows): + for c in range(ncols): + arr[idx] = ctypes.c_number(mat[r][c]) + idx += 1 + return self.pdll.ssc_data_set_matrix(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.pointer(arr), + ctypes.c_int(nrows), + ctypes.c_int(ncols)) + + def data_set_matrix_from_csv(self, p_data, name, fn): + f = open(fn, 'rb'); + data = []; + for line in f : + lst = ([n for n in map(float, line.split(b','))]) + data.append(lst); + f.close(); + return self.data_set_matrix(p_data, name, data); + + def data_set_table(self,p_data,name,tab): + return self.pdll.ssc_data_set_table(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.c_void_p(tab)); + + def data_get_string(self, p_data, name): + self.pdll.ssc_data_get_string.restype = ctypes.c_char_p + return self.pdll.ssc_data_get_string(ctypes.c_void_p(p_data), + ctypes.c_char_p(name)) + + def data_get_number(self, p_data, name): + val = ctypes.c_number(0) + self.pdll.ssc_data_get_number(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.byref(val)) + return val.value + + def data_get_array(self,p_data,name): + count = ctypes.c_int() + self.pdll.ssc_data_get_array.restype = ctypes.POINTER(ctypes.c_number) + parr = self.pdll.ssc_data_get_array(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.byref(count)) + arr = parr[0:count.value] # extract all at once + return arr + + def data_get_matrix(self,p_data,name): + nrows = ctypes.c_int() + ncols = ctypes.c_int() + self.pdll.ssc_data_get_matrix.restype = ctypes.POINTER(ctypes.c_number) + parr = self.pdll.ssc_data_get_matrix(ctypes.c_void_p(p_data), + ctypes.c_char_p(name), + ctypes.byref(nrows), + ctypes.byref(ncols)) + idx = 0 + mat = [] + for r in range(nrows.value): + row = [] + for c in range(ncols.value): + row.append( float(parr[idx]) ) + idx = idx + 1 + mat.append(row) + return mat + # don't call data_free() on the result, it's an internal + # pointer inside SSC + + def data_get_table(self,p_data,name): + return self.pdll.ssc_data_get_table(ctypes.c_void_p(p_data), name); + + def module_entry(self,index): + self.pdll.ssc_module_entry.restype = ctypes.c_void_p + return self.pdll.ssc_module_entry(ctypes.c_int(index)) + + def entry_name(self,p_entry): + self.pdll.ssc_entry_name.restype = ctypes.c_char_p + return self.pdll.ssc_entry_name(ctypes.c_void_p(p_entry)) + + def entry_description(self,p_entry): + self.pdll.ssc_entry_description.restype = ctypes.c_char_p + return self.pdll.ssc_entry_description(ctypes.c_void_p(p_entry)) + + def entry_version(self,p_entry): + self.pdll.ssc_entry_version.restype = ctypes.c_int + return self.pdll.ssc_entry_version(ctypes.c_void_p(p_entry)) + + def module_create(self,name): + self.pdll.ssc_module_create.restype = ctypes.c_void_p + return self.pdll.ssc_module_create(ctypes.c_char_p(name)) + + def module_free(self,p_mod): + self.pdll.ssc_module_free(ctypes.c_void_p(p_mod)) + + def module_var_info(self,p_mod,index): + self.pdll.ssc_module_var_info.restype = ctypes.c_void_p + return self.pdll.ssc_module_var_info(ctypes.c_void_p(p_mod), + ctypes.c_int(index)) + + def info_var_type(self, p_inf): + return self.pdll.ssc_info_var_type(ctypes.c_void_p(p_inf)) + + def info_data_type(self, p_inf): + return self.pdll.ssc_info_data_type(ctypes.c_void_p(p_inf)) + + def info_name(self, p_inf): + self.pdll.ssc_info_name.restype = ctypes.c_char_p + return self.pdll.ssc_info_name(ctypes.c_void_p(p_inf)) + + def info_label(self, p_inf): + self.pdll.ssc_info_label.restype = ctypes.c_char_p + return self.pdll.ssc_info_label(ctypes.c_void_p(p_inf)) + + def info_units(self, p_inf): + self.pdll.ssc_info_units.restype = ctypes.c_char_p + return self.pdll.ssc_info_units(ctypes.c_void_p(p_inf)) + + def info_meta(self, p_inf): + self.pdll.ssc_info_meta.restype = ctypes.c_char_p + return self.pdll.ssc_info_meta(ctypes.c_void_p(p_inf)) + + def info_group(self, p_inf): + self.pdll.ssc_info_group.restype = ctypes.c_char_p + return self.pdll.ssc_info_group(ctypes.c_void_p(p_inf)) + + def info_uihint(self, p_inf): + self.pdll.ssc_info_uihint.restype = ctypes.c_char_p + return self.pdll.ssc_info_uihint(ctypes.c_void_p(p_inf)) + + def info_required(self, p_inf): + self.pdll.ssc_info_required.restype = ctypes.c_char_p + return self.pdll.ssc_info_required(ctypes.c_void_p(p_inf)) + + def info_constraints(self, p_inf): + self.pdll.ssc_info_constraints.restype = ctypes.c_char_p + return self.pdll.ssc_info_constraints(ctypes.c_void_p(p_inf)) + + def module_exec(self, p_mod, p_data): + self.pdll.ssc_module_exec.restype = ctypes.c_int + return self.pdll.ssc_module_exec(ctypes.c_void_p(p_mod), + ctypes.c_void_p(p_data)) + + def module_exec_simple_no_thread(self, modname, data): + self.pdll.ssc_module_exec_simple_nothread.restype = ctypes.c_char_p; + return self.pdll.ssc_module_exec_simple_nothread( + ctypes.c_char_p(modname), ctypes.c_void_p(data)); + + def module_log(self, p_mod, index): + log_type = ctypes.c_int() + time = ctypes.c_float() + self.pdll.ssc_module_log.restype = ctypes.c_char_p + return self.pdll.ssc_module_log(ctypes.c_void_p(p_mod), + ctypes.c_int(index), + ctypes.byref(log_type), + ctypes.byref(time)) + + def module_exec_set_print(self, prn): + return self.pdll.ssc_module_exec_set_print(ctypes.c_int(prn)); diff --git a/pvlib/ivtools/fit_cec_model_with_sam.py b/pvlib/ivtools/fit_cec_model_with_sam.py new file mode 100644 index 0000000000..541c822398 --- /dev/null +++ b/pvlib/ivtools/fit_cec_model_with_sam.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu May 2 12:42:24 2019 + +@author: cwhanse +""" + +from PySSC import PySSC + +def fit_cec_model_with_sam(sam_dir, celltype, Vmp, Imp, Voc, Isc, alpha_sc, + beta_voc, gamma_pmp, cells_in_series, temp_ref=25): + ''' + Estimates parameters for the CEC single diode model using SAM SDK. + + Parameters + ---------- + sam_dir : str + Full path to folder containing the SAM file ssc.dll + celltype : str + Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', + 'amorphous' + Vmp : float + Voltage at maximum power point at standard test condition (STC) + Imp : float + Current at maximum power point at STC + Voc : float + Open circuit voltage at STC + Isc : float + Short circuit current at STC + alpha_sc : float + Temperature coefficient of short circuit current at STC, A/C + beta_voc : float + Temperature coefficient of open circuit voltage at STC, V/C + gamma_pmp : float + Temperature coefficient of power at maximum point point at STC, %/C + cells_in_series : int + Number of cells in series + temp_ref : float, default 25 + Reference temperature condition + + Returns + ------- + a_ref : float + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at reference + conditions, in units of V. + + I_L_ref : float + The light-generated current (or photocurrent) at reference conditions, + in amperes. + + I_o_ref : float + The dark or diode reverse saturation current at reference conditions, + in amperes. + + R_sh_ref : float + The shunt resistance at reference conditions, in ohms. + + R_s : float + The series resistance at reference conditions, in ohms. + + Adjust : float + The adjustment to the temperature coefficient for short circuit + current, in percent + ''' + + try: + ssc = PySSC(sam_dir) + except Exception as e: + raise(e) + + data = ssc.data_create() + + ssc.data_set_string(data, b'celltype', celltype.encode('utf-8')) + ssc.data_set_number(data, b'Vmp', Vmp) + ssc.data_set_number(data, b'Imp', Imp) + ssc.data_set_number(data, b'Voc', Voc) + ssc.data_set_number(data, b'Isc', Isc) + ssc.data_set_number(data, b'alpha_isc', alpha_sc) + ssc.data_set_number(data, b'beta_voc', beta_voc) + ssc.data_set_number(data, b'gamma_pmp', gamma_pmp) + ssc.data_set_number(data, b'Nser', cells_in_series) + ssc.data_set_number(data, b'Tref', temp_ref) + + solver = ssc.module_create(b'6parsolve') + ssc.module_exec_set_print(0) + if ssc.module_exec(solver, data) == 0: + print('IV curve fit error') + idx = 1 + msg = ssc.module_log(solver, 0) + while (msg != None): + print(' : ' + msg.decode("utf - 8")) + msg = ssc.module_log(solver, idx) + idx = idx + 1 + ssc.module_free(solver) + a_ref = ssc.data_get_number(data, b'a') + I_L_ref = ssc.data_get_number(data, b'Il') + I_o_ref = ssc.data_get_number(data, b'Io') + R_s = ssc.data_get_number(data, b'Rs') + R_sh_ref = ssc.data_get_number(data, b'Rsh') + Adjust = ssc.data_get_number(data, b'Adj') + + return a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust From 7454e4dffc17878b696375486a06029c2490c854 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 3 May 2019 14:56:21 -0600 Subject: [PATCH 05/98] outline ivcurves --- pvlib/ivtools/ivcurves.py | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pvlib/ivtools/ivcurves.py diff --git a/pvlib/ivtools/ivcurves.py b/pvlib/ivtools/ivcurves.py new file mode 100644 index 0000000000..50fe36b972 --- /dev/null +++ b/pvlib/ivtools/ivcurves.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri May 3 14:16:24 2019 + +@author: cwhanse +""" + +import numpy as np + + +class IVCurves(): + """ + Contains IV curves and methods for fitting models to the curves. + """ + + def __init__(self, data): + IVCurves.ivdata = data + IVCurves.voc = None + + def __repr(self): + pass + + def __print__(self): + pass + + def fit(): + """ + Fit a model to IV curve data. + """ + pass + + +class _IVCurve(): + """ + Contains a single IV curve + """ + + def __init__(self, V, I, Ee, Tc, Voc=None, Isc=None, Vmp=None, Imp=None): + self.V = V + self.I = I + self.Ee = Ee + self.Tc = Tc + if Voc is None: + self.Voc = V[-1] + if Isc is None: + self.Isc = I[0] + if Vmp is None: + self.Vmp, self.Imp = find_max_power(V, I) + + +def find_max_power(V, I): + """ Finds V, I pair where V*I is maximum + + Parameters + ---------- + V : numeric + I : numeric + + Returns + ------- + Vmax, Imax : values from V and I where V*I is maximum + """ + idx = np.argmax(V * I) + return V[idx], I[idx] From 9c5e1a9c99b948b8bcef32e50a3325d2c91142bd Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 6 May 2019 15:36:14 -0600 Subject: [PATCH 06/98] use nrel-pysam --- pvlib/ivtools/PySSC.py | 248 ------------------------ pvlib/ivtools/fit_cec_model_with_sam.py | 4 +- 2 files changed, 2 insertions(+), 250 deletions(-) delete mode 100644 pvlib/ivtools/PySSC.py diff --git a/pvlib/ivtools/PySSC.py b/pvlib/ivtools/PySSC.py deleted file mode 100644 index c638a11b74..0000000000 --- a/pvlib/ivtools/PySSC.py +++ /dev/null @@ -1,248 +0,0 @@ -#Created with SAM version 2018.11.11 -# Modified by C. W. Hansen for PEP8 compliance -import sys -import os -import ctypes -ctypes.c_number = ctypes.c_float # must be c_double or c_float depending on how defined in sscapi.h -class PySSC(): - def __init__(self, sam_dir): - if sys.platform in ['win32', 'cygwin']: - self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.dll")) - elif sys.platform == 'darwin': - self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.dylib")) - elif sys.platform == 'linux2': - self.pdll = ctypes.CDLL(os.path.join(sam_dir, "ssc.so")) # instead of relative path, require user to have on LD_LIBRARY_PATH - else: - print ('Platform not supported ', sys.platform) - INVALID=0 - STRING=1 - NUMBER=2 - ARRAY=3 - MATRIX=4 - INPUT=1 - OUTPUT=2 - INOUT=3 - - def version(self): - self.pdll.ssc_version.restype = ctypes.c_int - return self.pdll.ssc_version() - - def build_info(self): - self.pdll.ssc_build_info.restype = ctypes.c_char_p - return self.pdll.ssc_build_info() - - def data_create(self): - self.pdll.ssc_data_create.restype = ctypes.c_void_p - return self.pdll.ssc_data_create() - - def data_free(self, p_data): - self.pdll.ssc_data_free(ctypes.c_void_p(p_data)) - - def data_clear(self, p_data): - self.pdll.ssc_data_clear(ctypes.c_void_p(p_data)) - - def data_unassign(self, p_data, name): - self.pdll.ssc_data_unassign(ctypes.c_void_p(p_data), - ctypes.c_char_p(name)) - - def data_query(self, p_data, name): - self.pdll.ssc_data_query.restype = ctypes.c_int - return self.pdll.ssc_data_query(ctypes.c_void_p(p_data), - ctypes.c_char_p(name)) - - def data_first(self, p_data): - self.pdll.ssc_data_first.restype = ctypes.c_char_p - return self.pdll.ssc_data_first(ctypes.c_void_p(p_data)) - - def data_next(self, p_data): - self.pdll.ssc_data_next.restype = ctypes.c_char_p - return self.pdll.ssc_data_next(ctypes.c_void_p(p_data)) - - def data_set_string(self, p_data, name, value): - self.pdll.ssc_data_set_string(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.c_char_p(value)) - - def data_set_number(self, p_data, name, value): - self.pdll.ssc_data_set_number(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.c_number(value)) - - def data_set_array(self,p_data,name,parr): - count = len(parr) - arr = (ctypes.c_number * count)() - arr[:] = parr # set all at once instead of looping - return self.pdll.ssc_data_set_array(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.pointer(arr), - ctypes.c_int(count)) - - def data_set_array_from_csv(self, p_data, name, fn): - f = open(fn, 'rb'); - data = []; - for line in f : - data.extend([n for n in map(float, line.split(b','))]) - f.close() - return self.data_set_array(p_data, name, data) - - def data_set_matrix(self,p_data,name,mat): - nrows = len(mat) - ncols = len(mat[0]) - size = nrows * ncols - arr = (ctypes.c_number * size)() - idx=0 - for r in range(nrows): - for c in range(ncols): - arr[idx] = ctypes.c_number(mat[r][c]) - idx += 1 - return self.pdll.ssc_data_set_matrix(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.pointer(arr), - ctypes.c_int(nrows), - ctypes.c_int(ncols)) - - def data_set_matrix_from_csv(self, p_data, name, fn): - f = open(fn, 'rb'); - data = []; - for line in f : - lst = ([n for n in map(float, line.split(b','))]) - data.append(lst); - f.close(); - return self.data_set_matrix(p_data, name, data); - - def data_set_table(self,p_data,name,tab): - return self.pdll.ssc_data_set_table(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.c_void_p(tab)); - - def data_get_string(self, p_data, name): - self.pdll.ssc_data_get_string.restype = ctypes.c_char_p - return self.pdll.ssc_data_get_string(ctypes.c_void_p(p_data), - ctypes.c_char_p(name)) - - def data_get_number(self, p_data, name): - val = ctypes.c_number(0) - self.pdll.ssc_data_get_number(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.byref(val)) - return val.value - - def data_get_array(self,p_data,name): - count = ctypes.c_int() - self.pdll.ssc_data_get_array.restype = ctypes.POINTER(ctypes.c_number) - parr = self.pdll.ssc_data_get_array(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.byref(count)) - arr = parr[0:count.value] # extract all at once - return arr - - def data_get_matrix(self,p_data,name): - nrows = ctypes.c_int() - ncols = ctypes.c_int() - self.pdll.ssc_data_get_matrix.restype = ctypes.POINTER(ctypes.c_number) - parr = self.pdll.ssc_data_get_matrix(ctypes.c_void_p(p_data), - ctypes.c_char_p(name), - ctypes.byref(nrows), - ctypes.byref(ncols)) - idx = 0 - mat = [] - for r in range(nrows.value): - row = [] - for c in range(ncols.value): - row.append( float(parr[idx]) ) - idx = idx + 1 - mat.append(row) - return mat - # don't call data_free() on the result, it's an internal - # pointer inside SSC - - def data_get_table(self,p_data,name): - return self.pdll.ssc_data_get_table(ctypes.c_void_p(p_data), name); - - def module_entry(self,index): - self.pdll.ssc_module_entry.restype = ctypes.c_void_p - return self.pdll.ssc_module_entry(ctypes.c_int(index)) - - def entry_name(self,p_entry): - self.pdll.ssc_entry_name.restype = ctypes.c_char_p - return self.pdll.ssc_entry_name(ctypes.c_void_p(p_entry)) - - def entry_description(self,p_entry): - self.pdll.ssc_entry_description.restype = ctypes.c_char_p - return self.pdll.ssc_entry_description(ctypes.c_void_p(p_entry)) - - def entry_version(self,p_entry): - self.pdll.ssc_entry_version.restype = ctypes.c_int - return self.pdll.ssc_entry_version(ctypes.c_void_p(p_entry)) - - def module_create(self,name): - self.pdll.ssc_module_create.restype = ctypes.c_void_p - return self.pdll.ssc_module_create(ctypes.c_char_p(name)) - - def module_free(self,p_mod): - self.pdll.ssc_module_free(ctypes.c_void_p(p_mod)) - - def module_var_info(self,p_mod,index): - self.pdll.ssc_module_var_info.restype = ctypes.c_void_p - return self.pdll.ssc_module_var_info(ctypes.c_void_p(p_mod), - ctypes.c_int(index)) - - def info_var_type(self, p_inf): - return self.pdll.ssc_info_var_type(ctypes.c_void_p(p_inf)) - - def info_data_type(self, p_inf): - return self.pdll.ssc_info_data_type(ctypes.c_void_p(p_inf)) - - def info_name(self, p_inf): - self.pdll.ssc_info_name.restype = ctypes.c_char_p - return self.pdll.ssc_info_name(ctypes.c_void_p(p_inf)) - - def info_label(self, p_inf): - self.pdll.ssc_info_label.restype = ctypes.c_char_p - return self.pdll.ssc_info_label(ctypes.c_void_p(p_inf)) - - def info_units(self, p_inf): - self.pdll.ssc_info_units.restype = ctypes.c_char_p - return self.pdll.ssc_info_units(ctypes.c_void_p(p_inf)) - - def info_meta(self, p_inf): - self.pdll.ssc_info_meta.restype = ctypes.c_char_p - return self.pdll.ssc_info_meta(ctypes.c_void_p(p_inf)) - - def info_group(self, p_inf): - self.pdll.ssc_info_group.restype = ctypes.c_char_p - return self.pdll.ssc_info_group(ctypes.c_void_p(p_inf)) - - def info_uihint(self, p_inf): - self.pdll.ssc_info_uihint.restype = ctypes.c_char_p - return self.pdll.ssc_info_uihint(ctypes.c_void_p(p_inf)) - - def info_required(self, p_inf): - self.pdll.ssc_info_required.restype = ctypes.c_char_p - return self.pdll.ssc_info_required(ctypes.c_void_p(p_inf)) - - def info_constraints(self, p_inf): - self.pdll.ssc_info_constraints.restype = ctypes.c_char_p - return self.pdll.ssc_info_constraints(ctypes.c_void_p(p_inf)) - - def module_exec(self, p_mod, p_data): - self.pdll.ssc_module_exec.restype = ctypes.c_int - return self.pdll.ssc_module_exec(ctypes.c_void_p(p_mod), - ctypes.c_void_p(p_data)) - - def module_exec_simple_no_thread(self, modname, data): - self.pdll.ssc_module_exec_simple_nothread.restype = ctypes.c_char_p; - return self.pdll.ssc_module_exec_simple_nothread( - ctypes.c_char_p(modname), ctypes.c_void_p(data)); - - def module_log(self, p_mod, index): - log_type = ctypes.c_int() - time = ctypes.c_float() - self.pdll.ssc_module_log.restype = ctypes.c_char_p - return self.pdll.ssc_module_log(ctypes.c_void_p(p_mod), - ctypes.c_int(index), - ctypes.byref(log_type), - ctypes.byref(time)) - - def module_exec_set_print(self, prn): - return self.pdll.ssc_module_exec_set_print(ctypes.c_int(prn)); diff --git a/pvlib/ivtools/fit_cec_model_with_sam.py b/pvlib/ivtools/fit_cec_model_with_sam.py index 541c822398..4613996591 100644 --- a/pvlib/ivtools/fit_cec_model_with_sam.py +++ b/pvlib/ivtools/fit_cec_model_with_sam.py @@ -5,7 +5,7 @@ @author: cwhanse """ -from PySSC import PySSC +from PySAM.PySSC import PySSC def fit_cec_model_with_sam(sam_dir, celltype, Vmp, Imp, Voc, Isc, alpha_sc, beta_voc, gamma_pmp, cells_in_series, temp_ref=25): @@ -65,7 +65,7 @@ def fit_cec_model_with_sam(sam_dir, celltype, Vmp, Imp, Voc, Isc, alpha_sc, ''' try: - ssc = PySSC(sam_dir) + ssc = PySSC.PySSC() except Exception as e: raise(e) From 593f3555b2b90327d73cc7bde81776897259ba48 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 7 May 2019 08:35:02 -0600 Subject: [PATCH 07/98] remove sam_dir --- pvlib/ivtools/fit_cec_model_with_sam.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pvlib/ivtools/fit_cec_model_with_sam.py b/pvlib/ivtools/fit_cec_model_with_sam.py index 4613996591..b474c3e77c 100644 --- a/pvlib/ivtools/fit_cec_model_with_sam.py +++ b/pvlib/ivtools/fit_cec_model_with_sam.py @@ -7,15 +7,13 @@ from PySAM.PySSC import PySSC -def fit_cec_model_with_sam(sam_dir, celltype, Vmp, Imp, Voc, Isc, alpha_sc, - beta_voc, gamma_pmp, cells_in_series, temp_ref=25): +def fit_cec_model_with_sam(celltype, Vmp, Imp, Voc, Isc, alpha_sc, beta_voc, + gamma_pmp, cells_in_series, temp_ref=25): ''' - Estimates parameters for the CEC single diode model using SAM SDK. + Estimates parameters for the CEC single diode model using the SAM SDK. Parameters ---------- - sam_dir : str - Full path to folder containing the SAM file ssc.dll celltype : str Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', 'amorphous' From 4ab1fed955f60b6f49b149142c17c4ac94ad2bc7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 7 May 2019 08:56:00 -0600 Subject: [PATCH 08/98] add Sandia single curve fit --- pvlib/ivtools/fit_sde_sandia.py | 118 ++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 pvlib/ivtools/fit_sde_sandia.py diff --git a/pvlib/ivtools/fit_sde_sandia.py b/pvlib/ivtools/fit_sde_sandia.py new file mode 100644 index 0000000000..b16c7a14ca --- /dev/null +++ b/pvlib/ivtools/fit_sde_sandia.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 29 10:34:10 2019 + +@author: cwhanse +""" + +import numpy as np + + +def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): + """ Fits the single diode equation to an IV curve. + + If fitting fails, returns NaN in each parameter. + + Parameters + ---------- + V : numeric + Voltage at each point on the IV curve, from 0 to Voc + + I : numeric + Current at each point on the IV curve, from Isc to 0 + + Voc : float + Open circuit voltage + + Isc : float + Short circuit current + + Vmp : float + Voltage at maximum power point + + Imp : float + Current at maximum power point + + vlim : float, default 0.2 + defines linear portion of IV curve i.e. V <= vlim * Voc + + ilim : float, default 0.1 + defines exponential portion of IV curve i.e. I > ilim * Isc + + Returns + ------- + IL : float + photocurrent, A + + I0 : float + dark (saturation) current, A + + Rs : float + series resistance, ohm + + Rsh : float + shunt (parallel) resistance, ohm + + nNsVth : float + product of diode (ideality) factor n (unitless) x number of + cells in series Ns (unitless) x cell thermal voltage Vth (V), V + + References + ---------- + [1] C. B. Jones, C. W. Hansen, Single Diode Parameter Extraction from + In-Field Photovoltaic I-V Curves on a Single Board Computer, 46th IEEE + Photovoltaic Specialist Conference, Chicago, IL, 2019 + """ + # Find intercept and slope of linear portion of IV curve. + # Start with V < vlim * Voc, extend by adding points until slope is + # acceptable + beta = [np.nan for i in range(5)] + beta[0] = np.nan + beta[1] = np.nan + idx = len(V <= vlim * Voc) + while np.isnan(beta[1]) and (idx<=len(V)): + try: + p = np.polyfit(V[:idx], I[:idx], deg=1) + if p[1] < 0: + beta[0] = p[0] + beta[1] = -p[1] # sign change to get positive parameter value + except: + pass + if np.isnan(beta[1]): + idx += 1 + + if not np.isnan(beta[0]): + # Find parameters from exponential portion of IV curve + Y = beta[0] - beta[1] * V - I + X = np.array([V, I]) + idx = len(Y <= ilim * Isc) + try: + p = np.linalg.lstsq(X, Y) + beta[3] = p[1] + beta[4] = p[2] + except: + pass + + if not any([np.isnan(beta[i]) for i in [0, 1, 3, 4]]): + # calculate parameters + nNsVth = 1.0 / beta[3] + Rs = beta[4] / beta[3] + Gp = beta[1] / (1.0 - Rs * beta[1]) + Rsh = 1.0 / Gp + IL = (1 + Gp * Rs) * beta[0] + # calculate I0 + I0_Vmp = _calc_I0(IL, Imp, Vmp, Gp, Rs, beta[3]) + I0_Voc = _calc_I0(IL, 0, Voc, Gp, Rs, beta[3]) + if (I0_Vmp > 0) and (I0_Voc > 0): + I0 = 0.5 * (I0_Vmp + I0_Voc) + elif (I0_Vmp > 0): + I0 = I0_Vmp + elif (I0_Voc > 0): + I0 = I0_Voc + else: + I0 = np.nan + return IL, I0, Rs, Rsh, nNsVth + + +def _calc_I0(IL, I, V, Gp, Rs, beta3): + return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) \ No newline at end of file From 8a8890f87cf80501db1ff8bfcc77a8161275de72 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 9 May 2019 13:05:57 -0600 Subject: [PATCH 09/98] complete function --- pvlib/ivtools/fit_sde_sandia.py | 39 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/pvlib/ivtools/fit_sde_sandia.py b/pvlib/ivtools/fit_sde_sandia.py index b16c7a14ca..e889e1e565 100644 --- a/pvlib/ivtools/fit_sde_sandia.py +++ b/pvlib/ivtools/fit_sde_sandia.py @@ -67,15 +67,16 @@ def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): # Start with V < vlim * Voc, extend by adding points until slope is # acceptable beta = [np.nan for i in range(5)] - beta[0] = np.nan - beta[1] = np.nan - idx = len(V <= vlim * Voc) + # get index of largest voltage less than/equal to limit + idx = _max_index(V, vlim * Voc) while np.isnan(beta[1]) and (idx<=len(V)): try: - p = np.polyfit(V[:idx], I[:idx], deg=1) - if p[1] < 0: - beta[0] = p[0] - beta[1] = -p[1] # sign change to get positive parameter value + coef = np.polyfit(V[:idx], I[:idx], deg=1) + if coef[0] < 0: + # intercept term + beta[0] = coef[1].item() + # sign change of slope to get positive parameter value + beta[1] = -coef[0].item() except: pass if np.isnan(beta[1]): @@ -84,12 +85,13 @@ def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): if not np.isnan(beta[0]): # Find parameters from exponential portion of IV curve Y = beta[0] - beta[1] * V - I - X = np.array([V, I]) - idx = len(Y <= ilim * Isc) + X = np.array([np.ones_like(V), V, I]).T + idx = _min_index(Y, ilim * Isc) try: - p = np.linalg.lstsq(X, Y) - beta[3] = p[1] - beta[4] = p[2] + result = np.linalg.lstsq(X[idx:,], np.log(Y[idx:])) + coef = result[0] + beta[3] = coef[1].item() + beta[4] = coef[2].item() except: pass @@ -111,8 +113,19 @@ def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): I0 = I0_Voc else: I0 = np.nan + else: + IL = I0 = Rs = Rsh = nNsVth = np.nan + return IL, I0, Rs, Rsh, nNsVth def _calc_I0(IL, I, V, Gp, Rs, beta3): - return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) \ No newline at end of file + return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) + +def _max_index(x, xlim): + """ Finds maximum index of value of x <= xlim """ + return int(np.argwhere(x <= xlim)[-1]) + +def _min_index(x, xlim): + """ Finds minimum index of value of x > xlim """ + return int(np.argwhere(x > xlim)[0]) From b72b0b4ebe4154e2d12165c83e8930a3cbc1b3af Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 10 May 2019 09:25:16 -0600 Subject: [PATCH 10/98] add test, move code to ivtools.py --- pvlib/ivtools.py | 228 ++++++++++++++++++++++++++++++++ pvlib/ivtools/fit_sde_sandia.py | 8 +- pvlib/test/test_ivtools.py | 30 +++++ 3 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 pvlib/ivtools.py create mode 100644 pvlib/test/test_ivtools.py diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py new file mode 100644 index 0000000000..4c6d275def --- /dev/null +++ b/pvlib/ivtools.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Mar 29 10:34:10 2019 + +@author: cwhanse +""" + +import numpy as np +from PySAM import PySSC + + +def fit_cec_with_sam(celltype, Vmp, Imp, Voc, Isc, alpha_sc, beta_voc, + gamma_pmp, cells_in_series, temp_ref=25): + ''' + Estimates parameters for the CEC single diode model using the SAM SDK. + + Parameters + ---------- + celltype : str + Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', + 'amorphous' + Vmp : float + Voltage at maximum power point at standard test condition (STC) + Imp : float + Current at maximum power point at STC + Voc : float + Open circuit voltage at STC + Isc : float + Short circuit current at STC + alpha_sc : float + Temperature coefficient of short circuit current at STC, A/C + beta_voc : float + Temperature coefficient of open circuit voltage at STC, V/C + gamma_pmp : float + Temperature coefficient of power at maximum point point at STC, %/C + cells_in_series : int + Number of cells in series + temp_ref : float, default 25 + Reference temperature condition + + Returns + ------- + a_ref : float + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at reference + conditions, in units of V. + + I_L_ref : float + The light-generated current (or photocurrent) at reference conditions, + in amperes. + + I_o_ref : float + The dark or diode reverse saturation current at reference conditions, + in amperes. + + R_sh_ref : float + The shunt resistance at reference conditions, in ohms. + + R_s : float + The series resistance at reference conditions, in ohms. + + Adjust : float + The adjustment to the temperature coefficient for short circuit + current, in percent + ''' + + try: + ssc = PySSC.PySSC() + except Exception as e: + raise(e) + + data = ssc.data_create() + + ssc.data_set_string(data, b'celltype', celltype.encode('utf-8')) + ssc.data_set_number(data, b'Vmp', Vmp) + ssc.data_set_number(data, b'Imp', Imp) + ssc.data_set_number(data, b'Voc', Voc) + ssc.data_set_number(data, b'Isc', Isc) + ssc.data_set_number(data, b'alpha_isc', alpha_sc) + ssc.data_set_number(data, b'beta_voc', beta_voc) + ssc.data_set_number(data, b'gamma_pmp', gamma_pmp) + ssc.data_set_number(data, b'Nser', cells_in_series) + ssc.data_set_number(data, b'Tref', temp_ref) + + solver = ssc.module_create(b'6parsolve') + ssc.module_exec_set_print(0) + if ssc.module_exec(solver, data) == 0: + print('IV curve fit error') + idx = 1 + msg = ssc.module_log(solver, 0) + while (msg != None): + print(' : ' + msg.decode("utf - 8")) + msg = ssc.module_log(solver, idx) + idx = idx + 1 + ssc.module_free(solver) + a_ref = ssc.data_get_number(data, b'a') + I_L_ref = ssc.data_get_number(data, b'Il') + I_o_ref = ssc.data_get_number(data, b'Io') + R_s = ssc.data_get_number(data, b'Rs') + R_sh_ref = ssc.data_get_number(data, b'Rsh') + Adjust = ssc.data_get_number(data, b'Adj') + + return I_L_ref, I_o_ref, R_sh_ref, R_s, a_ref, Adjust + + +def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): + """ Fits the single diode equation to an IV curve. + + If fitting fails, returns NaN in each parameter. + + Parameters + ---------- + V : numeric + Voltage at each point on the IV curve, from 0 to Voc + + I : numeric + Current at each point on the IV curve, from Isc to 0 + + Voc : float + Open circuit voltage + + Isc : float + Short circuit current + + Vmp : float + Voltage at maximum power point + + Imp : float + Current at maximum power point + + vlim : float, default 0.2 + defines linear portion of IV curve i.e. V <= vlim * Voc + + ilim : float, default 0.1 + defines exponential portion of IV curve i.e. I > ilim * Isc + + Returns + ------- + IL : float + photocurrent, A + + I0 : float + dark (saturation) current, A + + Rsh : float + shunt (parallel) resistance, ohm + + Rs : float + series resistance, ohm + + nNsVth : float + product of diode (ideality) factor n (unitless) x number of + cells in series Ns (unitless) x cell thermal voltage Vth (V), V + + References + ---------- + [1] C. B. Jones, C. W. Hansen, Single Diode Parameter Extraction from + In-Field Photovoltaic I-V Curves on a Single Board Computer, 46th IEEE + Photovoltaic Specialist Conference, Chicago, IL, 2019 + """ + # Find intercept and slope of linear portion of IV curve. + # Start with V < vlim * Voc, extend by adding points until slope is + # acceptable + beta = [np.nan for i in range(5)] + # get index of largest voltage less than/equal to limit + idx = _max_index(V, vlim * Voc) + while np.isnan(beta[1]) and (idx<=len(V)): + try: + coef = np.polyfit(V[:idx], I[:idx], deg=1) + if coef[0] < 0: + # intercept term + beta[0] = coef[1].item() + # sign change of slope to get positive parameter value + beta[1] = -coef[0].item() + except: + pass + if np.isnan(beta[1]): + idx += 1 + + if not np.isnan(beta[0]): + # Find parameters from exponential portion of IV curve + Y = beta[0] - beta[1] * V - I + X = np.array([np.ones_like(V), V, I]).T + idx = _min_index(Y, ilim * Isc) + try: + result = np.linalg.lstsq(X[idx:,], np.log(Y[idx:])) + coef = result[0] + beta[3] = coef[1].item() + beta[4] = coef[2].item() + except: + pass + + if not any([np.isnan(beta[i]) for i in [0, 1, 3, 4]]): + # calculate parameters + nNsVth = 1.0 / beta[3] + Rs = beta[4] / beta[3] + Gp = beta[1] / (1.0 - Rs * beta[1]) + Rsh = 1.0 / Gp + IL = (1 + Gp * Rs) * beta[0] + # calculate I0 + I0_Vmp = _calc_I0(IL, Imp, Vmp, Gp, Rs, beta[3]) + I0_Voc = _calc_I0(IL, 0, Voc, Gp, Rs, beta[3]) + if (I0_Vmp > 0) and (I0_Voc > 0): + I0 = 0.5 * (I0_Vmp + I0_Voc) + elif (I0_Vmp > 0): + I0 = I0_Vmp + elif (I0_Voc > 0): + I0 = I0_Voc + else: + I0 = np.nan + else: + IL = I0 = Rs = Rsh = nNsVth = np.nan + + return IL, I0, Rsh, Rs, nNsVth + + +def _calc_I0(IL, I, V, Gp, Rs, beta3): + return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) + + +def _max_index(x, xlim): + """ Finds maximum index of value of x <= xlim """ + return int(np.argwhere(x <= xlim)[-1]) + + +def _min_index(x, xlim): + """ Finds minimum index of value of x > xlim """ + return int(np.argwhere(x > xlim)[0]) diff --git a/pvlib/ivtools/fit_sde_sandia.py b/pvlib/ivtools/fit_sde_sandia.py index e889e1e565..9515c954e6 100644 --- a/pvlib/ivtools/fit_sde_sandia.py +++ b/pvlib/ivtools/fit_sde_sandia.py @@ -47,12 +47,12 @@ def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): I0 : float dark (saturation) current, A - Rs : float - series resistance, ohm - Rsh : float shunt (parallel) resistance, ohm + Rs : float + series resistance, ohm + nNsVth : float product of diode (ideality) factor n (unitless) x number of cells in series Ns (unitless) x cell thermal voltage Vth (V), V @@ -116,7 +116,7 @@ def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): else: IL = I0 = Rs = Rsh = nNsVth = np.nan - return IL, I0, Rs, Rsh, nNsVth + return IL, I0, Rsh, Rs, nNsVth def _calc_I0(IL, I, V, Gp, Rs, beta3): diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py new file mode 100644 index 0000000000..71397e970a --- /dev/null +++ b/pvlib/test/test_ivtools.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu May 9 10:51:15 2019 + +@author: cwhanse +""" + +import numpy as np +from pvlib import pvsystem +from pvlib.ivtools import fit_sde_sandia + + +def get_test_iv_params(): + return {'IL': 8.0, 'I0': 5e-10, 'Rsh': 1000, 'Rs': 0.2, 'nNsVth': 1.61864} + + +def test_fit_sde_sandia(): + test_params = get_test_iv_params() + testcurve = pvsystem.singlediode(photocurrent=test_params['IL'], + saturation_current=test_params['I0'], + resistance_shunt=test_params['Rsh'], + resistance_series=test_params['Rs'], + nNsVth=test_params['nNsVth'], + ivcurve_pnts=300) + expected = tuple(test_params[k] for k in ['IL', 'I0', 'Rsh', 'Rs', + 'nNsVth']) + result = fit_sde_sandia(V=testcurve['v'], I=testcurve['i'], + Voc=testcurve['v_oc'], Isc=testcurve['i_sc'], + Vmp=testcurve['v_mp'], Imp=testcurve['i_mp']) + assert np.allclose(result, expected, rtol=5e-5) From 921aea89c77d25462dfdee9e35acd0d58140b30b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 10 May 2019 09:25:40 -0600 Subject: [PATCH 11/98] remove single files --- pvlib/ivtools/fit_cec_model_with_sam.py | 101 ------------------ pvlib/ivtools/fit_sde_sandia.py | 131 ------------------------ 2 files changed, 232 deletions(-) delete mode 100644 pvlib/ivtools/fit_cec_model_with_sam.py delete mode 100644 pvlib/ivtools/fit_sde_sandia.py diff --git a/pvlib/ivtools/fit_cec_model_with_sam.py b/pvlib/ivtools/fit_cec_model_with_sam.py deleted file mode 100644 index b474c3e77c..0000000000 --- a/pvlib/ivtools/fit_cec_model_with_sam.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu May 2 12:42:24 2019 - -@author: cwhanse -""" - -from PySAM.PySSC import PySSC - -def fit_cec_model_with_sam(celltype, Vmp, Imp, Voc, Isc, alpha_sc, beta_voc, - gamma_pmp, cells_in_series, temp_ref=25): - ''' - Estimates parameters for the CEC single diode model using the SAM SDK. - - Parameters - ---------- - celltype : str - Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', - 'amorphous' - Vmp : float - Voltage at maximum power point at standard test condition (STC) - Imp : float - Current at maximum power point at STC - Voc : float - Open circuit voltage at STC - Isc : float - Short circuit current at STC - alpha_sc : float - Temperature coefficient of short circuit current at STC, A/C - beta_voc : float - Temperature coefficient of open circuit voltage at STC, V/C - gamma_pmp : float - Temperature coefficient of power at maximum point point at STC, %/C - cells_in_series : int - Number of cells in series - temp_ref : float, default 25 - Reference temperature condition - - Returns - ------- - a_ref : float - The product of the usual diode ideality factor (n, unitless), - number of cells in series (Ns), and cell thermal voltage at reference - conditions, in units of V. - - I_L_ref : float - The light-generated current (or photocurrent) at reference conditions, - in amperes. - - I_o_ref : float - The dark or diode reverse saturation current at reference conditions, - in amperes. - - R_sh_ref : float - The shunt resistance at reference conditions, in ohms. - - R_s : float - The series resistance at reference conditions, in ohms. - - Adjust : float - The adjustment to the temperature coefficient for short circuit - current, in percent - ''' - - try: - ssc = PySSC.PySSC() - except Exception as e: - raise(e) - - data = ssc.data_create() - - ssc.data_set_string(data, b'celltype', celltype.encode('utf-8')) - ssc.data_set_number(data, b'Vmp', Vmp) - ssc.data_set_number(data, b'Imp', Imp) - ssc.data_set_number(data, b'Voc', Voc) - ssc.data_set_number(data, b'Isc', Isc) - ssc.data_set_number(data, b'alpha_isc', alpha_sc) - ssc.data_set_number(data, b'beta_voc', beta_voc) - ssc.data_set_number(data, b'gamma_pmp', gamma_pmp) - ssc.data_set_number(data, b'Nser', cells_in_series) - ssc.data_set_number(data, b'Tref', temp_ref) - - solver = ssc.module_create(b'6parsolve') - ssc.module_exec_set_print(0) - if ssc.module_exec(solver, data) == 0: - print('IV curve fit error') - idx = 1 - msg = ssc.module_log(solver, 0) - while (msg != None): - print(' : ' + msg.decode("utf - 8")) - msg = ssc.module_log(solver, idx) - idx = idx + 1 - ssc.module_free(solver) - a_ref = ssc.data_get_number(data, b'a') - I_L_ref = ssc.data_get_number(data, b'Il') - I_o_ref = ssc.data_get_number(data, b'Io') - R_s = ssc.data_get_number(data, b'Rs') - R_sh_ref = ssc.data_get_number(data, b'Rsh') - Adjust = ssc.data_get_number(data, b'Adj') - - return a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust diff --git a/pvlib/ivtools/fit_sde_sandia.py b/pvlib/ivtools/fit_sde_sandia.py deleted file mode 100644 index 9515c954e6..0000000000 --- a/pvlib/ivtools/fit_sde_sandia.py +++ /dev/null @@ -1,131 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 29 10:34:10 2019 - -@author: cwhanse -""" - -import numpy as np - - -def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): - """ Fits the single diode equation to an IV curve. - - If fitting fails, returns NaN in each parameter. - - Parameters - ---------- - V : numeric - Voltage at each point on the IV curve, from 0 to Voc - - I : numeric - Current at each point on the IV curve, from Isc to 0 - - Voc : float - Open circuit voltage - - Isc : float - Short circuit current - - Vmp : float - Voltage at maximum power point - - Imp : float - Current at maximum power point - - vlim : float, default 0.2 - defines linear portion of IV curve i.e. V <= vlim * Voc - - ilim : float, default 0.1 - defines exponential portion of IV curve i.e. I > ilim * Isc - - Returns - ------- - IL : float - photocurrent, A - - I0 : float - dark (saturation) current, A - - Rsh : float - shunt (parallel) resistance, ohm - - Rs : float - series resistance, ohm - - nNsVth : float - product of diode (ideality) factor n (unitless) x number of - cells in series Ns (unitless) x cell thermal voltage Vth (V), V - - References - ---------- - [1] C. B. Jones, C. W. Hansen, Single Diode Parameter Extraction from - In-Field Photovoltaic I-V Curves on a Single Board Computer, 46th IEEE - Photovoltaic Specialist Conference, Chicago, IL, 2019 - """ - # Find intercept and slope of linear portion of IV curve. - # Start with V < vlim * Voc, extend by adding points until slope is - # acceptable - beta = [np.nan for i in range(5)] - # get index of largest voltage less than/equal to limit - idx = _max_index(V, vlim * Voc) - while np.isnan(beta[1]) and (idx<=len(V)): - try: - coef = np.polyfit(V[:idx], I[:idx], deg=1) - if coef[0] < 0: - # intercept term - beta[0] = coef[1].item() - # sign change of slope to get positive parameter value - beta[1] = -coef[0].item() - except: - pass - if np.isnan(beta[1]): - idx += 1 - - if not np.isnan(beta[0]): - # Find parameters from exponential portion of IV curve - Y = beta[0] - beta[1] * V - I - X = np.array([np.ones_like(V), V, I]).T - idx = _min_index(Y, ilim * Isc) - try: - result = np.linalg.lstsq(X[idx:,], np.log(Y[idx:])) - coef = result[0] - beta[3] = coef[1].item() - beta[4] = coef[2].item() - except: - pass - - if not any([np.isnan(beta[i]) for i in [0, 1, 3, 4]]): - # calculate parameters - nNsVth = 1.0 / beta[3] - Rs = beta[4] / beta[3] - Gp = beta[1] / (1.0 - Rs * beta[1]) - Rsh = 1.0 / Gp - IL = (1 + Gp * Rs) * beta[0] - # calculate I0 - I0_Vmp = _calc_I0(IL, Imp, Vmp, Gp, Rs, beta[3]) - I0_Voc = _calc_I0(IL, 0, Voc, Gp, Rs, beta[3]) - if (I0_Vmp > 0) and (I0_Voc > 0): - I0 = 0.5 * (I0_Vmp + I0_Voc) - elif (I0_Vmp > 0): - I0 = I0_Vmp - elif (I0_Voc > 0): - I0 = I0_Voc - else: - I0 = np.nan - else: - IL = I0 = Rs = Rsh = nNsVth = np.nan - - return IL, I0, Rsh, Rs, nNsVth - - -def _calc_I0(IL, I, V, Gp, Rs, beta3): - return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) - -def _max_index(x, xlim): - """ Finds maximum index of value of x <= xlim """ - return int(np.argwhere(x <= xlim)[-1]) - -def _min_index(x, xlim): - """ Finds minimum index of value of x > xlim """ - return int(np.argwhere(x > xlim)[0]) From 3371830430031aed01f17c8ab4622ef7d240d228 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 12 Jul 2019 08:59:28 -0600 Subject: [PATCH 12/98] fix mock --- pvlib/test/test_celltemp.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index c6d33b2c10..b7fbc57128 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -52,7 +52,7 @@ def test_PVSystem_sapm_celltemp(mocker): racking_model = 'roof_mount_cell_glassback' system = pvsystem.PVSystem(racking_model=racking_model) - mocker.spy(pvsystem, 'sapm_celltemp') + mocker.spy(celltemp, 'sapm') temps = 25 irrads = 1000 winds = 1 @@ -112,12 +112,12 @@ def test_PVSystem_pvsyst_celltemp(mocker): module_parameters['eta_m'] = eta_m system = pvsystem.PVSystem(racking_model=racking_model, module_parameters=module_parameters) - mocker.spy(pvsystem, 'pvsyst_celltemp') + mocker.spy(celltemp, 'pvsyst') irrad = 800 temp = 45 wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) - pvsystem.pvsyst_celltemp.assert_called_once_with( + celltemp.pvsyst.assert_called_once_with( irrad, temp, wind, eta_m, alpha_absorption, racking_model) assert isinstance(out, float) assert out < 90 and out > 70 From 3baa037a8a6a8538b2a2bbab4dd510a7dc9e5fe5 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 12 Jul 2019 15:22:35 -0600 Subject: [PATCH 13/98] add celltemp.pvsyst as a ModelChain option, change order of arguments in celltemp.samp --- pvlib/celltemp.py | 8 ++++---- pvlib/modelchain.py | 16 ++++++++++++---- pvlib/pvsystem.py | 8 ++++---- pvlib/test/test_celltemp.py | 14 +++++++------- pvlib/test/test_modelchain.py | 14 ++++++++++---- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index cd10054e7d..88184b2c48 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -20,7 +20,7 @@ } -def sapm(poa_global, wind_speed, temp_air, model='open_rack_cell_glassback'): +def sapm(poa_global, temp_air, wind_speed, model='open_rack_cell_glassback'): ''' Estimate cell and module temperatures per the Sandia PV Array Performance Model (SAPM, SAND2004-3535), from the incident @@ -32,12 +32,12 @@ def sapm(poa_global, wind_speed, temp_air, model='open_rack_cell_glassback'): poa_global : float or Series Total incident irradiance in W/m^2. - wind_speed : float or Series - Wind speed in m/s at a height of 10 meters. - temp_air : float or Series Ambient dry bulb temperature in degrees C. + wind_speed : float or Series + Wind speed in m/s at a height of 10 meters. + model : string, list, or dict, default 'open_rack_cell_glassback' Model to be used. diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 1455a57ad3..badd751913 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -273,8 +273,8 @@ class ModelChain(object): as the first argument to a user-defined function. temp_model: str or function, default 'sapm' - Valid strings are 'sapm'. The ModelChain instance will be passed - as the first argument to a user-defined function. + Valid strings are 'sapm' and 'pvsyst'. The ModelChain instance will be + passed as the first argument to a user-defined function. losses_model: str or function, default 'no_loss' Valid strings are 'pvwatts', 'no_loss'. The ModelChain instance @@ -670,6 +670,8 @@ def temp_model(self, model): model = model.lower() if model == 'sapm': self._temp_model = self.sapm_temp + elif model == 'pvsyst': + self._temp_model = self.pvsyst_temp else: raise ValueError(model + ' is not a valid temp model') else: @@ -680,8 +682,14 @@ def infer_temp_model(self): def sapm_temp(self): self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], - self.weather['wind_speed'], - self.weather['temp_air']) + self.weather['temp_air'], + self.weather['wind_speed']) + return self + + def pvsyst_temp(self): + self.temps = self.system.pvsyst_celltemp( + self.total_irrad['poa_global'], self.weather['temp_air'], + self.weather['wind_speed']) return self @property diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 82105cdfd6..2ed6acc67f 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -422,7 +422,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, wind_speed, temp_air): + def sapm_celltemp(self, poa_global, temp_air, wind_speed): """Uses :py:func:`celltemp.sapm` to calculate module and cell temperatures based on ``self.racking_model`` and the input parameters. @@ -432,12 +432,12 @@ def sapm_celltemp(self, poa_global, wind_speed, temp_air): poa_global : float or Series Total incident irradiance in W/m^2. - wind_speed : float or Series - Wind speed in m/s at a height of 10 meters. - temp_air : float or Series Ambient dry bulb temperature in degrees C. + wind_speed : float or Series + Wind speed in m/s at a height of 10 meters. + model : string, list, or dict, default 'open_rack_cell_glassback' Model to be used. See celltemp.sapm for details diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index b7fbc57128..a71e2d0163 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -17,20 +17,20 @@ def test_sapm_celltemp(): - default = celltemp.sapm(900, 5, 20) + default = celltemp.sapm(900, 20, 5) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) assert_frame_equal(default, celltemp.sapm(900, 5, 20, [-3.47, -.0594, 3])) def test_sapm_celltemp_dict_like(): - default = celltemp.sapm(900, 5, 20) + default = celltemp.sapm(900, 20, 5) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} - assert_frame_equal(default, celltemp.sapm(900, 5, 20, model)) + assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) model = pd.Series(model) - assert_frame_equal(default, celltemp.sapm(900, 5, 20, model)) + assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) def test_sapm_celltemp_with_index(): @@ -39,7 +39,7 @@ def test_sapm_celltemp_with_index(): irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - pvtemps = celltemp.sapm(irrads, winds, temps) + pvtemps = celltemp.sapm(irrads, temps, winds) expected = pd.DataFrame({'temp_cell': [0., 23.06066166, 5.], 'temp_module': [0., 21.56066166, 5.]}, @@ -56,7 +56,7 @@ def test_PVSystem_sapm_celltemp(mocker): temps = 25 irrads = 1000 winds = 1 - out = system.sapm_celltemp(irrads, winds, temps) + out = system.sapm_celltemp(irrads, temps, winds) celltemp.sapm.assert_called_once_with( irrads, winds, temps, model=racking_model) assert isinstance(out, pd.DataFrame) @@ -126,6 +126,6 @@ def test_PVSystem_pvsyst_celltemp(mocker): @fail_on_pvlib_version('0.7') def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): - pvsystem.sapm_celltemp(1000, 1, 25) + pvsystem.sapm_celltemp(1000, 25, 1) with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 7fac62542c..b36d8c78d2 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -196,16 +196,22 @@ def test_run_model_gueymard_perez(system, location): def test_run_model_with_weather(system, location, weather, mocker): mc = ModelChain(system, location) - m = mocker.spy(system, 'sapm_celltemp') + m_sapm = mocker.spy(system, 'sapm_celltemp') weather['wind_speed'] = 5 weather['temp_air'] = 10 mc.run_model(weather.index, weather=weather) - assert m.call_count == 1 + assert m_sapm.call_count == 1 # assert_called_once_with cannot be used with series, so need to use # assert_series_equal on call_args - assert_series_equal(m.call_args[0][1], weather['wind_speed']) # wind - assert_series_equal(m.call_args[0][2], weather['temp_air']) # temp + assert_series_equal(m_sapm.call_args[0][1], weather['temp_air']) # temp + assert_series_equal(m_sapm.call_args[0][2], weather['wind_speed']) # wind assert not mc.ac.empty + m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') + mc.temp_model = 'pvsyst' + mc.run_model(weather.index, weather=weather) + assert m_pvsyst.call_count == 1 + assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) # temp + assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed']) # wind def test_run_model_tracker(system, location, weather, mocker): From eb3c996f00667f4d2e39ed673006bab5a8f7a7bd Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 12 Jul 2019 15:25:04 -0600 Subject: [PATCH 14/98] lint --- pvlib/test/test_modelchain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index b36d8c78d2..8cc2d27d53 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -210,8 +210,8 @@ def test_run_model_with_weather(system, location, weather, mocker): mc.temp_model = 'pvsyst' mc.run_model(weather.index, weather=weather) assert m_pvsyst.call_count == 1 - assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) # temp - assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed']) # wind + assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) + assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed']) def test_run_model_tracker(system, location, weather, mocker): From 62adf2afa42c8ca1c2a1a91738ed4dd96d22c9ab Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 08:37:54 -0600 Subject: [PATCH 15/98] argument order fixes --- pvlib/modelchain.py | 4 ++-- pvlib/test/test_celltemp.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index badd751913..477b4d1009 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -164,8 +164,8 @@ def basic_chain(times, latitude, longitude, weather = {'wind_speed': 0, 'temp_air': 20} temps = pvsystem.sapm_celltemp(total_irrad['poa_global'], - weather['wind_speed'], - weather['temp_air']) + weather['temp_air'], + weather['wind_speed']) effective_irradiance = pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index a71e2d0163..595a08660c 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -20,7 +20,7 @@ def test_sapm_celltemp(): default = celltemp.sapm(900, 20, 5) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) - assert_frame_equal(default, celltemp.sapm(900, 5, 20, [-3.47, -.0594, 3])) + assert_frame_equal(default, celltemp.sapm(900, 20, 5, [-3.47, -.0594, 3])) def test_sapm_celltemp_dict_like(): From 791926da9c715ea44b887febdcfc113e171e3e2a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 08:51:39 -0600 Subject: [PATCH 16/98] another argument order fix --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 2ed6acc67f..65df64c95a 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -446,7 +446,7 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed): DataFrame with columns 'temp_cell' and 'temp_module'. Values in degrees C. """ - return celltemp.sapm(poa_global, wind_speed, temp_air, + return celltemp.sapm(poa_global, temp_air, wind_speed, self.racking_model) def sapm_spectral_loss(self, airmass_absolute): From 3095907437db85d4d4dcb8eab4b13297a174989a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 09:08:44 -0600 Subject: [PATCH 17/98] another argument order fix, change model_params to model in celltemp.pvsyst --- pvlib/celltemp.py | 16 ++++++++-------- pvlib/test/test_celltemp.py | 2 +- pvlib/test/test_modelchain.py | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 88184b2c48..24a9d9acf9 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -106,7 +106,7 @@ def sapm(poa_global, temp_air, wind_speed, model='open_rack_cell_glassback'): def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, - alpha_absorption=0.9, model_params='freestanding'): + alpha_absorption=0.9, model='freestanding'): """ Calculate cell temperature using an emperical heat loss factor model as implemented in PVsyst. @@ -134,7 +134,7 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, alpha_absorption : numeric, default 0.9 Absorption coefficient - model_params : string, tuple, or list (no dict), default 'freestanding' + model : string, tuple, or list (no dict), default 'freestanding' Heat loss factors to be used. If string, can be: @@ -171,14 +171,14 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, pvsyst_presets = TEMP_MODEL_PARAMS['pvsyst'] - if isinstance(model_params, str): - model_params = model_params.lower() - constant_loss_factor, wind_loss_factor = pvsyst_presets[model_params] - elif isinstance(model_params, (tuple, list)): - constant_loss_factor, wind_loss_factor = model_params + if isinstance(model, str): + model = model.lower() + constant_loss_factor, wind_loss_factor = pvsyst_presets[model] + elif isinstance(model, (tuple, list)): + constant_loss_factor, wind_loss_factor = model else: raise TypeError( - "Please provide model_params as a str, or tuple/list." + "Please provide model as a str, or tuple/list." ) total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 595a08660c..4659dfea23 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -58,7 +58,7 @@ def test_PVSystem_sapm_celltemp(mocker): winds = 1 out = system.sapm_celltemp(irrads, temps, winds) celltemp.sapm.assert_called_once_with( - irrads, winds, temps, model=racking_model) + irrads, temps, winds, model=racking_model) assert isinstance(out, pd.DataFrame) assert out.shape == (1, 2) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 8cc2d27d53..98cfd51365 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -206,8 +206,9 @@ def test_run_model_with_weather(system, location, weather, mocker): assert_series_equal(m_sapm.call_args[0][1], weather['temp_air']) # temp assert_series_equal(m_sapm.call_args[0][2], weather['wind_speed']) # wind assert not mc.ac.empty - m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') + system.racking_model = 'freestanding' mc.temp_model = 'pvsyst' + m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') mc.run_model(weather.index, weather=weather) assert m_pvsyst.call_count == 1 assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) From abeccd3a83e90f2b0b8b0f7e9e7309d260fedf90 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 09:23:54 -0600 Subject: [PATCH 18/98] more model_params to model cleanup --- pvlib/celltemp.py | 2 +- pvlib/pvsystem.py | 4 ++-- pvlib/test/test_celltemp.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 24a9d9acf9..29386225e2 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -111,7 +111,7 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, Calculate cell temperature using an emperical heat loss factor model as implemented in PVsyst. - The heat loss factors provided through the 'model_params' argument + The heat loss factors provided through the 'model' argument represent the combined effect of convection, radiation and conduction, and their values are experimentally determined. diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 65df64c95a..c40a774475 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -541,7 +541,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): alpha_absorption : numeric, default 0.9 Absorption coefficient - model_params : string, tuple, or list (no dict), default 'freestanding' + model : string, tuple, or list (no dict), default 'freestanding' Heat loss factors to be used. See celltemp.pvsyst for details. Returns @@ -552,7 +552,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) return celltemp.pvsyst(poa_global, temp_air, wind_speed, - model_params=self.racking_model, **kwargs) + model=self.racking_model, **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 4659dfea23..1f936468c9 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -70,11 +70,11 @@ def test_pvsyst_celltemp_default(): def test_pvsyst_celltemp_non_model(): tup_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model_params=(23.5, 6.25)) + model=(23.5, 6.25)) assert_allclose(tup_non_model, 33.315, 0.001) list_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model_params=[26.5, 7.68]) + model=[26.5, 7.68]) assert_allclose(list_non_model, 31.233, 0.001) @@ -82,14 +82,14 @@ def test_pvsyst_celltemp_model_wrong_type(): with pytest.raises(TypeError): pvsystem.pvsyst_celltemp( 900, 20, 5, 0.1, - model_params={"won't": 23.5, "work": 7.68}) + model={"won't": 23.5, "work": 7.68}) def test_pvsyst_celltemp_model_non_option(): with pytest.raises(KeyError): pvsystem.pvsyst_celltemp( 900, 20, 5, 0.1, - model_params="not_an_option") + model="not_an_option") def test_pvsyst_celltemp_with_index(): From 71ee15b6e764ce889fdd43a43b9a92ca5a1a0133 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 11:01:11 -0600 Subject: [PATCH 19/98] pare back modelchain test --- pvlib/test/test_modelchain.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 98cfd51365..7bd82a2872 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -206,13 +206,6 @@ def test_run_model_with_weather(system, location, weather, mocker): assert_series_equal(m_sapm.call_args[0][1], weather['temp_air']) # temp assert_series_equal(m_sapm.call_args[0][2], weather['wind_speed']) # wind assert not mc.ac.empty - system.racking_model = 'freestanding' - mc.temp_model = 'pvsyst' - m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') - mc.run_model(weather.index, weather=weather) - assert m_pvsyst.call_count == 1 - assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) - assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed']) def test_run_model_tracker(system, location, weather, mocker): From d925b5ab578eec39c9e58df314b14f154ec96431 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 11:58:02 -0600 Subject: [PATCH 20/98] put test back in to hit ModelChain.pvsyst_temp --- pvlib/test/test_modelchain.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 7bd82a2872..f403130c02 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -195,10 +195,13 @@ def test_run_model_gueymard_perez(system, location): def test_run_model_with_weather(system, location, weather, mocker): - mc = ModelChain(system, location) - m_sapm = mocker.spy(system, 'sapm_celltemp') weather['wind_speed'] = 5 weather['temp_air'] = 10 + # test with sapm cell temperature model + system.racking_model = 'open_rack_cell_glassback' + mc = ModelChain(system, location) + mc.temp_model = 'sapm' + m_sapm = mocker.spy(system, 'sapm_celltemp') mc.run_model(weather.index, weather=weather) assert m_sapm.call_count == 1 # assert_called_once_with cannot be used with series, so need to use @@ -206,6 +209,16 @@ def test_run_model_with_weather(system, location, weather, mocker): assert_series_equal(m_sapm.call_args[0][1], weather['temp_air']) # temp assert_series_equal(m_sapm.call_args[0][2], weather['wind_speed']) # wind assert not mc.ac.empty + # test with pvsyst cell temperature model + system.racking_model = 'freestanding' + mc = ModelChain(system, location) + mc.temp_model = 'pvsyst' + m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') + mc.run_model(weather.index, weather=weather) + assert m_pvsyst.call_count == 1 + assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air']) + assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed']) + assert not mc.ac.empty def test_run_model_tracker(system, location, weather, mocker): From e1987bb16234b68560a3195fda68b10b3a89ede0 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 12:17:17 -0600 Subject: [PATCH 21/98] output dataframe from celltemp.pvsyst --- pvlib/celltemp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 29386225e2..29b9d2e140 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -186,4 +186,4 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, temp_difference = heat_input / total_loss_factor temp_cell = temp_air + temp_difference - return temp_cell + return pd.DataFrame({'temp_cell': temp_cell}) From 8d91be3135b9150fd54a7cdc298bc839ab1a54de Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 14:28:46 -0600 Subject: [PATCH 22/98] cleanup tests with pvsyst output as dataframe --- pvlib/test/test_celltemp.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 1f936468c9..0cacb7b711 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -65,17 +65,17 @@ def test_PVSystem_sapm_celltemp(mocker): def test_pvsyst_celltemp_default(): default = celltemp.pvsyst(900, 20, 5) - assert_allclose(default, 45.137, 0.001) + assert_allclose(default['temp_cell'], 45.137, 0.001) def test_pvsyst_celltemp_non_model(): tup_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, model=(23.5, 6.25)) - assert_allclose(tup_non_model, 33.315, 0.001) + assert_allclose(tup_non_model['temp_cell'], 33.315, 0.001) list_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, model=[26.5, 7.68]) - assert_allclose(list_non_model, 31.233, 0.001) + assert_allclose(list_non_model['temp_cell'], 31.233, 0.001) def test_pvsyst_celltemp_model_wrong_type(): @@ -99,8 +99,9 @@ def test_pvsyst_celltemp_with_index(): winds = pd.Series([10, 5, 0], index=times) pvtemps = pvsystem.pvsyst_celltemp(irrads, temps, wind_speed=winds) - expected = pd.Series([0.0, 23.96551, 5.0], index=times) - assert_series_equal(expected, pvtemps) + expected = pd.DataFrame([0.0, 23.96551, 5.0], index=times, + columns=['temp_cell']) + assert_frame_equal(expected, pvtemps) def test_PVSystem_pvsyst_celltemp(mocker): From 58725c883486c4efa53861fbb5a43a25af6a3722 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 14:45:21 -0600 Subject: [PATCH 23/98] fix dataframe creation in celltemp.pvsyst --- pvlib/celltemp.py | 8 ++++---- pvlib/test/test_celltemp.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 29b9d2e140..3456091312 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -117,13 +117,13 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, Parameters ---------- - poa_global : numeric + poa_global : float or Series Total incident irradiance in W/m^2. - temp_air : numeric + temp_air : float or Series Ambient dry bulb temperature in degrees C. - wind_speed : numeric, default 1.0 + wind_speed : float or Series, default 1.0 Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value is 1.0, which is the wind speed at module height used to determine NOCT. @@ -184,6 +184,6 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor heat_input = poa_global * alpha_absorption * (1 - eta_m) temp_difference = heat_input / total_loss_factor - temp_cell = temp_air + temp_difference + temp_cell = pd.Series(temp_air + temp_difference) return pd.DataFrame({'temp_cell': temp_cell}) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 0cacb7b711..d29b2bcf81 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -7,7 +7,7 @@ import pandas as pd import pytest -from pandas.util.testing import assert_series_equal, assert_frame_equal +from pandas.util.testing import assert_frame_equal from numpy.testing import assert_allclose from pvlib import celltemp, pvsystem From b2bd4eb0196592799144aff6f23aead847fb85b8 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 15:04:14 -0600 Subject: [PATCH 24/98] fix type checking --- pvlib/test/test_celltemp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index d29b2bcf81..fddd759377 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -120,8 +120,8 @@ def test_PVSystem_pvsyst_celltemp(mocker): out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) celltemp.pvsyst.assert_called_once_with( irrad, temp, wind, eta_m, alpha_absorption, racking_model) - assert isinstance(out, float) - assert out < 90 and out > 70 + assert isinstance(out, pd.DataFrame) + assert out['temp_cell'] < 90 and out['temp_cell'] > 70 @fail_on_pvlib_version('0.7') From 0ad5d4292c87c8538ad32d263d4702d0414e5d5a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 15:20:03 -0600 Subject: [PATCH 25/98] fix test condition --- pvlib/test/test_celltemp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index fddd759377..06fa02b3dd 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -121,7 +121,7 @@ def test_PVSystem_pvsyst_celltemp(mocker): celltemp.pvsyst.assert_called_once_with( irrad, temp, wind, eta_m, alpha_absorption, racking_model) assert isinstance(out, pd.DataFrame) - assert out['temp_cell'] < 90 and out['temp_cell'] > 70 + assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) @fail_on_pvlib_version('0.7') From 8bfc56888e8b122092d2419da2c61d571a406b8d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 15 Jul 2019 15:44:07 -0600 Subject: [PATCH 26/98] update api, whatsnew --- docs/sphinx/source/api.rst | 24 +++++++++++++++++------- docs/sphinx/source/whatsnew/v0.7.0.rst | 12 ++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 45ef9ec50a..bcea8457b8 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -221,6 +221,15 @@ AOI modifiers pvsystem.ashraeiam pvsystem.sapm_aoi_loss +PV temperature models +--------------------- + +.. autosummary:: + :toctree: generated/ + + celltemp.sapm + celltemp.pvsyst + Single diode models ------------------- @@ -269,18 +278,18 @@ PVWatts model .. autosummary:: :toctree: generated/ - pvsystem.pvwatts_dc - pvsystem.pvwatts_ac - pvsystem.pvwatts_losses - pvsystem.pvwatts_losses + celltemp.pvsyst -PVsyst model ------------- +PVWatts model +------------- .. autosummary:: :toctree: generated/ - pvsystem.pvsyst_celltemp + pvsystem.pvwatts_dc + pvsystem.pvwatts_ac + pvsystem.pvwatts_losses + pvsystem.pvwatts_losses Other ----- @@ -500,6 +509,7 @@ ModelChain model definitions. modelchain.ModelChain.sapm_spectral_loss modelchain.ModelChain.no_spectral_loss modelchain.ModelChain.sapm_temp + modelchain.ModelChain.pvsyst_temp modelchain.ModelChain.pvwatts_losses modelchain.ModelChain.no_extra_losses diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index a8b2f21ebd..711e2c007b 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -10,6 +10,14 @@ compatibility notes. **Python 2.7 support ended on June 1, 2019**. (:issue:`501`) **Minimum numpy version is now 1.10.4. Minimum pandas version is now 0.18.1.** +API Changes +~~~~~~~~~~~ +* Moved functions for cell temperature from `pvsystem.py` to `celltemp.py`, renamed from `pvsystem.sapm_celltemp` to `celltemp.sapm`, and from `pvsystem.pvsyst_celltemp` to `celltemp.pvsyst`. Argument order in `celltemp.sapm` changed and output type from `celltemp.pvsyst` have been changed to be consistent in `celltemp.py` for all cell temperature model functions. (:issue:`678') +* Implemented `pvsyst` as a key value for `ModelChain.temp_model`. (:issue:`678') + +Enhancements +~~~~~~~~~~~~ + Bug fixes ~~~~~~~~~ * Fix handling of keyword arguments in `forecasts.get_processed_data`. @@ -19,9 +27,13 @@ Testing ~~~~~~~ * Added 30 minutes to timestamps in `test_psm3.csv` to match change in NSRDB (:issue:`733`) +Documentation +~~~~~~~~~~~~~ +* Corrected docstring for `pvsystem.PVSystem.sapm` Contributors ~~~~~~~~~~~~ * Mark Campanellli (:ghuser:`markcampanelli`) * Will Holmgren (:ghuser:`wholmgren`) * Oscar Dowson (:ghuser:`odow`) +* Cliff Hansen (:ghuser:`cwhanse`) From 8d640a69c78a24778f94e2d22e5eb7b7449c7ca5 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 18 Jul 2019 09:15:35 -0600 Subject: [PATCH 27/98] comment responses --- docs/sphinx/source/api.rst | 4 +++- docs/sphinx/source/whatsnew/v0.7.0.rst | 7 +++++-- pvlib/modelchain.py | 8 ++++---- pvlib/pvsystem.py | 13 +++++++++---- pvlib/test/test_celltemp.py | 2 +- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index bcea8457b8..b7bf330639 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -272,9 +272,11 @@ Functions relevant for the SAPM model. pvsystem.sapm_aoi_loss pvsystem.snlinverter -PVWatts model +Pvsyst model ------------- +Functions relevant for the Pvsyst model. + .. autosummary:: :toctree: generated/ diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 711e2c007b..490000021c 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -12,8 +12,11 @@ compatibility notes. API Changes ~~~~~~~~~~~ -* Moved functions for cell temperature from `pvsystem.py` to `celltemp.py`, renamed from `pvsystem.sapm_celltemp` to `celltemp.sapm`, and from `pvsystem.pvsyst_celltemp` to `celltemp.pvsyst`. Argument order in `celltemp.sapm` changed and output type from `celltemp.pvsyst` have been changed to be consistent in `celltemp.py` for all cell temperature model functions. (:issue:`678') -* Implemented `pvsyst` as a key value for `ModelChain.temp_model`. (:issue:`678') +* Moved functions for cell temperature and constant `TEMP_MODEL_PARAMS` from `pvsystem.py` to `celltemp.py`. (:issue:`678') +* Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to + `celltemp.sapm` and `celltemp.pvsyst`. (:issue:`678') +* Argument order and output type of `pvsystem.sapm_celltemp`, `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have been altered to be consistent among cell temperature model functions. +* Implemented `pvsyst` as an option for `ModelChain.temp_model`. (:issue:`678') Enhancements ~~~~~~~~~~~~ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 477b4d1009..afaa348475 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -10,7 +10,8 @@ import warnings import pandas as pd -from pvlib import (solarposition, pvsystem, clearsky, atmosphere, tools) +from pvlib import (atmosphere, celltemp, clearsky, pvsystem, solarposition, + tools) from pvlib.tracking import SingleAxisTracker import pvlib.irradiance # avoid name conflict with full import from pvlib.pvsystem import DC_MODEL_PARAMS @@ -163,9 +164,8 @@ def basic_chain(times, latitude, longitude, if weather is None: weather = {'wind_speed': 0, 'temp_air': 20} - temps = pvsystem.sapm_celltemp(total_irrad['poa_global'], - weather['temp_air'], - weather['wind_speed']) + temps = celltemp.sapm(total_irrad['poa_global'], weather['temp_air'], + weather['wind_speed']) effective_irradiance = pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index c40a774475..04c9c4cad3 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1840,13 +1840,18 @@ def sapm(effective_irradiance, temp_cell, module): return out -sapm_celltemp = deprecated('0.6.2', alternative='celltemp.sapm', - name='sapm_celltemp', removal='0.7')(celltemp.sapm) +sapm_celltemp = deprecated('0.7', alternative='celltemp.sapm', + name='sapm_celltemp', removal='0.8', + addedum='Note that the argument order for ' + 'celltemp.sapm is different than for ' + 'sapm_celltemp')(celltemp.sapm) -pvsyst_celltemp = deprecated('0.6.2', alternative='celltemp.pvsyst', +pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst', name='pvsyst_celltemp', - removal='0.7')(celltemp.pvsyst) + removal='0.8', addedum='Note that ' + 'celltemp.pvsyst returns a DataFrame instead ' + 'of numeric or Series')(celltemp.pvsyst) def sapm_spectral_loss(airmass_absolute, module): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 06fa02b3dd..fc503daee6 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -124,7 +124,7 @@ def test_PVSystem_pvsyst_celltemp(mocker): assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) -@fail_on_pvlib_version('0.7') +@fail_on_pvlib_version('0.8') def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_celltemp(1000, 25, 1) From 9f589948e4a9640255f4b546bebeaf1acb45e9b6 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 14:23:56 -0600 Subject: [PATCH 28/98] change arguments for celltemp.sapm --- pvlib/celltemp.py | 92 ++++++++++++++++--------------------- pvlib/pvsystem.py | 13 ++++-- pvlib/test/test_celltemp.py | 43 +++++++++-------- 3 files changed, 73 insertions(+), 75 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 3456091312..84f642808e 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -20,12 +20,10 @@ } -def sapm(poa_global, temp_air, wind_speed, model='open_rack_cell_glassback'): - ''' +def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): + r''' Estimate cell and module temperatures per the Sandia PV Array - Performance Model (SAPM, SAND2004-3535), from the incident - irradiance, wind speed, ambient temperature, and SAPM module - parameters. + Performance Model [1]. Parameters ---------- @@ -38,70 +36,60 @@ def sapm(poa_global, temp_air, wind_speed, model='open_rack_cell_glassback'): wind_speed : float or Series Wind speed in m/s at a height of 10 meters. - model : string, list, or dict, default 'open_rack_cell_glassback' - Model to be used. - - If string, can be: - - * 'open_rack_cell_glassback' (default) - * 'roof_mount_cell_glassback' - * 'open_rack_cell_polymerback' - * 'insulated_back_polymerback' - * 'open_rack_polymer_thinfilm_steel' - * '22x_concentrator_tracker' - - If dict, supply the following parameters - (if list, in the following order): + a : float + Parameter `a` in :eq:`eq1`. - * a : float - SAPM module parameter for establishing the upper - limit for module temperature at low wind speeds and - high solar irradiance. + b : float + Parameter `b` in :eq:`eq1`. - * b : float - SAPM module parameter for establishing the rate at - which the module temperature drops as wind speed increases - (see SAPM eqn. 11). + deltaT : float, [C] + Parameter :math:`\Delta T` in :eq:`eq2`. - * deltaT : float - SAPM module parameter giving the temperature difference - between the cell and module back surface at the - reference irradiance, E0. + irrad_ref : float, default 1000 [W/m2] + Reference irradiance, parameter :math:`E0` in :eq:`eq2`. Returns -------- DataFrame with columns 'temp_cell' and 'temp_module'. Values in degrees C. - References - ---------- - [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance - Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, - NM. - - See Also - -------- - sapm - ''' + Notes + ----- + The model for cell temperature :math:`T_{C}` and module temperature + :math:`T_{m}` is given by a pair of equations (Eq. 11 and 12 in [1]). - temp_models = TEMP_MODEL_PARAMS['sapm'] + .. :math:: + :label: eq1 - if isinstance(model, str): - model = temp_models[model.lower()] + T_{m} = E \times \exp (a + b \times WS) + T_{a} - elif isinstance(model, (dict, pd.Series)): - model = [model['a'], model['b'], model['deltaT']] + :label: eq2 - a = model[0] - b = model[1] - deltaT = model[2] + T_{C} = T_{m} + \frac{E}{E0} \Delta T - E0 = 1000. # Reference irradiance + Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and + ambient air temperature :math:`T_{a}` (C). Model outputs are surface + temperature at the back of the module :math:`T_{m}` and cell temperature + :math:`T_{C}`. Model parameters depend both on the module construction and + its mounting. Parameter sets are provided in [1] for representative modules + and mounting. - temp_module = pd.Series(poa_global * np.exp(a + b * wind_speed) + temp_air) + | Module | Mounting | a | b | :math:`\Delta T [\degree C]` | + |:-------|:---------|---:|---:|-----------------------------:| + | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | + | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | + | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | + | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | - temp_cell = temp_module + (poa_global / E0) * (deltaT) + References + ---------- + [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance + Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, + NM. + ''' + temp_module = pd.Series(poa_global * np.exp(a + b * wind_speed) + temp_air) + temp_cell = temp_module + (poa_global / irrad_ref) * (deltaT) return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 04c9c4cad3..d055710e68 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -422,7 +422,8 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, temp_air, wind_speed): + def sapm_celltemp(self, poa_global, temp_air, wind_speed, + model='open_rack_cell_glassback'): """Uses :py:func:`celltemp.sapm` to calculate module and cell temperatures based on ``self.racking_model`` and the input parameters. @@ -446,8 +447,14 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed): DataFrame with columns 'temp_cell' and 'temp_module'. Values in degrees C. """ - return celltemp.sapm(poa_global, temp_air, wind_speed, - self.racking_model) + try: + a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][model] + except KeyError: + msg = ('{} is not a named set of parameters for the {} cell' + ' temperature model. See pvlib.celltemp.TEMP_MODEL_PARAMS' + ' for names'.format(model, 'sapm')) + raise KeyError(msg) + return celltemp.sapm(poa_global, temp_air, wind_speed, a, b, deltaT) def sapm_spectral_loss(self, airmass_absolute): """ diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index fc503daee6..0b5332a160 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -16,49 +16,52 @@ from conftest import fail_on_pvlib_version -def test_sapm_celltemp(): - default = celltemp.sapm(900, 20, 5) - assert_allclose(default['temp_cell'], 43.509, 3) - assert_allclose(default['temp_module'], 40.809, 3) - assert_frame_equal(default, celltemp.sapm(900, 20, 5, [-3.47, -.0594, 3])) +@pytest.fixture +def celltemp_sapm_default(): + return celltemp.TEMP_MODEL_PARAMS['sapm']['open_rack_cell_glassback'] -def test_sapm_celltemp_dict_like(): - default = celltemp.sapm(900, 20, 5) +def test_sapm_celltemp(celltemp_sapm_default): + a, b, deltaT = celltemp_sapm_default + default = celltemp.sapm(900, 20, 5, a, b, deltaT) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) - model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} - assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) - model = pd.Series(model) - assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) +# assert_frame_equal(default, celltemp.sapm(900, 20, 5, [-3.47, -.0594, 3])) + + +#def test_sapm_celltemp_dict_like(): +# default = celltemp.sapm(900, 20, 5) +# assert_allclose(default['temp_cell'], 43.509, 3) +# assert_allclose(default['temp_module'], 40.809, 3) +# model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} +# assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) +# model = pd.Series(model) +# assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) -def test_sapm_celltemp_with_index(): +def test_sapm_celltemp_with_index(celltemp_sapm_default): + a, b, deltaT = celltemp_sapm_default times = pd.date_range(start='2015-01-01', end='2015-01-02', freq='12H') temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - - pvtemps = celltemp.sapm(irrads, temps, winds) - + pvtemps = celltemp.sapm(irrads, temps, winds, a, b, deltaT) expected = pd.DataFrame({'temp_cell': [0., 23.06066166, 5.], 'temp_module': [0., 21.56066166, 5.]}, index=times) - assert_frame_equal(expected, pvtemps) def test_PVSystem_sapm_celltemp(mocker): racking_model = 'roof_mount_cell_glassback' - system = pvsystem.PVSystem(racking_model=racking_model) + a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][racking_model] mocker.spy(celltemp, 'sapm') temps = 25 irrads = 1000 winds = 1 - out = system.sapm_celltemp(irrads, temps, winds) - celltemp.sapm.assert_called_once_with( - irrads, temps, winds, model=racking_model) + out = system.sapm_celltemp(irrads, temps, winds, a, b, deltaT) + celltemp.sapm.assert_called_once_with(irrads, temps, winds, a, b, deltaT) assert isinstance(out, pd.DataFrame) assert out.shape == (1, 2) From 4ddb1cd335700b1dcc5efe197cb371002c25333a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 14:36:53 -0600 Subject: [PATCH 29/98] fix conflict --- docs/sphinx/source/whatsnew/v0.7.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 490000021c..7abc2d7480 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -40,3 +40,4 @@ Contributors * Will Holmgren (:ghuser:`wholmgren`) * Oscar Dowson (:ghuser:`odow`) * Cliff Hansen (:ghuser:`cwhanse`) +* Alexander Morgan (:ghuser:`alexandermorgan`) From dd0658a5f1214e5e6036c16b4fb8c3c03ab9fcc3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 14:40:52 -0600 Subject: [PATCH 30/98] fix kwarg error --- docs/sphinx/source/api.rst | 1 + pvlib/pvsystem.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index b7bf330639..a87e731340 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -271,6 +271,7 @@ Functions relevant for the SAPM model. pvsystem.sapm_spectral_loss pvsystem.sapm_aoi_loss pvsystem.snlinverter + celltemp.sapm Pvsyst model ------------- diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index d055710e68..000a83a9bf 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1849,14 +1849,14 @@ def sapm(effective_irradiance, temp_cell, module): sapm_celltemp = deprecated('0.7', alternative='celltemp.sapm', name='sapm_celltemp', removal='0.8', - addedum='Note that the argument order for ' + addendum='Note that the argument order for ' 'celltemp.sapm is different than for ' 'sapm_celltemp')(celltemp.sapm) pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst', name='pvsyst_celltemp', - removal='0.8', addedum='Note that ' + removal='0.8', addendum='Note that ' 'celltemp.pvsyst returns a DataFrame instead ' 'of numeric or Series')(celltemp.pvsyst) From c8c89d75f10868faac1d43671e3a8c0651bb6597 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 14:44:31 -0600 Subject: [PATCH 31/98] update whatsnew --- docs/sphinx/source/whatsnew/v0.7.0.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 7abc2d7480..d469d88c0e 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -16,6 +16,8 @@ API Changes * Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to `celltemp.sapm` and `celltemp.pvsyst`. (:issue:`678') * Argument order and output type of `pvsystem.sapm_celltemp`, `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have been altered to be consistent among cell temperature model functions. +* Removed `model` as a kwarg from `celltemp.sapm` and `celltemp.pvsyst`. These +functions now require model parameters as kwargs. * Implemented `pvsyst` as an option for `ModelChain.temp_model`. (:issue:`678') Enhancements @@ -39,5 +41,5 @@ Contributors * Mark Campanellli (:ghuser:`markcampanelli`) * Will Holmgren (:ghuser:`wholmgren`) * Oscar Dowson (:ghuser:`odow`) -* Cliff Hansen (:ghuser:`cwhanse`) * Alexander Morgan (:ghuser:`alexandermorgan`) +* Cliff Hansen (:ghuser:`cwhanse`) From 507d524f11e6f644c401330291f88128b5d611b4 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 15:16:37 -0600 Subject: [PATCH 32/98] test fixes, table formatting --- pvlib/celltemp.py | 18 ++++++++++++------ pvlib/test/test_celltemp.py | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 84f642808e..f83230f146 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -63,6 +63,7 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): T_{m} = E \times \exp (a + b \times WS) + T_{a} + .. :math:: :label: eq2 T_{C} = T_{m} + \frac{E}{E0} \Delta T @@ -74,12 +75,17 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): its mounting. Parameter sets are provided in [1] for representative modules and mounting. - | Module | Mounting | a | b | :math:`\Delta T [\degree C]` | - |:-------|:---------|---:|---:|-----------------------------:| - | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | - | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | - | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | - | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | + +--------------------+------------------+-----------------+---------------------+ + | Module | Mounting | a | b | :math:`\Delta T [C]`| + +====================+==================+===+===+===============================+ + | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | + +--------------------+------------------+-------+---------+---------------------+ + | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | + +--------------------+------------------+-------+---------+---------------------+ + | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | + +--------------------+------------------+-------+---------+---------------------+ + | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | + +--------------------+------------------+-------+---------+---------------------+ References ---------- diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 0b5332a160..4be8f1dcd2 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -130,6 +130,6 @@ def test_PVSystem_pvsyst_celltemp(mocker): @fail_on_pvlib_version('0.8') def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): - pvsystem.sapm_celltemp(1000, 25, 1) + pvsystem.sapm_celltemp(1000, 25, 1, -3.47, -0.0594, 3) with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) From fad7c7c4830378fbc4ad26d8915eb574f10db890 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 24 Jul 2019 21:12:09 -0600 Subject: [PATCH 33/98] docstring fixes --- pvlib/celltemp.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index f83230f146..bf32d70437 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -58,13 +58,13 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): The model for cell temperature :math:`T_{C}` and module temperature :math:`T_{m}` is given by a pair of equations (Eq. 11 and 12 in [1]). - .. :math:: - :label: eq1 + .. math:: + :label: eq1 T_{m} = E \times \exp (a + b \times WS) + T_{a} - .. :math:: - :label: eq2 + .. math:: + :label: eq2 T_{C} = T_{m} + \frac{E}{E0} \Delta T @@ -75,9 +75,9 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): its mounting. Parameter sets are provided in [1] for representative modules and mounting. - +--------------------+------------------+-----------------+---------------------+ + +--------------------+------------------+-------+---------+---------------------+ | Module | Mounting | a | b | :math:`\Delta T [C]`| - +====================+==================+===+===+===============================+ + +====================+==================+=======+=========+=====================+ | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | +--------------------+------------------+-------+---------+---------------------+ | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | From a9153fa06b57ccecc2a018139d0ded4c9d4d870d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 09:42:44 -0600 Subject: [PATCH 34/98] edits to celltemp.pvsyst --- pvlib/celltemp.py | 98 ++++++++++++++++++------------------- pvlib/pvsystem.py | 31 +++++++----- pvlib/test/test_celltemp.py | 35 +++++++------ 3 files changed, 88 insertions(+), 76 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index bf32d70437..2ef6167305 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -37,16 +37,16 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): Wind speed in m/s at a height of 10 meters. a : float - Parameter `a` in :eq:`eq1`. + Parameter :math:`a` in :eq:`sapm1`. b : float - Parameter `b` in :eq:`eq1`. + Parameter :math:`b` in :eq:`sapm1`. deltaT : float, [C] - Parameter :math:`\Delta T` in :eq:`eq2`. + Parameter :math:`\Delta T` in :eq:`sapm2`. irrad_ref : float, default 1000 [W/m2] - Reference irradiance, parameter :math:`E0` in :eq:`eq2`. + Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2`. Returns -------- @@ -59,14 +59,14 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): :math:`T_{m}` is given by a pair of equations (Eq. 11 and 12 in [1]). .. math:: - :label: eq1 + :label: sapm1 T_{m} = E \times \exp (a + b \times WS) + T_{a} .. math:: - :label: eq2 + :label: sapm2 - T_{C} = T_{m} + \frac{E}{E0} \Delta T + T_{C} = T_{m} + \frac{E}{E_{0}} \Delta T Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and ambient air temperature :math:`T_{a}` (C). Model outputs are surface @@ -99,10 +99,10 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) -def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, - alpha_absorption=0.9, model='freestanding'): - """ - Calculate cell temperature using an emperical heat loss factor model +def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, + wind_loss_factor=0.0, eta_m=0.1, alpha_absorption=0.9): + r""" + Calculate cell temperature using an empirical heat loss factor model as implemented in PVsyst. The heat loss factors provided through the 'model' argument @@ -112,48 +112,60 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, Parameters ---------- poa_global : float or Series - Total incident irradiance in W/m^2. + Total incident irradiance [:math:`\frac{W}{m^2} ]. temp_air : float or Series - Ambient dry bulb temperature in degrees C. + Ambient dry bulb temperature [C]. - wind_speed : float or Series, default 1.0 + wind_speed : float or Series, default 1.0 [m/s] Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value is 1.0, which is the wind speed at module height used to determine NOCT. + constant_loss_factor : float, default 29.0 [:math:`\frac{W}{m^2 C}] + Combined heat loss factor coefficient. The default value is + representative of freestanding modules with the rear surfaces exposed + to open air (e.g., rack mounted). Parameter :math:`U_{c}` in + :eq:`pvsyst`. + + wind_loss_factor : float, default 0.0 [:math:`\frac{W}{m^2 C} \frac{m}{s}`] + Combined heat loss factor influenced by wind. Parameter :math:`U_{c}` + in :eq:`pvsyst`. + eta_m : numeric, default 0.1 Module external efficiency as a fraction, i.e., DC power / poa_global. + Parameter :math:`\eta_{m}` in :eq:`pvsyst`. alpha_absorption : numeric, default 0.9 - Absorption coefficient + Absorption coefficient. Parameter :math:`\alpha` in :eq:`pvsyst`. - model : string, tuple, or list (no dict), default 'freestanding' - Heat loss factors to be used. - - If string, can be: - - * 'freestanding' (default) - Modules with rear surfaces exposed to open air (e.g. rack - mounted). - * 'insulated' - Modules with rear surfaces in close proximity to another - surface (e.g. roof mounted). - - If tuple/list, supply parameters in the following order: + Returns + ------- + DataFrame with columns 'temp_cell', values in degrees Celsius - * constant_loss_factor : float - Combined heat loss factor coefficient. Freestanding - default is 29, fully insulated arrays is 15. + Notes + ----- + The Pvsyst model for cell temperature :math:`T_{C}` is given by - * wind_loss_factor : float - Combined heat loss factor influenced by wind. Default is 0. + .. math:: + :label: pvsyst - Returns - ------- - temp_cell : numeric or Series - Cell temperature in degrees Celsius + T_{C} = T_{a} + \frac{\alpha E (1 - \eta_{m})}{U_{c} + U_{v} WS} + Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and + ambient air temperature :math:`T_{a}` (C). Model output is cell temperature + :math:`T_{C}`. Model parameters depend both on the module construction and + its mounting. Parameter sets are provided in [1] for open (freestanding) + close (insulated) mounting configurations. + + +--------------+---------------+---------------+ + | Mounting | :math:`U_{c}` | :math:`U_{v}` | + +==============+===============+===============+ + | freestanding | 29.0 | 0.0 | + +--------------+---------------+---------------+ + | insulated | 15.0 | 0.0 | + +--------------+---------------+---------------+ + References ---------- [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: @@ -163,18 +175,6 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, eta_m=0.1, photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. """ - pvsyst_presets = TEMP_MODEL_PARAMS['pvsyst'] - - if isinstance(model, str): - model = model.lower() - constant_loss_factor, wind_loss_factor = pvsyst_presets[model] - elif isinstance(model, (tuple, list)): - constant_loss_factor, wind_loss_factor = model - else: - raise TypeError( - "Please provide model as a str, or tuple/list." - ) - total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor heat_input = poa_global * alpha_absorption * (1 - eta_m) temp_difference = heat_input / total_loss_factor diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 000a83a9bf..14f6d24bf6 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -425,8 +425,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): def sapm_celltemp(self, poa_global, temp_air, wind_speed, model='open_rack_cell_glassback'): """Uses :py:func:`celltemp.sapm` to calculate module and cell - temperatures based on ``self.racking_model`` and - the input parameters. + temperatures. Parameters ---------- @@ -439,8 +438,8 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, wind_speed : float or Series Wind speed in m/s at a height of 10 meters. - model : string, list, or dict, default 'open_rack_cell_glassback' - Model to be used. See celltemp.sapm for details + model : string, default 'open_rack_cell_glassback' + Model parameters to be used. See celltemp.sapm for details Returns ------- @@ -524,9 +523,9 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) - def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): - """Uses :py:func:`celltemp.pvsyst` to calculate module temperatures - based on ``self.racking_model`` and the input parameters. + def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, + model='freestanding'): + """Uses :py:func:`celltemp.pvsyst` to calculate cell temperature. Parameters ---------- @@ -548,18 +547,26 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): alpha_absorption : numeric, default 0.9 Absorption coefficient - model : string, tuple, or list (no dict), default 'freestanding' - Heat loss factors to be used. See celltemp.pvsyst for details. + model : string, default 'freestanding' + Heat loss model factors to be used. See celltemp.pvsyst for details. Returns ------- - temp_cell : numeric or Series - Cell temperature in degrees C. + DataFrame with column 'temp_cell', values in degrees C. """ + try: + constant_loss_factor, wind_loss_factor = \ + celltemp.TEMP_MODEL_PARAMS['pvsyst'][model] + except KeyError: + msg = ('{} is not a named set of parameters for the {} cell' + ' temperature model. See pvlib.celltemp.TEMP_MODEL_PARAMS' + ' for names'.format(model, 'pvsyst')) + raise KeyError(msg) kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) return celltemp.pvsyst(poa_global, temp_air, wind_speed, - model=self.racking_model, **kwargs) + constant_loss_factor, wind_loss_factor, + **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 4be8f1dcd2..1d83480849 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -72,27 +72,29 @@ def test_pvsyst_celltemp_default(): def test_pvsyst_celltemp_non_model(): - tup_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model=(23.5, 6.25)) + tup_non_model = pvsystem.pvsyst_celltemp(900, 20, wind_speed=5.0, + constant_loss_factor=23.5, + wind_loss_factor=6.25, eta_m=0.1) assert_allclose(tup_non_model['temp_cell'], 33.315, 0.001) - list_non_model = pvsystem.pvsyst_celltemp(900, 20, 5, 0.1, - model=[26.5, 7.68]) + list_non_model = pvsystem.pvsyst_celltemp(900, 20, wind_speed=5.0, + constant_loss_factor=26.5, + wind_loss_factor=7.68, eta_m=0.1) assert_allclose(list_non_model['temp_cell'], 31.233, 0.001) -def test_pvsyst_celltemp_model_wrong_type(): - with pytest.raises(TypeError): - pvsystem.pvsyst_celltemp( - 900, 20, 5, 0.1, - model={"won't": 23.5, "work": 7.68}) +#def test_pvsyst_celltemp_model_wrong_type(): +# with pytest.raises(TypeError): +# pvsystem.pvsyst_celltemp( +# 900, 20, 5, 0.1, +# model={"won't": 23.5, "work": 7.68}) -def test_pvsyst_celltemp_model_non_option(): - with pytest.raises(KeyError): - pvsystem.pvsyst_celltemp( - 900, 20, 5, 0.1, - model="not_an_option") +#def test_pvsyst_celltemp_model_non_option(): +# with pytest.raises(KeyError): +# pvsystem.pvsyst_celltemp( +# 900, 20, 5, 0.1, +# model="not_an_option") def test_pvsyst_celltemp_with_index(): @@ -109,6 +111,8 @@ def test_pvsyst_celltemp_with_index(): def test_PVSystem_pvsyst_celltemp(mocker): racking_model = 'insulated' + constant_loss_factor, wind_loss_factor = \ + celltemp.TEMP_MODEL_PARAMS['pvsyst']['freestanding'] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {} @@ -122,7 +126,8 @@ def test_PVSystem_pvsyst_celltemp(mocker): wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) celltemp.pvsyst.assert_called_once_with( - irrad, temp, wind, eta_m, alpha_absorption, racking_model) + irrad, temp, wind, constant_loss_factor, wind_loss_factor, eta_m, + alpha_absorption) assert isinstance(out, pd.DataFrame) assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) From 60f370be92a68c20c51f394e369cf6e970028670 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 10:39:16 -0600 Subject: [PATCH 35/98] add PVsystem properties, move PVsystem tests back to test_pvsystem.py --- pvlib/pvsystem.py | 55 +++++++++++++++++++++---------------- pvlib/test/test_celltemp.py | 37 ------------------------- pvlib/test/test_pvsystem.py | 41 +++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 14f6d24bf6..87ee69697c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -106,9 +106,16 @@ class PVSystem(object): May be used to look up the module_parameters dictionary via some other method. + module_type : None or string, default 'glass_polymer' + Describes the module's construction. Valid strings are 'glass_polymer' + and 'glass_glass'. Used for cell and module temperature calculations. + module_parameters : None, dict or Series, default None Module parameters as defined by the SAPM, CEC, or other. + temperature_model_parameters : None, dict or Series, default None. + Temperature model parameters as defined by the SAPM, Pvsyst, or other. + modules_per_string: int or float, default 1 See system topology discussion above. @@ -123,8 +130,9 @@ class PVSystem(object): inverter_parameters : None, dict or Series, default None Inverter parameters as defined by the SAPM, CEC, or other. - racking_model : None or string, default 'open_rack_cell_glassback' - Used for cell and module temperature calculations. + racking_model : None or string, default 'open_rack' + Valid strings are 'open_rack' and 'close_mount'. Used for cell and + module temperature calculations. losses_parameters : None, dict or Series, default None Losses parameters as defined by PVWatts or other. @@ -145,13 +153,13 @@ class PVSystem(object): def __init__(self, surface_tilt=0, surface_azimuth=180, albedo=None, surface_type=None, - module=None, module_parameters=None, + module=None, module_type='glass_polymer', + module_parameters=None, + temperature_model_parameters=None, modules_per_string=1, strings_per_inverter=1, inverter=None, inverter_parameters=None, - racking_model='open_rack_cell_glassback', - losses_parameters=None, name=None, **kwargs): - - self.name = name + racking_model='open_rack', losses_parameters=None, name=None, + **kwargs): self.surface_tilt = surface_tilt self.surface_azimuth = surface_azimuth @@ -170,6 +178,13 @@ def __init__(self, else: self.module_parameters = module_parameters + self.module_type = module_type + + if temperature_model_parameters is None: + self.temperature_model_parameters = {} + else: + self.temperature_model_parameters = temperature_model_parameters + self.modules_per_string = modules_per_string self.strings_per_inverter = strings_per_inverter @@ -186,6 +201,8 @@ def __init__(self, self.racking_model = racking_model + self.name = name + def __repr__(self): attrs = ['name', 'surface_tilt', 'surface_azimuth', 'module', 'inverter', 'albedo', 'racking_model'] @@ -422,8 +439,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, temp_air, wind_speed, - model='open_rack_cell_glassback'): + def sapm_celltemp(self, poa_global, temp_air, wind_speed): """Uses :py:func:`celltemp.sapm` to calculate module and cell temperatures. @@ -438,22 +454,14 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, wind_speed : float or Series Wind speed in m/s at a height of 10 meters. - model : string, default 'open_rack_cell_glassback' - Model parameters to be used. See celltemp.sapm for details - Returns ------- DataFrame with columns 'temp_cell' and 'temp_module'. Values in degrees C. """ - try: - a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][model] - except KeyError: - msg = ('{} is not a named set of parameters for the {} cell' - ' temperature model. See pvlib.celltemp.TEMP_MODEL_PARAMS' - ' for names'.format(model, 'sapm')) - raise KeyError(msg) - return celltemp.sapm(poa_global, temp_air, wind_speed, a, b, deltaT) + kwargs = _build_kwargs(['a', 'b', 'deltaT'], + self.temperature_model_parameters) + return celltemp.sapm(poa_global, temp_air, wind_speed, **kwargs) def sapm_spectral_loss(self, airmass_absolute): """ @@ -564,9 +572,10 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, raise KeyError(msg) kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) - return celltemp.pvsyst(poa_global, temp_air, wind_speed, - constant_loss_factor, wind_loss_factor, - **kwargs) + kwargs.update(_build_kwargs(['constant_loss_factor', + 'wind_loss_factor'], + self.temperature_model_parameters)) + return celltemp.pvsyst(poa_global, temp_air, wind_speed, **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 1d83480849..4fe6e57b30 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -52,20 +52,6 @@ def test_sapm_celltemp_with_index(celltemp_sapm_default): assert_frame_equal(expected, pvtemps) -def test_PVSystem_sapm_celltemp(mocker): - racking_model = 'roof_mount_cell_glassback' - system = pvsystem.PVSystem(racking_model=racking_model) - a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][racking_model] - mocker.spy(celltemp, 'sapm') - temps = 25 - irrads = 1000 - winds = 1 - out = system.sapm_celltemp(irrads, temps, winds, a, b, deltaT) - celltemp.sapm.assert_called_once_with(irrads, temps, winds, a, b, deltaT) - assert isinstance(out, pd.DataFrame) - assert out.shape == (1, 2) - - def test_pvsyst_celltemp_default(): default = celltemp.pvsyst(900, 20, 5) assert_allclose(default['temp_cell'], 45.137, 0.001) @@ -109,29 +95,6 @@ def test_pvsyst_celltemp_with_index(): assert_frame_equal(expected, pvtemps) -def test_PVSystem_pvsyst_celltemp(mocker): - racking_model = 'insulated' - constant_loss_factor, wind_loss_factor = \ - celltemp.TEMP_MODEL_PARAMS['pvsyst']['freestanding'] - alpha_absorption = 0.85 - eta_m = 0.17 - module_parameters = {} - module_parameters['alpha_absorption'] = alpha_absorption - module_parameters['eta_m'] = eta_m - system = pvsystem.PVSystem(racking_model=racking_model, - module_parameters=module_parameters) - mocker.spy(celltemp, 'pvsyst') - irrad = 800 - temp = 45 - wind = 0.5 - out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) - celltemp.pvsyst.assert_called_once_with( - irrad, temp, wind, constant_loss_factor, wind_loss_factor, eta_m, - alpha_absorption) - assert isinstance(out, pd.DataFrame) - assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) - - @fail_on_pvlib_version('0.8') def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 9dac04cc92..0447c54b48 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -17,6 +17,7 @@ from pvlib import atmosphere from pvlib import solarposition from pvlib.location import Location +from pvlib import celltemp from conftest import needs_numpy_1_10, requires_scipy @@ -379,6 +380,46 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): assert_allclose(out, 1, atol=0.1) +def test_PVSystem_sapm_celltemp(mocker): + racking_model = 'roof_mount_cell_glassback' + a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][racking_model] + temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} + system = pvsystem.PVSystem(racking_model=racking_model, + temperature_model_parameters=temp_model_params) + mocker.spy(celltemp, 'sapm') + temps = 25 + irrads = 1000 + winds = 1 + out = system.sapm_celltemp(irrads, temps, winds) + celltemp.sapm.assert_called_once_with(irrads, temps, winds, a, b, deltaT) + assert isinstance(out, pd.DataFrame) + assert out.shape == (1, 2) + + +def test_PVSystem_pvsyst_celltemp(mocker): + racking_model = 'insulated' + constant_loss_factor, wind_loss_factor = \ + celltemp.TEMP_MODEL_PARAMS['pvsyst']['insulated'] + temp_model_params = {'constant_loss_factor': constant_loss_factor, + 'wind_loss_factor': wind_loss_factor} + alpha_absorption = 0.85 + eta_m = 0.17 + module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} + system = pvsystem.PVSystem(racking_model=racking_model, + module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) + mocker.spy(celltemp, 'pvsyst') + irrad = 800 + temp = 45 + wind = 0.5 + out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) + celltemp.pvsyst.assert_called_once_with( + irrad, temp, wind, constant_loss_factor, wind_loss_factor, eta_m, + alpha_absorption) + assert isinstance(out, pd.DataFrame) + assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) + + def test_calcparams_desoto(cec_module_params): times = pd.date_range(start='2015-01-01', periods=3, freq='12H') effective_irradiance = pd.Series([0.0, 800.0, 800.0], index=times) From fc8086718aa982bc9fc3931c007799d8353c9adb Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 11:58:07 -0600 Subject: [PATCH 36/98] start working on ModelChain --- pvlib/celltemp.py | 24 ++++++++++++------------ pvlib/modelchain.py | 11 +++++++++-- pvlib/pvsystem.py | 4 ++-- pvlib/test/test_celltemp.py | 25 ------------------------- pvlib/test/test_modelchain.py | 22 +++++++++++++++++++++- pvlib/test/test_pvsystem.py | 18 +++++++++++------- pvlib/test/test_tracking.py | 13 +++++++++++-- 7 files changed, 66 insertions(+), 51 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 2ef6167305..1af1bd567f 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -75,17 +75,17 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): its mounting. Parameter sets are provided in [1] for representative modules and mounting. - +--------------------+------------------+-------+---------+---------------------+ - | Module | Mounting | a | b | :math:`\Delta T [C]`| - +====================+==================+=======+=========+=====================+ - | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | - +--------------------+------------------+-------+---------+---------------------+ - | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | - +--------------------+------------------+-------+---------+---------------------+ - | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | - +--------------------+------------------+-------+---------+---------------------+ - | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | - +--------------------+------------------+-------+---------+---------------------+ + +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 + | Module | Mounting | a | b | :math:`\Delta T [C]`| # noqa: E501 + +====================+==================+=======+=========+=====================+ # noqa: E501 + | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | # noqa: E501 + +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 + | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | # noqa: E501 + +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 + | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | # noqa: E501 + +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 + | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | # noqa: E501 + +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 References ---------- @@ -165,7 +165,7 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, +--------------+---------------+---------------+ | insulated | 15.0 | 0.0 | +--------------+---------------+---------------+ - + References ---------- [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index afaa348475..f1481ec0a1 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -19,7 +19,8 @@ def basic_chain(times, latitude, longitude, - module_parameters, inverter_parameters, + module_parameters, temperature_model_parameters, + inverter_parameters, irradiance=None, weather=None, surface_tilt=None, surface_azimuth=None, orientation_strategy=None, @@ -49,6 +50,9 @@ def basic_chain(times, latitude, longitude, module_parameters : None, dict or Series Module parameters as defined by the SAPM. + temperature_model_parameters : None, dict or Series, default None. + Temperature model parameters as defined by the SAPM. + inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. @@ -165,7 +169,10 @@ def basic_chain(times, latitude, longitude, weather = {'wind_speed': 0, 'temp_air': 20} temps = celltemp.sapm(total_irrad['poa_global'], weather['temp_air'], - weather['wind_speed']) + weather['wind_speed'], + temperature_model_parameters['a'], + temperature_model_parameters['b'], + temperature_model_parameters['deltaT']) effective_irradiance = pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 87ee69697c..ee20a6d980 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -18,10 +18,10 @@ from pvlib._deprecation import deprecated -from pvlib import atmosphere, irradiance, tools, singlediode as _singlediode +from pvlib import atmosphere, celltemp, irradiance, singlediode as _singlediode +from pvlib import tools from pvlib.tools import _build_kwargs from pvlib.location import Location -from pvlib import celltemp # a dict of required parameter names for each DC power model diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 4fe6e57b30..2403eabbee 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -26,17 +26,6 @@ def test_sapm_celltemp(celltemp_sapm_default): default = celltemp.sapm(900, 20, 5, a, b, deltaT) assert_allclose(default['temp_cell'], 43.509, 3) assert_allclose(default['temp_module'], 40.809, 3) -# assert_frame_equal(default, celltemp.sapm(900, 20, 5, [-3.47, -.0594, 3])) - - -#def test_sapm_celltemp_dict_like(): -# default = celltemp.sapm(900, 20, 5) -# assert_allclose(default['temp_cell'], 43.509, 3) -# assert_allclose(default['temp_module'], 40.809, 3) -# model = {'a': -3.47, 'b': -.0594, 'deltaT': 3} -# assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) -# model = pd.Series(model) -# assert_frame_equal(default, celltemp.sapm(900, 20, 5, model)) def test_sapm_celltemp_with_index(celltemp_sapm_default): @@ -69,20 +58,6 @@ def test_pvsyst_celltemp_non_model(): assert_allclose(list_non_model['temp_cell'], 31.233, 0.001) -#def test_pvsyst_celltemp_model_wrong_type(): -# with pytest.raises(TypeError): -# pvsystem.pvsyst_celltemp( -# 900, 20, 5, 0.1, -# model={"won't": 23.5, "work": 7.68}) - - -#def test_pvsyst_celltemp_model_non_option(): -# with pytest.raises(KeyError): -# pvsystem.pvsyst_celltemp( -# 900, 20, 5, 0.1, -# model="not_an_option") - - def test_pvsyst_celltemp_with_index(): times = pd.date_range(start="2015-01-01", end="2015-01-02", freq="12H") temps = pd.Series([0, 10, 5], index=times) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index f403130c02..301b9c5e50 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -3,7 +3,7 @@ import numpy as np import pandas as pd -from pvlib import modelchain, pvsystem +from pvlib import modelchain, celltemp, pvsystem from pvlib.modelchain import ModelChain from pvlib.pvsystem import PVSystem from pvlib.tracking import SingleAxisTracker @@ -22,15 +22,31 @@ def system(sam_data): modules = sam_data['sandiamod'] module = 'Canadian_Solar_CS5P_220M___2009_' module_parameters = modules[module].copy() + # transfer temperature model parameters + temp_model_params = {'a': module_parameters['A'], + 'b': module_parameters['B'], + 'deltaT': module_parameters['DTC']} 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, + temperature_model_parameters = temp_model_params, inverter_parameters=inverter) return system +def get_sapm_temp_model_params(sam_data): + # SAPM temperature model parameters for Canadian_Solar_CS5P_220M in open + # rack + modules = sam_data['sandiamod'] + module = 'Canadian_Solar_CS5P_220M___2009_' + module_parameters = modules[module].copy() + # transfer temperature model parameters + return {'a': module_parameters['A'], 'b': module_parameters['B'], + 'deltaT': module_parameters['DTC']} + + @pytest.fixture def cec_dc_snl_ac_system(sam_data): modules = sam_data['cecmod'] @@ -39,11 +55,13 @@ def cec_dc_snl_ac_system(sam_data): module_parameters['b'] = 0.05 module_parameters['EgRef'] = 1.121 module_parameters['dEgdT'] = -0.0002677 + temp_model_params = get_sapm_temp_model_params(sam_data) 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, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system @@ -52,11 +70,13 @@ def cec_dc_snl_ac_system(sam_data): def cec_dc_native_snl_ac_system(sam_data): module = 'Canadian_Solar_CS5P_220M' module_parameters = sam_data['cecmod'][module].copy() + temp_model_params = get_sapm_temp_model_params(sam_data) 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, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 0447c54b48..80fc7b94cc 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -381,11 +381,10 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): def test_PVSystem_sapm_celltemp(mocker): - racking_model = 'roof_mount_cell_glassback' - a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'][racking_model] + a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'] \ + ['roof_mount_cell_glassback'] temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} - system = pvsystem.PVSystem(racking_model=racking_model, - temperature_model_parameters=temp_model_params) + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) mocker.spy(celltemp, 'sapm') temps = 25 irrads = 1000 @@ -399,7 +398,7 @@ def test_PVSystem_sapm_celltemp(mocker): def test_PVSystem_pvsyst_celltemp(mocker): racking_model = 'insulated' constant_loss_factor, wind_loss_factor = \ - celltemp.TEMP_MODEL_PARAMS['pvsyst']['insulated'] + celltemp.TEMP_MODEL_PARAMS['pvsyst'][racking_model] temp_model_params = {'constant_loss_factor': constant_loss_factor, 'wind_loss_factor': wind_loss_factor} alpha_absorption = 0.85 @@ -1227,7 +1226,9 @@ def test_PVSystem_localize_with_latlon(): def test_PVSystem___repr__(): system = pvsystem.PVSystem(module='blah', inverter='blarg', name='pv ftw') - expected = 'PVSystem: \n name: pv ftw\n surface_tilt: 0\n surface_azimuth: 180\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback' + expected = ('PVSystem: \n name: pv ftw\n surface_tilt: 0\n ' + 'surface_azimuth: 180\n module: blah\n inverter: blarg\n ' + 'albedo: 0.25\n racking_model: open_rack') assert system.__repr__() == expected @@ -1236,7 +1237,10 @@ def test_PVSystem_localize___repr__(): system = pvsystem.PVSystem(module='blah', inverter='blarg', name='pv ftw') localized_system = system.localize(latitude=32, longitude=-111) - expected = 'LocalizedPVSystem: \n name: None\n latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC\n surface_tilt: 0\n surface_azimuth: 180\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback' + expected = ('LocalizedPVSystem: \n name: None\n latitude: 32\n ' + 'longitude: -111\n altitude: 0\n tz: UTC\n ' + 'surface_tilt: 0\n surface_azimuth: 180\n module: blah\n ' + 'inverter: blarg\n albedo: 0.25\n racking_model: open_rack') assert localized_system.__repr__() == expected diff --git a/pvlib/test/test_tracking.py b/pvlib/test/test_tracking.py index 8ab8032831..61d7feb97b 100644 --- a/pvlib/test/test_tracking.py +++ b/pvlib/test/test_tracking.py @@ -440,7 +440,11 @@ def test_get_irradiance(): def test_SingleAxisTracker___repr__(): system = tracking.SingleAxisTracker(max_angle=45, gcr=.25, module='blah', inverter='blarg') - expected = 'SingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 45\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: None\n surface_azimuth: None\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback' + expected = ('SingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n ' + 'max_angle: 45\n backtrack: True\n gcr: 0.25\n ' + 'name: None\n surface_tilt: None\n surface_azimuth: None\n ' + 'module: blah\n inverter: blarg\n albedo: 0.25\n ' + 'racking_model: open_rack') assert system.__repr__() == expected @@ -451,6 +455,11 @@ def test_LocalizedSingleAxisTracker___repr__(): inverter='blarg', gcr=0.25) - expected = 'LocalizedSingleAxisTracker: \n axis_tilt: 0\n axis_azimuth: 0\n max_angle: 90\n backtrack: True\n gcr: 0.25\n name: None\n surface_tilt: None\n surface_azimuth: None\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback\n latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC' + expected = ('LocalizedSingleAxisTracker: \n axis_tilt: 0\n ' + 'axis_azimuth: 0\n max_angle: 90\n backtrack: True\n ' + 'gcr: 0.25\n name: None\n surface_tilt: None\n ' + 'surface_azimuth: None\n module: blah\n inverter: blarg\n ' + 'albedo: 0.25\n racking_model: open_rack_cell_glassback\n ' + 'latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC') assert localized_system.__repr__() == expected From 19571c9509ae0dee60685de99821ce9482212a98 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 13:18:10 -0600 Subject: [PATCH 37/98] fix ModelChain tests --- pvlib/test/test_modelchain.py | 30 +++++++++++++++++++++--------- pvlib/test/test_pvsystem.py | 5 ++++- pvlib/test/test_tracking.py | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 301b9c5e50..6a5a1840c5 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -23,15 +23,13 @@ def system(sam_data): module = 'Canadian_Solar_CS5P_220M___2009_' module_parameters = modules[module].copy() # transfer temperature model parameters - temp_model_params = {'a': module_parameters['A'], - 'b': module_parameters['B'], - 'deltaT': module_parameters['DTC']} + temp_model_params = get_sapm_temp_model_params(sam_data) 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, - temperature_model_parameters = temp_model_params, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system @@ -91,6 +89,7 @@ def pvsyst_dc_snl_ac_system(sam_data, pvsyst_module_params): system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module=module, module_parameters=module_parameters, + temperature_model_parameters={}, inverter_parameters=inverter) return system @@ -108,6 +107,7 @@ def cec_dc_adr_ac_system(sam_data): system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module=module, module_parameters=module_parameters, + temperature_model_parameters={}, inverter_parameters=inverter) return system @@ -119,6 +119,7 @@ def pvwatts_dc_snl_ac_system(sam_data): inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy() system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, + temperature_model_parameters={}, inverter_parameters=inverter) return system @@ -129,6 +130,7 @@ def pvwatts_dc_pvwatts_ac_system(sam_data): inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95} system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, + temperature_model_parameters={}, inverter_parameters=inverter_parameters) return system @@ -545,12 +547,15 @@ def test_basic_chain_required(sam_data): altitude = 700 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] + temp_model_params = get_sapm_temp_model_params(sam_data) inverters = sam_data['cecinverter'] inverter_parameters = inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] with pytest.raises(ValueError): dc, ac = modelchain.basic_chain(times, latitude, longitude, - module_parameters, inverter_parameters, + module_parameters, + temp_model_params, + inverter_parameters, altitude=altitude) @@ -564,12 +569,14 @@ def test_basic_chain_alt_az(sam_data): surface_azimuth = 0 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] + temp_model_params = get_sapm_temp_model_params(sam_data) 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, + module_parameters, temp_model_params, + inverter_parameters, surface_tilt=surface_tilt, surface_azimuth=surface_azimuth) @@ -587,12 +594,14 @@ def test_basic_chain_strategy(sam_data): altitude = 700 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] + temp_model_params = get_sapm_temp_model_params(sam_data) 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, + times, latitude, longitude, module_parameters, temp_model_params, + inverter_parameters, orientation_strategy='south_at_latitude_tilt', altitude=altitude) expected = pd.Series(np.array([ 183.522449305, -2.00000000e-02]), @@ -611,12 +620,14 @@ def test_basic_chain_altitude_pressure(sam_data): surface_azimuth = 0 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] + temp_model_params = get_sapm_temp_model_params(sam_data) 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, + module_parameters, temp_model_params, + inverter_parameters, surface_tilt=surface_tilt, surface_azimuth=surface_azimuth, pressure=93194) @@ -626,7 +637,8 @@ def test_basic_chain_altitude_pressure(sam_data): assert_series_equal(ac, expected, check_less_precise=1) dc, ac = modelchain.basic_chain(times, latitude, longitude, - module_parameters, inverter_parameters, + module_parameters, temp_model_params, + inverter_parameters, surface_tilt=surface_tilt, surface_azimuth=surface_azimuth, altitude=altitude) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 80fc7b94cc..b715a67aad 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1269,7 +1269,10 @@ def test_LocalizedPVSystem___repr__(): inverter='blarg', name='my name') - expected = 'LocalizedPVSystem: \n name: my name\n latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC\n surface_tilt: 0\n surface_azimuth: 180\n module: blah\n inverter: blarg\n albedo: 0.25\n racking_model: open_rack_cell_glassback' + expected = ('LocalizedPVSystem: \n name: my name\n latitude: 32\n ' + 'longitude: -111\n altitude: 0\n tz: UTC\n ' + 'surface_tilt: 0\n surface_azimuth: 180\n module: blah\n ' + 'inverter: blarg\n albedo: 0.25\n racking_model: open_rack') assert localized_system.__repr__() == expected diff --git a/pvlib/test/test_tracking.py b/pvlib/test/test_tracking.py index 61d7feb97b..983d8ae639 100644 --- a/pvlib/test/test_tracking.py +++ b/pvlib/test/test_tracking.py @@ -459,7 +459,7 @@ def test_LocalizedSingleAxisTracker___repr__(): 'axis_azimuth: 0\n max_angle: 90\n backtrack: True\n ' 'gcr: 0.25\n name: None\n surface_tilt: None\n ' 'surface_azimuth: None\n module: blah\n inverter: blarg\n ' - 'albedo: 0.25\n racking_model: open_rack_cell_glassback\n ' + 'albedo: 0.25\n racking_model: open_rack\n ' 'latitude: 32\n longitude: -111\n altitude: 0\n tz: UTC') assert localized_system.__repr__() == expected From 6283a564e2c399ebec38c75c06fbe59b5b2fcf69 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 14:05:27 -0600 Subject: [PATCH 38/98] modelchain fixes --- pvlib/celltemp.py | 50 +++++++++++++++++------------------ pvlib/test/test_modelchain.py | 6 ++--- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 1af1bd567f..cf65991d98 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -27,14 +27,14 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): Parameters ---------- - poa_global : float or Series - Total incident irradiance in W/m^2. + poa_global : float or Series [W/m^2] + Total incident irradiance. - temp_air : float or Series - Ambient dry bulb temperature in degrees C. + temp_air : float or Series [C] + Ambient dry bulb temperature. - wind_speed : float or Series - Wind speed in m/s at a height of 10 meters. + wind_speed : float or Series [m/s] + Wind speed at a height of 10 meters. a : float Parameter :math:`a` in :eq:`sapm1`. @@ -42,10 +42,10 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): b : float Parameter :math:`b` in :eq:`sapm1`. - deltaT : float, [C] + deltaT : float [C] Parameter :math:`\Delta T` in :eq:`sapm2`. - irrad_ref : float, default 1000 [W/m2] + irrad_ref : float, default 1000 [W/m^2] Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2`. Returns @@ -75,17 +75,17 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): its mounting. Parameter sets are provided in [1] for representative modules and mounting. - +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 - | Module | Mounting | a | b | :math:`\Delta T [C]`| # noqa: E501 - +====================+==================+=======+=========+=====================+ # noqa: E501 - | glass/cell/glass | open rack | -3.47 | -0.0594 | 3 | # noqa: E501 - +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 - | glass/cell/glass | close roof mount | -2.98 | -0.0471 | 1 | # noqa: E501 - +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 - | glass/cell/polymer | open rack | -3.56 | -0.075 | 3 | # noqa: E501 - +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 - | glass/cell/polymer | insulated back | -2.81 | -0.0455 | 0 | # noqa: E501 - +--------------------+------------------+-------+---------+---------------------+ # noqa: E501 + +---------------+----------------+-------+---------+---------------------+ + | Module | Mounting | a | b | :math:`\Delta T [C]`| + +===============+================+=======+=========+=====================+ + | glass/glass | open rack | -3.47 | -0.0594 | 3 | + +---------------+----------------+-------+---------+---------------------+ + | glass/glass | close roof | -2.98 | -0.0471 | 1 | + +---------------+----------------+-------+---------+---------------------+ + | glass/polymer | open rack | -3.56 | -0.075 | 3 | + +---------------+----------------+-------+---------+---------------------+ + | glass/polymer | insulated back | -2.81 | -0.0455 | 0 | + +---------------+----------------+-------+---------+---------------------+ References ---------- @@ -111,24 +111,24 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, Parameters ---------- - poa_global : float or Series - Total incident irradiance [:math:`\frac{W}{m^2} ]. + poa_global : float or Series [W/m^2] + Total incident irradiance. - temp_air : float or Series - Ambient dry bulb temperature [C]. + temp_air : float or Series [C] + Ambient dry bulb temperature. wind_speed : float or Series, default 1.0 [m/s] Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value is 1.0, which is the wind speed at module height used to determine NOCT. - constant_loss_factor : float, default 29.0 [:math:`\frac{W}{m^2 C}] + constant_loss_factor : float, default 29.0 [W/(m^2 C)] Combined heat loss factor coefficient. The default value is representative of freestanding modules with the rear surfaces exposed to open air (e.g., rack mounted). Parameter :math:`U_{c}` in :eq:`pvsyst`. - wind_loss_factor : float, default 0.0 [:math:`\frac{W}{m^2 C} \frac{m}{s}`] + wind_loss_factor : float, default 0.0 [(W/m^2 C)(m/s)] Combined heat loss factor influenced by wind. Parameter :math:`U_{c}` in :eq:`pvsyst`. diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 6a5a1840c5..31677e8e8e 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -186,7 +186,7 @@ def test_run_model_with_irradiance(system, location): index=times) ac = mc.run_model(times, weather=irradiance).ac - expected = pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), + expected = pd.Series(np.array([187.80746495e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -198,7 +198,7 @@ def test_run_model_perez(system, location): index=times) ac = mc.run_model(times, weather=irradiance).ac - expected = pd.Series(np.array([ 190.194545796, -2.00000000e-02]), + expected = pd.Series(np.array([187.94295642, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -211,7 +211,7 @@ def test_run_model_gueymard_perez(system, location): index=times) ac = mc.run_model(times, weather=irradiance).ac - expected = pd.Series(np.array([ 190.194760203, -2.00000000e-02]), + expected = pd.Series(np.array([187.94317405, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) From 719b9b842cc82b22f6704a9ce9a7072cfe16d5ee Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 25 Jul 2019 14:05:58 -0600 Subject: [PATCH 39/98] modelchain test fixes --- pvlib/test/test_modelchain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 31677e8e8e..5b1852a2b9 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -245,7 +245,8 @@ def test_run_model_with_weather(system, location, weather, mocker): def test_run_model_tracker(system, location, weather, mocker): system = SingleAxisTracker(module_parameters=system.module_parameters, - inverter_parameters=system.inverter_parameters) + temperature_model_parameters=system.temperature_model_parameters, + inverter_parameters=system.inverter_parameters) mocker.spy(system, 'singleaxis') mc = ModelChain(system, location) mc.run_model(weather.index, weather=weather) From c527075b3369796c42fa47e9141b2781df23a436 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 09:27:17 -0600 Subject: [PATCH 40/98] modelchain test fixes --- pvlib/celltemp.py | 42 +++++++++++++++++------------------ pvlib/test/test_celltemp.py | 21 +++++++----------- pvlib/test/test_modelchain.py | 36 +++++++++++++++--------------- 3 files changed, 47 insertions(+), 52 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index cf65991d98..b19667f4be 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -27,14 +27,14 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): Parameters ---------- - poa_global : float or Series [W/m^2] - Total incident irradiance. + poa_global : float or Series + Total incident irradiance [W/m^2]. - temp_air : float or Series [C] - Ambient dry bulb temperature. + temp_air : float or Series + Ambient dry bulb temperature [C]. - wind_speed : float or Series [m/s] - Wind speed at a height of 10 meters. + wind_speed : float or Series + Wind speed at a height of 10 meters [m/s]. a : float Parameter :math:`a` in :eq:`sapm1`. @@ -42,11 +42,11 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): b : float Parameter :math:`b` in :eq:`sapm1`. - deltaT : float [C] - Parameter :math:`\Delta T` in :eq:`sapm2`. + deltaT : float + Parameter :math:`\Delta T` in :eq:`sapm2` [C]. - irrad_ref : float, default 1000 [W/m^2] - Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2`. + irrad_ref : float, default 1000 + Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2` [W/m^2]. Returns -------- @@ -111,26 +111,26 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, Parameters ---------- - poa_global : float or Series [W/m^2] - Total incident irradiance. + poa_global : float or Series + Total incident irradiance [W/m^2]. - temp_air : float or Series [C] - Ambient dry bulb temperature. + temp_air : float or Series + Ambient dry bulb temperature [C]. - wind_speed : float or Series, default 1.0 [m/s] + wind_speed : float or Series, default 1.0 Wind speed in m/s measured at the same height for which the wind loss - factor was determined. The default value is 1.0, which is the wind - speed at module height used to determine NOCT. + factor was determined. The default value 1.0 m/2 is the wind + speed at module height used to determine NOCT. [m/s] - constant_loss_factor : float, default 29.0 [W/(m^2 C)] + constant_loss_factor : float, default 29.0 Combined heat loss factor coefficient. The default value is representative of freestanding modules with the rear surfaces exposed to open air (e.g., rack mounted). Parameter :math:`U_{c}` in - :eq:`pvsyst`. + :eq:`pvsyst` [W/(m^2 C)]. - wind_loss_factor : float, default 0.0 [(W/m^2 C)(m/s)] + wind_loss_factor : float, default 0.0 Combined heat loss factor influenced by wind. Parameter :math:`U_{c}` - in :eq:`pvsyst`. + in :eq:`pvsyst` [(W/m^2 C)(m/s)]. eta_m : numeric, default 0.1 Module external efficiency as a fraction, i.e., DC power / poa_global. diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 2403eabbee..af88378e4a 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -41,30 +41,25 @@ def test_sapm_celltemp_with_index(celltemp_sapm_default): assert_frame_equal(expected, pvtemps) -def test_pvsyst_celltemp_default(): +def test_celltemp_pvsyst_default(): default = celltemp.pvsyst(900, 20, 5) assert_allclose(default['temp_cell'], 45.137, 0.001) -def test_pvsyst_celltemp_non_model(): - tup_non_model = pvsystem.pvsyst_celltemp(900, 20, wind_speed=5.0, - constant_loss_factor=23.5, - wind_loss_factor=6.25, eta_m=0.1) - assert_allclose(tup_non_model['temp_cell'], 33.315, 0.001) +def test_celltemp_pvsyst_kwargs(): + result = celltemp.pvsyst(900, 20, wind_speed=5.0, + constant_loss_factor=23.5, wind_loss_factor=6.25, + eta_m=0.1) + assert_allclose(result['temp_cell'], 33.315, 0.001) - list_non_model = pvsystem.pvsyst_celltemp(900, 20, wind_speed=5.0, - constant_loss_factor=26.5, - wind_loss_factor=7.68, eta_m=0.1) - assert_allclose(list_non_model['temp_cell'], 31.233, 0.001) - -def test_pvsyst_celltemp_with_index(): +def test_celltemp_pvsyst_with_index(): times = pd.date_range(start="2015-01-01", end="2015-01-02", freq="12H") temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - pvtemps = pvsystem.pvsyst_celltemp(irrads, temps, wind_speed=winds) + pvtemps = celltemp.pvsyst(irrads, temps, wind_speed=winds) expected = pd.DataFrame([0.0, 23.96551, 5.0], index=times, columns=['temp_cell']) assert_frame_equal(expected, pvtemps) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 5b1852a2b9..817379efd2 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -17,13 +17,24 @@ from conftest import fail_on_pvlib_version, requires_scipy, requires_tables +def _get_sapm_temp_model_params(sam_data): + # SAPM temperature model parameters for Canadian_Solar_CS5P_220M + # (glass/polymer) in open rack + modules = sam_data['sandiamod'] + module = 'Canadian_Solar_CS5P_220M___2009_' + module_parameters = modules[module].copy() + # transfer temperature model parameters + return {'a': module_parameters['A'], 'b': module_parameters['B'], + 'deltaT': module_parameters['DTC']} + + @pytest.fixture def system(sam_data): modules = sam_data['sandiamod'] module = 'Canadian_Solar_CS5P_220M___2009_' module_parameters = modules[module].copy() # transfer temperature model parameters - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) 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, @@ -34,17 +45,6 @@ def system(sam_data): return system -def get_sapm_temp_model_params(sam_data): - # SAPM temperature model parameters for Canadian_Solar_CS5P_220M in open - # rack - modules = sam_data['sandiamod'] - module = 'Canadian_Solar_CS5P_220M___2009_' - module_parameters = modules[module].copy() - # transfer temperature model parameters - return {'a': module_parameters['A'], 'b': module_parameters['B'], - 'deltaT': module_parameters['DTC']} - - @pytest.fixture def cec_dc_snl_ac_system(sam_data): modules = sam_data['cecmod'] @@ -53,7 +53,7 @@ def cec_dc_snl_ac_system(sam_data): module_parameters['b'] = 0.05 module_parameters['EgRef'] = 1.121 module_parameters['dEgdT'] = -0.0002677 - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) 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, @@ -68,7 +68,7 @@ def cec_dc_snl_ac_system(sam_data): def cec_dc_native_snl_ac_system(sam_data): module = 'Canadian_Solar_CS5P_220M' module_parameters = sam_data['cecmod'][module].copy() - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) 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, @@ -119,7 +119,6 @@ def pvwatts_dc_snl_ac_system(sam_data): inverter = inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'].copy() system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, - temperature_model_parameters={}, inverter_parameters=inverter) return system @@ -127,10 +126,11 @@ def pvwatts_dc_snl_ac_system(sam_data): @pytest.fixture def pvwatts_dc_pvwatts_ac_system(sam_data): module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} + temp_model_params = _get_sapm_temp_model_params(sam_data) inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95} system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, - temperature_model_parameters={}, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter_parameters) return system @@ -174,7 +174,7 @@ def test_run_model(system, location): with pytest.warns(pvlibDeprecationWarning): ac = mc.run_model(times).ac - expected = pd.Series(np.array([ 183.522449305, -2.00000000e-02]), + expected = pd.Series(np.array([183.522449305, -2.00000000e-02]), index=times) assert_series_equal(ac, expected, check_less_precise=1) @@ -186,7 +186,7 @@ def test_run_model_with_irradiance(system, location): index=times) ac = mc.run_model(times, weather=irradiance).ac - expected = pd.Series(np.array([187.80746495e+02, -2.00000000e-02]), + expected = pd.Series(np.array([187.80746495, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) From 411c0dc3fab55a586efbd997bfed04687fd52e1c Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 09:40:49 -0600 Subject: [PATCH 41/98] more modelchain test fixes --- pvlib/test/test_modelchain.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 817379efd2..638587fb05 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -84,12 +84,13 @@ def pvsyst_dc_snl_ac_system(sam_data, pvsyst_module_params): module = 'PVsyst test module' module_parameters = pvsyst_module_params module_parameters['b'] = 0.05 + temp_model_params = _get_sapm_temp_model_params(sam_data) 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, - temperature_model_parameters={}, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system @@ -102,12 +103,13 @@ def cec_dc_adr_ac_system(sam_data): module_parameters['b'] = 0.05 module_parameters['EgRef'] = 1.121 module_parameters['dEgdT'] = -0.0002677 + temp_model_params = _get_sapm_temp_model_params(sam_data) inverters = sam_data['adrinverter'] inverter = inverters['Zigor__Sunzet_3_TL_US_240V__CEC_2011_'].copy() system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module=module, module_parameters=module_parameters, - temperature_model_parameters={}, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system @@ -115,10 +117,12 @@ def cec_dc_adr_ac_system(sam_data): @pytest.fixture def pvwatts_dc_snl_ac_system(sam_data): module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} + temp_model_params = _get_sapm_temp_model_params(sam_data) 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_parameters=module_parameters, + temperature_model_parameters=temp_model_params, inverter_parameters=inverter) return system From 0d0349af989ef4f222fe7095ffabbee57682d739 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 10:52:31 -0600 Subject: [PATCH 42/98] add infer_temp_model --- pvlib/celltemp.py | 12 ++++++------ pvlib/modelchain.py | 19 +++++++++++++++---- pvlib/test/test_modelchain.py | 21 +++++++++++++++++++-- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index b19667f4be..42599b8240 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -150,13 +150,13 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, .. math:: :label: pvsyst - T_{C} = T_{a} + \frac{\alpha E (1 - \eta_{m})}{U_{c} + U_{v} WS} + T_{C} = T_{a} + \frac{\alpha E (1 - \eta_{m})}{U_{c} + U_{v} \times WS} - Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and - ambient air temperature :math:`T_{a}` (C). Model output is cell temperature - :math:`T_{C}`. Model parameters depend both on the module construction and - its mounting. Parameter sets are provided in [1] for open (freestanding) - close (insulated) mounting configurations. + Inputs to the model are plane-of-array irradiance :math:`E` (W/m2), ambient + air temperature :math:`T_{a}` (C) and wind speed :math:`WS` (m/s). Model + output is cell temperature :math:`T_{C}`. Model parameters depend both on + the module construction and its mounting. Parameter sets are provided in + [1] for open (freestanding) close (insulated) mounting configurations. +--------------+---------------+---------------+ | Mounting | :math:`U_{c}` | :math:`U_{v}` | diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index f1481ec0a1..3d37f9d617 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -48,13 +48,16 @@ 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. See pvsystem.sapm for + details. temperature_model_parameters : None, dict or Series, default None. - Temperature model parameters as defined by the SAPM. + Temperature model parameters as defined by the SAPM. See celltemp.sapm + for details. inverter_parameters : None, dict or Series - Inverter parameters as defined by the CEC. + Inverter parameters as defined by the CEC. See pvsystem.snlinverter for + details. irradiance : None or DataFrame, default None If None, calculates clear sky data. @@ -685,7 +688,15 @@ def temp_model(self, model): self._temp_model = partial(model, self) def infer_temp_model(self): - raise NotImplementedError + params = set(self.system.temperature_model_parameters.keys()) + if set(['a', 'b', 'deltaT']) <= params: + return self.sapm_temp + elif set(['constant_loss_factor', 'wind_loss_factor']) <= params: + return self.pvsyst_temp + else: + raise ValueError('could not infer temperature model from ' + 'system.temperature_module_parameters {}.' + .format(self.system.temperature_model_parameters)) def sapm_temp(self): self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 638587fb05..2c59aec46b 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -28,6 +28,11 @@ def _get_sapm_temp_model_params(sam_data): 'deltaT': module_parameters['DTC']} +def _get_pvsyst_temp_model_params(): + # Pvsyst temperature model parameters for freestanding system + return {'constant_loss_factor': 29.0, 'wind_loss_factor': 0.0} + + @pytest.fixture def system(sam_data): modules = sam_data['sandiamod'] @@ -84,7 +89,7 @@ def pvsyst_dc_snl_ac_system(sam_data, pvsyst_module_params): module = 'PVsyst test module' module_parameters = pvsyst_module_params module_parameters['b'] = 0.05 - temp_model_params = _get_sapm_temp_model_params(sam_data) + temp_model_params = _get_pvsyst_temp_model_params() 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, @@ -315,6 +320,18 @@ def test_infer_spectral_model(location, system, cec_dc_snl_ac_system, assert isinstance(mc, ModelChain) +@pytest.mark.parametrize('temp_model', [ + 'sapm', pytest.param('pvsyst', marks=requires_scipy)]) +def test_infer_temp_model(location, system, pvsyst_dc_snl_ac_system, + temp_model): + dc_systems = {'sapm': system, + 'pvsyst': pvsyst_dc_snl_ac_system} + system = dc_systems[temp_model] + mc = ModelChain(system, location, + orientation_strategy='None', aoi_model='physical') + assert isinstance(mc, ModelChain) + + def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather, mocker): m = mocker.spy(sys.modules[__name__], 'poadc') @@ -552,7 +569,7 @@ def test_basic_chain_required(sam_data): altitude = 700 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) inverters = sam_data['cecinverter'] inverter_parameters = inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] From 520c10600ea6deeafd4535a054ef91ed5d5d1bcf Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 11:04:04 -0600 Subject: [PATCH 43/98] lint, test fixes --- pvlib/pvsystem.py | 3 ++- pvlib/test/test_modelchain.py | 4 ++-- pvlib/test/test_pvsystem.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index ee20a6d980..fbf76f7f92 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -556,7 +556,8 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, Absorption coefficient model : string, default 'freestanding' - Heat loss model factors to be used. See celltemp.pvsyst for details. + Heat loss model factors to be used. See celltemp.pvsyst for + details. Returns ------- diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 2c59aec46b..0c1a6640f0 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -591,7 +591,7 @@ def test_basic_chain_alt_az(sam_data): surface_azimuth = 0 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) inverters = sam_data['cecinverter'] inverter_parameters = inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] @@ -616,7 +616,7 @@ def test_basic_chain_strategy(sam_data): altitude = 700 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) inverters = sam_data['cecinverter'] inverter_parameters = inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index b715a67aad..36e83e9722 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -381,7 +381,7 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): def test_PVSystem_sapm_celltemp(mocker): - a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm'] \ + a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm']\ ['roof_mount_cell_glassback'] temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) From 69a212fb7a670d03af0fd384f1a419337456c533 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 11:33:49 -0600 Subject: [PATCH 44/98] yet more modelchain test fixes --- pvlib/test/test_modelchain.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 0c1a6640f0..d3cda7b861 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -293,13 +293,20 @@ def test_infer_dc_model(system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, 'pvsyst': 'calcparams_pvsyst', 'singlediode': 'calcparams_desoto', 'pvwatts_dc': 'pvwatts_dc'} + temp_model_function = {'sapm': 'sapm', + 'cec': 'sapm', + 'desoto': 'sapm', + 'pvsyst': 'pvsyst', + 'singlediode': 'sapm', + 'pvwatts_dc': 'sapm'} system = dc_systems[dc_model] # remove Adjust from model parameters for desoto, singlediode if dc_model in ['desoto', 'singlediode']: system.module_parameters.pop('Adjust') m = mocker.spy(system, dc_model_function[dc_model]) mc = ModelChain(system, location, - aoi_model='no_loss', spectral_model='no_loss') + aoi_model='no_loss', spectral_model='no_loss', + temp_model=temp_model_function[dc_model]) mc.run_model(weather.index, weather=weather) assert m.call_count == 1 assert isinstance(mc.dc, (pd.Series, pd.DataFrame)) @@ -328,7 +335,8 @@ def test_infer_temp_model(location, system, pvsyst_dc_snl_ac_system, 'pvsyst': pvsyst_dc_snl_ac_system} system = dc_systems[temp_model] mc = ModelChain(system, location, - orientation_strategy='None', aoi_model='physical') + orientation_strategy='None', aoi_model='physical', + spectral_model='no_loss') assert isinstance(mc, ModelChain) @@ -642,7 +650,7 @@ def test_basic_chain_altitude_pressure(sam_data): surface_azimuth = 0 modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] - temp_model_params = get_sapm_temp_model_params(sam_data) + temp_model_params = _get_sapm_temp_model_params(sam_data) inverters = sam_data['cecinverter'] inverter_parameters = inverters[ 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] From 683dd9cc92076e90c69056ff985353915afaa948 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 12:52:42 -0600 Subject: [PATCH 45/98] update whatsnew, improve coverage --- docs/sphinx/source/whatsnew/v0.7.0.rst | 36 +++++++++++++++++++------- pvlib/modelchain.py | 2 +- pvlib/test/test_modelchain.py | 2 +- pvlib/test/test_pvsystem.py | 3 +-- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 6af01ff9e4..68d1a875cd 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -12,26 +12,44 @@ compatibility notes. API Changes ~~~~~~~~~~~ -* Moved functions for cell temperature and constant `TEMP_MODEL_PARAMS` from `pvsystem.py` to `celltemp.py`. (:issue:`678') -* Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to - `celltemp.sapm` and `celltemp.pvsyst`. (:issue:`678') -* Argument order and output type of `pvsystem.sapm_celltemp`, `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have been altered to be consistent among cell temperature model functions. -* Removed `model` as a kwarg from `celltemp.sapm` and `celltemp.pvsyst`. These -functions now require model parameters as kwargs. -* Implemented `pvsyst` as an option for `ModelChain.temp_model`. (:issue:`678') +* Changes to functions and methods for cell temperature models: + (:issue:`678') + - Moved functions for cell temperature and constant + `TEMP_MODEL_PARAMS` from `pvsystem.py` to `celltemp.py`. + - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` + to `celltemp.sapm` and `celltemp.pvsyst`. + - Argument order and output type of `pvsystem.sapm_celltemp`, + `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have + been altered to be consistent among cell temperature model + functions. + - Removed `model` as a kwarg from `celltemp.sapm` and + `celltemp.pvsyst`. These functions now require model-specific + parameters as kwargs. + - Added attribute `PVSystem.module_type` (str) to record module + front and back materials, default is `glass_polymer`. + - Added attribute `PVSystem.temperature_model_parameters` (dict) + to contain temperature model parameters. + - Changed meaning of `PVSystem.racking_model` to describe racking + only, e.g., default is `open_rack`. + - Implemented `pvsyst` as an option for `ModelChain.temp_model`. + - `ModelChain.temp_model` now defaults to `None. The temperature + model can be inferred from + `PVSystem.temperature_model_parameters`. Enhancements ~~~~~~~~~~~~ Bug fixes ~~~~~~~~~ -* Fix handling of keyword arguments in `forecasts.get_processed_data`. +* Fix handling of keyword arguments in forecasts.get_processed_data`. (:issue:`745`) Testing ~~~~~~~ -* Added 30 minutes to timestamps in `test_psm3.csv` to match change in NSRDB (:issue:`733`) +* Added 30 minutes to timestamps in `test_psm3.csv` to match change + in NSRDB (:issue:`733`) * Added tests for methods in bifacial.py. +* Added tests for changes to cell temperature models. Documentation ~~~~~~~~~~~~~ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 3d37f9d617..ba98696570 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -305,7 +305,7 @@ def __init__(self, system, location, solar_position_method='nrel_numpy', airmass_model='kastenyoung1989', dc_model=None, ac_model=None, aoi_model=None, - spectral_model=None, temp_model='sapm', + spectral_model=None, temp_model=None, losses_model='no_loss', name=None, **kwargs): self.name = name diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index d3cda7b861..5723066974 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -3,7 +3,7 @@ import numpy as np import pandas as pd -from pvlib import modelchain, celltemp, pvsystem +from pvlib import modelchain, pvsystem from pvlib.modelchain import ModelChain from pvlib.pvsystem import PVSystem from pvlib.tracking import SingleAxisTracker diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 36e83e9722..2292441960 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -381,8 +381,7 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): def test_PVSystem_sapm_celltemp(mocker): - a, b, deltaT = celltemp.TEMP_MODEL_PARAMS['sapm']\ - ['roof_mount_cell_glassback'] + a, b, deltaT = (-3.47, -.0594, 3) # open_rack_cell_glassback temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) mocker.spy(celltemp, 'sapm') From d1244df5f14d594f332579b4e0030cbf20a03ad6 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 15:32:07 -0600 Subject: [PATCH 46/98] fix deprecated test --- pvlib/test/test_modelchain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 5723066974..ffab4ae539 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -545,11 +545,14 @@ def test_deprecated_07(): # does not matter what the parameters are, just fake it until we make it module_parameters = {'R_sh_ref': 1, 'a_ref': 1, 'I_o_ref': 1, 'alpha_sc': 1, 'I_L_ref': 1, 'R_s': 1} - system = PVSystem(module_parameters=module_parameters) + temp_model_params = {'a': -3.5, 'b': -0.05, 'deltaT': 3} + system = PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='singlediode', # this should fail after 0.7 aoi_model='no_loss', spectral_model='no_loss', + temp_model='sapm', ac_model='snlinverter') From 488c3dd7d8b47b5ac07ff1d988c5ba5f0ff95c32 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 26 Jul 2019 21:26:46 -0600 Subject: [PATCH 47/98] fix import --- pvlib/pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 937796bf1e..e74d457f00 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -13,8 +13,8 @@ from pvlib._deprecation import deprecated from pvlib import atmosphere, celltemp, irradiance, singlediode as _singlediode -from pvlib import tools, cosd -from pvlib.tools import _build_kwargs +from pvlib import tools +from pvlib.tools import _build_kwargs, cosd from pvlib.location import Location From 82c47c0a172fd3fd1d0553b915bbd7e1fadd4b42 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 27 Jul 2019 07:17:30 -0600 Subject: [PATCH 48/98] one more MC test fix --- pvlib/test/test_modelchain.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index ffab4ae539..6907ba685d 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -561,7 +561,9 @@ def test_deprecated_07(): def test_deprecated_clearsky_07(): # explicit system creation call because fail_on_pvlib_version # does not support decorators. - system = PVSystem(module_parameters={'pdc0': 1, 'gamma_pdc': -0.003}) + system = PVSystem(module_parameters={'pdc0': 1, 'gamma_pdc': -0.003}, + temperature_model_parameters={'a': -3.5, 'b': -0.05, + 'deltaT': 3}) location = Location(32.2, -110.9) mc = ModelChain(system, location, dc_model='pvwatts', ac_model='pvwatts', aoi_model='no_loss', spectral_model='no_loss') From 2a540ceb0a6699aac1c9c23ce124ad29e6eb8c19 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 30 Jul 2019 15:43:42 -0600 Subject: [PATCH 49/98] rename functions, change output type --- pvlib/celltemp.py | 135 ++++++++++++++++++++++++++---------- pvlib/test/test_celltemp.py | 85 +++++++++++++++-------- 2 files changed, 155 insertions(+), 65 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 42599b8240..42b3e8a932 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -6,31 +6,31 @@ """ import numpy as np -import pandas as pd -TEMP_MODEL_PARAMS = { - 'sapm': {'open_rack_cell_glassback': (-3.47, -.0594, 3), - 'roof_mount_cell_glassback': (-2.98, -.0471, 1), - 'open_rack_cell_polymerback': (-3.56, -.0750, 3), - 'insulated_back_polymerback': (-2.81, -.0455, 0), - 'open_rack_polymer_thinfilm_steel': (-3.58, -.113, 3), - '22x_concentrator_tracker': (-3.23, -.130, 13)}, - 'pvsyst': {'freestanding': (29.0, 0), 'insulated': (15.0, 0)} +TEMPERATURE_MODEL_PARAMETERS = { + 'sapm': { + 'open_rack_glass_glass': {'a': -3.47, 'b': -.0594, 'deltaT': 3}, + 'close_mount_glass_glass': {'a': -2.98, 'b': -.0471, 'deltaT': 1}, + 'open_rack_glass_polymer': {'a': -3.56, 'b': -.0750, 'deltaT': 3}, + 'insulated_back_glass_polymer': {'a': -2.81, 'b': -.0455, 'deltaT': 0}, + }, + 'pvsyst': {'freestanding': {'u_c': 29.0, 'u_v': 0}, + 'insulated': {'u_c': 15.0, 'u_v': 0}} } -def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): +def sapm_cell(poa_global, air_temperature, wind_speed, a, b, deltaT, + irrad_ref=1000): r''' - Estimate cell and module temperatures per the Sandia PV Array - Performance Model [1]. + Calculate cell temperature per the Sandia PV Array Performance Model [1]. Parameters ---------- poa_global : float or Series Total incident irradiance [W/m^2]. - temp_air : float or Series + air_temperature : float or Series Ambient dry bulb temperature [C]. wind_speed : float or Series @@ -49,14 +49,13 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2` [W/m^2]. Returns - -------- - DataFrame with columns 'temp_cell' and 'temp_module'. - Values in degrees C. + ------- + float or Series, values in degrees C. Notes ----- - The model for cell temperature :math:`T_{C}` and module temperature - :math:`T_{m}` is given by a pair of equations (Eq. 11 and 12 in [1]). + The model for cell temperature :math:`T_{C}` is given by a pair of + equations (Eq. 11 and 12 in [1]). .. math:: :label: sapm1 @@ -68,6 +67,73 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): T_{C} = T_{m} + \frac{E}{E_{0}} \Delta T + The module back surface temperature :math:`T_{m}` is implemented in + ``cell_temperature.sapm_module``. + + Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and + ambient air temperature :math:`T_{a}` (C). Model parameters depend both on + the module construction and its mounting. Parameter sets are provided in + [1] for representative modules and mounting, and are coded for convenience + in ``cell_temperature.TEMPERATURE_MODEL_PARAMETERS``. + + +---------------+----------------+-------+---------+---------------------+ + | Module | Mounting | a | b | :math:`\Delta T [C]`| + +===============+================+=======+=========+=====================+ + | glass/glass | open rack | -3.47 | -0.0594 | 3 | + +---------------+----------------+-------+---------+---------------------+ + | glass/glass | close roof | -2.98 | -0.0471 | 1 | + +---------------+----------------+-------+---------+---------------------+ + | glass/polymer | open rack | -3.56 | -0.075 | 3 | + +---------------+----------------+-------+---------+---------------------+ + | glass/polymer | insulated back | -2.81 | -0.0455 | 0 | + +---------------+----------------+-------+---------+---------------------+ + + References + ---------- + [1] King, D. et al, 2004, "Sandia Photovoltaic Array Performance + Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, + NM. + + ''' + module_temperature = sapm_module(poa_global, air_temperature, wind_speed, + a, b) + return module_temperature + (poa_global / irrad_ref) * (deltaT) + + +def sapm_module(poa_global, air_temperature, wind_speed, a, b): + r''' + Calculate module back surface temperature per the Sandia PV Array + Performance Model [1]. + + Parameters + ---------- + poa_global : float or Series + Total incident irradiance [W/m^2]. + + air_temperature : float or Series + Ambient dry bulb temperature [C]. + + wind_speed : float or Series + Wind speed at a height of 10 meters [m/s]. + + a : float + Parameter :math:`a` in :eq:`sapm1`. + + b : float + Parameter :math:`b` in :eq:`sapm1`. + + Returns + ------- + float or Series, values in degrees C. + + Notes + ----- + The model for module temperature :math:`T_{m}` is given by Eq. 11 in [1]. + + .. math:: + :label: sapm1 + T_{m} = E \times \exp (a + b \times WS) + T_{a} + Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and ambient air temperature :math:`T_{a}` (C). Model outputs are surface temperature at the back of the module :math:`T_{m}` and cell temperature @@ -94,27 +160,22 @@ def sapm(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): NM. ''' - temp_module = pd.Series(poa_global * np.exp(a + b * wind_speed) + temp_air) - temp_cell = temp_module + (poa_global / irrad_ref) * (deltaT) - return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) + return poa_global * np.exp(a + b * wind_speed) + air_temperature -def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, - wind_loss_factor=0.0, eta_m=0.1, alpha_absorption=0.9): +def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, + constant_loss_factor=29.0, wind_loss_factor=0.0, eta_m=0.1, + alpha_absorption=0.9): r""" Calculate cell temperature using an empirical heat loss factor model as implemented in PVsyst. - The heat loss factors provided through the 'model' argument - represent the combined effect of convection, radiation and conduction, - and their values are experimentally determined. - Parameters ---------- poa_global : float or Series Total incident irradiance [W/m^2]. - temp_air : float or Series + air_temperature : float or Series Ambient dry bulb temperature [C]. wind_speed : float or Series, default 1.0 @@ -122,14 +183,14 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, factor was determined. The default value 1.0 m/2 is the wind speed at module height used to determine NOCT. [m/s] - constant_loss_factor : float, default 29.0 + u_c : float, default 29.0 Combined heat loss factor coefficient. The default value is representative of freestanding modules with the rear surfaces exposed to open air (e.g., rack mounted). Parameter :math:`U_{c}` in :eq:`pvsyst` [W/(m^2 C)]. - wind_loss_factor : float, default 0.0 - Combined heat loss factor influenced by wind. Parameter :math:`U_{c}` + u_v : float, default 0.0 + Combined heat loss factor influenced by wind. Parameter :math:`U_{v}` in :eq:`pvsyst` [(W/m^2 C)(m/s)]. eta_m : numeric, default 0.1 @@ -141,7 +202,7 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, Returns ------- - DataFrame with columns 'temp_cell', values in degrees Celsius + float or Series, values in degrees Celsius Notes ----- @@ -155,8 +216,10 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, Inputs to the model are plane-of-array irradiance :math:`E` (W/m2), ambient air temperature :math:`T_{a}` (C) and wind speed :math:`WS` (m/s). Model output is cell temperature :math:`T_{C}`. Model parameters depend both on - the module construction and its mounting. Parameter sets are provided in - [1] for open (freestanding) close (insulated) mounting configurations. + the module construction and its mounting. Parameters are provided in + [1] for open (freestanding) and close (insulated) mounting configurations. + The heat loss factors provided represent the combined effect of convection, + radiation and conduction, and their values are experimentally determined. +--------------+---------------+---------------+ | Mounting | :math:`U_{c}` | :math:`U_{v}` | @@ -178,6 +241,4 @@ def pvsyst(poa_global, temp_air, wind_speed=1.0, constant_loss_factor=29.0, total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor heat_input = poa_global * alpha_absorption * (1 - eta_m) temp_difference = heat_input / total_loss_factor - temp_cell = pd.Series(temp_air + temp_difference) - - return pd.DataFrame({'temp_cell': temp_cell}) + return air_temperature + temp_difference diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index af88378e4a..06d2880b2a 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -5,9 +5,10 @@ @author: cwhanse """ import pandas as pd +import numpy as np import pytest -from pandas.util.testing import assert_frame_equal +from pandas.util.testing import assert_series_equal from numpy.testing import assert_allclose from pvlib import celltemp, pvsystem @@ -17,52 +18,80 @@ @pytest.fixture -def celltemp_sapm_default(): - return celltemp.TEMP_MODEL_PARAMS['sapm']['open_rack_cell_glassback'] +def sapm_default(): + return celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ + ['open_rack_glass_glass'] -def test_sapm_celltemp(celltemp_sapm_default): - a, b, deltaT = celltemp_sapm_default - default = celltemp.sapm(900, 20, 5, a, b, deltaT) - assert_allclose(default['temp_cell'], 43.509, 3) - assert_allclose(default['temp_module'], 40.809, 3) +def test_sapm_cell(sapm_default): + default = celltemp.sapm_cell(900, 20, 5, sapm_default['a'], + sapm_default['b'], sapm_default['deltaT']) + assert_allclose(default, 43.509, 3) -def test_sapm_celltemp_with_index(celltemp_sapm_default): - a, b, deltaT = celltemp_sapm_default +def test_sapm_module(sapm_default): + default = celltemp.sapm(900, 20, 5, sapm_default['a'], sapm_default['b']) + assert_allclose(default, 40.809, 3) + + +def test_sapm_ndarray(sapm_default): + temps = np.array([0, 10, 5]) + irrads = np.array([0, 500, 0]) + winds = np.array([10, 5, 0]) + cell_temps = celltemp.sapm_cell(irrads, temps, winds, sapm_default['a'], + sapm_default['b'], sapm_default['deltaT']) + module_temps = celltemp.sapm_module(irrads, temps, winds, + sapm_default['a'], sapm_default['b']) + expected_cell = np.array([0., 23.06066166, 5.]) + expected_module = np.array([0., 21.56066166, 5.]) + assert_allclose(expected_cell, cell_temps, 3) + assert_allclose(expected_module, module_temps, 3) + + +def test_sapm_series(sapm_default): times = pd.date_range(start='2015-01-01', end='2015-01-02', freq='12H') temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - pvtemps = celltemp.sapm(irrads, temps, winds, a, b, deltaT) - expected = pd.DataFrame({'temp_cell': [0., 23.06066166, 5.], - 'temp_module': [0., 21.56066166, 5.]}, - index=times) - assert_frame_equal(expected, pvtemps) + cell_temps = celltemp.sapm_cell(irrads, temps, winds, sapm_default['a'], + sapm_default['b'], sapm_default['deltaT']) + module_temps = celltemp.sapm_module(irrads, temps, winds, + sapm_default['a'], sapm_default['b']) + expected_cell = pd.Series([0., 23.06066166, 5.], index=times) + expected_module = pd.Series([0., 21.56066166, 5.], index=times) + assert_series_equal(expected_cell, cell_temps) + assert_series_equal(expected_module, module_temps) + + +def test_pvsyst_cell_default(): + result = celltemp.pvsyst_cell(900, 20, 5) + assert_allclose(result, 45.137, 0.001) -def test_celltemp_pvsyst_default(): - default = celltemp.pvsyst(900, 20, 5) - assert_allclose(default['temp_cell'], 45.137, 0.001) +def test_pvsyst_cell_kwargs(): + result = celltemp.pvsyst_cell(900, 20, wind_speed=5.0, u_c=23.5, u_v=6.25, + eta_m=0.1) + assert_allclose(result, 33.315, 0.001) -def test_celltemp_pvsyst_kwargs(): - result = celltemp.pvsyst(900, 20, wind_speed=5.0, - constant_loss_factor=23.5, wind_loss_factor=6.25, - eta_m=0.1) - assert_allclose(result['temp_cell'], 33.315, 0.001) +def test_pvsyst_cell_ndarray(): + temps = np.array([0, 10, 5]) + irrads = np.array([0, 500, 0]) + winds = np.array([10, 5, 0]) + result = celltemp.pvsyst_cell(irrads, temps, wind_speed=winds) + expected = np.array([0.0, 23.96551, 5.0]) + assert_allclose(expected, result, 3) -def test_celltemp_pvsyst_with_index(): +def test_pvsyst_cell_series(): times = pd.date_range(start="2015-01-01", end="2015-01-02", freq="12H") temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - pvtemps = celltemp.pvsyst(irrads, temps, wind_speed=winds) - expected = pd.DataFrame([0.0, 23.96551, 5.0], index=times, - columns=['temp_cell']) - assert_frame_equal(expected, pvtemps) + result = celltemp.pvsyst_cell(irrads, temps, wind_speed=winds) + expected = pd.Series([0.0, 23.96551, 5.0], index=times) + assert_series_equal(expected, result) @fail_on_pvlib_version('0.8') From dfd939ed957276a0d75cec4be5e880fbd2bf681f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 10:25:45 -0600 Subject: [PATCH 50/98] change argument names, add parameter_set lookup --- pvlib/celltemp.py | 19 ++++++++--- pvlib/pvsystem.py | 63 ++++++++++++++++++++----------------- pvlib/test/test_celltemp.py | 8 +++++ pvlib/test/test_pvsystem.py | 63 +++++++++++++++++++++++++++---------- 4 files changed, 104 insertions(+), 49 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 42b3e8a932..143ca0d2bd 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -20,6 +20,18 @@ } +def _temperature_model_params(model, parameter_set): + try: + params = TEMPERATURE_MODEL_PARAMETERS[model] + return params[parameter_set] + except KeyError: + msg = ('{} is not a named set of parameters for the {} cell' + ' temperature model.' + ' See pvlib.celltemp.TEMPERATURE_MODEL_PARAMETERS' + ' for names'.format(parameter_set, model)) + raise KeyError(msg) + + def sapm_cell(poa_global, air_temperature, wind_speed, a, b, deltaT, irrad_ref=1000): r''' @@ -163,9 +175,8 @@ def sapm_module(poa_global, air_temperature, wind_speed, a, b): return poa_global * np.exp(a + b * wind_speed) + air_temperature -def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, - constant_loss_factor=29.0, wind_loss_factor=0.0, eta_m=0.1, - alpha_absorption=0.9): +def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, u_c=29.0, u_v=0.0, + eta_m=0.1, alpha_absorption=0.9): r""" Calculate cell temperature using an empirical heat loss factor model as implemented in PVsyst. @@ -238,7 +249,7 @@ def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. """ - total_loss_factor = wind_loss_factor * wind_speed + constant_loss_factor + total_loss_factor = u_c + u_v * wind_speed heat_input = poa_global * alpha_absorption * (1 - eta_m) temp_difference = heat_input / total_loss_factor return air_temperature + temp_difference diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index e74d457f00..0703833800 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -433,29 +433,37 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, temp_air, wind_speed): - """Uses :py:func:`celltemp.sapm` to calculate module and cell - temperatures. + def sapm_celltemp(self, poa_global, air_temperature, wind_speed, + parameter_set=None): + """Uses :py:func:`celltemp.sapm_cell` to calculate cell temperatures. Parameters ---------- poa_global : float or Series Total incident irradiance in W/m^2. - temp_air : float or Series + air_temperature : float or Series Ambient dry bulb temperature in degrees C. wind_speed : float or Series Wind speed in m/s at a height of 10 meters. + parameter_set : string, default None + Heat loss model parameters to be used. + See celltemp.TEMPERATURE_MODEL_PARAMETERS for available parameter + sets. + Returns ------- - DataFrame with columns 'temp_cell' and 'temp_module'. - Values in degrees C. + float or Series, values in degrees C. """ - kwargs = _build_kwargs(['a', 'b', 'deltaT'], - self.temperature_model_parameters) - return celltemp.sapm(poa_global, temp_air, wind_speed, **kwargs) + if parameter_set is not None: + kwargs = celltemp._temperature_model_params('sapm', parameter_set) + else: + kwargs = _build_kwargs(['a', 'b', 'deltaT'], + self.temperature_model_parameters) + return celltemp.sapm_cell(poa_global, air_temperature, wind_speed, + **kwargs) def sapm_spectral_loss(self, airmass_absolute): """ @@ -525,16 +533,16 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) - def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, - model='freestanding'): - """Uses :py:func:`celltemp.pvsyst` to calculate cell temperature. + def pvsyst_celltemp(self, poa_global, air_temperature, wind_speed=1.0, + parameter_set=None): + """Uses :py:func:`celltemp.pvsyst_cell` to calculate cell temperature. Parameters ---------- poa_global : numeric Total incident irradiance in W/m^2. - temp_air : numeric + air_temperature : numeric Ambient dry bulb temperature in degrees C. wind_speed : numeric, default 1.0 @@ -549,28 +557,25 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, alpha_absorption : numeric, default 0.9 Absorption coefficient - model : string, default 'freestanding' - Heat loss model factors to be used. See celltemp.pvsyst for - details. + parameter_set : string, default None + Heat loss model parameters to be used. + See celltemp.TEMPERATURE_MODEL_PARAMETERS for available parameter + sets. Returns ------- - DataFrame with column 'temp_cell', values in degrees C. + float or Series, values in degrees C. """ - try: - constant_loss_factor, wind_loss_factor = \ - celltemp.TEMP_MODEL_PARAMS['pvsyst'][model] - except KeyError: - msg = ('{} is not a named set of parameters for the {} cell' - ' temperature model. See pvlib.celltemp.TEMP_MODEL_PARAMS' - ' for names'.format(model, 'pvsyst')) - raise KeyError(msg) kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) - kwargs.update(_build_kwargs(['constant_loss_factor', - 'wind_loss_factor'], - self.temperature_model_parameters)) - return celltemp.pvsyst(poa_global, temp_air, wind_speed, **kwargs) + if parameter_set is not None: + kwargs.update(celltemp._temperature_model_params('pvsyst', + parameter_set)) + else: + kwargs.update(_build_kwargs(['u_c', 'u_v'], + self.temperature_model_parameters)) + return celltemp.pvsyst(poa_global, air_temperature, wind_speed, + **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 06d2880b2a..3f2ad73772 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -100,3 +100,11 @@ def test_deprecated_07(): pvsystem.sapm_celltemp(1000, 25, 1, -3.47, -0.0594, 3) with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) + +def test__temperature_model_params(): + params = celltemp._temperature_model_params('sapm', + 'open_rack_glass_glass') + assert params == celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ + ['open_rack_glass_glass'] + with pytest.raises(KeyError): + celltemp._temperature_model_params('sapm', 'not_a_parameter_set') diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index f38a140a31..d75c8f9796 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -463,41 +463,72 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): def test_PVSystem_sapm_celltemp(mocker): - a, b, deltaT = (-3.47, -.0594, 3) # open_rack_cell_glassback + a, b, deltaT = (-3.47, -0.0594, 3) # open_rack_glass_glass temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) - mocker.spy(celltemp, 'sapm') + mocker.spy(celltemp, 'sapm_cell') temps = 25 irrads = 1000 winds = 1 out = system.sapm_celltemp(irrads, temps, winds) celltemp.sapm.assert_called_once_with(irrads, temps, winds, a, b, deltaT) - assert isinstance(out, pd.DataFrame) - assert out.shape == (1, 2) + assert_allclose(out, 57, atol=1) + + +def test_PVSystem_sapm_celltemp_kwargs(mocker): + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ + ['open_rack_glass_glass'] + system = pvsystem.PVSystem() + mocker.spy(celltemp, 'sapm_cell') + temps = 25 + irrads = 1000 + winds = 1 + out = system.sapm_celltemp(irrads, temps, winds, + parameter_set='open_rack_glass_glass') + celltemp.sapm.assert_called_once_with(irrads, temps, winds, + temp_model_params['a'], + temp_model_params['b'], + temp_model_params['deltaT']) + assert_allclose(out, 57, atol=1) def test_PVSystem_pvsyst_celltemp(mocker): - racking_model = 'insulated' - constant_loss_factor, wind_loss_factor = \ - celltemp.TEMP_MODEL_PARAMS['pvsyst'][racking_model] - temp_model_params = {'constant_loss_factor': constant_loss_factor, - 'wind_loss_factor': wind_loss_factor} + parameter_set = 'insulated' + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst']\ + [parameter_set] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} - system = pvsystem.PVSystem(racking_model=racking_model, - module_parameters=module_parameters, + system = pvsystem.PVSystem(module_parameters=module_parameters, temperature_model_parameters=temp_model_params) - mocker.spy(celltemp, 'pvsyst') + mocker.spy(celltemp, 'pvsyst_cell') irrad = 800 temp = 45 wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) celltemp.pvsyst.assert_called_once_with( - irrad, temp, wind, constant_loss_factor, wind_loss_factor, eta_m, - alpha_absorption) - assert isinstance(out, pd.DataFrame) - assert all(out['temp_cell'] < 90) and all(out['temp_cell'] > 70) + irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], + eta_m, alpha_absorption) + assert all(out < 90) and all(out > 70) + + +def test_PVSystem_pvsyst_celltemp_kwargs(mocker): + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst']\ + ['insulated'] + alpha_absorption = 0.85 + eta_m = 0.17 + module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} + system = pvsystem.PVSystem(module_parameters=module_parameters) + mocker.spy(celltemp, 'pvsyst_cell') + irrad = 800 + temp = 45 + wind = 0.5 + out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind, + parameter_set='insulated') + celltemp.pvsyst.assert_called_once_with( + irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], + eta_m, alpha_absorption) + assert all(out < 90) and all(out > 70) def test_calcparams_desoto(cec_module_params): From 4169e93d701b2e2070b36d94d9d82e5e801fa558 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 10:40:51 -0600 Subject: [PATCH 51/98] fix function names --- pvlib/pvsystem.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 0703833800..e86250f475 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2012,18 +2012,18 @@ def sapm(effective_irradiance, temp_cell, module): return out -sapm_celltemp = deprecated('0.7', alternative='celltemp.sapm', +sapm_celltemp = deprecated('0.7', alternative='celltemp.sapm_cell', name='sapm_celltemp', removal='0.8', - addendum='Note that the argument order for ' - 'celltemp.sapm is different than for ' + addendum='Note that the argument names and order ' + 'for celltemp.sapm_cell are different than for ' 'sapm_celltemp')(celltemp.sapm) -pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst', +pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst_cell', name='pvsyst_celltemp', - removal='0.8', addendum='Note that ' - 'celltemp.pvsyst returns a DataFrame instead ' - 'of numeric or Series')(celltemp.pvsyst) + removal='0.8', addendum='Note that the argument ' + 'names for celltemp.pvsyst_cell are different ' + 'than for pvsyst_celltemp')(celltemp.pvsyst) def sapm_spectral_loss(airmass_absolute, module): From 77064315f6a2faff9914db5a6440efcb84668486 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 10:49:59 -0600 Subject: [PATCH 52/98] fix function names more --- pvlib/pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index e86250f475..af9ccd58e3 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2016,14 +2016,14 @@ def sapm(effective_irradiance, temp_cell, module): name='sapm_celltemp', removal='0.8', addendum='Note that the argument names and order ' 'for celltemp.sapm_cell are different than for ' - 'sapm_celltemp')(celltemp.sapm) + 'sapm_celltemp')(celltemp.sapm_cell) pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst_cell', name='pvsyst_celltemp', removal='0.8', addendum='Note that the argument ' 'names for celltemp.pvsyst_cell are different ' - 'than for pvsyst_celltemp')(celltemp.pvsyst) + 'than for pvsyst_celltemp')(celltemp.pvsyst_cell) def sapm_spectral_loss(airmass_absolute, module): From aaeda877aeff3398a3aed73eccfda7abafd76007 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 13:06:14 -0600 Subject: [PATCH 53/98] update modelchain and tests --- pvlib/celltemp.py | 18 ++++++++-------- pvlib/modelchain.py | 39 +++++++++++++++++------------------ pvlib/pvsystem.py | 14 ++++++------- pvlib/test/test_celltemp.py | 3 ++- pvlib/test/test_modelchain.py | 2 +- pvlib/test/test_pvsystem.py | 11 +++++----- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 143ca0d2bd..5d7e0aee1d 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -32,7 +32,7 @@ def _temperature_model_params(model, parameter_set): raise KeyError(msg) -def sapm_cell(poa_global, air_temperature, wind_speed, a, b, deltaT, +def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, irrad_ref=1000): r''' Calculate cell temperature per the Sandia PV Array Performance Model [1]. @@ -42,7 +42,7 @@ def sapm_cell(poa_global, air_temperature, wind_speed, a, b, deltaT, poa_global : float or Series Total incident irradiance [W/m^2]. - air_temperature : float or Series + temp_air : float or Series Ambient dry bulb temperature [C]. wind_speed : float or Series @@ -107,12 +107,12 @@ def sapm_cell(poa_global, air_temperature, wind_speed, a, b, deltaT, NM. ''' - module_temperature = sapm_module(poa_global, air_temperature, wind_speed, + module_temperature = sapm_module(poa_global, temp_air, wind_speed, a, b) return module_temperature + (poa_global / irrad_ref) * (deltaT) -def sapm_module(poa_global, air_temperature, wind_speed, a, b): +def sapm_module(poa_global, temp_air, wind_speed, a, b): r''' Calculate module back surface temperature per the Sandia PV Array Performance Model [1]. @@ -122,7 +122,7 @@ def sapm_module(poa_global, air_temperature, wind_speed, a, b): poa_global : float or Series Total incident irradiance [W/m^2]. - air_temperature : float or Series + temp_air : float or Series Ambient dry bulb temperature [C]. wind_speed : float or Series @@ -172,10 +172,10 @@ def sapm_module(poa_global, air_temperature, wind_speed, a, b): NM. ''' - return poa_global * np.exp(a + b * wind_speed) + air_temperature + return poa_global * np.exp(a + b * wind_speed) + temp_air -def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, u_c=29.0, u_v=0.0, +def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, eta_m=0.1, alpha_absorption=0.9): r""" Calculate cell temperature using an empirical heat loss factor model @@ -186,7 +186,7 @@ def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, u_c=29.0, u_v=0.0, poa_global : float or Series Total incident irradiance [W/m^2]. - air_temperature : float or Series + temp_air : float or Series Ambient dry bulb temperature [C]. wind_speed : float or Series, default 1.0 @@ -252,4 +252,4 @@ def pvsyst_cell(poa_global, air_temperature, wind_speed=1.0, u_c=29.0, u_v=0.0, total_loss_factor = u_c + u_v * wind_speed heat_input = poa_global * alpha_absorption * (1 - eta_m) temp_difference = heat_input / total_loss_factor - return air_temperature + temp_difference + return temp_air + temp_difference diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index ba98696570..79a9ba5975 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -52,8 +52,8 @@ def basic_chain(times, latitude, longitude, details. temperature_model_parameters : None, dict or Series, default None. - Temperature model parameters as defined by the SAPM. See celltemp.sapm - for details. + Temperature model parameters as defined by the SAPM. + See celltemp.sapm_cell for details. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. See pvsystem.snlinverter for @@ -171,17 +171,16 @@ def basic_chain(times, latitude, longitude, if weather is None: weather = {'wind_speed': 0, 'temp_air': 20} - temps = celltemp.sapm(total_irrad['poa_global'], weather['temp_air'], - weather['wind_speed'], - temperature_model_parameters['a'], - temperature_model_parameters['b'], - temperature_model_parameters['deltaT']) + cell_temperature = celltemp.sapm_cell( + total_irrad['poa_global'], weather['temp_air'], weather['wind_speed'], + temperature_model_parameters['a'], temperature_model_parameters['b'], + temperature_model_parameters['deltaT']) effective_irradiance = pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, module_parameters) - dc = pvsystem.sapm(effective_irradiance, temps['temp_cell'], + dc = pvsystem.sapm(effective_irradiance, cell_temperature, module_parameters) ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters) @@ -430,7 +429,7 @@ def infer_dc_model(self): def sapm(self): self.dc = self.system.sapm(self.effective_irradiance/1000., - self.temps['temp_cell']) + self.cell_temperature) self.dc = self.system.scale_voltage_current_power(self.dc) @@ -440,7 +439,7 @@ def desoto(self): (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = ( self.system.calcparams_desoto(self.effective_irradiance, - self.temps['temp_cell'])) + self.cell_temperature)) self.diode_params = (photocurrent, saturation_current, resistance_series, @@ -458,7 +457,7 @@ def cec(self): (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = ( self.system.calcparams_cec(self.effective_irradiance, - self.temps['temp_cell'])) + self.cell_temperature)) self.diode_params = (photocurrent, saturation_current, resistance_series, @@ -476,7 +475,7 @@ def pvsyst(self): (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = ( self.system.calcparams_pvsyst(self.effective_irradiance, - self.temps['temp_cell'])) + self.cell_temperature)) self.diode_params = (photocurrent, saturation_current, resistance_series, @@ -495,7 +494,7 @@ def singlediode(self): (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) = ( self.system.calcparams_desoto(self.effective_irradiance, - self.temps['temp_cell'])) + self.cell_temperature)) self.desoto = (photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth) @@ -510,7 +509,7 @@ def singlediode(self): def pvwatts_dc(self): self.dc = self.system.pvwatts_dc(self.effective_irradiance, - self.temps['temp_cell']) + self.cell_temperature) return self @property @@ -691,7 +690,7 @@ def infer_temp_model(self): params = set(self.system.temperature_model_parameters.keys()) if set(['a', 'b', 'deltaT']) <= params: return self.sapm_temp - elif set(['constant_loss_factor', 'wind_loss_factor']) <= params: + elif set(['u_c', 'u_v']) <= params: return self.pvsyst_temp else: raise ValueError('could not infer temperature model from ' @@ -699,13 +698,13 @@ def infer_temp_model(self): .format(self.system.temperature_model_parameters)) def sapm_temp(self): - self.temps = self.system.sapm_celltemp(self.total_irrad['poa_global'], - self.weather['temp_air'], - self.weather['wind_speed']) + self.cell_temperature = self.system.sapm_celltemp( + self.total_irrad['poa_global'], self.weather['temp_air'], + self.weather['wind_speed']) return self def pvsyst_temp(self): - self.temps = self.system.pvsyst_celltemp( + self.cell_temperature = self.system.pvsyst_celltemp( self.total_irrad['poa_global'], self.weather['temp_air'], self.weather['wind_speed']) return self @@ -944,7 +943,7 @@ def run_model(self, times=None, weather=None): self Assigns attributes: times, solar_position, airmass, irradiance, - total_irrad, effective_irradiance, weather, temps, aoi, + total_irrad, effective_irradiance, weather, cell_temperature, aoi, aoi_modifier, spectral_modifier, dc, ac, losses. """ diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index af9ccd58e3..a5f5feb17d 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -433,7 +433,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, air_temperature, wind_speed, + def sapm_celltemp(self, poa_global, temp_air, wind_speed, parameter_set=None): """Uses :py:func:`celltemp.sapm_cell` to calculate cell temperatures. @@ -442,7 +442,7 @@ def sapm_celltemp(self, poa_global, air_temperature, wind_speed, poa_global : float or Series Total incident irradiance in W/m^2. - air_temperature : float or Series + temp_air : float or Series Ambient dry bulb temperature in degrees C. wind_speed : float or Series @@ -462,8 +462,7 @@ def sapm_celltemp(self, poa_global, air_temperature, wind_speed, else: kwargs = _build_kwargs(['a', 'b', 'deltaT'], self.temperature_model_parameters) - return celltemp.sapm_cell(poa_global, air_temperature, wind_speed, - **kwargs) + return celltemp.sapm_cell(poa_global, temp_air, wind_speed, **kwargs) def sapm_spectral_loss(self, airmass_absolute): """ @@ -533,7 +532,7 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) - def pvsyst_celltemp(self, poa_global, air_temperature, wind_speed=1.0, + def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, parameter_set=None): """Uses :py:func:`celltemp.pvsyst_cell` to calculate cell temperature. @@ -542,7 +541,7 @@ def pvsyst_celltemp(self, poa_global, air_temperature, wind_speed=1.0, poa_global : numeric Total incident irradiance in W/m^2. - air_temperature : numeric + temp_air : numeric Ambient dry bulb temperature in degrees C. wind_speed : numeric, default 1.0 @@ -574,8 +573,7 @@ def pvsyst_celltemp(self, poa_global, air_temperature, wind_speed=1.0, else: kwargs.update(_build_kwargs(['u_c', 'u_v'], self.temperature_model_parameters)) - return celltemp.pvsyst(poa_global, air_temperature, wind_speed, - **kwargs) + return celltemp.pvsyst_cell(poa_global, temp_air, wind_speed, **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 3f2ad73772..2cb9143f70 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -30,7 +30,8 @@ def test_sapm_cell(sapm_default): def test_sapm_module(sapm_default): - default = celltemp.sapm(900, 20, 5, sapm_default['a'], sapm_default['b']) + default = celltemp.sapm_module(900, 20, 5, sapm_default['a'], + sapm_default['b']) assert_allclose(default, 40.809, 3) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 6907ba685d..6e2c30c85b 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -229,7 +229,7 @@ def test_run_model_with_weather(system, location, weather, mocker): weather['wind_speed'] = 5 weather['temp_air'] = 10 # test with sapm cell temperature model - system.racking_model = 'open_rack_cell_glassback' + system.racking_model = 'open_rack_glass_glass' mc = ModelChain(system, location) mc.temp_model = 'sapm' m_sapm = mocker.spy(system, 'sapm_celltemp') diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index d75c8f9796..4f8c39c85e 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -471,7 +471,8 @@ def test_PVSystem_sapm_celltemp(mocker): irrads = 1000 winds = 1 out = system.sapm_celltemp(irrads, temps, winds) - celltemp.sapm.assert_called_once_with(irrads, temps, winds, a, b, deltaT) + celltemp.sapm_cell.assert_called_once_with(irrads, temps, winds, a, b, + deltaT) assert_allclose(out, 57, atol=1) @@ -485,10 +486,10 @@ def test_PVSystem_sapm_celltemp_kwargs(mocker): winds = 1 out = system.sapm_celltemp(irrads, temps, winds, parameter_set='open_rack_glass_glass') - celltemp.sapm.assert_called_once_with(irrads, temps, winds, - temp_model_params['a'], - temp_model_params['b'], - temp_model_params['deltaT']) + celltemp.sapm_cell.assert_called_once_with(irrads, temps, winds, + temp_model_params['a'], + temp_model_params['b'], + temp_model_params['deltaT']) assert_allclose(out, 57, atol=1) From 21ccba32363d686cd145796daaecd9872b78ca0d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 13:49:39 -0600 Subject: [PATCH 54/98] more test fixes --- pvlib/test/test_modelchain.py | 2 +- pvlib/test/test_pvsystem.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 6e2c30c85b..fb88e0e5b9 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -30,7 +30,7 @@ def _get_sapm_temp_model_params(sam_data): def _get_pvsyst_temp_model_params(): # Pvsyst temperature model parameters for freestanding system - return {'constant_loss_factor': 29.0, 'wind_loss_factor': 0.0} + return {'u_c': 29.0, 'u_v': 0.0} @pytest.fixture diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 4f8c39c85e..2fea7972ab 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -507,7 +507,7 @@ def test_PVSystem_pvsyst_celltemp(mocker): temp = 45 wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) - celltemp.pvsyst.assert_called_once_with( + celltemp.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) assert all(out < 90) and all(out > 70) @@ -526,7 +526,7 @@ def test_PVSystem_pvsyst_celltemp_kwargs(mocker): wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind, parameter_set='insulated') - celltemp.pvsyst.assert_called_once_with( + celltemp.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) assert all(out < 90) and all(out > 70) From 0233bb366c2eceebe28cabcaa5c87298bffc8930 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 14:05:40 -0600 Subject: [PATCH 55/98] test fixes --- pvlib/test/test_pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 2fea7972ab..76166be333 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -510,7 +510,7 @@ def test_PVSystem_pvsyst_celltemp(mocker): celltemp.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) - assert all(out < 90) and all(out > 70) + assert (out < 90) and (out > 70) def test_PVSystem_pvsyst_celltemp_kwargs(mocker): @@ -529,7 +529,7 @@ def test_PVSystem_pvsyst_celltemp_kwargs(mocker): celltemp.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) - assert all(out < 90) and all(out > 70) + assert (out < 90) and (out > 70) def test_calcparams_desoto(cec_module_params): From a5fb7d686c55590bc1e0da1cf0aa733e504388a2 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jul 2019 14:47:01 -0600 Subject: [PATCH 56/98] lint --- pvlib/celltemp.py | 6 ++---- pvlib/modelchain.py | 2 +- pvlib/test/test_celltemp.py | 1 + pvlib/test/test_pvsystem.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 5d7e0aee1d..408c031b6c 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- """ -Created on Fri Mar 29 09:40:42 2019 - -@author: cwhanse +The ``temperature`` module contains functions for modeling temperature of +PV modules and cells. """ import numpy as np diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 79a9ba5975..e2f634652e 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -52,7 +52,7 @@ def basic_chain(times, latitude, longitude, details. temperature_model_parameters : None, dict or Series, default None. - Temperature model parameters as defined by the SAPM. + Temperature model parameters as defined by the SAPM. See celltemp.sapm_cell for details. inverter_parameters : None, dict or Series diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 2cb9143f70..df1e17ee41 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -102,6 +102,7 @@ def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) + def test__temperature_model_params(): params = celltemp._temperature_model_params('sapm', 'open_rack_glass_glass') diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 76166be333..69ea564241 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -463,7 +463,7 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): def test_PVSystem_sapm_celltemp(mocker): - a, b, deltaT = (-3.47, -0.0594, 3) # open_rack_glass_glass + a, b, deltaT = (-3.47, -0.0594, 3) # open_rack_glass_glass temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) mocker.spy(celltemp, 'sapm_cell') From 14bddba8f71a2467236dc4961e6ca6c333d037dc Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 1 Aug 2019 09:21:43 -0600 Subject: [PATCH 57/98] lint, merge master --- pvlib/celltemp.py | 2 +- pvlib/pvsystem.py | 2 +- pvlib/test/test_celltemp.py | 4 ++-- pvlib/test/test_modelchain.py | 3 ++- pvlib/test/test_pvsystem.py | 12 ++++++------ 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pvlib/celltemp.py b/pvlib/celltemp.py index 408c031b6c..5f6574171d 100644 --- a/pvlib/celltemp.py +++ b/pvlib/celltemp.py @@ -12,7 +12,7 @@ 'close_mount_glass_glass': {'a': -2.98, 'b': -.0471, 'deltaT': 1}, 'open_rack_glass_polymer': {'a': -3.56, 'b': -.0750, 'deltaT': 3}, 'insulated_back_glass_polymer': {'a': -2.81, 'b': -.0455, 'deltaT': 0}, - }, + }, 'pvsyst': {'freestanding': {'u_c': 29.0, 'u_v': 0}, 'insulated': {'u_c': 15.0, 'u_v': 0}} } diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index a5f5feb17d..6fdd958264 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -572,7 +572,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, parameter_set)) else: kwargs.update(_build_kwargs(['u_c', 'u_v'], - self.temperature_model_parameters)) + self.temperature_model_parameters)) return celltemp.pvsyst_cell(poa_global, temp_air, wind_speed, **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index df1e17ee41..0f4b45af30 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -19,8 +19,8 @@ @pytest.fixture def sapm_default(): - return celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ - ['open_rack_glass_glass'] + return celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + 'open_rack_glass_glass'] def test_sapm_cell(sapm_default): diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index fb88e0e5b9..c973dad48d 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -253,7 +253,8 @@ def test_run_model_with_weather(system, location, weather, mocker): def test_run_model_tracker(system, location, weather, mocker): - system = SingleAxisTracker(module_parameters=system.module_parameters, + system = SingleAxisTracker( + module_parameters=system.module_parameters, temperature_model_parameters=system.temperature_model_parameters, inverter_parameters=system.inverter_parameters) mocker.spy(system, 'singleaxis') diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 69ea564241..78d87923be 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -477,8 +477,8 @@ def test_PVSystem_sapm_celltemp(mocker): def test_PVSystem_sapm_celltemp_kwargs(mocker): - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ - ['open_rack_glass_glass'] + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + 'open_rack_glass_glass'] system = pvsystem.PVSystem() mocker.spy(celltemp, 'sapm_cell') temps = 25 @@ -495,8 +495,8 @@ def test_PVSystem_sapm_celltemp_kwargs(mocker): def test_PVSystem_pvsyst_celltemp(mocker): parameter_set = 'insulated' - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst']\ - [parameter_set] + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ + parameter_set] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} @@ -514,8 +514,8 @@ def test_PVSystem_pvsyst_celltemp(mocker): def test_PVSystem_pvsyst_celltemp_kwargs(mocker): - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst']\ - ['insulated'] + temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ + 'insulated'] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} From f3aa87519eb8cf2f78236c9dd46440c863036032 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 1 Aug 2019 09:35:40 -0600 Subject: [PATCH 58/98] lint, remove pvwatts_dc_pvwatts_ac_system fixture from test_modelchain.py --- pvlib/test/test_celltemp.py | 4 ++-- pvlib/test/test_modelchain.py | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index 0f4b45af30..ff0d191f91 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -106,7 +106,7 @@ def test_deprecated_07(): def test__temperature_model_params(): params = celltemp._temperature_model_params('sapm', 'open_rack_glass_glass') - assert params == celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm']\ - ['open_rack_glass_glass'] + assert params == celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + 'open_rack_glass_glass'] with pytest.raises(KeyError): celltemp._temperature_model_params('sapm', 'not_a_parameter_set') diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index c973dad48d..9d0cd5d348 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -119,19 +119,6 @@ def cec_dc_adr_ac_system(sam_data): return system -@pytest.fixture -def pvwatts_dc_snl_ac_system(sam_data): - module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} - temp_model_params = _get_sapm_temp_model_params(sam_data) - 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_parameters=module_parameters, - temperature_model_parameters=temp_model_params, - inverter_parameters=inverter) - return system - - @pytest.fixture def pvwatts_dc_pvwatts_ac_system(sam_data): module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} From 7825d42ce2c58eff10b26663da90ab1f2b2f3d6e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 1 Aug 2019 10:21:42 -0600 Subject: [PATCH 59/98] rename from celltemp to temperature --- docs/sphinx/source/api.rst | 9 ++--- docs/sphinx/source/whatsnew/v0.7.0.rst | 14 ++++---- pvlib/modelchain.py | 6 ++-- pvlib/pvsystem.py | 44 +++++++++++++---------- pvlib/{celltemp.py => temperature.py} | 0 pvlib/test/test_celltemp.py | 50 ++++++++++++++------------ pvlib/test/test_pvsystem.py | 24 ++++++------- 7 files changed, 80 insertions(+), 67 deletions(-) rename pvlib/{celltemp.py => temperature.py} (100%) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index a87e731340..176e0f6a65 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -227,8 +227,9 @@ PV temperature models .. autosummary:: :toctree: generated/ - celltemp.sapm - celltemp.pvsyst + temperature.sapm_cell + temperature.sapm_module + temperature.pvsyst_cell Single diode models ------------------- @@ -271,7 +272,7 @@ Functions relevant for the SAPM model. pvsystem.sapm_spectral_loss pvsystem.sapm_aoi_loss pvsystem.snlinverter - celltemp.sapm + temperature.sapm_cell Pvsyst model ------------- @@ -281,7 +282,7 @@ Functions relevant for the Pvsyst model. .. autosummary:: :toctree: generated/ - celltemp.pvsyst + temperature.pvsyst_cell PVWatts model ------------- diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index e94afbef19..8c1b6ef487 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -15,29 +15,31 @@ API Changes * Changes to functions and methods for cell temperature models: (:issue:`678') - Moved functions for cell temperature and constant - `TEMP_MODEL_PARAMS` from `pvsystem.py` to `celltemp.py`. + `TEMP_MODEL_PARAMS` from `pvsystem.py` to `temperature.py`. - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` - to `celltemp.sapm` and `celltemp.pvsyst`. + to `temperature.sapm_cell` and `temperature.pvsyst_cell`. + - Created `temperature.sapm_module`. - Argument order and output type of `pvsystem.sapm_celltemp`, `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have been altered to be consistent among cell temperature model functions. - - Removed `model` as a kwarg from `celltemp.sapm` and - `celltemp.pvsyst`. These functions now require model-specific + - Removed `model` as a kwarg from `temperature.sapm_cell` and + `temperature.pvsyst_cell`. These functions now require model-specific parameters as kwargs. + - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. + - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dicts rather than tuple for parameter sets. dict keys for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. - Added attribute `PVSystem.temperature_model_parameters` (dict) to contain temperature model parameters. - Changed meaning of `PVSystem.racking_model` to describe racking only, e.g., default is `open_rack`. + - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - Implemented `pvsyst` as an option for `ModelChain.temp_model`. - `ModelChain.temp_model` now defaults to `None. The temperature model can be inferred from `PVSystem.temperature_model_parameters`. -Enhancements -~~~~~~~~~~~~ Enhancements ~~~~~~~~~~~~ * Created two new incidence angle modifier functions: :py:func:`pvlib.pvsystem.iam_martin_ruiz` diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index e2f634652e..b1eadca592 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -10,7 +10,7 @@ import warnings import pandas as pd -from pvlib import (atmosphere, celltemp, clearsky, pvsystem, solarposition, +from pvlib import (atmosphere, clearsky, pvsystem, solarposition, temperature, tools) from pvlib.tracking import SingleAxisTracker import pvlib.irradiance # avoid name conflict with full import @@ -53,7 +53,7 @@ def basic_chain(times, latitude, longitude, temperature_model_parameters : None, dict or Series, default None. Temperature model parameters as defined by the SAPM. - See celltemp.sapm_cell for details. + See temperature.sapm_cell for details. inverter_parameters : None, dict or Series Inverter parameters as defined by the CEC. See pvsystem.snlinverter for @@ -171,7 +171,7 @@ def basic_chain(times, latitude, longitude, if weather is None: weather = {'wind_speed': 0, 'temp_air': 20} - cell_temperature = celltemp.sapm_cell( + cell_temperature = temperature.sapm_cell( total_irrad['poa_global'], weather['temp_air'], weather['wind_speed'], temperature_model_parameters['a'], temperature_model_parameters['b'], temperature_model_parameters['deltaT']) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 6fdd958264..55da8ed382 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -12,7 +12,8 @@ from pvlib._deprecation import deprecated -from pvlib import atmosphere, celltemp, irradiance, singlediode as _singlediode +from pvlib import (atmosphere, irradiance, singlediode as _singlediode, + temperature) from pvlib import tools from pvlib.tools import _build_kwargs, cosd from pvlib.location import Location @@ -435,7 +436,8 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): def sapm_celltemp(self, poa_global, temp_air, wind_speed, parameter_set=None): - """Uses :py:func:`celltemp.sapm_cell` to calculate cell temperatures. + """Uses :py:func:`temperature.sapm_cell` to calculate cell + temperatures. Parameters ---------- @@ -450,19 +452,21 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, parameter_set : string, default None Heat loss model parameters to be used. - See celltemp.TEMPERATURE_MODEL_PARAMETERS for available parameter - sets. + See temperature.TEMPERATURE_MODEL_PARAMETERS for available + parameter sets. Returns ------- float or Series, values in degrees C. """ if parameter_set is not None: - kwargs = celltemp._temperature_model_params('sapm', parameter_set) + kwargs = temperature._temperature_model_params('sapm', + parameter_set) else: kwargs = _build_kwargs(['a', 'b', 'deltaT'], self.temperature_model_parameters) - return celltemp.sapm_cell(poa_global, temp_air, wind_speed, **kwargs) + return temperature.sapm_cell(poa_global, temp_air, wind_speed, + **kwargs) def sapm_spectral_loss(self, airmass_absolute): """ @@ -534,7 +538,8 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, parameter_set=None): - """Uses :py:func:`celltemp.pvsyst_cell` to calculate cell temperature. + """Uses :py:func:`temperature.pvsyst_cell` to calculate cell + temperature. Parameters ---------- @@ -558,8 +563,8 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, parameter_set : string, default None Heat loss model parameters to be used. - See celltemp.TEMPERATURE_MODEL_PARAMETERS for available parameter - sets. + See temperature.TEMPERATURE_MODEL_PARAMETERS for available + parameter sets. Returns ------- @@ -568,12 +573,13 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) if parameter_set is not None: - kwargs.update(celltemp._temperature_model_params('pvsyst', + kwargs.update(temperature._temperature_model_params('pvsyst', parameter_set)) else: kwargs.update(_build_kwargs(['u_c', 'u_v'], self.temperature_model_parameters)) - return celltemp.pvsyst_cell(poa_global, temp_air, wind_speed, **kwargs) + return temperature.pvsyst_cell(poa_global, temp_air, wind_speed, + **kwargs) def first_solar_spectral_loss(self, pw, airmass_absolute): @@ -2010,18 +2016,18 @@ def sapm(effective_irradiance, temp_cell, module): return out -sapm_celltemp = deprecated('0.7', alternative='celltemp.sapm_cell', +sapm_celltemp = deprecated('0.7', alternative='temperature.sapm_cell', name='sapm_celltemp', removal='0.8', addendum='Note that the argument names and order ' - 'for celltemp.sapm_cell are different than for ' - 'sapm_celltemp')(celltemp.sapm_cell) + 'for temperature.sapm_cell are different than for ' + 'sapm_celltemp')(temperature.sapm_cell) -pvsyst_celltemp = deprecated('0.7', alternative='celltemp.pvsyst_cell', - name='pvsyst_celltemp', - removal='0.8', addendum='Note that the argument ' - 'names for celltemp.pvsyst_cell are different ' - 'than for pvsyst_celltemp')(celltemp.pvsyst_cell) +pvsyst_celltemp = deprecated( + '0.7', alternative='temperature.pvsyst_cell', name='pvsyst_celltemp', + removal='0.8', addendum='Note that the argument names for ' + 'temperature.pvsyst_cell are different than ' + 'for pvsyst_celltemp')(temperature.pvsyst_cell) def sapm_spectral_loss(airmass_absolute, module): diff --git a/pvlib/celltemp.py b/pvlib/temperature.py similarity index 100% rename from pvlib/celltemp.py rename to pvlib/temperature.py diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_celltemp.py index ff0d191f91..babc8d0ac1 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_celltemp.py @@ -11,7 +11,7 @@ from pandas.util.testing import assert_series_equal from numpy.testing import assert_allclose -from pvlib import celltemp, pvsystem +from pvlib import pvsystem, temperature from pvlib._deprecation import pvlibDeprecationWarning from conftest import fail_on_pvlib_version @@ -19,19 +19,19 @@ @pytest.fixture def sapm_default(): - return celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + return temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ 'open_rack_glass_glass'] def test_sapm_cell(sapm_default): - default = celltemp.sapm_cell(900, 20, 5, sapm_default['a'], - sapm_default['b'], sapm_default['deltaT']) + default = temperature.sapm_cell(900, 20, 5, sapm_default['a'], + sapm_default['b'], sapm_default['deltaT']) assert_allclose(default, 43.509, 3) def test_sapm_module(sapm_default): - default = celltemp.sapm_module(900, 20, 5, sapm_default['a'], - sapm_default['b']) + default = temperature.sapm_module(900, 20, 5, sapm_default['a'], + sapm_default['b']) assert_allclose(default, 40.809, 3) @@ -39,10 +39,12 @@ def test_sapm_ndarray(sapm_default): temps = np.array([0, 10, 5]) irrads = np.array([0, 500, 0]) winds = np.array([10, 5, 0]) - cell_temps = celltemp.sapm_cell(irrads, temps, winds, sapm_default['a'], - sapm_default['b'], sapm_default['deltaT']) - module_temps = celltemp.sapm_module(irrads, temps, winds, - sapm_default['a'], sapm_default['b']) + cell_temps = temperature.sapm_cell(irrads, temps, winds, sapm_default['a'], + sapm_default['b'], + sapm_default['deltaT']) + module_temps = temperature.sapm_module(irrads, temps, winds, + sapm_default['a'], + sapm_default['b']) expected_cell = np.array([0., 23.06066166, 5.]) expected_module = np.array([0., 21.56066166, 5.]) assert_allclose(expected_cell, cell_temps, 3) @@ -54,10 +56,12 @@ def test_sapm_series(sapm_default): temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - cell_temps = celltemp.sapm_cell(irrads, temps, winds, sapm_default['a'], - sapm_default['b'], sapm_default['deltaT']) - module_temps = celltemp.sapm_module(irrads, temps, winds, - sapm_default['a'], sapm_default['b']) + cell_temps = temperature.sapm_cell(irrads, temps, winds, sapm_default['a'], + sapm_default['b'], + sapm_default['deltaT']) + module_temps = temperature.sapm_module(irrads, temps, winds, + sapm_default['a'], + sapm_default['b']) expected_cell = pd.Series([0., 23.06066166, 5.], index=times) expected_module = pd.Series([0., 21.56066166, 5.], index=times) assert_series_equal(expected_cell, cell_temps) @@ -65,13 +69,13 @@ def test_sapm_series(sapm_default): def test_pvsyst_cell_default(): - result = celltemp.pvsyst_cell(900, 20, 5) + result = temperature.pvsyst_cell(900, 20, 5) assert_allclose(result, 45.137, 0.001) def test_pvsyst_cell_kwargs(): - result = celltemp.pvsyst_cell(900, 20, wind_speed=5.0, u_c=23.5, u_v=6.25, - eta_m=0.1) + result = temperature.pvsyst_cell(900, 20, wind_speed=5.0, u_c=23.5, + u_v=6.25, eta_m=0.1) assert_allclose(result, 33.315, 0.001) @@ -79,7 +83,7 @@ def test_pvsyst_cell_ndarray(): temps = np.array([0, 10, 5]) irrads = np.array([0, 500, 0]) winds = np.array([10, 5, 0]) - result = celltemp.pvsyst_cell(irrads, temps, wind_speed=winds) + result = temperature.pvsyst_cell(irrads, temps, wind_speed=winds) expected = np.array([0.0, 23.96551, 5.0]) assert_allclose(expected, result, 3) @@ -90,7 +94,7 @@ def test_pvsyst_cell_series(): irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) - result = celltemp.pvsyst_cell(irrads, temps, wind_speed=winds) + result = temperature.pvsyst_cell(irrads, temps, wind_speed=winds) expected = pd.Series([0.0, 23.96551, 5.0], index=times) assert_series_equal(expected, result) @@ -104,9 +108,9 @@ def test_deprecated_07(): def test__temperature_model_params(): - params = celltemp._temperature_model_params('sapm', - 'open_rack_glass_glass') - assert params == celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + params = temperature._temperature_model_params('sapm', + 'open_rack_glass_glass') + assert params == temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ 'open_rack_glass_glass'] with pytest.raises(KeyError): - celltemp._temperature_model_params('sapm', 'not_a_parameter_set') + temperature._temperature_model_params('sapm', 'not_a_parameter_set') diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 78d87923be..b0d0fca0c6 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -17,7 +17,7 @@ from pvlib import atmosphere from pvlib import solarposition from pvlib.location import Location -from pvlib import celltemp +from pvlib import temperature from conftest import needs_numpy_1_10, requires_scipy @@ -466,27 +466,27 @@ def test_PVSystem_sapm_celltemp(mocker): a, b, deltaT = (-3.47, -0.0594, 3) # open_rack_glass_glass temp_model_params = {'a': a, 'b': b, 'deltaT': deltaT} system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) - mocker.spy(celltemp, 'sapm_cell') + mocker.spy(temperature, 'sapm_cell') temps = 25 irrads = 1000 winds = 1 out = system.sapm_celltemp(irrads, temps, winds) - celltemp.sapm_cell.assert_called_once_with(irrads, temps, winds, a, b, + temperature.sapm_cell.assert_called_once_with(irrads, temps, winds, a, b, deltaT) assert_allclose(out, 57, atol=1) def test_PVSystem_sapm_celltemp_kwargs(mocker): - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + temp_model_params = temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ 'open_rack_glass_glass'] system = pvsystem.PVSystem() - mocker.spy(celltemp, 'sapm_cell') + mocker.spy(temperature, 'sapm_cell') temps = 25 irrads = 1000 winds = 1 out = system.sapm_celltemp(irrads, temps, winds, parameter_set='open_rack_glass_glass') - celltemp.sapm_cell.assert_called_once_with(irrads, temps, winds, + temperature.sapm_cell.assert_called_once_with(irrads, temps, winds, temp_model_params['a'], temp_model_params['b'], temp_model_params['deltaT']) @@ -495,38 +495,38 @@ def test_PVSystem_sapm_celltemp_kwargs(mocker): def test_PVSystem_pvsyst_celltemp(mocker): parameter_set = 'insulated' - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ + temp_model_params = temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ parameter_set] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} system = pvsystem.PVSystem(module_parameters=module_parameters, temperature_model_parameters=temp_model_params) - mocker.spy(celltemp, 'pvsyst_cell') + mocker.spy(temperature, 'pvsyst_cell') irrad = 800 temp = 45 wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) - celltemp.pvsyst_cell.assert_called_once_with( + temperature.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) assert (out < 90) and (out > 70) def test_PVSystem_pvsyst_celltemp_kwargs(mocker): - temp_model_params = celltemp.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ + temp_model_params = temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][ 'insulated'] alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} system = pvsystem.PVSystem(module_parameters=module_parameters) - mocker.spy(celltemp, 'pvsyst_cell') + mocker.spy(temperature, 'pvsyst_cell') irrad = 800 temp = 45 wind = 0.5 out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind, parameter_set='insulated') - celltemp.pvsyst_cell.assert_called_once_with( + temperature.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) assert (out < 90) and (out > 70) From 8b52ea035d100cd67840229e21b491a647e849cc Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 1 Aug 2019 10:56:13 -0600 Subject: [PATCH 60/98] lint, rename test_celltemp.py --- pvlib/pvsystem.py | 2 +- pvlib/test/test_pvsystem.py | 8 ++++---- pvlib/test/{test_celltemp.py => test_temperature.py} | 6 ------ 3 files changed, 5 insertions(+), 11 deletions(-) rename pvlib/test/{test_celltemp.py => test_temperature.py} (97%) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 55da8ed382..38b74e2c28 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -574,7 +574,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, self.module_parameters) if parameter_set is not None: kwargs.update(temperature._temperature_model_params('pvsyst', - parameter_set)) + parameter_set)) else: kwargs.update(_build_kwargs(['u_c', 'u_v'], self.temperature_model_parameters)) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index b0d0fca0c6..62a7792ae6 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -472,7 +472,7 @@ def test_PVSystem_sapm_celltemp(mocker): winds = 1 out = system.sapm_celltemp(irrads, temps, winds) temperature.sapm_cell.assert_called_once_with(irrads, temps, winds, a, b, - deltaT) + deltaT) assert_allclose(out, 57, atol=1) @@ -487,9 +487,9 @@ def test_PVSystem_sapm_celltemp_kwargs(mocker): out = system.sapm_celltemp(irrads, temps, winds, parameter_set='open_rack_glass_glass') temperature.sapm_cell.assert_called_once_with(irrads, temps, winds, - temp_model_params['a'], - temp_model_params['b'], - temp_model_params['deltaT']) + temp_model_params['a'], + temp_model_params['b'], + temp_model_params['deltaT']) assert_allclose(out, 57, atol=1) diff --git a/pvlib/test/test_celltemp.py b/pvlib/test/test_temperature.py similarity index 97% rename from pvlib/test/test_celltemp.py rename to pvlib/test/test_temperature.py index babc8d0ac1..3589088bb5 100644 --- a/pvlib/test/test_celltemp.py +++ b/pvlib/test/test_temperature.py @@ -1,9 +1,3 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 29 10:07:25 2019 - -@author: cwhanse -""" import pandas as pd import numpy as np From 9c2d5d3c86475e7d2e6e0a9c38562a55a7b819dd Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 2 Aug 2019 09:01:08 -0600 Subject: [PATCH 61/98] cleanup docstrings --- pvlib/pvsystem.py | 16 ++++++++-------- pvlib/temperature.py | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 38b74e2c28..7c4588533a 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -441,13 +441,13 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, Parameters ---------- - poa_global : float or Series + poa_global : numeric or Series Total incident irradiance in W/m^2. - temp_air : float or Series + temp_air : numeric or Series Ambient dry bulb temperature in degrees C. - wind_speed : float or Series + wind_speed : numeric or Series Wind speed in m/s at a height of 10 meters. parameter_set : string, default None @@ -457,7 +457,7 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, Returns ------- - float or Series, values in degrees C. + numeric or Series, values in degrees C. """ if parameter_set is not None: kwargs = temperature._temperature_model_params('sapm', @@ -543,13 +543,13 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, Parameters ---------- - poa_global : numeric + poa_global : numeric or Series Total incident irradiance in W/m^2. - temp_air : numeric + temp_air : numeric or Series Ambient dry bulb temperature in degrees C. - wind_speed : numeric, default 1.0 + wind_speed : numeric or Series, default 1.0 Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value is 1.0, which is the wind speed at module height used to determine NOCT. @@ -568,7 +568,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, Returns ------- - float or Series, values in degrees C. + numeric or Series, values in degrees C. """ kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 5f6574171d..e26f0c50eb 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -37,13 +37,13 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Parameters ---------- - poa_global : float or Series + poa_global : numeric or Series Total incident irradiance [W/m^2]. - temp_air : float or Series + temp_air : numeric or Series Ambient dry bulb temperature [C]. - wind_speed : float or Series + wind_speed : numeric or Series Wind speed at a height of 10 meters [m/s]. a : float @@ -60,7 +60,7 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Returns ------- - float or Series, values in degrees C. + numeric or Series, values in degrees C. Notes ----- @@ -117,13 +117,13 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): Parameters ---------- - poa_global : float or Series + poa_global : numeric or Series Total incident irradiance [W/m^2]. - temp_air : float or Series + temp_air : numeric or Series Ambient dry bulb temperature [C]. - wind_speed : float or Series + wind_speed : numeric or Series Wind speed at a height of 10 meters [m/s]. a : float @@ -134,7 +134,7 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): Returns ------- - float or Series, values in degrees C. + numeric or Series, values in degrees C. Notes ----- @@ -181,13 +181,13 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, Parameters ---------- - poa_global : float or Series + poa_global : numeric or Series Total incident irradiance [W/m^2]. - temp_air : float or Series + temp_air : numeric or Series Ambient dry bulb temperature [C]. - wind_speed : float or Series, default 1.0 + wind_speed : numeric or Series, default 1.0 Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value 1.0 m/2 is the wind speed at module height used to determine NOCT. [m/s] @@ -211,7 +211,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, Returns ------- - float or Series, values in degrees Celsius + numeric or Series, values in degrees Celsius Notes ----- From 33f4f5726ae23181f7d8a4c37e498f08155dd0ea Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 19 Aug 2019 13:42:43 -0600 Subject: [PATCH 62/98] add invalid test for infer_temp_model --- pvlib/test/test_modelchain.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 9d0cd5d348..6f8e0048c7 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -328,6 +328,14 @@ def test_infer_temp_model(location, system, pvsyst_dc_snl_ac_system, assert isinstance(mc, ModelChain) +@requires_scipy +def test_infer_temp_model_invalid(location, system): + system.temperature_model_parameters.pop('a') + with pytest.raises(ValueError): + ModelChain(system, location, orientation_strategy='None', + aoi_model='physical', spectral_model='no_loss') + + def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather, mocker): m = mocker.spy(sys.modules[__name__], 'poadc') From a14deb225f75a4384d30139eab33fff29aa273bd Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 21 Aug 2019 16:02:52 -0600 Subject: [PATCH 63/98] review comments --- docs/sphinx/source/whatsnew/v0.7.0.rst | 8 +++++--- pvlib/temperature.py | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 8c1b6ef487..30b9294cf5 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -27,7 +27,8 @@ API Changes `temperature.pvsyst_cell`. These functions now require model-specific parameters as kwargs. - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. - - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dicts rather than tuple for parameter sets. dict keys for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. dict keys for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. - Added attribute `PVSystem.temperature_model_parameters` (dict) @@ -36,9 +37,10 @@ API Changes only, e.g., default is `open_rack`. - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - Implemented `pvsyst` as an option for `ModelChain.temp_model`. - - `ModelChain.temp_model` now defaults to `None. The temperature + - `ModelChain.temp_model` now defaults to `None`. The temperature model can be inferred from `PVSystem.temperature_model_parameters`. + - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. Enhancements ~~~~~~~~~~~~ @@ -47,7 +49,7 @@ Enhancements Bug fixes ~~~~~~~~~ -* Fix handling of keyword arguments in forecasts.get_processed_data`. +* Fix handling of keyword arguments in `forecasts.get_processed_data`. (:issue:`745`) * Fix output as Series feature in :py:func:`pvlib.pvsystem.ashraeiam`. * Fix rounding issue in `clearsky._linearly_scale`, a function that converts diff --git a/pvlib/temperature.py b/pvlib/temperature.py index e26f0c50eb..6db0b15a0e 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -37,13 +37,13 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Parameters ---------- - poa_global : numeric or Series + poa_global : numeric Total incident irradiance [W/m^2]. - temp_air : numeric or Series + temp_air : numeric Ambient dry bulb temperature [C]. - wind_speed : numeric or Series + wind_speed : numeric Wind speed at a height of 10 meters [m/s]. a : float @@ -60,7 +60,7 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Returns ------- - numeric or Series, values in degrees C. + numeric, values in degrees C. Notes ----- @@ -107,7 +107,7 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, ''' module_temperature = sapm_module(poa_global, temp_air, wind_speed, a, b) - return module_temperature + (poa_global / irrad_ref) * (deltaT) + return module_temperature + (poa_global / irrad_ref) * deltaT def sapm_module(poa_global, temp_air, wind_speed, a, b): @@ -117,13 +117,13 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): Parameters ---------- - poa_global : numeric or Series + poa_global : numeric Total incident irradiance [W/m^2]. - temp_air : numeric or Series + temp_air : numeric Ambient dry bulb temperature [C]. - wind_speed : numeric or Series + wind_speed : numeric Wind speed at a height of 10 meters [m/s]. a : float @@ -134,7 +134,7 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): Returns ------- - numeric or Series, values in degrees C. + numeric, values in degrees C. Notes ----- @@ -181,13 +181,13 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, Parameters ---------- - poa_global : numeric or Series + poa_global : numeric Total incident irradiance [W/m^2]. - temp_air : numeric or Series + temp_air : numeric Ambient dry bulb temperature [C]. - wind_speed : numeric or Series, default 1.0 + wind_speed : numeric, default 1.0 Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value 1.0 m/2 is the wind speed at module height used to determine NOCT. [m/s] @@ -211,7 +211,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, Returns ------- - numeric or Series, values in degrees Celsius + numeric, values in degrees Celsius Notes ----- From 7cea61c1ab9e1fb836d5397f1fabc7b90327c72e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 22 Aug 2019 12:17:25 -0600 Subject: [PATCH 64/98] remove unrelated code --- pvlib/ivtools.py | 228 ------------------------------------- pvlib/ivtools/ivcurves.py | 64 ----------- pvlib/test/test_ivtools.py | 30 ----- 3 files changed, 322 deletions(-) delete mode 100644 pvlib/ivtools.py delete mode 100644 pvlib/ivtools/ivcurves.py delete mode 100644 pvlib/test/test_ivtools.py diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py deleted file mode 100644 index 4c6d275def..0000000000 --- a/pvlib/ivtools.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Mar 29 10:34:10 2019 - -@author: cwhanse -""" - -import numpy as np -from PySAM import PySSC - - -def fit_cec_with_sam(celltype, Vmp, Imp, Voc, Isc, alpha_sc, beta_voc, - gamma_pmp, cells_in_series, temp_ref=25): - ''' - Estimates parameters for the CEC single diode model using the SAM SDK. - - Parameters - ---------- - celltype : str - Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', - 'amorphous' - Vmp : float - Voltage at maximum power point at standard test condition (STC) - Imp : float - Current at maximum power point at STC - Voc : float - Open circuit voltage at STC - Isc : float - Short circuit current at STC - alpha_sc : float - Temperature coefficient of short circuit current at STC, A/C - beta_voc : float - Temperature coefficient of open circuit voltage at STC, V/C - gamma_pmp : float - Temperature coefficient of power at maximum point point at STC, %/C - cells_in_series : int - Number of cells in series - temp_ref : float, default 25 - Reference temperature condition - - Returns - ------- - a_ref : float - The product of the usual diode ideality factor (n, unitless), - number of cells in series (Ns), and cell thermal voltage at reference - conditions, in units of V. - - I_L_ref : float - The light-generated current (or photocurrent) at reference conditions, - in amperes. - - I_o_ref : float - The dark or diode reverse saturation current at reference conditions, - in amperes. - - R_sh_ref : float - The shunt resistance at reference conditions, in ohms. - - R_s : float - The series resistance at reference conditions, in ohms. - - Adjust : float - The adjustment to the temperature coefficient for short circuit - current, in percent - ''' - - try: - ssc = PySSC.PySSC() - except Exception as e: - raise(e) - - data = ssc.data_create() - - ssc.data_set_string(data, b'celltype', celltype.encode('utf-8')) - ssc.data_set_number(data, b'Vmp', Vmp) - ssc.data_set_number(data, b'Imp', Imp) - ssc.data_set_number(data, b'Voc', Voc) - ssc.data_set_number(data, b'Isc', Isc) - ssc.data_set_number(data, b'alpha_isc', alpha_sc) - ssc.data_set_number(data, b'beta_voc', beta_voc) - ssc.data_set_number(data, b'gamma_pmp', gamma_pmp) - ssc.data_set_number(data, b'Nser', cells_in_series) - ssc.data_set_number(data, b'Tref', temp_ref) - - solver = ssc.module_create(b'6parsolve') - ssc.module_exec_set_print(0) - if ssc.module_exec(solver, data) == 0: - print('IV curve fit error') - idx = 1 - msg = ssc.module_log(solver, 0) - while (msg != None): - print(' : ' + msg.decode("utf - 8")) - msg = ssc.module_log(solver, idx) - idx = idx + 1 - ssc.module_free(solver) - a_ref = ssc.data_get_number(data, b'a') - I_L_ref = ssc.data_get_number(data, b'Il') - I_o_ref = ssc.data_get_number(data, b'Io') - R_s = ssc.data_get_number(data, b'Rs') - R_sh_ref = ssc.data_get_number(data, b'Rsh') - Adjust = ssc.data_get_number(data, b'Adj') - - return I_L_ref, I_o_ref, R_sh_ref, R_s, a_ref, Adjust - - -def fit_sde_sandia(V, I, Voc, Isc, Vmp, Imp, vlim=0.2, ilim=0.1): - """ Fits the single diode equation to an IV curve. - - If fitting fails, returns NaN in each parameter. - - Parameters - ---------- - V : numeric - Voltage at each point on the IV curve, from 0 to Voc - - I : numeric - Current at each point on the IV curve, from Isc to 0 - - Voc : float - Open circuit voltage - - Isc : float - Short circuit current - - Vmp : float - Voltage at maximum power point - - Imp : float - Current at maximum power point - - vlim : float, default 0.2 - defines linear portion of IV curve i.e. V <= vlim * Voc - - ilim : float, default 0.1 - defines exponential portion of IV curve i.e. I > ilim * Isc - - Returns - ------- - IL : float - photocurrent, A - - I0 : float - dark (saturation) current, A - - Rsh : float - shunt (parallel) resistance, ohm - - Rs : float - series resistance, ohm - - nNsVth : float - product of diode (ideality) factor n (unitless) x number of - cells in series Ns (unitless) x cell thermal voltage Vth (V), V - - References - ---------- - [1] C. B. Jones, C. W. Hansen, Single Diode Parameter Extraction from - In-Field Photovoltaic I-V Curves on a Single Board Computer, 46th IEEE - Photovoltaic Specialist Conference, Chicago, IL, 2019 - """ - # Find intercept and slope of linear portion of IV curve. - # Start with V < vlim * Voc, extend by adding points until slope is - # acceptable - beta = [np.nan for i in range(5)] - # get index of largest voltage less than/equal to limit - idx = _max_index(V, vlim * Voc) - while np.isnan(beta[1]) and (idx<=len(V)): - try: - coef = np.polyfit(V[:idx], I[:idx], deg=1) - if coef[0] < 0: - # intercept term - beta[0] = coef[1].item() - # sign change of slope to get positive parameter value - beta[1] = -coef[0].item() - except: - pass - if np.isnan(beta[1]): - idx += 1 - - if not np.isnan(beta[0]): - # Find parameters from exponential portion of IV curve - Y = beta[0] - beta[1] * V - I - X = np.array([np.ones_like(V), V, I]).T - idx = _min_index(Y, ilim * Isc) - try: - result = np.linalg.lstsq(X[idx:,], np.log(Y[idx:])) - coef = result[0] - beta[3] = coef[1].item() - beta[4] = coef[2].item() - except: - pass - - if not any([np.isnan(beta[i]) for i in [0, 1, 3, 4]]): - # calculate parameters - nNsVth = 1.0 / beta[3] - Rs = beta[4] / beta[3] - Gp = beta[1] / (1.0 - Rs * beta[1]) - Rsh = 1.0 / Gp - IL = (1 + Gp * Rs) * beta[0] - # calculate I0 - I0_Vmp = _calc_I0(IL, Imp, Vmp, Gp, Rs, beta[3]) - I0_Voc = _calc_I0(IL, 0, Voc, Gp, Rs, beta[3]) - if (I0_Vmp > 0) and (I0_Voc > 0): - I0 = 0.5 * (I0_Vmp + I0_Voc) - elif (I0_Vmp > 0): - I0 = I0_Vmp - elif (I0_Voc > 0): - I0 = I0_Voc - else: - I0 = np.nan - else: - IL = I0 = Rs = Rsh = nNsVth = np.nan - - return IL, I0, Rsh, Rs, nNsVth - - -def _calc_I0(IL, I, V, Gp, Rs, beta3): - return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) - - -def _max_index(x, xlim): - """ Finds maximum index of value of x <= xlim """ - return int(np.argwhere(x <= xlim)[-1]) - - -def _min_index(x, xlim): - """ Finds minimum index of value of x > xlim """ - return int(np.argwhere(x > xlim)[0]) diff --git a/pvlib/ivtools/ivcurves.py b/pvlib/ivtools/ivcurves.py deleted file mode 100644 index 50fe36b972..0000000000 --- a/pvlib/ivtools/ivcurves.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri May 3 14:16:24 2019 - -@author: cwhanse -""" - -import numpy as np - - -class IVCurves(): - """ - Contains IV curves and methods for fitting models to the curves. - """ - - def __init__(self, data): - IVCurves.ivdata = data - IVCurves.voc = None - - def __repr(self): - pass - - def __print__(self): - pass - - def fit(): - """ - Fit a model to IV curve data. - """ - pass - - -class _IVCurve(): - """ - Contains a single IV curve - """ - - def __init__(self, V, I, Ee, Tc, Voc=None, Isc=None, Vmp=None, Imp=None): - self.V = V - self.I = I - self.Ee = Ee - self.Tc = Tc - if Voc is None: - self.Voc = V[-1] - if Isc is None: - self.Isc = I[0] - if Vmp is None: - self.Vmp, self.Imp = find_max_power(V, I) - - -def find_max_power(V, I): - """ Finds V, I pair where V*I is maximum - - Parameters - ---------- - V : numeric - I : numeric - - Returns - ------- - Vmax, Imax : values from V and I where V*I is maximum - """ - idx = np.argmax(V * I) - return V[idx], I[idx] diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py deleted file mode 100644 index 71397e970a..0000000000 --- a/pvlib/test/test_ivtools.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu May 9 10:51:15 2019 - -@author: cwhanse -""" - -import numpy as np -from pvlib import pvsystem -from pvlib.ivtools import fit_sde_sandia - - -def get_test_iv_params(): - return {'IL': 8.0, 'I0': 5e-10, 'Rsh': 1000, 'Rs': 0.2, 'nNsVth': 1.61864} - - -def test_fit_sde_sandia(): - test_params = get_test_iv_params() - testcurve = pvsystem.singlediode(photocurrent=test_params['IL'], - saturation_current=test_params['I0'], - resistance_shunt=test_params['Rsh'], - resistance_series=test_params['Rs'], - nNsVth=test_params['nNsVth'], - ivcurve_pnts=300) - expected = tuple(test_params[k] for k in ['IL', 'I0', 'Rsh', 'Rs', - 'nNsVth']) - result = fit_sde_sandia(V=testcurve['v'], I=testcurve['i'], - Voc=testcurve['v_oc'], Isc=testcurve['i_sc'], - Vmp=testcurve['v_mp'], Imp=testcurve['i_mp']) - assert np.allclose(result, expected, rtol=5e-5) From d18ee632d6c3b4ed9192ebefce184b359b90725a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Aug 2019 09:06:10 -0600 Subject: [PATCH 65/98] handle deprecated attributes and functions --- docs/sphinx/source/whatsnew/v0.7.0.rst | 12 +++-- pvlib/modelchain.py | 42 +++++++++++----- pvlib/pvsystem.py | 69 ++++++++++++++++++++++++-- pvlib/test/test_modelchain.py | 16 +++--- pvlib/test/test_pvsystem.py | 42 +++++++++++++++- pvlib/test/test_temperature.py | 13 +---- 6 files changed, 150 insertions(+), 44 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 30b9294cf5..73e062cb73 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -24,10 +24,11 @@ API Changes been altered to be consistent among cell temperature model functions. - Removed `model` as a kwarg from `temperature.sapm_cell` and - `temperature.pvsyst_cell`. These functions now require model-specific - parameters as kwargs. + `temperature.pvsyst_cell`. These functions now require model-specific parameters. + - Added the argument `irrad_ref`, default value 1000, to `temperature.sapm_cell`. - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. - - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. dict keys for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. + - Parameter set names for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. @@ -36,8 +37,9 @@ API Changes - Changed meaning of `PVSystem.racking_model` to describe racking only, e.g., default is `open_rack`. - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - - Implemented `pvsyst` as an option for `ModelChain.temp_model`. - - `ModelChain.temp_model` now defaults to `None`. The temperature + - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. + - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. + - `ModelChain.temperature_model` now defaults to `None`. The temperature model can be inferred from `PVSystem.temperature_model_parameters`. - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index b1eadca592..3da8b3aa35 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -281,7 +281,7 @@ class ModelChain(object): 'first_solar', 'no_loss'. The ModelChain instance will be passed as the first argument to a user-defined function. - temp_model: str or function, default 'sapm' + temperature_model: str or function, default 'sapm' Valid strings are 'sapm' and 'pvsyst'. The ModelChain instance will be passed as the first argument to a user-defined function. @@ -305,6 +305,7 @@ def __init__(self, system, location, airmass_model='kastenyoung1989', dc_model=None, ac_model=None, aoi_model=None, spectral_model=None, temp_model=None, + temperature_model=None, losses_model='no_loss', name=None, **kwargs): self.name = name @@ -320,7 +321,22 @@ def __init__(self, system, location, self.ac_model = ac_model self.aoi_model = aoi_model self.spectral_model = spectral_model - self.temp_model = temp_model + + # TODO: deprecated kwarg temp_model. Remove in v0.8 + if temp_model is not None: + warnings.warn('The temp_model keyword argument is deprecated. Use ' + 'temperature_model instead', pvlibDeprecationWarning) + if temperature_model is None: + temperature_model = temp_model + elif temp_model == temperature_model: + raise warnings.warn('Provide only one of temperature_model or ' + 'temp_model (deprecated).') + else: + raise ValueError('Conflicting values for temperature_model and' + ' temp_model (deprecated). Specify only ' + 'temperature_model.') + self.temperature_model = temperature_model + self.losses_model = losses_model self.orientation_strategy = orientation_strategy @@ -333,7 +349,7 @@ def __repr__(self): 'name', 'orientation_strategy', 'clearsky_model', 'transposition_model', 'solar_position_method', 'airmass_model', 'dc_model', 'ac_model', 'aoi_model', - 'spectral_model', 'temp_model', 'losses_model' + 'spectral_model', 'temperature_model', 'losses_model' ] def getmcattr(self, attr): @@ -668,23 +684,23 @@ def no_spectral_loss(self): return self @property - def temp_model(self): - return self._temp_model + def temperature_model(self): + return self._temperature_model - @temp_model.setter - def temp_model(self, model): + @temperature_model.setter + def temperature_model(self, model): if model is None: - self._temp_model = self.infer_temp_model() + self._temperature_model = self.infer_temp_model() elif isinstance(model, str): model = model.lower() if model == 'sapm': - self._temp_model = self.sapm_temp + self._temperature_model = self.sapm_temp elif model == 'pvsyst': - self._temp_model = self.pvsyst_temp + self._temperature_model = self.pvsyst_temp else: - raise ValueError(model + ' is not a valid temp model') + raise ValueError(model + ' is not a valid temperature model') else: - self._temp_model = partial(model, self) + self._temperature_model = partial(model, self) def infer_temp_model(self): params = set(self.system.temperature_model_parameters.keys()) @@ -951,7 +967,7 @@ def run_model(self, times=None, weather=None): self.aoi_model() self.spectral_model() self.effective_irradiance_model() - self.temp_model() + self.temperature_model() self.dc_model() self.losses_model() self.ac_model() diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 7c4588533a..f5b4dc651a 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1952,7 +1952,8 @@ def sapm(effective_irradiance, temp_cell, module): See Also -------- retrieve_sam - sapm_celltemp + temperature.sapm_cell + temperature.sapm_module ''' T0 = 25 @@ -2016,18 +2017,76 @@ def sapm(effective_irradiance, temp_cell, module): return out +def _sapm_celltemp_translator(*args, **kwargs): + #TODO: remove this function after deprecation period for sapm_celltemp + new_kwargs = {} + # convert position arguments to kwargs + old_arg_list = ['poa_global', 'wind_speed', 'temp_air', 'model'] + for pos in range(len(args)): + new_kwargs[old_arg_list[pos]] = args[pos] + # determine value for new kwarg 'model' + try: + model = new_kwargs['model'] + except KeyError(): + # 'model' not in positional arguments, check kwargs + try: + model = kwargs['model'] + kwargs.pop('model') + except KeyError(): + # 'model' not in kwargs, use old default value + model = 'open_rack_cell_glassback' + new_kwargs.update({ + 'a': temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][model]['a'], + 'b': temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][model]['b'], + 'deltaT': temperature.TEMPERATURE_MODEL_PARAMETERS[ + 'sapm'][model]['deltaT'] + }) + new_kwargs.update(kwargs) # kwargs with unchanged names + new_kwargs['irrad_ref'] = 1000 # default for new kwarg + # convert old positional arguments to named kwargs + return temperature.sapm_cell(**new_kwargs) + + sapm_celltemp = deprecated('0.7', alternative='temperature.sapm_cell', name='sapm_celltemp', removal='0.8', - addendum='Note that the argument names and order ' - 'for temperature.sapm_cell are different than for ' - 'sapm_celltemp')(temperature.sapm_cell) + addendum='Note that the arguments and argument ' + 'order for temperature.sapm_cell are different ' + 'than for sapm_celltemp')(_sapm_celltemp_translator) + + +def _pvsyst_celltemp_translator(*args, **kwargs): + #TODO: remove this function after deprecation period for pvsyst_celltemp + new_kwargs = {} + # convert position arguments to kwargs + old_arg_list = ['poa_global', 'temp_air', 'wind_speed', 'eta_m', + 'alpha_absorption', 'model_params'] + for pos in range(len(args)): + new_kwargs[old_arg_list[pos]] = args[pos] + # determine value for new kwarg 'model' + try: + model = new_kwargs['model_params'] + except KeyError(): + # 'model' not in positional arguments, check kwargs + try: + model = kwargs['model_params'] + kwargs.pop('model_params') + except KeyError(): + # 'model' not in kwargs, use old default value + model = 'freestanding' + new_kwargs.update({ + 'u_c': temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][model]['a'], + 'u_v': temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][model]['b'] + }) + new_kwargs.update(kwargs) # kwargs with unchanged names + # convert old positional arguments to named kwargs + return temperature.pvsyst_cell(**new_kwargs) pvsyst_celltemp = deprecated( '0.7', alternative='temperature.pvsyst_cell', name='pvsyst_celltemp', removal='0.8', addendum='Note that the argument names for ' 'temperature.pvsyst_cell are different than ' - 'for pvsyst_celltemp')(temperature.pvsyst_cell) + 'for pvsyst_celltemp')(_pvsyst_celltemp_translator) def sapm_spectral_loss(airmass_absolute, module): diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 6f8e0048c7..5f7a3260e3 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -218,7 +218,7 @@ def test_run_model_with_weather(system, location, weather, mocker): # test with sapm cell temperature model system.racking_model = 'open_rack_glass_glass' mc = ModelChain(system, location) - mc.temp_model = 'sapm' + mc.temperature_model = 'sapm' m_sapm = mocker.spy(system, 'sapm_celltemp') mc.run_model(weather.index, weather=weather) assert m_sapm.call_count == 1 @@ -230,7 +230,7 @@ def test_run_model_with_weather(system, location, weather, mocker): # test with pvsyst cell temperature model system.racking_model = 'freestanding' mc = ModelChain(system, location) - mc.temp_model = 'pvsyst' + mc.temperature_model = 'pvsyst' m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') mc.run_model(weather.index, weather=weather) assert m_pvsyst.call_count == 1 @@ -294,7 +294,7 @@ def test_infer_dc_model(system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, m = mocker.spy(system, dc_model_function[dc_model]) mc = ModelChain(system, location, aoi_model='no_loss', spectral_model='no_loss', - temp_model=temp_model_function[dc_model]) + temperature_model=temp_model_function[dc_model]) mc.run_model(weather.index, weather=weather) assert m.call_count == 1 assert isinstance(mc.dc, (pd.Series, pd.DataFrame)) @@ -499,7 +499,7 @@ def test_invalid_dc_model_params(system, cec_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_system, location): kwargs = {'dc_model': 'sapm', 'ac_model': 'snlinverter', 'aoi_model': 'no_loss', 'spectral_model': 'no_loss', - 'temp_model': 'sapm', 'losses_model': 'no_loss'} + 'temperature_model': 'sapm', 'losses_model': 'no_loss'} system.module_parameters.pop('A0') # remove a parameter with pytest.raises(ValueError): ModelChain(system, location, **kwargs) @@ -518,12 +518,12 @@ def test_invalid_dc_model_params(system, cec_dc_snl_ac_system, @pytest.mark.parametrize('model', [ 'dc_model', 'ac_model', 'aoi_model', 'spectral_model', 'losses_model', - 'temp_model', 'losses_model' + 'temperature_model', 'losses_model' ]) def test_invalid_models(model, system, location): kwargs = {'dc_model': 'pvwatts', 'ac_model': 'pvwatts', 'aoi_model': 'no_loss', 'spectral_model': 'no_loss', - 'temp_model': 'sapm', 'losses_model': 'no_loss'} + 'temperature_model': 'sapm', 'losses_model': 'no_loss'} kwargs[model] = 'invalid' with pytest.raises(ValueError): ModelChain(system, location, **kwargs) @@ -548,7 +548,7 @@ def test_deprecated_07(): ModelChain(system, location, dc_model='singlediode', # this should fail after 0.7 aoi_model='no_loss', spectral_model='no_loss', - temp_model='sapm', + temperature_model='sapm', ac_model='snlinverter') @@ -699,7 +699,7 @@ def test_ModelChain___repr__(system, location, strategy, strategy_str): ' ac_model: snlinverter', ' aoi_model: sapm_aoi_loss', ' spectral_model: sapm_spectral_loss', - ' temp_model: sapm_temp', + ' temperature_model: sapm_temp', ' losses_model: no_extra_losses' ]) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 62a7792ae6..258e21bc17 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -18,8 +18,9 @@ from pvlib import solarposition from pvlib.location import Location from pvlib import temperature +from pvlib._deprecation import pvlibDeprecationWarning -from conftest import needs_numpy_1_10, requires_scipy +from conftest import needs_numpy_1_10, requires_scipy, fail_on_pvlib_version def test_systemdef_tmy3(): @@ -1544,3 +1545,42 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker): pvsystem.pvwatts_ac.assert_called_once_with(pdc, **system.inverter_parameters) assert out < pdc + + +@fail_on_pvlib_version('0.8') +def test_deprecated_07(): + with pytest.warns(pvlibDeprecationWarning): + pvsystem.sapm_celltemp(1000, 25, 1, -3.47, -0.0594, 3) + with pytest.warns(pvlibDeprecationWarning): + pvsystem.pvsyst_celltemp(1000, 25) + + +@fail_on_pvlib_version('0.8') +def test__pvsyst_celltemp_translator(): + result = pvsystem._pvsyst_celltemp_translator(900, 20, 5) + assert_allclose(result, 45.137, 0.001) + result = pvsystem._pvsyst_celltemp_translator(poa_global=900, temp_air=20, + wind_speed=5) + assert_allclose(result, 45.137, 0.001) + result = pvsystem._pvsyst_celltemp_translator(900, 20, wind_speed=5) + assert_allclose(result, 45.137, 0.001) + result = pvsystem._pvsyst_celltemp_translator(900, 20, wind_speed=5.0, + u_c=23.5, u_v=6.25, + eta_m=0.1) + assert_allclose(result, 33.315, 0.001) + + +@pytest.fixture +def sapm_default(): + return temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + 'open_rack_glass_glass'] + + +@fail_on_pvlib_version('0.8') +def test__sapm_celltemp_translator(sapm_default): + result = pvsystem._sapm_celltemp_translator(900, 5, 20, + 'open_rack_glass_glass') + assert_allclose(result, 43.509, 3) + result = pvsystem._sapm_celltemp_translator(900, 5, temp_air=20, + model='open_rack_glass_glass') + assert_allclose(result, 43.509, 3) diff --git a/pvlib/test/test_temperature.py b/pvlib/test/test_temperature.py index 3589088bb5..c3112d85a6 100644 --- a/pvlib/test/test_temperature.py +++ b/pvlib/test/test_temperature.py @@ -5,10 +5,7 @@ from pandas.util.testing import assert_series_equal from numpy.testing import assert_allclose -from pvlib import pvsystem, temperature -from pvlib._deprecation import pvlibDeprecationWarning - -from conftest import fail_on_pvlib_version +from pvlib import temperature @pytest.fixture @@ -93,14 +90,6 @@ def test_pvsyst_cell_series(): assert_series_equal(expected, result) -@fail_on_pvlib_version('0.8') -def test_deprecated_07(): - with pytest.warns(pvlibDeprecationWarning): - pvsystem.sapm_celltemp(1000, 25, 1, -3.47, -0.0594, 3) - with pytest.warns(pvlibDeprecationWarning): - pvsystem.pvsyst_celltemp(1000, 25) - - def test__temperature_model_params(): params = temperature._temperature_model_params('sapm', 'open_rack_glass_glass') From 79f6d024f16e063cec642598bcf4d8ae702b0b99 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Aug 2019 09:43:36 -0600 Subject: [PATCH 66/98] test fixes --- pvlib/pvsystem.py | 8 ++++---- pvlib/test/test_pvsystem.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f5b4dc651a..319d91c654 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2027,12 +2027,12 @@ def _sapm_celltemp_translator(*args, **kwargs): # determine value for new kwarg 'model' try: model = new_kwargs['model'] - except KeyError(): + except KeyError: # 'model' not in positional arguments, check kwargs try: model = kwargs['model'] kwargs.pop('model') - except KeyError(): + except KeyError: # 'model' not in kwargs, use old default value model = 'open_rack_cell_glassback' new_kwargs.update({ @@ -2065,12 +2065,12 @@ def _pvsyst_celltemp_translator(*args, **kwargs): # determine value for new kwarg 'model' try: model = new_kwargs['model_params'] - except KeyError(): + except KeyError: # 'model' not in positional arguments, check kwargs try: model = kwargs['model_params'] kwargs.pop('model_params') - except KeyError(): + except KeyError: # 'model' not in kwargs, use old default value model = 'freestanding' new_kwargs.update({ diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 258e21bc17..4ebd8659ab 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1550,7 +1550,7 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker): @fail_on_pvlib_version('0.8') def test_deprecated_07(): with pytest.warns(pvlibDeprecationWarning): - pvsystem.sapm_celltemp(1000, 25, 1, -3.47, -0.0594, 3) + pvsystem.sapm_celltemp(1000, 25, 1) with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) From d9960144817db19fe586f70341e93152573a43d1 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 23 Aug 2019 10:39:01 -0700 Subject: [PATCH 67/98] fix documentation --- docs/sphinx/source/api.rst | 1 - docs/sphinx/source/clearsky.rst | 6 +-- docs/sphinx/source/forecasts.rst | 11 +++-- docs/sphinx/source/introexamples.rst | 20 +++++---- docs/sphinx/source/modelchain.rst | 61 ++++++++++++++++++++------ docs/sphinx/source/whatsnew/v0.7.0.rst | 27 +++++++----- pvlib/temperature.py | 33 ++++++++++---- 7 files changed, 108 insertions(+), 51 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 176e0f6a65..d65992f61f 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -268,7 +268,6 @@ Functions relevant for the SAPM model. pvsystem.sapm pvsystem.sapm_effective_irradiance - pvsystem.sapm_celltemp pvsystem.sapm_spectral_loss pvsystem.sapm_aoi_loss pvsystem.snlinverter diff --git a/docs/sphinx/source/clearsky.rst b/docs/sphinx/source/clearsky.rst index c47212f42b..669ca07ca0 100644 --- a/docs/sphinx/source/clearsky.rst +++ b/docs/sphinx/source/clearsky.rst @@ -68,7 +68,7 @@ returns a :py:class:`pandas.DataFrame`. In [1]: tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') - In [1]: times = pd.DatetimeIndex(start='2016-07-01', end='2016-07-04', freq='1min', tz=tus.tz) + In [1]: times = pd.date_range(start='2016-07-01', end='2016-07-04', freq='1min', tz=tus.tz) In [1]: cs = tus.get_clearsky(times) # ineichen with climatology table by default @@ -168,7 +168,7 @@ varies from 300 m to 1500 m. .. ipython:: - In [1]: times = pd.DatetimeIndex(start='2015-01-01', end='2016-01-01', freq='1D') + In [1]: times = pd.date_range(start='2015-01-01', end='2016-01-01', freq='1D') In [1]: sites = [(32, -111, 'Tucson1'), (32.2, -110.9, 'Tucson2'), ...: (33.5, -112.1, 'Phoenix'), (35.1, -106.6, 'Albuquerque')] @@ -608,7 +608,7 @@ GHI data. We first generate and plot the clear sky and measured data. abq = Location(35.04, -106.62, altitude=1619) - times = pd.DatetimeIndex(start='2012-04-01 10:30:00', tz='Etc/GMT+7', periods=30, freq='1min') + times = pd.date_range(start='2012-04-01 10:30:00', tz='Etc/GMT+7', periods=30, freq='1min') cs = abq.get_clearsky(times) diff --git a/docs/sphinx/source/forecasts.rst b/docs/sphinx/source/forecasts.rst index f959982e2c..56e88acf74 100644 --- a/docs/sphinx/source/forecasts.rst +++ b/docs/sphinx/source/forecasts.rst @@ -440,6 +440,7 @@ for details. .. ipython:: python from pvlib.pvsystem import PVSystem, retrieve_sam + from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS from pvlib.tracking import SingleAxisTracker from pvlib.modelchain import ModelChain @@ -447,12 +448,10 @@ for details. cec_inverters = retrieve_sam('cecinverter') module = sandia_modules['Canadian_Solar_CS5P_220M___2009_'] inverter = cec_inverters['SMA_America__SC630CP_US_315V__CEC_2012_'] + temperature_model_parameters = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass'] # model a big tracker for more fun - system = SingleAxisTracker(module_parameters=module, - inverter_parameters=inverter, - modules_per_string=15, - strings_per_inverter=300) + system = SingleAxisTracker(module_parameters=module, inverter_parameters=inverter, temperature_model_parameters=temperature_model_parameters, modules_per_string=15, strings_per_inverter=300) # fx is a common abbreviation for forecast fx_model = GFS() @@ -480,9 +479,9 @@ Here's the forecast plane of array irradiance... .. ipython:: python - mc.temps.plot(); + mc.cell_temperature.plot(); @savefig pv_temps.png width=6in - plt.ylabel('Temperature (C)'); + plt.ylabel('Cell Temperature (C)'); @suppress plt.close(); diff --git a/docs/sphinx/source/introexamples.rst b/docs/sphinx/source/introexamples.rst index 6cd0d54f63..e1b01d94ca 100644 --- a/docs/sphinx/source/introexamples.rst +++ b/docs/sphinx/source/introexamples.rst @@ -30,7 +30,7 @@ configuration at a handful of sites listed below. import pandas as pd import matplotlib.pyplot as plt - naive_times = pd.DatetimeIndex(start='2015', end='2016', freq='1h') + naive_times = pd.date_range(start='2015', end='2016', freq='1h') # very approximate # latitude, longitude, name, altitude, timezone @@ -46,6 +46,7 @@ configuration at a handful of sites listed below. sapm_inverters = pvlib.pvsystem.retrieve_sam('cecinverter') module = sandia_modules['Canadian_Solar_CS5P_220M___2009_'] inverter = sapm_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'] + temperature_model_parameters = pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass'] # specify constant ambient air temp and wind for simplicity temp_air = 20 @@ -88,12 +89,13 @@ to accomplish our system modeling goal: cs['dni'], cs['ghi'], cs['dhi'], dni_extra=dni_extra, model='haydavies') - temps = pvlib.pvsystem.sapm_celltemp(total_irrad['poa_global'], - wind_speed, temp_air) + tcell = pvlib.temperature.sapm_cell(total_irrad['poa_global'], + temp_air, wind_speed, + **temperature_model_parameters) effective_irradiance = pvlib.pvsystem.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], am_abs, aoi, module) - dc = pvlib.pvsystem.sapm(effective_irradiance, temps['temp_cell'], module) + dc = pvlib.pvsystem.sapm(effective_irradiance, tcell, module) ac = pvlib.pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter) annual_energy = ac.sum() energies[name] = annual_energy @@ -149,7 +151,8 @@ by examining the parameters defined for the module. from pvlib.modelchain import ModelChain system = PVSystem(module_parameters=module, - inverter_parameters=inverter) + inverter_parameters=inverter, + temperature_model_parameters=temperature_model_parameters) energies = {} for latitude, longitude, name, altitude, timezone in coordinates: @@ -214,6 +217,7 @@ modeling goal: for latitude, longitude, name, altitude, timezone in coordinates: localized_system = LocalizedPVSystem(module_parameters=module, inverter_parameters=inverter, + temperature_model_parameters=temperature_model_parameters, surface_tilt=latitude, surface_azimuth=180, latitude=latitude, @@ -229,15 +233,15 @@ modeling goal: clearsky['dni'], clearsky['ghi'], clearsky['dhi']) - temps = localized_system.sapm_celltemp(total_irrad['poa_global'], - wind_speed, temp_air) + tcell = localized_system.sapm_celltemp(total_irrad['poa_global'], + temp_air, wind_speed) aoi = localized_system.get_aoi(solar_position['apparent_zenith'], solar_position['azimuth']) airmass = localized_system.get_airmass(solar_position=solar_position) effective_irradiance = localized_system.sapm_effective_irradiance( total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass['airmass_absolute'], aoi) - dc = localized_system.sapm(effective_irradiance, temps['temp_cell']) + dc = localized_system.sapm(effective_irradiance, tcell) ac = localized_system.snlinverter(dc['v_mp'], dc['p_mp']) annual_energy = ac.sum() energies[name] = annual_energy diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst index 1b324801b3..9501dff1d4 100644 --- a/docs/sphinx/source/modelchain.rst +++ b/docs/sphinx/source/modelchain.rst @@ -45,6 +45,8 @@ objects, module data, and inverter data. from pvlib.pvsystem import PVSystem from pvlib.location import Location from pvlib.modelchain import ModelChain + from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS + temperature_model_parameters = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass'] # load some module and inverter specifications sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod') @@ -61,7 +63,8 @@ object. location = Location(latitude=32.2, longitude=-110.9) system = PVSystem(surface_tilt=20, surface_azimuth=200, module_parameters=sandia_module, - inverter_parameters=cec_inverter) + inverter_parameters=cec_inverter, + temperature_model_parameters=temperature_model_parameters) mc = ModelChain(system, location) Printing a ModelChain object will display its models. @@ -87,6 +90,10 @@ examples are shown below. mc.aoi +.. ipython:: python + + mc.cell_temperature + .. ipython:: python mc.dc @@ -141,8 +148,11 @@ model, AC model, AOI loss model, and spectral loss model. .. ipython:: python - sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) - mc = ModelChain(system, location) + sapm_system = PVSystem( + module_parameters=sandia_module, + inverter_parameters=cec_inverter, + temperature_model_parameters=temperature_model_parameters) + mc = ModelChain(sapm_system, location) print(mc) .. ipython:: python @@ -160,7 +170,10 @@ information to determine which of those models to choose. .. ipython:: python - pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + pvwatts_system = PVSystem( + module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}, + inverter_parameters={'pdc0': 240}, + temperature_model_parameters=temperature_model_parameters) mc = ModelChain(pvwatts_system, location, aoi_model='physical', spectral_model='no_loss') print(mc) @@ -176,8 +189,11 @@ functions for a PVSystem that contains SAPM-specific parameters. .. ipython:: python - sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) - mc = ModelChain(system, location, aoi_model='physical', spectral_model='no_loss') + sapm_system = PVSystem( + module_parameters=sandia_module, + inverter_parameters=cec_inverter, + temperature_model_parameters=temperature_model_parameters) + mc = ModelChain(sapm_system, location, aoi_model='physical', spectral_model='no_loss') print(mc) .. ipython:: python @@ -264,21 +280,24 @@ the ModelChain.pvwatts_dc method is shown below. Its only argument is The ModelChain.pvwatts_dc method calls the pvwatts_dc method of the PVSystem object that we supplied using data that is stored in its own -``effective_irradiance`` and ``temps`` attributes. Then it assigns the +``effective_irradiance`` and ``cell_temperature`` attributes. Then it assigns the result to the ``dc`` attribute of the ModelChain object. The code below shows a simple example of this. .. ipython:: python # make the objects - pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + pvwatts_system = PVSystem( + module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}, + inverter_parameters={'pdc0': 240}, + temperature_model_parameters=temperature_model_parameters) mc = ModelChain(pvwatts_system, location, aoi_model='no_loss', spectral_model='no_loss') # manually assign data to the attributes that ModelChain.pvwatts_dc will need. # for standard workflows, run_model would assign these attributes. mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')]) - mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) + mc.cell_temperature = pd.Series(50, index=[pd.Timestamp('20170401 1200-0700')]) # run ModelChain.pvwatts_dc and look at the result mc.pvwatts_dc(); @@ -304,13 +323,16 @@ PVSystem.scale_voltage_current_power method. .. ipython:: python # make the objects - sapm_system = PVSystem(module_parameters=sandia_module, inverter_parameters=cec_inverter) + sapm_system = PVSystem( + module_parameters=sandia_module, + inverter_parameters=cec_inverter, + temperature_model_parameters=temperature_model_parameters) mc = ModelChain(sapm_system, location) # manually assign data to the attributes that ModelChain.sapm will need. # for standard workflows, run_model would assign these attributes. mc.effective_irradiance = pd.Series(1000, index=[pd.Timestamp('20170401 1200-0700')]) - mc.temps = pd.DataFrame({'temp_cell': 50, 'temp_module': 50}, index=[pd.Timestamp('20170401 1200-0700')]) + mc.cell_temperature = pd.Series(50, index=[pd.Timestamp('20170401 1200-0700')]) # run ModelChain.sapm and look at the result mc.sapm(); @@ -333,7 +355,10 @@ section. .. ipython:: python - pvwatts_system = PVSystem(module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}) + pvwatts_system = PVSystem( + module_parameters={'pdc0': 240, 'gamma_pdc': -0.004}, + inverter_parameters={'pdc0': 240}, + temperature_model_parameters=temperature_model_parameters) mc = ModelChain(pvwatts_system, location, aoi_model='no_loss', spectral_model='no_loss') mc.dc_model.__func__ @@ -403,18 +428,26 @@ function if you wanted to. return mc - def pvusa_ac_mc_wrapper(mc): + def pvusa_ac_mc(mc): # keep it simple mc.ac = mc.dc return mc + + def no_loss_temperature(mc): + # keep it simple + mc.cell_temperature = mc.weather['temp_air'] + return mc + + .. ipython:: python module_parameters = {'a': 0.2, 'b': 0.00001, 'c': 0.001, 'd': -0.00005} pvusa_system = PVSystem(module_parameters=module_parameters) mc = ModelChain(pvusa_system, location, - dc_model=pvusa_mc_wrapper, ac_model=pvusa_ac_mc_wrapper, + dc_model=pvusa_mc_wrapper, ac_model=pvusa_ac_mc, + temp_model=no_loss_temperature, aoi_model='no_loss', spectral_model='no_loss') A ModelChain object uses Python’s functools.partial function to assign diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 30b9294cf5..21edb22246 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -1,7 +1,7 @@ .. _whatsnew_0700: v0.7.0 (MONTH DAY, YEAR) ---------------------- +------------------------ This is a major release that drops support for Python 2 and Python 3.4. We recommend all users of v0.6.3 upgrade to this release after checking API @@ -12,9 +12,8 @@ compatibility notes. API Changes ~~~~~~~~~~~ -* Changes to functions and methods for cell temperature models: - (:issue:`678') - - Moved functions for cell temperature and constant +* Changes to functions and methods for cell temperature models (:issue:`678`): + - Moved functions for cell temperature and constant `TEMP_MODEL_PARAMS` from `pvsystem.py` to `temperature.py`. - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to `temperature.sapm_cell` and `temperature.pvsyst_cell`. @@ -27,20 +26,28 @@ API Changes `temperature.pvsyst_cell`. These functions now require model-specific parameters as kwargs. - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. - - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. dict keys for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. + - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than + tuple for a parameter set. dict keys for + `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + - Parameter sets for the SAPM cell temperature model named + 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' + are considered obsolete and have been removed. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. - Added attribute `PVSystem.temperature_model_parameters` (dict) to contain temperature model parameters. - Changed meaning of `PVSystem.racking_model` to describe racking only, e.g., default is `open_rack`. - - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. + - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, + changed kwarg `model` to `parameter_set`. `parameter_set` expects + a str which is a valid key for + `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding + temperature model. - Implemented `pvsyst` as an option for `ModelChain.temp_model`. - `ModelChain.temp_model` now defaults to `None`. The temperature - model can be inferred from - `PVSystem.temperature_model_parameters`. - - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. + model can be inferred from `PVSystem.temperature_model_parameters`. + - `modelchain.basic_chain` has a new required argument + `temperature_model_parameters`. Enhancements ~~~~~~~~~~~~ diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 6db0b15a0e..ac77850dc6 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -56,7 +56,8 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Parameter :math:`\Delta T` in :eq:`sapm2` [C]. irrad_ref : float, default 1000 - Reference irradiance, parameter :math:`E_{0}` in :eq:`sapm2` [W/m^2]. + Reference irradiance, parameter :math:`E_{0}` in + :eq:`sapm2` [W/m^2]. Returns ------- @@ -70,21 +71,21 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, .. math:: :label: sapm1 - T_{m} = E \times \exp (a + b \times WS) + T_{a} + T_{m} = E \times \exp (a + b \times WS) + T_{a} .. math:: :label: sapm2 - T_{C} = T_{m} + \frac{E}{E_{0}} \Delta T + T_{C} = T_{m} + \frac{E}{E_{0}} \Delta T The module back surface temperature :math:`T_{m}` is implemented in - ``cell_temperature.sapm_module``. + :py:func:`~pvlib.temperature.sapm_module`. Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and ambient air temperature :math:`T_{a}` (C). Model parameters depend both on the module construction and its mounting. Parameter sets are provided in [1] for representative modules and mounting, and are coded for convenience - in ``cell_temperature.TEMPERATURE_MODEL_PARAMETERS``. + in ``pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS``. +---------------+----------------+-------+---------+---------------------+ | Module | Mounting | a | b | :math:`\Delta T [C]`| @@ -104,6 +105,12 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, Model", SAND Report 3535, Sandia National Laboratories, Albuquerque, NM. + Examples + -------- + >>> from pvlib.temperature import sapm_cell, TEMPERATURE_MODEL_PARAMETERS + >>> params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass'] + >>> sapm_cell(1000, 10, 0, **params) + 44.11703066106086 ''' module_temperature = sapm_module(poa_global, temp_air, wind_speed, a, b) @@ -127,10 +134,10 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): Wind speed at a height of 10 meters [m/s]. a : float - Parameter :math:`a` in :eq:`sapm1`. + Parameter :math:`a` in :eq:`sapm1mod`. b : float - Parameter :math:`b` in :eq:`sapm1`. + Parameter :math:`b` in :eq:`sapm1mod`. Returns ------- @@ -141,8 +148,9 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): The model for module temperature :math:`T_{m}` is given by Eq. 11 in [1]. .. math:: - :label: sapm1 - T_{m} = E \times \exp (a + b \times WS) + T_{a} + :label: sapm1mod + + T_{m} = E \times \exp (a + b \times WS) + T_{a} Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and ambient air temperature :math:`T_{a}` (C). Model outputs are surface @@ -245,6 +253,13 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, [2] Faiman, D. (2008). "Assessing the outdoor operating temperature of photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. + + Examples + -------- + >>> from pvlib.temperature import pvsyst_cell, TEMPERATURE_MODEL_PARAMETERS + >>> params = TEMPERATURE_MODEL_PARAMETERS['pvsyst']['freestanding'] + >>> pvsyst_cell(1000, 10, **params) + 37.93103448275862 """ total_loss_factor = u_c + u_v * wind_speed From 4d58361319b0d2a1687e3c06cc37e41516bd1908 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 23 Aug 2019 11:02:39 -0700 Subject: [PATCH 68/98] fix infer_temp_model naming, whatsnew --- docs/sphinx/source/api.rst | 4 ++-- docs/sphinx/source/modelchain.rst | 2 +- docs/sphinx/source/whatsnew/v0.7.0.rst | 19 ++++++------------- pvlib/modelchain.py | 8 ++++---- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index d65992f61f..8af2bf0f40 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -484,7 +484,7 @@ ModelChain properties that are aliases for your specific modeling functions. modelchain.ModelChain.ac_model modelchain.ModelChain.aoi_model modelchain.ModelChain.spectral_model - modelchain.ModelChain.temp_model + modelchain.ModelChain.temperature_model modelchain.ModelChain.losses_model modelchain.ModelChain.effective_irradiance_model @@ -529,7 +529,7 @@ on the information in the associated :py:class:`~pvsystem.PVSystem` object. modelchain.ModelChain.infer_ac_model modelchain.ModelChain.infer_aoi_model modelchain.ModelChain.infer_spectral_model - modelchain.ModelChain.infer_temp_model + modelchain.ModelChain.infer_temperature_model modelchain.ModelChain.infer_losses_model Functions diff --git a/docs/sphinx/source/modelchain.rst b/docs/sphinx/source/modelchain.rst index 9501dff1d4..4025451cda 100644 --- a/docs/sphinx/source/modelchain.rst +++ b/docs/sphinx/source/modelchain.rst @@ -447,7 +447,7 @@ function if you wanted to. mc = ModelChain(pvusa_system, location, dc_model=pvusa_mc_wrapper, ac_model=pvusa_ac_mc, - temp_model=no_loss_temperature, + temperature_model=no_loss_temperature, aoi_model='no_loss', spectral_model='no_loss') A ModelChain object uses Python’s functools.partial function to assign diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 6226fa1603..48ceaf6d4d 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -27,14 +27,12 @@ API Changes - Added the argument `irrad_ref`, default value 1000, to `temperature.sapm_cell`. - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than - tuple for a parameter set. dict keys for - `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + tuple for a parameter set. + - Parameter set names for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. - - Parameter set names for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. - Added attribute `PVSystem.temperature_model_parameters` (dict) @@ -46,18 +44,13 @@ API Changes a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - - Implemented `pvsyst` as an option for `ModelChain.temp_model`. - - `ModelChain.temp_model` now defaults to `None`. The temperature + - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. + - `ModelChain.temperature_model` now defaults to `None`. The temperature model can be inferred from `PVSystem.temperature_model_parameters`. + - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. - - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. - - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. - - `ModelChain.temperature_model` now defaults to `None`. The temperature - model can be inferred from - `PVSystem.temperature_model_parameters`. - - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. + Enhancements ~~~~~~~~~~~~ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 3da8b3aa35..f11884fc1b 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -304,8 +304,7 @@ def __init__(self, system, location, solar_position_method='nrel_numpy', airmass_model='kastenyoung1989', dc_model=None, ac_model=None, aoi_model=None, - spectral_model=None, temp_model=None, - temperature_model=None, + spectral_model=None, temperature_model=None, losses_model='no_loss', name=None, **kwargs): self.name = name @@ -323,6 +322,7 @@ def __init__(self, system, location, self.spectral_model = spectral_model # TODO: deprecated kwarg temp_model. Remove in v0.8 + temp_model = kwargs.pop('temp_model', None) if temp_model is not None: warnings.warn('The temp_model keyword argument is deprecated. Use ' 'temperature_model instead', pvlibDeprecationWarning) @@ -690,7 +690,7 @@ def temperature_model(self): @temperature_model.setter def temperature_model(self, model): if model is None: - self._temperature_model = self.infer_temp_model() + self._temperature_model = self.infer_temperature_model() elif isinstance(model, str): model = model.lower() if model == 'sapm': @@ -702,7 +702,7 @@ def temperature_model(self, model): else: self._temperature_model = partial(model, self) - def infer_temp_model(self): + def infer_temperature_model(self): params = set(self.system.temperature_model_parameters.keys()) if set(['a', 'b', 'deltaT']) <= params: return self.sapm_temp From 62ed9dccc7fe9935b25900be8b0c6338ce081fca Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Aug 2019 12:18:01 -0600 Subject: [PATCH 69/98] test fixes and docstring edits --- docs/sphinx/source/whatsnew/v0.7.0.rst | 8 +++++--- pvlib/modelchain.py | 6 +++--- pvlib/temperature.py | 17 ++++++++++------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 73e062cb73..c85badbe3e 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -18,9 +18,10 @@ API Changes `TEMP_MODEL_PARAMS` from `pvsystem.py` to `temperature.py`. - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to `temperature.sapm_cell` and `temperature.pvsyst_cell`. - - Created `temperature.sapm_module`. - - Argument order and output type of `pvsystem.sapm_celltemp`, - `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` have + - `temperature.sapm_cell` returns only the cell temperature, whereas the old `pvsystem.sapm_celltemp` returned a `DataFrame` with both cell and module temperatures. + - Created `temperature.sapm_module` to return module temperature using the SAPM temperature model. + - Argument order for`pvsystem.sapm_celltemp`, + `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` has been altered to be consistent among cell temperature model functions. - Removed `model` as a kwarg from `temperature.sapm_cell` and @@ -38,6 +39,7 @@ API Changes only, e.g., default is `open_rack`. - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, changed kwarg `model` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. + - `ModelChain.temps` attribute renamed to `ModelChain.cell_temperature`, and its datatype is now `numeric` rather than `DataFrame`. - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. - `ModelChain.temperature_model` now defaults to `None`. The temperature model can be inferred from diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 3da8b3aa35..f60120191c 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -51,7 +51,7 @@ def basic_chain(times, latitude, longitude, Module parameters as defined by the SAPM. See pvsystem.sapm for details. - temperature_model_parameters : None, dict or Series, default None. + temperature_model_parameters : None, dict or Series. Temperature model parameters as defined by the SAPM. See temperature.sapm_cell for details. @@ -329,8 +329,8 @@ def __init__(self, system, location, if temperature_model is None: temperature_model = temp_model elif temp_model == temperature_model: - raise warnings.warn('Provide only one of temperature_model or ' - 'temp_model (deprecated).') + warnings.warn('Provide only one of temperature_model or ' + 'temp_model (deprecated).') else: raise ValueError('Conflicting values for temperature_model and' ' temp_model (deprecated). Specify only ' diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 6db0b15a0e..3d3045df84 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -25,7 +25,7 @@ def _temperature_model_params(model, parameter_set): except KeyError: msg = ('{} is not a named set of parameters for the {} cell' ' temperature model.' - ' See pvlib.celltemp.TEMPERATURE_MODEL_PARAMETERS' + ' See pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS' ' for names'.format(parameter_set, model)) raise KeyError(msg) @@ -78,13 +78,13 @@ def sapm_cell(poa_global, temp_air, wind_speed, a, b, deltaT, T_{C} = T_{m} + \frac{E}{E_{0}} \Delta T The module back surface temperature :math:`T_{m}` is implemented in - ``cell_temperature.sapm_module``. + ``temperature.sapm_module``. Inputs to the model are plane-of-array irradiance :math:`E` (W/m2) and ambient air temperature :math:`T_{a}` (C). Model parameters depend both on the module construction and its mounting. Parameter sets are provided in [1] for representative modules and mounting, and are coded for convenience - in ``cell_temperature.TEMPERATURE_MODEL_PARAMETERS``. + in ``temperature.TEMPERATURE_MODEL_PARAMETERS``. +---------------+----------------+-------+---------+---------------------+ | Module | Mounting | a | b | :math:`\Delta T [C]`| @@ -149,7 +149,8 @@ def sapm_module(poa_global, temp_air, wind_speed, a, b): temperature at the back of the module :math:`T_{m}` and cell temperature :math:`T_{C}`. Model parameters depend both on the module construction and its mounting. Parameter sets are provided in [1] for representative modules - and mounting. + and mounting, and are coded for convenience in + ``temperature.TEMPERATURE_MODEL_PARAMETERS``. +---------------+----------------+-------+---------+---------------------+ | Module | Mounting | a | b | :math:`\Delta T [C]`| @@ -226,9 +227,11 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, air temperature :math:`T_{a}` (C) and wind speed :math:`WS` (m/s). Model output is cell temperature :math:`T_{C}`. Model parameters depend both on the module construction and its mounting. Parameters are provided in - [1] for open (freestanding) and close (insulated) mounting configurations. - The heat loss factors provided represent the combined effect of convection, - radiation and conduction, and their values are experimentally determined. + [1] for open (freestanding) and close (insulated) mounting configurations, + , and are coded for convenience in + ``temperature.TEMPERATURE_MODEL_PARAMETERS``. The heat loss factors + provided represent the combined effect of convection, radiation and + conduction, and their values are experimentally determined. +--------------+---------------+---------------+ | Mounting | :math:`U_{c}` | :math:`U_{v}` | From 4a60c37ffdbd20858a32ad4110480f80088892ec Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 24 Aug 2019 14:28:22 -0600 Subject: [PATCH 70/98] handle ModelChain deprecated behavior, test type input of _translate functions --- pvlib/modelchain.py | 14 +++++++++++ pvlib/pvsystem.py | 44 +++++++++++++++++++++-------------- pvlib/test/test_modelchain.py | 17 ++++++++++++++ pvlib/test/test_pvsystem.py | 24 +++++++++++++------ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 27fdeb1165..16799fea4a 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -309,6 +309,20 @@ def __init__(self, system, location, self.name = name self.system = system + + # TODO: deprecated behavior if PVSystem.temperature_model_parameters + # is not specified. Remove in v0.8 + if self.system.temperature_model_parameters is None: + warnings.warn('PVSystem temperature_model_parameters attribute ' + 'is not assigned. Reverting to deprecated default: ' + 'the SAPM cell temperature model with parameters ' + 'representing a glass/glass module in open racking.' + 'In the future PVSystem.temperature_model_parameters' + ' will be required', pvlibDeprecationWarning) + params = temperature._temperature_model_params('sapm') + self.system.temperature_model_parameters = params[ + 'open_rack_glass_glass'] + self.location = location self.clearsky_model = clearsky_model self.transposition_model = transposition_model diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 319d91c654..224eb79bb8 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2026,21 +2026,26 @@ def _sapm_celltemp_translator(*args, **kwargs): new_kwargs[old_arg_list[pos]] = args[pos] # determine value for new kwarg 'model' try: - model = new_kwargs['model'] + param_set = new_kwargs['model'] except KeyError: # 'model' not in positional arguments, check kwargs try: - model = kwargs['model'] + param_set = kwargs['model'] kwargs.pop('model') except KeyError: # 'model' not in kwargs, use old default value - model = 'open_rack_cell_glassback' - new_kwargs.update({ - 'a': temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][model]['a'], - 'b': temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][model]['b'], - 'deltaT': temperature.TEMPERATURE_MODEL_PARAMETERS[ - 'sapm'][model]['deltaT'] - }) + param_set = 'open_rack_glass_glass' + if type(param_set) is list: + new_kwargs.update({'a': param_set[0], + 'b': param_set[1], + 'deltaT': param_set[2]}) + elif type(param_set) is dict: + new_kwargs.update(param_set) + else: # string + params = temperature._temperature_model_params('sapm') + new_kwargs.update({'a': params[param_set]['a'], + 'b': params[param_set]['b'], + 'deltaT': params[param_set]['deltaT']}) new_kwargs.update(kwargs) # kwargs with unchanged names new_kwargs['irrad_ref'] = 1000 # default for new kwarg # convert old positional arguments to named kwargs @@ -2064,19 +2069,22 @@ def _pvsyst_celltemp_translator(*args, **kwargs): new_kwargs[old_arg_list[pos]] = args[pos] # determine value for new kwarg 'model' try: - model = new_kwargs['model_params'] + param_set = new_kwargs['model_params'] except KeyError: - # 'model' not in positional arguments, check kwargs + # 'model_params' not in positional arguments, check kwargs try: - model = kwargs['model_params'] + param_set = kwargs['model_params'] kwargs.pop('model_params') except KeyError: - # 'model' not in kwargs, use old default value - model = 'freestanding' - new_kwargs.update({ - 'u_c': temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][model]['a'], - 'u_v': temperature.TEMPERATURE_MODEL_PARAMETERS['pvsyst'][model]['b'] - }) + # 'model_params' not in kwargs, use old default value + param_set = 'freestanding' + if type(param_set) in (list, tuple): + new_kwargs.update({'u_c': param_set[0], + 'u_v': param_set[1]}) + else: # string + params = temperature._temperature_model_params('pvsyst') + new_kwargs.update({'u_c': params[param_set]['u_c'], + 'u_v': params[param_set]['u_v']}) new_kwargs.update(kwargs) # kwargs with unchanged names # convert old positional arguments to named kwargs return temperature.pvsyst_cell(**new_kwargs) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 5f7a3260e3..31b298a0b3 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -552,6 +552,23 @@ def test_deprecated_07(): ac_model='snlinverter') +@fail_on_pvlib_version('0.8') +def test_deprecated_08(): + # explicit system creation call because fail_on_pvlib_version + # does not support decorators. + # does not matter what the parameters are, just fake it until we make it + module_parameters = {'R_sh_ref': 1, 'a_ref': 1, 'I_o_ref': 1, + 'alpha_sc': 1, 'I_L_ref': 1, 'R_s': 1} + # do not assign PVSystem.temperature_model_parameters + system = PVSystem(module_parameters=module_parameters) + with pytest.warns(pvlibDeprecationWarning): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temperature_model='sapm', + ac_model='snlinverter') + + @requires_tables @fail_on_pvlib_version('0.7') def test_deprecated_clearsky_07(): diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 4ebd8659ab..942daebb32 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1548,7 +1548,7 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker): @fail_on_pvlib_version('0.8') -def test_deprecated_07(): +def test_deprecated_08(): with pytest.warns(pvlibDeprecationWarning): pvsystem.sapm_celltemp(1000, 25, 1) with pytest.warns(pvlibDeprecationWarning): @@ -1568,12 +1568,14 @@ def test__pvsyst_celltemp_translator(): u_c=23.5, u_v=6.25, eta_m=0.1) assert_allclose(result, 33.315, 0.001) - - -@pytest.fixture -def sapm_default(): - return temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ - 'open_rack_glass_glass'] + result = pvsystem._pvsyst_celltemp_translator(900, 20, wind_speed=5.0, + eta_m=0.1, + model_params=[23.5, 6.25]) + assert_allclose(result, 33.315, 0.001) + result = pvsystem._pvsyst_celltemp_translator(900, 20, wind_speed=5.0, + eta_m=0.1, + model_params=(23.5, 6.25)) + assert_allclose(result, 33.315, 0.001) @fail_on_pvlib_version('0.8') @@ -1584,3 +1586,11 @@ def test__sapm_celltemp_translator(sapm_default): result = pvsystem._sapm_celltemp_translator(900, 5, temp_air=20, model='open_rack_glass_glass') assert_allclose(result, 43.509, 3) + result = pvsystem._sapm_celltemp_translator(900, 5, 20, sapm_default) + assert_allclose(result, 43.509, 3) + params = temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ + 'open_rack_glass_glass'] + result = pvsystem._sapm_celltemp_translator(900, 5, 20, + [params['a'], params['b'], + params['deltaT']]) + assert_allclose(result, 43.509, 3) From 9eae773a9c2de440f0af405f197adae3be89ab07 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 24 Aug 2019 19:22:18 -0600 Subject: [PATCH 71/98] test fixes --- docs/sphinx/source/whatsnew/v0.7.0.rst | 1 + pvlib/modelchain.py | 26 +++++++++++++------------- pvlib/pvsystem.py | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index fa9b08a5a6..faa896f2af 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -49,6 +49,7 @@ API Changes - `ModelChain.temps` attribute renamed to `ModelChain.cell_temperature`, and its datatype is now `numeric` rather than `DataFrame`. - `ModelChain.temperature_model` now defaults to `None`. The temperature model can be inferred from `PVSystem.temperature_model_parameters`. + - If `PVSystem.temperature_model_parameters` is not specified, `ModelChain` defaults to old behavior, using the SAPM temperautre model with parameters for a glass/glass model on open racking. This behavior is deprecated, and will be removed in v0.8. In v0.8 `PVSystem.temperature_model_parameters` will be required for `ModelChain`. - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. - `modelchain.basic_chain` has a new required argument `temperature_model_parameters`. diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 16799fea4a..0ea8d0a8d2 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -310,19 +310,6 @@ def __init__(self, system, location, self.name = name self.system = system - # TODO: deprecated behavior if PVSystem.temperature_model_parameters - # is not specified. Remove in v0.8 - if self.system.temperature_model_parameters is None: - warnings.warn('PVSystem temperature_model_parameters attribute ' - 'is not assigned. Reverting to deprecated default: ' - 'the SAPM cell temperature model with parameters ' - 'representing a glass/glass module in open racking.' - 'In the future PVSystem.temperature_model_parameters' - ' will be required', pvlibDeprecationWarning) - params = temperature._temperature_model_params('sapm') - self.system.temperature_model_parameters = params[ - 'open_rack_glass_glass'] - self.location = location self.clearsky_model = clearsky_model self.transposition_model = transposition_model @@ -351,6 +338,19 @@ def __init__(self, system, location, 'temperature_model.') self.temperature_model = temperature_model + # TODO: deprecated behavior if PVSystem.temperature_model_parameters + # is not specified. Remove in v0.8 + if not any(self.system.temperature_model_parameters): + warnings.warn('PVSystem temperature_model_parameters attribute ' + 'is not assigned. Reverting to deprecated default: ' + 'the SAPM cell temperature model with parameters ' + 'representing a glass/glass module in open racking.' + 'In the future PVSystem.temperature_model_parameters' + ' will be required', pvlibDeprecationWarning) + params = temperature._temperature_model_params('sapm') + self.system.temperature_model_parameters = params[ + 'open_rack_glass_glass'] + self.losses_model = losses_model self.orientation_strategy = orientation_strategy diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 224eb79bb8..e56041c592 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2018,7 +2018,7 @@ def sapm(effective_irradiance, temp_cell, module): def _sapm_celltemp_translator(*args, **kwargs): - #TODO: remove this function after deprecation period for sapm_celltemp + # TODO: remove this function after deprecation period for sapm_celltemp new_kwargs = {} # convert position arguments to kwargs old_arg_list = ['poa_global', 'wind_speed', 'temp_air', 'model'] @@ -2060,7 +2060,7 @@ def _sapm_celltemp_translator(*args, **kwargs): def _pvsyst_celltemp_translator(*args, **kwargs): - #TODO: remove this function after deprecation period for pvsyst_celltemp + # TODO: remove this function after deprecation period for pvsyst_celltemp new_kwargs = {} # convert position arguments to kwargs old_arg_list = ['poa_global', 'temp_air', 'wind_speed', 'eta_m', From 919562a06bf7574c5c474e624ad381860826d13e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sun, 25 Aug 2019 09:51:53 -0600 Subject: [PATCH 72/98] fix tests --- pvlib/modelchain.py | 6 +++--- pvlib/pvsystem.py | 11 ++++------- pvlib/test/test_pvsystem.py | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 0ea8d0a8d2..9297aeda6d 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -347,9 +347,9 @@ def __init__(self, system, location, 'representing a glass/glass module in open racking.' 'In the future PVSystem.temperature_model_parameters' ' will be required', pvlibDeprecationWarning) - params = temperature._temperature_model_params('sapm') - self.system.temperature_model_parameters = params[ - 'open_rack_glass_glass'] + params = temperature._temperature_model_params( + 'sapm', 'open_rack_glass_glass') + self.system.temperature_model_parameters = params self.losses_model = losses_model self.orientation_strategy = orientation_strategy diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index e56041c592..42ff8ccd37 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2042,10 +2042,8 @@ def _sapm_celltemp_translator(*args, **kwargs): elif type(param_set) is dict: new_kwargs.update(param_set) else: # string - params = temperature._temperature_model_params('sapm') - new_kwargs.update({'a': params[param_set]['a'], - 'b': params[param_set]['b'], - 'deltaT': params[param_set]['deltaT']}) + params = temperature._temperature_model_params('sapm', param_set) + new_kwargs.update(params) new_kwargs.update(kwargs) # kwargs with unchanged names new_kwargs['irrad_ref'] = 1000 # default for new kwarg # convert old positional arguments to named kwargs @@ -2082,9 +2080,8 @@ def _pvsyst_celltemp_translator(*args, **kwargs): new_kwargs.update({'u_c': param_set[0], 'u_v': param_set[1]}) else: # string - params = temperature._temperature_model_params('pvsyst') - new_kwargs.update({'u_c': params[param_set]['u_c'], - 'u_v': params[param_set]['u_v']}) + params = temperature._temperature_model_params('pvsyst', param_set) + new_kwargs.update(params) new_kwargs.update(kwargs) # kwargs with unchanged names # convert old positional arguments to named kwargs return temperature.pvsyst_cell(**new_kwargs) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 942daebb32..707b852d87 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1579,7 +1579,7 @@ def test__pvsyst_celltemp_translator(): @fail_on_pvlib_version('0.8') -def test__sapm_celltemp_translator(sapm_default): +def test__sapm_celltemp_translator(): result = pvsystem._sapm_celltemp_translator(900, 5, 20, 'open_rack_glass_glass') assert_allclose(result, 43.509, 3) From e120bde278782adc5b3f75d3bc66b1da650fbc83 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sun, 25 Aug 2019 09:54:08 -0600 Subject: [PATCH 73/98] another test fix --- pvlib/test/test_pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 707b852d87..258382ba71 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1586,10 +1586,10 @@ def test__sapm_celltemp_translator(): result = pvsystem._sapm_celltemp_translator(900, 5, temp_air=20, model='open_rack_glass_glass') assert_allclose(result, 43.509, 3) - result = pvsystem._sapm_celltemp_translator(900, 5, 20, sapm_default) - assert_allclose(result, 43.509, 3) params = temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ 'open_rack_glass_glass'] + result = pvsystem._sapm_celltemp_translator(900, 5, 20, params) + assert_allclose(result, 43.509, 3) result = pvsystem._sapm_celltemp_translator(900, 5, 20, [params['a'], params['b'], params['deltaT']]) From b2b959bd0ff2a7cd8bb7fe15732125944dee9b50 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sun, 25 Aug 2019 17:25:00 -0600 Subject: [PATCH 74/98] pop unused kwarg --- pvlib/pvsystem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 42ff8ccd37..595134138c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2027,6 +2027,7 @@ def _sapm_celltemp_translator(*args, **kwargs): # determine value for new kwarg 'model' try: param_set = new_kwargs['model'] + new_kwargs.pop('model') # model is not a new kwarg except KeyError: # 'model' not in positional arguments, check kwargs try: @@ -2068,6 +2069,7 @@ def _pvsyst_celltemp_translator(*args, **kwargs): # determine value for new kwarg 'model' try: param_set = new_kwargs['model_params'] + new_kwargs.pop('model_params') # model_params is not a new kwarg except KeyError: # 'model_params' not in positional arguments, check kwargs try: From fd6a97d45f48a5a2a081207c6d46163195660bf3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 08:26:25 -0600 Subject: [PATCH 75/98] improve test coverage --- pvlib/modelchain.py | 38 +++++++++++++++++++++-------------- pvlib/test/test_modelchain.py | 28 +++++++++++++++++++++++++- pvlib/test/test_pvsystem.py | 3 +++ 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 9297aeda6d..3804e7412b 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -281,7 +281,7 @@ class ModelChain(object): 'first_solar', 'no_loss'. The ModelChain instance will be passed as the first argument to a user-defined function. - temperature_model: str or function, default 'sapm' + temperature_model: None, str or function, default None Valid strings are 'sapm' and 'pvsyst'. The ModelChain instance will be passed as the first argument to a user-defined function. @@ -333,24 +333,32 @@ def __init__(self, system, location, warnings.warn('Provide only one of temperature_model or ' 'temp_model (deprecated).') else: - raise ValueError('Conflicting values for temperature_model and' - ' temp_model (deprecated). Specify only ' - 'temperature_model.') + raise ValueError('Conflicting values for temperature_model {}' + ' and temp_model (deprecated) {}. Specify ' + 'only temperature_model.'.format( + temperature_model, temp_model)) self.temperature_model = temperature_model # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): warnings.warn('PVSystem temperature_model_parameters attribute ' - 'is not assigned. Reverting to deprecated default: ' - 'the SAPM cell temperature model with parameters ' - 'representing a glass/glass module in open racking.' - 'In the future PVSystem.temperature_model_parameters' - ' will be required', pvlibDeprecationWarning) - params = temperature._temperature_model_params( - 'sapm', 'open_rack_glass_glass') - self.system.temperature_model_parameters = params - + 'is not assigned.') + if self.temperature_model=='sapm': + warnings.warn( + 'Reverting to deprecated default SAPM cell temperature ' + 'model parameters representing a glass/glass module in ' + 'open racking. In the future ' + 'PVSystem.temperature_model_parameters will be required', + pvlibDeprecationWarning) + params = temperature._temperature_model_params( + 'sapm', 'open_rack_glass_glass') + self.system.temperature_model_parameters = params + elif self.temperature_model=='pvsyst': + raise ValueError( + 'ModelChain assigned Pvsyst temperature model but no ' + 'parameters provided. Assign temperature model parameters ' + 'to PVSystem.temperature_model_parameters') self.losses_model = losses_model self.orientation_strategy = orientation_strategy @@ -707,9 +715,9 @@ def temperature_model(self, model): self._temperature_model = self.infer_temperature_model() elif isinstance(model, str): model = model.lower() - if model == 'sapm': + if model=='sapm': self._temperature_model = self.sapm_temp - elif model == 'pvsyst': + elif model=='pvsyst': self._temperature_model = self.pvsyst_temp else: raise ValueError(model + ' is not a valid temperature model') diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 31b298a0b3..a2692da65a 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -561,11 +561,37 @@ def test_deprecated_08(): 'alpha_sc': 1, 'I_L_ref': 1, 'R_s': 1} # do not assign PVSystem.temperature_model_parameters system = PVSystem(module_parameters=module_parameters) + with pytest.warns(pvlibDeprecationWarning): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temp_model='sapm', + ac_model='snlinverter') with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='desoto', aoi_model='no_loss', spectral_model='no_loss', temperature_model='sapm', + temp_model='sapm', + ac_model='snlinverter') + with pytest.raises(ValueError): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temperature_model='pvsyst', + temp_model='sapm', + ac_model='snlinverter') + with pytest.warns(pvlibDeprecationWarning): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temperature_model='sapm', + ac_model='snlinverter') + with pytest.raises(ValueError): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temperature_model='pvsyst', ac_model='snlinverter') @@ -716,7 +742,7 @@ def test_ModelChain___repr__(system, location, strategy, strategy_str): ' ac_model: snlinverter', ' aoi_model: sapm_aoi_loss', ' spectral_model: sapm_spectral_loss', - ' temperature_model: sapm_temp', + ' temperature_model: None', ' losses_model: no_extra_losses' ]) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 258382ba71..ce7258bec0 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1559,6 +1559,9 @@ def test_deprecated_08(): def test__pvsyst_celltemp_translator(): result = pvsystem._pvsyst_celltemp_translator(900, 20, 5) assert_allclose(result, 45.137, 0.001) + result = pvsystem._pvsyst_celltemp_translator(900, 20, 5, 0.1, 0.9, + [29.0, 0.0]) + assert_allclose(result, 45.137, 0.001) result = pvsystem._pvsyst_celltemp_translator(poa_global=900, temp_air=20, wind_speed=5) assert_allclose(result, 45.137, 0.001) From 6a9d03f5b3a137a0c1d254f647c83a05af5f1c76 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 08:52:46 -0600 Subject: [PATCH 76/98] test fixes --- pvlib/test/test_modelchain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index a2692da65a..01cf6c7d6a 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -581,7 +581,7 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') - with pytest.warns(pvlibDeprecationWarning): + with pytest.warns(): ModelChain(system, location, dc_model='desoto', aoi_model='no_loss', spectral_model='no_loss', @@ -742,7 +742,7 @@ def test_ModelChain___repr__(system, location, strategy, strategy_str): ' ac_model: snlinverter', ' aoi_model: sapm_aoi_loss', ' spectral_model: sapm_spectral_loss', - ' temperature_model: None', + ' temperature_model: sapm_temp', ' losses_model: no_extra_losses' ]) From 04eb5a43814ea0a1c8110281bb1d783cb4a27ec2 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 09:27:11 -0600 Subject: [PATCH 77/98] add UserWarning --- pvlib/test/test_modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 01cf6c7d6a..41c9d11216 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -581,7 +581,7 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') - with pytest.warns(): + with pytest.warns(UserWarning): ModelChain(system, location, dc_model='desoto', aoi_model='no_loss', spectral_model='no_loss', From ddb84aea7d0312b86ea4d023634b730eed117c39 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 10:02:28 -0600 Subject: [PATCH 78/98] improve warnings --- pvlib/modelchain.py | 10 +++++----- pvlib/test/test_modelchain.py | 6 ------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 3804e7412b..da6a5542a0 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -342,10 +342,10 @@ def __init__(self, system, location, # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): - warnings.warn('PVSystem temperature_model_parameters attribute ' - 'is not assigned.') if self.temperature_model=='sapm': warnings.warn( + 'SAPM temperature model is specified but PVSystem ' + 'temperature_model_parameters attribute is not assigned.' 'Reverting to deprecated default SAPM cell temperature ' 'model parameters representing a glass/glass module in ' 'open racking. In the future ' @@ -356,9 +356,9 @@ def __init__(self, system, location, self.system.temperature_model_parameters = params elif self.temperature_model=='pvsyst': raise ValueError( - 'ModelChain assigned Pvsyst temperature model but no ' - 'parameters provided. Assign temperature model parameters ' - 'to PVSystem.temperature_model_parameters') + 'Pvsyst temperature model is specified but no temperature ' + 'model parameters are provided. Assign temperature model ' + 'parameters to PVSystem.temperature_model_parameters') self.losses_model = losses_model self.orientation_strategy = orientation_strategy diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 41c9d11216..0a4a1e52f5 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -581,12 +581,6 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') - with pytest.warns(UserWarning): - ModelChain(system, location, - dc_model='desoto', - aoi_model='no_loss', spectral_model='no_loss', - temperature_model='sapm', - ac_model='snlinverter') with pytest.raises(ValueError): ModelChain(system, location, dc_model='desoto', From 8b9ce93ee877139a0f69f380d0d43bebce1da653 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 14:50:41 -0600 Subject: [PATCH 79/98] use property setter --- pvlib/modelchain.py | 5 +++-- pvlib/test/test_modelchain.py | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index da6a5542a0..921a2090e3 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -342,7 +342,7 @@ def __init__(self, system, location, # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): - if self.temperature_model=='sapm': + if self.temperature_model==self.sapm_temp: warnings.warn( 'SAPM temperature model is specified but PVSystem ' 'temperature_model_parameters attribute is not assigned.' @@ -354,11 +354,12 @@ def __init__(self, system, location, params = temperature._temperature_model_params( 'sapm', 'open_rack_glass_glass') self.system.temperature_model_parameters = params - elif self.temperature_model=='pvsyst': + elif self.temperature_model==self.sapm_pvsyst: raise ValueError( 'Pvsyst temperature model is specified but no temperature ' 'model parameters are provided. Assign temperature model ' 'parameters to PVSystem.temperature_model_parameters') + self.losses_model = losses_model self.orientation_strategy = orientation_strategy diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 0a4a1e52f5..e3352598ac 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -581,6 +581,12 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') + with pytest.warns(pvlibDeprecationWarning): + ModelChain(system, location, + dc_model='desoto', + aoi_model='no_loss', spectral_model='no_loss', + temperature_model='sapm', + ac_model='snlinverter') with pytest.raises(ValueError): ModelChain(system, location, dc_model='desoto', From 0ba33aeedd3e950639cb29b6e4e7fde5193e7cde Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 15:30:42 -0600 Subject: [PATCH 80/98] test fixes --- pvlib/modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 921a2090e3..313e9b9e39 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -354,7 +354,7 @@ def __init__(self, system, location, params = temperature._temperature_model_params( 'sapm', 'open_rack_glass_glass') self.system.temperature_model_parameters = params - elif self.temperature_model==self.sapm_pvsyst: + elif self.temperature_model==self.pvsyst_temp: raise ValueError( 'Pvsyst temperature model is specified but no temperature ' 'model parameters are provided. Assign temperature model ' From 0aaa9fccc54d5bd64e9566171e1b646952791a9a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 15:34:11 -0600 Subject: [PATCH 81/98] yet another code fix trying to distinguish kwargs from attribute of same name --- pvlib/modelchain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 313e9b9e39..865803dda8 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -342,7 +342,7 @@ def __init__(self, system, location, # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): - if self.temperature_model==self.sapm_temp: + if temperature_model=='sapm': warnings.warn( 'SAPM temperature model is specified but PVSystem ' 'temperature_model_parameters attribute is not assigned.' @@ -354,7 +354,7 @@ def __init__(self, system, location, params = temperature._temperature_model_params( 'sapm', 'open_rack_glass_glass') self.system.temperature_model_parameters = params - elif self.temperature_model==self.pvsyst_temp: + elif temperature_model=='pvsyst': raise ValueError( 'Pvsyst temperature model is specified but no temperature ' 'model parameters are provided. Assign temperature model ' From 47538576ac2f5f3172d5c93a78ccec28d91fc4c5 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 26 Aug 2019 19:59:15 -0600 Subject: [PATCH 82/98] still struggling with tests --- pvlib/modelchain.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 865803dda8..18d9e5cf4e 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -322,23 +322,6 @@ def __init__(self, system, location, self.aoi_model = aoi_model self.spectral_model = spectral_model - # TODO: deprecated kwarg temp_model. Remove in v0.8 - temp_model = kwargs.pop('temp_model', None) - if temp_model is not None: - warnings.warn('The temp_model keyword argument is deprecated. Use ' - 'temperature_model instead', pvlibDeprecationWarning) - if temperature_model is None: - temperature_model = temp_model - elif temp_model == temperature_model: - warnings.warn('Provide only one of temperature_model or ' - 'temp_model (deprecated).') - else: - raise ValueError('Conflicting values for temperature_model {}' - ' and temp_model (deprecated) {}. Specify ' - 'only temperature_model.'.format( - temperature_model, temp_model)) - self.temperature_model = temperature_model - # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): @@ -360,6 +343,24 @@ def __init__(self, system, location, 'model parameters are provided. Assign temperature model ' 'parameters to PVSystem.temperature_model_parameters') + # TODO: deprecated kwarg temp_model. Remove in v0.8 + temp_model = kwargs.pop('temp_model', None) + if temp_model is not None: + warnings.warn('The temp_model keyword argument is deprecated. Use ' + 'temperature_model instead', pvlibDeprecationWarning) + if temperature_model is None: + temperature_model = temp_model + elif temp_model == temperature_model: + warnings.warn('Provide only one of temperature_model or ' + 'temp_model (deprecated).') + else: + raise ValueError('Conflicting values for temperature_model {}' + ' and temp_model (deprecated) {}. Specify ' + 'only temperature_model.'.format( + temperature_model, temp_model)) + self.temperature_model = temperature_model + + self.losses_model = losses_model self.orientation_strategy = orientation_strategy From fcd7a5d0bbc709f3ec9cb4eb664deebf550b6cc8 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 27 Aug 2019 10:46:34 -0600 Subject: [PATCH 83/98] reset system between ModelChain init --- pvlib/test/test_modelchain.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index e3352598ac..c1f9a63934 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -567,6 +567,7 @@ def test_deprecated_08(): aoi_model='no_loss', spectral_model='no_loss', temp_model='sapm', ac_model='snlinverter') + system = PVSystem(module_parameters=module_parameters) with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='desoto', @@ -574,6 +575,7 @@ def test_deprecated_08(): temperature_model='sapm', temp_model='sapm', ac_model='snlinverter') + system = PVSystem(module_parameters=module_parameters) with pytest.raises(ValueError): ModelChain(system, location, dc_model='desoto', @@ -581,12 +583,14 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') + system = PVSystem(module_parameters=module_parameters) with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='desoto', aoi_model='no_loss', spectral_model='no_loss', temperature_model='sapm', ac_model='snlinverter') + system = PVSystem(module_parameters=module_parameters) with pytest.raises(ValueError): ModelChain(system, location, dc_model='desoto', From 4be420e3777ae46970ab9de55eca14c7feb7c795 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 29 Aug 2019 15:26:39 -0600 Subject: [PATCH 84/98] infer parameters from racking, module_type --- docs/sphinx/source/whatsnew/v0.7.0.rst | 4 +--- pvlib/modelchain.py | 16 ++++++++-------- pvlib/pvsystem.py | 19 ++++++++++++++----- pvlib/test/test_pvsystem.py | 12 ++++++++++++ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index faa896f2af..0f8b7aa61d 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -13,8 +13,7 @@ compatibility notes. API Changes ~~~~~~~~~~~ * Changes to functions and methods for cell temperature models (:issue:`678`): - - Moved functions for cell temperature and constant - `TEMP_MODEL_PARAMS` from `pvsystem.py` to `temperature.py`. + - Moved functions for cell temperature from `pvsystem.py` to `temperature.py`. - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` to `temperature.sapm_cell` and `temperature.pvsyst_cell`. - `temperature.sapm_cell` returns only the cell temperature, whereas the old `pvsystem.sapm_celltemp` returned a `DataFrame` with both cell and module temperatures. @@ -33,7 +32,6 @@ API Changes - Parameter sets for the SAPM cell temperature model named 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. - - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than tuple for a parameter set. - Added attribute `PVSystem.module_type` (str) to record module front and back materials, default is `glass_polymer`. - Added attribute `PVSystem.temperature_model_parameters` (dict) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 18d9e5cf4e..15aed947ac 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -325,7 +325,7 @@ def __init__(self, system, location, # TODO: deprecated behavior if PVSystem.temperature_model_parameters # is not specified. Remove in v0.8 if not any(self.system.temperature_model_parameters): - if temperature_model=='sapm': + if temperature_model == 'sapm': warnings.warn( 'SAPM temperature model is specified but PVSystem ' 'temperature_model_parameters attribute is not assigned.' @@ -337,7 +337,7 @@ def __init__(self, system, location, params = temperature._temperature_model_params( 'sapm', 'open_rack_glass_glass') self.system.temperature_model_parameters = params - elif temperature_model=='pvsyst': + elif temperature_model == 'pvsyst': raise ValueError( 'Pvsyst temperature model is specified but no temperature ' 'model parameters are provided. Assign temperature model ' @@ -354,10 +354,10 @@ def __init__(self, system, location, warnings.warn('Provide only one of temperature_model or ' 'temp_model (deprecated).') else: - raise ValueError('Conflicting values for temperature_model {}' - ' and temp_model (deprecated) {}. Specify ' - 'only temperature_model.'.format( - temperature_model, temp_model)) + raise ValueError( + 'Conflicting temperature_model {} and temp_model {}. ' + 'temp_model is deprecated. Specify only temperature_model.' + .format(temperature_model, temp_model)) self.temperature_model = temperature_model @@ -717,9 +717,9 @@ def temperature_model(self, model): self._temperature_model = self.infer_temperature_model() elif isinstance(model, str): model = model.lower() - if model=='sapm': + if model == 'sapm': self._temperature_model = self.sapm_temp - elif model=='pvsyst': + elif model == 'pvsyst': self._temperature_model = self.pvsyst_temp else: raise ValueError(model + ' is not a valid temperature model') diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 595134138c..e0c9f823fb 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -126,8 +126,8 @@ class PVSystem(object): Inverter parameters as defined by the SAPM, CEC, or other. racking_model : None or string, default 'open_rack' - Valid strings are 'open_rack' and 'close_mount'. Used for cell and - module temperature calculations. + Valid strings are 'open_rack', 'close_mount', and 'insulated_back'. + Used to identify a parameter set for the SAPM cell temperature model. losses_parameters : None, dict or Series, default None Losses parameters as defined by PVWatts or other. @@ -174,9 +174,11 @@ def __init__(self, self.module_parameters = module_parameters self.module_type = module_type + self.racking_model = racking_model if temperature_model_parameters is None: - self.temperature_model_parameters = {} + self.temperature_model_parameters = \ + self._infer_temperature_model_params() else: self.temperature_model_parameters = temperature_model_parameters @@ -194,8 +196,6 @@ def __init__(self, else: self.losses_parameters = losses_parameters - self.racking_model = racking_model - self.name = name def __repr__(self): @@ -468,6 +468,15 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, return temperature.sapm_cell(poa_global, temp_air, wind_speed, **kwargs) + def _infer_temperature_model_params(self): + # try to infer temperature model parameters from from racking_model + # and module_type + param_set = self.racking_model + '_' + self.module_type + if param_set in temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']: + return temperature._temperature_model_params('sapm', param_set) + else: + return {} + def sapm_spectral_loss(self, airmass_absolute): """ Use the :py:func:`sapm_spectral_loss` function, the input diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index ce7258bec0..9a40a4cdb7 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -533,6 +533,18 @@ def test_PVSystem_pvsyst_celltemp_kwargs(mocker): assert (out < 90) and (out > 70) +def test__infer_temperature_model_params(): + system = pvsystem.PVSystem(module_parameters={}, + racking_model='open_rack', + module_type='glass_polymer') + expected = temperature.TEMPERATURE_MODEL_PARAMETERS[ + 'sapm']['open_rack_glass_polymer'] + assert expected == system._infer_temperature_model_params() + system = pvsystem.PVSystem(module_parameters={}, + racking_model='freestanding', + module_type='glass_polymer') + assert {} == system._infer_temperature_model_params() + def test_calcparams_desoto(cec_module_params): times = pd.date_range(start='2015-01-01', periods=3, freq='12H') effective_irradiance = pd.Series([0.0, 800.0, 800.0], index=times) From 7271b00cd89969cbfbc703138ee8a70d0d894716 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 29 Aug 2019 20:38:56 -0600 Subject: [PATCH 85/98] add the missing colon --- pvlib/test/test_modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 2fb4a19071..58e309cb45 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -93,7 +93,7 @@ def cec_dc_adr_ac_system(sam_data, cec_module_cs5p_220m, @pytest.fixture -def pvwatts_dc_snl_ac_system(cec_inverter_parameters) +def pvwatts_dc_snl_ac_system(cec_inverter_parameters): module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, From 17cfd2af0d88ecffea226adada206f69ef0f24fa Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 29 Aug 2019 20:50:37 -0600 Subject: [PATCH 86/98] delete vestigial lines --- pvlib/test/test_modelchain.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 58e309cb45..1de301ba06 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -609,8 +609,6 @@ def test_basic_chain_required(sam_data, cec_inverter_parameters, modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] temp_model_params = sapm_temperature_cs5p_220m.copy() - inverters = sam_data['cecinverter'] - inverter_parameters = inverters[ with pytest.raises(ValueError): dc, ac = modelchain.basic_chain( times, latitude, longitude, module_parameters, temp_model_params, From 5762abe44405d4ef8b9bcafb733dba530832a6d5 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 29 Aug 2019 21:12:04 -0600 Subject: [PATCH 87/98] test fixes --- pvlib/test/test_modelchain.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 1de301ba06..c67799efff 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -102,9 +102,9 @@ def pvwatts_dc_snl_ac_system(cec_inverter_parameters): @pytest.fixture -def pvwatts_dc_pvwatts_ac_system(sam_data): +def pvwatts_dc_pvwatts_ac_system(sapm_temperature_cs5p_220m): module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003} - temp_model_params = _get_sapm_temp_model_params(sam_data) + temp_model_params = sapm_temperature_cs5p_220m.copy() inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95} system = PVSystem(surface_tilt=32.2, surface_azimuth=180, module_parameters=module_parameters, @@ -628,10 +628,6 @@ def test_basic_chain_alt_az(sam_data, cec_inverter_parameters, modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] temp_model_params = sapm_temperature_cs5p_220m.copy() - 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, temp_model_params, cec_inverter_parameters, @@ -654,10 +650,6 @@ def test_basic_chain_strategy(sam_data, cec_inverter_parameters, modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] temp_model_params = sapm_temperature_cs5p_220m.copy() - 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, temp_model_params, cec_inverter_parameters, orientation_strategy='south_at_latitude_tilt', @@ -681,10 +673,6 @@ def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters, modules = sam_data['sandiamod'] module_parameters = modules['Canadian_Solar_CS5P_220M___2009_'] temp_model_params = sapm_temperature_cs5p_220m.copy() - 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, temp_model_params, cec_inverter_parameters, From 0c24bbb27a3387e8f14d1219161f34cec7d75e31 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 30 Aug 2019 08:45:12 -0600 Subject: [PATCH 88/98] fix tests, reformat whatsnew section --- docs/sphinx/source/whatsnew/v0.7.0.rst | 95 +++++++++++++++----------- pvlib/test/test_modelchain.py | 3 +- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index b50d5f9fe1..b578126a53 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -12,45 +12,62 @@ compatibility notes. API Changes ~~~~~~~~~~~ -* Changes to functions and methods for cell temperature models (:issue:`678`): - - Moved functions for cell temperature from `pvsystem.py` to `temperature.py`. - - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` - to `temperature.sapm_cell` and `temperature.pvsyst_cell`. - - `temperature.sapm_cell` returns only the cell temperature, whereas the old `pvsystem.sapm_celltemp` returned a `DataFrame` with both cell and module temperatures. - - Created `temperature.sapm_module` to return module temperature using the SAPM temperature model. - - Argument order for`pvsystem.sapm_celltemp`, - `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` has - been altered to be consistent among cell temperature model - functions. - - Removed `model` as a kwarg from `temperature.sapm_cell` and - `temperature.pvsyst_cell`. These functions now require model-specific parameters. - - Added the argument `irrad_ref`, default value 1000, to `temperature.sapm_cell`. - - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. - - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than - tuple for a parameter set. - - Parameter set names for `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. - - Parameter sets for the SAPM cell temperature model named - 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' - are considered obsolete and have been removed. - - Added attribute `PVSystem.module_type` (str) to record module - front and back materials, default is `glass_polymer`. - - Added attribute `PVSystem.temperature_model_parameters` (dict) - to contain temperature model parameters. - - Changed meaning of `PVSystem.racking_model` to describe racking - only, e.g., default is `open_rack`. - - In `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp`, - changed kwarg `model` to `parameter_set`. `parameter_set` expects - a str which is a valid key for - `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding - temperature model. - - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. - - `ModelChain.temps` attribute renamed to `ModelChain.cell_temperature`, and its datatype is now `numeric` rather than `DataFrame`. - - `ModelChain.temperature_model` now defaults to `None`. The temperature - model can be inferred from `PVSystem.temperature_model_parameters`. - - If `PVSystem.temperature_model_parameters` is not specified, `ModelChain` defaults to old behavior, using the SAPM temperautre model with parameters for a glass/glass model on open racking. This behavior is deprecated, and will be removed in v0.8. In v0.8 `PVSystem.temperature_model_parameters` will be required for `ModelChain`. - - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. - - `modelchain.basic_chain` has a new required argument - `temperature_model_parameters`. +* Changes related to cell temperature models (:issue:`678`): + * Changes to functions + - Moved functions for cell temperature from `pvsystem.py` to `temperature.py`. + - Renamed `pvsystem.sapm_celltemp` and `pvsystem.pvsyst_celltemp` + to `temperature.sapm_cell` and `temperature.pvsyst_cell`. + - `temperature.sapm_cell` returns only the cell temperature, whereas the + old `pvsystem.sapm_celltemp` returned a `DataFrame` with both cell and module temperatures. + - Created `temperature.sapm_module` to return module temperature using the SAPM temperature model. + - Changed the order of arguments for`pvsystem.sapm_celltemp`, + `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` to be consistent + among cell temperature model functions. + - Removed `model` as a kwarg from `temperature.sapm_cell` and + `temperature.pvsyst_cell`. These functions now require model-specific parameters. + - Added the argument `irrad_ref`, default value 1000, to `temperature.sapm_cell`. + * Changes to named temperature model parameter sets + - Renamed `pvsystem.TEMP_MODEL_PARAMS` to `temperature.TEMPERATURE_MODEL_PARAMETERS`. + - `temperature.TEMPERATURE_MODEL_PARAMETERS` uses dict rather than + tuple for a parameter set. + - Names for parameter sets in `temperature.TEMPERATURE_MODEL_PARAMETERS` have changed. + - Parameter sets for the SAPM cell temperature model named + 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' + are considered obsolete and have been removed. + * Changes to `PVSystem` class + - Changed the `model` kwarg in `PVSystem.sapm_celltemp` and + `PVSystem.pvsyst_celltemp` to `parameter_set`. `parameter_set` expects + a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` + for the corresponding temperature model. + - Added an attribute `PVSystem.module_type` (str) to record module + front and back materials, default is `glass_polymer`. + - Changed meaning of `PVSystem.racking_model` to describe racking + only, e.g., default is `open_rack`. + - Added an attribute `PVSystem.temperature_model_parameters` (dict). + to contain temperature model parameters. + - If `PVSystem.temperature_model_parameters` is not specified and + `PVSystem.racking_model` and `PVSystem.module_type` combine to a valid + parameter set name for the SAPM cell temperature model, that parameter + set is assigned to `PVSystem.temperature_model_parameters`. Otherwise + `PVSystem.temperature_model_parameters` is assigned an empty dict. The + result is that the default parameter set for SAPM cell temperature model + is `open_rack_glass_polymer`; the old default was `open_rack_glass_glass`. + * Changes to `ModelChain` class + - `ModelChain.temp_model` renamed to `ModelChain.temperature_model`. + - `ModelChain.temperature_model` now defaults to `None`. The temperature + model can be inferred from `PVSystem.temperature_model_parameters`. + - `ModelChain.temperature_model_parameters` now defaults to `None`. The temperature + model can be inferred from `PVSystem.temperature_model_parameters`. + - `ModelChain.temps` attribute renamed to `ModelChain.cell_temperature`, + and its datatype is now `numeric` rather than `DataFrame`. + - If `PVSystem.temperature_model_parameters` is not specified, `ModelChain` + defaults to old behavior, using the SAPM temperature model with parameter + set `open_rack_glass_glass`. This behavior is deprecated, and will be + removed in v0.8. In v0.8 `PVSystem.temperature_model_parameters` will + be required for `ModelChain`. + - Implemented `pvsyst` as an option for `ModelChain.temperature_model`. + - `modelchain.basic_chain` has a new required argument + `temperature_model_parameters`. * Calling :py:func:`pvlib.pvsystem.retrieve_sam` with no parameters will raise an exception instead of displaying a dialog. diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index c67799efff..7cdcfac0d7 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -565,7 +565,8 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') - system = PVSystem(module_parameters=module_parameters) + system = PVSystem(module_parameters=module_parameters, + self.racking_model='close_mount') with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='desoto', From ae64e04883bd36d23ce64a9cd4ad28a84b6fb5cd Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 30 Aug 2019 08:52:24 -0600 Subject: [PATCH 89/98] test fix --- pvlib/test/test_modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 7cdcfac0d7..f4b2eb54cc 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -566,7 +566,7 @@ def test_deprecated_08(): temp_model='sapm', ac_model='snlinverter') system = PVSystem(module_parameters=module_parameters, - self.racking_model='close_mount') + racking_model='close_mount') with pytest.warns(pvlibDeprecationWarning): ModelChain(system, location, dc_model='desoto', From 7226983453bf8ba1f7bfd3e703a93d60e39b2589 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 30 Aug 2019 14:12:35 -0600 Subject: [PATCH 90/98] better handling of deprecated behavior, add consistency check temperature_model vs. temperature_model_parameters --- pvlib/modelchain.py | 31 ++++++++----------------------- pvlib/pvsystem.py | 24 ++++++++++++++++++++++++ pvlib/test/test_modelchain.py | 23 ++++++++--------------- pvlib/test/test_pvsystem.py | 12 ++++++++++++ 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 15aed947ac..9d02fcc0c9 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -322,28 +322,7 @@ def __init__(self, system, location, self.aoi_model = aoi_model self.spectral_model = spectral_model - # TODO: deprecated behavior if PVSystem.temperature_model_parameters - # is not specified. Remove in v0.8 - if not any(self.system.temperature_model_parameters): - if temperature_model == 'sapm': - warnings.warn( - 'SAPM temperature model is specified but PVSystem ' - 'temperature_model_parameters attribute is not assigned.' - 'Reverting to deprecated default SAPM cell temperature ' - 'model parameters representing a glass/glass module in ' - 'open racking. In the future ' - 'PVSystem.temperature_model_parameters will be required', - pvlibDeprecationWarning) - params = temperature._temperature_model_params( - 'sapm', 'open_rack_glass_glass') - self.system.temperature_model_parameters = params - elif temperature_model == 'pvsyst': - raise ValueError( - 'Pvsyst temperature model is specified but no temperature ' - 'model parameters are provided. Assign temperature model ' - 'parameters to PVSystem.temperature_model_parameters') - - # TODO: deprecated kwarg temp_model. Remove in v0.8 + # TODO: deprecated kwarg temp_model. Remove use of temp_model in v0.8 temp_model = kwargs.pop('temp_model', None) if temp_model is not None: warnings.warn('The temp_model keyword argument is deprecated. Use ' @@ -360,7 +339,6 @@ def __init__(self, system, location, .format(temperature_model, temp_model)) self.temperature_model = temperature_model - self.losses_model = losses_model self.orientation_strategy = orientation_strategy @@ -723,6 +701,13 @@ def temperature_model(self, model): self._temperature_model = self.pvsyst_temp else: raise ValueError(model + ' is not a valid temperature model') + # check system.temperature_model_parameters for consistency + if self._temperature_model != self.infer_temperature_model(): + raise ValueError( + 'Temperature model {} is inconsistent with ' + 'PVsystem.temperature_model_parameters {}'.format( + self._temperature_model, + self.system.temperature_model_parameters)) else: self._temperature_model = partial(model, self) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 9612e6771a..26aa9607df 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -7,6 +7,7 @@ import io import os from urllib.request import urlopen +import warnings import numpy as np import pandas as pd @@ -17,6 +18,7 @@ from pvlib import tools from pvlib.tools import _build_kwargs, cosd from pvlib.location import Location +from pvlib._deprecation import pvlibDeprecationWarning # a dict of required parameter names for each DC power model @@ -179,9 +181,25 @@ def __init__(self, if temperature_model_parameters is None: self.temperature_model_parameters = \ self._infer_temperature_model_params() + # TODO: in v0.8 check for empty dict and raise error else: self.temperature_model_parameters = temperature_model_parameters + # TODO: deprecated behavior if PVSystem.temperature_model_parameters + # are not specified. Remove in v0.8 + if not any(self.temperature_model_parameters): + warnings.warn( + 'Required temperature_model_parameters is not specified ' + 'and parameters are not inferred from racking_model and ' + 'module_type. Reverting to deprecated default: SAPM cell ' + 'temperature model parameters for a glass/glass module in ' + 'open racking. In the future ' + 'PVSystem.temperature_model_parameters will be required', + pvlibDeprecationWarning) + params = temperature._temperature_model_params( + 'sapm', 'open_rack_glass_glass') + self.temperature_model_parameters = params + self.modules_per_string = modules_per_string self.strings_per_inverter = strings_per_inverter @@ -474,6 +492,12 @@ def _infer_temperature_model_params(self): param_set = self.racking_model + '_' + self.module_type if param_set in temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']: return temperature._temperature_model_params('sapm', param_set) + elif 'freestanding' in param_set: + return temperature._temperature_model_params('pvsyst', + 'freestanding') + elif 'insulated' in param_set: # after SAPM to avoid confusing keys + return temperature._temperature_model_params('pvsyst', + 'insulated') else: return {} diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index f4b2eb54cc..0e7811f61c 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -318,6 +318,14 @@ def test_infer_temp_model_invalid(location, system): aoi_model='physical', spectral_model='no_loss') +@requires_scipy +def test_temperature_model_inconsistent(location, system): + with pytest.raises(ValueError): + ModelChain(system, location, orientation_strategy='None', + aoi_model='physical', spectral_model='no_loss', + temperature_model='pvsyst') + + def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather, mocker): m = mocker.spy(sys.modules[__name__], 'poadc') @@ -565,21 +573,6 @@ def test_deprecated_08(): temperature_model='pvsyst', temp_model='sapm', ac_model='snlinverter') - system = PVSystem(module_parameters=module_parameters, - racking_model='close_mount') - with pytest.warns(pvlibDeprecationWarning): - ModelChain(system, location, - dc_model='desoto', - aoi_model='no_loss', spectral_model='no_loss', - temperature_model='sapm', - ac_model='snlinverter') - system = PVSystem(module_parameters=module_parameters) - with pytest.raises(ValueError): - ModelChain(system, location, - dc_model='desoto', - aoi_model='no_loss', spectral_model='no_loss', - temperature_model='pvsyst', - ac_model='snlinverter') @requires_tables diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 28753a6719..f014672d25 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -576,11 +576,18 @@ def test__infer_temperature_model_params(): expected = temperature.TEMPERATURE_MODEL_PARAMETERS[ 'sapm']['open_rack_glass_polymer'] assert expected == system._infer_temperature_model_params() + expected = temperature.TEMPERATURE_MODEL_PARAMETERS[ + 'pvsyst']['freestanding'] system = pvsystem.PVSystem(module_parameters={}, racking_model='freestanding', module_type='glass_polymer') + assert expected == system._infer_temperature_model_params() + system = pvsystem.PVSystem(module_parameters={}, + racking_model='not_a_rack_model', + module_type='glass_polymer') assert {} == system._infer_temperature_model_params() + def test_calcparams_desoto(cec_module_params): times = pd.date_range(start='2015-01-01', periods=3, freq='12H') effective_irradiance = pd.Series([0.0, 800.0, 800.0], index=times) @@ -1614,6 +1621,11 @@ def test_deprecated_08(): pvsystem.sapm_celltemp(1000, 25, 1) with pytest.warns(pvlibDeprecationWarning): pvsystem.pvsyst_celltemp(1000, 25) + module_parameters = {'R_sh_ref': 1, 'a_ref': 1, 'I_o_ref': 1, + 'alpha_sc': 1, 'I_L_ref': 1, 'R_s': 1} + with pytest.warns(pvlibDeprecationWarning): + pvsystem.PVSystem(module_parameters=module_parameters, + racking_model='open', module_type='glass_glass') @fail_on_pvlib_version('0.8') From 8aa45302d111549bcd5cbd190b9cc2b85dc92c3d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 30 Aug 2019 16:31:25 -0600 Subject: [PATCH 91/98] fix method check --- pvlib/modelchain.py | 5 +++-- pvlib/pvsystem.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 9d02fcc0c9..c8c46a6140 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -702,11 +702,12 @@ def temperature_model(self, model): else: raise ValueError(model + ' is not a valid temperature model') # check system.temperature_model_parameters for consistency - if self._temperature_model != self.infer_temperature_model(): + name_from_params = self.infer_temperature_model().__name__ + if self._temperature_model__name__ != name_from_params: raise ValueError( 'Temperature model {} is inconsistent with ' 'PVsystem.temperature_model_parameters {}'.format( - self._temperature_model, + self._temperature_model__name__, self.system.temperature_model_parameters)) else: self._temperature_model = partial(model, self) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 26aa9607df..43cc69e973 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -181,7 +181,7 @@ def __init__(self, if temperature_model_parameters is None: self.temperature_model_parameters = \ self._infer_temperature_model_params() - # TODO: in v0.8 check for empty dict and raise error +# TODO: in v0.8 check for empty dict and raise error else: self.temperature_model_parameters = temperature_model_parameters From 07c5da7755c00f0fc62055593084037b9200fac7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 30 Aug 2019 16:55:55 -0600 Subject: [PATCH 92/98] missing . --- pvlib/modelchain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index c8c46a6140..3244dc2977 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -703,11 +703,11 @@ def temperature_model(self, model): raise ValueError(model + ' is not a valid temperature model') # check system.temperature_model_parameters for consistency name_from_params = self.infer_temperature_model().__name__ - if self._temperature_model__name__ != name_from_params: + if self._temperature_model.__name__ != name_from_params: raise ValueError( 'Temperature model {} is inconsistent with ' 'PVsystem.temperature_model_parameters {}'.format( - self._temperature_model__name__, + self._temperature_model.__name__, self.system.temperature_model_parameters)) else: self._temperature_model = partial(model, self) From abd9c7adc57374a3a6ccfd9cc8b599343a834408 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 31 Aug 2019 11:35:09 -0600 Subject: [PATCH 93/98] update tests --- pvlib/test/test_modelchain.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 0e7811f61c..67a886a7d1 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -3,7 +3,7 @@ import numpy as np import pandas as pd -from pvlib import modelchain, pvsystem +from pvlib import modelchain, pvsystem, temperature from pvlib.modelchain import ModelChain from pvlib.pvsystem import PVSystem from pvlib.tracking import SingleAxisTracker @@ -198,7 +198,8 @@ def test_run_model_with_weather(system, location, weather, mocker): weather['wind_speed'] = 5 weather['temp_air'] = 10 # test with sapm cell temperature model - system.racking_model = 'open_rack_glass_glass' + system.racking_model = 'open_rack' + system.module_type = 'glass_glass' mc = ModelChain(system, location) mc.temperature_model = 'sapm' m_sapm = mocker.spy(system, 'sapm_celltemp') @@ -211,6 +212,8 @@ def test_run_model_with_weather(system, location, weather, mocker): assert not mc.ac.empty # test with pvsyst cell temperature model system.racking_model = 'freestanding' + system.temperature_model_parameters = \ + temperature._temperature_model_params('pvsyst', 'freestanding') mc = ModelChain(system, location) mc.temperature_model = 'pvsyst' m_pvsyst = mocker.spy(system, 'pvsyst_celltemp') @@ -269,7 +272,11 @@ def test_infer_dc_model(system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, 'pvsyst': 'pvsyst', 'singlediode': 'sapm', 'pvwatts_dc': 'sapm'} + temp_model_params = {'sapm': {'a': -3.40641, 'b': -0.0842075, 'deltaT': 3}, + 'pvsyst': {'u_c': 29.0, 'u_v': 0}} system = dc_systems[dc_model] + system.temperature_model_parameters = temp_model_params[ + temp_model_function[dc_model]] # remove Adjust from model parameters for desoto, singlediode if dc_model in ['desoto', 'singlediode']: system.module_parameters.pop('Adjust') From d59b016e7bd8375eec98117a64b9255ab56a1d31 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 31 Aug 2019 13:32:27 -0600 Subject: [PATCH 94/98] remove duplicate test value, clarify TODO comment --- pvlib/pvsystem.py | 2 +- pvlib/test/test_modelchain.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 43cc69e973..38a357f4fe 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -181,7 +181,7 @@ def __init__(self, if temperature_model_parameters is None: self.temperature_model_parameters = \ self._infer_temperature_model_params() -# TODO: in v0.8 check for empty dict and raise error +# TODO: in v0.8 check if an empty dict is returned and raise error else: self.temperature_model_parameters = temperature_model_parameters diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 67a886a7d1..20a8a956bc 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -514,7 +514,7 @@ def test_invalid_dc_model_params(system, cec_dc_snl_ac_system, @pytest.mark.parametrize('model', [ - 'dc_model', 'ac_model', 'aoi_model', 'spectral_model', 'losses_model', + 'dc_model', 'ac_model', 'aoi_model', 'spectral_model', 'temperature_model', 'losses_model' ]) def test_invalid_models(model, system, location): From 34cedafbff632b6b34f68ee1f370078aef97d054 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 5 Sep 2019 10:04:26 -0600 Subject: [PATCH 95/98] consistent import of tools --- pvlib/pvsystem.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 38a357f4fe..89e9212b61 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -15,8 +15,7 @@ from pvlib import (atmosphere, irradiance, singlediode as _singlediode, temperature) -from pvlib import tools -from pvlib.tools import _build_kwargs, cosd +from pvlib.tools import _build_kwargs, cosd, asind, sind, tand from pvlib.location import Location from pvlib._deprecation import pvlibDeprecationWarning @@ -181,7 +180,7 @@ def __init__(self, if temperature_model_parameters is None: self.temperature_model_parameters = \ self._infer_temperature_model_params() -# TODO: in v0.8 check if an empty dict is returned and raise error + # TODO: in v0.8 check if an empty dict is returned and raise error else: self.temperature_model_parameters = temperature_model_parameters @@ -459,13 +458,13 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, Parameters ---------- - poa_global : numeric or Series + poa_global : numeric Total incident irradiance in W/m^2. - temp_air : numeric or Series + temp_air : numeric Ambient dry bulb temperature in degrees C. - wind_speed : numeric or Series + wind_speed : numeric Wind speed in m/s at a height of 10 meters. parameter_set : string, default None @@ -475,7 +474,7 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, Returns ------- - numeric or Series, values in degrees C. + numeric, values in degrees C. """ if parameter_set is not None: kwargs = temperature._temperature_model_params('sapm', @@ -576,13 +575,13 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, Parameters ---------- - poa_global : numeric or Series + poa_global : numeric Total incident irradiance in W/m^2. - temp_air : numeric or Series + temp_air : numeric Ambient dry bulb temperature in degrees C. - wind_speed : numeric or Series, default 1.0 + wind_speed : numeric, default 1.0 Wind speed in m/s measured at the same height for which the wind loss factor was determined. The default value is 1.0, which is the wind speed at module height used to determine NOCT. @@ -601,7 +600,7 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, Returns ------- - numeric or Series, values in degrees C. + numeric, values in degrees C. """ kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) @@ -1102,20 +1101,20 @@ def physicaliam(aoi, n=1.526, K=4., L=0.002): aoi = np.where(aoi == 0, zeroang, aoi) # angle of reflection - thetar_deg = tools.asind(1.0 / n*(tools.sind(aoi))) + thetar_deg = asind(1.0 / n * (sind(aoi))) # reflectance and transmittance for normal incidence light rho_zero = ((1-n) / (1+n)) ** 2 tau_zero = np.exp(-K*L) # reflectance for parallel and perpendicular polarized light - rho_para = (tools.tand(thetar_deg - aoi) / - tools.tand(thetar_deg + aoi)) ** 2 - rho_perp = (tools.sind(thetar_deg - aoi) / - tools.sind(thetar_deg + aoi)) ** 2 + rho_para = (tand(thetar_deg - aoi) / + tand(thetar_deg + aoi)) ** 2 + rho_perp = (sind(thetar_deg - aoi) / + sind(thetar_deg + aoi)) ** 2 # transmittance for non-normal light - tau = np.exp(-K*L / tools.cosd(thetar_deg)) + tau = np.exp(-K * L / cosd(thetar_deg)) # iam is ratio of non-normal to normal incidence transmitted light # after deducting the reflected portion of each From 4aaf3180c83ab33e3d75aa33c2e69f36f2220197 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 5 Sep 2019 10:05:17 -0600 Subject: [PATCH 96/98] insert blank line --- docs/sphinx/source/whatsnew/v0.7.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index b578126a53..9dfd58ecfd 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -99,6 +99,7 @@ Testing Documentation ~~~~~~~~~~~~~ * Corrected docstring for `pvsystem.PVSystem.sapm` + Removal of prior version deprecations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Removed `irradiance.extraradiation`. From f2e0de061fb07b1088f9663a9c3c935d95c3d5ae Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 5 Sep 2019 10:35:12 -0600 Subject: [PATCH 97/98] remove parameter_set arg from PVSystem methods --- pvlib/pvsystem.py | 32 ++++++-------------------------- pvlib/test/test_pvsystem.py | 11 +++++------ 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 89e9212b61..f395aa139f 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -451,8 +451,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): """ return sapm(effective_irradiance, temp_cell, self.module_parameters) - def sapm_celltemp(self, poa_global, temp_air, wind_speed, - parameter_set=None): + def sapm_celltemp(self, poa_global, temp_air, wind_speed): """Uses :py:func:`temperature.sapm_cell` to calculate cell temperatures. @@ -467,21 +466,12 @@ def sapm_celltemp(self, poa_global, temp_air, wind_speed, wind_speed : numeric Wind speed in m/s at a height of 10 meters. - parameter_set : string, default None - Heat loss model parameters to be used. - See temperature.TEMPERATURE_MODEL_PARAMETERS for available - parameter sets. - Returns ------- numeric, values in degrees C. """ - if parameter_set is not None: - kwargs = temperature._temperature_model_params('sapm', - parameter_set) - else: - kwargs = _build_kwargs(['a', 'b', 'deltaT'], - self.temperature_model_parameters) + kwargs = _build_kwargs(['a', 'b', 'deltaT'], + self.temperature_model_parameters) return temperature.sapm_cell(poa_global, temp_air, wind_speed, **kwargs) @@ -568,8 +558,7 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) - def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, - parameter_set=None): + def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0): """Uses :py:func:`temperature.pvsyst_cell` to calculate cell temperature. @@ -593,23 +582,14 @@ def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0, alpha_absorption : numeric, default 0.9 Absorption coefficient - parameter_set : string, default None - Heat loss model parameters to be used. - See temperature.TEMPERATURE_MODEL_PARAMETERS for available - parameter sets. - Returns ------- numeric, values in degrees C. """ kwargs = _build_kwargs(['eta_m', 'alpha_absorption'], self.module_parameters) - if parameter_set is not None: - kwargs.update(temperature._temperature_model_params('pvsyst', - parameter_set)) - else: - kwargs.update(_build_kwargs(['u_c', 'u_v'], - self.temperature_model_parameters)) + kwargs.update(_build_kwargs(['u_c', 'u_v'], + self.temperature_model_parameters)) return temperature.pvsyst_cell(poa_global, temp_air, wind_speed, **kwargs) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index f014672d25..41589c23b6 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -516,13 +516,12 @@ def test_PVSystem_sapm_celltemp(mocker): def test_PVSystem_sapm_celltemp_kwargs(mocker): temp_model_params = temperature.TEMPERATURE_MODEL_PARAMETERS['sapm'][ 'open_rack_glass_glass'] - system = pvsystem.PVSystem() + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) mocker.spy(temperature, 'sapm_cell') temps = 25 irrads = 1000 winds = 1 - out = system.sapm_celltemp(irrads, temps, winds, - parameter_set='open_rack_glass_glass') + out = system.sapm_celltemp(irrads, temps, winds) temperature.sapm_cell.assert_called_once_with(irrads, temps, winds, temp_model_params['a'], temp_model_params['b'], @@ -556,13 +555,13 @@ def test_PVSystem_pvsyst_celltemp_kwargs(mocker): alpha_absorption = 0.85 eta_m = 0.17 module_parameters = {'alpha_absorption': alpha_absorption, 'eta_m': eta_m} - system = pvsystem.PVSystem(module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) mocker.spy(temperature, 'pvsyst_cell') irrad = 800 temp = 45 wind = 0.5 - out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind, - parameter_set='insulated') + out = system.pvsyst_celltemp(irrad, temp, wind_speed=wind) temperature.pvsyst_cell.assert_called_once_with( irrad, temp, wind, temp_model_params['u_c'], temp_model_params['u_v'], eta_m, alpha_absorption) From 6a138305be165bb9214aa51cdb305b7c0073790d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 5 Sep 2019 10:37:28 -0600 Subject: [PATCH 98/98] lint --- pvlib/pvsystem.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f395aa139f..5a00907b18 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1088,10 +1088,8 @@ def physicaliam(aoi, n=1.526, K=4., L=0.002): tau_zero = np.exp(-K*L) # reflectance for parallel and perpendicular polarized light - rho_para = (tand(thetar_deg - aoi) / - tand(thetar_deg + aoi)) ** 2 - rho_perp = (sind(thetar_deg - aoi) / - sind(thetar_deg + aoi)) ** 2 + rho_para = (tand(thetar_deg - aoi) / tand(thetar_deg + aoi)) ** 2 + rho_perp = (sind(thetar_deg - aoi) / sind(thetar_deg + aoi)) ** 2 # transmittance for non-normal light tau = np.exp(-K * L / cosd(thetar_deg))