PySDM_examples.Szumowski_et_al_1998.gui_settings
1import inspect 2 3import numpy as np 4from PySDM_examples.utils.widgets import ( 5 Accordion, 6 Checkbox, 7 Dropdown, 8 FloatSlider, 9 IntSlider, 10 Layout, 11 RadioButtons, 12 VBox, 13) 14 15from PySDM import Formulae, formulae, physics 16from PySDM.initialisation.spectra import Lognormal 17from PySDM.physics import si 18 19 20class GUISettings: 21 def __dir__(self): 22 return self.__settings.__dir__() 23 24 def __init__(self, settings): 25 self.__settings = settings 26 27 self.ui_dth0 = FloatSlider( 28 description="$\\Delta\\theta_0$ [K]", value=0, min=-15, max=15 29 ) 30 self.ui_delta_initial_water_vapour_mixing_ratio = FloatSlider( 31 description="$\\Delta initial_water_vapour_mixing_ratio$ [g/kg]", 32 value=0, 33 min=-6, 34 max=6, 35 ) 36 self.ui_rhod_w_max = FloatSlider( 37 description="$\\rho_d w_{max}$", 38 value=settings.rhod_w_max, 39 min=0.1, 40 max=4, 41 step=0.1, 42 ) 43 self.ui_kappa = FloatSlider( 44 description="$\\kappa$ [1]", value=settings.kappa, min=0, max=1.5 45 ) 46 47 self.ui_freezing = { 48 "model": RadioButtons( 49 options=("singular", "time-dependent"), 50 description="immersion frz", 51 layout={"width": "max-content"}, 52 ), 53 "INP surface": RadioButtons( 54 options=("as dry surface", "lognormal(A, sgm_g)"), 55 description="INP surface", 56 ), 57 "lognormal_log10_A_um2": FloatSlider( 58 description="log10(A/$μm^2$)", min=-3, max=1, step=0.5 59 ), 60 "lognormal_ln_sgm_g": FloatSlider( 61 description="ln(sgm_g)", min=0.5, max=3, step=0.5 62 ), 63 "ABIFM fit": Dropdown(description="ABIFM fit", options=("Nonadecanol",)), 64 "INAS fit": Dropdown( 65 description="INAS fit", options=("Niemand et al. 2012",) 66 ), 67 } 68 # TODO #599 cool rate product + sing/tdep diff prod 69 70 self.ui_nx = IntSlider( 71 value=settings.grid[0], min=10, max=100, description="nx" 72 ) 73 self.ui_nz = IntSlider( 74 value=settings.grid[1], min=10, max=100, description="nz" 75 ) 76 self.ui_dt = FloatSlider( 77 value=settings.dt, min=0.5, max=60, description="dt (Eulerian)" 78 ) 79 self.ui_simulation_time = IntSlider( 80 value=settings.simulation_time, 81 min=1800, 82 max=7200, 83 description="simulation time $[s]$", 84 ) 85 self.ui_condensation_rtol_x = IntSlider( 86 value=np.log10(settings.condensation_rtol_thd), 87 min=-9, 88 max=-3, 89 description="log$_{10}$(rtol$_x$)", 90 ) 91 self.ui_condensation_rtol_thd = IntSlider( 92 value=np.log10(settings.condensation_rtol_thd), 93 min=-9, 94 max=-3, 95 description="log$_{10}$(rtol$_\\theta$)", 96 ) 97 self.ui_condensation_adaptive = Checkbox( 98 value=settings.condensation_adaptive, 99 description="condensation adaptive time-step", 100 ) 101 self.ui_coalescence_adaptive = Checkbox( 102 value=settings.condensation_adaptive, 103 description="coalescence adaptive time-step", 104 ) 105 self.ui_displacement_rtol = IntSlider( 106 value=np.log10(settings.displacement_rtol), 107 min=-3, 108 max=0, 109 description="log$_{10}$(rtol)", 110 ) 111 self.ui_displacement_adaptive = Checkbox( 112 value=settings.displacement_adaptive, 113 description="displacement adaptive time-step", 114 ) 115 self.ui_processes = [ 116 Checkbox(value=settings.processes[key], description=key) 117 for key in settings.processes.keys() 118 ] 119 self.ui_sdpg = IntSlider( 120 value=settings.n_sd_per_gridbox, description="n_sd/gridbox", min=1, max=1000 121 ) 122 self.fct_description = "MPDATA: flux-corrected transport option" 123 self.tot_description = "MPDATA: third-order terms option" 124 self.iga_description = "MPDATA: infinite gauge option" 125 self.nit_description = "MPDATA: number of iterations (1=UPWIND)" 126 self.ui_mpdata_options = [ 127 Checkbox(value=settings.mpdata_fct, description=self.fct_description), 128 Checkbox(value=settings.mpdata_tot, description=self.tot_description), 129 Checkbox(value=settings.mpdata_iga, description=self.iga_description), 130 IntSlider( 131 value=settings.mpdata_iters, 132 description=self.nit_description, 133 min=1, 134 max=5, 135 ), 136 ] 137 138 formulae_init_params = inspect.signature(Formulae.__init__).parameters.items() 139 defaults = {k: v.default for k, v in formulae_init_params} 140 defaults["freezing_temperature_spectrum"] = "Niemand_et_al_2012" 141 defaults["heterogeneous_ice_nucleation_rate"] = "ABIFM" 142 self.ui_formulae_options = [ 143 Dropdown( 144 description=k, 145 options=formulae._choices(getattr(physics, k)).keys(), 146 value=defaults[k], 147 ) 148 for k, v in formulae_init_params 149 if k 150 not in ( 151 "self", 152 "fastmath", 153 "seed", 154 "constants", 155 "handle_all_breakups", 156 "terminal_velocity", 157 ) 158 ] 159 160 self.ui_formulae_options.append( 161 Checkbox( 162 value=inspect.signature(Formulae.__init__) 163 .parameters["fastmath"] 164 .default, 165 description="fastmath", 166 ) 167 ) 168 self.ui_output_options = { 169 "interval": IntSlider( 170 description="interval [s]", 171 value=settings.output_interval, 172 min=30, 173 max=60 * 15, 174 ), 175 "aerosol_radius_threshold": FloatSlider( 176 description="aerosol/cloud threshold [um]", 177 value=settings.aerosol_radius_threshold / physics.si.um, 178 min=0.1, 179 max=1, 180 step=0.1, 181 ), 182 "drizzle_radius_threshold": IntSlider( 183 description="cloud/drizzle threshold [um]", 184 value=settings.drizzle_radius_threshold / physics.si.um, 185 min=10, 186 max=100, 187 step=5, 188 ), 189 } 190 191 self.r_bins_edges = settings.r_bins_edges 192 self.T_bins_edges = settings.T_bins_edges 193 self.terminal_velocity_radius_bin_edges = ( 194 settings.terminal_velocity_radius_bin_edges 195 ) 196 197 self.size = settings.size 198 self.condensation_substeps = settings.condensation_substeps 199 self.condensation_dt_cond_range = settings.condensation_dt_cond_range 200 self.condensation_schedule = settings.condensation_schedule 201 self.kernel = settings.kernel 202 self.spectrum_per_mass_of_dry_air = settings.spectrum_per_mass_of_dry_air 203 self.coalescence_dt_coal_range = settings.coalescence_dt_coal_range 204 self.coalescence_optimized_random = settings.coalescence_optimized_random 205 self.coalescence_substeps = settings.coalescence_substeps 206 self.freezing_inp_frac = settings.freezing_inp_frac 207 self.coalescence_efficiency = settings.coalescence_efficiency 208 self.breakup_efficiency = settings.breakup_efficiency 209 self.breakup_fragmentation = settings.breakup_fragmentation 210 211 for attr in ("rhod_of_zZ", "versions", "n_spin_up"): 212 setattr(self, attr, getattr(settings, attr)) 213 214 @property 215 def n_sd(self): 216 return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox 217 218 @property 219 def initial_vapour_mixing_ratio_profile(self): 220 return np.full( 221 self.grid[-1], 222 self.__settings.initial_water_vapour_mixing_ratio 223 + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, 224 ) 225 226 @property 227 def initial_dry_potential_temperature_profile(self): 228 return np.full( 229 self.grid[-1], 230 self.formulae.state_variable_triplet.th_dry( 231 self.__settings.th_std0 + self.ui_dth0.value, 232 self.__settings.initial_water_vapour_mixing_ratio 233 + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, 234 ), 235 ) 236 237 @property 238 def aerosol_radius_threshold(self): 239 return self.ui_output_options["aerosol_radius_threshold"].value * physics.si.um 240 241 @property 242 def drizzle_radius_threshold(self): 243 return self.ui_output_options["drizzle_radius_threshold"].value * physics.si.um 244 245 @property 246 def output_interval(self): 247 return self.ui_output_options["interval"].value 248 249 @property 250 def formulae(self) -> Formulae: 251 return Formulae( 252 **{widget.description: widget.value for widget in self.ui_formulae_options}, 253 constants={"NIEMAND_A": 0, "NIEMAND_B": 0, "ABIFM_M": 0, "ABIFM_C": 0} 254 ) 255 256 @property 257 def steps_per_output_interval(self) -> int: 258 return int(self.output_interval / self.ui_dt.value) 259 260 @property 261 def output_steps(self) -> np.ndarray: 262 return np.arange(0, self.n_steps + 1, self.steps_per_output_interval) 263 264 @property 265 def rhod_w_max(self): 266 return self.ui_rhod_w_max.value 267 268 @property 269 def kappa(self): 270 return self.ui_kappa.value 271 272 @property 273 def freezing_singular(self): 274 return self.ui_freezing["model"].value == "singular" 275 276 @property 277 def grid(self): 278 return self.ui_nx.value, self.ui_nz.value 279 280 @property 281 def dt(self): 282 return self.ui_dt.value 283 284 @property 285 def n_steps(self): 286 return int(self.ui_simulation_time.value / self.ui_dt.value) # TODO #413 287 288 @property 289 def condensation_rtol_x(self): 290 return 10**self.ui_condensation_rtol_x.value 291 292 @property 293 def condensation_rtol_thd(self): 294 return 10**self.ui_condensation_rtol_thd.value 295 296 @property 297 def condensation_adaptive(self): 298 return self.ui_condensation_adaptive.value 299 300 @property 301 def coalescence_adaptive(self): 302 return self.ui_coalescence_adaptive.value 303 304 @property 305 def displacement_rtol(self): 306 return 10**self.ui_displacement_rtol.value 307 308 @property 309 def displacement_adaptive(self): 310 return self.ui_displacement_adaptive.value 311 312 @property 313 def processes(self): 314 result = {} 315 for checkbox in self.ui_processes: 316 result[checkbox.description] = checkbox.value 317 return result 318 319 @property 320 def n_sd_per_gridbox(self): 321 return self.ui_sdpg.value 322 323 @property 324 def mpdata_tot(self): 325 for widget in self.ui_mpdata_options: 326 if widget.description == self.tot_description: 327 return widget.value 328 raise NotImplementedError() 329 330 @property 331 def mpdata_fct(self): 332 for widget in self.ui_mpdata_options: 333 if widget.description == self.fct_description: 334 return widget.value 335 raise NotImplementedError() 336 337 @property 338 def mpdata_iga(self): 339 for widget in self.ui_mpdata_options: 340 if widget.description == self.iga_description: 341 return widget.value 342 raise NotImplementedError() 343 344 @property 345 def mpdata_iters(self): 346 for widget in self.ui_mpdata_options: 347 if widget.description == self.nit_description: 348 return widget.value 349 raise NotImplementedError() 350 351 @property 352 def stream_function(self): 353 assert hasattr(self.__settings, "rhod_w_max") 354 self.__settings.rhod_w_max = self.ui_rhod_w_max.value 355 356 def fun(xX, zZ, _): 357 return self.__settings.stream_function(xX, zZ, _) 358 359 return fun 360 361 @property 362 def freezing_inp_spec(self): 363 if self.ui_freezing["INP surface"].value == "as dry surface": 364 return None 365 if self.ui_freezing["INP surface"].value == "lognormal(A, sgm_g)": 366 return Lognormal( 367 norm_factor=1, 368 m_mode=10 ** (self.ui_freezing["lognormal_log10_A_um2"].value) 369 * si.um**2, 370 s_geom=np.exp(self.ui_freezing["lognormal_ln_sgm_g"].value), 371 ) 372 raise NotImplementedError() 373 374 def box(self): 375 layout = Accordion( 376 children=[ 377 VBox( 378 [ 379 self.ui_dth0, 380 self.ui_delta_initial_water_vapour_mixing_ratio, 381 self.ui_kappa, 382 self.ui_rhod_w_max, 383 *self.ui_freezing.values(), 384 ] 385 ), 386 VBox([*self.ui_processes]), 387 VBox( 388 [ 389 self.ui_nx, 390 self.ui_nz, 391 self.ui_sdpg, 392 self.ui_dt, 393 self.ui_simulation_time, 394 self.ui_condensation_rtol_x, 395 self.ui_condensation_rtol_thd, 396 self.ui_condensation_adaptive, 397 self.ui_coalescence_adaptive, 398 self.ui_displacement_rtol, 399 self.ui_displacement_adaptive, 400 *self.ui_mpdata_options, 401 ] 402 ), 403 VBox([*self.ui_formulae_options]), 404 VBox([*self.ui_output_options.values()]), 405 ] 406 ) 407 layout.set_title(0, "parameters") 408 layout.set_title(1, "processes") 409 layout.set_title(2, "discretisation") 410 layout.set_title(3, "formulae") 411 layout.set_title(4, "output") 412 413 layout.observe(self.hide_and_show, names="selected_index") 414 self.hide_and_show() 415 416 return layout 417 418 def hide_and_show(self, _=None): 419 freezing_enabled = self.processes["freezing"] 420 for widget in self.ui_freezing.values(): 421 set_visibility(widget, freezing_enabled) 422 423 424def set_visibility(widget, visible): 425 if visible: 426 widget.layout = Layout() 427 else: 428 widget.layout = Layout(visibility="hidden")
class
GUISettings:
21class GUISettings: 22 def __dir__(self): 23 return self.__settings.__dir__() 24 25 def __init__(self, settings): 26 self.__settings = settings 27 28 self.ui_dth0 = FloatSlider( 29 description="$\\Delta\\theta_0$ [K]", value=0, min=-15, max=15 30 ) 31 self.ui_delta_initial_water_vapour_mixing_ratio = FloatSlider( 32 description="$\\Delta initial_water_vapour_mixing_ratio$ [g/kg]", 33 value=0, 34 min=-6, 35 max=6, 36 ) 37 self.ui_rhod_w_max = FloatSlider( 38 description="$\\rho_d w_{max}$", 39 value=settings.rhod_w_max, 40 min=0.1, 41 max=4, 42 step=0.1, 43 ) 44 self.ui_kappa = FloatSlider( 45 description="$\\kappa$ [1]", value=settings.kappa, min=0, max=1.5 46 ) 47 48 self.ui_freezing = { 49 "model": RadioButtons( 50 options=("singular", "time-dependent"), 51 description="immersion frz", 52 layout={"width": "max-content"}, 53 ), 54 "INP surface": RadioButtons( 55 options=("as dry surface", "lognormal(A, sgm_g)"), 56 description="INP surface", 57 ), 58 "lognormal_log10_A_um2": FloatSlider( 59 description="log10(A/$μm^2$)", min=-3, max=1, step=0.5 60 ), 61 "lognormal_ln_sgm_g": FloatSlider( 62 description="ln(sgm_g)", min=0.5, max=3, step=0.5 63 ), 64 "ABIFM fit": Dropdown(description="ABIFM fit", options=("Nonadecanol",)), 65 "INAS fit": Dropdown( 66 description="INAS fit", options=("Niemand et al. 2012",) 67 ), 68 } 69 # TODO #599 cool rate product + sing/tdep diff prod 70 71 self.ui_nx = IntSlider( 72 value=settings.grid[0], min=10, max=100, description="nx" 73 ) 74 self.ui_nz = IntSlider( 75 value=settings.grid[1], min=10, max=100, description="nz" 76 ) 77 self.ui_dt = FloatSlider( 78 value=settings.dt, min=0.5, max=60, description="dt (Eulerian)" 79 ) 80 self.ui_simulation_time = IntSlider( 81 value=settings.simulation_time, 82 min=1800, 83 max=7200, 84 description="simulation time $[s]$", 85 ) 86 self.ui_condensation_rtol_x = IntSlider( 87 value=np.log10(settings.condensation_rtol_thd), 88 min=-9, 89 max=-3, 90 description="log$_{10}$(rtol$_x$)", 91 ) 92 self.ui_condensation_rtol_thd = IntSlider( 93 value=np.log10(settings.condensation_rtol_thd), 94 min=-9, 95 max=-3, 96 description="log$_{10}$(rtol$_\\theta$)", 97 ) 98 self.ui_condensation_adaptive = Checkbox( 99 value=settings.condensation_adaptive, 100 description="condensation adaptive time-step", 101 ) 102 self.ui_coalescence_adaptive = Checkbox( 103 value=settings.condensation_adaptive, 104 description="coalescence adaptive time-step", 105 ) 106 self.ui_displacement_rtol = IntSlider( 107 value=np.log10(settings.displacement_rtol), 108 min=-3, 109 max=0, 110 description="log$_{10}$(rtol)", 111 ) 112 self.ui_displacement_adaptive = Checkbox( 113 value=settings.displacement_adaptive, 114 description="displacement adaptive time-step", 115 ) 116 self.ui_processes = [ 117 Checkbox(value=settings.processes[key], description=key) 118 for key in settings.processes.keys() 119 ] 120 self.ui_sdpg = IntSlider( 121 value=settings.n_sd_per_gridbox, description="n_sd/gridbox", min=1, max=1000 122 ) 123 self.fct_description = "MPDATA: flux-corrected transport option" 124 self.tot_description = "MPDATA: third-order terms option" 125 self.iga_description = "MPDATA: infinite gauge option" 126 self.nit_description = "MPDATA: number of iterations (1=UPWIND)" 127 self.ui_mpdata_options = [ 128 Checkbox(value=settings.mpdata_fct, description=self.fct_description), 129 Checkbox(value=settings.mpdata_tot, description=self.tot_description), 130 Checkbox(value=settings.mpdata_iga, description=self.iga_description), 131 IntSlider( 132 value=settings.mpdata_iters, 133 description=self.nit_description, 134 min=1, 135 max=5, 136 ), 137 ] 138 139 formulae_init_params = inspect.signature(Formulae.__init__).parameters.items() 140 defaults = {k: v.default for k, v in formulae_init_params} 141 defaults["freezing_temperature_spectrum"] = "Niemand_et_al_2012" 142 defaults["heterogeneous_ice_nucleation_rate"] = "ABIFM" 143 self.ui_formulae_options = [ 144 Dropdown( 145 description=k, 146 options=formulae._choices(getattr(physics, k)).keys(), 147 value=defaults[k], 148 ) 149 for k, v in formulae_init_params 150 if k 151 not in ( 152 "self", 153 "fastmath", 154 "seed", 155 "constants", 156 "handle_all_breakups", 157 "terminal_velocity", 158 ) 159 ] 160 161 self.ui_formulae_options.append( 162 Checkbox( 163 value=inspect.signature(Formulae.__init__) 164 .parameters["fastmath"] 165 .default, 166 description="fastmath", 167 ) 168 ) 169 self.ui_output_options = { 170 "interval": IntSlider( 171 description="interval [s]", 172 value=settings.output_interval, 173 min=30, 174 max=60 * 15, 175 ), 176 "aerosol_radius_threshold": FloatSlider( 177 description="aerosol/cloud threshold [um]", 178 value=settings.aerosol_radius_threshold / physics.si.um, 179 min=0.1, 180 max=1, 181 step=0.1, 182 ), 183 "drizzle_radius_threshold": IntSlider( 184 description="cloud/drizzle threshold [um]", 185 value=settings.drizzle_radius_threshold / physics.si.um, 186 min=10, 187 max=100, 188 step=5, 189 ), 190 } 191 192 self.r_bins_edges = settings.r_bins_edges 193 self.T_bins_edges = settings.T_bins_edges 194 self.terminal_velocity_radius_bin_edges = ( 195 settings.terminal_velocity_radius_bin_edges 196 ) 197 198 self.size = settings.size 199 self.condensation_substeps = settings.condensation_substeps 200 self.condensation_dt_cond_range = settings.condensation_dt_cond_range 201 self.condensation_schedule = settings.condensation_schedule 202 self.kernel = settings.kernel 203 self.spectrum_per_mass_of_dry_air = settings.spectrum_per_mass_of_dry_air 204 self.coalescence_dt_coal_range = settings.coalescence_dt_coal_range 205 self.coalescence_optimized_random = settings.coalescence_optimized_random 206 self.coalescence_substeps = settings.coalescence_substeps 207 self.freezing_inp_frac = settings.freezing_inp_frac 208 self.coalescence_efficiency = settings.coalescence_efficiency 209 self.breakup_efficiency = settings.breakup_efficiency 210 self.breakup_fragmentation = settings.breakup_fragmentation 211 212 for attr in ("rhod_of_zZ", "versions", "n_spin_up"): 213 setattr(self, attr, getattr(settings, attr)) 214 215 @property 216 def n_sd(self): 217 return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox 218 219 @property 220 def initial_vapour_mixing_ratio_profile(self): 221 return np.full( 222 self.grid[-1], 223 self.__settings.initial_water_vapour_mixing_ratio 224 + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, 225 ) 226 227 @property 228 def initial_dry_potential_temperature_profile(self): 229 return np.full( 230 self.grid[-1], 231 self.formulae.state_variable_triplet.th_dry( 232 self.__settings.th_std0 + self.ui_dth0.value, 233 self.__settings.initial_water_vapour_mixing_ratio 234 + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, 235 ), 236 ) 237 238 @property 239 def aerosol_radius_threshold(self): 240 return self.ui_output_options["aerosol_radius_threshold"].value * physics.si.um 241 242 @property 243 def drizzle_radius_threshold(self): 244 return self.ui_output_options["drizzle_radius_threshold"].value * physics.si.um 245 246 @property 247 def output_interval(self): 248 return self.ui_output_options["interval"].value 249 250 @property 251 def formulae(self) -> Formulae: 252 return Formulae( 253 **{widget.description: widget.value for widget in self.ui_formulae_options}, 254 constants={"NIEMAND_A": 0, "NIEMAND_B": 0, "ABIFM_M": 0, "ABIFM_C": 0} 255 ) 256 257 @property 258 def steps_per_output_interval(self) -> int: 259 return int(self.output_interval / self.ui_dt.value) 260 261 @property 262 def output_steps(self) -> np.ndarray: 263 return np.arange(0, self.n_steps + 1, self.steps_per_output_interval) 264 265 @property 266 def rhod_w_max(self): 267 return self.ui_rhod_w_max.value 268 269 @property 270 def kappa(self): 271 return self.ui_kappa.value 272 273 @property 274 def freezing_singular(self): 275 return self.ui_freezing["model"].value == "singular" 276 277 @property 278 def grid(self): 279 return self.ui_nx.value, self.ui_nz.value 280 281 @property 282 def dt(self): 283 return self.ui_dt.value 284 285 @property 286 def n_steps(self): 287 return int(self.ui_simulation_time.value / self.ui_dt.value) # TODO #413 288 289 @property 290 def condensation_rtol_x(self): 291 return 10**self.ui_condensation_rtol_x.value 292 293 @property 294 def condensation_rtol_thd(self): 295 return 10**self.ui_condensation_rtol_thd.value 296 297 @property 298 def condensation_adaptive(self): 299 return self.ui_condensation_adaptive.value 300 301 @property 302 def coalescence_adaptive(self): 303 return self.ui_coalescence_adaptive.value 304 305 @property 306 def displacement_rtol(self): 307 return 10**self.ui_displacement_rtol.value 308 309 @property 310 def displacement_adaptive(self): 311 return self.ui_displacement_adaptive.value 312 313 @property 314 def processes(self): 315 result = {} 316 for checkbox in self.ui_processes: 317 result[checkbox.description] = checkbox.value 318 return result 319 320 @property 321 def n_sd_per_gridbox(self): 322 return self.ui_sdpg.value 323 324 @property 325 def mpdata_tot(self): 326 for widget in self.ui_mpdata_options: 327 if widget.description == self.tot_description: 328 return widget.value 329 raise NotImplementedError() 330 331 @property 332 def mpdata_fct(self): 333 for widget in self.ui_mpdata_options: 334 if widget.description == self.fct_description: 335 return widget.value 336 raise NotImplementedError() 337 338 @property 339 def mpdata_iga(self): 340 for widget in self.ui_mpdata_options: 341 if widget.description == self.iga_description: 342 return widget.value 343 raise NotImplementedError() 344 345 @property 346 def mpdata_iters(self): 347 for widget in self.ui_mpdata_options: 348 if widget.description == self.nit_description: 349 return widget.value 350 raise NotImplementedError() 351 352 @property 353 def stream_function(self): 354 assert hasattr(self.__settings, "rhod_w_max") 355 self.__settings.rhod_w_max = self.ui_rhod_w_max.value 356 357 def fun(xX, zZ, _): 358 return self.__settings.stream_function(xX, zZ, _) 359 360 return fun 361 362 @property 363 def freezing_inp_spec(self): 364 if self.ui_freezing["INP surface"].value == "as dry surface": 365 return None 366 if self.ui_freezing["INP surface"].value == "lognormal(A, sgm_g)": 367 return Lognormal( 368 norm_factor=1, 369 m_mode=10 ** (self.ui_freezing["lognormal_log10_A_um2"].value) 370 * si.um**2, 371 s_geom=np.exp(self.ui_freezing["lognormal_ln_sgm_g"].value), 372 ) 373 raise NotImplementedError() 374 375 def box(self): 376 layout = Accordion( 377 children=[ 378 VBox( 379 [ 380 self.ui_dth0, 381 self.ui_delta_initial_water_vapour_mixing_ratio, 382 self.ui_kappa, 383 self.ui_rhod_w_max, 384 *self.ui_freezing.values(), 385 ] 386 ), 387 VBox([*self.ui_processes]), 388 VBox( 389 [ 390 self.ui_nx, 391 self.ui_nz, 392 self.ui_sdpg, 393 self.ui_dt, 394 self.ui_simulation_time, 395 self.ui_condensation_rtol_x, 396 self.ui_condensation_rtol_thd, 397 self.ui_condensation_adaptive, 398 self.ui_coalescence_adaptive, 399 self.ui_displacement_rtol, 400 self.ui_displacement_adaptive, 401 *self.ui_mpdata_options, 402 ] 403 ), 404 VBox([*self.ui_formulae_options]), 405 VBox([*self.ui_output_options.values()]), 406 ] 407 ) 408 layout.set_title(0, "parameters") 409 layout.set_title(1, "processes") 410 layout.set_title(2, "discretisation") 411 layout.set_title(3, "formulae") 412 layout.set_title(4, "output") 413 414 layout.observe(self.hide_and_show, names="selected_index") 415 self.hide_and_show() 416 417 return layout 418 419 def hide_and_show(self, _=None): 420 freezing_enabled = self.processes["freezing"] 421 for widget in self.ui_freezing.values(): 422 set_visibility(widget, freezing_enabled)
GUISettings(settings)
25 def __init__(self, settings): 26 self.__settings = settings 27 28 self.ui_dth0 = FloatSlider( 29 description="$\\Delta\\theta_0$ [K]", value=0, min=-15, max=15 30 ) 31 self.ui_delta_initial_water_vapour_mixing_ratio = FloatSlider( 32 description="$\\Delta initial_water_vapour_mixing_ratio$ [g/kg]", 33 value=0, 34 min=-6, 35 max=6, 36 ) 37 self.ui_rhod_w_max = FloatSlider( 38 description="$\\rho_d w_{max}$", 39 value=settings.rhod_w_max, 40 min=0.1, 41 max=4, 42 step=0.1, 43 ) 44 self.ui_kappa = FloatSlider( 45 description="$\\kappa$ [1]", value=settings.kappa, min=0, max=1.5 46 ) 47 48 self.ui_freezing = { 49 "model": RadioButtons( 50 options=("singular", "time-dependent"), 51 description="immersion frz", 52 layout={"width": "max-content"}, 53 ), 54 "INP surface": RadioButtons( 55 options=("as dry surface", "lognormal(A, sgm_g)"), 56 description="INP surface", 57 ), 58 "lognormal_log10_A_um2": FloatSlider( 59 description="log10(A/$μm^2$)", min=-3, max=1, step=0.5 60 ), 61 "lognormal_ln_sgm_g": FloatSlider( 62 description="ln(sgm_g)", min=0.5, max=3, step=0.5 63 ), 64 "ABIFM fit": Dropdown(description="ABIFM fit", options=("Nonadecanol",)), 65 "INAS fit": Dropdown( 66 description="INAS fit", options=("Niemand et al. 2012",) 67 ), 68 } 69 # TODO #599 cool rate product + sing/tdep diff prod 70 71 self.ui_nx = IntSlider( 72 value=settings.grid[0], min=10, max=100, description="nx" 73 ) 74 self.ui_nz = IntSlider( 75 value=settings.grid[1], min=10, max=100, description="nz" 76 ) 77 self.ui_dt = FloatSlider( 78 value=settings.dt, min=0.5, max=60, description="dt (Eulerian)" 79 ) 80 self.ui_simulation_time = IntSlider( 81 value=settings.simulation_time, 82 min=1800, 83 max=7200, 84 description="simulation time $[s]$", 85 ) 86 self.ui_condensation_rtol_x = IntSlider( 87 value=np.log10(settings.condensation_rtol_thd), 88 min=-9, 89 max=-3, 90 description="log$_{10}$(rtol$_x$)", 91 ) 92 self.ui_condensation_rtol_thd = IntSlider( 93 value=np.log10(settings.condensation_rtol_thd), 94 min=-9, 95 max=-3, 96 description="log$_{10}$(rtol$_\\theta$)", 97 ) 98 self.ui_condensation_adaptive = Checkbox( 99 value=settings.condensation_adaptive, 100 description="condensation adaptive time-step", 101 ) 102 self.ui_coalescence_adaptive = Checkbox( 103 value=settings.condensation_adaptive, 104 description="coalescence adaptive time-step", 105 ) 106 self.ui_displacement_rtol = IntSlider( 107 value=np.log10(settings.displacement_rtol), 108 min=-3, 109 max=0, 110 description="log$_{10}$(rtol)", 111 ) 112 self.ui_displacement_adaptive = Checkbox( 113 value=settings.displacement_adaptive, 114 description="displacement adaptive time-step", 115 ) 116 self.ui_processes = [ 117 Checkbox(value=settings.processes[key], description=key) 118 for key in settings.processes.keys() 119 ] 120 self.ui_sdpg = IntSlider( 121 value=settings.n_sd_per_gridbox, description="n_sd/gridbox", min=1, max=1000 122 ) 123 self.fct_description = "MPDATA: flux-corrected transport option" 124 self.tot_description = "MPDATA: third-order terms option" 125 self.iga_description = "MPDATA: infinite gauge option" 126 self.nit_description = "MPDATA: number of iterations (1=UPWIND)" 127 self.ui_mpdata_options = [ 128 Checkbox(value=settings.mpdata_fct, description=self.fct_description), 129 Checkbox(value=settings.mpdata_tot, description=self.tot_description), 130 Checkbox(value=settings.mpdata_iga, description=self.iga_description), 131 IntSlider( 132 value=settings.mpdata_iters, 133 description=self.nit_description, 134 min=1, 135 max=5, 136 ), 137 ] 138 139 formulae_init_params = inspect.signature(Formulae.__init__).parameters.items() 140 defaults = {k: v.default for k, v in formulae_init_params} 141 defaults["freezing_temperature_spectrum"] = "Niemand_et_al_2012" 142 defaults["heterogeneous_ice_nucleation_rate"] = "ABIFM" 143 self.ui_formulae_options = [ 144 Dropdown( 145 description=k, 146 options=formulae._choices(getattr(physics, k)).keys(), 147 value=defaults[k], 148 ) 149 for k, v in formulae_init_params 150 if k 151 not in ( 152 "self", 153 "fastmath", 154 "seed", 155 "constants", 156 "handle_all_breakups", 157 "terminal_velocity", 158 ) 159 ] 160 161 self.ui_formulae_options.append( 162 Checkbox( 163 value=inspect.signature(Formulae.__init__) 164 .parameters["fastmath"] 165 .default, 166 description="fastmath", 167 ) 168 ) 169 self.ui_output_options = { 170 "interval": IntSlider( 171 description="interval [s]", 172 value=settings.output_interval, 173 min=30, 174 max=60 * 15, 175 ), 176 "aerosol_radius_threshold": FloatSlider( 177 description="aerosol/cloud threshold [um]", 178 value=settings.aerosol_radius_threshold / physics.si.um, 179 min=0.1, 180 max=1, 181 step=0.1, 182 ), 183 "drizzle_radius_threshold": IntSlider( 184 description="cloud/drizzle threshold [um]", 185 value=settings.drizzle_radius_threshold / physics.si.um, 186 min=10, 187 max=100, 188 step=5, 189 ), 190 } 191 192 self.r_bins_edges = settings.r_bins_edges 193 self.T_bins_edges = settings.T_bins_edges 194 self.terminal_velocity_radius_bin_edges = ( 195 settings.terminal_velocity_radius_bin_edges 196 ) 197 198 self.size = settings.size 199 self.condensation_substeps = settings.condensation_substeps 200 self.condensation_dt_cond_range = settings.condensation_dt_cond_range 201 self.condensation_schedule = settings.condensation_schedule 202 self.kernel = settings.kernel 203 self.spectrum_per_mass_of_dry_air = settings.spectrum_per_mass_of_dry_air 204 self.coalescence_dt_coal_range = settings.coalescence_dt_coal_range 205 self.coalescence_optimized_random = settings.coalescence_optimized_random 206 self.coalescence_substeps = settings.coalescence_substeps 207 self.freezing_inp_frac = settings.freezing_inp_frac 208 self.coalescence_efficiency = settings.coalescence_efficiency 209 self.breakup_efficiency = settings.breakup_efficiency 210 self.breakup_fragmentation = settings.breakup_fragmentation 211 212 for attr in ("rhod_of_zZ", "versions", "n_spin_up"): 213 setattr(self, attr, getattr(settings, attr))
initial_dry_potential_temperature_profile
227 @property 228 def initial_dry_potential_temperature_profile(self): 229 return np.full( 230 self.grid[-1], 231 self.formulae.state_variable_triplet.th_dry( 232 self.__settings.th_std0 + self.ui_dth0.value, 233 self.__settings.initial_water_vapour_mixing_ratio 234 + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, 235 ), 236 )
formulae: PySDM.formulae.Formulae
freezing_inp_spec
362 @property 363 def freezing_inp_spec(self): 364 if self.ui_freezing["INP surface"].value == "as dry surface": 365 return None 366 if self.ui_freezing["INP surface"].value == "lognormal(A, sgm_g)": 367 return Lognormal( 368 norm_factor=1, 369 m_mode=10 ** (self.ui_freezing["lognormal_log10_A_um2"].value) 370 * si.um**2, 371 s_geom=np.exp(self.ui_freezing["lognormal_ln_sgm_g"].value), 372 ) 373 raise NotImplementedError()
def
box(self):
375 def box(self): 376 layout = Accordion( 377 children=[ 378 VBox( 379 [ 380 self.ui_dth0, 381 self.ui_delta_initial_water_vapour_mixing_ratio, 382 self.ui_kappa, 383 self.ui_rhod_w_max, 384 *self.ui_freezing.values(), 385 ] 386 ), 387 VBox([*self.ui_processes]), 388 VBox( 389 [ 390 self.ui_nx, 391 self.ui_nz, 392 self.ui_sdpg, 393 self.ui_dt, 394 self.ui_simulation_time, 395 self.ui_condensation_rtol_x, 396 self.ui_condensation_rtol_thd, 397 self.ui_condensation_adaptive, 398 self.ui_coalescence_adaptive, 399 self.ui_displacement_rtol, 400 self.ui_displacement_adaptive, 401 *self.ui_mpdata_options, 402 ] 403 ), 404 VBox([*self.ui_formulae_options]), 405 VBox([*self.ui_output_options.values()]), 406 ] 407 ) 408 layout.set_title(0, "parameters") 409 layout.set_title(1, "processes") 410 layout.set_title(2, "discretisation") 411 layout.set_title(3, "formulae") 412 layout.set_title(4, "output") 413 414 layout.observe(self.hide_and_show, names="selected_index") 415 self.hide_and_show() 416 417 return layout
def
set_visibility(widget, visible):