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 Builder
  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
 34        builder = Builder(
 35            n_sd=N_SD_NON_GCCN + n_gccn,
 36            backend=CPU(
 37                formulae=settings.formulae,
 38                override_jit_flags={"parallel": False},
 39            ),
 40            environment=Parcel(
 41                dt=settings.dt,
 42                mass_of_dry_air=666 * si.kg,
 43                p0=settings.p0,
 44                initial_relative_humidity=settings.RH0,
 45                T0=settings.T0,
 46                w=settings.vertical_velocity,
 47                z0=settings.z0,
 48            ),
 49            dynamics=[
 50                # TODO #1266: order matters here, but error message is not saying it!
 51                AmbientThermodynamics(),
 52                Condensation(),
 53            ]
 54            + (
 55                []
 56                if not gravitational_coalsecence
 57                else [Coalescence(collision_kernel=Geometric())]
 58            ),
 59        )
 60
 61        additional_derived_attributes = ("radius", "equilibrium saturation")
 62        for additional_derived_attribute in additional_derived_attributes:
 63            builder.request_attribute(additional_derived_attribute)
 64
 65        self.r_dry, n_in_unit_volume = Logarithmic(
 66            spectrum=settings.dry_radii_spectrum,
 67        ).sample_deterministic(builder.particulator.n_sd - n_gccn)
 68
 69        if gccn:
 70            nonzero_concentration_mask = np.nonzero(table_3.NA)
 71            self.r_dry = np.concatenate(
 72                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 73            )
 74            n_in_unit_volume = np.concatenate(
 75                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 76            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 77
 78        pd0 = settings.formulae.trivia.p_d(
 79            settings.p0,
 80            settings.formulae.trivia.water_vapour_mixing_ratio(
 81                settings.p0,
 82                settings.RH0,
 83                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 84            ),
 85        )
 86        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 87
 88        attributes = builder.particulator.environment.init_attributes(
 89            n_in_dv=n_in_unit_volume
 90            * builder.particulator.environment.mass_of_dry_air
 91            / rhod0,
 92            kappa=settings.kappa,
 93            r_dry=self.r_dry,
 94        )
 95
 96        super().__init__(
 97            builder.build(
 98                attributes=attributes,
 99                products=(
100                    PeakSaturation(name="S_max"),
101                    ParcelDisplacement(name="z"),
102                    Time(name="t"),
103                    ActivatedMeanRadius(
104                        name="r_mean_act", count_activated=True, count_unactivated=False
105                    ),
106                    RadiusStandardDeviation(
107                        name="r_std_act", count_activated=True, count_unactivated=False
108                    ),
109                ),
110            )
111        )
112
113        # TODO #1266: copied from G & P 2023
114        self.output_attributes = {
115            attr: tuple([] for _ in range(self.particulator.n_sd))
116            for attr in additional_derived_attributes
117        }
118
119    def run(
120        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
121    ):  # TODO #1266: essentially copied from G & P 2023
122        output_products = super()._run(
123            nt=n_steps, steps_per_output_interval=steps_per_output_interval
124        )
125        return {"products": output_products, "attributes": self.output_attributes}
126
127    def _save(self, output):  # TODO #1266: copied from G&P 2023
128        for key, attr in self.output_attributes.items():
129            attr_data = self.particulator.attributes[key].to_ndarray()
130            for drop_id in range(self.particulator.n_sd):
131                attr[drop_id].append(attr_data[drop_id])
132        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
 35        builder = Builder(
 36            n_sd=N_SD_NON_GCCN + n_gccn,
 37            backend=CPU(
 38                formulae=settings.formulae,
 39                override_jit_flags={"parallel": False},
 40            ),
 41            environment=Parcel(
 42                dt=settings.dt,
 43                mass_of_dry_air=666 * si.kg,
 44                p0=settings.p0,
 45                initial_relative_humidity=settings.RH0,
 46                T0=settings.T0,
 47                w=settings.vertical_velocity,
 48                z0=settings.z0,
 49            ),
 50            dynamics=[
 51                # TODO #1266: order matters here, but error message is not saying it!
 52                AmbientThermodynamics(),
 53                Condensation(),
 54            ]
 55            + (
 56                []
 57                if not gravitational_coalsecence
 58                else [Coalescence(collision_kernel=Geometric())]
 59            ),
 60        )
 61
 62        additional_derived_attributes = ("radius", "equilibrium saturation")
 63        for additional_derived_attribute in additional_derived_attributes:
 64            builder.request_attribute(additional_derived_attribute)
 65
 66        self.r_dry, n_in_unit_volume = Logarithmic(
 67            spectrum=settings.dry_radii_spectrum,
 68        ).sample_deterministic(builder.particulator.n_sd - n_gccn)
 69
 70        if gccn:
 71            nonzero_concentration_mask = np.nonzero(table_3.NA)
 72            self.r_dry = np.concatenate(
 73                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 74            )
 75            n_in_unit_volume = np.concatenate(
 76                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 77            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 78
 79        pd0 = settings.formulae.trivia.p_d(
 80            settings.p0,
 81            settings.formulae.trivia.water_vapour_mixing_ratio(
 82                settings.p0,
 83                settings.RH0,
 84                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 85            ),
 86        )
 87        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 88
 89        attributes = builder.particulator.environment.init_attributes(
 90            n_in_dv=n_in_unit_volume
 91            * builder.particulator.environment.mass_of_dry_air
 92            / rhod0,
 93            kappa=settings.kappa,
 94            r_dry=self.r_dry,
 95        )
 96
 97        super().__init__(
 98            builder.build(
 99                attributes=attributes,
100                products=(
101                    PeakSaturation(name="S_max"),
102                    ParcelDisplacement(name="z"),
103                    Time(name="t"),
104                    ActivatedMeanRadius(
105                        name="r_mean_act", count_activated=True, count_unactivated=False
106                    ),
107                    RadiusStandardDeviation(
108                        name="r_std_act", count_activated=True, count_unactivated=False
109                    ),
110                ),
111            )
112        )
113
114        # TODO #1266: copied from G & P 2023
115        self.output_attributes = {
116            attr: tuple([] for _ in range(self.particulator.n_sd))
117            for attr in additional_derived_attributes
118        }
119
120    def run(
121        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
122    ):  # TODO #1266: essentially copied from G & P 2023
123        output_products = super()._run(
124            nt=n_steps, steps_per_output_interval=steps_per_output_interval
125        )
126        return {"products": output_products, "attributes": self.output_attributes}
127
128    def _save(self, output):  # TODO #1266: copied from G&P 2023
129        for key, attr in self.output_attributes.items():
130            attr_data = self.particulator.attributes[key].to_ndarray()
131            for drop_id in range(self.particulator.n_sd):
132                attr[drop_id].append(attr_data[drop_id])
133        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
 35        builder = Builder(
 36            n_sd=N_SD_NON_GCCN + n_gccn,
 37            backend=CPU(
 38                formulae=settings.formulae,
 39                override_jit_flags={"parallel": False},
 40            ),
 41            environment=Parcel(
 42                dt=settings.dt,
 43                mass_of_dry_air=666 * si.kg,
 44                p0=settings.p0,
 45                initial_relative_humidity=settings.RH0,
 46                T0=settings.T0,
 47                w=settings.vertical_velocity,
 48                z0=settings.z0,
 49            ),
 50            dynamics=[
 51                # TODO #1266: order matters here, but error message is not saying it!
 52                AmbientThermodynamics(),
 53                Condensation(),
 54            ]
 55            + (
 56                []
 57                if not gravitational_coalsecence
 58                else [Coalescence(collision_kernel=Geometric())]
 59            ),
 60        )
 61
 62        additional_derived_attributes = ("radius", "equilibrium saturation")
 63        for additional_derived_attribute in additional_derived_attributes:
 64            builder.request_attribute(additional_derived_attribute)
 65
 66        self.r_dry, n_in_unit_volume = Logarithmic(
 67            spectrum=settings.dry_radii_spectrum,
 68        ).sample_deterministic(builder.particulator.n_sd - n_gccn)
 69
 70        if gccn:
 71            nonzero_concentration_mask = np.nonzero(table_3.NA)
 72            self.r_dry = np.concatenate(
 73                [self.r_dry, table_3.RD[nonzero_concentration_mask]]
 74            )
 75            n_in_unit_volume = np.concatenate(
 76                [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]]
 77            )  # TODO #1266: check which temp, pres, RH assumed in the paper for NA???
 78
 79        pd0 = settings.formulae.trivia.p_d(
 80            settings.p0,
 81            settings.formulae.trivia.water_vapour_mixing_ratio(
 82                settings.p0,
 83                settings.RH0,
 84                settings.formulae.saturation_vapour_pressure.pvs_water(settings.T0),
 85            ),
 86        )
 87        rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0)
 88
 89        attributes = builder.particulator.environment.init_attributes(
 90            n_in_dv=n_in_unit_volume
 91            * builder.particulator.environment.mass_of_dry_air
 92            / rhod0,
 93            kappa=settings.kappa,
 94            r_dry=self.r_dry,
 95        )
 96
 97        super().__init__(
 98            builder.build(
 99                attributes=attributes,
100                products=(
101                    PeakSaturation(name="S_max"),
102                    ParcelDisplacement(name="z"),
103                    Time(name="t"),
104                    ActivatedMeanRadius(
105                        name="r_mean_act", count_activated=True, count_unactivated=False
106                    ),
107                    RadiusStandardDeviation(
108                        name="r_std_act", count_activated=True, count_unactivated=False
109                    ),
110                ),
111            )
112        )
113
114        # TODO #1266: copied from G & P 2023
115        self.output_attributes = {
116            attr: tuple([] for _ in range(self.particulator.n_sd))
117            for attr in additional_derived_attributes
118        }
output_attributes
def run(self, *, n_steps: int = 2250, steps_per_output_interval: int = 10):
120    def run(
121        self, *, n_steps: int = 2250, steps_per_output_interval: int = 10
122    ):  # TODO #1266: essentially copied from G & P 2023
123        output_products = super()._run(
124            nt=n_steps, steps_per_output_interval=steps_per_output_interval
125        )
126        return {"products": output_products, "attributes": self.output_attributes}