Skip to content

Commit 4bdbfd6

Browse files
adriessecwhanseAdamRJensen
authored
Implement enhanced Faiman temperature model (#1595)
* New faiman_rad function. No tests yet. * Adjust arguments and default behaviour. * Improve doc string, add tests, fix some old tests. * Update pvlib/temperature.py Co-authored-by: Cliff Hansen <[email protected]> * Minor improvements here and there. * Add entry in pv_modeling.rst * Update whatsnew and touch up doc strings. * Enhance reference in doc string. * Add DOI. * Finishing touches. * Address review comments. * Whatever it takes to get merged. Co-authored-by: Cliff Hansen <[email protected]> Co-authored-by: Adam R. Jensen <[email protected]>
1 parent e8da6a5 commit 4bdbfd6

File tree

4 files changed

+160
-13
lines changed

4 files changed

+160
-13
lines changed

docs/sphinx/source/reference/pv_modeling.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ PV temperature models
4242
temperature.sapm_cell_from_module
4343
temperature.pvsyst_cell
4444
temperature.faiman
45+
temperature.faiman_rad
4546
temperature.fuentes
4647
temperature.ross
4748
temperature.noct_sam

docs/sphinx/source/whatsnew/v0.9.4.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ Enhancements
1616
* Added a function to calculate one of GHI, DHI, and DNI from values of the other two.
1717
:py:func:`~pvlib.irradiance.complete_irradiance`.
1818
(:issue:`1565`, :pull:`1567`)
19-
* Add optional ``return_components`` parameter to :py:func:`pvlib.irradiance.haydavies` to return
20-
individual diffuse irradiance components. (:issue:`1553`, :pull:`1568`)
21-
19+
* Added optional ``return_components`` parameter to :py:func:`pvlib.irradiance.haydavies` to return
20+
individual diffuse irradiance components (:issue:`1553`, :pull:`1568`)
21+
* Added a module temperature model that accounts for radiative losses to the sky
22+
in a simplified way, using the Faiman model as an example.
23+
:py:func:`~pvlib.temperature.faiman_rad`
24+
(:issue:`1594`, :pull:`1595`)
2225

2326
Bug fixes
2427
~~~~~~~~~
@@ -42,7 +45,6 @@ Benchmarking
4245
~~~~~~~~~~~~~
4346
* Removed ``time_tracker_singleaxis`` function from tracking.py (:issue:`1508`, :pull:`1535`)
4447

45-
4648
Requirements
4749
~~~~~~~~~~~~
4850

@@ -59,4 +61,5 @@ Contributors
5961
* Kevin Anderson (:ghuser:`kanderso-nrel`)
6062
* Karel De Brabandere (:ghuser:`kdebrab`)
6163
* Naman Priyadarshi (:ghuser:`Naman-Priyadarshi`)
64+
* Adam R. Jensen (:ghuser:`AdamRJensen`)
6265
* Echedey Luis (:ghuser:`echedey-ls`)

pvlib/temperature.py

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pvlib._deprecation import warn_deprecated
1010
from pvlib.tools import _get_sample_intervals
1111
import scipy
12+
import scipy.constants
1213
import warnings
1314

1415

@@ -318,7 +319,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0,
318319
u_v : float, default 0.0
319320
Combined heat loss factor influenced by wind. Parameter :math:`U_{v}`
320321
in :eq:`pvsyst`.
321-
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]` # noQA: E501
322+
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
322323
323324
eta_m : numeric, default None (deprecated, use module_efficiency instead)
324325
@@ -375,7 +376,7 @@ def pvsyst_cell(poa_global, temp_air, wind_speed=1.0, u_c=29.0, u_v=0.0,
375376
>>> params = TEMPERATURE_MODEL_PARAMETERS['pvsyst']['freestanding']
376377
>>> pvsyst_cell(1000, 10, **params)
377378
37.93103448275862
378-
"""
379+
""" # noQA: E501
379380

380381
if eta_m:
381382
warn_deprecated(
@@ -413,12 +414,14 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84):
413414
414415
u0 : numeric, default 25.0
415416
Combined heat loss factor coefficient. The default value is one
416-
determined by Faiman for 7 silicon modules.
417+
determined by Faiman for 7 silicon modules
418+
in the Negev desert on an open rack at 30.9° tilt.
417419
:math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]`
418420
419421
u1 : numeric, default 6.84
420422
Combined heat loss factor influenced by wind. The default value is one
421-
determined by Faiman for 7 silicon modules.
423+
determined by Faiman for 7 silicon modules
424+
in the Negev desert on an open rack at 30.9° tilt.
422425
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
423426
424427
Returns
@@ -434,6 +437,7 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84):
434437
----------
435438
.. [1] Faiman, D. (2008). "Assessing the outdoor operating temperature of
436439
photovoltaic modules." Progress in Photovoltaics 16(4): 307-315.
440+
:doi:`10.1002/pip.813`
437441
438442
.. [2] "IEC 61853-2 Photovoltaic (PV) module performance testing and energy
439443
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):
442446
.. [3] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy
443447
rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018.
444448
445-
'''
449+
See also
450+
--------
451+
pvlib.temperature.faiman_rad
452+
453+
''' # noQA: E501
454+
446455
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Dec., 2019
447456

