PySDM_examples.Lowe_et_al_2019.simulation
1import numpy as np 2from PySDM_examples.utils import BasicSimulation 3 4import PySDM.products as PySDM_products 5from PySDM import Builder 6from PySDM.dynamics import AmbientThermodynamics, Condensation 7from PySDM.environments import Parcel 8from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii 9from PySDM.initialisation.spectra import Sum 10from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity 11 12 13class Simulation(BasicSimulation): 14 def __init__(self, settings, products=None): 15 n_sd = settings.n_sd_per_mode * len(settings.aerosol.modes) 16 builder = Builder( 17 n_sd=n_sd, 18 backend=settings.backend, 19 environment=Parcel( 20 dt=settings.dt, 21 mass_of_dry_air=settings.mass_of_dry_air, 22 p0=settings.p0, 23 initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, 24 T0=settings.T0, 25 w=settings.w, 26 ), 27 dynamics=(AmbientThermodynamics(), Condensation()), 28 ) 29 30 attributes = { 31 "dry volume": np.empty(0), 32 "dry volume organic": np.empty(0), 33 "kappa times dry volume": np.empty(0), 34 "multiplicity": np.ndarray(0), 35 } 36 initial_volume = settings.mass_of_dry_air / settings.rho0 37 for mode in settings.aerosol.modes: 38 r_dry, n_in_dv = ConstantMultiplicity( 39 spectrum=mode["spectrum"] 40 ).sample_deterministic(settings.n_sd_per_mode) 41 v_dry = settings.formulae.trivia.volume(radius=r_dry) 42 attributes["multiplicity"] = np.append( 43 attributes["multiplicity"], n_in_dv * initial_volume 44 ) 45 attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) 46 attributes["dry volume organic"] = np.append( 47 attributes["dry volume organic"], mode["f_org"] * v_dry 48 ) 49 attributes["kappa times dry volume"] = np.append( 50 attributes["kappa times dry volume"], 51 v_dry * mode["kappa"][settings.model], 52 ) 53 for attribute in attributes.values(): 54 assert attribute.shape[0] == n_sd 55 56 np.testing.assert_approx_equal( 57 np.sum(attributes["multiplicity"]) / initial_volume, 58 Sum( 59 tuple( 60 settings.aerosol.modes[i]["spectrum"] 61 for i in range(len(settings.aerosol.modes)) 62 ) 63 ).norm_factor, 64 significant=5, 65 ) 66 r_wet = equilibrate_wet_radii( 67 r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), 68 environment=builder.particulator.environment, 69 kappa_times_dry_volume=attributes["kappa times dry volume"], 70 f_org=attributes["dry volume organic"] / attributes["dry volume"], 71 ) 72 attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) 73 74 if settings.model == "Constant": 75 del attributes["dry volume organic"] 76 77 products = products or ( 78 PySDM_products.ParcelDisplacement(name="z"), 79 PySDM_products.Time(name="t"), 80 PySDM_products.PeakSaturation(name="S_max"), 81 PySDM_products.AmbientRelativeHumidity(name="RH"), 82 PySDM_products.ActivatedParticleConcentration( 83 name="CDNC_cm3", 84 unit="cm^-3", 85 count_activated=True, 86 count_unactivated=False, 87 ), 88 PySDM_products.ParticleSizeSpectrumPerVolume( 89 radius_bins_edges=settings.wet_radius_bins_edges 90 ), 91 PySDM_products.ActivableFraction(name="Activated Fraction"), 92 PySDM_products.WaterMixingRatio(), 93 PySDM_products.AmbientDryAirDensity(name="rhod"), 94 PySDM_products.ActivatedEffectiveRadius( 95 name="reff", count_activated=True, count_unactivated=False 96 ), 97 PySDM_products.ParcelLiquidWaterPath( 98 name="lwp", count_activated=True, count_unactivated=False 99 ), 100 PySDM_products.CloudOpticalDepth(name="tau"), 101 PySDM_products.CloudAlbedo(name="albedo"), 102 ) 103 104 particulator = builder.build(attributes=attributes, products=products) 105 self.settings = settings 106 super().__init__(particulator=particulator) 107 108 def _save_scalars(self, output): 109 for k, v in self.particulator.products.items(): 110 if len(v.shape) > 1 or k in ("lwp", "Activated Fraction", "tau", "albedo"): 111 continue 112 value = v.get() 113 if isinstance(value, np.ndarray) and value.size == 1: 114 value = value[0] 115 output[k].append(value) 116 117 def _save_final_timestep_products(self, output): 118 output["spectrum"] = self.particulator.products[ 119 "particle size spectrum per volume" 120 ].get() 121 122 for name, args_call in { 123 "Activated Fraction": lambda: {"S_max": np.nanmax(output["S_max"])}, 124 "lwp": lambda: {}, 125 "tau": lambda: { 126 "effective_radius": output["reff"][-1], 127 "liquid_water_path": output["lwp"][0], 128 }, 129 "albedo": lambda: {"optical_depth": output["tau"]}, 130 }.items(): 131 output[name] = self.particulator.products[name].get(**args_call()) 132 133 def run(self): 134 output = {k: [] for k in self.particulator.products} 135 for step in self.settings.output_steps: 136 self.particulator.run(step - self.particulator.n_steps) 137 self._save_scalars(output) 138 self._save_final_timestep_products(output) 139 return output
14class Simulation(BasicSimulation): 15 def __init__(self, settings, products=None): 16 n_sd = settings.n_sd_per_mode * len(settings.aerosol.modes) 17 builder = Builder( 18 n_sd=n_sd, 19 backend=settings.backend, 20 environment=Parcel( 21 dt=settings.dt, 22 mass_of_dry_air=settings.mass_of_dry_air, 23 p0=settings.p0, 24 initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, 25 T0=settings.T0, 26 w=settings.w, 27 ), 28 dynamics=(AmbientThermodynamics(), Condensation()), 29 ) 30 31 attributes = { 32 "dry volume": np.empty(0), 33 "dry volume organic": np.empty(0), 34 "kappa times dry volume": np.empty(0), 35 "multiplicity": np.ndarray(0), 36 } 37 initial_volume = settings.mass_of_dry_air / settings.rho0 38 for mode in settings.aerosol.modes: 39 r_dry, n_in_dv = ConstantMultiplicity( 40 spectrum=mode["spectrum"] 41 ).sample_deterministic(settings.n_sd_per_mode) 42 v_dry = settings.formulae.trivia.volume(radius=r_dry) 43 attributes["multiplicity"] = np.append( 44 attributes["multiplicity"], n_in_dv * initial_volume 45 ) 46 attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) 47 attributes["dry volume organic"] = np.append( 48 attributes["dry volume organic"], mode["f_org"] * v_dry 49 ) 50 attributes["kappa times dry volume"] = np.append( 51 attributes["kappa times dry volume"], 52 v_dry * mode["kappa"][settings.model], 53 ) 54 for attribute in attributes.values(): 55 assert attribute.shape[0] == n_sd 56 57 np.testing.assert_approx_equal( 58 np.sum(attributes["multiplicity"]) / initial_volume, 59 Sum( 60 tuple( 61 settings.aerosol.modes[i]["spectrum"] 62 for i in range(len(settings.aerosol.modes)) 63 ) 64 ).norm_factor, 65 significant=5, 66 ) 67 r_wet = equilibrate_wet_radii( 68 r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), 69 environment=builder.particulator.environment, 70 kappa_times_dry_volume=attributes["kappa times dry volume"], 71 f_org=attributes["dry volume organic"] / attributes["dry volume"], 72 ) 73 attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) 74 75 if settings.model == "Constant": 76 del attributes["dry volume organic"] 77 78 products = products or ( 79 PySDM_products.ParcelDisplacement(name="z"), 80 PySDM_products.Time(name="t"), 81 PySDM_products.PeakSaturation(name="S_max"), 82 PySDM_products.AmbientRelativeHumidity(name="RH"), 83 PySDM_products.ActivatedParticleConcentration( 84 name="CDNC_cm3", 85 unit="cm^-3", 86 count_activated=True, 87 count_unactivated=False, 88 ), 89 PySDM_products.ParticleSizeSpectrumPerVolume( 90 radius_bins_edges=settings.wet_radius_bins_edges 91 ), 92 PySDM_products.ActivableFraction(name="Activated Fraction"), 93 PySDM_products.WaterMixingRatio(), 94 PySDM_products.AmbientDryAirDensity(name="rhod"), 95 PySDM_products.ActivatedEffectiveRadius( 96 name="reff", count_activated=True, count_unactivated=False 97 ), 98 PySDM_products.ParcelLiquidWaterPath( 99 name="lwp", count_activated=True, count_unactivated=False 100 ), 101 PySDM_products.CloudOpticalDepth(name="tau"), 102 PySDM_products.CloudAlbedo(name="albedo"), 103 ) 104 105 particulator = builder.build(attributes=attributes, products=products) 106 self.settings = settings 107 super().__init__(particulator=particulator) 108 109 def _save_scalars(self, output): 110 for k, v in self.particulator.products.items(): 111 if len(v.shape) > 1 or k in ("lwp", "Activated Fraction", "tau", "albedo"): 112 continue 113 value = v.get() 114 if isinstance(value, np.ndarray) and value.size == 1: 115 value = value[0] 116 output[k].append(value) 117 118 def _save_final_timestep_products(self, output): 119 output["spectrum"] = self.particulator.products[ 120 "particle size spectrum per volume" 121 ].get() 122 123 for name, args_call in { 124 "Activated Fraction": lambda: {"S_max": np.nanmax(output["S_max"])}, 125 "lwp": lambda: {}, 126 "tau": lambda: { 127 "effective_radius": output["reff"][-1], 128 "liquid_water_path": output["lwp"][0], 129 }, 130 "albedo": lambda: {"optical_depth": output["tau"]}, 131 }.items(): 132 output[name] = self.particulator.products[name].get(**args_call()) 133 134 def run(self): 135 output = {k: [] for k in self.particulator.products} 136 for step in self.settings.output_steps: 137 self.particulator.run(step - self.particulator.n_steps) 138 self._save_scalars(output) 139 self._save_final_timestep_products(output) 140 return output
Simulation(settings, products=None)
15 def __init__(self, settings, products=None): 16 n_sd = settings.n_sd_per_mode * len(settings.aerosol.modes) 17 builder = Builder( 18 n_sd=n_sd, 19 backend=settings.backend, 20 environment=Parcel( 21 dt=settings.dt, 22 mass_of_dry_air=settings.mass_of_dry_air, 23 p0=settings.p0, 24 initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, 25 T0=settings.T0, 26 w=settings.w, 27 ), 28 dynamics=(AmbientThermodynamics(), Condensation()), 29 ) 30 31 attributes = { 32 "dry volume": np.empty(0), 33 "dry volume organic": np.empty(0), 34 "kappa times dry volume": np.empty(0), 35 "multiplicity": np.ndarray(0), 36 } 37 initial_volume = settings.mass_of_dry_air / settings.rho0 38 for mode in settings.aerosol.modes: 39 r_dry, n_in_dv = ConstantMultiplicity( 40 spectrum=mode["spectrum"] 41 ).sample_deterministic(settings.n_sd_per_mode) 42 v_dry = settings.formulae.trivia.volume(radius=r_dry) 43 attributes["multiplicity"] = np.append( 44 attributes["multiplicity"], n_in_dv * initial_volume 45 ) 46 attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) 47 attributes["dry volume organic"] = np.append( 48 attributes["dry volume organic"], mode["f_org"] * v_dry 49 ) 50 attributes["kappa times dry volume"] = np.append( 51 attributes["kappa times dry volume"], 52 v_dry * mode["kappa"][settings.model], 53 ) 54 for attribute in attributes.values(): 55 assert attribute.shape[0] == n_sd 56 57 np.testing.assert_approx_equal( 58 np.sum(attributes["multiplicity"]) / initial_volume, 59 Sum( 60 tuple( 61 settings.aerosol.modes[i]["spectrum"] 62 for i in range(len(settings.aerosol.modes)) 63 ) 64 ).norm_factor, 65 significant=5, 66 ) 67 r_wet = equilibrate_wet_radii( 68 r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), 69 environment=builder.particulator.environment, 70 kappa_times_dry_volume=attributes["kappa times dry volume"], 71 f_org=attributes["dry volume organic"] / attributes["dry volume"], 72 ) 73 attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) 74 75 if settings.model == "Constant": 76 del attributes["dry volume organic"] 77 78 products = products or ( 79 PySDM_products.ParcelDisplacement(name="z"), 80 PySDM_products.Time(name="t"), 81 PySDM_products.PeakSaturation(name="S_max"), 82 PySDM_products.AmbientRelativeHumidity(name="RH"), 83 PySDM_products.ActivatedParticleConcentration( 84 name="CDNC_cm3", 85 unit="cm^-3", 86 count_activated=True, 87 count_unactivated=False, 88 ), 89 PySDM_products.ParticleSizeSpectrumPerVolume( 90 radius_bins_edges=settings.wet_radius_bins_edges 91 ), 92 PySDM_products.ActivableFraction(name="Activated Fraction"), 93 PySDM_products.WaterMixingRatio(), 94 PySDM_products.AmbientDryAirDensity(name="rhod"), 95 PySDM_products.ActivatedEffectiveRadius( 96 name="reff", count_activated=True, count_unactivated=False 97 ), 98 PySDM_products.ParcelLiquidWaterPath( 99 name="lwp", count_activated=True, count_unactivated=False 100 ), 101 PySDM_products.CloudOpticalDepth(name="tau"), 102 PySDM_products.CloudAlbedo(name="albedo"), 103 ) 104 105 particulator = builder.build(attributes=attributes, products=products) 106 self.settings = settings 107 super().__init__(particulator=particulator)