Skip to content

Commit f942453

Browse files
other isotopes in isotope methods; cache D_ratio and alpha in bolin_number; add diffusivity ratios using Grahams law for other isotopes; tests for Bolin number (#1834)
Co-authored-by: Sylwester Arabas <sylwester.arabas@agh.edu.pl>
1 parent 0830f8e commit f942453

16 files changed

Lines changed: 303 additions & 208 deletions

File tree

PySDM/attributes/isotopes/bolin_numbers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
Derived droplet attribute representing the Bolin number for heavy
3-
water isotopologues (D, 17O, 18O substitutions).
3+
water isotopologues (2H, 17O, 18O substitutions).
44
55
The Bolin number is a dimensionless coefficient relating the
66
heavy-isotope mole tendency to the bulk liquid-water mass tendency
@@ -24,6 +24,7 @@ def __init__(self, builder, *, heavy_isotope: str):
2424
heavy_isotope : str
2525
Heavy isotopologue identifier (entry of ``HEAVY_ISOTOPES``).
2626
"""
27+
self.isotope = heavy_isotope
2728
self.moles_heavy = builder.get_attribute(f"moles_{heavy_isotope}")
2829
self.moles_light = builder.get_attribute("moles light water") # TODO #1787
2930
self.cell_id = builder.get_attribute("cell id")
@@ -41,9 +42,10 @@ def recalculate(self):
4142
self.particulator.backend.bolin_number(
4243
output=self.data,
4344
cell_id=self.cell_id.data,
45+
isotope=self.isotope,
4446
relative_humidity=self.particulator.environment["RH"],
4547
temperature=self.particulator.environment["T"],
46-
density_dry_air=self.particulator.environment["dry_air_density"],
48+
density_dry_air=self.particulator.environment["rhod"],
4749
moles_light_molecule=self.moles_light.data,
4850
moles_heavy=self.moles_heavy.data,
4951
molality_in_dry_air=self.molality_in_dry_air,

PySDM/backends/impl_numba/methods/isotope_methods.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CPU implementation of isotope-relates backend methods
33
"""
44

5-
from functools import cached_property
5+
from functools import cached_property, lru_cache
66

77
import numba
88

@@ -76,9 +76,7 @@ def body(
7676
dm_heavy = dm_total[sd_id] / Bo * mass_ratio_heavy_to_total
7777
dn_heavy_molecule = dm_heavy / molar_mass_heavy_molecule
7878
moles_heavy_molecule[sd_id] += dn_heavy_molecule
79-
mass_of_dry_air = (
80-
dry_air_density[cell_id[sd_id]] * cell_volume[cell_id[sd_id]]
81-
)
79+
mass_of_dry_air = dry_air_density[cell_id[sd_id]] * cell_volume
8280
molality_in_dry_air[cell_id[sd_id]] -= (
8381
dn_heavy_molecule * multiplicity[sd_id] / mass_of_dry_air
8482
)
@@ -102,7 +100,7 @@ def isotopic_fractionation(
102100
"""Update heavy-isotope composition during droplet growth/evaporation."""
103101
self._isotopic_fractionation_body(
104102
cell_id=cell_id.data,
105-
cell_volume=cell_volume.data,
103+
cell_volume=cell_volume,
106104
multiplicity=multiplicity.data,
107105
dm_total=dm_total.data,
108106
signed_water_mass=signed_water_mass.data,
@@ -133,6 +131,8 @@ def body(
133131
*,
134132
output,
135133
cell_id,
134+
alpha,
135+
D_ratio,
136136
relative_humidity,
137137
temperature,
138138
density_dry_air,
@@ -148,38 +148,51 @@ def body(
148148
conc_vap_total = (
149149
pvs_water * relative_humidity[cell_id[i]] / ff.constants.R_str / T
150150
)
151-
rho_v = pvs_water / T / ff.constants.Rv
152-
153151
isotopic_fraction = ff.trivia__isotopic_fraction(
154152
molality_in_dry_air=molality_in_dry_air[cell_id[i]],
155153
density_dry_air=density_dry_air[cell_id[i]],
156154
total_vap_concentration=conc_vap_total,
157155
)
158-
D_ratio_heavy_to_light = (
159-
ff.isotope_diffusivity_ratios__ratio_2H_heavy_to_light(T)
160-
)
161156
output[i] = ff.isotope_relaxation_timescale__bolin_number(
162-
D_ratio_heavy_to_light=D_ratio_heavy_to_light,
163-
alpha=ff.isotope_equilibrium_fractionation_factors__alpha_l_2H(T),
164-
D_light=ff.constants.D0,
157+
D_ratio_heavy_to_light=D_ratio(T),
158+
alpha=alpha(T),
165159
Fk=ff.drop_growth__Fk(
166160
T=T, K=ff.constants.K0, lv=ff.constants.l_tri
167161
),
162+
Fd=ff.drop_growth__Fd(
163+
T=T,
164+
D=ff.constants.D0,
165+
pvs=pvs_water,
166+
),
168167
R_vap=ff.trivia__isotopic_ratio_assuming_single_heavy_isotope(
169168
isotopic_fraction
170169
),
171170
R_liq=moles_heavy_atom / moles_light_isotope,
172171
relative_humidity=relative_humidity[cell_id[i]],
173-
rho_v=rho_v,
174172
)
175173

176174
return body
177175

176+
@lru_cache
177+
def alpha_l(self, isotope):
178+
return getattr(
179+
self.formulae_flattened,
180+
f"isotope_equilibrium_fractionation_factors__alpha_l_{isotope}",
181+
)
182+
183+
@lru_cache
184+
def D_ratio(self, isotope):
185+
return getattr(
186+
self.formulae_flattened,
187+
f"isotope_diffusivity_ratios__ratio_{isotope}_heavy_to_light",
188+
)
189+
178190
def bolin_number(
179191
self,
180192
*,
181193
output,
182194
cell_id,
195+
isotope,
183196
relative_humidity,
184197
temperature,
185198
density_dry_air,
@@ -191,6 +204,8 @@ def bolin_number(
191204
self._bolin_number_body(
192205
output=output.data,
193206
cell_id=cell_id.data,
207+
D_ratio=self.D_ratio(isotope),
208+
alpha=self.alpha_l(isotope),
194209
relative_humidity=relative_humidity.data,
195210
temperature=temperature.data,
196211
density_dry_air=density_dry_air.data,

PySDM/backends/impl_thrust_rtc/methods/isotope_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def __isotopic_fractionation(self):
6363
6464
moles_heavy_molecule[i] += dn_heavy;
6565
66-
real_type mass_of_dry_air = dry_air_density[cid] * cell_volume[cid];
66+
real_type mass_of_dry_air = dry_air_density[cid] * cell_volume;
6767
6868
atomicAdd((real_type*) &molality_in_dry_air[cid], -dn_heavy * multiplicity[i] / mass_of_dry_air);
6969
""".replace("real_type", self._get_c_type()),
@@ -87,7 +87,7 @@ def isotopic_fractionation(
8787
n=len(multiplicity),
8888
args=(
8989
cell_id.data,
90-
cell_volume.data,
90+
self._get_floating_point(cell_volume),
9191
multiplicity.data,
9292
dm_total.data,
9393
signed_water_mass.data,

PySDM/dynamics/isotopic_fractionation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ def register(self, builder):
4141
raise AssertionError(
4242
f"Isotopic fractionation not implemented for {isotope}"
4343
)
44-
builder.request_attribute(f"moles_{isotope}")
4544
builder.request_attribute(f"Bolin number for {isotope}")
4645

46+
for isotope in HEAVY_ISOTOPES:
47+
builder.request_attribute(f"moles_{isotope}")
48+
4749
def __call__(self):
4850
self.particulator.isotopic_fractionation(self.isotopes)

PySDM/particulator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ def isotopic_fractionation(self, heavy_isotopes: tuple):
456456
multiplicity=self.attributes["multiplicity"],
457457
dm_total=self.attributes["diffusional growth mass change"],
458458
signed_water_mass=self.attributes["signed water mass"],
459-
dry_air_density=self.environment["dry_air_density"],
459+
dry_air_density=self.environment["rhod"],
460460
molar_mass_heavy_molecule=getattr(
461461
self.formulae.constants,
462462
{

PySDM/physics/drop_growth/howell_1949.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
same as in [Mason 1951](https://doi.org/10.1088/0370-1301/64/9/307)
55
66
The notation for terms associated with heat conduction and diffusion are from eq. 7.17
7-
in [Rogers & Yau 1971](https://archive.org/details/shortcourseinclo0000roge_m3k2).
7+
in [Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2).
88
"""
99

1010
from .fick import Fick

PySDM/physics/isotope_diffusivity_ratios/grahams_law.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,15 @@ def ratio_3H_heavy_to_light(const, temperature): # pylint: disable=unused-argum
1818
return (
1919
(2 * const.M_1H + const.M_16O) / (const.M_3H + const.M_1H + const.M_16O)
2020
) ** const.ONE_HALF
21+
22+
@staticmethod
23+
def ratio_17O_heavy_to_light(const, temperature): # pylint: disable=unused-argument
24+
return (
25+
(2 * const.M_1H + const.M_16O) / (2 * const.M_1H + const.M_17O)
26+
) ** const.ONE_HALF
27+
28+
@staticmethod
29+
def ratio_18O_heavy_to_light(const, temperature): # pylint: disable=unused-argument
30+
return (
31+
(2 * const.M_1H + const.M_16O) / (2 * const.M_1H + const.M_18O)
32+
) ** const.ONE_HALF

PySDM/physics/isotope_relaxation_timescale/zaba_et_al.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,13 @@ def tau(
2222

2323
@staticmethod
2424
def bolin_number(
25-
const,
25+
relative_humidity,
2626
D_ratio_heavy_to_light,
2727
alpha,
28-
D_light,
29-
Fk,
3028
R_vap,
3129
R_liq,
32-
relative_humidity,
33-
rho_v,
30+
Fd,
31+
Fk,
3432
): # pylint: disable=too-many-arguments,too-many-positional-arguments
3533
# TODO #1809 Numba can't compile when * in bolin_number
3634
"""Heavy to total isotopic-timescales ratio (tau_heavy/tau_total).
@@ -43,9 +41,7 @@ def bolin_number(
4341
* (1 - relative_humidity)
4442
/ D_ratio_heavy_to_light
4543
/ (
46-
(1 + rho_v * D_light * Fk / const.rho_w)
47-
* relative_humidity
48-
* (1 - alpha * R_vap / R_liq)
44+
(1 + Fk / Fd) * relative_humidity * (1 - alpha * R_vap / R_liq)
4945
- relative_humidity
5046
+ 1
5147
)

docs/bibliography.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,8 @@
757757
"PySDM/physics/drop_growth/howell_1949.py",
758758
"PySDM/physics/drop_growth/mason_1971.py",
759759
"PySDM/dynamics/terminal_velocity/rogers_and_yau.py",
760-
"PySDM/dynamics/terminal_velocity/power_series.py"
760+
"PySDM/dynamics/terminal_velocity/power_series.py",
761+
"examples/PySDM_examples/Gedzelman_and_Arnold_1994/fig_2.ipynb"
761762
],
762763
"title": "A short course in clouds physics",
763764
"label": "Rogers & Yau 1989 (Pergamon Press)"

0 commit comments

Comments
 (0)