448457
# 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):
457466
return temp_air + temp_difference
458467

459468

469+
def faiman_rad(poa_global, temp_air, wind_speed=1.0, ir_down=None,
470+
u0=25.0, u1=6.84, sky_view=1.0, emissivity=0.88):
471+
r'''
472+
Calculate cell or module temperature using the Faiman model augmented
473+
with a radiative loss term.
474+
475+
The Faiman model uses an empirical heat loss factor model [1]_ and is
476+
adopted in the IEC 61853 standards [2]_ and [3]_. The radiative loss
477+
term was proposed and developed by Driesse [4]_.
478+
479+
The model can be used to represent cell or module temperature.
480+
481+
Parameters
482+
----------
483+
poa_global : numeric
484+
Total incident irradiance [W/m^2].
485+
486+
temp_air : numeric
487+
Ambient dry bulb temperature [C].
488+
489+
wind_speed : numeric, default 1.0
490+
Wind speed measured at the same height for which the wind loss
491+
factor was determined. The default value 1.0 m/s is the wind
492+
speed at module height used to determine NOCT. [m/s]
493+
494+
ir_down : numeric, default 0.0
495+
Downwelling infrared radiation from the sky, measured on a horizontal
496+
surface. [W/m^2]
497+
498+
u0 : numeric, default 25.0
499+
Combined heat loss factor coefficient. The default value is one
500+
determined by Faiman for 7 silicon modules
501+
in the Negev desert on an open rack at 30.9° tilt.
502+
:math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]`
503+
504+
u1 : numeric, default 6.84
505+
Combined heat loss factor influenced by wind. The default value is one
506+
determined by Faiman for 7 silicon modules
507+
in the Negev desert on an open rack at 30.9° tilt.
508+
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
509+
510+
sky_view : numeric, default 1.0
511+
Effective view factor limiting the radiative exchange between the
512+
module and the sky. For a tilted array the expressions
513+
(1 + 3*cos(tilt)) / 4 can be used as a first estimate for sky_view
514+
as discussed in [4]_. The default value is for a horizontal module.
515+
[unitless]
516+
517+
emissivity : numeric, default 0.88
518+
Infrared emissivity of the module surface facing the sky. The default
519+
value represents the middle of a range of values found in the
520+
literature. [unitless]
521+
522+
Returns
523+
-------
524+
numeric, values in degrees Celsius
525+
526+
Notes
527+
-----
528+
All arguments may be scalars or vectors. If multiple arguments
529+
are vectors they must be the same length.
530+
531+
When only irradiance, air temperature and wind speed inputs are provided
532+
(`ir_down` is `None`) this function calculates the same device temperature
533+
as the original faiman model. When down-welling long-wave radiation data
534+
are provided as well (`ir_down` is not None) the default u0 and u1 values
535+
from the original model should not be used because a portion of the
536+
radiative losses would be double-counted.
537+
538+
References
539+
----------
540+
.. [1] Faiman, D. (2008). "Assessing the outdoor operating temperature of
541+
photovoltaic modules." Progress in Photovoltaics 16(4): 307-315.
542+
:doi:`10.1002/pip.813`
543+
544+
.. [2] "IEC 61853-2 Photovoltaic (PV) module performance testing and energy
545+
rating - Part 2: Spectral responsivity, incidence angle and module
546+
operating temperature measurements". IEC, Geneva, 2018.
547+
548+
.. [3] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy
549+
rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018.
550+
551+
.. [4] Driesse, A. et al (2022) "Improving Common PV Module Temperature
552+
Models by Incorporating Radiative Losses to the Sky". SAND2022-11604.
553+
:doi:`10.2172/1884890`
554+
555+
See also
556+
--------
557+
pvlib.temperature.faiman
558+
559+
''' # noQA: E501
560+
561+
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Nov., 2022
562+
563+
abs_zero = -273.15
564+
sigma = scipy.constants.Stefan_Boltzmann
565+
566+
if ir_down is None:
567+
qrad_sky = 0.0
568+
else:
569+
ir_up = sigma * ((temp_air - abs_zero)**4)
570+
qrad_sky = emissivity * sky_view * (ir_up - ir_down)
571+
572+
heat_input = poa_global - qrad_sky
573+
total_loss_factor = u0 + u1 * wind_speed
574+
temp_difference = heat_input / total_loss_factor
575+
return temp_air + temp_difference
576+
577+
460578
def ross(poa_global, temp_air, noct):
461579
r'''
462580
Calculate cell temperature using the Ross model.

pvlib/tests/test_temperature.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ def test_pvsyst_cell_eta_m_deprecated():
108108

109109
def test_faiman_default():
110110
result = temperature.faiman(900, 20, 5)
111-
assert_allclose(result, 35.203, 0.001)
111+
assert_allclose(result, 35.203, atol=0.001)
112112

113113

114114
def test_faiman_kwargs():
115115
result = temperature.faiman(900, 20, wind_speed=5.0, u0=22.0, u1=6.)
116-
assert_allclose(result, 37.308, 0.001)
116+
assert_allclose(result, 37.308, atol=0.001)
117117

118118

119119
def test_faiman_list():
@@ -122,7 +122,7 @@ def test_faiman_list():
122122
winds = [10, 5, 0]
123123
result = temperature.faiman(irrads, temps, wind_speed=winds)
124124
expected = np.array([0.0, 18.446, 5.0])
125-
assert_allclose(expected, result, 3)
125+
assert_allclose(expected, result, atol=0.001)
126126

127127

128128
def test_faiman_ndarray():
@@ -131,7 +131,32 @@ def test_faiman_ndarray():
131131
winds = np.array([10, 5, 0])
132132
result = temperature.faiman(irrads, temps, wind_speed=winds)
133133
expected = np.array([0.0, 18.446, 5.0])
134-
assert_allclose(expected, result, 3)
134+
assert_allclose(expected, result, atol=0.001)
135+
136+
137+
def test_faiman_rad_no_ir():
138+
expected = temperature.faiman(900, 20, 5)
139+
result = temperature.faiman_rad(900, 20, 5)
140+
assert_allclose(result, expected)
141+
142+
143+
def test_faiman_rad_ir():
144+
ir_down = np.array([0, 100, 200, 315.6574, 400])
145+
expected = [-11.111, -7.591, -4.071, -0.000, 2.969]
146+
result = temperature.faiman_rad(0, 0, 0, ir_down)
147+
assert_allclose(result, expected, atol=0.001)
148+
149+
sky_view = np.array([1.0, 0.5, 0.0])
150+
expected = [-4.071, -2.036, 0.000]
151+
result = temperature.faiman_rad(0, 0, 0, ir_down=200,
152+
sky_view=sky_view)
153+
assert_allclose(result, expected, atol=0.001)
154+
155+
emissivity = np.array([1.0, 0.88, 0.5, 0.0])
156+
expected = [-4.626, -4.071, -2.313, 0.000]
157+
result = temperature.faiman_rad(0, 0, 0, ir_down=200,
158+
emissivity=emissivity)
159+
assert_allclose(result, expected, atol=0.001)
135160

136161

137162
def test_ross():

0 commit comments

Comments
 (0)