PySDM_examples.Pyrcel.simulation

  1import numpy as np
  2from PySDM_examples.utils import BasicSimulation
  3
  4from PySDM import Builder
  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        rtol_thd=1e-10,
 21        rtol_x=1e-10,
 22        mass_of_dry_air=44 * si.kg,
 23    ):
 24        n_sd = sum(settings.n_sd_per_mode)
 25        builder = Builder(
 26            n_sd=n_sd,
 27            backend=CPU(
 28                formulae=settings.formulae, override_jit_flags={"parallel": False}
 29            ),
 30            environment=Parcel(
 31                dt=settings.timestep,
 32                p0=settings.initial_pressure,
 33                initial_water_vapour_mixing_ratio=settings.initial_vapour_mixing_ratio,
 34                T0=settings.initial_temperature,
 35                w=settings.vertical_velocity,
 36                mass_of_dry_air=mass_of_dry_air,
 37            ),
 38        )
 39        builder.add_dynamic(AmbientThermodynamics())
 40        builder.add_dynamic(Condensation(rtol_thd=rtol_thd, rtol_x=rtol_x))
 41
 42        volume = (
 43            builder.particulator.environment.mass_of_dry_air
 44            / settings.initial_air_density
 45        )
 46        attributes = {
 47            k: np.empty(0)
 48            for k in ("dry volume", "kappa times dry volume", "multiplicity")
 49        }
 50        for i, (kappa, spectrum) in enumerate(settings.aerosol_modes_by_kappa.items()):
 51            sampling = ConstantMultiplicity(spectrum)
 52            r_dry, n_per_volume = sampling.sample_deterministic(
 53                settings.n_sd_per_mode[i]
 54            )
 55            v_dry = settings.formulae.trivia.volume(radius=r_dry)
 56            attributes["multiplicity"] = np.append(
 57                attributes["multiplicity"], n_per_volume * volume
 58            )
 59            attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 60            attributes["kappa times dry volume"] = np.append(
 61                attributes["kappa times dry volume"], v_dry * kappa
 62            )
 63        r_wet = equilibrate_wet_radii(
 64            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
 65            environment=builder.particulator.environment,
 66            kappa_times_dry_volume=attributes["kappa times dry volume"],
 67        )
 68        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
 69
 70        super().__init__(
 71            particulator=builder.build(attributes=attributes, products=products)
 72        )
 73        if scipy_solver:
 74            scipy_ode_condensation_solver.patch_particulator(self.particulator)
 75
 76        self.output_attributes = {
 77            "volume": tuple([] for _ in range(self.particulator.n_sd))
 78        }
 79        self.settings = settings
 80
 81        self.__sanity_checks(attributes, volume)
 82
 83    def __sanity_checks(self, attributes, volume):
 84        for attribute in attributes.values():
 85            assert attribute.shape[0] == self.particulator.n_sd
 86        np.testing.assert_approx_equal(
 87            sum(attributes["multiplicity"]) / volume,
 88            sum(
 89                mode.norm_factor
 90                for mode in self.settings.aerosol_modes_by_kappa.values()
 91            ),
 92            significant=4,
 93        )
 94
 95    def _save(self, output):
 96        for key, attr in self.output_attributes.items():
 97            attr_data = self.particulator.attributes[key].to_ndarray()
 98            for drop_id in range(self.particulator.n_sd):
 99                attr[drop_id].append(attr_data[drop_id])
