Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions docs/sphinx/source/whatsnew/v0.13.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Bug fixes

Enhancements
~~~~~~~~~~~~
* Add k coefficient in :py:func:`~pvlib.temperature.ross`
(:issue:`2506`, :pull:`2521`)
* Add iotools functions to retrieve irradiance and weather data from Meteonorm:
:py:func:`~pvlib.iotools.get_meteonorm_forecast_basic`, :py:func:`~pvlib.iotools.get_meteonorm_forecast_precision`,
:py:func:`~pvlib.iotools.get_meteonorm_observation_realtime`, :py:func:`~pvlib.iotools.get_meteonorm_observation_training`,
Expand Down Expand Up @@ -65,5 +67,6 @@ Contributors
* Ioannis Sifnaios (:ghuser:`IoannisSifnaios`)
* Rajiv Daxini (:ghuser:`RDaxini`)
* Omar Bahamida (:ghuser:`OmarBahamida`)
* Rodrigo Amaro e Silva (:ghuser:`ramaroesilva`)
* Kevin Anderson (:ghuser:`kandersolar`)
* Mikaella Brewer (:ghuser:`brwerx`)
68 changes: 58 additions & 10 deletions pvlib/temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ def faiman_rad(poa_global, temp_air, wind_speed=1.0, ir_down=None,
return temp_air + temp_difference


def ross(poa_global, temp_air, noct):
def ross(poa_global, temp_air, noct=None, k=None):
r'''
Calculate cell temperature using the Ross model.

Expand All @@ -630,14 +630,19 @@ def ross(poa_global, temp_air, noct):
Parameters
----------
poa_global : numeric
Total incident irradiance. [W/m^2]
Total incident irradiance. [W/m⁻²]

temp_air : numeric
Ambient dry bulb temperature. [C]

noct : numeric
noct : numeric, optional
Nominal operating cell temperature [C], determined at conditions of
800 W/m^2 irradiance, 20 C ambient air temperature and 1 m/s wind.
800 W/m⁻² irradiance, 20 C ambient air temperature and 1 m/s wind.
If ``noct`` is not provided, ``k`` is required.
k: numeric, optional
Ross coefficient [Km²W⁻¹], which is an alternative to employing
NOCT in Ross's equation. If ``k`` is not provided, ``noct`` is
required.

Returns
-------
Expand All @@ -650,19 +655,62 @@ def ross(poa_global, temp_air, noct):

.. math::

T_{C} = T_{a} + \frac{NOCT - 20}{80} S

where :math:`S` is the plane of array irradiance in :math:`mW/{cm}^2`.
This function expects irradiance in :math:`W/m^2`.
T_{C} = T_{a} + \frac{NOCT - 20}{80} S = T_{a} + k × S

where :math:`S` is the plane of array irradiance in mWcm⁻².
This function expects irradiance in Wm⁻².

Representative values for k are provided in [2]_, covering different types
of mounting and degrees of back ventialtion. The naming designations,
however, are adapted from [3]_ to enhance clarity and usability.

+--------------------------------------+-----------+
| Mounting | :math:`k` |
+======================================+===========+
| Sloped roof, well ventilated | 0.02 |
+--------------------------------------+-----------+
| Free-standing system | 0.0208 |
+--------------------------------------+-----------+
| Flat roof, well ventilated | 0.026 |
+--------------------------------------+-----------+
| Sloped roof, poorly ventilated | 0.0342 |
+--------------------------------------+-----------+
| Facade integrated, semi-ventilated | 0.0455 |
+--------------------------------------+-----------+
| Facade integrated, poorly ventilated | 0.0538 |
+--------------------------------------+-----------+
| Sloped roof, non-ventilated | 0.0563 |
+--------------------------------------+-----------+

It is also worth noting that the semi-ventilated facade case refers to
partly transparent compound glass insulation modules, while the non-
ventilated case corresponds to opaque, insulated PV-cladding elements.
However, the emphasis in [3]_ appears to be on ventilation conditions
rather than module construction.

References
----------
.. [1] Ross, R. G. Jr., (1981). "Design Techniques for Flat-Plate
Photovoltaic Arrays". 15th IEEE Photovoltaic Specialist Conference,
Orlando, FL.
.. [2] E. Skoplaki and J. A. Palyvos, "Operating temperature of
photovoltaic modules: A survey of pertinent correlations," Renewable
Energy, vol. 34, no. 1, pp. 23–29, Jan. 2009,
:doi:`10.1016/j.renene.2008.04.009`
.. [3] T. Nordmann and L. Clavadetscher, "Understanding temperature
effects on PV system performance," Proceedings of 3rd World Conference
on Photovoltaic Energy Conversion, May 2003.
'''
# factor of 0.1 converts irradiance from W/m2 to mW/cm2
return temp_air + (noct - 20.) / 80. * poa_global * 0.1
if (noct is None) & (k is None):
raise ValueError("Either noct or k is required.")
elif (noct is not None) & (k is not None):
raise ValueError("Provide only one of noct or k, not both.")
elif k is None:
# factor of 0.1 converts irradiance from W/m2 to mW/cm2
return temp_air + (noct - 20.) / 80. * poa_global * 0.1
elif noct is None:
# k assumes irradiance in W.m-2, dismissing 0.1 factor
return temp_air + k * poa_global


def _fuentes_hconv(tave, windmod, tinoct, temp_delta, xlen, tilt,
Expand Down
44 changes: 39 additions & 5 deletions tests/test_temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,45 @@ def test_faiman_rad_ir():


def test_ross():
result = temperature.ross(np.array([1000., 600., 1000.]),
np.array([20., 40., 60.]),
np.array([40., 100., 20.]))
expected = np.array([45., 100., 60.])
assert_allclose(expected, result)
# single values
result1 = temperature.ross(1000., 30., noct=50)
result2 = temperature.ross(1000., 30., k=0.0375)

expected = 67.5
assert_allclose(expected, result1)
assert_allclose(expected, result2)

# pd.Series
times = pd.date_range('2025-07-30 14:00', '2025-07-30 16:00', freq='h')

df = pd.DataFrame({'t_air': np.array([20., 30., 40.]),
'ghi': np.array([800., 700., 600.])},
index=times)

result1 = temperature.ross(df['ghi'], df['t_air'], noct=50.)
result2 = temperature.ross(df['ghi'], df['t_air'], k=0.0375)

expected = pd.Series([50., 56.25, 62.5], index=times)
assert_allclose(expected, result1)
assert_allclose(expected, result2)

# np.array
ghi_array = df['ghi'].values
t_air_array = df['t_air'].values

result1 = temperature.ross(ghi_array, t_air_array, noct=50.)
result2 = temperature.ross(ghi_array, t_air_array, k=0.0375)

expected = expected.values
assert_allclose(expected, result1)
assert_allclose(expected, result2)


def test_ross_errors():
with pytest.raises(ValueError, match='Either noct or k is required'):
temperature.ross(1000., 30.)
with pytest.raises(ValueError, match='Provide only one of noct or k'):
temperature.ross(1000., 30., noct=45., k=0.02)


def test_faiman_series():
Expand Down
Loading