diff --git a/docs/sphinx/source/whatsnew/v0.13.2.rst b/docs/sphinx/source/whatsnew/v0.13.2.rst index b6170a6053..72dce0efb0 100644 --- a/docs/sphinx/source/whatsnew/v0.13.2.rst +++ b/docs/sphinx/source/whatsnew/v0.13.2.rst @@ -46,6 +46,8 @@ Enhancements MERRA-2 reanalysis data. (:pull:`2572`) * Add :py:func:`~pvlib.spectrum.spectral_factor_polo`, a function for estimating spectral mismatch factors for vertical PV façades. (:issue:`2406`, :pull:`2491`) +* Include `ross` and `faiman_rad` in the allowed models within + :py:meth:`pvlib.pvsystem.PVSystem.get_cell_temperature` (:issue:`2625`, :pull:`2631`) Documentation ~~~~~~~~~~~~~ @@ -87,3 +89,4 @@ Contributors * Anton Driesse (:ghuser:`adriesse`) * Rajiv Daxini (:ghuser:`RDaxini`) * Kevin Anderson (:ghuser:`kandersolar`) +* Rodrigo Amaro e Silva (:ghuser:`ramaroesilva`) \ No newline at end of file diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index fe85359b99..bf4dcb685a 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -414,7 +414,7 @@ def get_iam(self, aoi, iam_model='physical'): @_unwrap_single_value def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, - effective_irradiance=None): + effective_irradiance=None, ir_down=None): """ Determine cell temperature using the method specified by ``model``. @@ -431,12 +431,17 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, model : str Supported models include ``'sapm'``, ``'pvsyst'``, - ``'faiman'``, ``'fuentes'``, and ``'noct_sam'`` + ``'faiman'``, ``'faiman_rad'``, ``'fuentes'``, ``'noct_sam'``, + and ``'ross'`` effective_irradiance : numeric or tuple of numeric, optional The irradiance that is converted to photocurrent in W/m^2. Only used for some models. + ir_down: numeric, optional + Downwelling infrared radiation from the sky, measured on a + horizontal surface in W/m^2. Only used in ``'faiman_rad'`` model. + Returns ------- numeric or tuple of numeric @@ -459,14 +464,16 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, # Not used for all models, but Array.get_cell_temperature handles it effective_irradiance = self._validate_per_array(effective_irradiance, system_wide=True) + ir_down = self._validate_per_array(ir_down, system_wide=True) return tuple( array.get_cell_temperature(poa_global, temp_air, wind_speed, - model, effective_irradiance) - for array, poa_global, temp_air, wind_speed, effective_irradiance + model, effective_irradiance, ir_down) + for array, poa_global, temp_air, wind_speed, effective_irradiance, + ir_down in zip( self.arrays, poa_global, temp_air, wind_speed, - effective_irradiance + effective_irradiance, ir_down ) ) @@ -1204,7 +1211,7 @@ def get_iam(self, aoi, iam_model='physical'): raise ValueError(model + ' is not a valid IAM model') def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, - effective_irradiance=None): + effective_irradiance=None, ir_down=None): """ Determine cell temperature using the method specified by ``model``. @@ -1217,16 +1224,21 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, Ambient dry bulb temperature [C] wind_speed : numeric - Wind speed [m/s] + Wind speed [m/s], although can be ``None`` for ``'ross'`` model model : str Supported models include ``'sapm'``, ``'pvsyst'``, - ``'faiman'``, ``'fuentes'``, and ``'noct_sam'`` + ``'faiman'``, ``'faiman_rad'``, ``'fuentes'``, ``'noct_sam'``, + and ``'ross'`` effective_irradiance : numeric, optional The irradiance that is converted to photocurrent in W/m^2. Only used for some models. + ir_down: numeric, optional + Downwelling infrared radiation from the sky, measured on a + horizontal surface in W/m^2. Only used in ``'faiman_rad'`` model. + Returns ------- numeric @@ -1235,8 +1247,9 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, See Also -------- pvlib.temperature.sapm_cell, pvlib.temperature.pvsyst_cell, - pvlib.temperature.faiman, pvlib.temperature.fuentes, - pvlib.temperature.noct_sam + pvlib.temperature.faiman, pvlib.temperature.faiman_rad, + pvlib.temperature.fuentes, pvlib.temperature.noct_sam, + pvlib.temperature.ross Notes ----- @@ -1267,6 +1280,13 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, required = tuple() optional = _build_kwargs(['u0', 'u1'], self.temperature_model_parameters) + elif model == 'faiman_rad': + func = functools.partial(temperature.faiman_rad, + ir_down=ir_down) + required = () + optional = _build_kwargs(['ir_down', 'u0', 'u1', + 'sky_view', 'emissivity'], + self.temperature_model_parameters) elif model == 'fuentes': func = temperature.fuentes required = _build_tcell_args(['noct_installed']) @@ -1283,11 +1303,21 @@ def get_cell_temperature(self, poa_global, temp_air, wind_speed, model, optional = _build_kwargs(['transmittance_absorptance', 'array_height', 'mount_standoff'], self.temperature_model_parameters) + elif model == 'ross': + func = temperature.ross + required = () + # either noct or k must be defined + optional = _build_kwargs(['noct', 'k'], + self.temperature_model_parameters) else: raise ValueError(f'{model} is not a valid cell temperature model') - temperature_cell = func(poa_global, temp_air, wind_speed, - *required, **optional) + if model == 'ross': + temperature_cell = func(poa_global, temp_air, + *required, **optional) + else: + temperature_cell = func(poa_global, temp_air, wind_speed, + *required, **optional) return temperature_cell def dc_ohms_from_percent(self): diff --git a/tests/test_pvsystem.py b/tests/test_pvsystem.py index 4fbd782e65..ab74566c92 100644 --- a/tests/test_pvsystem.py +++ b/tests/test_pvsystem.py @@ -494,6 +494,44 @@ def test_PVSystem_faiman_celltemp(mocker): assert_allclose(out, 56.4, atol=1e-1) +def test_PVSystem_faiman_rad_celltemp(mocker): + ir_down = 50 # arbitrary value + # default values, u0 and u1 being adjusted in same proportion as in + # https://www.osti.gov/servlets/purl/1884890/ (not suggested, just example) + u0, u1 = 25.0*0.86, 6.84*0.88 + sky_view = 1.0 + emissivity = 0.88 + + temp_model_params = {'u0': u0, 'u1': u1, 'sky_view': sky_view, + 'emissivity': emissivity} + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) + mocker.spy(temperature, 'faiman_rad') + temps = 25 + irrads = 1000 + winds = 1 + out = system.get_cell_temperature(irrads, temps, winds, ir_down=ir_down, + model='faiman_rad') + temperature.faiman_rad.assert_called_once_with(irrads, temps, winds, + ir_down, u0, u1, + sky_view, emissivity) + assert_allclose(out, 48.6, atol=1e-1) + + +def test_PVSystem_ross_celltemp(mocker): + # example value (could use equivalent noct as alternative input) + k = 0.0208 # free-standing system + + temp_model_params = {'k': k} + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) + mocker.spy(temperature, 'ross') + temps = 25 + irrads = 1000 + winds = None + out = system.get_cell_temperature(irrads, temps, winds, model='ross') + temperature.ross.assert_called_once_with(irrads, temps, k=k) + assert_allclose(out, 45.8, atol=1e-1) + + def test_PVSystem_noct_celltemp(mocker): poa_global, temp_air, wind_speed, noct, module_efficiency = ( 1000., 25., 1., 45., 0.2)