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