From c2ef481c9305a4740f08337a95c359408b18d874 Mon Sep 17 00:00:00 2001 From: Halvor Lund Date: Thu, 30 May 2024 09:33:13 +0200 Subject: [PATCH] Complete implementation of Cigre 207, extract common solar heating --- .../equations/cigre207/convective_cooling.py | 1 - linerate/equations/cigre207/solar_heating.py | 99 ++---------- linerate/equations/cigre601/solar_heating.py | 34 +--- linerate/equations/solar_heating.py | 33 ++++ linerate/model.py | 4 +- linerate/models/Cigre207.md | 16 +- linerate/models/cigre207.py | 5 +- tests/equations/__init__.py | 0 .../cigre207/test_convective_cooling.py | 150 ++++++++++++++++++ .../equations/cigre601/test_solar_heating.py | 37 ----- tests/equations/test_convective_cooling.py | 36 +++++ tests/equations/test_radiative_cooling.py | 17 ++ tests/equations/test_solar_heating.py | 51 ++++++ 13 files changed, 318 insertions(+), 165 deletions(-) create mode 100644 linerate/equations/solar_heating.py create mode 100644 tests/equations/__init__.py create mode 100644 tests/equations/cigre207/test_convective_cooling.py create mode 100644 tests/equations/test_solar_heating.py diff --git a/linerate/equations/cigre207/convective_cooling.py b/linerate/equations/cigre207/convective_cooling.py index 78e7215..8a0be0a 100644 --- a/linerate/equations/cigre207/convective_cooling.py +++ b/linerate/equations/cigre207/convective_cooling.py @@ -299,7 +299,6 @@ def _compute_horizontal_natural_nusselt_number( elif GrPr < 1e6: return 0.480 * GrPr**0.250 else: - warnings.warn("GrPr out of bounds: Must be < 10^6.") # Outside table range, what should we do here? return 0.125 * GrPr**0.333 diff --git a/linerate/equations/cigre207/solar_heating.py b/linerate/equations/cigre207/solar_heating.py index 5f0cf88..df756fa 100644 --- a/linerate/equations/cigre207/solar_heating.py +++ b/linerate/equations/cigre207/solar_heating.py @@ -1,39 +1,31 @@ import numpy as np from numpy import pi -from ...units import Meter, Unitless, WattPerMeter, WattPerSquareMeter +from .. import cigre601 +from ...units import Meter, Unitless, WattPerSquareMeter def compute_direct_solar_radiation( sin_solar_altitude: Unitless, - clearness_ratio: Unitless, height_above_sea_level: Meter, ) -> WattPerSquareMeter: r"""Compute the direct solar radiation. - Equation (10-11) on page 19 of :cite:p:`cigre601`. Equation (10) states that the direct solar + On page 38 of :cite:p:`cigre601`. Equation (10) states that the direct solar radiation on a surface normal to the solar beam at sea level, :math:`I_{B(0)}`, is given by .. math:: N_s \frac{1280 \sin(H_s)}{\sin(H_s) + 0.314}, - where :math:`N_s` is the clearness ratio which is used to adjust the amount of radiation - compared to what goes through a standard Indian atmosphere, and :math:`H_s` is the solar - altitude. - - While the solar radiation model is based on :cite:p:`sharma1965interrelationships` and - therefore have parameters estimated for an Indian atmosphere, it gives comparable results to - the solar radiation model in the IEEE standard :cite:p:`ieee738`. It is therefore reasonable to - assume that the parameters work in other climates as well. + where :math:`H_s` is the solar altitude. + To correct for height above sea level, we use the Eq. 19 from Cigre 601, since no equation is provided + in Cigre 207. Parameters ---------- sin_solar_altitude: :math:`\sin\left(H_s\right)`. The sine of the solar altitude. - clearness_ratio: - :math:`N_s`. The clearness ratio (or clearness number in - :cite:p:`sharma1965interrelationships,cigre207`). height_above_sea_level: :math:`y~\left[\text{m}\right]`. The conductor's altitude. @@ -41,40 +33,9 @@ def compute_direct_solar_radiation( ------- Union[float, float64, ndarray[Any, dtype[float64]]] :math:`I_B~\left[\text{W}~\text{m}^{-2}\right]`. The direct solar radiation. - - Note - ---- - The 1280 originates and 0.314 in the above equation originates from - :cite:p:`sharma1965interrelationships`, which is cited in :cite:p:`morgan1982thermal` (which is - listed as the reference in :cite:p:`cigre601`). In :cite:p:`sharma1965interrelationships` the - empirical relationship - - .. math:: - - I_{B(0)} = \frac{1.842 \sin(H_s)}{\sin(H_s) + 0.3135}~\text{Ly}~\text{min}^{-1} - - is introduced, and by converting from Langley per minute to :math:`\text{W}~\text{m}^{-2}`, we - obtain - - .. math:: - - I_{B(0)} = N_s \frac{1284.488 \sin(H_s)}{\sin(H_s) + 0.3135}~\text{W}~\text{m}^{-2}, - - which is equal to the equation we use (with three significant digits). """ - sin_H_s = sin_solar_altitude - N_s = clearness_ratio - y = height_above_sea_level - - I_B_0 = N_s * 1280 * sin_H_s / (sin_H_s + 0.314) - # Equation 19 says that - # I_B = I_B_0 * (1 + 1.4e-4 * y * (1367/I_B_0 - 1)) - # However, if I_B_0 = 0, this will divide by 0. To return NaN-values if and only - # if the input is NaN, we therefore reformulate it - scaled_y = 1.4e-4 * y - I_B = I_B_0 * (1 - scaled_y) + 1367 * scaled_y - - return np.where(sin_H_s >= 0, I_B, 0 * I_B) + clearness_ratio = 1.0 + return cigre601.solar_heating.compute_direct_solar_radiation(sin_solar_altitude, clearness_ratio, height_above_sea_level) def compute_diffuse_sky_radiation( @@ -83,9 +44,9 @@ def compute_diffuse_sky_radiation( ) -> WattPerSquareMeter: r"""Compute the diffuse radiation (light scattered in the atmosphere). - Equation (13) on page 20 of :cite:p:`cigre601`. + On page 38 of :cite:p:`cigre207`. - This equation differ from :cite:p:`cigre207`, however the difference is small, and the + This equation differ from :cite:p:`cigre601`, however the difference is small, and the diffuse radiation is a small contributor to the overall solar radiation, so the total discrepancy between the models is small. @@ -103,7 +64,7 @@ def compute_diffuse_sky_radiation( """ sin_H_s = sin_solar_altitude I_B = direct_solar_radiation - return np.maximum(0, (430.5 - 0.3288 * I_B)) * np.maximum(0, sin_H_s) + return np.maximum(0, (570 - 0.47 * I_B)) * np.maximum(0, sin_H_s)**1.2 def compute_global_radiation_intensity( @@ -115,14 +76,14 @@ def compute_global_radiation_intensity( ) -> WattPerSquareMeter: r"""Compute the global radiation intensity experienced by the conductor. - Equation (9) on page 18 of :cite:p:`cigre601` state that the global radiation intensity, + Equation (47) on page 38 of :cite:p:`cigre207` state that the global radiation intensity, :math:`I_T`, is given by .. math:: I_T = I_B \left(\sin(\eta) + 0.5 F \pi \sin(H_s)\right) + - I_d \left(1 + 0.5 F \pi\right), + 0.5 I_d \pi \left(1 + F \right), where :math:`\eta` is the incidence angle of the sun on the line, :math:`H_s` is the solar altitude and :math:`F` is the ground albedo (amount of radiation diffusely reflected from the @@ -181,36 +142,4 @@ def compute_global_radiation_intensity( sin_eta = sin_angle_of_sun_on_line F_pi_half = 0.5 * pi * F - return I_B * (sin_eta + F_pi_half * sin_H_s) + I_d * (1 + F_pi_half) # type: ignore - - -def compute_solar_heating( - absorptivity: Unitless, - global_radiation_intensity: WattPerSquareMeter, - conductor_diameter: Meter, -) -> WattPerMeter: - r"""Compute the solar heating experienced by the conductor. - - Equation (8) on page 18 of :cite:p:`cigre601`. - - Parameters - ---------- - absorptivity: - :math:`\alpha_s`. Material constant. According to :cite:p:`cigre601`, it starts at - approximately 0.2 for new cables and reaches a constant value of approximately 0.9 - after about one year. - global_radiation_intensity: - :math:`I_T~\left[\text{W}~\text{m}^{-2}\right]`.The global radiation intensity. - conductor_diameter: - :math:`D~\left[\text{m}\right]`. Outer diameter of the conductor. - - Returns - ------- - Union[float, float64, ndarray[Any, dtype[float64]]] - :math:`P_S~\left[\text{W}~\text{m}^{-1}\right]`. The solar heating of the conductor - """ - alpha_s = absorptivity - I_T = global_radiation_intensity - D = conductor_diameter - - return alpha_s * I_T * D + return I_B * (sin_eta + F_pi_half * sin_H_s) + I_d * pi/2 * (1 + F) # type: ignore diff --git a/linerate/equations/cigre601/solar_heating.py b/linerate/equations/cigre601/solar_heating.py index cb2c0bc..575920f 100644 --- a/linerate/equations/cigre601/solar_heating.py +++ b/linerate/equations/cigre601/solar_heating.py @@ -85,7 +85,7 @@ def compute_diffuse_sky_radiation( Equation (13) on page 20 of :cite:p:`cigre601`. - This equation differ from :cite:p:`cigre207`, however the difference is small, and the + This equation differs from :cite:p:`cigre207`, however the difference is small, and the diffuse radiation is a small contributor to the overall solar radiation, so the total discrepancy between the models is small. @@ -182,35 +182,3 @@ def compute_global_radiation_intensity( F_pi_half = 0.5 * pi * F return I_B * (sin_eta + F_pi_half * sin_H_s) + I_d * (1 + F_pi_half) # type: ignore - - -def compute_solar_heating( - absorptivity: Unitless, - global_radiation_intensity: WattPerSquareMeter, - conductor_diameter: Meter, -) -> WattPerMeter: - r"""Compute the solar heating experienced by the conductor. - - Equation (8) on page 18 of :cite:p:`cigre601`. - - Parameters - ---------- - absorptivity: - :math:`\alpha_s`. Material constant. According to :cite:p:`cigre601`, it starts at - approximately 0.2 for new cables and reaches a constant value of approximately 0.9 - after about one year. - global_radiation_intensity: - :math:`I_T~\left[\text{W}~\text{m}^{-2}\right]`.The global radiation intensity. - conductor_diameter: - :math:`D~\left[\text{m}\right]`. Outer diameter of the conductor. - - Returns - ------- - Union[float, float64, ndarray[Any, dtype[float64]]] - :math:`P_S~\left[\text{W}~\text{m}^{-1}\right]`. The solar heating of the conductor - """ - alpha_s = absorptivity - I_T = global_radiation_intensity - D = conductor_diameter - - return alpha_s * I_T * D diff --git a/linerate/equations/solar_heating.py b/linerate/equations/solar_heating.py new file mode 100644 index 0000000..0cfe465 --- /dev/null +++ b/linerate/equations/solar_heating.py @@ -0,0 +1,33 @@ +from linerate.units import WattPerSquareMeter, Unitless, Meter, WattPerMeter + + +def compute_solar_heating( + absorptivity: Unitless, + global_radiation_intensity: WattPerSquareMeter, + conductor_diameter: Meter, +) -> WattPerMeter: + r"""Compute the solar heating experienced by the conductor. + + Equation (8) on page 18 of :cite:p:`cigre601` and (11) on page 4 in :cite:p:`cigre207`. + + Parameters + ---------- + absorptivity: + :math:`\alpha_s`. Material constant. According to :cite:p:`cigre601`, it starts at + approximately 0.2 for new cables and reaches a constant value of approximately 0.9 + after about one year. + global_radiation_intensity: + :math:`I_T~\left[\text{W}~\text{m}^{-2}\right]`.The global radiation intensity. + conductor_diameter: + :math:`D~\left[\text{m}\right]`. Outer diameter of the conductor. + + Returns + ------- + Union[float, float64, ndarray[Any, dtype[float64]]] + :math:`P_S~\left[\text{W}~\text{m}^{-1}\right]`. The solar heating of the conductor + """ + alpha_s = absorptivity + I_T = global_radiation_intensity + D = conductor_diameter + + return alpha_s * I_T * D diff --git a/linerate/model.py b/linerate/model.py index ce01631..2a4ad26 100644 --- a/linerate/model.py +++ b/linerate/model.py @@ -19,7 +19,7 @@ joule_heating, math, radiative_cooling, - solar_angles, convective_cooling, dimensionless, + solar_angles, convective_cooling, dimensionless, solar_heating, ) from linerate.equations.math import switch_cos_sin from linerate.types import Span, Weather @@ -355,7 +355,7 @@ def compute_solar_heating( I_T = cigre601.solar_heating.compute_global_radiation_intensity( I_B, I_d, F, sin_eta, sin_H_s ) - return cigre601.solar_heating.compute_solar_heating( + return solar_heating.compute_solar_heating( alpha_s, I_T, D, diff --git a/linerate/models/Cigre207.md b/linerate/models/Cigre207.md index 3912193..62c190a 100644 --- a/linerate/models/Cigre207.md +++ b/linerate/models/Cigre207.md @@ -15,16 +15,22 @@ We do not implement skin effect, since this is included in the AC resistance val ## Magnetic effect The effect of magnetic heating seems to be intertwined with the skin effect in Cigre 207. -To make things simpler and hopefully accurate enough, we use the same linearised model as our implementation of -Cigre 601. -**Maybe we have to come back to this one...** +To make things simpler and hopefully accurate enough, we use the same linearised model for magnetic effects in ACSR +lines as our implementation of Cigre 601. ## Solar heating -Solar heating is the same as for Cigre 601, but with clearness ratio 1. +Cigre 207 assumes that the diffuse radiation is uniformly directed, this differs from Cigre 601. +Apart from this, solar heating is the same as for Cigre 601, with clearness ratio 1. ## Convective cooling + There are a few inconsistencies in the Cigre 207 modelling of convective cooling. The definition of the Reynolds number is given as $Re = \rho_r V D/\nu_f$, where \rho_r is the air density relative to density at sea level. -This factor is not present in the ordinary definition of Reynolds number, so we choose to omit it. \ No newline at end of file +This factor is not present in the ordinary definition of Reynolds number, but seems to indicate that the kinematic +viscosity $\nu_f$ has to be corrected for the air relative density. + +Cigre 207 has a non-smooth transition to low wind speeds (<0.5m/s) at angles of attack less than 45 degrees. +This leads to an increase in cooling as the wind speed drops below 0.5m/s, which seems non-physical. +This discontinuity is removed in Cigre 601. \ No newline at end of file diff --git a/linerate/models/cigre207.py b/linerate/models/cigre207.py index 9f2ae92..5343364 100644 --- a/linerate/models/cigre207.py +++ b/linerate/models/cigre207.py @@ -1,7 +1,8 @@ from abc import abstractmethod from linerate import ThermalModel, Span, Weather -from linerate.equations import solar_angles, cigre601, math, cigre207, dimensionless, convective_cooling, joule_heating +from linerate.equations import solar_angles, cigre601, math, cigre207, dimensionless, convective_cooling, joule_heating, \ + solar_heating from linerate.equations.math import switch_cos_sin from linerate.model import _copy_method_docstring from linerate.units import Date, Celsius, Ampere, WattPerMeter, OhmPerMeter @@ -53,7 +54,7 @@ def compute_solar_heating( I_T = cigre601.solar_heating.compute_global_radiation_intensity( I_B, I_d, F, sin_eta, sin_H_s ) - return cigre601.solar_heating.compute_solar_heating( + return solar_heating.compute_solar_heating( alpha_s, I_T, D, diff --git a/tests/equations/__init__.py b/tests/equations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/equations/cigre207/test_convective_cooling.py b/tests/equations/cigre207/test_convective_cooling.py new file mode 100644 index 0000000..e6b0a7f --- /dev/null +++ b/tests/equations/cigre207/test_convective_cooling.py @@ -0,0 +1,150 @@ +import numpy as np +from pytest import approx +from linerate.equations import cigre207, dimensionless, convective_cooling + + +def test_matches_example1(): + # See Appendix 1, Example 1 in Cigre 207 + y = 1600 + rho_r = cigre207.convective_cooling.compute_relative_air_density(y) + assert rho_r == approx(0.8306, rel=1e-4) + T_s = 57 + T_amb = 40 + T_f = (T_s + T_amb)/2 + nu_f = cigre207.convective_cooling.compute_kinematic_viscosity_of_air(T_f) + assert nu_f == approx(1.78e-5, rel=1e-3) + v = 2 + D = 0.0286 + Re = cigre207.convective_cooling.compute_reynolds_number(v, D, nu_f, rho_r) + assert Re == approx(2670, rel=1e-3) + lambda_f = cigre207.convective_cooling.compute_thermal_conductivity_of_air(T_f) + assert lambda_f == approx(0.0277, 1e-3) + + +def test_matches_example1_nusselt_number(): + # See Appendix 1, Example 1 in Cigre 207 + D = 0.0286 + d = 0.00318 + Rs = dimensionless.compute_conductor_roughness(D, d) + Re = 2670 + Nu_90 = cigre207.convective_cooling.compute_perpendicular_flow_nusseltnumber(Re, Rs) + assert Nu_90 == approx(26.45, 1e-4) + Nu_45 = cigre207.convective_cooling.correct_wind_direction_effect_on_nusselt_number(Nu_90, np.radians(45)) + assert Nu_45 == approx(22.34, rel=1e-4) + + +def test_matches_example2(): + # See Appendix 1, Example 2 in Cigre 207 + y = 1600 + rho_r = cigre207.convective_cooling.compute_relative_air_density(y) + T_s = 93 + T_amb = 40 + v = 0.2 + D = 0.0286 + T_f = (T_s + T_amb)/2 + nu_f = cigre207.convective_cooling.compute_kinematic_viscosity_of_air(T_f) + nu_f_exp = 1.95e-5 + assert nu_f == approx(nu_f_exp, rel=1e-3) + Re = cigre207.convective_cooling.compute_reynolds_number(v, D, nu_f, rho_r) + assert Re == approx(243.8, rel=2e-3) + lambda_f = cigre207.convective_cooling.compute_thermal_conductivity_of_air(T_f) + assert lambda_f == approx(0.0290, 1e-3) + d = 0.00318 + Rs = dimensionless.compute_conductor_roughness(D, d) + Nu_90 = cigre207.convective_cooling.compute_perpendicular_flow_nusseltnumber(Re, Rs) + assert Nu_90 == approx(8.53, 1e-3) + Nu_45 = cigre207.convective_cooling.correct_wind_direction_effect_on_nusselt_number(Nu_90, np.radians(45)) + assert Nu_45 == approx(7.20, rel=2e-3) + Nu_cor = cigre207.convective_cooling.compute_low_wind_speed_nusseltnumber(Nu_90) + assert Nu_cor == approx(4.69, rel=1e-3) + Gr = dimensionless.compute_grashof_number(D, T_s, T_amb, nu_f_exp) + assert Gr == approx(94387, rel=3e-3) + Pr = cigre207.convective_cooling.compute_prandtl_number(T_f) + assert Pr == approx(0.698, rel=1e-3) + Nu_natural = cigre207.convective_cooling.compute_horizontal_natural_nusselt_number(Gr, Pr) + assert Nu_natural == approx(7.69, rel=1e-3) + Nu_eff = cigre207.convective_cooling.compute_nusselt_number(Nu_45, Nu_natural, Nu_cor, v) + assert Nu_eff == approx(7.69, rel=1e-3) + + +def test_matches_example3(): + # See Appendix 1, Example 3 in Cigre 207 + y = 1600 + rho_r = cigre207.convective_cooling.compute_relative_air_density(y) + T_s = 75 + T_amb = 40 + v = 2 + D = 0.0286 + d = 0.00318 + T_f = (T_s + T_amb)/2 + nu_f = cigre207.convective_cooling.compute_kinematic_viscosity_of_air(T_f) + nu_f_exp = 1.866e-5 + assert nu_f == approx(nu_f_exp, rel=1e-3) + Re = cigre207.convective_cooling.compute_reynolds_number(v, D, nu_f, rho_r) + assert Re == approx(2547.5, rel=1e-3) + lambda_f = cigre207.convective_cooling.compute_thermal_conductivity_of_air(T_f) + assert lambda_f == approx(0.0283, 2e-3) + Rs = dimensionless.compute_conductor_roughness(D, d) + Nu_90 = cigre207.convective_cooling.compute_perpendicular_flow_nusseltnumber(Re, Rs) + assert Nu_90 == approx(25.77, 1e-3) + Nu_45 = cigre207.convective_cooling.correct_wind_direction_effect_on_nusselt_number(Nu_90, np.radians(45)) + assert Nu_45 == approx(21.8, rel=2e-3) + + +def test_matches_example4(): + # See Appendix 1, Example 4 in Cigre 207 + y = 1600 + rho_r = cigre207.convective_cooling.compute_relative_air_density(y) + T_s = 75 + T_amb = 40 + v = 0.4 + D = 0.0286 + T_f = (T_s + T_amb)/2 + nu_f = cigre207.convective_cooling.compute_kinematic_viscosity_of_air(T_f) + nu_f_exp = 1.866e-5 + assert nu_f == approx(nu_f_exp, rel=1e-3) + Re = cigre207.convective_cooling.compute_reynolds_number(v, D, nu_f, rho_r) + assert Re == approx(509.6, rel=2e-3) + lambda_f = cigre207.convective_cooling.compute_thermal_conductivity_of_air(T_f) + assert lambda_f == approx(0.0283, 2e-3) + d = 0.00318 + Rs = dimensionless.compute_conductor_roughness(D, d) + Nu_90 = cigre207.convective_cooling.compute_perpendicular_flow_nusseltnumber(Re, Rs) + assert Nu_90 == approx(12.08, 1e-3) + Nu_45 = cigre207.convective_cooling.correct_wind_direction_effect_on_nusselt_number(Nu_90, np.radians(45)) + assert Nu_45 == approx(10.2, rel=2e-3) + Nu_cor = cigre207.convective_cooling.compute_low_wind_speed_nusseltnumber(Nu_90) + assert Nu_cor == approx(6.64, rel=1e-3) + Gr = dimensionless.compute_grashof_number(D, T_s, T_amb, nu_f_exp) + assert Gr == approx(69922.7, rel=3e-3) + Pr = cigre207.convective_cooling.compute_prandtl_number(T_f) + assert Pr == approx(0.701, rel=1e-3) + Nu_natural = cigre207.convective_cooling.compute_horizontal_natural_nusselt_number(Gr, Pr) + assert Nu_natural == approx(7.14, rel=1e-3) + Nu_eff = cigre207.convective_cooling.compute_nusselt_number(Nu_45, Nu_natural, Nu_cor, v) + assert Nu_eff == approx(10.2, rel=1e-3) + + +def test_matches_example5(): + # See Appendix 1, Example 5 in Cigre 207 + y = 300 + rho_r = cigre207.convective_cooling.compute_relative_air_density(y) + assert rho_r == approx(0.966, rel=1e-3) + T_s = 57 + T_amb = 40 + v = 2 + D = 0.0286 + T_f = (T_s + T_amb)/2 + nu_f = cigre207.convective_cooling.compute_kinematic_viscosity_of_air(T_f) + nu_f_exp = 1.78e-5 + assert nu_f == approx(nu_f_exp, rel=1e-3) + Re = cigre207.convective_cooling.compute_reynolds_number(v, D, nu_f, rho_r) + assert Re == approx(3106, rel=2e-3) + lambda_f = cigre207.convective_cooling.compute_thermal_conductivity_of_air(T_f) + assert lambda_f == approx(0.0277, 2e-3) + d = 0.00318 + Rs = dimensionless.compute_conductor_roughness(D, d) + Nu_90 = cigre207.convective_cooling.compute_perpendicular_flow_nusseltnumber(Re, Rs) + assert Nu_90 == approx(29.85, 1e-3) + Nu_45 = cigre207.convective_cooling.correct_wind_direction_effect_on_nusselt_number(Nu_90, np.radians(45)) + assert Nu_45 == approx(25.21, rel=2e-3) diff --git a/tests/equations/cigre601/test_solar_heating.py b/tests/equations/cigre601/test_solar_heating.py index bd875fc..abe7091 100644 --- a/tests/equations/cigre601/test_solar_heating.py +++ b/tests/equations/cigre601/test_solar_heating.py @@ -218,40 +218,3 @@ def test_global_radiation_intensity_with_examples(): I_T = solar_heating.compute_global_radiation_intensity(I_B, I_d, F, sin_eta, sin_H_s) assert I_T == approx(2.5) - - -@hypothesis.given(conductor_diameter=st.floats(allow_nan=False)) -def test_solar_heating_scales_linearly_with_conductor_diameter(conductor_diameter): - D = conductor_diameter - alpha_s = 1 - I_T = 1 - - assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(D) - - -@hypothesis.given(solar_absorptivity=st.floats(allow_nan=False)) -def test_solar_heating_scales_linearly_with_solar_absorptivity(solar_absorptivity): - D = 1 - alpha_s = solar_absorptivity - I_T = 1 - - assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(alpha_s) - - -@hypothesis.given(global_radiation_intensity=st.floats(allow_nan=False)) -def test_solar_heating_scales_linearly_with_global_radiation_intensity(global_radiation_intensity): - D = 1 - alpha_s = 1 - I_T = global_radiation_intensity - - assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx( - global_radiation_intensity - ) - - -def test_solar_heating_scales_linearly_with_example(): - D = 0.6 - alpha_s = 0.5 - I_T = 2 - - assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(0.6) diff --git a/tests/equations/test_convective_cooling.py b/tests/equations/test_convective_cooling.py index 876f749..f16f208 100644 --- a/tests/equations/test_convective_cooling.py +++ b/tests/equations/test_convective_cooling.py @@ -95,3 +95,39 @@ def test_convective_cooling_scales_affinely_with_surface_temperature(surface_tem lambda_f = 1 / np.pi assert convective_cooling.compute_convective_cooling(T_s, T_a, Nu, lambda_f) + 1 == approx(T_s) + + +def test_cooling_matches_cigre207_example1(): + # See Appendix 1, Example 1 in Cigre 207 + T_s = 57 + T_amb = 40 + Nu_45 = 22.34 + cooling = convective_cooling.compute_convective_cooling(T_s, T_amb, Nu_45, 0.0277) + assert cooling == approx(33.04, rel=1e-3) + + +def test_cooling_matches_cigre207_example2(): + # See Appendix 1, Example 2 in Cigre 207 + T_s = 93 + T_amb = 40 + Nu_45 = 7.69 + cooling = convective_cooling.compute_convective_cooling(T_s, T_amb, Nu_45, 0.0290) + assert cooling == approx(37.13, rel=1e-3) + + +def test_cooling_matches_cigre207_example3(): + # See Appendix 1, Example 3 in Cigre 207 + T_s = 75 + T_amb = 40 + Nu_45 = 21.8 + cooling = convective_cooling.compute_convective_cooling(T_s, T_amb, Nu_45, 0.0283) + assert cooling == approx(67.8, rel=1e-3) + + +def test_cooling_matches_cigre207_example4(): + # See Appendix 1, Example 4 in Cigre 207 + T_s = 75 + T_amb = 40 + Nu_45 = 10.2 + cooling = convective_cooling.compute_convective_cooling(T_s, T_amb, Nu_45, 0.0283) + assert cooling == approx(31.78, rel=2e-3) \ No newline at end of file diff --git a/tests/equations/test_radiative_cooling.py b/tests/equations/test_radiative_cooling.py index 9b8211d..6170138 100644 --- a/tests/equations/test_radiative_cooling.py +++ b/tests/equations/test_radiative_cooling.py @@ -83,3 +83,20 @@ def test_radiative_cooling_with_example( T_a = air_temperature assert radiative_cooling.compute_radiative_cooling(T_s, T_a, D, epsilon) == approx(cooling) + + +@pytest.mark.parametrize( + "surface_temperature, expected_cooling", + [ + (57, 5.76), + (93, 21.27), + (75, 12.92) + ], +) +def test_cooling_matches_cigre207_examples(surface_temperature, expected_cooling): + # See Appendix 1, Example 1 in Cigre 207 + T_a = 40 + D = 0.0286 + epsilon = 0.5 + cooling = radiative_cooling.compute_radiative_cooling(surface_temperature, T_a, D, epsilon) + assert cooling == approx(expected_cooling, rel=2e-3) \ No newline at end of file diff --git a/tests/equations/test_solar_heating.py b/tests/equations/test_solar_heating.py new file mode 100644 index 0000000..864366d --- /dev/null +++ b/tests/equations/test_solar_heating.py @@ -0,0 +1,51 @@ +import hypothesis +import hypothesis.strategies as st +from pytest import approx + +from linerate.equations import solar_heating + + +def test_matches_solar_heating_example_from_cigre207(): + # See Appendix 1, Example 1 in Cigre 207 + D = 0.0286 + global_solar_radiation = 980 + absorptivity = 0.5 + heating = solar_heating.compute_solar_heating(absorptivity, global_solar_radiation, D) + assert heating == approx(14.02, rel=1e-3) + + +@hypothesis.given(conductor_diameter=st.floats(allow_nan=False)) +def test_solar_heating_scales_linearly_with_conductor_diameter(conductor_diameter): + D = conductor_diameter + alpha_s = 1 + I_T = 1 + + assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(D) + + +@hypothesis.given(solar_absorptivity=st.floats(allow_nan=False)) +def test_solar_heating_scales_linearly_with_solar_absorptivity(solar_absorptivity): + D = 1 + alpha_s = solar_absorptivity + I_T = 1 + + assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(alpha_s) + + +@hypothesis.given(global_radiation_intensity=st.floats(allow_nan=False)) +def test_solar_heating_scales_linearly_with_global_radiation_intensity(global_radiation_intensity): + D = 1 + alpha_s = 1 + I_T = global_radiation_intensity + + assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx( + global_radiation_intensity + ) + + +def test_solar_heating_scales_linearly_with_example(): + D = 0.6 + alpha_s = 0.5 + I_T = 2 + + assert solar_heating.compute_solar_heating(alpha_s, I_T, D) == approx(0.6)