diff --git a/docs/sphinx/source/reference/pv_modeling.rst b/docs/sphinx/source/reference/pv_modeling.rst index 0f33cf8c70..2208e932bd 100644 --- a/docs/sphinx/source/reference/pv_modeling.rst +++ b/docs/sphinx/source/reference/pv_modeling.rst @@ -42,6 +42,7 @@ PV temperature models temperature.sapm_cell_from_module temperature.pvsyst_cell temperature.faiman + temperature.faiman_rad temperature.fuentes temperature.ross temperature.noct_sam diff --git a/docs/sphinx/source/whatsnew/v0.9.4.rst b/docs/sphinx/source/whatsnew/v0.9.4.rst index abbabc2840..6524c1745f 100644 --- a/docs/sphinx/source/whatsnew/v0.9.4.rst +++ b/docs/sphinx/source/whatsnew/v0.9.4.rst @@ -16,9 +16,12 @@ Enhancements * Added a function to calculate one of GHI, DHI, and DNI from values of the other two. :py:func:`~pvlib.irradiance.complete_irradiance`. (:issue:`1565`, :pull:`1567`) -* Add optional ``return_components`` parameter to :py:func:`pvlib.irradiance.haydavies` to return - individual diffuse irradiance components. (:issue:`1553`, :pull:`1568`) - +* Added optional ``return_components`` parameter to :py:func:`pvlib.irradiance.haydavies` to return + individual diffuse irradiance components (:issue:`1553`, :pull:`1568`) +* Added a module temperature model that accounts for radiative losses to the sky + in a simplified way, using the Faiman model as an example. + :py:func:`~pvlib.temperature.faiman_rad` + (:issue:`1594`, :pull:`1595`) Bug fixes ~~~~~~~~~ @@ -42,7 +45,6 @@ Benchmarking ~~~~~~~~~~~~~ * Removed ``time_tracker_singleaxis`` function from tracking.py (:issue:`1508`, :pull:`1535`) - Requirements ~~~~~~~~~~~~ @@ -59,4 +61,5 @@ Contributors * Kevin Anderson (:ghuser:`kanderso-nrel`) * Karel De Brabandere (:ghuser:`kdebrab`) * Naman Priyadarshi (:ghuser:`Naman-Priyadarshi`) +* Adam R. Jensen (:ghuser:`AdamRJensen`) * Echedey Luis (:ghuser:`echedey-ls`) diff --git a/pvlib/temperature.py b/pvlib/temperature.py index e52032f6ec..9748965ccd 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -9,6 +9,7 @@ from pvlib._deprecation import warn_deprecated from pvlib.tools import _get_sample_intervals import scipy +import scipy.constants import warnings @@ -318,7 +319,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, u_v : float, default 0.0 Combined heat loss factor influenced by wind. Parameter :math:`U_{v}` in :eq:`pvsyst`. - :math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` # noQA: E501 + :math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` eta_m : numeric, default None (deprecated, use module_efficiency instead) @@ -375,7 +376,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0, >>> params = TEMPERATURE_MODEL_PARAMETERS['pvsyst']['freestanding'] >>> pvsyst_cell(1000, 10, **params) 37.93103448275862 - """ + """ # noQA: E501 if eta_m: warn_deprecated( @@ -413,12 +414,14 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84): u0 : numeric, default 25.0 Combined heat loss factor coefficient. The default value is one - determined by Faiman for 7 silicon modules. + determined by Faiman for 7 silicon modules + in the Negev desert on an open rack at 30.9° tilt. :math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]` u1 : numeric, default 6.84 Combined heat loss factor influenced by wind. The default value is one - determined by Faiman for 7 silicon modules. + determined by Faiman for 7 silicon modules + in the Negev desert on an open rack at 30.9° tilt. :math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` Returns @@ -434,6 +437,7 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84): ---------- .. [1] Faiman, D. (2008). "Assessing the outdoor operating temperature of photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. + :doi:`10.1002/pip.813` .. [2] "IEC 61853-2 Photovoltaic (PV) module performance testing and energy rating - Part 2: Spectral responsivity, incidence angle and module @@ -442,7 +446,12 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84): .. [3] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. - ''' + See also + -------- + pvlib.temperature.faiman_rad + + ''' # noQA: E501 + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Dec., 2019 # The following lines may seem odd since u0 & u1 are probably scalar, @@ -457,6 +466,115 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84): return temp_air + temp_difference +def faiman_rad(poa_global, temp_air, wind_speed=1.0, ir_down=None, + u0=25.0, u1=6.84, sky_view=1.0, emissivity=0.88): + r''' + Calculate cell or module temperature using the Faiman model augmented + with a radiative loss term. + + The Faiman model uses an empirical heat loss factor model [1]_ and is + adopted in the IEC 61853 standards [2]_ and [3]_. The radiative loss + term was proposed and developed by Driesse [4]_. + + The model can be used to represent cell or module temperature. + + Parameters + ---------- + poa_global : numeric + Total incident irradiance [W/m^2]. + + temp_air : numeric + Ambient dry bulb temperature [C]. + + wind_speed : numeric, default 1.0 + Wind speed measured at the same height for which the wind loss + factor was determined. The default value 1.0 m/s is the wind + speed at module height used to determine NOCT. [m/s] + + ir_down : numeric, default 0.0 + Downwelling infrared radiation from the sky, measured on a horizontal + surface. [W/m^2] + + u0 : numeric, default 25.0 + Combined heat loss factor coefficient. The default value is one + determined by Faiman for 7 silicon modules + in the Negev desert on an open rack at 30.9° tilt. + :math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]` + + u1 : numeric, default 6.84 + Combined heat loss factor influenced by wind. The default value is one + determined by Faiman for 7 silicon modules + in the Negev desert on an open rack at 30.9° tilt. + :math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` + + sky_view : numeric, default 1.0 + Effective view factor limiting the radiative exchange between the + module and the sky. For a tilted array the expressions + (1 + 3*cos(tilt)) / 4 can be used as a first estimate for sky_view + as discussed in [4]_. The default value is for a horizontal module. + [unitless] + + emissivity : numeric, default 0.88 + Infrared emissivity of the module surface facing the sky. The default + value represents the middle of a range of values found in the + literature. [unitless] + + Returns + ------- + numeric, values in degrees Celsius + + Notes + ----- + All arguments may be scalars or vectors. If multiple arguments + are vectors they must be the same length. + + When only irradiance, air temperature and wind speed inputs are provided + (`ir_down` is `None`) this function calculates the same device temperature + as the original faiman model. When down-welling long-wave radiation data + are provided as well (`ir_down` is not None) the default u0 and u1 values + from the original model should not be used because a portion of the + radiative losses would be double-counted. + + References + ---------- + .. [1] Faiman, D. (2008). "Assessing the outdoor operating temperature of + photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. + :doi:`10.1002/pip.813` + + .. [2] "IEC 61853-2 Photovoltaic (PV) module performance testing and energy + rating - Part 2: Spectral responsivity, incidence angle and module + operating temperature measurements". IEC, Geneva, 2018. + + .. [3] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy + rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. + + .. [4] Driesse, A. et al (2022) "Improving Common PV Module Temperature + Models by Incorporating Radiative Losses to the Sky". SAND2022-11604. + :doi:`10.2172/1884890` + + See also + -------- + pvlib.temperature.faiman + + ''' # noQA: E501 + + # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2022 + + abs_zero = -273.15 + sigma = scipy.constants.Stefan_Boltzmann + + if ir_down is None: + qrad_sky = 0.0 + else: + ir_up = sigma * ((temp_air - abs_zero)**4) + qrad_sky = emissivity * sky_view * (ir_up - ir_down) + + heat_input = poa_global - qrad_sky + total_loss_factor = u0 + u1 * wind_speed + temp_difference = heat_input / total_loss_factor + return temp_air + temp_difference + + def ross(poa_global, temp_air, noct): r''' Calculate cell temperature using the Ross model. diff --git a/pvlib/tests/test_temperature.py b/pvlib/tests/test_temperature.py index 5e36714d12..bd6831fd0a 100644 --- a/pvlib/tests/test_temperature.py +++ b/pvlib/tests/test_temperature.py @@ -108,12 +108,12 @@ def test_pvsyst_cell_eta_m_deprecated(): def test_faiman_default(): result = temperature.faiman(900, 20, 5) - assert_allclose(result, 35.203, 0.001) + assert_allclose(result, 35.203, atol=0.001) def test_faiman_kwargs(): result = temperature.faiman(900, 20, wind_speed=5.0, u0=22.0, u1=6.) - assert_allclose(result, 37.308, 0.001) + assert_allclose(result, 37.308, atol=0.001) def test_faiman_list(): @@ -122,7 +122,7 @@ def test_faiman_list(): winds = [10, 5, 0] result = temperature.faiman(irrads, temps, wind_speed=winds) expected = np.array([0.0, 18.446, 5.0]) - assert_allclose(expected, result, 3) + assert_allclose(expected, result, atol=0.001) def test_faiman_ndarray(): @@ -131,7 +131,32 @@ def test_faiman_ndarray(): winds = np.array([10, 5, 0]) result = temperature.faiman(irrads, temps, wind_speed=winds) expected = np.array([0.0, 18.446, 5.0]) - assert_allclose(expected, result, 3) + assert_allclose(expected, result, atol=0.001) + + +def test_faiman_rad_no_ir(): + expected = temperature.faiman(900, 20, 5) + result = temperature.faiman_rad(900, 20, 5) + assert_allclose(result, expected) + + +def test_faiman_rad_ir(): + ir_down = np.array([0, 100, 200, 315.6574, 400]) + expected = [-11.111, -7.591, -4.071, -0.000, 2.969] + result = temperature.faiman_rad(0, 0, 0, ir_down) + assert_allclose(result, expected, atol=0.001) + + sky_view = np.array([1.0, 0.5, 0.0]) + expected = [-4.071, -2.036, 0.000] + result = temperature.faiman_rad(0, 0, 0, ir_down=200, + sky_view=sky_view) + assert_allclose(result, expected, atol=0.001) + + emissivity = np.array([1.0, 0.88, 0.5, 0.0]) + expected = [-4.626, -4.071, -2.313, 0.000] + result = temperature.faiman_rad(0, 0, 0, ir_down=200, + emissivity=emissivity) + assert_allclose(result, expected, atol=0.001) def test_ross():