diff options
Diffstat (limited to 'Python')
-rw-r--r-- | Python/pywarpx/PICMI.py | 304 | ||||
-rw-r--r-- | Python/pywarpx/__init__.py | 2 | ||||
-rw-r--r-- | Python/pywarpx/picmi.py | 388 |
3 files changed, 389 insertions, 305 deletions
diff --git a/Python/pywarpx/PICMI.py b/Python/pywarpx/PICMI.py deleted file mode 100644 index 19fa665c6..000000000 --- a/Python/pywarpx/PICMI.py +++ /dev/null @@ -1,304 +0,0 @@ -"""Classes following the PICMI standard -""" -import PICMI_Base -import numpy as np -import pywarpx - -codename = 'WarpX' - -# --- Values from WarpXConst.H -c = 299792458. -ep0 = 8.854187817e-12 -mu0 = 1.2566370614359173e-06 -q_e = 1.602176462e-19 -m_e = 9.10938291e-31 -m_p = 1.6726231e-27 - - -class Species(PICMI_Base.PICMI_Species): - def init(self, **kw): - - if self.type == 'electron': - if self.charge is None: self.charge = '-q_e' - if self.mass is None: self.mass = 'm_e' - elif self.type == 'positron': - if self.charge is None: self.charge = 'q_e' - if self.mass is None: self.mass = 'm_e' - elif self.type == 'proton': - if self.charge is None: self.charge = 'q_e' - if self.mass is None: self.mass = 'm_p' - elif self.type == 'anti-proton': - if self.charge is None: self.charge = '-q_e' - if self.mass is None: self.mass = 'm_p' - - self.species_number = pywarpx.particles.nspecies - pywarpx.particles.nspecies += 1 - - if self.name is None: - self.name = 'species{}'.format(self.species_number) - - if pywarpx.particles.species_names is None: - pywarpx.particles.species_names = self.name - else: - pywarpx.particles.species_names += ' ' + self.name - - self.bucket = pywarpx.Bucket.Bucket(self.name, mass=self.mass, charge=self.charge, injection_style = 'python') - pywarpx.Particles.particles_list.append(self.bucket) - - -class GaussianBeam(PICMI_Base.PICMI_GaussianBeam): - def init(self, **kw): - - self.species.bucket.injection_style = "gaussian_beam" - self.species.bucket.x_m = self.Xmean - self.species.bucket.y_m = self.Ymean - self.species.bucket.z_m = self.Zmean - self.species.bucket.x_rms = self.Xrms - self.species.bucket.y_rms = self.Yrms - self.species.bucket.z_rms = self.Zrms - self.species.bucket.npart = self.number_sim_particles - - # --- Calculate the total charge. Note that charge might be a string instead of a number. - charge = self.species.bucket.charge - if charge == 'q_e' or charge == '+q_e': - charge = q_e - elif charge == '-q_e': - charge = -q_e - self.species.bucket.q_tot = self.number_real_particles*charge - - # --- These need to be defined even though they are not used - self.species.bucket.profile = "constant" - self.species.bucket.density = 1 - - # --- The PICMI standard doesn't yet have a way of specifying these values. - # --- They should default to the size of the domain. They are not typically - # --- necessary though since any particles outside the domain are rejected. - #self.species.bucket.xmin - #self.species.bucket.xmax - #self.species.bucket.ymin - #self.species.bucket.ymax - #self.species.bucket.zmin - #self.species.bucket.zmax - - if self.UXdiv != 0. and self.UYdiv != 0. and self.UZdiv != 0.: - self.species.bucket.momentum_distribution_type = "radial_expansion" - self.species.bucket.u_over_r = self.UXdiv - #self.species.bucket.u_over_y = self.UYdiv - #self.species.bucket.u_over_z = self.UZdiv - elif self.UXrms != 0. or self.UYrms != 0. or self.UZrms != 0.: - self.species.bucket.momentum_distribution_type = "gaussian" - self.species.bucket.ux_m = self.UXmean - self.species.bucket.uy_m = self.UYmean - self.species.bucket.uz_m = self.UZmean - self.species.bucket.ux_th = self.UXrms - self.species.bucket.uy_th = self.UYrms - self.species.bucket.uz_th = self.UZrms - else: - self.species.bucket.momentum_distribution_type = "constant" - self.species.bucket.ux = self.UXmean - self.species.bucket.uy = self.UYmean - self.species.bucket.uz = self.UZmean - - -class Plasma(PICMI_Base.PICMI_Plasma): - def init(self, **kw): - - for species in self.species: - if self.number_per_cell_each_dim is not None: - species.bucket.injection_style = "nuniformpercell" - species.bucket.num_particles_per_cell_each_dim = self.number_per_cell_each_dim - elif self.number_per_cell is not None: - species.bucket.injection_style = "nrandompercell" - species.bucket.num_particles_per_cell = self.number_per_cell - else: - raise Exception('Either nuniformpercell or nrandompercell must be specified') - - species.bucket.xmin = self.xmin - species.bucket.xmax = self.xmax - species.bucket.ymin = self.ymin - species.bucket.ymax = self.ymax - species.bucket.zmin = self.zmin - species.bucket.zmax = self.zmax - - # --- Only constant density is supported at this time - species.bucket.profile = "constant" - species.bucket.density = self.density - - if self.vthx != 0. or self.vthy != 0. or self.vthz != 0.: - species.bucket.momentum_distribution_type = "gaussian" - species.bucket.ux_m = self.vxmean - species.bucket.uy_m = self.vymean - species.bucket.uz_m = self.vzmean - species.bucket.ux_th = self.vthx - species.bucket.uy_th = self.vthy - species.bucket.uz_th = self.vthz - else: - species.bucket.momentum_distribution_type = "constant" - species.bucket.ux = self.vxmean - species.bucket.uy = self.vymean - species.bucket.uz = self.vzmean - - if self.fill_in: - pywarpx.warpx.do_plasma_injection = 1 - if not hasattr(pywarpx.warpx, 'injected_plasma_species'): - pywarpx.warpx.injected_plasma_species = [] - - pywarpx.warpx.injected_plasma_species.append(species.species_number) - pywarpx.warpx.num_injected_species = len(pywarpx.warpx.injected_plasma_species) - - -class ParticleList(PICMI_Base.PICMI_ParticleList): - def init(self, **kw): - - if len(x) > 1: - raise Exception('Only a single particle can be loaded') - - self.species.bucket.injection_style = "singleparticle" - self.species.bucket.single_particle_pos = [self.x[0], self.y[0], self.z[0]] - self.species.bucket.single_particle_vel = [self.ux[0]/c, self.uy[0]/c, self.uz[0]/c] - self.species.bucket.single_particle_weight = self.weight - - # --- These need to be defined even though they are not used - self.species.bucket.profile = "constant" - self.species.bucket.density = 1 - self.species.bucket.momentum_distribution_type = 'constant' - - -class Grid(PICMI_Base.PICMI_Grid): - def init(self, **kw): - - pywarpx.amr.n_cell = [self.nx, self.ny, self.nz] - - # Maximum allowable size of each subdomain in the problem domain; - # this is used to decompose the domain for parallel calculations. - pywarpx.amr.max_grid_size = kw.get('max_grid_size', 32) - - # Maximum level in hierarchy (for now must be 0, i.e., one level in total) - pywarpx.amr.max_level = kw.get('max_level', 0) - - # Geometry - pywarpx.geometry.coord_sys = kw.get('coord_sys', 0) # 0: Cartesian - pywarpx.geometry.is_periodic = '%d %d %d'%(self.bcxmin=='periodic', self.bcymin=='periodic', self.bczmin=='periodic') # Is periodic? - pywarpx.geometry.prob_lo = [self.xmin, self.ymin, self.zmin] # physical domain - pywarpx.geometry.prob_hi = [self.xmax, self.ymax, self.zmax] - - if self.moving_window_velocity is not None and np.any(np.not_equal(self.moving_window_velocity, 0.)): - pywarpx.warpx.do_moving_window = 1 - if self.moving_window_velocity[0] != 0.: - pywarpx.warpx.moving_window_dir = 'x' - pywarpx.warpx.moving_window_v = self.moving_window_velocity[0]/c # in units of the speed of light - if self.moving_window_velocity[1] != 0.: - pywarpx.warpx.moving_window_dir = 'y' - pywarpx.warpx.moving_window_v = self.moving_window_velocity[1]/c # in units of the speed of light - if self.moving_window_velocity[2] != 0.: - pywarpx.warpx.moving_window_dir = 'z' - pywarpx.warpx.moving_window_v = self.moving_window_velocity[2]/c # in units of the speed of light - - def getmins(self, **kw): - return np.array([pywarpx.warpx.getProbLo(0), pywarpx.warpx.getProbLo(1), pywarpx.warpx.getProbLo(2)]) - - def getmaxs(self, **kw): - return np.array([pywarpx.warpx.getProbHi(0), pywarpx.warpx.getProbHi(1), pywarpx.warpx.getProbHi(2)]) - - def getxmin(self): - return pywarpx.warpx.getProbLo(0) - - def getxmax(self): - return pywarpx.warpx.getProbHi(0) - - def getymin(self): - return pywarpx.warpx.getProbLo(1) - - def getymax(self): - return pywarpx.warpx.getProbHi(1) - - def getzmin(self): - return pywarpx.warpx.getProbLo(2) - - def getzmax(self): - return pywarpx.warpx.getProbHi(2) - - -class EM_solver(PICMI_Base.PICMI_EM_solver): - def init(self, **kw): - - if self.method is None: - self.method = 'Yee' - - assert self.method in ['Yee'], Exception("Only 'Yee' FDTD is supported") - - if 'current_deposition_algo' in kw: - pywarpx.algo.current_deposition = kw['current_deposition_algo'] - if 'charge_deposition_algo' in kw: - pywarpx.algo.charge_deposition = kw['charge_deposition_algo'] - if 'field_gathering_algo' in kw: - pywarpx.algo.field_gathering = kw['field_gathering_algo'] - if 'particle_pusher_algo' in kw: - pywarpx.algo.particle_pusher = kw['particle_pusher_algo'] - - pywarpx.interpolation.nox = self.norderx - pywarpx.interpolation.noy = self.nordery - pywarpx.interpolation.noz = self.norderz - -class Simulation(PICMI_Base.PICMI_Simulation): - def init(self, **kw): - - pywarpx.warpx.verbose = self.verbose - pywarpx.warpx.cfl = self.timestep_over_cfl - if self.timestep == 0.: - pywarpx.warpx.cfl = self.timestep_over_cfl - else: - pywarpx.warpx.const_dt = self.timestep - - if 'plot_int' in kw: - pywarpx.amr.plot_int = kw['plot_int'] - - self.initialized = False - - def initialize(self, inputs_name=None): - if not self.initialized: - self.initialized = True - pywarpx.warpx.init() - - def write_inputs(self, inputs_name='inputs'): - kw = {} - if self.max_step is not None: - kw['max_step'] = self.max_step - if self.max_time is not None: - kw['stop_time'] = self.max_time - pywarpx.warpx.write_inputs(inputs_name, **kw) - - def step(self, nsteps=None): - self.initialize() - if nsteps is None: - if self.max_step is not None: - nsteps = self.max_step - else: - nsteps = -1 - pywarpx.warpx.evolve(nsteps) - - def finalize(self): - if self.initialized: - self.initialized = False - pywarpx.warpx.finalize() - - -class Gaussian_laser(PICMI_Base.PICMI_Gaussian_laser): - def init(self, **kw): - - pywarpx.warpx.use_laser = 1 - pywarpx.laser.profile = "Gaussian" - pywarpx.laser.wavelength = self.wavelength # The wavelength of the laser (in meters) - pywarpx.laser.e_max = self.E0 # Maximum amplitude of the laser field (in V/m) - pywarpx.laser.polarization = [np.cos(self.pol_angle), np.sin(self.pol_angle), 0.] # The main polarization vector - pywarpx.laser.profile_waist = self.waist # The waist of the laser (in meters) - pywarpx.laser.profile_duration = self.duration # The duration of the laser (in seconds) - pywarpx.laser.profile_t_peak = (self.focal_position - self.z0)/c # The time at which the laser reaches its peak (in seconds) - - -class Laser_antenna(PICMI_Base.PICMI_Laser_antenna): - def init(self, **kw): - - pywarpx.laser.position = [self.antenna_x0, self.antenna_y0, self.antenna_z0] # This point is on the laser plane - pywarpx.laser.direction = [self.antenna_xvec, self.antenna_yvec, self.antenna_zvec] # The plane normal direction - pywarpx.laser.profile_focal_distance = self.laser.focal_position - self.antenna_z0 # Focal distance from the antenna (in meters) diff --git a/Python/pywarpx/__init__.py b/Python/pywarpx/__init__.py index cd6237d39..14d028a45 100644 --- a/Python/pywarpx/__init__.py +++ b/Python/pywarpx/__init__.py @@ -11,7 +11,7 @@ from .Laser import laser #from .timestepper import TimeStepper from .PGroup import PGroup from .PGroup import PGroups -from .WarpXPIC import WarpXPIC +#from .WarpXPIC import WarpXPIC from ._libwarpx import add_particles diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py new file mode 100644 index 000000000..771edf85f --- /dev/null +++ b/Python/pywarpx/picmi.py @@ -0,0 +1,388 @@ +"""Classes following the PICMI standard +""" +import PICMI_Base +import numpy as np +import pywarpx + +codename = 'WarpX' + +# --- Values from WarpXConst.H +c = 299792458. +ep0 = 8.854187817e-12 +mu0 = 1.2566370614359173e-06 +q_e = 1.602176462e-19 +m_e = 9.10938291e-31 +m_p = 1.6726231e-27 + + +class Species(PICMI_Base.PICMI_Species): + def init(self, **kw): + + if self.particle_type == 'electron': + if self.charge is None: self.charge = '-q_e' + if self.mass is None: self.mass = 'm_e' + elif self.particle_type == 'positron': + if self.charge is None: self.charge = 'q_e' + if self.mass is None: self.mass = 'm_e' + elif self.particle_type == 'proton': + if self.charge is None: self.charge = 'q_e' + if self.mass is None: self.mass = 'm_p' + elif self.particle_type == 'anti-proton': + if self.charge is None: self.charge = '-q_e' + if self.mass is None: self.mass = 'm_p' + + def initialize_inputs(self, layout): + self.species_number = pywarpx.particles.nspecies + pywarpx.particles.nspecies += 1 + + if self.name is None: + self.name = 'species{}'.format(self.species_number) + + if pywarpx.particles.species_names is None: + pywarpx.particles.species_names = self.name + else: + pywarpx.particles.species_names += ' ' + self.name + + self.species = pywarpx.Bucket.Bucket(self.name, mass=self.mass, charge=self.charge, injection_style = 'python') + pywarpx.Particles.particles_list.append(self.species) + + if self.initial_distribution is not None: + self.initial_distribution.initialize_inputs(self.species_number, layout, self.species) + + +PICMI_Base.PICMI_MultiSpecies.Species_class = Species +class MultiSpecies(PICMI_Base.PICMI_MultiSpecies): + pass + + +class GaussianBunchDistribution(PICMI_Base.PICMI_GaussianBunchDistribution): + def init(self, **kw): + if self.seed is not None: + print('Warning: WarpX does not support specifying the random number seed') + + def initialize_inputs(self, species_number, layout, species): + species.injection_style = "gaussian_beam" + species.x_m = self.centroid_position[0] + species.y_m = self.centroid_position[1] + species.z_m = self.centroid_position[2] + species.x_rms = self.rms_bunch_size[0] + species.y_rms = self.rms_bunch_size[1] + species.z_rms = self.rms_bunch_size[2] + + # --- Only PseudoRandomLayout is supported + species.npart = layout.n_macroparticles + + # --- Calculate the total charge. Note that charge might be a string instead of a number. + charge = species.charge + if charge == 'q_e' or charge == '+q_e': + charge = q_e + elif charge == '-q_e': + charge = -q_e + species.q_tot = self.number_real_particles*charge + + # --- These need to be defined even though they are not used + species.profile = "constant" + species.density = 1 + + # --- The PICMI standard doesn't yet have a way of specifying these values. + # --- They should default to the size of the domain. They are not typically + # --- necessary though since any particles outside the domain are rejected. + #species.xmin + #species.xmax + #species.ymin + #species.ymax + #species.zmin + #species.zmax + + if np.any(np.not_equal(self.velocity_divergence, 0.)): + species.momentum_distribution_type = "radial_expansion" + species.u_over_r = self.velocity_divergence[0] + #species.u_over_y = self.velocity_divergence[1] + #species.u_over_z = self.velocity_divergence[2] + elif np.any(np.not_equal(self.rms_velocity, 0.)): + species.momentum_distribution_type = "gaussian" + species.ux_m = self.centroid_velocity[0] + species.uy_m = self.centroid_velocity[1] + species.uz_m = self.centroid_velocity[2] + species.ux_th = self.rms_velocity[0] + species.uy_th = self.rms_velocity[1] + species.uz_th = self.rms_velocity[2] + else: + species.momentum_distribution_type = "constant" + species.ux = self.centroid_velocity[0] + species.uy = self.centroid_velocity[1] + species.uz = self.centroid_velocity[2] + + +class UniformDistribution(PICMI_Base.PICMI_UniformDistribution): + + def initialize_inputs(self, species_number, layout, species): + + if isinstance(layout, GriddedLayout): + species.injection_style = "nuniformpercell" + species.num_particles_per_cell_each_dim = layout.n_macroparticle_per_cell + elif isinstance(layout, PseudoRandomLayout): + assert (layout.n_macroparticles_per_cell is not None), Exception('WarpX only supports n_macroparticles_per_cell for the GriddedLayout with UniformDistribution') + species.injection_style = "nrandompercell" + species.num_particles_per_cell = layout.n_macroparticles_per_cell + else: + raise Exception('WarpX does not support the specified layout for UniformDistribution') + + species.xmin = self.lower_bound[0] + species.xmax = self.upper_bound[0] + species.ymin = self.lower_bound[1] + species.ymax = self.upper_bound[1] + species.zmin = self.lower_bound[2] + species.zmax = self.upper_bound[2] + + # --- Only constant density is supported at this time + species.profile = "constant" + species.density = self.density + + if np.any(np.not_equal(self.rms_velocity, 0.)): + species.momentum_distribution_type = "gaussian" + species.ux_m = self.directed_velocity[0] + species.uy_m = self.directed_velocity[1] + species.uz_m = self.directed_velocity[2] + species.ux_th = self.rms_velocity[0] + species.uy_th = self.rms_velocity[1] + species.uz_th = self.rms_velocity[2] + else: + species.momentum_distribution_type = "constant" + species.ux = self.directed_velocity[0] + species.uy = self.directed_velocity[1] + species.uz = self.directed_velocity[2] + + if self.fill_in: + pywarpx.warpx.do_plasma_injection = 1 + if not hasattr(pywarpx.warpx, 'injected_plasma_species'): + pywarpx.warpx.injected_plasma_species = [] + + pywarpx.warpx.injected_plasma_species.append(species_number) + pywarpx.warpx.num_injected_species = len(pywarpx.warpx.injected_plasma_species) + + +class AnalyticDistribution(PICMI_Base.PICMI_AnalyticDistribution): + + def initialize_inputs(self, species_number, layout, species): + raise Exception('WarpX does not support AnalyticDistribution') + + +class ParticleList(PICMI_Base.PICMI_ParticleList): + def init(self, **kw): + + if len(x) > 1: + raise Exception('Only a single particle can be loaded') + + def initialize_inputs(self, species_number, layout, species): + + species.injection_style = "singleparticle" + species.single_particle_pos = [self.x[0], self.y[0], self.z[0]] + species.single_particle_vel = [self.ux[0]/c, self.uy[0]/c, self.uz[0]/c] + species.single_particle_weight = self.weight + + # --- These need to be defined even though they are not used + species.profile = "constant" + species.density = 1 + species.momentum_distribution_type = 'constant' + + +class ParticleDistributionPlanarInjector(PICMI_Base.PICMI_ParticleDistributionPlanarInjector): + pass + + +class GriddedLayout(PICMI_Base.PICMI_GriddedLayout): + pass + + +class PseudoRandomLayout(PICMI_Base.PICMI_PseudoRandomLayout): + pass + + +class BinomialSmoother(PICMI_Base.PICMI_BinomialSmoother): + pass + + +class CylindricalGrid(PICMI_Base.PICMI_CylindricalGrid): + def init(self, **kw): + raise Exception('WarpX does not support CylindricalGrid') + + +class Cartesian2DGrid(PICMI_Base.PICMI_Cartesian2DGrid): + def init(self, **kw): + self.max_grid_size = kw.get('max_grid_size', 32) + self.max_level = kw.get('max_level', 0) + self.coord_sys = kw.get('coord_sys', 0) + + def initialize_inputs(self): + pywarpx.amr.n_cell = self.number_of_cells + + # Maximum allowable size of each subdomain in the problem domain; + # this is used to decompose the domain for parallel calculations. + pywarpx.amr.max_grid_size = self.max_grid_size + + # Maximum level in hierarchy (for now must be 0, i.e., one level in total) + pywarpx.amr.max_level = self.max_level + + # Geometry + pywarpx.geometry.coord_sys = self.coord_sys + pywarpx.geometry.is_periodic = '%d %d %d'%(self.bc_xmin=='periodic', self.bc_ymin=='periodic') # Is periodic? + pywarpx.geometry.prob_lo = self.lower_bound # physical domain + pywarpx.geometry.prob_hi = self.upper_bound + + if self.moving_window_velocity is not None and np.any(np.not_equal(self.moving_window_velocity, 0.)): + pywarpx.warpx.do_moving_window = 1 + if self.moving_window_velocity[0] != 0.: + pywarpx.warpx.moving_window_dir = 'x' + pywarpx.warpx.moving_window_v = self.moving_window_velocity[0]/c # in units of the speed of light + if self.moving_window_velocity[1] != 0.: + pywarpx.warpx.moving_window_dir = 'y' + pywarpx.warpx.moving_window_v = self.moving_window_velocity[1]/c # in units of the speed of light + + +class Cartesian3DGrid(PICMI_Base.PICMI_Cartesian3DGrid): + def init(self, **kw): + self.max_grid_size = kw.get('max_grid_size', 32) + self.max_level = kw.get('max_level', 0) + self.coord_sys = kw.get('coord_sys', 0) + + def initialize_inputs(self): + pywarpx.amr.n_cell = self.number_of_cells + + # Maximum allowable size of each subdomain in the problem domain; + # this is used to decompose the domain for parallel calculations. + pywarpx.amr.max_grid_size = self.max_grid_size + + # Maximum level in hierarchy (for now must be 0, i.e., one level in total) + pywarpx.amr.max_level = self.max_level + + # Geometry + pywarpx.geometry.coord_sys = self.coord_sys + pywarpx.geometry.is_periodic = '%d %d %d'%(self.bc_xmin=='periodic', self.bc_ymin=='periodic', self.bc_zmin=='periodic') # Is periodic? + pywarpx.geometry.prob_lo = self.lower_bound # physical domain + pywarpx.geometry.prob_hi = self.upper_bound + + if self.moving_window_velocity is not None and np.any(np.not_equal(self.moving_window_velocity, 0.)): + pywarpx.warpx.do_moving_window = 1 + if self.moving_window_velocity[0] != 0.: + pywarpx.warpx.moving_window_dir = 'x' + pywarpx.warpx.moving_window_v = self.moving_window_velocity[0]/c # in units of the speed of light + if self.moving_window_velocity[1] != 0.: + pywarpx.warpx.moving_window_dir = 'y' + pywarpx.warpx.moving_window_v = self.moving_window_velocity[1]/c # in units of the speed of light + if self.moving_window_velocity[2] != 0.: + pywarpx.warpx.moving_window_dir = 'z' + pywarpx.warpx.moving_window_v = self.moving_window_velocity[2]/c # in units of the speed of light + + +class ElectromagneticSolver(PICMI_Base.PICMI_ElectromagneticSolver): + def init(self, **kw): + assert self.method is None or self.method in ['Yee'], Exception("Only 'Yee' FDTD is supported") + + def initialize_inputs(self): + + self.grid.initialize_inputs() + + if self.cfl is not None: + pywarpx.warpx.cfl = self.cfl + + if self.stencil_order is not None: + pywarpx.interpolation.nox = self.stencil_order[0] + pywarpx.interpolation.noy = self.stencil_order[1] + pywarpx.interpolation.noz = self.stencil_order[2] + + +class Electrostatic_solver(PICMI_Base.PICMI_Electrostatic_solver): + def initialize_inputs(self): + pass + + +class GaussianLaser(PICMI_Base.PICMI_GaussianLaser): + + def initialize_inputs(self): + pywarpx.warpx.use_laser = 1 + pywarpx.laser.profile = "Gaussian" + pywarpx.laser.wavelength = self.wavelength # The wavelength of the laser (in meters) + pywarpx.laser.e_max = self.E0 # Maximum amplitude of the laser field (in V/m) + pywarpx.laser.polarization = [np.cos(self.polarization_angle), np.sin(self.polarization_angle), 0.] # The main polarization vector + pywarpx.laser.profile_waist = self.waist # The waist of the laser (in meters) + pywarpx.laser.profile_duration = self.duration # The duration of the laser (in seconds) + + +class LaserAntenna(PICMI_Base.PICMI_LaserAntenna): + + def initialize_inputs(self, laser): + pywarpx.laser.position = self.position # This point is on the laser plane + pywarpx.laser.direction = self.normal_vector # The plane normal direction + pywarpx.laser.profile_focal_distance = laser.focal_position[2] - self.position[2] # Focal distance from the antenna (in meters) + pywarpx.laser.profile_t_peak = (self.position[2] - laser.centroid_position[2])/c # The time at which the laser reaches its peak (in seconds) + + +class Simulation(PICMI_Base.PICMI_Simulation): + def init(self, **kw): + + self.plot_int = kw.get('plot_int', None) + self.current_deposition_algo = kw.get('current_deposition_algo', None) + self.charge_deposition_algo = kw.get('charge_deposition_algo', None) + self.field_gathering_algo = kw.get('field_gathering_algo', None) + self.particle_pusher_algo = kw.get('particle_pusher_algo', None) + + self.inputs_initialized = False + self.warpx_initialized = False + + def initialize_inputs(self): + if self.inputs_initialized: + return + + self.inputs_initialized = True + + pywarpx.warpx.verbose = self.verbose + if self.time_step_size is not None: + pywarpx.warpx.const_dt = self.timestep + + pywarpx.amr.plot_int = self.plot_int + pywarpx.algo.current_deposition = self.current_deposition_algo + pywarpx.algo.charge_deposition = self.charge_deposition_algo + pywarpx.algo.field_gathering = self.field_gathering_algo + pywarpx.algo.particle_pusher = self.particle_pusher_algo + + self.solver.initialize_inputs() + + for i in range(len(self.species)): + assert self.calculate_self_fields[i], Exception('WarpX does not support species without self fields') + self.species[i].initialize_inputs(self.layouts[i]) + + for i in range(len(self.lasers)): + self.lasers[i].initialize_inputs() + self.laser_injection_methods[i].initialize_inputs(self.lasers[i]) + + def initialize_warpx(self, inputs_name=None): + if self.warpx_initialized: + return + + self.warpx_initialized = True + pywarpx.warpx.init() + + def write_input_file(self, inputs_name='inputs'): + self.initialize_inputs() + kw = {} + if self.max_steps is not None: + kw['max_step'] = self.max_steps + if self.max_time is not None: + kw['stop_time'] = self.max_time + pywarpx.warpx.write_inputs(inputs_name, **kw) + + def step(self, nsteps=None): + self.initialize_inputs() + self.initialize_warpx() + if nsteps is None: + if self.max_steps is not None: + nsteps = self.max_steps + else: + nsteps = -1 + pywarpx.warpx.evolve(nsteps) + + def finalize(self): + if self.warpx_initialized: + self.warpx_initialized = False + pywarpx.warpx.finalize() |