PySDM_examples.Arabas_and_Pawlowska_2011.simulation
1import numpy as np 2 3from PySDM_examples.utils.basic_simulation import BasicSimulation 4 5from PySDM import products, Particulator 6from PySDM.backends import CPU 7from PySDM.dynamics import AmbientThermodynamics, Condensation 8from PySDM.environments import Parcel 9from PySDM.initialisation import discretise_multiplicities 10from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii 11from PySDM.initialisation.sampling import spectral_sampling 12 13 14class Simulation(BasicSimulation): 15 def __init__( 16 self, 17 settings, 18 product_list=None, 19 ): 20 self.settings = settings 21 22 self.environment = Parcel( 23 dt=settings.dt, 24 mass_of_dry_air=settings.mass_of_dry_air, 25 p0=settings.p0, 26 initial_relative_humidity=settings.RH0, 27 T0=settings.T0, 28 w=settings.w, 29 backend=CPU(settings.formulae), 30 ) 31 32 attributes, self.mode_id = self._make_attributes() 33 34 if product_list is None: 35 product_list = ( 36 products.AmbientRelativeHumidity(name="RH"), 37 products.Time(name="time"), 38 products.AmbientTemperature(name="T"), 39 ) 40 41 particulator = Particulator( 42 n_sd=settings.n_sd, 43 environment=self.environment, 44 dynamics=( 45 AmbientThermodynamics(), 46 Condensation(), 47 ), 48 attributes=attributes, 49 products=product_list, 50 requested_attributes=("radius",), 51 ) 52 53 super().__init__( 54 particulator=particulator, 55 output_attributes=["radius"], 56 ) 57 58 def _make_attributes(self): 59 settings = self.settings 60 formulae = settings.formulae 61 environment = self.environment 62 63 parcel_volume = environment.mass_of_dry_air / settings.initial_air_density 64 65 n_components = len(settings.aerosol_modes_by_kappa) 66 67 if settings.n_sd % n_components != 0: 68 raise ValueError( 69 f"settings.n_sd={settings.n_sd} must be divisible by " 70 f"n_components={n_components}" 71 ) 72 73 n_sd_per_component = settings.n_sd // n_components 74 75 dry_volume_parts = [] 76 kappa_vdry_parts = [] 77 multiplicity_parts = [] 78 mode_id_parts = [] 79 80 for component_id, (kappa, spectrum) in enumerate( 81 settings.aerosol_modes_by_kappa.items() 82 ): 83 r_dry, concentration = spectral_sampling.Logarithmic( 84 spectrum=spectrum 85 ).sample_deterministic(n_sd_per_component) 86 87 v_dry = formulae.trivia.volume(radius=r_dry) 88 89 dry_volume_parts.append(v_dry) 90 kappa_vdry_parts.append(kappa * v_dry) 91 92 multiplicity_parts.append( 93 discretise_multiplicities(concentration * parcel_volume) 94 ) 95 96 mode_id_parts.append( 97 np.full(n_sd_per_component, component_id, dtype=np.int64) 98 ) 99 100 dry_volume = np.concatenate(dry_volume_parts) 101 kappa_times_dry_volume = np.concatenate(kappa_vdry_parts) 102 multiplicity = np.concatenate(multiplicity_parts) 103 mode_id = np.concatenate(mode_id_parts) 104 105 r_wet = equilibrate_wet_radii( 106 r_dry=formulae.trivia.radius(volume=dry_volume), 107 environment=environment, 108 kappa_times_dry_volume=kappa_times_dry_volume, 109 ) 110 111 attributes = { 112 "multiplicity": multiplicity, 113 "dry volume": dry_volume, 114 "kappa times dry volume": kappa_times_dry_volume, 115 "volume": formulae.trivia.volume(radius=r_wet), 116 } 117 118 self._sanity_check_attributes(attributes, mode_id, parcel_volume) 119 120 return attributes, mode_id 121 122 def _sanity_check_attributes(self, attributes, mode_id, volume): 123 for attribute in attributes.values(): 124 assert attribute.shape[0] == self.settings.n_sd 125 126 assert mode_id.shape[0] == self.settings.n_sd 127 128 assert np.all(attributes["multiplicity"] > 0) 129 assert np.all(attributes["dry volume"] > 0) 130 assert np.all(attributes["volume"] >= attributes["dry volume"]) 131 132 kappa_eff = attributes["kappa times dry volume"] / attributes["dry volume"] 133 134 for component_id, kappa in enumerate( 135 self.settings.aerosol_modes_by_kappa.keys() 136 ): 137 mask = mode_id == component_id 138 assert np.any(mask) 139 assert np.allclose(kappa_eff[mask], kappa) 140 141 np.testing.assert_allclose( 142 np.sum(attributes["multiplicity"]) / volume, 143 self.settings.total_aerosol_concentration, 144 rtol=1e-2, 145 ) 146 147 def run(self): 148 return self._run( 149 nt=self.settings.output_interval * self.settings.output_points, 150 steps_per_output_interval=self.settings.output_interval, 151 )
15class Simulation(BasicSimulation): 16 def __init__( 17 self, 18 settings, 19 product_list=None, 20 ): 21 self.settings = settings 22 23 self.environment = Parcel( 24 dt=settings.dt, 25 mass_of_dry_air=settings.mass_of_dry_air, 26 p0=settings.p0, 27 initial_relative_humidity=settings.RH0, 28 T0=settings.T0, 29 w=settings.w, 30 backend=CPU(settings.formulae), 31 ) 32 33 attributes, self.mode_id = self._make_attributes() 34 35 if product_list is None: 36 product_list = ( 37 products.AmbientRelativeHumidity(name="RH"), 38 products.Time(name="time"), 39 products.AmbientTemperature(name="T"), 40 ) 41 42 particulator = Particulator( 43 n_sd=settings.n_sd, 44 environment=self.environment, 45 dynamics=( 46 AmbientThermodynamics(), 47 Condensation(), 48 ), 49 attributes=attributes, 50 products=product_list, 51 requested_attributes=("radius",), 52 ) 53 54 super().__init__( 55 particulator=particulator, 56 output_attributes=["radius"], 57 ) 58 59 def _make_attributes(self): 60 settings = self.settings 61 formulae = settings.formulae 62 environment = self.environment 63 64 parcel_volume = environment.mass_of_dry_air / settings.initial_air_density 65 66 n_components = len(settings.aerosol_modes_by_kappa) 67 68 if settings.n_sd % n_components != 0: 69 raise ValueError( 70 f"settings.n_sd={settings.n_sd} must be divisible by " 71 f"n_components={n_components}" 72 ) 73 74 n_sd_per_component = settings.n_sd // n_components 75 76 dry_volume_parts = [] 77 kappa_vdry_parts = [] 78 multiplicity_parts = [] 79 mode_id_parts = [] 80 81 for component_id, (kappa, spectrum) in enumerate( 82 settings.aerosol_modes_by_kappa.items() 83 ): 84 r_dry, concentration = spectral_sampling.Logarithmic( 85 spectrum=spectrum 86 ).sample_deterministic(n_sd_per_component) 87 88 v_dry = formulae.trivia.volume(radius=r_dry) 89 90 dry_volume_parts.append(v_dry) 91 kappa_vdry_parts.append(kappa * v_dry) 92 93 multiplicity_parts.append( 94 discretise_multiplicities(concentration * parcel_volume) 95 ) 96 97 mode_id_parts.append( 98 np.full(n_sd_per_component, component_id, dtype=np.int64) 99 ) 100 101 dry_volume = np.concatenate(dry_volume_parts) 102 kappa_times_dry_volume = np.concatenate(kappa_vdry_parts) 103 multiplicity = np.concatenate(multiplicity_parts) 104 mode_id = np.concatenate(mode_id_parts) 105 106 r_wet = equilibrate_wet_radii( 107 r_dry=formulae.trivia.radius(volume=dry_volume), 108 environment=environment, 109 kappa_times_dry_volume=kappa_times_dry_volume, 110 ) 111 112 attributes = { 113 "multiplicity": multiplicity, 114 "dry volume": dry_volume, 115 "kappa times dry volume": kappa_times_dry_volume, 116 "volume": formulae.trivia.volume(radius=r_wet), 117 } 118 119 self._sanity_check_attributes(attributes, mode_id, parcel_volume) 120 121 return attributes, mode_id 122 123 def _sanity_check_attributes(self, attributes, mode_id, volume): 124 for attribute in attributes.values(): 125 assert attribute.shape[0] == self.settings.n_sd 126 127 assert mode_id.shape[0] == self.settings.n_sd 128 129 assert np.all(attributes["multiplicity"] > 0) 130 assert np.all(attributes["dry volume"] > 0) 131 assert np.all(attributes["volume"] >= attributes["dry volume"]) 132 133 kappa_eff = attributes["kappa times dry volume"] / attributes["dry volume"] 134 135 for component_id, kappa in enumerate( 136 self.settings.aerosol_modes_by_kappa.keys() 137 ): 138 mask = mode_id == component_id 139 assert np.any(mask) 140 assert np.allclose(kappa_eff[mask], kappa) 141 142 np.testing.assert_allclose( 143 np.sum(attributes["multiplicity"]) / volume, 144 self.settings.total_aerosol_concentration, 145 rtol=1e-2, 146 ) 147 148 def run(self): 149 return self._run( 150 nt=self.settings.output_interval * self.settings.output_points, 151 steps_per_output_interval=self.settings.output_interval, 152 )
Simulation(settings, product_list=None)
16 def __init__( 17 self, 18 settings, 19 product_list=None, 20 ): 21 self.settings = settings 22 23 self.environment = Parcel( 24 dt=settings.dt, 25 mass_of_dry_air=settings.mass_of_dry_air, 26 p0=settings.p0, 27 initial_relative_humidity=settings.RH0, 28 T0=settings.T0, 29 w=settings.w, 30 backend=CPU(settings.formulae), 31 ) 32 33 attributes, self.mode_id = self._make_attributes() 34 35 if product_list is None: 36 product_list = ( 37 products.AmbientRelativeHumidity(name="RH"), 38 products.Time(name="time"), 39 products.AmbientTemperature(name="T"), 40 ) 41 42 particulator = Particulator( 43 n_sd=settings.n_sd, 44 environment=self.environment, 45 dynamics=( 46 AmbientThermodynamics(), 47 Condensation(), 48 ), 49 attributes=attributes, 50 products=product_list, 51 requested_attributes=("radius",), 52 ) 53 54 super().__init__( 55 particulator=particulator, 56 output_attributes=["radius"], 57 )