Skip to content

Commit a383324

Browse files
authored
Merge pull request #657 from slayoo/dev
adding critical supersaturation attribute and activable fraction product
2 parents ac337b1 + e0417af commit a383324

File tree

8 files changed

+116
-6
lines changed

8 files changed

+116
-6
lines changed

PySDM/attributes/impl/mapper.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from PySDM.attributes.ice import FreezingTemperature, ImmersedSurfaceArea
88
from PySDM.attributes.numerics import CellID, CellOrigin, PositionInCell
99
from PySDM.attributes.chemistry import MoleAmount, Concentration, pH, HydrogenIonConcentration
10+
from PySDM.attributes.physics.critical_supersaturation import CriticalSupersaturation
1011
from PySDM.physics.aqueous_chemistry.support import AQUEOUS_COMPOUNDS
1112
from PySDM.physics.surface_tension import Constant
1213

@@ -15,14 +16,20 @@
1516
'volume': lambda _: Volume,
1617
'dry volume organic': lambda dynamics: (
1718
DummyAttributeImpl('dry volume organic')
18-
if isinstance(dynamics['Condensation'].particulator.formulae.surface_tension, Constant)
19+
if 'Condensation' in dynamics and isinstance(
20+
dynamics['Condensation'].particulator.formulae.surface_tension,
21+
Constant
22+
)
1923
else DryVolumeOrganic
2024
),
2125
'dry volume': lambda dynamics:
2226
DryVolumeDynamic if 'AqueousChemistry' in dynamics else DryVolume,
2327
'dry volume organic fraction': lambda dynamics: (
2428
DummyAttributeImpl('dry volume organic fraction')
25-
if isinstance(dynamics['Condensation'].particulator.formulae.surface_tension, Constant)
29+
if 'Condensation' in dynamics and isinstance(
30+
dynamics['Condensation'].particulator.formulae.surface_tension,
31+
Constant
32+
)
2633
else OrganicFraction
2734
),
2835
'kappa times dry volume': lambda _: KappaTimesDryVolume,
@@ -43,7 +50,8 @@
4350
'pH': lambda _: pH,
4451
'conc_H': lambda _: HydrogenIonConcentration,
4552
'freezing temperature': lambda _: FreezingTemperature,
46-
'immersed surface area': lambda _: ImmersedSurfaceArea
53+
'immersed surface area': lambda _: ImmersedSurfaceArea,
54+
'critical supersaturation': lambda _: CriticalSupersaturation
4755
}
4856

4957

PySDM/attributes/physics/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
from .temperature import Temperature
88
from .heat import Heat
99
from .critical_volume import CriticalVolume
10+
from .critical_supersaturation import CriticalSupersaturation
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from PySDM.attributes.impl.derived_attribute import DerivedAttribute
2+
from PySDM.physics import constants as const
3+
4+
5+
class CriticalSupersaturation(DerivedAttribute):
6+
def __init__(self, builder):
7+
self.v_crit = builder.get_attribute('critical volume')
8+
self.v_dry = builder.get_attribute('dry volume')
9+
self.kappa = builder.get_attribute('kappa')
10+
self.f_org = builder.get_attribute('dry volume organic fraction')
11+
12+
super().__init__(
13+
builder=builder,
14+
name='critical supersaturation',
15+
dependencies=(self.v_crit, self.kappa, self.v_dry, self.f_org)
16+
)
17+
self.formulae = builder.particulator.formulae
18+
self.environment = builder.particulator.environment
19+
20+
def recalculate(self):
21+
if len(self.environment['T']) != 1:
22+
raise NotImplementedError()
23+
T = self.environment['T'][0]
24+
r_cr = self.formulae.trivia.radius(self.v_crit.data.data)
25+
rd3 = self.v_dry.data.data / const.pi_4_3
26+
sgm = self.formulae.surface_tension.sigma(
27+
T, self.v_crit.data.data, self.v_dry.data.data, self.f_org.data.data
28+
)
29+
30+
self.data.data[:] = self.formulae.hygroscopicity.RH_eq(
31+
r_cr,
32+
T=T,
33+
kp=self.kappa.data.data,
34+
rd3=rd3,
35+
sgm=sgm
36+
)

PySDM/attributes/physics/dry_radius.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ def __init__(self, builder):
99
super().__init__(builder, name='dry radius', dependencies=dependencies)
1010

1111
def recalculate(self):
12-
self.data.idx = self.volume_dry.data.idx
1312
self.data.product(self.volume_dry.get(), 1/const.pi_4_3)
1413
self.data **= 1/3
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .condensation_timestep import CondensationTimestepMin, CondensationTimestepMax
22
from .event_rates import RipeningRate, ActivatingRate, DeactivatingRate
33
from .peak_supersaturation import PeakSupersaturation
4+
from .activable_fraction import ActivableFraction
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from ...product import MomentProduct
2+
3+
4+
class ActivableFraction(MomentProduct):
5+
def __init__(self):
6+
super().__init__(
7+
name="activable fraction",
8+
unit="1",
9+
description=""
10+
)
11+
12+
def register(self, builder):
13+
super().register(builder)
14+
builder.request_attribute('critical supersaturation')
15+
16+
def get(self, S_max):
17+
self.download_moment_to_buffer(
18+
'volume',
19+
rank=0,
20+
filter_range=(0, 1 + S_max / 100),
21+
filter_attr='critical supersaturation'
22+
)
23+
frac = self.buffer.copy()
24+
self.download_moment_to_buffer(
25+
'volume',
26+
rank=0
27+
)
28+
frac /= self.buffer
29+
return frac

PySDM/products/dynamics/freezing/ice_water_content.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import numpy as np
12
from ...product import MomentProduct
23
from ....physics import constants as const
3-
import numpy as np
4+
45

56
class IceWaterContent(MomentProduct):
67

@@ -24,4 +25,4 @@ def get(self):
2425
self.download_to_buffer(self.particulator.environment['rhod'])
2526
result[:] /= self.buffer
2627
const.convert_to(result, const.si.gram / const.si.kilogram)
27-
return result
28+
return result
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import numpy as np
2+
from PySDM.products.dynamics.condensation import ActivableFraction
3+
from PySDM import Builder
4+
from PySDM.backends import CPU
5+
from PySDM.environments import Box
6+
from PySDM.physics import si
7+
8+
9+
def test_critical_supersaturation():
10+
# arrange
11+
T = 300 * si.K
12+
n_sd = 100
13+
S_max = .01
14+
vdry = np.linspace(.001, 1, n_sd) * si.um**3
15+
16+
builder = Builder(n_sd=n_sd, backend=CPU())
17+
env = Box(dt=np.nan, dv=np.nan)
18+
builder.set_environment(env)
19+
env['T'] = T
20+
particulator = builder.build(
21+
attributes={
22+
'n': np.ones(n_sd),
23+
'volume': np.linspace(.01, 10, n_sd) * si.um**3,
24+
'dry volume': vdry,
25+
'kappa times dry volume': .9 * vdry,
26+
'dry volume organic': np.zeros(n_sd)
27+
},
28+
products=[ActivableFraction()]
29+
)
30+
31+
# act
32+
AF = particulator.products['activable fraction'].get(S_max)
33+
34+
# assert
35+
assert 0 < AF < 1

0 commit comments

Comments
 (0)