PySDM_examples.Jensen_and_Nugent_2017.simulation

  1import numpy as np
  2from PySDM_examples.utils import BasicSimulation
  3from PySDM_examples.Jensen_and_Nugent_2017.settings import Settings
  4from PySDM_examples.Jensen_and_Nugent_2017 import table_3
  5from PySDM import Particulator
  6from PySDM.physics import si
  7from PySDM.backends import CPU
  8from PySDM.products import (
  9    PeakSaturation,
 10    ParcelDisplacement,
 11    Time,
 12    ActivatedMeanRadius,
 13    RadiusStandardDeviation,
 14)
 15from PySDM.environments import Parcel
 16from PySDM.dynamics import Condensation, AmbientThermodynamics, Coalescence
 17from PySDM.dynamics.collisions.collision_kernels import Geometric
 18from PySDM.initialisation.sampling.spectral_sampling import Logarithmic
 19
 20# note: 100 in caption of Table 1
 21N_SD_NON_GCCN = 100
 22
 23
 24class Simulation(BasicSimulation):
 25    def __init__(
 26        self,
 27        settings: Settings,
 28        gccn: bool = False,
 29        gravitational_coalsecence: bool = False,
 30    ):
 31
 32        n_gccn = np.count_nonzero(table_3.NA) if gccn else 0
 33        environment = Parcel(
 34            dt=settings.dt,
 35            mass_of_dry_air=666 * si.kg,
 36            p0=settings.p0,
 37            initial_relative_humidity=settings.RH0,
 38            T0=settings.T0,
 39            w=settings.vertical_velocity,
 40            z0=settings.z0,
 41            backend=CPU(
 42                formulae=settings.formulae,
 43                override_jit_flags={"parallel": False},
 44            ),
 45        )
 46
 47        self.r_dry, n_in_unit_volume = Logarithmic(
 48            spectrum=settings.dry_radii_spectrum,
 49        ).sample_deterministic(N_SD_NON_GCCN)
 50
 51        if gccn:
 52            nonzero_concentration_mask = np.nonzero(table_3.NA)
 53            self.r_dry = np.concatenate(
 54                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 55            )
 56            n_in_unit_volume = np.concatenate(
 57                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 58            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 59
 60        pd0 = settings.formulae.trivia.p_d(
 61            settings.p0,
 62            settings.formulae.trivia.water_vapour_mixing_ratio(
 63                settings.p0,
 64                settings.RH0,
 65                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 66            ),
 67        )
 68        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 69
 70        attributes = environment.init_attributes(
 71            n_in_dv=n_in_unit_volume * environment.mass_of_dry_air / rhod0,
 72            kappa=settings.kappa,
 73            r_dry=self.r_dry,
 74        )
 75
 76        super().__init__(
 77            Particulator(
 78                n_sd=N_SD_NON_GCCN + n_gccn,
 79                environment=environment,
 80                dynamics=[
 81                    # TODO #1266: order matters here, but error message is not saying it!
 82                    AmbientThermodynamics(),
 83                    Condensation(),
 84                ]
 85                + (
 86                    []
 87                    if not gravitational_coalsecence
 88                    else [Coalescence(collision_kernel=Geometric())]
 89                ),
 90                attributes=attributes,
 91                products=(
 92                    PeakSaturation(name="S_max"),
 93                    ParcelDisplacement(name="z"),
 94                    Time(name="t"),
 95                    ActivatedMeanRadius(
 96                        name="r_mean_act", count_activated=True, count_unactivated=False
 97                    ),
 98                    RadiusStandardDeviation(
 99                        name="r_std_act", count_activated=True, count_unactivated=False
100                    ),
101                ),
102                requested_attributes=(
103                    additional_derived_attributes := (
104                        "radius",
105                        "equilibrium saturation",
106                    )
107                ),
108            )
109        )
110
111        # TODO #1266: copied from G & P 2023
112        self.output_attributes = {
113            attr: tuple([] for _ in range(self.particulator.n_sd))
114            for attr in additional_derived_attributes
115        }
116
117    def run(
118        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
119    ):  # TODO #1266: essentially copied from G & P 2023
120        output_products = super()._run(
121            nt=n_steps, steps_per_output_interval=steps_per_output_interval
122        )
123        return {"products": output_products, "attributes": self.output_attributes}
124
125    def _save(self, output):  # TODO #1266: copied from G&P 2023
126        for key, attr in self.output_attributes.items():
127            attr_data = self.particulator.attributes[key].to_ndarray()
128            for drop_id in range(self.particulator.n_sd):
129                attr[drop_id].append(attr_data[drop_id])
130        super()._save(output)
N_SD_NON_GCCN = 100
 25class Simulation(BasicSimulation):
 26    def __init__(
 27        self,
 28        settings: Settings,
 29        gccn: bool = False,
 30        gravitational_coalsecence: bool = False,
 31    ):
 32
 33        n_gccn = np.count_nonzero(table_3.NA) if gccn else 0
 34        environment = Parcel(
 35            dt=settings.dt,
 36            mass_of_dry_air=666 * si.kg,
 37            p0=settings.p0,
 38            initial_relative_humidity=settings.RH0,
 39            T0=settings.T0,
 40            w=settings.vertical_velocity,
 41            z0=settings.z0,
 42            backend=CPU(
 43                formulae=settings.formulae,
 44                override_jit_flags={"parallel": False},
 45            ),
 46        )
 47
 48        self.r_dry, n_in_unit_volume = Logarithmic(
 49            spectrum=settings.dry_radii_spectrum,
 50        ).sample_deterministic(N_SD_NON_GCCN)
 51
 52        if gccn:
 53            nonzero_concentration_mask = np.nonzero(table_3.NA)
 54            self.r_dry = np.concatenate(
 55                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 56            )
 57            n_in_unit_volume = np.concatenate(
 58                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 59            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 60
 61        pd0 = settings.formulae.trivia.p_d(
 62            settings.p0,
 63            settings.formulae.trivia.water_vapour_mixing_ratio(
 64                settings.p0,
 65                settings.RH0,
 66                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 67            ),
 68        )
 69        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 70
 71        attributes = environment.init_attributes(
 72            n_in_dv=n_in_unit_volume * environment.mass_of_dry_air / rhod0,
 73            kappa=settings.kappa,
 74            r_dry=self.r_dry,
 75        )
 76
 77        super().__init__(
 78            Particulator(
 79                n_sd=N_SD_NON_GCCN + n_gccn,
 80                environment=environment,
 81                dynamics=[
 82                    # TODO #1266: order matters here, but error message is not saying it!
 83                    AmbientThermodynamics(),
 84                    Condensation(),
 85                ]
 86                + (
 87                    []
 88                    if not gravitational_coalsecence
 89                    else [Coalescence(collision_kernel=Geometric())]
 90                ),
 91                attributes=attributes,
 92                products=(
 93                    PeakSaturation(name="S_max"),
 94                    ParcelDisplacement(name="z"),
 95                    Time(name="t"),
 96                    ActivatedMeanRadius(
 97                        name="r_mean_act", count_activated=True, count_unactivated=False
 98                    ),
 99                    RadiusStandardDeviation(
100                        name="r_std_act", count_activated=True, count_unactivated=False
101                    ),
102                ),
103                requested_attributes=(
104                    additional_derived_attributes := (
105                        "radius",
106                        "equilibrium saturation",
107                    )
108                ),
109            )
110        )
111
112        # TODO #1266: copied from G & P 2023
113        self.output_attributes = {
114            attr: tuple([] for _ in range(self.particulator.n_sd))
115            for attr in additional_derived_attributes
116        }
117
118    def run(
119        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
120    ):  # TODO #1266: essentially copied from G & P 2023
121        output_products = super()._run(
122            nt=n_steps, steps_per_output_interval=steps_per_output_interval
123        )
124        return {"products": output_products, "attributes": self.output_attributes}
125
126    def _save(self, output):  # TODO #1266: copied from G&P 2023
127        for key, attr in self.output_attributes.items():
128            attr_data = self.particulator.attributes[key].to_ndarray()
129            for drop_id in range(self.particulator.n_sd):
130                attr[drop_id].append(attr_data[drop_id])
131        super()._save(output)
Simulation( settings: PySDM_examples.Jensen_and_Nugent_2017.settings.Settings, gccn: bool = False, gravitational_coalsecence: bool = False)
 26    def __init__(
 27        self,
 28        settings: Settings,
 29        gccn: bool = False,
 30        gravitational_coalsecence: bool = False,
 31    ):
 32
 33        n_gccn = np.count_nonzero(table_3.NA) if gccn else 0
 34        environment = Parcel(
 35            dt=settings.dt,
 36            mass_of_dry_air=666 * si.kg,
 37            p0=settings.p0,
 38            initial_relative_humidity=settings.RH0,
 39            T0=settings.T0,
 40            w=settings.vertical_velocity,
 41            z0=settings.z0,
 42            backend=CPU(
 43                formulae=settings.formulae,
 44                override_jit_flags={"parallel": False},
 45            ),
 46        )
 47
 48        self.r_dry, n_in_unit_volume = Logarithmic(
 49            spectrum=settings.dry_radii_spectrum,
 50        ).sample_deterministic(N_SD_NON_GCCN)
 51
 52        if gccn:
 53            nonzero_concentration_mask = np.nonzero(table_3.NA)
 54            self.r_dry = np.concatenate(
 55                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 56            )
 57            n_in_unit_volume = np.concatenate(
 58                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 59            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 60
 61        pd0 = settings.formulae.trivia.p_d(
 62            settings.p0,
 63            settings.formulae.trivia.water_vapour_mixing_ratio(
 64                settings.p0,
 65                settings.RH0,
 66                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 67            ),
 68        )
 69        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 70
 71        attributes = environment.init_attributes(
 72            n_in_dv=n_in_unit_volume * environment.mass_of_dry_air / rhod0,
 73            kappa=settings.kappa,
 74            r_dry=self.r_dry,
 75        )
 76
 77        super().__init__(
 78            Particulator(
 79                n_sd=N_SD_NON_GCCN + n_gccn,
 80                environment=environment,
 81                dynamics=[
 82                    # TODO #1266: order matters here, but error message is not saying it!
 83                    AmbientThermodynamics(),
 84                    Condensation(),
 85                ]
 86                + (
 87                    []
 88                    if not gravitational_coalsecence
 89                    else [Coalescence(collision_kernel=Geometric())]
 90                ),
 91                attributes=attributes,
 92                products=(
 93                    PeakSaturation(name="S_max"),
 94                    ParcelDisplacement(name="z"),
 95                    Time(name="t"),
 96                    ActivatedMeanRadius(
 97                        name="r_mean_act", count_activated=True, count_unactivated=False
 98                    ),
 99                    RadiusStandardDeviation(
100                        name="r_std_act", count_activated=True, count_unactivated=False
101                    ),
102                ),
103                requested_attributes=(
104                    additional_derived_attributes := (
105                        "radius",
106                        "equilibrium saturation",
107                    )
108                ),
109            )
110        )
111
112        # TODO #1266: copied from G & P 2023
113        self.output_attributes = {
114            attr: tuple([] for _ in range(self.particulator.n_sd))
115            for attr in additional_derived_attributes
116        }
output_attributes
def run(self, *, n_steps: int = 2250, steps_per_output_interval: int = 10):
118    def run(
119        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
120    ):  # TODO #1266: essentially copied from G & P 2023
121        output_products = super()._run(
122            nt=n_steps, steps_per_output_interval=steps_per_output_interval
123        )
124        return {"products": output_products, "attributes": self.output_attributes}