Skip to content

Commit 80043a8

Browse files
refactor saturation vapour pressure formulae to avoid using temperatures in Celsius (#1388)
Co-authored-by: Sylwester Arabas <[email protected]>
1 parent 20bed75 commit 80043a8

File tree

34 files changed

+3477
-289
lines changed

34 files changed

+3477
-289
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ repos:
1818
hooks:
1919
- id: trailing-whitespace
2020
- id: end-of-file-fixer
21-
- id: debug-statements
21+
- id: debug-statements

PySDM/backends/impl_numba/methods/condensation_methods.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,7 @@ def step_impl( # pylint: disable=too-many-arguments,too-many-locals
291291
)
292292
pv = formulae.state_variable_triplet__pv(p, water_vapour_mixing_ratio)
293293
lv = formulae.latent_heat__lv(T)
294-
pvs = formulae.saturation_vapour_pressure__pvs_Celsius(
295-
T - formulae.constants.T0
296-
)
294+
pvs = formulae.saturation_vapour_pressure__pvs_water(T)
297295
DTp = formulae.diffusion_thermics__D(T, p)
298296
RH = pv / pvs
299297
Sc = formulae.trivia__air_schmidt_number(

PySDM/backends/impl_numba/methods/physics_methods.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def body(*, rhod, thd, water_vapour_mixing_ratio, T, p, RH):
5959
)
6060
RH[i] = ff.state_variable_triplet__pv(
6161
p[i], water_vapour_mixing_ratio[i]
62-
) / ff.saturation_vapour_pressure__pvs_Celsius(T[i] - ff.constants.T0)
62+
) / ff.saturation_vapour_pressure__pvs_water(T[i])
6363

6464
return body
6565

@@ -82,9 +82,7 @@ def _a_w_ice_body(self):
8282
@numba.njit(**self.default_jit_flags)
8383
def body(*, T_in, p_in, RH_in, water_vapour_mixing_ratio_in, a_w_ice_out):
8484
for i in prange(T_in.shape[0]): # pylint: disable=not-an-iterable
85-
pvi = ff.saturation_vapour_pressure__ice_Celsius(
86-
T_in[i] - ff.constants.T0
87-
)
85+
pvi = ff.saturation_vapour_pressure__pvs_ice(T_in[i])
8886
pv = ff.state_variable_triplet__pv(
8987
p_in[i], water_vapour_mixing_ratio_in[i]
9088
)

PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
_CellData,
1919
_Counters,
2020
)
21-
from PySDM.physics.constants_defaults import PI_4_3, T0
21+
from PySDM.physics.constants_defaults import PI_4_3
2222

