Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For merge #732

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ionization_module_tests_and_man/cmap_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import matplotlib
import numpy as np
import matplotlib.pyplot as plt

def cmap_map(function, cmap):
""" Applies function (which should operate on vectors of shape 3: [r, g, b]), on colormap cmap.
This routine will break any discontinuous points in a colormap.
"""
cdict = cmap._segmentdata
step_dict = {}
# Firt get the list of points where the segments start or end
for key in ('red', 'green', 'blue'):
step_dict[key] = list(map(lambda x: x[0], cdict[key]))
step_list = sum(step_dict.values(), [])
step_list = np.array(list(set(step_list)))
# Then compute the LUT, and apply the function to the LUT
reduced_cmap = lambda step : np.array(cmap(step)[0:3])
old_LUT = np.array(list(map(reduced_cmap, step_list)))
new_LUT = np.array(list(map(function, old_LUT)))
# Now try to make a minimal segment definition of the new LUT
cdict = {}
for i, key in enumerate(['red','green','blue']):
this_cdict = {}
for j, step in enumerate(step_list):
if step in step_dict[key]:
this_cdict[step] = new_LUT[j, i]
elif new_LUT[j,i] != old_LUT[j, i]:
this_cdict[step] = new_LUT[j, i]
colorvector = list(map(lambda x: x + (x[1], ), this_cdict.items()))
colorvector.sort()
cdict[key] = colorvector

return matplotlib.colors.LinearSegmentedColormap('colormap',cdict,1024)
210 changes: 210 additions & 0 deletions ionization_module_tests_and_man/input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import numpy as np
# import sys
# sys.path.append('/home/ent/Smilei_ionization/simulations/')


l0 = 2.0*np.pi # laser wavelength
t0 = l0 # optical cicle
Lsim = [12.*l0] # length of the simulation
Tsim = 24.*t0 # duration of the simulation
resx = 128. # nb of cells in one laser wavelength
dx = dy = dz = l0/resx
cell_length = [dx]
dt = 0.95*dx/np.sqrt(1.)
rest = t0/dt
timesteps = int(Tsim/dt)

Z = 18
mass = 39.948


# laser pulse input parameters
a0 = 100.
omega = 1.
N_cycles = 10.
xf = Lsim[0]/2.
tcenter = N_cycles*l0

Lmu = 0.8 # mu-m

c = 299792458. # m/sec
omega_ref = 2*np.pi*c/(Lmu*1.e-6) # 1/sec
eps0 = 8.8541878128e-12 # F⋅m^−1
charge_SI = 1.60217663e-19 # C
m_e_SI = 9.1093837e-31 # kg
N_r = eps0*m_e_SI*omega_ref**2/charge_SI**2
print('Reference density = ',N_r*10**(-6), ' cm-3')

I_S = 4.65e29 #W/cm^2
l_C = 3.8615901e-13 #m
I_ref = 0.5*(a0*omega_ref*l_C/c)**2.*4.65e29
print('Reference intensity = ',I_ref, ' W/cm^2')


# n0 = 5.e13/(N_r*1.e-6) # initial density 5.e13 cm^(-3)
# nppc = 16 # nb of particle per cells


n0 = 5.e13/(N_r*1.e-6)
thickness = 0.25*l0
position = Lsim[0]/2.-thickness/2.
def nf(x):
return n0*np.heaviside(x-position, 1.)*np.heaviside(position+thickness-x, 1.)
nppc = 128

Main(
geometry = "1Dcartesian",

interpolation_order = 2 ,

cell_length = cell_length,
grid_length = Lsim,

number_of_patches = [1],

timestep = dt,
simulation_time = Tsim,

reference_angular_frequency_SI = omega_ref,

EM_boundary_conditions = [ ["silver-muller", "silver-muller"] ],

)

def sin2_envelope(t, tcenter, N):
if (-np.pi*N <= t-tcenter) and (t-tcenter <= np.pi*N):
return np.sin((t-tcenter+np.pi*N)/2./N)**2
else:
return 0.



Laser(
box_side = "xmin",
omega = omega,
space_time_profile = [ lambda t: 0.,
lambda t: a0*sin2_envelope(t, tcenter, N_cycles)\
*np.cos(omega*((t-tcenter)-xf)) ],
)


Species(
name = 'atom_tunnel',
ionization_model = 'tunnel',
ionization_electrons = 'electron',
atomic_number = Z,
position_initialization = 'regular',
momentum_initialization = 'cold',
particles_per_cell = nppc,
mass = mass*1836.0,
charge = 0.,
# number_density = n0,
number_density = nf,
boundary_conditions = [['remove','remove']]
)


