diff --git a/docs/sphinx/source/reference/pv_modeling/temperature.rst b/docs/sphinx/source/reference/pv_modeling/temperature.rst index 4c0c7c596b..ff50546154 100644 --- a/docs/sphinx/source/reference/pv_modeling/temperature.rst +++ b/docs/sphinx/source/reference/pv_modeling/temperature.rst @@ -18,6 +18,7 @@ PV temperature models temperature.noct_sam temperature.prilliman pvsystem.PVSystem.get_cell_temperature + temperature.lindholm_floating temperature.generic_linear temperature.GenericLinearModel diff --git a/pvlib/temperature.py b/pvlib/temperature.py index 6269014969..266602fae8 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -1092,6 +1092,208 @@ def prilliman(temp_cell, wind_speed, unit_mass=11.1, coefficients=None): return smoothed +def lindholm_floating(poa_global, temp_air, characteristic_length, + wind_speed, temp_module_front, temp_module_back, + temp_water, water_velocity, temp_sky, + kinematic_viscocity_water=0.000001189, + lamda_water=0.596, + prandtl_water=8.2, + glass_thickness_front=0.0025, + glass_thickness_back=0.0025, + encapsulant_thickness_front=0.0004, + encapsulant_thickness_back=0.0004, + wafer_thickness=0.000175, + glass_conductivity_front=1.8, + glass_conductivity_back=1.8, + encapsulant_conductivity_front=0.21, + encapsulant_conductivity_back=0.21, + wafer_conductivity=148, + membrane_thickness=0.001, + membrane_conductivity=0.2, + module_efficiency=0.1, alpha_absorption=0.9, + glass_transmittance=1, glass_cover_emissivity=0.89, + flat_module=False): + r""" + Calculate cell temperature for floating PV systems. + + Systems can be either in direct contact with the water + or elevated modules using the heat transfer + energy balance model developed by Lindholm et al. [1]_. + + Implementation details are provided in [2]_. + + Parameters + ---------- + poa_global : numeric + Total incident irradiance. [W/m^2] + + temp_air : numeric + Ambient dry bulb temperature. [C] + + characteristic_length : float + Characterisitc length of the PV module. Panel width or height + depnding on wind direction. [m] + + wind_speed : numeric + Wind speed measured at panel height. [m/s] + + temp_module_front : numeric + Front surface temperature of PV module. [C] + + temp_module_back : numeric + Back surface temperature of PV module. [C] + + temp_water : numeric + Water temperature. [C] + + water_velocity : numeric + Water velocity. [m/s] + + temp_sky : numeric + Sky temperature. [C] + + kinematic_viscocity_water : numeric, default 0.000001189 + Kinematic viscocity of seawater. Default value is for seawater + at 15 °C. [m^2/s] + + lamda_water : numeric, default 0.596 + Thermal conductivity of seawater. Default value is for seawater + at 15 °C. [W/(m K)] + + prandtl_water : numeric, default 8.2 + Prandtl number for seawater. Default value is for seawater + at 15 °C. [W/(m K)] + + glass_thickness_front : float, default 0.0025 + Thickness of the front glass of PV module. [m] + + glass_thickness_back : float, default 0.0025 + Thickness of the back glass of PV module. [m] + + encapsulant_thickness_front : float, default 0.0004 + Thickness of the front encapsulant of PV module. [m] + + encapsulant_thickness_back : float, default 0.0004 + Thickness of the back encapsulant of PV module. [m] + + wafer_thickness : float, default 0.000175 + Thickness of the wafer of PV module. [m] + + glass_conductivity_front : float, default 1.8 + Thermal conductivity of the front glass of PV module. [W/(m K)] + + glass_conductivity_back : float, default 1.8 + Thermal conductivity of the back glass of PV module. [W/(m K)] + + encapsulant_conductivity_front : float, default 0.21 + Thermal conductivity of the front encapsulant of PV module. [W/(m K)] + + encapsulant_conductivity_back : float, default 0.21 + Thermal conductivity of the back encapsulant of PV module. [W/(m K)] + + wafer_conductivity : float, default 148 + Thermal conductivity of the wafer of PV module. [W/(m K)] + + membrane_thickness : float, default 0.001 + Thickness of the membrane placed between the floating modules + and water surface. [m] + + membrane_conductivity : float, default 0.2 + Thermal conductivity of the membrane placed between the floating + modules and water surface. [W/(m K)] + + module_efficiency : numeric, default 0.1 + Module external efficiency as a fraction. See + :py:func:`pvlib.temprature.pvsyst_cell`. + + alpha_absorption : float, default 0.9 + Absorption coefficient. See :py:func:`pvlib.temprature.pvsyst_cell`. + + glass_transmittance : float, default 1 + Transmittance of glass covering the PV module. + + glass_cover_emissivity : float, default 0.89 + Emissivity of the glass covering the PV module. + + flat_module : boolean, default False + Back surface of the module is in contact with the water. + + Returns + ------- + numeric, values in degrees Celsius + + References + ---------- + .. [1] Lindholm, D. et al. (2021). "Heat loss coefficients computed + for floating PV modules." Progress in Photovoltaics 29: 1262-1273. + :doi:`10.1002/pip.3451` + + .. [2] Kjeldstad, T. et al. (2021). "Cooling of floating photovoltaics + and the importance of water temperature." Solar Energy 218: 544-551. + :doi:`10.1016/j.solener.2021.03.022` + """ + + # covective heat transfer coefficient to ambient air + # https://www.sciencedirect.com/science/article/pii/S0038092X21002085 + # this expression is valid for wind speeds lower than 7 m/s + h_air = 2.8 + 3 * wind_speed + + # radiative heat transfer coefficient to the sky + h_air_rad = (glass_cover_emissivity * 5.67*10**(-8) * + (temp_module_front + temp_sky) * + (temp_module_front**2 + temp_sky**2)) + + # Reynolds number + Re = water_velocity * characteristic_length / kinematic_viscocity_water + # Nusselt number + Nu = 0.664 * Re**(1/2) * prandtl_water**(1/3) + # convective heat transfer coefficient to water + h_water = Nu * lamda_water / characteristic_length + + # radiative heat transfer coefficient to water + h_water_rad = (glass_cover_emissivity * 5.67*10**(-8) * + (temp_module_back + temp_water) * + (temp_module_back**2 + temp_water**2)) + + # Thermal resistance of front area of PV module in (m^2 K)/W + A_front = 1 / ( + glass_thickness_front / glass_conductivity_front + + encapsulant_thickness_front / encapsulant_conductivity_front + + (wafer_thickness / 2) / wafer_conductivity + ) + + B_front = A_front + h_air + h_air_rad + + C_front = h_air * temp_air + h_air_rad * temp_sky + + # Thermal resistance of back area of PV module in (m^2 K)/W + A_back = 1 / ( + membrane_thickness / membrane_conductivity + + glass_thickness_back / glass_conductivity_back + + encapsulant_thickness_back / encapsulant_conductivity_back + + (wafer_thickness / 2) / wafer_conductivity + ) + + # if the PV module lays flat on the water there is no radiative heat flux + # toward the water + if flat_module: + B_back = A_back + h_water + C_back = h_water * temp_water + else: + B_back = A_back + h_water + h_water_rad + C_back = h_water * temp_air + h_water_rad * temp_water + + # Cell temperature + temp_cell = ((B_front * B_back * + (alpha_absorption * glass_transmittance - module_efficiency) * + poa_global + A_front * B_back * C_front + + A_back * B_front * C_back) / + (B_front * B_back * (A_back + A_front) - + A_back**2 * B_front - A_front**2 * B_back)) + + return temp_cell + + def generic_linear(poa_global, temp_air, wind_speed, u_const, du_wind, module_efficiency, absorptance): """ diff --git a/pvlib/tests/test_temperature.py b/pvlib/tests/test_temperature.py index 18560501fb..281a84fafc 100644 --- a/pvlib/tests/test_temperature.py +++ b/pvlib/tests/test_temperature.py @@ -468,3 +468,108 @@ def test_glm_repr(): "'alpha': 0.9}") assert glm.__repr__() == expected + + +def test_lindholm_floating_default(): + result = temperature.lindholm_floating(poa_global=800, + temp_air=20, + characteristic_length=2, + wind_speed=1, + temp_module_front=24, + temp_module_back=23, + temp_water=16, + water_velocity=0.1, + temp_sky=5) + assert_allclose(result, 28.520, 0.001) + + +def test_lindholm_floating_flat_module(): + result = temperature.lindholm_floating(poa_global=800, + temp_air=20, + characteristic_length=2, + wind_speed=1, + temp_module_front=24, + temp_module_back=23, + temp_water=16, + water_velocity=0.1, + temp_sky=5, + flat_module=True) + assert_allclose(result, 24.823, 0.001) + + +def test_lindholm_floating_kwargs(): + result = temperature.lindholm_floating(poa_global=800, + temp_air=20, + characteristic_length=2, + wind_speed=1, + temp_module_front=24, + temp_module_back=23, + temp_water=16, + water_velocity=0.1, + temp_sky=5, + kinematic_viscocity_water=0.0000012, + lamda_water=0.6, + prandtl_water=9, + glass_thickness_front=0.002, + glass_thickness_back=0.002, + encapsulant_thickness_front=0.0002, + encapsulant_thickness_back=0.0002, + wafer_thickness=0.00016, + glass_conductivity_front=2, + glass_conductivity_back=2, + encapsulant_conductivity_front=0.25, + encapsulant_conductivity_back=0.25, + wafer_conductivity=155, + membrane_thickness=0.002, + membrane_conductivity=0.3, + module_efficiency=0.2, + alpha_absorption=0.95, + glass_transmittance=0.98, + glass_cover_emissivity=0.95) + assert_allclose(result, 27.767, 0.001) + + +def test_lindholm_floating_ndarray(): + temps = np.array([0, 10, 25]) + irrads = np.array([0, 250, 600]) + winds = np.array([0, 3.5, 7]) + front_temps = np.array([0, 25, 40]) + back_temps = np.array([0, 25, 30]) + water_temps = np.array([5, 10, 20]) + water_speeds = np.array([1, 0.1, 0]) + sky_temps = np.array([0, 2, 5]) + result = temperature.lindholm_floating(poa_global=irrads, + temp_air=temps, + characteristic_length=2, + wind_speed=winds, + temp_module_front=front_temps, + temp_module_back=back_temps, + temp_water=water_temps, + water_velocity=water_speeds, + temp_sky=sky_temps) + expected = np.array([0.0, 12.4341, 46.7391]) + assert_allclose(expected, result, 3) + + +def test_lindholm_floating_series(): + times = pd.date_range(start="2015-01-01 00:00", end="2015-01-01 12:00", + freq="6h") + temps = pd.Series([0, 10, 25], index=times) + irrads = pd.Series([0, 250, 600], index=times) + winds = pd.Series([0, 3.5, 7], index=times) + front_temps = pd.Series([0, 25, 40], index=times) + back_temps = pd.Series([0, 25, 30], index=times) + water_temps = pd.Series([5, 10, 20], index=times) + water_speeds = pd.Series([1, 0.1, 0], index=times) + sky_temps = pd.Series([0, 2, 5], index=times) + result = temperature.lindholm_floating(poa_global=irrads, + temp_air=temps, + characteristic_length=2, + wind_speed=winds, + temp_module_front=front_temps, + temp_module_back=back_temps, + temp_water=water_temps, + water_velocity=water_speeds, + temp_sky=sky_temps) + expected = pd.Series([0.0, 12.4341, 46.7391], index=times) + assert_series_equal(expected, result)