PyMPDATA_examples.Olesik_et_al_2022.analysis
1from collections import namedtuple 2from copy import deepcopy 3 4import numpy as np 5from joblib import Parallel, delayed, parallel_backend 6from PyMPDATA_examples.Olesik_et_al_2022.coordinates import x_id, x_log_of_pn, x_p2 7from PyMPDATA_examples.Olesik_et_al_2022.equilibrium_drop_growth import PdfEvolver 8from PyMPDATA_examples.Olesik_et_al_2022.settings import ( 9 Settings, 10 default_mixing_ratios_g_kg, 11) 12from PyMPDATA_examples.Olesik_et_al_2022.simulation import Simulation 13from PyMPDATA_examples.utils.discretisation import discretised_analytical_solution 14from PyMPDATA_examples.utils.error_norms import L2 15 16from PyMPDATA import Options 17 18 19def analysis(settings, grid_layout, psi_coord, options_dict, GC_max): 20 options_str = str(options_dict) 21 options = Options(**options_dict) 22 simulation = Simulation(settings, grid_layout, psi_coord, options, GC_max) 23 result = {"n": [], "n_analytical": [], "error_norm_L2": [], "wall_time": []} 24 last_step = 0 25 for n_steps in simulation.out_steps: 26 steps = n_steps - last_step 27 wall_time = simulation.step(steps) if steps > 0 else 0 28 last_step += steps 29 result["n"].append(simulation.n_of_r.copy()) 30 result["wall_time"].append(wall_time) 31 result["r"] = simulation.r.copy() 32 result["rh"] = simulation.rh.copy() 33 result["dx"] = simulation.dx 34 return Result( 35 grid_layout_str=grid_layout.__class__.__name__, 36 option_str=options_str, 37 result=result, 38 out_steps=simulation.out_steps, 39 dt=simulation.dt, 40 ) 41 42 43def compute_figure_data( 44 *, 45 nr, 46 GC_max, 47 psi_coord=x_id(), 48 grid_layouts=(x_id(), x_p2(), x_log_of_pn(r0=1, n=1)), 49 opt_set=({"n_iters": 1},), 50 mixing_ratios_g_kg=default_mixing_ratios_g_kg 51): 52 settings = Settings(nr=nr, mixing_ratios_g_kg=mixing_ratios_g_kg) 53 with parallel_backend("threading", n_jobs=-2): 54 results = Parallel(verbose=10)( 55 delayed(analysis)(settings, grid_layout, psi_coord, options, GC_max) 56 for grid_layout in grid_layouts 57 for options in deepcopy(opt_set) 58 ) 59 60 cases = {} 61 for result in results: 62 case = Case(result) 63 if case.grid_layout_str not in cases: 64 cases[case.grid_layout_str] = case 65 66 output = {} 67 for coord, case in cases.items(): 68 output[coord] = {"numerical": {}, "wall_time": {}} 69 for result in results: 70 if coord == result.grid_layout_str: 71 opts = result.option_str 72 data = result.result 73 rh = data.pop("rh") 74 r = data.pop("r") 75 dx = data.pop("dx") 76 77 if "grid" not in output[coord]: 78 output[coord]["grid"] = { 79 "rh": rh, 80 "r": r, 81 "dx": dx, 82 "dt": case.dt, 83 "out_steps": case.out_steps, 84 } 85 output[coord]["numerical"][opts] = data["n"] 86 87 for coord, case in cases.items(): 88 analytical = [] 89 for t in [case.dt * nt for nt in case.out_steps]: 90 pdf_t = PdfEvolver(settings.pdf, settings.drdt, t) 91 rh = output[coord]["grid"]["rh"] 92 analytical.append( 93 discretised_analytical_solution( 94 rh.magnitude, 95 lambda r, pdf_t=pdf_t, rh=rh: pdf_t(r * rh.units).magnitude, 96 midpoint_value=True, 97 r=output[coord]["grid"]["r"].magnitude, 98 ) 99 * pdf_t(rh[0]).units 100 ) 101 output[coord]["analytical"] = analytical 102 103 for coord, case in cases.items(): 104 error_L2 = {} 105 analytical = output[coord]["analytical"] 106 for opts in output[coord]["numerical"]: 107 numerical = output[coord]["numerical"][opts] 108 error_L2[opts] = L2( 109 numerical[-1].magnitude, analytical[-1].magnitude, case.out_steps[-1] 110 ) 111 output[coord]["error_L2"] = error_L2 112 113 for coord, case in cases.items(): 114 for result in results: 115 data = result.result 116 for opts in output[coord]["numerical"]: 117 output[coord]["wall_time"][opts] = data["wall_time"] 118 119 return output, settings 120 121 122class Result( 123 namedtuple("Result", ("dt", "out_steps", "grid_layout_str", "option_str", "result")) 124): 125 __slots__ = () 126 127 128def Case(result: Result): 129 return namedtuple("Case", ("dt", "out_steps", "grid_layout_str"))( 130 dt=result.dt, out_steps=result.out_steps, grid_layout_str=result.grid_layout_str 131 ) 132 133 134def rel_disp(r, psi, psi_coord): 135 mom0 = 0 136 mom1 = 0 137 mom2 = 0 138 for i, psi_i in enumerate(psi): 139 dp_i = psi_coord.moment_of_r_integral( 140 psi_coord.x(r[i + 1]), 0 141 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 0) 142 A_i = psi_coord.moment_of_r_integral( 143 psi_coord.x(r[i + 1]), 1 144 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 1) 145 B_i = psi_coord.moment_of_r_integral( 146 psi_coord.x(r[i + 1]), 2 147 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 2) 148 bin_mom0 = psi_i * dp_i 149 bin_mom1 = psi_i * A_i 150 bin_mom2 = psi_i * B_i 151 mom0 += bin_mom0 152 mom1 += bin_mom1 153 mom2 += bin_mom2 154 mu = mom1 / mom0 155 std = np.sqrt(mom2 / mom0 - mu**2) 156 d = std / mu 157 return d 158 159 160def third_moment(r, psi, psi_coord, normalised=True): 161 mom0 = 0 162 mom3 = 0 163 for i, psi_i in enumerate(psi): 164 dp_i = psi_coord.moment_of_r_integral( 165 psi_coord.x(r[i + 1]), 0 166 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 0) 167 integral_i = psi_coord.moment_of_r_integral( 168 psi_coord.x(r[i + 1]), 3 169 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 3) 170 bin_mom0 = psi_i * dp_i 171 bin_mom3 = psi_i * integral_i 172 mom0 += bin_mom0 173 mom3 += bin_mom3 174 if normalised: 175 mom3 /= mom0 176 return mom3
def
analysis(settings, grid_layout, psi_coord, options_dict, GC_max):
20def analysis(settings, grid_layout, psi_coord, options_dict, GC_max): 21 options_str = str(options_dict) 22 options = Options(**options_dict) 23 simulation = Simulation(settings, grid_layout, psi_coord, options, GC_max) 24 result = {"n": [], "n_analytical": [], "error_norm_L2": [], "wall_time": []} 25 last_step = 0 26 for n_steps in simulation.out_steps: 27 steps = n_steps - last_step 28 wall_time = simulation.step(steps) if steps > 0 else 0 29 last_step += steps 30 result["n"].append(simulation.n_of_r.copy()) 31 result["wall_time"].append(wall_time) 32 result["r"] = simulation.r.copy() 33 result["rh"] = simulation.rh.copy() 34 result["dx"] = simulation.dx 35 return Result( 36 grid_layout_str=grid_layout.__class__.__name__, 37 option_str=options_str, 38 result=result, 39 out_steps=simulation.out_steps, 40 dt=simulation.dt, 41 )
def
compute_figure_data( *, nr, GC_max, psi_coord=<PyMPDATA_examples.Olesik_et_al_2022.coordinates.x_id object>, grid_layouts=(<PyMPDATA_examples.Olesik_et_al_2022.coordinates.x_id object>, <PyMPDATA_examples.Olesik_et_al_2022.coordinates.x_p2 object>, <PyMPDATA_examples.Olesik_et_al_2022.coordinates.x_log_of_pn object>), opt_set=({'n_iters': 1},), mixing_ratios_g_kg=array([ 1, 2, 4, 6, 8, 10])):
44def compute_figure_data( 45 *, 46 nr, 47 GC_max, 48 psi_coord=x_id(), 49 grid_layouts=(x_id(), x_p2(), x_log_of_pn(r0=1, n=1)), 50 opt_set=({"n_iters": 1},), 51 mixing_ratios_g_kg=default_mixing_ratios_g_kg 52): 53 settings = Settings(nr=nr, mixing_ratios_g_kg=mixing_ratios_g_kg) 54 with parallel_backend("threading", n_jobs=-2): 55 results = Parallel(verbose=10)( 56 delayed(analysis)(settings, grid_layout, psi_coord, options, GC_max) 57 for grid_layout in grid_layouts 58 for options in deepcopy(opt_set) 59 ) 60 61 cases = {} 62 for result in results: 63 case = Case(result) 64 if case.grid_layout_str not in cases: 65 cases[case.grid_layout_str] = case 66 67 output = {} 68 for coord, case in cases.items(): 69 output[coord] = {"numerical": {}, "wall_time": {}} 70 for result in results: 71 if coord == result.grid_layout_str: 72 opts = result.option_str 73 data = result.result 74 rh = data.pop("rh") 75 r = data.pop("r") 76 dx = data.pop("dx") 77 78 if "grid" not in output[coord]: 79 output[coord]["grid"] = { 80 "rh": rh, 81 "r": r, 82 "dx": dx, 83 "dt": case.dt, 84 "out_steps": case.out_steps, 85 } 86 output[coord]["numerical"][opts] = data["n"] 87 88 for coord, case in cases.items(): 89 analytical = [] 90 for t in [case.dt * nt for nt in case.out_steps]: 91 pdf_t = PdfEvolver(settings.pdf, settings.drdt, t) 92 rh = output[coord]["grid"]["rh"] 93 analytical.append( 94 discretised_analytical_solution( 95 rh.magnitude, 96 lambda r, pdf_t=pdf_t, rh=rh: pdf_t(r * rh.units).magnitude, 97 midpoint_value=True, 98 r=output[coord]["grid"]["r"].magnitude, 99 ) 100 * pdf_t(rh[0]).units 101 ) 102 output[coord]["analytical"] = analytical 103 104 for coord, case in cases.items(): 105 error_L2 = {} 106 analytical = output[coord]["analytical"] 107 for opts in output[coord]["numerical"]: 108 numerical = output[coord]["numerical"][opts] 109 error_L2[opts] = L2( 110 numerical[-1].magnitude, analytical[-1].magnitude, case.out_steps[-1] 111 ) 112 output[coord]["error_L2"] = error_L2 113 114 for coord, case in cases.items(): 115 for result in results: 116 data = result.result 117 for opts in output[coord]["numerical"]: 118 output[coord]["wall_time"][opts] = data["wall_time"] 119 120 return output, settings
123class Result( 124 namedtuple("Result", ("dt", "out_steps", "grid_layout_str", "option_str", "result")) 125): 126 __slots__ = ()
Result(dt, out_steps, grid_layout_str, option_str, result)
def
rel_disp(r, psi, psi_coord):
135def rel_disp(r, psi, psi_coord): 136 mom0 = 0 137 mom1 = 0 138 mom2 = 0 139 for i, psi_i in enumerate(psi): 140 dp_i = psi_coord.moment_of_r_integral( 141 psi_coord.x(r[i + 1]), 0 142 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 0) 143 A_i = psi_coord.moment_of_r_integral( 144 psi_coord.x(r[i + 1]), 1 145 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 1) 146 B_i = psi_coord.moment_of_r_integral( 147 psi_coord.x(r[i + 1]), 2 148 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 2) 149 bin_mom0 = psi_i * dp_i 150 bin_mom1 = psi_i * A_i 151 bin_mom2 = psi_i * B_i 152 mom0 += bin_mom0 153 mom1 += bin_mom1 154 mom2 += bin_mom2 155 mu = mom1 / mom0 156 std = np.sqrt(mom2 / mom0 - mu**2) 157 d = std / mu 158 return d
def
third_moment(r, psi, psi_coord, normalised=True):
161def third_moment(r, psi, psi_coord, normalised=True): 162 mom0 = 0 163 mom3 = 0 164 for i, psi_i in enumerate(psi): 165 dp_i = psi_coord.moment_of_r_integral( 166 psi_coord.x(r[i + 1]), 0 167 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 0) 168 integral_i = psi_coord.moment_of_r_integral( 169 psi_coord.x(r[i + 1]), 3 170 ) - psi_coord.moment_of_r_integral(psi_coord.x(r[i]), 3) 171 bin_mom0 = psi_i * dp_i 172 bin_mom3 = psi_i * integral_i 173 mom0 += bin_mom0 174 mom3 += bin_mom3 175 if normalised: 176 mom3 /= mom0 177 return mom3