Species(
name = 'atom_TL',
ionization_model = 'tunnel_TL',
ionization_tl_parameter = 6,
ionization_electrons = 'electron',
atomic_number = Z,
position_initialization = 'regular',
momentum_initialization = 'cold',
particles_per_cell = nppc,
mass = mass*1836.0,
charge = 0.,
# number_density = n0,
number_density = nf,
boundary_conditions = [['remove','remove']]
)

Species(
name = 'atom_BSI',
ionization_model = 'tunnel_BSI',
ionization_electrons = 'electron',
atomic_number = Z,
position_initialization = 'regular',
momentum_initialization = 'cold',
particles_per_cell = nppc,
mass = mass*1836.0,
charge = 0.,
# number_density = n0,
number_density = nf,
boundary_conditions = [['remove','remove']]
)

Species(
name = 'atom_full_PPT',
ionization_model = 'tunnel_full_PPT',
ionization_electrons = 'electron',
atomic_number = Z,
position_initialization = 'regular',
momentum_initialization = 'cold',
particles_per_cell = nppc,
mass = mass*1836.0,
charge = 0.,
# number_density = n0,
number_density = nf,
boundary_conditions = [['remove','remove']]
)

Species(
name = 'electron',
position_initialization = 'regular',
momentum_initialization = 'cold',
particles_per_cell = 0,
mass = 1.0,
charge = -1.0,
charge_density = 0.0,
boundary_conditions = [['remove','remove']],
# time_frozen = 2.*Tsim
)


# DiagScalar(
# every = rest
# )


DiagParticleBinning(
deposited_quantity = "weight",
every = 1,
species = ["atom_tunnel"],
axes = [
["charge", -0.5, Z+0.5, Z+1]
]
)

DiagParticleBinning(
deposited_quantity = "weight",
every = 1,
species = ["atom_TL"],
axes = [
["charge", -0.5, Z+0.5, Z+1]
]
)

DiagParticleBinning(
deposited_quantity = "weight",
every = 1,
species = ["atom_BSI"],
axes = [
["charge", -0.5, Z+0.5, Z+1]
]
)

DiagParticleBinning(
deposited_quantity = "weight",
every = 1,
species = ["atom_full_PPT"],
axes = [
["charge", -0.5, Z+0.5, Z+1]
]
)

DiagFields(
every = 100,
fields = ['Ex','Ey','Ez','Bx','By','Bz']
)
Binary file not shown.
141 changes: 141 additions & 0 deletions ionization_module_tests_and_man/manual/ionization_manual.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
\documentclass[prd, preprint,
aps,
amsmath,
amssymb,
onecolumn,
%twocolumn,
nofootinbib,
%showpacs,
%showkeys,
superscriptaddress,
%longbibliography
]{revtex4-2}
\usepackage{graphicx,dcolumn,bm}
\usepackage{amsfonts,amsmath,amssymb,amsthm,amscd}
\usepackage[english]{babel}
\hyphenation{ALPGEN}
\hyphenation{EVTGEN}
\hyphenation{PYTHIA}
%\usepackage{multirow}
\usepackage{physics}
\sloppy\raggedbottom
\usepackage{needspace}
\usepackage{xcolor}
\usepackage{slashed}
\usepackage[export]{adjustbox}
\usepackage{tabularx}
\usepackage{hyperref}

\begin{document}

\title{Ionization modules for SMILEI beyond the PPT-ADK formula}

\author{A.~A. Mironov}
\affiliation{LULI, Sorbonne Université, CNRS, CEA, École Polytechnique, Institut Polytechnique de Paris, F-75255 Paris, France; \url{[email protected]}}

\maketitle