100        super()._save(output)
101
102    def run(self, observers=()):
103        for observer in observers:
104            self.particulator.observers.append(observer)
105        output_products = super()._run(
106            self.settings.nt, self.settings.steps_per_output_interval
107        )
108        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        rtol_thd=1e-10,
 22        rtol_x=1e-10,
 23        mass_of_dry_air=44 * si.kg,
 24    ):
 25        n_sd = sum(settings.n_sd_per_mode)
 26        builder = Builder(
 27            n_sd=n_sd,
 28            backend=CPU(
 29                formulae=settings.formulae, override_jit_flags={"parallel": False}
 30            ),
 31            environment=Parcel(
 32                dt=settings.timestep,
 33                p0=settings.initial_pressure,
 34                initial_water_vapour_mixing_ratio=settings.initial_vapour_mixing_ratio,
 35                T0=settings.initial_temperature,
 36                w=settings.vertical_velocity,
 37                mass_of_dry_air=mass_of_dry_air,
 38            ),
 39        )
 40        builder.add_dynamic(AmbientThermodynamics())
 41        builder.add_dynamic(Condensation(rtol_thd=rtol_thd, rtol_x=rtol_x))
 42
 43        volume = (
 44            builder.particulator.environment.mass_of_dry_air
 45            / settings.initial_air_density
 46        )
 47        attributes = {
 48            k: np.empty(0)
 49            for k in ("dry volume", "kappa times dry volume", "multiplicity")
 50        }
 51        for i, (kappa, spectrum) in enumerate(settings.aerosol_modes_by_kappa.items()):
 52            sampling = ConstantMultiplicity(spectrum)
 53            r_dry, n_per_volume = sampling.sample_deterministic(
 54                settings.n_sd_per_mode[i]
 55            )
 56            v_dry = settings.formulae.trivia.volume(radius=r_dry)
 57            attributes["multiplicity"] = np.append(
 58                attributes["multiplicity"], n_per_volume * volume
 59            )
 60            attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 61            attributes["kappa times dry volume"] = np.append(
 62                attributes["kappa times dry volume"], v_dry * kappa
 63            )
 64        r_wet = equilibrate_wet_radii(
 65            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
 66            environment=builder.particulator.environment,
 67            kappa_times_dry_volume=attributes["kappa times dry volume"],
 68        )
 69        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
 70
 71        super().__init__(
 72            particulator=builder.build(attributes=attributes, products=products)
 73        )
 74        if scipy_solver:
 75            scipy_ode_condensation_solver.patch_particulator(self.particulator)
 76
 77        self.output_attributes = {
 78            "volume": tuple([] for _ in range(self.particulator.n_sd))
 79        }
 80        self.settings = settings
 81
 82        self.__sanity_checks(attributes, volume)
 83
 84    def __sanity_checks(self, attributes, volume):
 85        for attribute in attributes.values():
 86            assert attribute.shape[0] == self.particulator.n_sd
 87        np.testing.assert_approx_equal(
 88            sum(attributes["multiplicity"]) / volume,
 89            sum(
 90                mode.norm_factor
 91                for mode in self.settings.aerosol_modes_by_kappa.values()
 92            ),
 93            significant=4,
 94        )
 95
 96    def _save(self, output):
 97        for key, attr in self.output_attributes.items():
 98            attr_data = self.particulator.attributes[key].to_ndarray()
 99            for drop_id in range(self.particulator.n_sd):
100                attr[drop_id].append(attr_data[drop_id])
101        super()._save(output)
102
103    def run(self, observers=()):
104        for observer in observers:
105            self.particulator.observers.append(observer)
106        output_products = super()._run(
107            self.settings.nt, self.settings.steps_per_output_interval
108        )
109        return {"products": output_products, "attributes": self.output_attributes}
Simulation( settings, products=None, scipy_solver=False, rtol_thd=1e-10, rtol_x=1e-10, mass_of_dry_air=44.0)
16    def __init__(
17        self,
18        settings,
19        products=None,
20        scipy_solver=False,
21        rtol_thd=1e-10,
22        rtol_x=1e-10,
23        mass_of_dry_air=44 * si.kg,
24    ):
25        n_sd = sum(settings.n_sd_per_mode)
26        builder = Builder(
27            n_sd=n_sd,
28            backend=CPU(
29                formulae=settings.formulae, override_jit_flags={"parallel": False}
30            ),
31            environment=Parcel(
32                dt=settings.timestep,
33                p0=settings.initial_pressure,
34                initial_water_vapour_mixing_ratio=settings.initial_vapour_mixing_ratio,
35                T0=settings.initial_temperature,
36                w=settings.vertical_velocity,
37                mass_of_dry_air=mass_of_dry_air,
38            ),
39        )
40        builder.add_dynamic(AmbientThermodynamics())
41        builder.add_dynamic(Condensation(rtol_thd=rtol_thd, rtol_x=rtol_x))
42
43        volume = (
44            builder.particulator.environment.mass_of_dry_air
45            / settings.initial_air_density
46        )
47        attributes = {
48            k: np.empty(0)
49            for k in ("dry volume", "kappa times dry volume", "multiplicity")
50        }
51        for i, (kappa, spectrum) in enumerate(settings.aerosol_modes_by_kappa.items()):
52            sampling = ConstantMultiplicity(spectrum)
53            r_dry, n_per_volume = sampling.sample_deterministic(
54                settings.n_sd_per_mode[i]
55            )
56            v_dry = settings.formulae.trivia.volume(radius=r_dry)
57            attributes["multiplicity"] = np.append(
58                attributes["multiplicity"], n_per_volume * volume
59            )
60            attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
61            attributes["kappa times dry volume"] = np.append(
62                attributes["kappa times dry volume"], v_dry * kappa
63            )
64        r_wet = equilibrate_wet_radii(
65            r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
66            environment=builder.particulator.environment,
67            kappa_times_dry_volume=attributes["kappa times dry volume"],
68        )
69        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
70
71        super().__init__(
72            particulator=builder.build(attributes=attributes, products=products)
73        )
74        if scipy_solver:
75            scipy_ode_condensation_solver.patch_particulator(self.particulator)
76
77        self.output_attributes = {
78            "volume": tuple([] for _ in range(self.particulator.n_sd))
79        }
80        self.settings = settings
81
82        self.__sanity_checks(attributes, volume)
output_attributes
settings
def run(self, observers=()):
103    def run(self, observers=()):
104        for observer in observers:
105            self.particulator.observers.append(observer)
106        output_products = super()._run(
107            self.settings.nt, self.settings.steps_per_output_interval
108        )
109        return {"products": output_products, "attributes": self.output_attributes}