diff --git a/docs/sphinx/source/contributing.rst b/docs/sphinx/source/contributing.rst index d60d4fcaeb..10d5867529 100644 --- a/docs/sphinx/source/contributing.rst +++ b/docs/sphinx/source/contributing.rst @@ -421,7 +421,9 @@ reasonable. # set up inputs module_parameters = {'b': 0.05} - system = pvsystem.PVSystem(module_parameters=module_parameters) + temp_model_params = {'u_c': 29.0, 'u_v': 0} + system = pvsystem.PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) thetas = 1 # call the method @@ -459,8 +461,10 @@ PVSystem method is called through ``ModelChain.run_model``. def test_modelchain_dc_model(mocker): # set up location and system for model chain location = location.Location(32, -111) + temp_model_params = {'u_c': 29.0, 'u_v': 0} system = pvsystem.PVSystem(module_parameters=some_sandia_mod_params, - inverter_parameters=some_cecinverter_params) + inverter_parameters=some_cecinverter_params, + temperature_model_parameters=temp_model_params) # mocker.spy adds code to the system.sapm method to keep track of how # it is called. use returned mock object m to make assertion later, diff --git a/docs/sphinx/source/user_guide/pvsystem.rst b/docs/sphinx/source/user_guide/pvsystem.rst index c3d666fbfe..638dba0b8d 100644 --- a/docs/sphinx/source/user_guide/pvsystem.rst +++ b/docs/sphinx/source/user_guide/pvsystem.rst @@ -62,10 +62,15 @@ that describe a PV system's modules and inverter are stored in .. ipython:: python + from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS + + temp_params = TEMPERATURE_MODEL_PARAMETERS["pvsyst"]["freestanding"] + module_parameters = {'pdc0': 5000, 'gamma_pdc': -0.004} inverter_parameters = {'pdc0': 5000, 'eta_inv_nom': 0.96} system = pvsystem.PVSystem(inverter_parameters=inverter_parameters, - module_parameters=module_parameters) + module_parameters=module_parameters, + temperature_model_parameters = temp_params) print(system.inverter_parameters) @@ -125,7 +130,8 @@ passed to `PVSystem.module_parameters`: module_parameters = {'pdc0': 5000, 'gamma_pdc': -0.004} inverter_parameters = {'pdc0': 5000, 'eta_inv_nom': 0.96} system = pvsystem.PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + inverter_parameters=inverter_parameters, + temperature_model_parameters=temp_params) print(system.arrays[0].module_parameters) print(system.inverter_parameters) @@ -140,16 +146,16 @@ provided for each array, and the arrays are provided to .. ipython:: python module_parameters = {'pdc0': 5000, 'gamma_pdc': -0.004} - mount = pvsystem.FixedMount(surface_tilt=20, surface_azimuth=180) - array_one = pvsystem.Array(mount=mount, module_parameters=module_parameters) - array_two = pvsystem.Array(mount=mount, module_parameters=module_parameters) + mount = pvsystem.FixedMount(surface_tilt=20, surface_azimuth=180, racking_model='close_mount') + array_one = pvsystem.Array(mount=mount, module_parameters=module_parameters, module_type='glass_glass') + array_two = pvsystem.Array(mount=mount, module_parameters=module_parameters, module_type='glass_glass') system_two_arrays = pvsystem.PVSystem(arrays=[array_one, array_two], inverter_parameters=inverter_parameters) print([array.module_parameters for array in system_two_arrays.arrays]) print(system_two_arrays.inverter_parameters) -The :py:class:`~pvlib.pvsystem.Array` class includes those +The :py:class:`~pvlib.pvsystem.Array` class includes those :py:class:`~pvlib.pvsystem.PVSystem` attributes that may vary from array to array. These attributes include `module_parameters`, `temperature_model_parameters`, `modules_per_string`, @@ -171,7 +177,7 @@ PVSystem attributes ------------------- Here we review the most commonly used PVSystem and Array attributes. -Please see the :py:class:`~pvlib.pvsystem.PVSystem` and +Please see the :py:class:`~pvlib.pvsystem.PVSystem` and :py:class:`~pvlib.pvsystem.Array` class documentation for a comprehensive list of attributes. @@ -182,14 +188,27 @@ Tilt and azimuth The first parameters which describe the DC part of a PV system are the tilt and azimuth of the modules. In the case of a PV system with a single array, these parameters can be specified using the `PVSystem.surface_tilt` and -`PVSystem.surface_azimuth` attributes. This will automatically create +`PVSystem.surface_azimuth` attributes. + +Temperature model parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The `PVSystem.temperature_model_parameters` attribute must be specified +for further downstream modeling with `ModelChain`. +In lieu of this attribute, the `PVSystem.racking_model` and +`PVSystem.module_type` can be specified, in which case the temperature model +parameters will be inferred. + +This will automatically create an :py:class:`~pvlib.pvsystem.Array` with a :py:class:`~pvlib.pvsystem.FixedMount` -at the specified tilt and azimuth: +at the specified tilt and azimuth and temperature model parameters: .. ipython:: python # single south-facing array at 20 deg tilt - system_one_array = pvsystem.PVSystem(surface_tilt=20, surface_azimuth=180) + system_one_array = pvsystem.PVSystem(surface_tilt=20, + surface_azimuth=180, + temperature_model_parameters=temp_params) print(system_one_array.arrays[0].mount) @@ -199,9 +218,11 @@ for each array by passing a different :py:class:`~pvlib.pvsystem.FixedMount` .. ipython:: python - array_one = pvsystem.Array(pvsystem.FixedMount(surface_tilt=30, surface_azimuth=90)) + mount_one = pvsystem.FixedMount(surface_tilt=30, surface_azimuth=90, racking_model='close_mount') + array_one = pvsystem.Array(mount=mount_one, module_type='glass_glass') print(array_one.mount.surface_tilt, array_one.mount.surface_azimuth) - array_two = pvsystem.Array(pvsystem.FixedMount(surface_tilt=30, surface_azimuth=220)) + mount_two = pvsystem.FixedMount(surface_tilt=30, surface_azimuth=220, racking_model='close_mount') + array_two = pvsystem.Array(mount=mount_two, module_type='glass_glass') system = pvsystem.PVSystem(arrays=[array_one, array_two]) system.num_arrays for array in system.arrays: @@ -220,7 +241,7 @@ and `solar_azimuth` as arguments. .. ipython:: python # single south-facing array at 20 deg tilt - system_one_array = pvsystem.PVSystem(surface_tilt=20, surface_azimuth=180) + system_one_array = pvsystem.PVSystem(surface_tilt=20, surface_azimuth=180, temperature_model_parameters=temp_params) print(system_one_array.arrays[0].mount) # call get_aoi with solar_zenith, solar_azimuth @@ -234,7 +255,9 @@ operates in a similar manner. .. ipython:: python # two arrays each at 30 deg tilt with different facing - array_one = pvsystem.Array(pvsystem.FixedMount(surface_tilt=30, surface_azimuth=90)) + mount = pvsystem.FixedMount(surface_tilt=30, surface_azimuth=90) + array_one = pvsystem.Array(mount=mount, + temperature_model_parameters=temp_params) array_one_aoi = array_one.get_aoi(solar_zenith=30, solar_azimuth=180) print(array_one_aoi) @@ -245,7 +268,9 @@ operates on all `Array` instances in the `PVSystem`, whereas the the .. ipython:: python - array_two = pvsystem.Array(pvsystem.FixedMount(surface_tilt=30, surface_azimuth=220)) + mount = pvsystem.FixedMount(surface_tilt=30, surface_azimuth=220) + array_two = pvsystem.Array(mount=mount, + temperature_model_parameters=temp_params) system_multiarray = pvsystem.PVSystem(arrays=[array_one, array_two]) print(system_multiarray.num_arrays) # call get_aoi with solar_zenith, solar_azimuth @@ -286,7 +311,8 @@ included with pvlib python by using the :py:func:`~pvlib.pvsystem.retrieve_sam` inverters = pvsystem.retrieve_sam('cecinverter') inverter_parameters = inverters['ABB__MICRO_0_25_I_OUTD_US_208__208V_'] system_one_array = pvsystem.PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + inverter_parameters=inverter_parameters, + temperature_model_parameters=temp_params) The module and/or inverter parameters can also be specified manually. @@ -307,7 +333,9 @@ arranged into 5 strings of 7 modules each. .. ipython:: python - system = pvsystem.PVSystem(modules_per_string=7, strings_per_inverter=5) + system = pvsystem.PVSystem(modules_per_string=7, + strings_per_inverter=5, + temperature_model_parameters=temp_params) # crude numbers from a single module data = pd.DataFrame({'v_mp': 8, 'v_oc': 10, 'i_mp': 5, 'i_x': 6, 'i_xx': 4, 'i_sc': 7, 'p_mp': 40}, index=[0]) diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst index 83b04cc01e..0515a62e00 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -11,7 +11,11 @@ Enhancements Bug fixes ~~~~~~~~~ - +* Fixes `AttributeError: 'PVSystem' object has no attribute 'racking_model'` +error in `ModelChain` instantiation when the `temperature_model_parameters` +for a `PVSystem` have not been set. Update requires `PVSystem` or `Array` +to have `temperature_model_parameters` explicitly or implicitly by providing +`racking_model` and `module_type`. Testing ~~~~~~~ @@ -19,7 +23,8 @@ Testing Documentation ~~~~~~~~~~~~~ - +* Update all examples in :ref:`pvsystem` User's guide page that reference +`pvsystem` to adhere to new required argument. Requirements ~~~~~~~~~~~~ @@ -27,4 +32,5 @@ Requirements Contributors ~~~~~~~~~~~~ +* :ghuser:`matsuobasho` diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 296356971b..0a63a9997b 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -1118,9 +1118,7 @@ def infer_temperature_model(self): array.temperature_model_parameters for array in self.system.arrays) params = _common_keys(temperature_model_parameters) # remove or statement in v0.9 - if {'a', 'b', 'deltaT'} <= params or ( - not params and self.system.racking_model is None - and self.system.module_type is None): + if {'a', 'b', 'deltaT'} <= params: return self.sapm_temp elif {'u_c', 'u_v'} <= params: return self.pvsyst_temp diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 7b2f662b13..9af229be88 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -942,6 +942,13 @@ class Array: name : str, optional Name of Array instance. + + Raises + ------ + ValueError + If `temperature_model_parameters` is None or if `racking_module` + in the `mount` attribute and `module_type` are either None or cannot be + used to infer the temperature model parameters. """ def __init__(self, mount, @@ -977,6 +984,18 @@ def __init__(self, mount, else: self.temperature_model_parameters = temperature_model_parameters + if self.temperature_model_parameters=={}: + raise ValueError("The `temperature_model_parameters` is empty " + "after an attempt to infer it. " + "Pass either `temperature_model_parameters` to " + "`Array` or `PVSystem` (if not passing arrays), or " + "`racking_module` to the `Array` `mount` " + "object and `module_type` to `Array. For guidance " + "on allowed values to use for `racking_module` and " + "`module_type`, examine the " + "TEMPERATURE_MODEL_PARAMETERS global variable from " + "the `temperature` module.") + if array_losses_parameters is None: self.array_losses_parameters = {} else: diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index 6557d77e10..89ffc7673a 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -23,29 +23,36 @@ from pvlib.tools import cosd from pvlib.singlediode import VOLTAGE_BUILTIN from pvlib.tests.test_singlediode import get_pvsyst_fs_495 +from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS +@pytest.fixture() +def temp_model_params(): + return TEMPERATURE_MODEL_PARAMETERS["pvsyst"]["freestanding"] @pytest.mark.parametrize('iam_model,model_params', [ ('ashrae', {'b': 0.05}), ('physical', {'K': 4, 'L': 0.002, 'n': 1.526}), ('martin_ruiz', {'a_r': 0.16}), ]) -def test_PVSystem_get_iam(mocker, iam_model, model_params): +def test_PVSystem_get_iam(mocker, iam_model, model_params, temp_model_params): m = mocker.spy(_iam, iam_model) - system = pvsystem.PVSystem(module_parameters=model_params) + system = pvsystem.PVSystem(module_parameters=model_params, + temperature_model_parameters=temp_model_params) thetas = 1 iam = system.get_iam(thetas, iam_model=iam_model) m.assert_called_with(thetas, **model_params) assert iam < 1. -def test_PVSystem_multi_array_get_iam(): +def test_PVSystem_multi_array_get_iam(temp_model_params): model_params = {'b': 0.05} system = pvsystem.PVSystem( arrays=[pvsystem.Array(mount=pvsystem.FixedMount(0, 180), - module_parameters=model_params), + module_parameters=model_params, + temperature_model_parameters=temp_model_params), pvsystem.Array(mount=pvsystem.FixedMount(0, 180), - module_parameters=model_params)] + module_parameters=model_params, + temperature_model_parameters=temp_model_params)] ) iam = system.get_iam((1, 5), iam_model='ashrae') assert len(iam) == 2 @@ -55,8 +62,9 @@ def test_PVSystem_multi_array_get_iam(): system.get_iam((1,), iam_model='ashrae') -def test_PVSystem_get_iam_sapm(sapm_module_params, mocker): - system = pvsystem.PVSystem(module_parameters=sapm_module_params) +def test_PVSystem_get_iam_sapm(sapm_module_params, mocker, temp_model_params): + system = pvsystem.PVSystem(module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params) mocker.spy(_iam, 'sapm') aoi = 0 out = system.get_iam(aoi, 'sapm') @@ -64,9 +72,10 @@ def test_PVSystem_get_iam_sapm(sapm_module_params, mocker): assert_allclose(out, 1.0, atol=0.01) -def test_PVSystem_get_iam_interp(mocker): +def test_PVSystem_get_iam_interp(mocker, temp_model_params): interp_module_params = {'iam_ref': (1., 0.8), 'theta_ref': (0., 80.)} - system = pvsystem.PVSystem(module_parameters=interp_module_params) + system = pvsystem.PVSystem(module_parameters=interp_module_params, + temperature_model_parameters=temp_model_params) spy = mocker.spy(_iam, 'interp') aoi = ((0., 40., 80.),) expected = (1., 0.9, 0.8) @@ -98,8 +107,9 @@ def test__normalize_sam_product_names(): assert list(norm_names) == NORM_NAMES -def test_PVSystem_get_iam_invalid(sapm_module_params, mocker): - system = pvsystem.PVSystem(module_parameters=sapm_module_params) +def test_PVSystem_get_iam_invalid(sapm_module_params, temp_model_params, mocker): + system = pvsystem.PVSystem(module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params) with pytest.raises(ValueError): system.get_iam(45, iam_model='not_a_model') @@ -226,9 +236,10 @@ def test_sapm(sapm_module_params): pd.Series(sapm_module_params)) -def test_PVSystem_sapm(sapm_module_params, mocker): +def test_PVSystem_sapm(sapm_module_params, temp_model_params, mocker): mocker.spy(pvsystem, 'sapm') - system = pvsystem.PVSystem(module_parameters=sapm_module_params) + system = pvsystem.PVSystem(module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params) effective_irradiance = 500 temp_cell = 25 out = system.sapm(effective_irradiance, temp_cell) @@ -237,12 +248,14 @@ def test_PVSystem_sapm(sapm_module_params, mocker): assert_allclose(out['p_mp'], 100, atol=100) -def test_PVSystem_multi_array_sapm(sapm_module_params): +def test_PVSystem_multi_array_sapm(sapm_module_params, temp_model_params): system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params), + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params)] + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params)] ) effective_irradiance = (100, 500) temp_cell = (15, 25) @@ -268,9 +281,10 @@ def test_sapm_spectral_loss_deprecated(sapm_module_params): pvsystem.sapm_spectral_loss(1, sapm_module_params) -def test_PVSystem_sapm_spectral_loss(sapm_module_params, mocker): +def test_PVSystem_sapm_spectral_loss(sapm_module_params, temp_model_params, mocker): mocker.spy(spectrum, 'spectral_factor_sapm') - system = pvsystem.PVSystem(module_parameters=sapm_module_params) + system = pvsystem.PVSystem(module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params) airmass = 2 out = system.sapm_spectral_loss(airmass) spectrum.spectral_factor_sapm.assert_called_once_with(airmass, @@ -278,12 +292,14 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params, mocker): assert_allclose(out, 1, atol=0.5) -def test_PVSystem_multi_array_sapm_spectral_loss(sapm_module_params): +def test_PVSystem_multi_array_sapm_spectral_loss(sapm_module_params, temp_model_params): system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params), + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params)] + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params)] ) loss_one, loss_two = system.sapm_spectral_loss(2) assert loss_one == loss_two @@ -300,10 +316,11 @@ def test_PVSystem_multi_array_sapm_spectral_loss(sapm_module_params): None, (0.84, -0.03, -0.008, 0.14, 0.04, -0.002)) ]) -def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type, - coefficients, mocker): +def test_PVSystem_first_solar_spectral_loss(module_parameters, temp_model_params, + module_type, coefficients, mocker): mocker.spy(spectrum, 'spectral_factor_firstsolar') - system = pvsystem.PVSystem(module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) pw = 3 airmass_absolute = 3 out = system.first_solar_spectral_loss(pw, airmass_absolute) @@ -312,18 +329,20 @@ def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type, assert_allclose(out, 1, atol=0.5) -def test_PVSystem_multi_array_first_solar_spectral_loss(): +def test_PVSystem_multi_array_first_solar_spectral_loss(temp_model_params): system = pvsystem.PVSystem( arrays=[ pvsystem.Array( mount=pvsystem.FixedMount(0, 180), module_parameters={'Technology': 'mc-Si'}, - module_type='multisi' + module_type='multisi', + temperature_model_parameters=temp_model_params ), pvsystem.Array( mount=pvsystem.FixedMount(0, 180), module_parameters={'Technology': 'mc-Si'}, - module_type='multisi' + module_type='multisi', + temperature_model_parameters=temp_model_params ) ] ) @@ -351,8 +370,9 @@ def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): assert_allclose(out, expected, atol=1e-1) -def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): - system = pvsystem.PVSystem(module_parameters=sapm_module_params) +def test_PVSystem_sapm_effective_irradiance(sapm_module_params, temp_model_params, mocker): + system = pvsystem.PVSystem(module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params) mocker.spy(pvsystem, 'sapm_effective_irradiance') poa_direct = 900 @@ -371,12 +391,14 @@ def test_PVSystem_sapm_effective_irradiance(sapm_module_params, mocker): assert_allclose(out, expected, atol=0.1) -def test_PVSystem_multi_array_sapm_effective_irradiance(sapm_module_params): +def test_PVSystem_multi_array_sapm_effective_irradiance(sapm_module_params, temp_model_params): system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params), + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(0, 180), - module_parameters=sapm_module_params)] + module_parameters=sapm_module_params, + temperature_model_parameters=temp_model_params)] ) poa_direct = (500, 900) poa_diffuse = (50, 100) @@ -627,8 +649,8 @@ def test_PVSystem_multi_array_celltemp_multi_wind(model, two_array_system): assert_series_equal(temp_two, temp_one_swtich) -def test_PVSystem_get_cell_temperature_invalid(): - system = pvsystem.PVSystem() +def test_PVSystem_get_cell_temperature_invalid(temp_model_params): + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) with pytest.raises(ValueError, match='not a valid'): system.get_cell_temperature(1000, 25, 1, 'not_a_model') @@ -714,6 +736,21 @@ def test_PVSystem_fuentes_module_height(mocker): assert spy.call_args[1]['module_height'] == 3 +@pytest.mark.parametrize("racking_model, module_type", + [(None, None), + (None, 'glass_polymer'), + (None, 'glass_glass'), + ('open_rack', None), + ('close_mount', None), + ('close_mount', 'glass_polymer'), + ]) +def test_Array_temperature_model_params_error(racking_model, module_type): + mount = pvsystem.FixedMount(surface_tilt=20, surface_azimuth=180, + racking_model=racking_model) + with pytest.raises(ValueError, match='`temperature_model_parameters` is empty'): + pvsystem.Array(mount = mount, module_type=module_type) + + def test_Array__infer_temperature_model_params(): array = pvsystem.Array(mount=FixedMount(0, 180, racking_model='open_rack'), @@ -738,9 +775,10 @@ def test_Array__infer_temperature_model_params(): assert expected == array._infer_temperature_model_params() -def test_Array__infer_cell_type(): +def test_Array__infer_cell_type(temp_model_params): array = pvsystem.Array(mount=pvsystem.FixedMount(0, 180), - module_parameters={}) + module_parameters={}, + temperature_model_parameters=temp_model_params) assert array._infer_cell_type() is None @@ -1067,12 +1105,13 @@ def test_calcparams_pvsyst(pvsyst_module_params): nNsVth.round(decimals=4), pd.Series([1.6186, 1.7961], index=times)) -def test_PVSystem_calcparams_desoto(cec_module_params, mocker): +def test_PVSystem_calcparams_desoto(cec_module_params, mocker, temp_model_params): mocker.spy(pvsystem, 'calcparams_desoto') module_parameters = cec_module_params.copy() module_parameters['EgRef'] = 1.121 module_parameters['dEgdT'] = -0.0002677 - system = pvsystem.PVSystem(module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) effective_irradiance = np.array([0, 800]) temp_cell = 25 IL, I0, Rs, Rsh, nNsVth = system.calcparams_desoto(effective_irradiance, @@ -1097,10 +1136,11 @@ def test_PVSystem_calcparams_desoto(cec_module_params, mocker): assert_allclose(nNsVth, np.array([0.5, 0.5]), atol=0.1) -def test_PVSystem_calcparams_pvsyst(pvsyst_module_params, mocker): +def test_PVSystem_calcparams_pvsyst(temp_model_params, pvsyst_module_params, mocker): mocker.spy(pvsystem, 'calcparams_pvsyst') module_parameters = pvsyst_module_params.copy() - system = pvsystem.PVSystem(module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters, + temperature_model_parameters=temp_model_params) effective_irradiance = np.array([0, 800]) temp_cell = np.array([25, 50]) IL, I0, Rs, Rsh, nNsVth = system.calcparams_pvsyst(effective_irradiance, @@ -1411,8 +1451,8 @@ def test_i_from_v(fixture_i_from_v, method, atol): assert_allclose(current, I_expected, atol=atol) -def test_PVSystem_i_from_v(mocker): - system = pvsystem.PVSystem() +def test_PVSystem_i_from_v(temp_model_params, mocker): + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) m = mocker.patch('pvlib.pvsystem.i_from_v', autospec=True) args = (7.5049875193450521, 7, 6e-7, 0.1, 20, 0.5) system.i_from_v(*args) @@ -1689,22 +1729,28 @@ def test_scale_voltage_current_power(): assert_frame_equal(out, expected, check_less_precise=5) -def test_PVSystem_scale_voltage_current_power(mocker): +def test_PVSystem_scale_voltage_current_power(temp_model_params, mocker): data = None - system = pvsystem.PVSystem(modules_per_string=2, strings_per_inverter=3) + system = pvsystem.PVSystem(modules_per_string=2, + strings_per_inverter=3, + temperature_model_parameters=temp_model_params) m = mocker.patch( 'pvlib.pvsystem.scale_voltage_current_power', autospec=True) system.scale_voltage_current_power(data) m.assert_called_once_with(data, voltage=2, current=3) -def test_PVSystem_multi_scale_voltage_current_power(mocker): +def test_PVSystem_multi_scale_voltage_current_power(temp_model_params, mocker): data = (1, 2) system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), - modules_per_string=2, strings=3), + modules_per_string=2, + strings=3, + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(0, 180), - modules_per_string=3, strings=5)] + modules_per_string=3, + strings=5, + temperature_model_parameters=temp_model_params)] ) m = mocker.patch( 'pvlib.pvsystem.scale_voltage_current_power', autospec=True @@ -1720,11 +1766,12 @@ def test_PVSystem_multi_scale_voltage_current_power(mocker): system.scale_voltage_current_power(None) -def test_PVSystem_get_ac_sandia(cec_inverter_parameters, mocker): +def test_PVSystem_get_ac_sandia(cec_inverter_parameters, temp_model_params, mocker): inv_fun = mocker.spy(inverter, 'sandia') system = pvsystem.PVSystem( inverter=cec_inverter_parameters['Name'], inverter_parameters=cec_inverter_parameters, + temperature_model_parameters=temp_model_params ) vdcs = pd.Series(np.linspace(0, 50, 3)) idcs = pd.Series(np.linspace(0, 11, 3)) @@ -1734,11 +1781,13 @@ def test_PVSystem_get_ac_sandia(cec_inverter_parameters, mocker): assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000])) -def test_PVSystem_get_ac_sandia_multi(cec_inverter_parameters, mocker): +def test_PVSystem_get_ac_sandia_multi(cec_inverter_parameters, temp_model_params, mocker): inv_fun = mocker.spy(inverter, 'sandia_multi') system = pvsystem.PVSystem( - arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180))], + arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)], inverter=cec_inverter_parameters['Name'], inverter_parameters=cec_inverter_parameters, ) @@ -1778,15 +1827,17 @@ def test_PVSystem_get_ac_pvwatts_kwargs(pvwatts_system_kwargs, mocker): def test_PVSystem_get_ac_pvwatts_multi( - pvwatts_system_defaults, pvwatts_system_kwargs, mocker): + pvwatts_system_defaults, pvwatts_system_kwargs, temp_model_params, mocker): mocker.spy(inverter, 'pvwatts_multi') expected = [pd.Series([0.0, 48.123524, 86.400000]), pd.Series([0.0, 45.893550, 85.500000])] systems = [pvwatts_system_defaults, pvwatts_system_kwargs] for base_sys, exp in zip(systems, expected): system = pvsystem.PVSystem( - arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180),)], + arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)], inverter_parameters=base_sys.inverter_parameters, ) pdcs = pd.Series([0., 25., 50.]) @@ -1809,7 +1860,8 @@ def test_PVSystem_get_ac_single_array_tuple_input( model, pvwatts_system_defaults, cec_inverter_parameters, - adr_inverter_parameters): + adr_inverter_parameters, + temp_model_params): vdcs = { 'sandia': pd.Series(np.linspace(0, 50, 3)), 'pvwatts': None, @@ -1828,7 +1880,8 @@ def test_PVSystem_get_ac_single_array_tuple_input( 'sandia': pd.Series([-0.020000, 132.004308, 250.000000]) } system = pvsystem.PVSystem( - arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180))], + arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)], inverter_parameters=inverter_parameters[model] ) ac = system.get_ac(p_dc=(pdcs[model],), v_dc=(vdcs[model],), model=model) @@ -1838,10 +1891,11 @@ def test_PVSystem_get_ac_single_array_tuple_input( assert_series_equal(ac, expected[model]) -def test_PVSystem_get_ac_adr(adr_inverter_parameters, mocker): +def test_PVSystem_get_ac_adr(adr_inverter_parameters, temp_model_params, mocker): mocker.spy(inverter, 'adr') system = pvsystem.PVSystem( inverter_parameters=adr_inverter_parameters, + temperature_model_parameters=temp_model_params ) vdcs = pd.Series([135, 154, 390, 420, 551]) pdcs = pd.Series([135, 1232, 1170, 420, 551]) @@ -1852,10 +1906,12 @@ def test_PVSystem_get_ac_adr(adr_inverter_parameters, mocker): system.inverter_parameters) -def test_PVSystem_get_ac_adr_multi(adr_inverter_parameters): +def test_PVSystem_get_ac_adr_multi(adr_inverter_parameters, temp_model_params): system = pvsystem.PVSystem( - arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180))], + arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)], inverter_parameters=adr_inverter_parameters, ) pdcs = pd.Series([135, 1232, 1170, 420, 551]) @@ -1864,43 +1920,52 @@ def test_PVSystem_get_ac_adr_multi(adr_inverter_parameters): system.get_ac(model='adr', p_dc=pdcs) -def test_PVSystem_get_ac_invalid(cec_inverter_parameters): +def test_PVSystem_get_ac_invalid(cec_inverter_parameters, temp_model_params): system = pvsystem.PVSystem( inverter_parameters=cec_inverter_parameters, + temperature_model_parameters=temp_model_params ) pdcs = pd.Series(np.linspace(0, 50, 3)) with pytest.raises(ValueError, match="is not a valid AC power model"): system.get_ac(model='not_a_model', p_dc=pdcs) -def test_PVSystem_creation(): - pv_system = pvsystem.PVSystem(module='blah', inverter='blarg') +def test_PVSystem_creation(temp_model_params): + pv_system = pvsystem.PVSystem(module='blah', + inverter='blarg', + temperature_model_parameters=temp_model_params) # ensure that parameter attributes are dict-like. GH 294 pv_system.inverter_parameters['Paco'] = 1 -def test_PVSystem_multiple_array_creation(): - array_one = pvsystem.Array(pvsystem.FixedMount(surface_tilt=32)) +def test_PVSystem_multiple_array_creation(temp_model_params): + array_one = pvsystem.Array(pvsystem.FixedMount(surface_tilt=32), + temperature_model_parameters=temp_model_params) array_two = pvsystem.Array(pvsystem.FixedMount(surface_tilt=15), - module_parameters={'pdc0': 1}) + module_parameters={'pdc0': 1}, + temperature_model_parameters=temp_model_params) pv_system = pvsystem.PVSystem(arrays=[array_one, array_two]) assert pv_system.arrays[0].module_parameters == {} assert pv_system.arrays[1].module_parameters == {'pdc0': 1} assert pv_system.arrays == (array_one, array_two) -def test_PVSystem_get_aoi(): - system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135) +def test_PVSystem_get_aoi(temp_model_params): + system = pvsystem.PVSystem(surface_tilt=32, + surface_azimuth=135, + temperature_model_parameters=temp_model_params) aoi = system.get_aoi(30, 225) assert np.round(aoi, 4) == 42.7408 -def test_PVSystem_multiple_array_get_aoi(): +def test_PVSystem_multiple_array_get_aoi(temp_model_params): system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(surface_tilt=15, - surface_azimuth=135)), + surface_azimuth=135), + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(surface_tilt=32, - surface_azimuth=135))] + surface_azimuth=135), + temperature_model_parameters=temp_model_params)] ) aoi_one, aoi_two = system.get_aoi(30, 225) assert np.round(aoi_two, 4) == 42.7408 @@ -1916,8 +1981,10 @@ def solar_pos(): return location.get_solarposition(times) -def test_PVSystem_get_irradiance(solar_pos): - system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135) +def test_PVSystem_get_irradiance(temp_model_params, solar_pos): + system = pvsystem.PVSystem(surface_tilt=32, + surface_azimuth=135, + temperature_model_parameters=temp_model_params) irrads = pd.DataFrame({'dni':[900,0], 'ghi':[600,0], 'dhi':[100,0]}, index=solar_pos.index) @@ -1937,8 +2004,10 @@ def test_PVSystem_get_irradiance(solar_pos): assert_frame_equal(irradiance, expected, check_less_precise=2) -def test_PVSystem_get_irradiance_albedo(solar_pos): - system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135) +def test_PVSystem_get_irradiance_albedo(temp_model_params, solar_pos): + system = pvsystem.PVSystem(surface_tilt=32, + surface_azimuth=135, + temperature_model_parameters=temp_model_params) irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0], 'albedo': [0.5, 0.5]}, index=solar_pos.index) @@ -1958,10 +2027,12 @@ def test_PVSystem_get_irradiance_albedo(solar_pos): assert_frame_equal(irradiance, expected, check_less_precise=2) -def test_PVSystem_get_irradiance_model(mocker, solar_pos): +def test_PVSystem_get_irradiance_model(mocker, temp_model_params, solar_pos): spy_perez = mocker.spy(irradiance, 'perez') spy_haydavies = mocker.spy(irradiance, 'haydavies') - system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135) + system = pvsystem.PVSystem(surface_tilt=32, + surface_azimuth=135, + temperature_model_parameters=temp_model_params) irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0]}, index=solar_pos.index) system.get_irradiance(solar_pos['apparent_zenith'], @@ -1979,11 +2050,13 @@ def test_PVSystem_get_irradiance_model(mocker, solar_pos): spy_perez.assert_called_once() -def test_PVSystem_multi_array_get_irradiance(solar_pos): +def test_PVSystem_multi_array_get_irradiance(temp_model_params, solar_pos): array_one = pvsystem.Array(pvsystem.FixedMount(surface_tilt=32, - surface_azimuth=135)) + surface_azimuth=135), + temperature_model_parameters=temp_model_params) array_two = pvsystem.Array(pvsystem.FixedMount(surface_tilt=5, - surface_azimuth=150)) + surface_azimuth=150), + temperature_model_parameters=temp_model_params) system = pvsystem.PVSystem(arrays=[array_one, array_two]) irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0]}, @@ -2011,7 +2084,7 @@ def test_PVSystem_multi_array_get_irradiance(solar_pos): ) -def test_PVSystem_multi_array_get_irradiance_multi_irrad(solar_pos): +def test_PVSystem_multi_array_get_irradiance_multi_irrad(temp_model_params, solar_pos): """Test a system with two identical arrays but different irradiance. Because only the irradiance is different we expect the same output @@ -2019,8 +2092,10 @@ def test_PVSystem_multi_array_get_irradiance_multi_irrad(solar_pos): for each array when different GHI/DHI/DNI input is given. For the later case we verify that the correct irradiance data is passed to each array. """ - array_one = pvsystem.Array(pvsystem.FixedMount(0, 180)) - array_two = pvsystem.Array(pvsystem.FixedMount(0, 180)) + array_one = pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params) + array_two = pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params) system = pvsystem.PVSystem(arrays=[array_one, array_two]) irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0]}, @@ -2077,9 +2152,10 @@ def test_PVSystem_multi_array_get_irradiance_multi_irrad(solar_pos): assert not array_irrad[0].equals(array_irrad[1]) -def test_Array_get_irradiance(solar_pos): +def test_Array_get_irradiance(temp_model_params, solar_pos): array = pvsystem.Array(pvsystem.FixedMount(surface_tilt=32, - surface_azimuth=135)) + surface_azimuth=135), + temperature_model_parameters=temp_model_params) irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0]}, index=solar_pos.index) # defaults for kwargs @@ -2135,13 +2211,15 @@ def test_PVSystem___repr__(): assert system.__repr__() == expected -def test_PVSystem_multi_array___repr__(): +def test_PVSystem_multi_array___repr__(temp_model_params): system = pvsystem.PVSystem( arrays=[pvsystem.Array(pvsystem.FixedMount(surface_tilt=30, - surface_azimuth=100)), + surface_azimuth=100), + temperature_model_parameters=temp_model_params), pvsystem.Array(pvsystem.FixedMount(surface_tilt=20, surface_azimuth=220), - name='foo')], + temperature_model_parameters=temp_model_params, + name='foo')], inverter='blarg', ) expected = """PVSystem: @@ -2152,7 +2230,7 @@ def test_PVSystem_multi_array___repr__(): module: None albedo: 0.25 module_type: None - temperature_model_parameters: {} + temperature_model_parameters: {'u_c': 29.0, 'u_v': 0} strings: 1 modules_per_string: 1 Array: @@ -2161,7 +2239,7 @@ def test_PVSystem_multi_array___repr__(): module: None albedo: 0.25 module_type: None - temperature_model_parameters: {} + temperature_model_parameters: {'u_c': 29.0, 'u_v': 0} strings: 1 modules_per_string: 1 inverter: blarg""" # noqa: E501 @@ -2237,20 +2315,22 @@ def test_pvwatts_losses_series(): @pytest.fixture -def pvwatts_system_defaults(): +def pvwatts_system_defaults(temp_model_params): module_parameters = {'pdc0': 100, 'gamma_pdc': -0.003} inverter_parameters = {'pdc0': 90} system = pvsystem.PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + inverter_parameters=inverter_parameters, + temperature_model_parameters=temp_model_params) return system @pytest.fixture -def pvwatts_system_kwargs(): +def pvwatts_system_kwargs(temp_model_params): module_parameters = {'pdc0': 100, 'gamma_pdc': -0.003, 'temp_ref': 20} inverter_parameters = {'pdc0': 90, 'eta_inv_nom': 0.95, 'eta_inv_ref': 1.0} system = pvsystem.PVSystem(module_parameters=module_parameters, - inverter_parameters=inverter_parameters) + inverter_parameters=inverter_parameters, + temperature_model_parameters=temp_model_params) return system @@ -2277,20 +2357,22 @@ def test_PVSystem_pvwatts_dc_kwargs(pvwatts_system_kwargs, mocker): assert_allclose(expected, out, atol=10) -def test_PVSystem_multiple_array_pvwatts_dc(): +def test_PVSystem_multiple_array_pvwatts_dc(temp_model_params): array_one_module_parameters = { 'pdc0': 100, 'gamma_pdc': -0.003, 'temp_ref': 20 } array_one = pvsystem.Array( pvsystem.FixedMount(0, 180), - module_parameters=array_one_module_parameters + module_parameters=array_one_module_parameters, + temperature_model_parameters=temp_model_params ) array_two_module_parameters = { 'pdc0': 150, 'gamma_pdc': -0.002, 'temp_ref': 25 } array_two = pvsystem.Array( pvsystem.FixedMount(0, 180), - module_parameters=array_two_module_parameters + module_parameters=array_two_module_parameters, + temperature_model_parameters=temp_model_params ) system = pvsystem.PVSystem(arrays=[array_one, array_two]) irrad_one = 900 @@ -2307,11 +2389,14 @@ def test_PVSystem_multiple_array_pvwatts_dc(): assert dc_two == expected_two -def test_PVSystem_multiple_array_pvwatts_dc_value_error(): +def test_PVSystem_multiple_array_pvwatts_dc_value_error(temp_model_params): system = pvsystem.PVSystem( - arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180))] + arrays=[pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)] ) error_message = 'Length mismatch for per-array parameter' with pytest.raises(ValueError, match=error_message): @@ -2345,11 +2430,13 @@ def test_PVSystem_pvwatts_losses(pvwatts_system_defaults, mocker): assert out < expected -def test_PVSystem_num_arrays(): - system_one = pvsystem.PVSystem() +def test_PVSystem_num_arrays(temp_model_params): + system_one = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) system_two = pvsystem.PVSystem(arrays=[ - pvsystem.Array(pvsystem.FixedMount(0, 180)), - pvsystem.Array(pvsystem.FixedMount(0, 180))]) + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params), + pvsystem.Array(pvsystem.FixedMount(0, 180), + temperature_model_parameters=temp_model_params)]) assert system_one.num_arrays == 1 assert system_two.num_arrays == 2 @@ -2360,9 +2447,10 @@ def test_PVSystem_at_least_one_array(): pvsystem.PVSystem(arrays=[]) -def test_PVSystem_single_array(): +def test_PVSystem_single_array(temp_model_params): # GH 1831 - single_array = pvsystem.Array(pvsystem.FixedMount()) + single_array = pvsystem.Array(pvsystem.FixedMount(), + temperature_model_parameters=temp_model_params) system = pvsystem.PVSystem(arrays=single_array) assert isinstance(system.arrays, tuple) assert system.arrays[0] is single_array @@ -2443,13 +2531,14 @@ def test_dc_ohms_from_percent(): assert_allclose(out, expected) -def test_PVSystem_dc_ohms_from_percent(mocker): +def test_PVSystem_dc_ohms_from_percent(mocker, temp_model_params): mocker.spy(pvsystem, 'dc_ohms_from_percent') expected = .1425 system = pvsystem.PVSystem(losses_parameters={'dc_ohmic_percent': 3}, module_parameters={'I_mp_ref': 8, - 'V_mp_ref': 38}) + 'V_mp_ref': 38}, + temperature_model_parameters=temp_model_params) out = system.dc_ohms_from_percent() pvsystem.dc_ohms_from_percent.assert_called_once_with( @@ -2469,7 +2558,7 @@ def test_dc_ohmic_losses(): assert_allclose(out, expected) -def test_Array_dc_ohms_from_percent(mocker): +def test_Array_dc_ohms_from_percent(mocker, temp_model_params): mocker.spy(pvsystem, 'dc_ohms_from_percent') expected = .1425 @@ -2477,7 +2566,8 @@ def test_Array_dc_ohms_from_percent(mocker): array = pvsystem.Array(pvsystem.FixedMount(0, 180), array_losses_parameters={'dc_ohmic_percent': 3}, module_parameters={'I_mp_ref': 8, - 'V_mp_ref': 38}) + 'V_mp_ref': 38}, + temperature_model_parameters=temp_model_params) out = array.dc_ohms_from_percent() pvsystem.dc_ohms_from_percent.assert_called_with( dc_ohmic_percent=3, @@ -2491,7 +2581,8 @@ def test_Array_dc_ohms_from_percent(mocker): array = pvsystem.Array(pvsystem.FixedMount(0, 180), array_losses_parameters={'dc_ohmic_percent': 3}, module_parameters={'Impo': 8, - 'Vmpo': 38}) + 'Vmpo': 38}, + temperature_model_parameters=temp_model_params) out = array.dc_ohms_from_percent() pvsystem.dc_ohms_from_percent.assert_called_with( dc_ohmic_percent=3, @@ -2505,7 +2596,8 @@ def test_Array_dc_ohms_from_percent(mocker): array = pvsystem.Array(pvsystem.FixedMount(0, 180), array_losses_parameters={'dc_ohmic_percent': 3}, module_parameters={'Impp': 8, - 'Vmpp': 38}) + 'Vmpp': 38}, + temperature_model_parameters=temp_model_params) out = array.dc_ohms_from_percent() pvsystem.dc_ohms_from_percent.assert_called_with( @@ -2525,7 +2617,8 @@ def test_Array_dc_ohms_from_percent(mocker): '{"Vmpo", "Impo"}, or ' '{"Vmpp", "Impp"}.')): array = pvsystem.Array(pvsystem.FixedMount(0, 180), - array_losses_parameters={'dc_ohmic_percent': 3}) + array_losses_parameters={'dc_ohmic_percent': 3}, + temperature_model_parameters=temp_model_params) out = array.dc_ohms_from_percent()