Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/pv_modeling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PV temperature models
temperature.sapm_cell_from_module
temperature.pvsyst_cell
temperature.faiman
temperature.faiman_rad
temperature.fuentes
temperature.ross
temperature.noct_sam
Expand Down
102 changes: 101 additions & 1 deletion pvlib/temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84):
u1 : numeric, default 6.84
Combined heat loss factor influenced by wind. The default value is one
determined by Faiman for 7 silicon modules.
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` # noQA: E501

Returns
-------
Expand Down Expand Up @@ -457,6 +457,106 @@ 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 developed in [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.
: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.
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` # noQA: E501

sky_view : numeric, default 1.0
Effective view factor limiting the radiative exchange between the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "effective" adding something here? sky_view looks to be the fraction of the sky dome "seen" by the module, so that's the usual view factor. I wonder if the formula could be considered part of the model and the input would be surface_tilt, but I'll defer to the model author.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I snuck in the "effective" because the infra-red sky is very non-isotropic and that needs to be taken into account. The formula is really just a starting point for future work, not adequately explored and validated.

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.

If the input `ir_down` is not supplied the model output is the same as the
original Faiman model.

References
----------
.. [1] Faiman, D. (2008). "Assessing the outdoor operating temperature of
photovoltaic modules." Progress in Photovoltaics 16(4): 307-315.

.. [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.

'''
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2022

u0 = np.asanyarray(u0)
u1 = np.asanyarray(u1)
emissivity = np.asanyarray(emissivity)

abs_zero = np.array(-273.15)
kstefbolz = np.array(5.670367e-8)

if ir_down is None:
qrad_sky = np.array(0.0)
else:
ir_up = kstefbolz * ((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.
Expand Down
33 changes: 29 additions & 4 deletions pvlib/tests/test_temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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():
Expand All @@ -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 = [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 = [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 = [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():
Expand Down