Skip to content

Adds k parameter to temperature.ross #2521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
10 changes: 7 additions & 3 deletions docs/sphinx/source/whatsnew/v0.13.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ Bug fixes

Enhancements
~~~~~~~~~~~~


* Rename parameter name ``aparent_azimuth`` to ``solar_azimuth`` in :py:func:`~pvlib.tracking.singleaxis`.
(:issue:`2479`, :pull:`2480`)
* Add k coefficient in :py:func:`~pvlib.temperature.ross`
(:issue:`2506`, :pull:`2521`)

Documentation
~~~~~~~~~~~~~
* Substantiate definitions of solar/surface azimuth/zenith and aoi on the
Expand Down Expand Up @@ -48,4 +51,5 @@ Contributors
~~~~~~~~~~~~
* Elijah Passmore (:ghuser:`eljpsm`)
* Rajiv Daxini (:ghuser:`RDaxini`)
* Omar Bahamida (:ghuser:`OmarBahamida`)
* Omar Bahamida (:ghuser:`OmarBahamida`)
* Rodrigo Amaro e Silva (:ghuser:`ramaroesilva`)
44 changes: 40 additions & 4 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 @@ -638,6 +638,9 @@ def ross(poa_global, temp_air, noct):
noct : numeric
Nominal operating cell temperature [C], determined at conditions of
800 W/m^2 irradiance, 20 C ambient air temperature and 1 m/s wind.
Comment on lines 638 to 640
Copy link
Member

Choose a reason for hiding this comment

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

It's good to keep PR scope focused, but our units convention was only recently introduced and we aim to update the documentation gradually. Since you're working on this function anyway, I don't think anyone would object to (personally, I would encourage) updating the formatting of these units here and elsewhere in this function. That said, anyone, feel free to object

Suggested change
noct : numeric
Nominal operating cell temperature [C], determined at conditions of
800 W/m^2 irradiance, 20 C ambient air temperature and 1 m/s wind.
noct : numeric
Nominal operating cell temperature [°C], determined at conditions of
800 Wm⁻² irradiance, 20 °C ambient air temperature and 1 ms⁻¹ wind speed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the initiative @RDaxini. I thought about that but then I noticed that the issue applies to the whole file. So to make the PR more focused, I kept the original formatting and moved that to a separate issue (#2519).

If that's okay for you, I would leave this PR with the Adds » Add and correct the doi formatting (which I've already commited).

Next week I will address the unit styling in #2519, feel free to join the discussion. There I'm proposing to have a . between units (e.g. W.m⁻²) to make reading easier, and to present the Ross equation with 800 in the denominator to shift from the historically cell-motivated mW.cm⁻² to a more module-friendly W.m⁻² (which also dismisses the current 0.1 factor to match the irradiance unit).

Copy link
Member

Choose a reason for hiding this comment

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

I agree, let's do edits for style in #2519 for all of temperature.py.

Copy link
Member

Choose a reason for hiding this comment

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

If there is already work planned to do this separately then that's fine

k: numeric
Ross coefficient [Km^2/W], which is an alternative to employing
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Ross coefficient [Km^2/W], which is an alternative to employing
Ross coefficient [Km²W⁻¹], which is an alternative to employing

NOCT in Ross's equation.

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

.. math::

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

where :math:`S` is the plane of array irradiance in :math:`mW/{cm}^2`.
This function expects irradiance in :math:`W/m^2`.
Comment on lines 658 to 659
Copy link
Member

Choose a reason for hiding this comment

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

Let others comment, but I think Wm⁻² looks cleaner than the larger font bolt+italic math type for units. Might be worth saving that for variables(?)
At least if sticking with :math: then I think superscript would be better than a /: `:math:`mW{cm}^{-2}

Suggested change
where :math:`S` is the plane of array irradiance in :math:`mW/{cm}^2`.
This function expects irradiance in :math:`W/m^2`.
where :math:`S` is the plane of array irradiance in mWm⁻².
This function expects irradiance in Wm⁻².


Representative values for k are provided in [2]_, covering different types
of mounting and module structure.

