|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +from pywarpx import picmi |
| 4 | + |
| 5 | +# Physical constants |
| 6 | +c = picmi.constants.c |
| 7 | +q_e = picmi.constants.q_e |
| 8 | + |
| 9 | +# We only run 100 steps for tests |
| 10 | +# Disable `max_step` below to run until the physical `stop_time`. |
| 11 | +max_step = 100 |
| 12 | +# time-scale with highly kinetic dynamics |
| 13 | +stop_time = 0.2e-12 |
| 14 | + |
| 15 | +# proper resolution for 30 n_c (dx<=3.33nm) incl. acc. length |
| 16 | +# (>=6x V100) |
| 17 | +# --> choose larger `max_grid_size` and `blocking_factor` for 1 to 8 grids per GPU accordingly |
| 18 | +#nx = 7488 |
| 19 | +#nz = 14720 |
| 20 | + |
| 21 | +# Number of cells |
| 22 | +nx = 384 |
| 23 | +nz = 512 |
| 24 | + |
| 25 | +# Domain decomposition (deactivate `warpx_numprocs` in `picmi.Simulation` for this to take effect) |
| 26 | +max_grid_size = 64 |
| 27 | +blocking_factor = 32 |
| 28 | + |
| 29 | +# Physical domain |
| 30 | +xmin = -7.5e-06 |
| 31 | +xmax = 7.5e-06 |
| 32 | +zmin = -5.0e-06 |
| 33 | +zmax = 25.0e-06 |
| 34 | + |
| 35 | +# Create grid |
| 36 | +grid = picmi.Cartesian2DGrid( |
| 37 | + number_of_cells=[nx, nz], |
| 38 | + lower_bound=[xmin, zmin], |
| 39 | + upper_bound=[xmax, zmax], |
| 40 | + lower_boundary_conditions=['open', 'open'], |
| 41 | + upper_boundary_conditions=['open', 'open'], |
| 42 | + lower_boundary_conditions_particles=['absorbing', 'absorbing'], |
| 43 | + upper_boundary_conditions_particles=['absorbing', 'absorbing'], |
| 44 | + warpx_max_grid_size=max_grid_size, |
| 45 | + warpx_blocking_factor=blocking_factor) |
| 46 | + |
| 47 | +# Particles: plasma parameters |
| 48 | +# critical plasma density |
| 49 | +nc = 1.742e27 # [m^-3] 1.11485e21 * 1.e6 / 0.8**2 |
| 50 | +# number density: "fully ionized" electron density as reference |
| 51 | +# [material 1] cryogenic H2 |
| 52 | +n0 = 30.0 # [n_c] |
| 53 | +# [material 2] liquid crystal |
| 54 | +# n0 = 192 |
| 55 | +# [material 3] PMMA |
| 56 | +# n0 = 230 |
| 57 | +# [material 4] Copper (ion density: 8.49e28/m^3; times ionization level) |
| 58 | +# n0 = 1400 |
| 59 | +plasma_density = n0 * nc |
| 60 | +preplasma_L = 0.05e-6 # [m] scale length (>0) |
| 61 | +preplasma_Lcut = 2.0e-6 # [m] hard cutoff from surface |
| 62 | +plasma_r0 = 2.5e-6 # [m] radius or half-thickness |
| 63 | +plasma_eps_z = 0.05e-6 # [m] small offset in z to make zmin, zmax interval larger than 2*(r0 + Lcut) |
| 64 | +plasma_creation_limit_z = plasma_r0 + preplasma_Lcut + plasma_eps_z # [m] upper limit in z for particle creation |
| 65 | + |
| 66 | +plasma_xmin = None |
| 67 | +plasma_ymin = None |
| 68 | +plasma_zmin = -plasma_creation_limit_z |
| 69 | +plasma_xmax = None |
| 70 | +plasma_ymax = None |
| 71 | +plasma_zmax = plasma_creation_limit_z |
| 72 | + |
| 73 | +density_expression_str = f'{plasma_density}*((abs(z)<={plasma_r0}) + (abs(z)<{plasma_r0}+{preplasma_Lcut}) * (abs(z)>{plasma_r0}) * exp(-(abs(z)-{plasma_r0})/{preplasma_L}))' |
| 74 | + |
| 75 | +slab_with_ramp_dist_hydrogen = picmi.AnalyticDistribution( |
| 76 | + density_expression=density_expression_str, |
| 77 | + lower_bound=[plasma_xmin, plasma_ymin, plasma_zmin], |
| 78 | + upper_bound=[plasma_xmax, plasma_ymax, plasma_zmax] |
| 79 | +) |
| 80 | + |
| 81 | +# thermal velocity spread for electrons in gamma*beta |
| 82 | +ux_th = .01 |
| 83 | +uz_th = .01 |
| 84 | + |
| 85 | +slab_with_ramp_dist_electrons = picmi.AnalyticDistribution( |
| 86 | + density_expression=density_expression_str, |
| 87 | + lower_bound=[plasma_xmin, plasma_ymin, plasma_zmin], |
| 88 | + upper_bound=[plasma_xmax, plasma_ymax, plasma_zmax], |
| 89 | + # if `momentum_expressions` and `momentum_spread_expressions` are unset, |
| 90 | + # a Gaussian momentum distribution is assumed given that `rms_velocity` has any non-zero elements |
| 91 | + rms_velocity=[c*ux_th, 0., c*uz_th] # thermal velocity spread in m/s |
| 92 | +) |
| 93 | + |
| 94 | +# TODO: add additional attributes orig_x and orig_z |
| 95 | +electrons = picmi.Species( |
| 96 | + particle_type='electron', |
| 97 | + name='electrons', |
| 98 | + initial_distribution=slab_with_ramp_dist_electrons, |
| 99 | +) |
| 100 | + |
| 101 | +# TODO: add additional attributes orig_x and orig_z |
| 102 | +hydrogen = picmi.Species( |
| 103 | + particle_type='proton', |
| 104 | + name='hydrogen', |
| 105 | + initial_distribution=slab_with_ramp_dist_hydrogen |
| 106 | +) |
| 107 | + |
| 108 | +# Laser |
| 109 | +# e_max = a0 * 3.211e12 / lambda_0[mu] |
| 110 | +# a0 = 16, lambda_0 = 0.8mu -> e_max = 64.22 TV/m |
| 111 | +e_max = 64.22e12 |
| 112 | +position_z = -4.0e-06 |
| 113 | +profile_t_peak = 50.e-15 |
| 114 | +profile_focal_distance = 4.0e-06 |
| 115 | +laser = picmi.GaussianLaser( |
| 116 | + wavelength=0.8e-06, |
| 117 | + waist=4.e-06, |
| 118 | + duration=30.e-15, |
| 119 | + focal_position=[0, 0, profile_focal_distance + position_z], |
| 120 | + centroid_position=[0, 0, position_z - c * profile_t_peak], |
| 121 | + propagation_direction=[0, 0, 1], |
| 122 | + polarization_direction=[1, 0, 0], |
| 123 | + E0=e_max, |
| 124 | + fill_in=False) |
| 125 | +laser_antenna = picmi.LaserAntenna( |
| 126 | + position=[0., 0., position_z], |
| 127 | + normal_vector=[0, 0, 1]) |
| 128 | + |
| 129 | +# Electromagnetic solver |
| 130 | +solver = picmi.ElectromagneticSolver( |
| 131 | + grid=grid, |
| 132 | + method='Yee', |
| 133 | + cfl=0.999, |
| 134 | + divE_cleaning=0, |
| 135 | + #warpx_pml_ncell=10 |
| 136 | +) |
| 137 | + |
| 138 | +# Diagnostics |
| 139 | +diag_field_list = ['B', 'E', 'J', 'rho', 'rho_electrons', 'rho_hydrogen'] |
| 140 | +particle_diag = picmi.ParticleDiagnostic( |
| 141 | + name='diag1', |
| 142 | + period=100, |
| 143 | + write_dir='./diags', |
| 144 | + species=[electrons], |
| 145 | + data_list=['weighting', 'position', 'momentum'], |
| 146 | + warpx_format='openpmd', |
| 147 | + warpx_openpmd_backend='bp', |
| 148 | + # >500keV for electrons (first term is m*c*c/q_e) |
| 149 | + warpx_plot_filter_function='(x<1.0e-6) * (x>-1.0e-6) * (5.1100e+05*(sqrt(1.0+ux*ux+uy*uy+uz*uz)-1.0)>500.0e3)' |
| 150 | +) |
| 151 | +# reduce resolution of output fields |
| 152 | +coarsening_ratio = [4, 4] |
| 153 | +ncell_field = [] |
| 154 | +for (ncell_comp, cr) in zip([nx,nz], coarsening_ratio): |
| 155 | + ncell_field.append(int(ncell_comp/cr)) |
| 156 | +field_diag = picmi.FieldDiagnostic( |
| 157 | + name='diag1', |
| 158 | + grid=grid, |
| 159 | + period=100, |
| 160 | + number_of_cells=ncell_field, |
| 161 | + data_list=diag_field_list, |
| 162 | + write_dir='./diags', |
| 163 | + warpx_format='openpmd', |
| 164 | + warpx_openpmd_backend='bp' |
| 165 | +) |
| 166 | + |
| 167 | +particle_fw_diag = picmi.ParticleDiagnostic( |
| 168 | + name='openPMDfw', |
| 169 | + period=100, |
| 170 | + write_dir='./diags', |
| 171 | + species=[hydrogen], |
| 172 | + data_list=['weighting', 'position', 'momentum'], |
| 173 | + warpx_format='openpmd', |
| 174 | + warpx_openpmd_backend='h5', |
| 175 | + warpx_plot_filter_function='(uz>=0) * (x<1.0e-6) * (x>-1.0e-6)' |
| 176 | +) |
| 177 | + |
| 178 | +particle_bw_diag = picmi.ParticleDiagnostic( |
| 179 | + name='openPMDbw', |
| 180 | + period=100, |
| 181 | + write_dir='./diags', |
| 182 | + species=[hydrogen, electrons], |
| 183 | + data_list=['weighting', 'position', 'momentum'], |
| 184 | + warpx_format='openpmd', |
| 185 | + warpx_openpmd_backend='h5', |
| 186 | + warpx_plot_filter_function='(uz<0)' |
| 187 | +) |
| 188 | + |
| 189 | +# histograms with 2.0 degree acceptance angle in fw direction |
| 190 | +# 2 deg * pi / 180 : 0.03490658503 rad |
| 191 | +# half-angle +/- : 0.017453292515 rad |
| 192 | +histuH_rdiag = picmi.ReducedDiagnostic( |
| 193 | + diag_type='ParticleHistogram', |
| 194 | + name='histuH', |
| 195 | + period=100, |
| 196 | + species=hydrogen, |
| 197 | + bin_number=1000, |
| 198 | + bin_min=0.0, |
| 199 | + bin_max=0.474, # 100 MeV protons |
| 200 | + histogram_function='u2=ux*ux+uy*uy+uz*uz; if(u2>0, sqrt(u2), 0.0)', |
| 201 | + filter_function='u2=ux*ux+uy*uy+uz*uz; if(u2>0, abs(acos(uz / sqrt(u2))) <= 0.017453, 0)') |
| 202 | + |
| 203 | +histue_rdiag = picmi.ReducedDiagnostic( |
| 204 | + diag_type='ParticleHistogram', |
| 205 | + name='histue', |
| 206 | + period=100, |
| 207 | + species=electrons, |
| 208 | + bin_number=1000, |
| 209 | + bin_min=0.0, |
| 210 | + bin_max=197.0, # 100 MeV electrons |
| 211 | + histogram_function='u2=ux*ux+uy*uy+uz*uz; if(u2>0, sqrt(u2), 0.0)', |
| 212 | + filter_function='u2=ux*ux+uy*uy+uz*uz; if(u2>0, abs(acos(uz / sqrt(u2))) <= 0.017453, 0)') |
| 213 | + |
| 214 | +# just a test entry to make sure that the histogram filter is purely optional: |
| 215 | +# this one just records uz of all hydrogen ions, independent of their pointing |
| 216 | +histuzAll_rdiag = picmi.ReducedDiagnostic( |
| 217 | + diag_type='ParticleHistogram', |
| 218 | + name='histuzAll', |
| 219 | + period=100, |
| 220 | + species=hydrogen, |
| 221 | + bin_number=1000, |
| 222 | + bin_min=-0.474, |
| 223 | + bin_max=0.474, |
| 224 | + histogram_function='uz') |
| 225 | + |
| 226 | +field_probe_z_rdiag = picmi.ReducedDiagnostic( |
| 227 | + diag_type='FieldProbe', |
| 228 | + name='FieldProbe_Z', |
| 229 | + period=100, |
| 230 | + integrate=0, |
| 231 | + probe_geometry='Line', |
| 232 | + x_probe=0.0, |
| 233 | + z_probe=-5.0e-6, |
| 234 | + x1_probe=0.0, |
| 235 | + z1_probe=25.0e-6, |
| 236 | + resolution=3712) |
| 237 | + |
| 238 | +field_probe_scat_point_rdiag = picmi.ReducedDiagnostic( |
| 239 | + diag_type='FieldProbe', |
| 240 | + name='FieldProbe_ScatPoint', |
| 241 | + period=1, |
| 242 | + integrate=0, |
| 243 | + probe_geometry='Point', |
| 244 | + x_probe=0.0, |
| 245 | + z_probe=15.0e-6) |
| 246 | + |
| 247 | +field_probe_scat_line_rdiag = picmi.ReducedDiagnostic( |
| 248 | + diag_type='FieldProbe', |
| 249 | + name='FieldProbe_ScatLine', |
| 250 | + period=100, |
| 251 | + integrate=1, |
| 252 | + probe_geometry='Line', |
| 253 | + x_probe=-2.5e-6, |
| 254 | + z_probe=15.0e-6, |
| 255 | + x1_probe=2.5e-6, |
| 256 | + z1_probe=15e-6, |
| 257 | + resolution=201) |
| 258 | + |
| 259 | +load_balance_costs_rdiag = picmi.ReducedDiagnostic( |
| 260 | + diag_type='LoadBalanceCosts', |
| 261 | + name='LBC', |
| 262 | + period=100) |
| 263 | + |
| 264 | +# Set up simulation |
| 265 | +sim = picmi.Simulation( |
| 266 | + solver=solver, |
| 267 | + max_time=stop_time, # need to remove `max_step` to run this far |
| 268 | + verbose=1, |
| 269 | + particle_shape='cubic', |
| 270 | + warpx_numprocs=[1, 2], # deactivate `numprocs` for dynamic load balancing |
| 271 | + warpx_use_filter=1, |
| 272 | + warpx_load_balance_intervals=100, |
| 273 | + warpx_load_balance_costs_update='heuristic' |
| 274 | +) |
| 275 | + |
| 276 | +# Add plasma electrons |
| 277 | +sim.add_species( |
| 278 | + electrons, |
| 279 | + layout=picmi.GriddedLayout(grid=grid, n_macroparticle_per_cell=[2,2]) |
| 280 | + # for more realistic simulations, try to avoid that macro-particles represent more than 1 n_c |
| 281 | + #layout=picmi.GriddedLayout(grid=grid, n_macroparticle_per_cell=[4,8]) |
| 282 | +) |
| 283 | + |
| 284 | +# Add hydrogen ions |
| 285 | +sim.add_species( |
| 286 | + hydrogen, |
| 287 | + layout=picmi.GriddedLayout(grid=grid, n_macroparticle_per_cell=[2,2]) |
| 288 | + # for more realistic simulations, try to avoid that macro-particles represent more than 1 n_c |
| 289 | + #layout=picmi.GriddedLayout(grid=grid, n_macroparticle_per_cell=[4,8]) |
| 290 | +) |
| 291 | + |
| 292 | +# Add laser |
| 293 | +sim.add_laser( |
| 294 | + laser, |
| 295 | + injection_method=laser_antenna) |
| 296 | + |
| 297 | +# Add full diagnostics |
| 298 | +sim.add_diagnostic(particle_diag) |
| 299 | +sim.add_diagnostic(field_diag) |
| 300 | +sim.add_diagnostic(particle_fw_diag) |
| 301 | +sim.add_diagnostic(particle_bw_diag) |
| 302 | +# Add reduced diagnostics |
| 303 | +sim.add_diagnostic(histuH_rdiag) |
| 304 | +sim.add_diagnostic(histue_rdiag) |
| 305 | +sim.add_diagnostic(histuzAll_rdiag) |
| 306 | +sim.add_diagnostic(field_probe_z_rdiag) |
| 307 | +sim.add_diagnostic(field_probe_scat_point_rdiag) |
| 308 | +sim.add_diagnostic(field_probe_scat_line_rdiag) |
| 309 | +sim.add_diagnostic(load_balance_costs_rdiag) |
| 310 | +# TODO: make ParticleHistogram2D available |
| 311 | + |
| 312 | +# Write input file that can be used to run with the compiled version |
| 313 | +sim.write_input_file(file_name='inputs_2d_picmi') |
| 314 | + |
| 315 | +# Initialize inputs and WarpX instance |
| 316 | +sim.initialize_inputs() |
| 317 | +sim.initialize_warpx() |
| 318 | + |
| 319 | +# Advance simulation until last time step |
| 320 | +sim.step(max_step) |
0 commit comments