2323
idx_thd = 0
2424
idx_x = 1
@@ -205,7 +205,7 @@ def _odesys( # pylint: disable=too-many-arguments,too-many-locals
205205
T = jit_formulae.state_variable_triplet__T(rhod, thd)
206206
p = jit_formulae.state_variable_triplet__p(rhod, T, water_vapour_mixing_ratio)
207207
pv = jit_formulae.state_variable_triplet__pv(p, water_vapour_mixing_ratio)
208-
pvs = jit_formulae.saturation_vapour_pressure__pvs_Celsius(T - T0)
208+
pvs = jit_formulae.saturation_vapour_pressure__pvs_water(T)
209209
RH = pv / pvs
210210

211211
dy_dt = np.empty_like(y)

PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ def __pre(self):
318318
p='p[i]', water_vapour_mixing_ratio='predicted_water_vapour_mixing_ratio[i]')};
319319
lv[i] = {phys.latent_heat.lv.c_inline(
320320
T='T[i]')};
321-
pvs[i] = {phys.saturation_vapour_pressure.pvs_Celsius.c_inline(
322-
T='T[i] - const.T0')};
321+
pvs[i] = {phys.saturation_vapour_pressure.pvs_water.c_inline(
322+
T='T[i]')};
323323
RH[i] = pv[i] / pvs[i];
324324
RH_max[i] = max(RH_max[i], RH[i]);
325325
DTp[i] = {phys.diffusion_thermics.D.c_inline(

PySDM/backends/impl_thrust_rtc/methods/physics_methods.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ def _temperature_pressure_rh_body(self):
2727
)};
2828
RH[i] = {self.formulae.state_variable_triplet.pv.c_inline(
2929
p="p[i]", water_vapour_mixing_ratio="water_vapour_mixing_ratio[i]"
30-
)} / {self.formulae.saturation_vapour_pressure.pvs_Celsius.c_inline(
31-
T="T[i] - const.T0"
32-
)};
30+
)} / {self.formulae.saturation_vapour_pressure.pvs_water.c_inline(T="T[i]")};
3331
""".replace(
3432
"real_type", self._get_c_type()
3533
),

PySDM/physics/saturation_vapour_pressure/august_roche_magnus.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ def __init__(self, _):
1212
pass
1313

1414
@staticmethod
15-
def pvs_Celsius(const, T):
16-
return const.ARM_C1 * np.exp((const.ARM_C2 * T) / (T + const.ARM_C3))
15+
def pvs_water(const, T):
16+
return const.ARM_C1 * np.exp(
17+
(const.ARM_C2 * (T - const.T0)) / ((T - const.T0) + const.ARM_C3)
18+
)
1719

1820
@staticmethod
19-
def ice_Celsius(const, T):
21+
def pvs_ice(const, T):
2022
"""NaN with unit of pressure and correct dimension"""
21-
return np.nan * T / const.ARM_C3 * const.ARM_C1
23+
return np.nan * (T - const.T0) / const.ARM_C3 * const.ARM_C1

PySDM/physics/saturation_vapour_pressure/bolton_1980.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ def __init__(self, _):
1010
pass
1111

1212
@staticmethod
13-
def pvs_Celsius(const, T):
14-
"""valid for -30 <= T <= 35 C, eq (10)"""
15-
return const.B80W_G0 * np.exp((const.B80W_G1 * T) / (T + const.B80W_G2))
13+
def pvs_water(const, T):
14+
"""valid for 243.15(-30) <= T <= 308.15(35) K(C), eq. (10)"""
15+
return const.B80W_G0 * np.exp(
16+
(const.B80W_G1 * (T - const.T0)) / ((T - const.T0) + const.B80W_G2)
17+
)
1618

1719
@staticmethod
18-
def ice_Celsius(const, T):
20+
def pvs_ice(const, T):
1921
"""NaN with unit of pressure and correct dimension"""
20-
return np.nan * T / const.B80W_G2 * const.B80W_G0
22+
return np.nan * (T - const.T0) / const.B80W_G2 * const.B80W_G0

PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,55 @@ def __init__(self, _):
99
pass
1010

1111
@staticmethod
12-
def pvs_Celsius(const, T):
13-
return const.FWC_C0 + T * (
12+
def pvs_water(const, T):
13+
return const.FWC_C0 + (T - const.T0) * (
1414
const.FWC_C1
15-
+ T
15+
+ (T - const.T0)
1616
* (
1717
const.FWC_C2
18-
+ T
18+
+ (T - const.T0)
1919
* (
2020
const.FWC_C3
21-
+ T
21+
+ (T - const.T0)
2222
* (
2323
const.FWC_C4
24-
+ T
24+
+ (T - const.T0)
2525
* (
2626
const.FWC_C5
27-
+ T * (const.FWC_C6 + T * (const.FWC_C7 + T * const.FWC_C8))
27+
+ (T - const.T0)
28+
* (
29+
const.FWC_C6
30+
+ (T - const.T0)
31+
* (const.FWC_C7 + (T - const.T0) * const.FWC_C8)
32+
)
2833
)
2934
)
3035
)
3136
)
3237
)
3338

3439
@staticmethod
35-
def ice_Celsius(const, T):
36-
return const.FWC_I0 + T * (
40+
def pvs_ice(const, T):
41+
return const.FWC_I0 + (T - const.T0) * (
3742
const.FWC_I1
38-
+ T
43+
+ (T - const.T0)
3944
* (
4045
const.FWC_I2
41-
+ T
46+
+ (T - const.T0)
4247
* (
4348
const.FWC_I3
44-
+ T
49+
+ (T - const.T0)
4550
* (
4651
const.FWC_I4
47-
+ T
52+
+ (T - const.T0)
4853
* (
4954
const.FWC_I5
50-
+ T * (const.FWC_I6 + T * (const.FWC_I7 + T * const.FWC_I8))
55+
+ (T - const.T0)
56+
* (
57+
const.FWC_I6
58+
+ (T - const.T0)
59+
* (const.FWC_I7 + (T - const.T0) * const.FWC_I8)
60+
)
5161
)
5262
)
5363
)

PySDM/physics/saturation_vapour_pressure/lowe1977.py

+20-10
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,41 @@ def __init__(self, _):
99
pass
1010

1111
@staticmethod
12-
def pvs_Celsius(const, T):
13-
return const.L77W_A0 + T * (
12+
def pvs_water(const, T):
13+
return const.L77W_A0 + (T - const.T0) * (
1414
const.L77W_A1
15-
+ T
15+
+ (T - const.T0)
1616
* (
1717
const.L77W_A2
18-
+ T
18+
+ (T - const.T0)
1919
* (
2020
const.L77W_A3
21-
+ T * (const.L77W_A4 + T * (const.L77W_A5 + T * (const.L77W_A6)))
21+
+ (T - const.T0)
22+
* (
23+
const.L77W_A4
24+
+ (T - const.T0)
25+
* (const.L77W_A5 + (T - const.T0) * (const.L77W_A6))
26+
)
2227
)
2328
)
2429
)
2530

2631
@staticmethod
27-
def ice_Celsius(const, T):
28-
return const.L77I_A0 + T * (
32+
def pvs_ice(const, T):
33+
return const.L77I_A0 + (T - const.T0) * (
2934
const.L77I_A1
30-
+ T
35+
+ (T - const.T0)
3136
* (
3237
const.L77I_A2
33-
+ T
38+
+ (T - const.T0)
3439
* (
3540
const.L77I_A3
36-
+ T * (const.L77I_A4 + T * (const.L77I_A5 + T * (const.L77I_A6)))
41+
+ (T - const.T0)
42+
* (
43+
const.L77I_A4
44+
+ (T - const.T0)
45+
* (const.L77I_A5 + (T - const.T0) * (const.L77I_A6))
46+
)
3747
)
3848
)
3949
)

PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,28 @@ def __init__(self, _):
1010
pass
1111

1212
@staticmethod
13-
def pvs_Celsius(const, T):
13+
def pvs_water(const, T):
1414
"""valid for 123 < T < 332 K, eq (10)"""
1515
return const.MK05_LIQ_C1 * np.exp(
1616
const.MK05_LIQ_C2
17-
- const.MK05_LIQ_C3 / (T + const.T0)
18-
- const.MK05_LIQ_C4 * np.log((T + const.T0) / const.MK05_LIQ_C5)
19-
+ const.MK05_LIQ_C6 * (T + const.T0)
20-
+ np.tanh(const.MK05_LIQ_C7 * (T + const.T0 - const.MK05_LIQ_C8))
17+
- const.MK05_LIQ_C3 / (T)
18+
- const.MK05_LIQ_C4 * np.log(T / const.MK05_LIQ_C5)
19+
+ const.MK05_LIQ_C6 * (T)
20+
+ np.tanh(const.MK05_LIQ_C7 * (T - const.MK05_LIQ_C8))
2121
* (
2222
const.MK05_LIQ_C9
23-
- const.MK05_LIQ_C10 / (T + const.T0)
24-
- const.MK05_LIQ_C11 * np.log((T + const.T0) / const.MK05_LIQ_C12)
25-
+ const.MK05_LIQ_C13 * (T + const.T0)
23+
- const.MK05_LIQ_C10 / T
24+
- const.MK05_LIQ_C11 * np.log(T / const.MK05_LIQ_C12)
25+
+ const.MK05_LIQ_C13 * T
2626
)
2727
)
2828

2929
@staticmethod
30-
def ice_Celsius(const, T):
30+
def pvs_ice(const, T):
3131
"""valid for T > 110 K, eq (7)"""
3232
return const.MK05_ICE_C1 * np.exp(
3333
const.MK05_ICE_C2
34-
- const.MK05_ICE_C3 / (T + const.T0)
35-
+ const.MK05_ICE_C4 * np.log((T + const.T0) / const.MK05_ICE_C5)
36-
- const.MK05_ICE_C6 * (T + const.T0)
34+
- const.MK05_ICE_C3 / T
35+
+ const.MK05_ICE_C4 * np.log(T / const.MK05_ICE_C5)
36+
- const.MK05_ICE_C6 * T
3737
)

PySDM/physics/saturation_vapour_pressure/wexler_1976.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ def __init__(self, _):
1010
pass
1111

1212
@staticmethod
13-
def pvs_Celsius(const, T):
13+
def pvs_water(const, T):
1414
return (
1515
np.exp(
16-
const.W76W_G0 / (T + const.T0) ** 2
17-
+ const.W76W_G1 / (T + const.T0)
16+
const.W76W_G0 / T**2
17+
+ const.W76W_G1 / T
1818
+ const.W76W_G2
19-
+ const.W76W_G3 * (T + const.T0)
20-
+ const.W76W_G4 * (T + const.T0) ** 2
21-
+ const.W76W_G5 * (T + const.T0) ** 3
22-
+ const.W76W_G6 * (T + const.T0) ** 4
23-
+ const.W76W_G7 * np.log((T + const.T0) / const.one_kelvin)
19+
+ const.W76W_G3 * T
20+
+ const.W76W_G4 * T**2
21+
+ const.W76W_G5 * T**3
22+
+ const.W76W_G6 * T**4
23+
+ const.W76W_G7 * np.log(T / const.one_kelvin)
2424
)
2525
* const.W76W_G8
2626
)
2727

2828
@staticmethod
29-
def ice_Celsius(const, T):
29+
def pvs_ice(const, T):
3030
"""NaN with unit of pressure and correct dimension"""
31-
return np.nan * T / const.B80W_G2 * const.B80W_G0
31+
return np.nan * (T - const.T0) / const.B80W_G2 * const.B80W_G0

examples/PySDM_examples/Abdul_Razzak_Ghan_2000/run_ARG_parcel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def run_parcel(
3535

3636
formulae = Formulae(constants=CONSTANTS_ARG)
3737
const = formulae.constants
38-
pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_Celsius(T0 - const.T0)
38+
pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_water(T0)
3939

4040
env = Parcel(
4141
dt=dt,

examples/PySDM_examples/Alpert_and_Knopf_2016/simulation.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from PySDM.environments import Box
1212
from PySDM.initialisation import discretise_multiplicities
1313
from PySDM.initialisation.sampling import spectral_sampling
14-
from PySDM.physics import constants as const
1514
from PySDM.physics import si
1615
from PySDM.products import IceWaterContent, TotalUnfrozenImmersedSurfaceArea
1716

@@ -143,7 +142,7 @@ def plot_j_het(self, variant: str, abifm_params_case: str, ylim=None):
143142
svp = formulae.saturation_vapour_pressure
144143
plot_x = np.linspace(*self.temperature_range) * si.K
145144
plot_y = formulae.heterogeneous_ice_nucleation_rate.j_het(
146-
svp.ice_Celsius(plot_x - const.T0) / svp.pvs_Celsius(plot_x - const.T0)
145+
svp.pvs_ice(plot_x) / svp.pvs_water(plot_x)
147146
)
148147
pyplot.grid()
149148
pyplot.plot(plot_x, plot_y / yunit, color="red", label="ABIFM $J_{het}$")
@@ -252,9 +251,7 @@ def simulation(
252251
for i in range(int(total_time / time_step) + 1):
253252
if cooling_rate != 0:
254253
env["T"] -= np.full((1,), cooling_rate * time_step / 2)
255-
env["a_w_ice"] = svp.ice_Celsius(env["T"][0] - const.T0) / svp.pvs_Celsius(
256-
env["T"][0] - const.T0
257-
)
254+
env["a_w_ice"] = svp.pvs_ice(env["T"][0]) / svp.pvs_water(env["T"][0])
258255
particulator.run(0 if i == 0 else 1)
259256
if cooling_rate != 0:
260257
env["T"] -= np.full((1,), cooling_rate * time_step / 2)

examples/PySDM_examples/Arabas_and_Shima_2017/settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(
2727
self.T0 = 300 * si.kelvin
2828
self.z_half = 150 * si.metres
2929

30-
pvs = self.formulae.saturation_vapour_pressure.pvs_Celsius(self.T0 - const.T0)
30+
pvs = self.formulae.saturation_vapour_pressure.pvs_water(self.T0)
3131
self.initial_water_vapour_mixing_ratio = const.eps / (
3232
self.p0 / self.RH0 / pvs - 1
3333
)

examples/PySDM_examples/Arabas_et_al_2023/fig_2.ipynb

+2,108-13
Large diffs are not rendered by default.

examples/PySDM_examples/Arabas_et_al_2023/run_simulation.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
def update_thermo(particulator, T):
55
env = particulator.environment
66
svp = particulator.formulae.saturation_vapour_pressure
7-
const = particulator.formulae.constants
87

98
env["T"] = T
10-
env["a_w_ice"] = svp.ice_Celsius(T - const.T0) / svp.pvs_Celsius(T - const.T0)
9+
env["a_w_ice"] = svp.pvs_ice(T) / svp.pvs_water(T)
1110

1211

1312
def run_simulation(particulator, temperature_profile, n_steps):

examples/PySDM_examples/Gedzelman_and_Arnold_1994/fig_2.ipynb

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
" )(kwargs['temperature'])\n",
175175
"\n",
176176
" missing_b_multiplier = (\n",
177-
" kwargs['formulae'].saturation_vapour_pressure.pvs_Celsius(kwargs['temperature'] - T0)\n",
177+
" kwargs['formulae'].saturation_vapour_pressure.pvs_water(kwargs['temperature'])\n",
178178
" / kwargs['temperature']\n",
179179
" / const.Rv\n",
180180
" )\n",

examples/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ def __init__(
6161
self.initial_temperature = initial_temperature
6262
pv0 = (
6363
initial_relative_humidity
64-
* self.formulae.saturation_vapour_pressure.pvs_Celsius(
65-
initial_temperature - const.T0
66-
)
64+
* self.formulae.saturation_vapour_pressure.pvs_water(initial_temperature)
6765
)
6866
self.initial_vapour_mixing_ratio = const.eps * pv0 / (initial_pressure - pv0)
6967
self.t_max = displacement / vertical_velocity

0 commit comments

Comments
 (0)