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)