diff --git a/pvlib/temperature.py b/pvlib/temperature.py index cb487ee77b..4a09a8819e 100644 --- a/pvlib/temperature.py +++ b/pvlib/temperature.py @@ -713,7 +713,8 @@ def ross(poa_global, temp_air, noct=None, k=None): return temp_air + k * poa_global -def _fuentes_hconv(tave, windmod, temp_delta, xlen, tilt, check_reynold): +def _fuentes_hconv(tave, windmod, tinoct, temp_delta, xlen, tilt, + check_reynold): # Calculate the convective coefficient as in Fuentes 1987 -- a mixture of # free, laminar, and turbulent convection. densair = 0.003484 * 101325.0 / tave # density @@ -835,7 +836,7 @@ def fuentes(poa_global, temp_air, wind_speed, noct_installed, module_height=5, # convective coefficient of top surface of module at NOCT windmod = 1.0 tave = (tinoct + 293.15) / 2 - hconv = _fuentes_hconv(tave, windmod, tinoct - 293.15, xlen, + hconv = _fuentes_hconv(tave, windmod, tinoct, tinoct - 293.15, xlen, surface_tilt, False) # determine the ground temperature ratio and the ratio of the total @@ -895,7 +896,7 @@ def fuentes(poa_global, temp_air, wind_speed, noct_installed, module_height=5, for j in range(10): # overall convective coefficient tave = (tmod + tamb) / 2 - hconv = convrat * _fuentes_hconv(tave, windmod, + hconv = convrat * _fuentes_hconv(tave, windmod, tinoct, abs(tmod-tamb), xlen, surface_tilt, True) # sky radiation coefficient (Equation 3) @@ -1011,10 +1012,9 @@ def noct_sam(poa_global, temp_air, wind_speed, noct, module_efficiency, # - Geff_total (SAM) is POA irradiance after reflections and # adjustment for spectrum. Equivalent to effective_irradiance if effective_irradiance is None: - irr_ratio = 1. + Geff = poa_global else: - irr_ratio = effective_irradiance / poa_global - + Geff= np.maximum(effective_irradiance, 0) if array_height == 1: wind_adj = 0.51 * wind_speed elif array_height == 2: @@ -1022,13 +1022,12 @@ def noct_sam(poa_global, temp_air, wind_speed, noct, module_efficiency, else: raise ValueError( f'array_height must be 1 or 2, {array_height} was given') - noct_adj = noct + _adj_for_mounting_standoff(mount_standoff) - tau_alpha = transmittance_absorptance * irr_ratio - # [1] Eq. 10.37 isn't clear on exactly what "G" is. SAM SSC code uses # poa_global where G appears - cell_temp_init = poa_global / 800. * (noct_adj - 20.) + irr_ratio = Geff / poa_global + cell_temp_init = (poa_global / 800.0) * (noct_adj - 20.0) * irr_ratio + tau_alpha = transmittance_absorptance heat_loss = 1 - module_efficiency / tau_alpha wind_loss = 9.5 / (5.7 + 3.8 * wind_adj) return temp_air + cell_temp_init * heat_loss * wind_loss diff --git a/tests/test_noct_sam_eff_irradiance.py b/tests/test_noct_sam_eff_irradiance.py new file mode 100644 index 0000000000..741e9dbe34 --- /dev/null +++ b/tests/test_noct_sam_eff_irradiance.py @@ -0,0 +1,51 @@ +import numpy as np +from pvlib.temperature import noct_sam + +def test_noct_sam_effective_irr_reduces_temp(): + # Effective irradiance should lower predicted module temp + t_full = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.20 + ) + + t_eff_600 = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.20, + effective_irradiance=600 + ) + + t_eff_200 = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.20, + effective_irradiance=200 + ) + + assert t_eff_600 < t_full + assert t_eff_200 < t_eff_600 + + +def test_noct_sam_oc_case_reduces_temp(): + # Open-circuit (efficiency=0) should still respond to effective irradiance + t_oc_full = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.0 + ) + + t_oc_eff = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.0, + effective_irradiance=300 + ) + + assert t_oc_eff < t_oc_full + + +def test_noct_sam_not_below_ambient_for_small_eff(): + # Very small effective irradiance should not predict cooling below ambient + t_small = noct_sam( + poa_global=800, temp_air=20, wind_speed=2, + noct=45, module_efficiency=0.20, + effective_irradiance=1.0 + ) + + assert t_small >= 20.0 - 1e-6 # small numerical tolerance diff --git a/tests/test_pvsystem.py b/tests/test_pvsystem.py index 4fbd782e65..b5c99e5a04 100644 --- a/tests/test_pvsystem.py +++ b/tests/test_pvsystem.py @@ -522,7 +522,8 @@ def test_PVSystem_noct_celltemp(mocker): temp_model_params.update({'transmittance_absorptance': 0.8, 'array_height': 2, 'mount_standoff': 2.0}) - expected = 60.477703576 + expected =62.877666 + system = pvsystem.PVSystem(temperature_model_parameters=temp_model_params) out = system.get_cell_temperature(poa_global, temp_air, wind_speed, effective_irradiance=1100., diff --git a/tests/test_temperature.py b/tests/test_temperature.py index e482df6214..6a0ad82f70 100644 --- a/tests/test_temperature.py +++ b/tests/test_temperature.py @@ -323,7 +323,7 @@ def test_noct_sam_against_sam(): module_efficiency, effective_irradiance, transmittance_absorptance, array_height, mount_standoff) - expected = 43.0655 + expected = 43.024616 # rtol from limited SAM output precision assert_allclose(result, expected, rtol=1e-5) @@ -339,7 +339,7 @@ def test_noct_sam_options(): module_efficiency, effective_irradiance, transmittance_absorptance, array_height, mount_standoff) - expected = 60.477703576 + expected = 62.877666 assert_allclose(result, expected)