PySDM_examples.Shipway_and_Hill_2012.simulation
1from collections import namedtuple 2from typing import List, Any 3 4import numpy as np 5from PySDM_examples.Shipway_and_Hill_2012.mpdata_1d import MPDATA_1D 6 7import PySDM.products as PySDM_products 8from PySDM import Builder 9from PySDM.backends import CPU 10from PySDM.dynamics import ( 11 AmbientThermodynamics, 12 Coalescence, 13 Condensation, 14 Displacement, 15 EulerianAdvection, 16) 17from PySDM.environments.kinematic_1d import Kinematic1D 18from PySDM.impl.mesh import Mesh 19from PySDM.initialisation.sampling import spatial_sampling, spectral_sampling 20 21 22class Simulation: 23 def __init__(self, settings, backend=CPU): 24 self.nt = settings.nt 25 self.z0 = -settings.particle_reservoir_depth 26 self.save_spec_and_attr_times = settings.save_spec_and_attr_times 27 self.number_of_bins = settings.number_of_bins 28 29 self.particulator = None 30 self.output_attributes = None 31 self.output_products = None 32 33 self.mesh = Mesh( 34 grid=(settings.nz,), 35 size=(settings.z_max + settings.particle_reservoir_depth,), 36 ) 37 38 env = Kinematic1D( 39 dt=settings.dt, 40 mesh=self.mesh, 41 thd_of_z=settings.thd, 42 rhod_of_z=settings.rhod, 43 z0=-settings.particle_reservoir_depth, 44 ) 45 46 def zZ_to_z_above_reservoir(zZ): 47 z_above_reservoir = zZ * (settings.nz * settings.dz) + self.z0 48 return z_above_reservoir 49 50 mpdata = MPDATA_1D( 51 nz=settings.nz, 52 dt=settings.dt, 53 mpdata_settings=settings.mpdata_settings, 54 advector_of_t=lambda t: settings.rho_times_w(t) * settings.dt / settings.dz, 55 advectee_of_zZ_at_t0=lambda zZ: settings.water_vapour_mixing_ratio( 56 zZ_to_z_above_reservoir(zZ) 57 ), 58 g_factor_of_zZ=lambda zZ: settings.rhod(zZ_to_z_above_reservoir(zZ)), 59 ) 60 61 _extra_nz = settings.particle_reservoir_depth // settings.dz 62 _z_vec = settings.dz * np.linspace( 63 -_extra_nz, settings.nz - _extra_nz, settings.nz + 1 64 ) 65 self.g_factor_vec = settings.rhod(_z_vec) 66 67 dynamics: List[Any] = [AmbientThermodynamics()] 68 69 if settings.enable_condensation: 70 dynamics.append( 71 Condensation( 72 adaptive=settings.condensation_adaptive, 73 rtol_thd=settings.condensation_rtol_thd, 74 rtol_x=settings.condensation_rtol_x, 75 update_thd=settings.condensation_update_thd, 76 ) 77 ) 78 dynamics.append(EulerianAdvection(mpdata)) 79 80 self.products = [] 81 if settings.precip: 82 self.add_collision_dynamic(dynamics, settings, self.products) 83 84 dynamics.append( 85 Displacement( 86 enable_sedimentation=settings.precip, 87 precipitation_counting_level_index=int( 88 settings.particle_reservoir_depth / settings.dz 89 ), 90 ) 91 ) 92 93 self.builder = Builder( 94 n_sd=settings.n_sd, 95 backend=backend(formulae=settings.formulae), 96 environment=env, 97 dynamics=dynamics, 98 ) 99 100 self.attributes = self.builder.particulator.environment.init_attributes( 101 spatial_discretisation=spatial_sampling.Pseudorandom(), 102 spectral_discretisation=spectral_sampling.ConstantMultiplicity( 103 spectrum=settings.wet_radius_spectrum_per_mass_of_dry_air 104 ), 105 kappa=settings.kappa, 106 collisions_only=not settings.enable_condensation, 107 z_part=settings.z_part, 108 ) 109 self.products += [ 110 PySDM_products.WaterMixingRatio( 111 name="cloud water mixing ratio", 112 unit="g/kg", 113 radius_range=settings.cloud_water_radius_range, 114 ), 115 PySDM_products.WaterMixingRatio( 116 name="rain water mixing ratio", 117 unit="g/kg", 118 radius_range=settings.rain_water_radius_range, 119 ), 120 PySDM_products.AmbientDryAirDensity(name="rhod"), 121 PySDM_products.AmbientDryAirPotentialTemperature(name="thd"), 122 PySDM_products.ParticleSizeSpectrumPerVolume( 123 name="wet spectrum", radius_bins_edges=settings.r_bins_edges 124 ), 125 PySDM_products.ParticleConcentration( 126 name="nc", radius_range=settings.cloud_water_radius_range 127 ), 128 PySDM_products.ParticleConcentration( 129 name="nr", radius_range=settings.rain_water_radius_range 130 ), 131 PySDM_products.ParticleConcentration( 132 name="na", radius_range=(0, settings.cloud_water_radius_range[0]) 133 ), 134 PySDM_products.MeanRadius(), 135 PySDM_products.EffectiveRadius( 136 radius_range=settings.cloud_water_radius_range 137 ), 138 PySDM_products.SuperDropletCountPerGridbox(), 139 PySDM_products.AveragedTerminalVelocity( 140 name="rain averaged terminal velocity", 141 radius_range=settings.rain_water_radius_range, 142 ), 143 PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"), 144 PySDM_products.AmbientPressure(name="p"), 145 PySDM_products.AmbientTemperature(name="T"), 146 PySDM_products.AmbientWaterVapourMixingRatio( 147 name="water_vapour_mixing_ratio" 148 ), 149 ] 150 if settings.enable_condensation: 151 self.products.extend( 152 [ 153 PySDM_products.RipeningRate(name="ripening"), 154 PySDM_products.ActivatingRate(name="activating"), 155 PySDM_products.DeactivatingRate(name="deactivating"), 156 PySDM_products.PeakSaturation(), 157 PySDM_products.ParticleSizeSpectrumPerVolume( 158 name="dry spectrum", 159 radius_bins_edges=settings.r_bins_edges_dry, 160 dry=True, 161 ), 162 ] 163 ) 164 if settings.precip: 165 self.products.extend( 166 [ 167 PySDM_products.CollisionRatePerGridbox( 168 name="collision_rate", 169 ), 170 PySDM_products.CollisionRateDeficitPerGridbox( 171 name="collision_deficit", 172 ), 173 PySDM_products.CoalescenceRatePerGridbox( 174 name="coalescence_rate", 175 ), 176 PySDM_products.SurfacePrecipitation(), 177 ] 178 ) 179 self.particulator = self.builder.build( 180 attributes=self.attributes, products=tuple(self.products) 181 ) 182 183 self.output_attributes = { 184 "cell origin": [], 185 "position in cell": [], 186 "radius": [], 187 "multiplicity": [], 188 } 189 self.output_products = {} 190 for k, v in self.particulator.products.items(): 191 if len(v.shape) == 0: 192 self.output_products[k] = np.zeros(self.nt + 1) 193 elif len(v.shape) == 1: 194 self.output_products[k] = np.zeros((self.mesh.grid[-1], self.nt + 1)) 195 elif len(v.shape) == 2: 196 number_of_time_sections = len(self.save_spec_and_attr_times) 197 self.output_products[k] = np.zeros( 198 (self.mesh.grid[-1], self.number_of_bins, number_of_time_sections) 199 ) 200 201 @staticmethod 202 def add_collision_dynamic(dynamics, settings, _): 203 dynamics.append( 204 Coalescence( 205 collision_kernel=settings.collision_kernel, 206 adaptive=settings.coalescence_adaptive, 207 ) 208 ) 209 210 def save_scalar(self, step): 211 for k, v in self.particulator.products.items(): 212 if len(v.shape) > 1: 213 continue 214 if len(v.shape) == 1: 215 self.output_products[k][:, step] = v.get() 216 else: 217 self.output_products[k][step] = v.get() 218 219 def save_spectrum(self, index): 220 for k, v in self.particulator.products.items(): 221 if len(v.shape) == 2: 222 self.output_products[k][:, :, index] = v.get() 223 224 def save_attributes(self): 225 for k, v in self.output_attributes.items(): 226 v.append(self.particulator.attributes[k].to_ndarray()) 227 228 def save(self, step): 229 self.save_scalar(step) 230 time = step * self.particulator.dt 231 if len(self.save_spec_and_attr_times) > 0 and ( 232 np.min( 233 np.abs( 234 np.ones_like(self.save_spec_and_attr_times) * time 235 - np.array(self.save_spec_and_attr_times) 236 ) 237 ) 238 < 0.1 239 ): 240 save_index = np.argmin( 241 np.abs( 242 np.ones_like(self.save_spec_and_attr_times) * time 243 - np.array(self.save_spec_and_attr_times) 244 ) 245 ) 246 self.save_spectrum(save_index) 247 self.save_attributes() 248 249 def run(self): 250 mesh = self.particulator.mesh 251 252 assert "t" not in self.output_products and "z" not in self.output_products 253 self.output_products["t"] = np.linspace( 254 0, self.nt * self.particulator.dt, self.nt + 1, endpoint=True 255 ) 256 self.output_products["z"] = np.linspace( 257 self.z0 + mesh.dz / 2, 258 self.z0 + (mesh.grid[-1] - 1 / 2) * mesh.dz, 259 mesh.grid[-1], 260 endpoint=True, 261 ) 262 263 self.save(0) 264 for step in range(self.nt): 265 mpdata = self.particulator.dynamics["EulerianAdvection"].solvers 266 mpdata.update_advector_field() 267 if "Displacement" in self.particulator.dynamics: 268 self.particulator.dynamics["Displacement"].upload_courant_field( 269 (mpdata.advector / self.g_factor_vec,) 270 ) 271 self.particulator.run(steps=1) 272 self.save(step + 1) 273 274 Outputs = namedtuple("Outputs", "products attributes") 275 output_results = Outputs(self.output_products, self.output_attributes) 276 return output_results
class
Simulation:
23class Simulation: 24 def __init__(self, settings, backend=CPU): 25 self.nt = settings.nt 26 self.z0 = -settings.particle_reservoir_depth 27 self.save_spec_and_attr_times = settings.save_spec_and_attr_times 28 self.number_of_bins = settings.number_of_bins 29 30 self.particulator = None 31 self.output_attributes = None 32 self.output_products = None 33 34 self.mesh = Mesh( 35 grid=(settings.nz,), 36 size=(settings.z_max + settings.particle_reservoir_depth,), 37 ) 38 39 env = Kinematic1D( 40 dt=settings.dt, 41 mesh=self.mesh, 42 thd_of_z=settings.thd, 43 rhod_of_z=settings.rhod, 44 z0=-settings.particle_reservoir_depth, 45 ) 46 47 def zZ_to_z_above_reservoir(zZ): 48 z_above_reservoir = zZ * (settings.nz * settings.dz) + self.z0 49 return z_above_reservoir 50 51 mpdata = MPDATA_1D( 52 nz=settings.nz, 53 dt=settings.dt, 54 mpdata_settings=settings.mpdata_settings, 55 advector_of_t=lambda t: settings.rho_times_w(t) * settings.dt / settings.dz, 56 advectee_of_zZ_at_t0=lambda zZ: settings.water_vapour_mixing_ratio( 57 zZ_to_z_above_reservoir(zZ) 58 ), 59 g_factor_of_zZ=lambda zZ: settings.rhod(zZ_to_z_above_reservoir(zZ)), 60 ) 61 62 _extra_nz = settings.particle_reservoir_depth // settings.dz 63 _z_vec = settings.dz * np.linspace( 64 -_extra_nz, settings.nz - _extra_nz, settings.nz + 1 65 ) 66 self.g_factor_vec = settings.rhod(_z_vec) 67 68 dynamics: List[Any] = [AmbientThermodynamics()] 69 70 if settings.enable_condensation: 71 dynamics.append( 72 Condensation( 73 adaptive=settings.condensation_adaptive, 74 rtol_thd=settings.condensation_rtol_thd, 75 rtol_x=settings.condensation_rtol_x, 76 update_thd=settings.condensation_update_thd, 77 ) 78 ) 79 dynamics.append(EulerianAdvection(mpdata)) 80 81 self.products = [] 82 if settings.precip: 83 self.add_collision_dynamic(dynamics, settings, self.products) 84 85 dynamics.append( 86 Displacement( 87 enable_sedimentation=settings.precip, 88 precipitation_counting_level_index=int( 89 settings.particle_reservoir_depth / settings.dz 90 ), 91 ) 92 ) 93 94 self.builder = Builder( 95 n_sd=settings.n_sd, 96 backend=backend(formulae=settings.formulae), 97 environment=env, 98 dynamics=dynamics, 99 ) 100 101 self.attributes = self.builder.particulator.environment.init_attributes( 102 spatial_discretisation=spatial_sampling.Pseudorandom(), 103 spectral_discretisation=spectral_sampling.ConstantMultiplicity( 104 spectrum=settings.wet_radius_spectrum_per_mass_of_dry_air 105 ), 106 kappa=settings.kappa, 107 collisions_only=not settings.enable_condensation, 108 z_part=settings.z_part, 109 ) 110 self.products += [ 111 PySDM_products.WaterMixingRatio( 112 name="cloud water mixing ratio", 113 unit="g/kg", 114 radius_range=settings.cloud_water_radius_range, 115 ), 116 PySDM_products.WaterMixingRatio( 117 name="rain water mixing ratio", 118 unit="g/kg", 119 radius_range=settings.rain_water_radius_range, 120 ), 121 PySDM_products.AmbientDryAirDensity(name="rhod"), 122 PySDM_products.AmbientDryAirPotentialTemperature(name="thd"), 123 PySDM_products.ParticleSizeSpectrumPerVolume( 124 name="wet spectrum", radius_bins_edges=settings.r_bins_edges 125 ), 126 PySDM_products.ParticleConcentration( 127 name="nc", radius_range=settings.cloud_water_radius_range 128 ), 129 PySDM_products.ParticleConcentration( 130 name="nr", radius_range=settings.rain_water_radius_range 131 ), 132 PySDM_products.ParticleConcentration( 133 name="na", radius_range=(0, settings.cloud_water_radius_range[0]) 134 ), 135 PySDM_products.MeanRadius(), 136 PySDM_products.EffectiveRadius( 137 radius_range=settings.cloud_water_radius_range 138 ), 139 PySDM_products.SuperDropletCountPerGridbox(), 140 PySDM_products.AveragedTerminalVelocity( 141 name="rain averaged terminal velocity", 142 radius_range=settings.rain_water_radius_range, 143 ), 144 PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"), 145 PySDM_products.AmbientPressure(name="p"), 146 PySDM_products.AmbientTemperature(name="T"), 147 PySDM_products.AmbientWaterVapourMixingRatio( 148 name="water_vapour_mixing_ratio" 149 ), 150 ] 151 if settings.enable_condensation: 152 self.products.extend( 153 [ 154 PySDM_products.RipeningRate(name="ripening"), 155 PySDM_products.ActivatingRate(name="activating"), 156 PySDM_products.DeactivatingRate(name="deactivating"), 157 PySDM_products.PeakSaturation(), 158 PySDM_products.ParticleSizeSpectrumPerVolume( 159 name="dry spectrum", 160 radius_bins_edges=settings.r_bins_edges_dry, 161 dry=True, 162 ), 163 ] 164 ) 165 if settings.precip: 166 self.products.extend( 167 [ 168 PySDM_products.CollisionRatePerGridbox( 169 name="collision_rate", 170 ), 171 PySDM_products.CollisionRateDeficitPerGridbox( 172 name="collision_deficit", 173 ), 174 PySDM_products.CoalescenceRatePerGridbox( 175 name="coalescence_rate", 176 ), 177 PySDM_products.SurfacePrecipitation(), 178 ] 179 ) 180 self.particulator = self.builder.build( 181 attributes=self.attributes, products=tuple(self.products) 182 ) 183 184 self.output_attributes = { 185 "cell origin": [], 186 "position in cell": [], 187 "radius": [], 188 "multiplicity": [], 189 } 190 self.output_products = {} 191 for k, v in self.particulator.products.items(): 192 if len(v.shape) == 0: 193 self.output_products[k] = np.zeros(self.nt + 1) 194 elif len(v.shape) == 1: 195 self.output_products[k] = np.zeros((self.mesh.grid[-1], self.nt + 1)) 196 elif len(v.shape) == 2: 197 number_of_time_sections = len(self.save_spec_and_attr_times) 198 self.output_products[k] = np.zeros( 199 (self.mesh.grid[-1], self.number_of_bins, number_of_time_sections) 200 ) 201 202 @staticmethod 203 def add_collision_dynamic(dynamics, settings, _): 204 dynamics.append( 205 Coalescence( 206 collision_kernel=settings.collision_kernel, 207 adaptive=settings.coalescence_adaptive, 208 ) 209 ) 210 211 def save_scalar(self, step): 212 for k, v in self.particulator.products.items(): 213 if len(v.shape) > 1: 214 continue 215 if len(v.shape) == 1: 216 self.output_products[k][:, step] = v.get() 217 else: 218 self.output_products[k][step] = v.get() 219 220 def save_spectrum(self, index): 221 for k, v in self.particulator.products.items(): 222 if len(v.shape) == 2: 223 self.output_products[k][:, :, index] = v.get() 224 225 def save_attributes(self): 226 for k, v in self.output_attributes.items(): 227 v.append(self.particulator.attributes[k].to_ndarray()) 228 229 def save(self, step): 230 self.save_scalar(step) 231 time = step * self.particulator.dt 232 if len(self.save_spec_and_attr_times) > 0 and ( 233 np.min( 234 np.abs( 235 np.ones_like(self.save_spec_and_attr_times) * time 236 - np.array(self.save_spec_and_attr_times) 237 ) 238 ) 239 < 0.1 240 ): 241 save_index = np.argmin( 242 np.abs( 243 np.ones_like(self.save_spec_and_attr_times) * time 244 - np.array(self.save_spec_and_attr_times) 245 ) 246 ) 247 self.save_spectrum(save_index) 248 self.save_attributes() 249 250 def run(self): 251 mesh = self.particulator.mesh 252 253 assert "t" not in self.output_products and "z" not in self.output_products 254 self.output_products["t"] = np.linspace( 255 0, self.nt * self.particulator.dt, self.nt + 1, endpoint=True 256 ) 257 self.output_products["z"] = np.linspace( 258 self.z0 + mesh.dz / 2, 259 self.z0 + (mesh.grid[-1] - 1 / 2) * mesh.dz, 260 mesh.grid[-1], 261 endpoint=True, 262 ) 263 264 self.save(0) 265 for step in range(self.nt): 266 mpdata = self.particulator.dynamics["EulerianAdvection"].solvers 267 mpdata.update_advector_field() 268 if "Displacement" in self.particulator.dynamics: 269 self.particulator.dynamics["Displacement"].upload_courant_field( 270 (mpdata.advector / self.g_factor_vec,) 271 ) 272 self.particulator.run(steps=1) 273 self.save(step + 1) 274 275 Outputs = namedtuple("Outputs", "products attributes") 276 output_results = Outputs(self.output_products, self.output_attributes) 277 return output_results
Simulation( settings, backend=functools.partial(<function _cached_backend>, backend_class=<class 'PySDM.backends.Numba'>))
24 def __init__(self, settings, backend=CPU): 25 self.nt = settings.nt 26 self.z0 = -settings.particle_reservoir_depth 27 self.save_spec_and_attr_times = settings.save_spec_and_attr_times 28 self.number_of_bins = settings.number_of_bins 29 30 self.particulator = None 31 self.output_attributes = None 32 self.output_products = None 33 34 self.mesh = Mesh( 35 grid=(settings.nz,), 36 size=(settings.z_max + settings.particle_reservoir_depth,), 37 ) 38 39 env = Kinematic1D( 40 dt=settings.dt, 41 mesh=self.mesh, 42 thd_of_z=settings.thd, 43 rhod_of_z=settings.rhod, 44 z0=-settings.particle_reservoir_depth, 45 ) 46 47 def zZ_to_z_above_reservoir(zZ): 48 z_above_reservoir = zZ * (settings.nz * settings.dz) + self.z0 49 return z_above_reservoir 50 51 mpdata = MPDATA_1D( 52 nz=settings.nz, 53 dt=settings.dt, 54 mpdata_settings=settings.mpdata_settings, 55 advector_of_t=lambda t: settings.rho_times_w(t) * settings.dt / settings.dz, 56 advectee_of_zZ_at_t0=lambda zZ: settings.water_vapour_mixing_ratio( 57 zZ_to_z_above_reservoir(zZ) 58 ), 59 g_factor_of_zZ=lambda zZ: settings.rhod(zZ_to_z_above_reservoir(zZ)), 60 ) 61 62 _extra_nz = settings.particle_reservoir_depth // settings.dz 63 _z_vec = settings.dz * np.linspace( 64 -_extra_nz, settings.nz - _extra_nz, settings.nz + 1 65 ) 66 self.g_factor_vec = settings.rhod(_z_vec) 67 68 dynamics: List[Any] = [AmbientThermodynamics()] 69 70 if settings.enable_condensation: 71 dynamics.append( 72 Condensation( 73 adaptive=settings.condensation_adaptive, 74 rtol_thd=settings.condensation_rtol_thd, 75 rtol_x=settings.condensation_rtol_x, 76 update_thd=settings.condensation_update_thd, 77 ) 78 ) 79 dynamics.append(EulerianAdvection(mpdata)) 80 81 self.products = [] 82 if settings.precip: 83 self.add_collision_dynamic(dynamics, settings, self.products) 84 85 dynamics.append( 86 Displacement( 87 enable_sedimentation=settings.precip, 88 precipitation_counting_level_index=int( 89 settings.particle_reservoir_depth / settings.dz 90 ), 91 ) 92 ) 93 94 self.builder = Builder( 95 n_sd=settings.n_sd, 96 backend=backend(formulae=settings.formulae), 97 environment=env, 98 dynamics=dynamics, 99 ) 100 101 self.attributes = self.builder.particulator.environment.init_attributes( 102 spatial_discretisation=spatial_sampling.Pseudorandom(), 103 spectral_discretisation=spectral_sampling.ConstantMultiplicity( 104 spectrum=settings.wet_radius_spectrum_per_mass_of_dry_air 105 ), 106 kappa=settings.kappa, 107 collisions_only=not settings.enable_condensation, 108 z_part=settings.z_part, 109 ) 110 self.products += [ 111 PySDM_products.WaterMixingRatio( 112 name="cloud water mixing ratio", 113 unit="g/kg", 114 radius_range=settings.cloud_water_radius_range, 115 ), 116 PySDM_products.WaterMixingRatio( 117 name="rain water mixing ratio", 118 unit="g/kg", 119 radius_range=settings.rain_water_radius_range, 120 ), 121 PySDM_products.AmbientDryAirDensity(name="rhod"), 122 PySDM_products.AmbientDryAirPotentialTemperature(name="thd"), 123 PySDM_products.ParticleSizeSpectrumPerVolume( 124 name="wet spectrum", radius_bins_edges=settings.r_bins_edges 125 ), 126 PySDM_products.ParticleConcentration( 127 name="nc", radius_range=settings.cloud_water_radius_range 128 ), 129 PySDM_products.ParticleConcentration( 130 name="nr", radius_range=settings.rain_water_radius_range 131 ), 132 PySDM_products.ParticleConcentration( 133 name="na", radius_range=(0, settings.cloud_water_radius_range[0]) 134 ), 135 PySDM_products.MeanRadius(), 136 PySDM_products.EffectiveRadius( 137 radius_range=settings.cloud_water_radius_range 138 ), 139 PySDM_products.SuperDropletCountPerGridbox(), 140 PySDM_products.AveragedTerminalVelocity( 141 name="rain averaged terminal velocity", 142 radius_range=settings.rain_water_radius_range, 143 ), 144 PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"), 145 PySDM_products.AmbientPressure(name="p"), 146 PySDM_products.AmbientTemperature(name="T"), 147 PySDM_products.AmbientWaterVapourMixingRatio( 148 name="water_vapour_mixing_ratio" 149 ), 150 ] 151 if settings.enable_condensation: 152 self.products.extend( 153 [ 154 PySDM_products.RipeningRate(name="ripening"), 155 PySDM_products.ActivatingRate(name="activating"), 156 PySDM_products.DeactivatingRate(name="deactivating"), 157 PySDM_products.PeakSaturation(), 158 PySDM_products.ParticleSizeSpectrumPerVolume( 159 name="dry spectrum", 160 radius_bins_edges=settings.r_bins_edges_dry, 161 dry=True, 162 ), 163 ] 164 ) 165 if settings.precip: 166 self.products.extend( 167 [ 168 PySDM_products.CollisionRatePerGridbox( 169 name="collision_rate", 170 ), 171 PySDM_products.CollisionRateDeficitPerGridbox( 172 name="collision_deficit", 173 ), 174 PySDM_products.CoalescenceRatePerGridbox( 175 name="coalescence_rate", 176 ), 177 PySDM_products.SurfacePrecipitation(), 178 ] 179 ) 180 self.particulator = self.builder.build( 181 attributes=self.attributes, products=tuple(self.products) 182 ) 183 184 self.output_attributes = { 185 "cell origin": [], 186 "position in cell": [], 187 "radius": [], 188 "multiplicity": [], 189 } 190 self.output_products = {} 191 for k, v in self.particulator.products.items(): 192 if len(v.shape) == 0: 193 self.output_products[k] = np.zeros(self.nt + 1) 194 elif len(v.shape) == 1: 195 self.output_products[k] = np.zeros((self.mesh.grid[-1], self.nt + 1)) 196 elif len(v.shape) == 2: 197 number_of_time_sections = len(self.save_spec_and_attr_times) 198 self.output_products[k] = np.zeros( 199 (self.mesh.grid[-1], self.number_of_bins, number_of_time_sections) 200 )
def
save(self, step):
229 def save(self, step): 230 self.save_scalar(step) 231 time = step * self.particulator.dt 232 if len(self.save_spec_and_attr_times) > 0 and ( 233 np.min( 234 np.abs( 235 np.ones_like(self.save_spec_and_attr_times) * time 236 - np.array(self.save_spec_and_attr_times) 237 ) 238 ) 239 < 0.1 240 ): 241 save_index = np.argmin( 242 np.abs( 243 np.ones_like(self.save_spec_and_attr_times) * time 244 - np.array(self.save_spec_and_attr_times) 245 ) 246 ) 247 self.save_spectrum(save_index) 248 self.save_attributes()
def
run(self):
250 def run(self): 251 mesh = self.particulator.mesh 252 253 assert "t" not in self.output_products and "z" not in self.output_products 254 self.output_products["t"] = np.linspace( 255 0, self.nt * self.particulator.dt, self.nt + 1, endpoint=True 256 ) 257 self.output_products["z"] = np.linspace( 258 self.z0 + mesh.dz / 2, 259 self.z0 + (mesh.grid[-1] - 1 / 2) * mesh.dz, 260 mesh.grid[-1], 261 endpoint=True, 262 ) 263 264 self.save(0) 265 for step in range(self.nt): 266 mpdata = self.particulator.dynamics["EulerianAdvection"].solvers 267 mpdata.update_advector_field() 268 if "Displacement" in self.particulator.dynamics: 269 self.particulator.dynamics["Displacement"].upload_courant_field( 270 (mpdata.advector / self.g_factor_vec,) 271 ) 272 self.particulator.run(steps=1) 273 self.save(step + 1) 274 275 Outputs = namedtuple("Outputs", "products attributes") 276 output_results = Outputs(self.output_products, self.output_attributes) 277 return output_results