diff --git a/docs/sphinx/source/reference/floating.rst b/docs/sphinx/source/reference/floating.rst new file mode 100644 index 0000000000..5fa22a6049 --- /dev/null +++ b/docs/sphinx/source/reference/floating.rst @@ -0,0 +1,12 @@ +.. currentmodule:: pvlib + +Floating +======== + +Functions +--------- + +.. autosummary:: + :toctree: generated/ + + floating.stream_temperature_stefan diff --git a/docs/sphinx/source/reference/index.rst b/docs/sphinx/source/reference/index.rst index 8a990ac923..92de6cc6fe 100644 --- a/docs/sphinx/source/reference/index.rst +++ b/docs/sphinx/source/reference/index.rst @@ -21,3 +21,4 @@ API reference scaling location transformer + floating diff --git a/docs/sphinx/source/whatsnew/v0.11.1.rst b/docs/sphinx/source/whatsnew/v0.11.1.rst index 7c8f01743e..1db92ed0d2 100644 --- a/docs/sphinx/source/whatsnew/v0.11.1.rst +++ b/docs/sphinx/source/whatsnew/v0.11.1.rst @@ -10,6 +10,12 @@ Deprecations Enhancements ~~~~~~~~~~~~ +* Add function :py:func:`pvlib.floating.stream_temperature_stefan`, to calculate the + water temperature for streams and rivers. + (:pull:`2104`) +* Add new losses function that accounts for non-uniform irradiance on bifacial + modules, :py:func:`pvlib.bifacial.power_mismatch_deline`. + (:issue:`2045`, :pull:`2046`) * Add new function to calculate the average photon energy, :py:func:`pvlib.spectrum.average_photon_energy`. (:issue:`2135`, :pull:`2140`) @@ -31,6 +37,7 @@ Enhancements :py:func:`pvlib.atmosphere.windspeed_powerlaw`. (:issue:`2118`, :pull:`2124`) + Bug fixes ~~~~~~~~~ diff --git a/pvlib/__init__.py b/pvlib/__init__.py index b5b07866a4..74f39a075a 100644 --- a/pvlib/__init__.py +++ b/pvlib/__init__.py @@ -8,6 +8,7 @@ atmosphere, bifacial, clearsky, + floating, iam, inverter, iotools, diff --git a/pvlib/floating.py b/pvlib/floating.py new file mode 100644 index 0000000000..156fb26570 --- /dev/null +++ b/pvlib/floating.py @@ -0,0 +1,65 @@ +""" +The ``floating`` module contains functions for calculating parameters related +to floating PV systems. +""" + +import numpy as np +import pandas as pd + + +def stream_temperature_stefan(temp_air): + r""" + Estimate daily stream water temperature from daily ambient air temperature. + + Parameters + ---------- + temp_air : numeric + Daily average ambient dry bulb temperature. [°C] + + Returns + ------- + water_temperature : numeric + Daily average stream water temperature. [°C] + + Notes + ----- + Daily average stream water temperature + :math:`T_w` is calculated from daily ambient air temperature + :math:`T_{air}` as provided in [1]_: + + .. math:: + + T_w = 5 + 0.75 \cdot T_{air} + + The predicted daily stream water temperatures had a + standard deviation of 2.7 °C compared to measurements. Small, shallow + streams had smaller deviations than large, deep rivers. + + It should be noted that this equation is limited to streams, i.e., water + bodies that are well mixed in the vertical and transverse directions of a + cross section. Also, it is only tested on ice-free streams. Consequently, + when the mean ambient air temperature is lower than -6.66 °C, the surface + stream water temperature is returned as ``np.nan``. + +..warning:: + This model was developed to estimate stream temperature. A number of + a number of publications have used it to estimate lakewater and + seawater temperatures for floating PV without evidence of validity + for such applications. + + References + ---------- + .. [1] Stefan H. G., Preud'homme E. B. (1993). "Stream temperature + estimation from air temperature." Journal of the American Water + Resources Association 29-1: 27-45. + :doi:`10.1111/j.1752-1688.1993.tb01502.x` + """ + + temp_stream = 5 + 0.75 * temp_air + + temp_stream = np.where(temp_stream < 0, np.nan, temp_stream) + + if isinstance(temp_air, pd.Series): + temp_stream = pd.Series(temp_stream, index=temp_air.index) + + return temp_stream diff --git a/pvlib/tests/test_floating.py b/pvlib/tests/test_floating.py new file mode 100644 index 0000000000..2f5278887f --- /dev/null +++ b/pvlib/tests/test_floating.py @@ -0,0 +1,43 @@ +import numpy as np +import pandas as pd +from pvlib import floating + +from .conftest import assert_series_equal +from numpy.testing import assert_allclose +import pytest + + +@pytest.mark.parametrize('air_temp,water_temp', [ + (-15, np.nan), + (-5, 1.25), + (40, 35), +]) +def test_stream_temperature_stefan(air_temp, water_temp): + result = floating.stream_temperature_stefan(air_temp) + assert_allclose(result, water_temp) + + +@pytest.fixture +def times(): + return pd.date_range(start="2015-01-01 00:00", end="2015-01-07 00:00", + freq="1d") + + +@pytest.fixture +def air_temps(times): + return pd.Series([-15, -5, 2.5, 15, 20, 30, 40], index=times) + + +@pytest.fixture +def water_temps_expected(times): + return pd.Series([np.nan, 1.25, 6.875, 16.25, 20, 27.5, 35], index=times) + + +def test_stream_temperature_stefan_ndarray(air_temps, water_temps_expected): + result = floating.stream_temperature_stefan(temp_air=air_temps.to_numpy()) + assert_allclose(water_temps_expected.to_numpy(), result) + + +def test_stream_temperature_stefan_series(air_temps, water_temps_expected): + result = floating.stream_temperature_stefan(temp_air=air_temps) + assert_series_equal(water_temps_expected, result)