PySDM_examples.featured.Grabowski_and_Pawlowska_2023.simulation

  1import numpy as np
  2from PySDM_examples.utils import BasicSimulation
  3
  4from PySDM import Particulator
  5from PySDM.backends import CPU
  6from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver
  7from PySDM.dynamics import AmbientThermodynamics, Condensation
  8from PySDM.environments import Parcel
  9from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii
 10from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity
 11from PySDM.physics import si
 12
 13
 14class Simulation(BasicSimulation):
 15    def __init__(
 16        self,
 17        settings,
 18        products=None,
 19        scipy_solver=False,
 20    ):
 21
 22        environment = Parcel(
 23            dt=settings.timestep,
 24            p0=settings.initial_pressure,
 25            initial_relative_humidity=settings.initial_relative_humidity,
 26            T0=settings.initial_temperature,
 27            w=settings.vertical_velocity,
 28            mass_of_dry_air=44 * si.kg,
 29            backend=CPU(formulae=settings.formulae),
 30        )
 31        volume = environment.mass_of_dry_air / settings.initial_air_density
 32        attributes = {
 33            k: np.empty(0)
 34            for k in ("dry volume", "kappa times dry volume", "multiplicity")
 35        }
 36
 37        assert len(settings.aerosol_modes_by_kappa.keys()) == 1
 38        kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0]
 39        spectrum = settings.aerosol_modes_by_kappa[kappa]
 40
 41        r_dry, n_per_volume = ConstantMultiplicity(spectrum).sample_deterministic(
 42            settings.n_sd
 43        )
 44        v_dry = settings.formulae.trivia.volume(radius=r_dry)
 45        attributes["multiplicity"] = np.append(
 46            attributes["multiplicity"], n_per_volume * volume
 47        )
 48        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 49        attributes["kappa times dry volume"] = np.append(
 50            attributes["kappa times dry volume"], v_dry * kappa
 51        )
 52        r_wet = equilibrate_wet_radii(
 53            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
 54            environment=environment,
 55            kappa_times_dry_volume=attributes["kappa times dry volume"],
 56        )
 57        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
 58
 59        particulator = Particulator(
 60            n_sd=settings.n_sd,
 61            environment=environment,
 62            dynamics=(
 63                AmbientThermodynamics(),
 64                Condensation(rtol_thd=settings.rtol_thd, rtol_x=settings.rtol_x),
 65            ),
 66            attributes=attributes,
 67            products=products,
 68            requested_attributes=(
 69                "critical saturation",
 70                "equilibrium saturation",
 71                "critical volume",
 72            ),
 73        )
 74
 75        super().__init__(particulator=particulator)
 76        if scipy_solver:
 77            scipy_ode_condensation_solver.patch_particulator(self.particulator)
 78
 79        self.output_attributes = {
 80            "volume": tuple([] for _ in range(self.particulator.n_sd)),
 81            "dry volume": tuple([] for _ in range(self.particulator.n_sd)),
 82            "critical saturation": tuple([] for _ in range(self.particulator.n_sd)),
 83            "equilibrium saturation": tuple([] for _ in range(self.particulator.n_sd)),
 84            "critical volume": tuple([] for _ in range(self.particulator.n_sd)),
 85            "multiplicity": tuple([] for _ in range(self.particulator.n_sd)),
 86        }
 87        self.settings = settings
 88
 89        self.__sanity_checks(attributes, volume)
 90
 91    def __sanity_checks(self, attributes, volume):
 92        for attribute in attributes.values():
 93            assert attribute.shape[0] == self.particulator.n_sd
 94        np.testing.assert_approx_equal(
 95            sum(attributes["multiplicity"]) / volume,
 96            sum(
 97                mode.norm_factor
 98                for mode in self.settings.aerosol_modes_by_kappa.values()
 99            ),
100            significant=4,
101        )
102
103    def _save(self, output):
104        for key, attr in self.output_attributes.items():
105            attr_data = self.particulator.attributes[key].to_ndarray()
106            for drop_id in range(self.particulator.n_sd):
107                attr[drop_id].append(attr_data[drop_id])
108        super()._save(output)
109
110    def run(self):
111        output_products = super()._run(
112            self.settings.nt, self.settings.steps_per_output_interval
113        )
114        return {"products": output_products, "attributes": self.output_attributes}
 15class Simulation(BasicSimulation):
 16    def __init__(
 17        self,
 18        settings,
 19        products=None,
 20        scipy_solver=False,
 21    ):
 22
 23        environment = Parcel(
 24            dt=settings.timestep,
 25            p0=settings.initial_pressure,
 26            initial_relative_humidity=settings.initial_relative_humidity,
 27            T0=settings.initial_temperature,
 28            w=settings.vertical_velocity,
 29            mass_of_dry_air=44 * si.kg,
 30            backend=CPU(formulae=settings.formulae),
 31        )
 32        volume = environment.mass_of_dry_air / settings.initial_air_density
 33        attributes = {
 34            k: np.empty(0)
 35            for k in ("dry volume", "kappa times dry volume", "multiplicity")
 36        }
 37
 38        assert len(settings.aerosol_modes_by_kappa.keys()) == 1
 39        kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0]
 40        spectrum = settings.aerosol_modes_by_kappa[kappa]
 41
 42        r_dry, n_per_volume = ConstantMultiplicity(spectrum).sample_deterministic(
 43            settings.n_sd
 44        )
 45        v_dry = settings.formulae.trivia.volume(radius=r_dry)
 46        attributes["multiplicity"] = np.append(
 47            attributes["multiplicity"], n_per_volume * volume
 48        )
 49        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 50        attributes["kappa times dry volume"] = np.append(
 51            attributes["kappa times dry volume"], v_dry * kappa
 52        )
 53        r_wet = equilibrate_wet_radii(
 54            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
 55            environment=environment,
 56            kappa_times_dry_volume=attributes["kappa times dry volume"],
 57        )
 58        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
 59
 60        particulator = Particulator(
 61            n_sd=settings.n_sd,
 62            environment=environment,
 63            dynamics=(
 64                AmbientThermodynamics(),
 65                Condensation(rtol_thd=settings.rtol_thd, rtol_x=settings.rtol_x),
 66            ),
 67            attributes=attributes,
 68            products=products,
 69            requested_attributes=(
 70                "critical saturation",
 71                "equilibrium saturation",
 72                "critical volume",
 73            ),
 74        )
 75
 76        super().__init__(particulator=particulator)
 77        if scipy_solver:
 78            scipy_ode_condensation_solver.patch_particulator(self.particulator)
 79
 80        self.output_attributes = {
 81            "volume": tuple([] for _ in range(self.particulator.n_sd)),
 82            "dry volume": tuple([] for _ in range(self.particulator.n_sd)),
 83            "critical saturation": tuple([] for _ in range(self.particulator.n_sd)),
 84            "equilibrium saturation": tuple([] for _ in range(self.particulator.n_sd)),
 85            "critical volume": tuple([] for _ in range(self.particulator.n_sd)),
 86            "multiplicity": tuple([] for _ in range(self.particulator.n_sd)),
 87        }
 88        self.settings = settings
 89
 90        self.__sanity_checks(attributes, volume)
 91
 92    def __sanity_checks(self, attributes, volume):
 93        for attribute in attributes.values():
 94            assert attribute.shape[0] == self.particulator.n_sd
 95        np.testing.assert_approx_equal(
 96            sum(attributes["multiplicity"]) / volume,
 97            sum(
 98                mode.norm_factor
 99                for mode in self.settings.aerosol_modes_by_kappa.values()
100            ),
101            significant=4,
102        )
103
104    def _save(self, output):
105        for key, attr in self.output_attributes.items():
106            attr_data = self.particulator.attributes[key].to_ndarray()
107            for drop_id in range(self.particulator.n_sd):
108                attr[drop_id].append(attr_data[drop_id])
109        super()._save(output)
110
111    def run(self):
112        output_products = super()._run(
113            self.settings.nt, self.settings.steps_per_output_interval
114        )
115        return {"products": output_products, "attributes": self.output_attributes}
Simulation(settings, products=None, scipy_solver=False)
16    def __init__(
17        self,
18        settings,
19        products=None,
20        scipy_solver=False,
21    ):
22
23        environment = Parcel(
24            dt=settings.timestep,
25            p0=settings.initial_pressure,
26            initial_relative_humidity=settings.initial_relative_humidity,
27            T0=settings.initial_temperature,
28            w=settings.vertical_velocity,
29            mass_of_dry_air=44 * si.kg,
30            backend=CPU(formulae=settings.formulae),
31        )
32        volume = environment.mass_of_dry_air / settings.initial_air_density
33        attributes = {
34            k: np.empty(0)
35            for k in ("dry volume", "kappa times dry volume", "multiplicity")
36        }
37
38        assert len(settings.aerosol_modes_by_kappa.keys()) == 1
39        kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0]
40        spectrum = settings.aerosol_modes_by_kappa[kappa]
41
42        r_dry, n_per_volume = ConstantMultiplicity(spectrum).sample_deterministic(
43            settings.n_sd
44        )
45        v_dry = settings.formulae.trivia.volume(radius=r_dry)
46        attributes["multiplicity"] = np.append(
47            attributes["multiplicity"], n_per_volume * volume
48        )
49        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
50        attributes["kappa times dry volume"] = np.append(
51            attributes["kappa times dry volume"], v_dry * kappa
52        )
53        r_wet = equilibrate_wet_radii(
54            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
55            environment=environment,
56            kappa_times_dry_volume=attributes["kappa times dry volume"],
57        )
58        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
59
60        particulator = Particulator(
61            n_sd=settings.n_sd,
62            environment=environment,
63            dynamics=(
64                AmbientThermodynamics(),
65                Condensation(rtol_thd=settings.rtol_thd, rtol_x=settings.rtol_x),
66            ),
67            attributes=attributes,
68            products=products,
69            requested_attributes=(
70                "critical saturation",
71                "equilibrium saturation",
72                "critical volume",
73            ),
74        )
75
76        super().__init__(particulator=particulator)
77        if scipy_solver:
78            scipy_ode_condensation_solver.patch_particulator(self.particulator)
79
80        self.output_attributes = {
81            "volume": tuple([] for _ in range(self.particulator.n_sd)),
82            "dry volume": tuple([] for _ in range(self.particulator.n_sd)),
83            "critical saturation": tuple([] for _ in range(self.particulator.n_sd)),
84            "equilibrium saturation": tuple([] for _ in range(self.particulator.n_sd)),
85            "critical volume": tuple([] for _ in range(self.particulator.n_sd)),
86            "multiplicity": tuple([] for _ in range(self.particulator.n_sd)),
87        }
88        self.settings = settings
89
90        self.__sanity_checks(attributes, volume)
output_attributes
settings
def run(self):
111    def run(self):
112        output_products = super()._run(
113            self.settings.nt, self.settings.steps_per_output_interval
114        )
115        return {"products": output_products, "attributes": self.output_attributes}