+---------------------+-----------+
| Mounting | :math:`k` |
+=====================+===========+
| Well cooled | 0.02 |
+---------------------+-----------+
| Free standing | 0.0208 |
+---------------------+-----------+
| Flat on roof | 0.026 |
+---------------------+-----------+
| Not so well cooled | 0.0342 |
+---------------------+-----------+
| Transparent pv | 0.0455 |
+---------------------+-----------+
| Facade integrated | 0.0538 |
+---------------------+-----------+
| On sloped roof | 0.0563 |
+---------------------+-----------+

Copy link
Member

Choose a reason for hiding this comment

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

I feel like we'll be asked what is meant by some of these descriptors: "not so well cooled", "on sloped roof" - is that a sloped roof, or modules tilted up away from the roof? I've got a copy of the paper that [2] references, I'll leave a comment with suggested text to add.

Copy link
Member

@cwhanse cwhanse Aug 1, 2025

Choose a reason for hiding this comment

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

On sloped roofs:
Well cooled: On a sloped roof, gap between modules and the mounting surface allows for convective cooling.
Not so well cooled: On a sloped roof, air flow is restricted between the modules and the mounting surface.

On flat roofs:
Free standing: On a flat roof, gap between modules and the mounting surface allows for convective cooling.
Flat on roof: On a flat roof, air flow is restricted between the modules and the mounting surface.

Building integrated:
On sloped roof: modules integrated with the roof/mounting surface, without air flow cooling the underside.
Facade integrated: opaque modules integrated with a facade, with air flow cooling the inner surface of the mounting structure.
Transparent PV: I have no idea from [2] or [3] what this describes. transparent modules integrated with a facade, with air flow cooling the inner surface of the mounting structure.

Copy link
Contributor Author

@ramaroesilva ramaroesilva Aug 1, 2025

Choose a reason for hiding this comment

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

Thanks for your effort @cwhanse and for pointing out the unclear designations!

I disagree with the author flat on roof designation, which I believe misled your interpretation. The original reference mentions

Freestanding and flat roof systems usually allow a free airflow around the modules and have therefore lower temperature losses.

This makes me think both are tilted modules, but one on the ground and another on top of a flat roof.

Copy link
Member

Choose a reason for hiding this comment

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

This makes me think both are tilted modules, but one on the ground and another on top of a flat roof.

I concur.

Copy link
Contributor Author

@ramaroesilva ramaroesilva Aug 1, 2025

Choose a reason for hiding this comment

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

Instead I would propose a new, more self-explanatory nomenclature, mentioning the original reference as inspiration (with the values made explicit in our documentation, it should be easy for people to make the correspondance with the Skoplaki publication).

Well cooled » Sloped roof, well ventilated
Free standing » Free-standing system
Flat on roof » Flat roof, well ventilated
Not so well cooled » Sloped roof, poorly ventilation
Transparent PV » Facade, semi-ventilated
Facade integrated » Facade, poorly ventilated
On sloped roof » Sloped roof, non-ventilated

I hesitated on removing the transparent part of the name, but the author seems to point out more to a possible interior airflow rather than the transparency. Maybe we can add a note referring that the two facades also differed in the materials/transparency of the module structure.

Would also include a note mentioning that the ventilation in the namings refers to the back side of the module (and, thus, the spacing available with the surface).

Copy link
Member

Choose a reason for hiding this comment

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

I agree, ventilation is the primary distinction, rather than level of transparency of the module itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cwhanse documentation improved in latest commit, let me know what you think. And thanks for the brainstorming! :-)

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`
'''
# 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 need is required.")
elif (noct is not None) & (k is not None):
raise ValueError("Provide only one of noct or k, not both.")
Comment on lines +702 to +705
Copy link
Member

Choose a reason for hiding this comment

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

The codecov checks on this PR fail because these ValueErrors are not covered
You can address this using with pytest.raises():

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you be a bit more explicit? Never used that before.

Also, fyi, there is a similar raise ValueError in the function noct_sam in this same file. Should it also be changed?

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
Copy link
Member

Choose a reason for hiding this comment

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

For testing: please check that the function returns correct values when k is passed. Please test with input float, np.array and pd.Series. Also, please test that both ValueError are raised when the conditions are met.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the types you mention refer to poa_global and air_temperature all seems good. For noct and k I've tested int the first and float for the two.

Copy link
Member

Choose a reason for hiding this comment

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

Please add the tests to test_temperature.py

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@RDaxini tests added :-)



def _fuentes_hconv(tave, windmod, tinoct, temp_delta, xlen, tilt,
Expand Down