\section{Ionization rates}
\subsection{Tunneling ionization (PPT-ADK)}
All the notations are the same as on the reference page in the SMILEI manual \url{https://smileipic.github.io/Smilei/Understand/ionization.html}:
\begin{gather}
\label{PPT_definition}
\Gamma_{Z^*}=A_{n^*,l^*} B_{l,|m|}\left( \frac{2(2I_p)^{3/2}}{|E|} \right)^{2n^*-|m|-1} \exp\left\lbrace - \frac{2(2I_p)^{3/2}}{3|E|}\right\rbrace,\\
A_{n^*,l^*} = \frac{2^{2n^*}}{n^*\Gamma(n^*+l^*+1)\Gamma(n^*-l^*)},\\
B_{l,|m|} = \frac{(2l+1)\Gamma(l+|m|+1)}{2^{|m|}\Gamma(|m|+1)\Gamma(l-|m|+1)},
\end{gather}
where $\Gamma(x)$ is the Gamma function.

\subsubsection{Representation in the code (as in the standard ionization module)}

First, note that the effective orbital angular momentum $l^*=n^*-1$. This allows simplifying $A_{n^*,l^*}$. Second, we assume that the ionization is successive (outer shell electrons are extracted first) and that at the instant of ionization, each electron has the magnetic quantum number $m=0$. The ionization probability for an electron with $m=0$ is higher than for the one with $|m|>0$. The hypothesis is that electrons with $|m|>0$ have enough time to relax to the $m=0$ state once it is not occupied. With this, we set $m=0$ for all the electrons, and rewrite the PPT-ADK formula accordingly (note that in the code notations $Z=Z^*-1$):
\begin{gather}
\label{tunnel}
\Gamma_{Z}=\beta(Z)\exp\left\lbrace -\frac{\Delta}{3} + \alpha(Z)\log\Delta \right\rbrace,\\
\alpha(Z) = c^*-1,\quad c^*=(Z+1)\sqrt{\frac{2}{I_p}} \equiv 2n^*,\\
\beta(Z) = I_p\frac{2^{\alpha(Z)} \left[ 8l+4 \right]}{c^*\Gamma(c^*)} ,\\
\label{gamma_Delta}
\Delta = \frac{\gamma(Z)}{E}, \quad \gamma(Z)=2(2I_p)^{3/2}.
\end{gather}

\subsubsection{Bug fix in the standard model}
In \texttt{Smilei/src/Ionization/IonizationTunnel.cpp}, in the method \texttt{void IonizationTunnel::operator()}, array \texttt{Dnom[Z]} is not initialized. As a result, for high $Z$ atoms, the array can be uncontrollably allocated with random data, and the result of the operation \texttt{Dnom\_tunnel[k\_times+1] -= D\_sum} is not defined. As the value for \texttt{Dnom[k\_times+1]} is not defined anywhere before this line in the algorithm, the correction \texttt{Dnom\_tunnel[k\_times+1] = -D\_sum} resolves the bug. This correction is implemented in the standard ionization module and all new modules described below. However, it is recommended to review the code and initialize the array properly.

\subsection{Tunnelling ionization (PPT-ADK) with account for $|m|>0$}
The model adds the dependence on the magnetic quantum number $m$ as in Eq.~\eqref{PPT_definition}. It is implemented in the code by changing the definitions for $\alpha(Z)$ and $\beta(Z)$ in Eq.~\eqref{tunnel} as follows:
\begin{gather}
\alpha(Z) = c^*-|m|-1,\\
\beta(Z) = I_p\frac{2^{\alpha(Z)} \left[ 8l+4 \right]}{c^*\Gamma(c^*)} \times \frac{\Gamma(l+|m|+1)}{\Gamma(|m|+1)\Gamma(l-|m|+1)}.
\end{gather}
Note that $\beta(Z)$ depends both on $l^*$ and $l$.

The model is implemented in file \texttt{Smilei/src/Ionization/IonizationTunnelFullPPT.cpp} and can be used in the namelist by putting \texttt{ionization\_model = 'tunnel\_full\_PPT'} in the corresponding atom \texttt{Species}.

\subsubsection{Implementation details}
The magnetic quantum number $m$ is attributed to each electron in accordance with the following rules.
\begin{enumerate}
\item Since $\Gamma_Z(m=0)>\Gamma_Z(|m|=1)>\Gamma_Z(|m|=2)>\ldots$, we assume that electrons in the state with the lowest value of $|m|$ are ionized first.

\clearpage
\item Electrons with the same azimuthal quantum number $l$ occupy the sub-shells in the order of increasing $|m|$ and for the same $|m|$ in the order of increasing $m$:
\begin{table}[!h]
\begin{tabular}{l | c | c | c | c | c | c | c | c | c | c | c}
Level order num & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & $\ldots$ \\ \hline
Magnetic q. num $m$ & 0 & 0 & -1 & -1 & 1 & 1 & -2 & -2 & 2 & 2 & $\ldots$
\end{tabular}
\end{table}

For example, for the atomic configuration with 3 $p$-electrons ($l=1$) in the outer shell, we choose $m=0,\, 0, -1$ for these electrons.

\item The states with lower $|m|$ are ionized first.

\end{enumerate}
With this algorithm, by knowing the atomic number $A$, we can assign a unique set of quantum numbers $nlm$ to each electron on the atomic sub-shells and identify their extraction order during successive ionization.
For example, the full configuration for ${}_{12}$Mg and the corresponding order for the ionization process is:
\begin{table}[h!]
\begin{tabular}{l | c | c | c | c | c | c | c | c | c | c | c | c}
Shell & $3s$ & $3s$ & $2p$ & $2p$ & $2p$ & $2p$ & $2p$ & $2p$ & $2s$ & $2s$ & $1s$ & $1s$ \\\hline
$n$ & 3 & 3 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 2 & 1 & 1 \\\hline
$l$ & 0 & 0 & 1 & 1 & 1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 \\\hline
$m$ & 0 & 0 & 0 & 0 & -1 & -1 & 1 & 1 & 0 & 0 & 0 & 0 \\\hline
Ionization order & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12
\end{tabular}
\end{table}

The values for $m$ for each atom with $A=1\ldots99$ are stored in \texttt{Smilei/src/Ionization/IonizationTables.h} in the table named \texttt{magneticQuantumNumber}, which is similar to the table for $l$ used in the tunnelling formula \eqref{tunnel} in the standard SMILEI package. The table for $m$ was generated based on the table for $l$ following the rules presented above.

\textbf{Important note!} As a temporary solution for the $f$- ($l=3$) and higher shells, $m$ is always set to zero [namely, the rates are calculated with Eqs.~\eqref{tunnel}-\eqref{gamma_Delta}]. This is due to possible sub-level overlap for the $d$- and $f$-shells, that cannot be reproduced with the described algorithm for the table generation. This issue is to be resolved in the future.


\clearpage
\subsection{Tong \& Ling formula}
The formula proposed by Tong and Lin [\href{https://iopscience.iop.org/article/10.1088/0953-4075/38/15/001}{Tong X. M. and Lin C. D., J. Phys. B: At. Mol. Opt. Phys. 38 2593 (2005)}] extends the tunnelling ionization rate to the barrier-suppression regime. This is achieved by introducing the \textit{empirical} factor in Eq.~\eqref{PPT_definition}:
\begin{equation}
\Gamma_{Z^*}^{\text{TL}} = \Gamma_{Z^*}\times \exp\left( -2\alpha_{\text{TL}} n^{*2} \frac{E}{(2 I_p)^{3/2}}\right),
\end{equation}
where $\alpha_{\text{TL}}$ is an empirical constant. Typically, the value for it stays in the interval from 6 to 9. The actual value should be guessed from experimental data. When such data is not available, the formula can be used for qualitative analysis of the barrier-suppression ionization (BSI), e.g. see [\href{https://iopscience.iop.org/article/10.1088/1612-202X/ab6559}{M. F. Ciappina and S. V. Popruzhenko., Laser Phys. Lett. 17 025301 (2020)}]. The module was tested to reproduce the results from this paper.

\subsubsection{Representation in the code}
In the code, the Tong-Ling formula is implemented as follows:
\begin{gather}
\label{tonglin}
\Gamma_{Z}^\text{TL}=\Gamma_{Z}\times \exp\left[ -E\lambda(Z) \right] ,\\
\lambda(Z) = \frac{\alpha_{\text{TL}} c^{*2}}{\gamma(Z)},
\end{gather}
where $\gamma(Z)$ is given by Eq.~\eqref{gamma_Delta}.

The model is implemented in file \texttt{Smilei/src/Ionization/IonizationTunnelTL.cpp} and can be used in the namelist by putting \texttt{ionization\_model = 'tunnel\_TL'} in the corresponding atom \texttt{Species}. The $\alpha_{\text{TL}}$ can be set with the (newly added) parameter \texttt{ionization\_tl\_parameter = <value>} in the corresponding \texttt{Species}
(set to 6 by default).


\clearpage
\subsection{BSI formula}
I. Ouatu implemented a different model of BSI initially proposed in [\href{https://journals.aps.org/pra/abstract/10.1103/PhysRevA.98.043407}{I. Yu. Kostyukov and A. A. Golovanov, Phys. Rev. A 98, 043407 (2018)}]. The original implementation by I. Ouatu is available on Github \url{https://github.com/iouatu/mySmilei}. It was used in the study [\href{https://journals.aps.org/pre/abstract/10.1103/PhysRevE.106.015205}{I. Ouatu et al,
Phys. Rev. E 106, 015205 (2022)}]. The module is included in this version of SMILEI without changes (except for some small corrections in the header file) and was tested to reproduce the results of this paper.

The model uses the ionization rate Eq.~\eqref{tunnel} when applicable and switches to an empirical formula described in the original paper by Kostyukov \& Golovanov. There are no additional parameters introduced to tune the model.

The model is implemented in file \texttt{Smilei/src/Ionization/IonizationTunnelBSI.cpp} and can be used in the namelist by putting \texttt{ionization\_model = 'tunnel\_BSI'} in the corresponding atom \texttt{Species}. In the implementation, the code relies on some additional functionality introduced in \texttt{Smilei/src/Ionization/continuity\_tool.cpp}.


\end{document}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@CONTROL{REVTEX42Control}
@CONTROL{apsrev42Control,author="08",editor="1",pages="0",title="0",year="1"}
Loading