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