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