Laser-wakefield acceleration with ionizationΒΆ
You can download the script below by clicking on this link
.
"""
This is an input script that runs a simulation of
laser-wakefield acceleration with ionization, using FBPIC.
More precisely, this uses a mix of Helium and Nitrogen atoms. To save
computational time, the Helium is assumed to be already pre-ionized
up to level 1 (He+) and the Nitrogen is assumed to be pre-ionized up to
level 5 (N 5+)
Usage
-----
- Modify the parameters below to suit your needs
- Type "python ionization_script.py" in a terminal
Help
----
All the structures implemented in FBPIC are internally documented.
Enter "print(fbpic_object.__doc__)" to have access to this documentation,
where fbpic_object is any of the objects or function of FBPIC.
"""
# -------
# Imports
# -------
import numpy as np
from scipy.constants import c, e, m_e, m_p
# Import the relevant structures from fbpic
from fbpic.main import Simulation
from fbpic.utils.random_seed import set_random_seed
from fbpic.lpa_utils.laser import add_laser_pulse
from fbpic.lpa_utils.laser.laser_profiles import GaussianLaser
from fbpic.openpmd_diag import FieldDiagnostic, \
ParticleDiagnostic, ParticleChargeDensityDiagnostic, \
set_periodic_checkpoint, restart_from_checkpoint
# ----------
# Parameters
# ----------
# Whether to use the GPU
use_cuda = True
# Order of the stencil for z derivatives in the Maxwell solver.
# Use -1 for infinite order, i.e. for exact dispersion relation in
# all direction (adviced for single-GPU/single-CPU simulation).
# Use a positive number (and multiple of 2) for a finite-order stencil
# (required for multi-GPU/multi-CPU with MPI). A large `n_order` leads
# to more overhead in MPI communications, but also to a more accurate
# dispersion relation for electromagnetic waves. (Typically,
# `n_order = 32` is a good trade-off.)
# See https://arxiv.org/abs/1611.05712 for more information.
n_order = -1
# The simulation box
Nz = 800 # Number of gridpoints along z
zmax = 10.e-6 # Right end of the simulation box (meters)
zmin = -30.e-6 # Left end of the simulation box (meters)
Nr = 50 # Number of gridpoints along r
rmax = 20.e-6 # Length of the box along r (meters)
Nm = 2 # Number of modes used
# The simulation timestep
dt = (zmax-zmin)/Nz/c # Timestep (seconds)
# The particles
p_zmin = 0.e-6 # Position of the beginning of the plasma (meters)
n_He = 2.e24 # Density of Helium atoms
n_N = 1.e24 # Density of Nitrogen atoms
p_nz = 1 # Number of particles per cell along z
p_nr = 2 # Number of particles per cell along r
p_nt = 4 # Number of particles per cell along theta
# The laser
a0 = 4. # Laser amplitude
w0 = 5.e-6 # Laser waist
tau = 16.e-15 # Laser duration
z0 = -5.e-6 # Laser centroid
z_foc = 20.e-6 # Focal position
# The moving window
v_window = c # Speed of the window
# The diagnostics and the checkpoints/restarts
diag_period = 50 # Period of the diagnostics in number of timesteps
save_checkpoints = False # Whether to write checkpoint files
checkpoint_period = 100 # Period for writing the checkpoints
use_restart = False # Whether to restart from a previous checkpoint
track_electrons = False # Whether to track and write particle ids
# The density profile
ramp_length = 20.e-6
def dens_func( z, r ) :
"""Returns relative density at position z and r"""
# Allocate relative density
n = np.ones_like(z)
# Make sine-like ramp
n = np.where( z<ramp_length, np.sin(np.pi/2*z/ramp_length)**2, n )
# Supress density before the ramp
n = np.where( z<0, 0., n )
return(n)
# The interaction length of the simulation (meters)
L_interact = 50.e-6 # increase to simulate longer distance!
# Interaction time (seconds) (to calculate number of PIC iterations)
T_interact = ( L_interact + (zmax-zmin) ) / v_window
# (i.e. the time it takes for the moving window to slide across the plasma)
# ---------------------------
# Carrying out the simulation
# ---------------------------
if __name__ == '__main__':
# Set the random seed
set_random_seed(0)
# Initialize the simulation object
sim = Simulation( Nz, zmax, Nr, rmax, Nm, dt, zmin=zmin,
n_order=n_order, use_cuda=use_cuda,
boundaries={'z':'open', 'r':'reflective'})
# 'r': 'open' can also be used, but is more computationally expensive
# Add the Helium ions (pre-ionized up to level 1),
# the Nitrogen ions (pre-ionized up to level 5)
# and the associated electrons (from the pre-ionized levels)
atoms_He = sim.add_new_species( q=e, m=4.*m_p, n=n_He,
dens_func=dens_func, p_nz=p_nz, p_nr=p_nr, p_nt=p_nt, p_zmin=p_zmin )
atoms_N = sim.add_new_species( q=5*e, m=14.*m_p, n=n_N,
dens_func=dens_func, p_nz=p_nz, p_nr=p_nr, p_nt=p_nt, p_zmin=p_zmin )
# Important: the electron density from N5+ is 5x larger than that from He+
n_e = n_He + 5*n_N
elec = sim.add_new_species( q=-e, m=m_e, n=n_e,
dens_func=dens_func, p_nz=p_nz, p_nr=p_nr, p_nt=p_nt, p_zmin=p_zmin )
# Activate ionization of He ions (for levels above 1).
# Store the created electrons in the species `elec`
atoms_He.make_ionizable( 'He', target_species=elec, level_start=1 )
# Activate ionization of N ions (for levels above 5).
# Store the created electrons in a new dedicated electron species that
# does not contain any macroparticles initially
elec_from_N = sim.add_new_species( q=-e, m=m_e )
atoms_N.make_ionizable( 'N', target_species=elec_from_N, level_start=5 )
# Create a Gaussian laser profile
laser_profile = GaussianLaser(a0, w0, tau, z0, zf=z_foc)
# Add the laser to the fields of the simulation
add_laser_pulse( sim, laser_profile)
if use_restart is False:
# Track electrons if required (species 0 correspond to the electrons)
if track_electrons:
elec.track( sim.comm )
else:
# Load the fields and particles from the latest checkpoint file
restart_from_checkpoint( sim )
# Configure the moving window
sim.set_moving_window( v=v_window )
# Add diagnostics
sim.diags = [
FieldDiagnostic( diag_period, sim.fld, comm=sim.comm ),
ParticleDiagnostic( diag_period,
{"electrons from N": elec_from_N, "electrons": elec},
comm=sim.comm ),
# Since rho from `FieldDiagnostic` is 0 almost everywhere
# (neutral plasma), it is useful to see the charge density
# of individual particles
ParticleChargeDensityDiagnostic( diag_period, sim,
{"electrons": elec} )
]
# Add checkpoints
if save_checkpoints:
set_periodic_checkpoint( sim, checkpoint_period )
# Number of iterations to perform
N_step = int(T_interact/sim.dt)
### Run the simulation
sim.step( N_step )
print('')