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