Skip to content

Commit

Permalink
Added CI to test secondary ion emission in RZ.
Browse files Browse the repository at this point in the history
  • Loading branch information
oshapoval committed Jan 18, 2025
1 parent e4bdcae commit b8e49e1
Show file tree
Hide file tree
Showing 6 changed files with 359 additions and 0 deletions.
1 change: 1 addition & 0 deletions Examples/Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ add_subdirectory(resampling)
add_subdirectory(restart)
add_subdirectory(restart_eb)
add_subdirectory(rigid_injection)
add_subdirectory(secondary_ion_emission)
add_subdirectory(scraping)
add_subdirectory(effective_potential_electrostatic)
add_subdirectory(silver_mueller)
Expand Down
14 changes: 14 additions & 0 deletions Examples/Tests/secondary_ion_emission/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Add tests (alphabetical order) ##############################################
#

if(WarpX_EB)
add_warpx_test(
test_rz_secondary_ion_emission_picmi # name
RZ # dims
1 # nprocs
inputs_test_rz_secondary_ion_emission_picmi.py # inputs
"analysis.py diags/diag1/" # analysis
"analysis_default_regression.py --path diags/diag1/" # checksum
OFF # dependency
)
endif()
62 changes: 62 additions & 0 deletions Examples/Tests/secondary_ion_emission/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env python
"""
This script tests the last coordinates of the emitted secondary electrons.
The EB sphere is centered on O and has a radius of 0.2.
The proton is initially at: (0,0,-0.25) and moves with a velocity:
(0.1e6,0,1.5e6) with a time step of dt = 0.000000015.
The simulation uses a fixed random seed (np.random.seed(10025015))
to ensure the emission of secodnary electrons.
An input file inputs_test_rz_secondary_ion_emission_picmi.py is used.
"""

import sys

import numpy as np
import yt
from openpmd_viewer import OpenPMDTimeSeries

yt.funcs.mylog.setLevel(0)

# Open plotfile specified in command line
filename = sys.argv[1]
ts = OpenPMDTimeSeries(filename)

it = ts.iterations
x, y, z = ts.get_particle(["x", "y", "z"], species="electrons", iteration=it[-1])
print("x", x)
print("y", y)
print("z", z)
# Analytical results calculated
x_analytic = [0.004028, 0.003193]
y_analytic = [-0.0001518, -0.0011041]
z_analytic = [-0.19967, -0.19926]

N_sec_e = np.size(z) # number of the secondary electrons

assert N_sec_e == 2, (
"Test did not pass: for this set up we expect 2 secondary electrons emitted"
)

tolerance = 1e-3

for i in range(0, N_sec_e):
print("\n")
print(f"Electron # {i}:")
print("NUMERICAL coordinates of the emitted electrons:")
print("x=%5.5f, y=%5.5f, z=%5.5f" % (x[i], y[i], z[i]))
print("\n")
print("ANALYTICAL coordinates of the point of contact:")
print("x=%5.5f, y=%5.5f, z=%5.5f" % (x_analytic[i], y_analytic[i], z_analytic[i]))

diff_x = np.abs((x[i] - x_analytic[i]) / x_analytic[i])
diff_y = np.abs((y[i] - y_analytic[i]) / y_analytic[i])
diff_z = np.abs((z[i] - z_analytic[i]) / z_analytic[i])

print("\n")
print("percentage error for x = %5.4f %%" % (diff_x * 100))
print("percentage error for y = %5.4f %%" % (diff_y * 100))
print("percentage error for z = %5.4f %%" % (diff_z * 100))

assert (diff_x < tolerance) and (diff_y < tolerance) and (diff_z < tolerance), (
"Test particle_boundary_interaction did not pass"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
#!/usr/bin/env python3
# --- Input file for secondary-ion emission testing in RZ via callback function.
import numpy as np
from scipy.constants import e, elementary_charge, m_e, proton_mass

from pywarpx import callbacks, particle_containers, picmi

##########################
# numerics parameters
##########################

dt = 0.000000015

# --- Nb time steps
Te = 0.0259 # in eV
dist_th = np.sqrt(Te * elementary_charge / m_e)

max_steps = 3
diagnostic_interval = 1

# --- grid
nr = 64
nz = 64

rmin = 0.0
rmax = 2
zmin = -2
zmax = 2
delta_H = 0.4
E_HMax = 250

np.random.seed(10025015)
##########################
# numerics components
##########################

grid = picmi.CylindricalGrid(
number_of_cells=[nr, nz],
n_azimuthal_modes=1,
lower_bound=[rmin, zmin],
upper_bound=[rmax, zmax],
lower_boundary_conditions=["none", "dirichlet"],
upper_boundary_conditions=["dirichlet", "dirichlet"],
lower_boundary_conditions_particles=["none", "reflecting"],
upper_boundary_conditions_particles=["absorbing", "reflecting"],
)

solver = picmi.ElectrostaticSolver(
grid=grid, method="Multigrid", warpx_absolute_tolerance=1e-7
)

embedded_boundary = picmi.EmbeddedBoundary(
implicit_function="-(x**2+y**2+z**2-radius**2)", radius=0.2
)

##########################
# physics components
##########################

i_dist = picmi.ParticleListDistribution(
x=0.0, y=0.0, z=-0.25, ux=0.1e6, uy=0.0, uz=1.50e6, weight=1
)
electrons = picmi.Species(
particle_type="electron", # Specify the particle type
name="electrons", # Name of the species
)

ions = picmi.Species(
name="ions",
mass=proton_mass,
charge=e,
initial_distribution=i_dist,
warpx_save_particles_at_eb=1,
)

##########################
# diagnostics
##########################

field_diag = picmi.FieldDiagnostic(
name="diag1",
grid=grid,
period=diagnostic_interval,
data_list=["Er", "Ez", "phi", "rho"], # , "rho_electrons"],
warpx_format="openpmd",
)

part_diag = picmi.ParticleDiagnostic(
name="diag1",
period=diagnostic_interval,
species=[ions, electrons],
warpx_format="openpmd",
)

##########################
# simulation setup
##########################
# num_procs = [1, 1]
# warpx_numprocs=num_procs,
sim = picmi.Simulation(
solver=solver,
time_step_size=dt,
max_steps=max_steps,
warpx_embedded_boundary=embedded_boundary,
warpx_amrex_the_arena_is_managed=1,
)

sim.add_species(
electrons,
layout=picmi.GriddedLayout(n_macroparticle_per_cell=[0, 0, 0], grid=grid),
)

sim.add_species(
ions,
layout=picmi.GriddedLayout(n_macroparticle_per_cell=[10, 1, 1], grid=grid),
)

sim.add_diagnostic(part_diag)
sim.add_diagnostic(field_diag)

sim.initialize_inputs()
sim.initialize_warpx()

##########################
# python particle data access
##########################


def concat(list_of_arrays):
if len(list_of_arrays) == 0:
# Return a 1d array of size 0
return np.empty(0)
else:
return np.concatenate(list_of_arrays)


def sigma_nascap(energy_kEv, delta_H, E_HMax):
"""
Compute sigma_nascap for each element in the energy array using a loop.
Parameters:
- energy: ndarray or list, energy values in KeV
- delta_H: float, parameter for the formula
- E_HMax: float, parameter for the formula in KeV
Returns:
- numpy array, computed probability sigma_nascap
"""
sigma_nascap = np.array([])
# Loop through each energy value
for energy in energy_kEv:
if energy > 0.0:
sigma = (
delta_H
* (E_HMax + 1.0)
/ (E_HMax * 1.0 + energy)
* np.sqrt(energy / 1.0)
)
else:
sigma = 0.0
sigma_nascap = np.append(sigma_nascap, sigma)
return sigma_nascap


def secondary_emission():
buffer = particle_containers.ParticleBoundaryBufferWrapper() # boundary buffer
# STEP 1: extract the different parameters of the boundary buffer (normal, time, position)
lev = 0 # level 0 (no mesh refinement here)
n = buffer.get_particle_boundary_buffer_size("ions", "eb")
elect_pc = particle_containers.ParticleContainerWrapper("electrons")

if n != 0:
r = concat(buffer.get_particle_boundary_buffer("ions", "eb", "x", lev))
theta = concat(buffer.get_particle_boundary_buffer("ions", "eb", "theta", lev))
z = concat(buffer.get_particle_boundary_buffer("ions", "eb", "z", lev))
x = r * np.cos(theta) # from RZ coordinates to 3D coordinates
y = r * np.sin(theta)
ux = concat(buffer.get_particle_boundary_buffer("ions", "eb", "ux", lev))
uy = concat(buffer.get_particle_boundary_buffer("ions", "eb", "uy", lev))
uz = concat(buffer.get_particle_boundary_buffer("ions", "eb", "uz", lev))
w = concat(buffer.get_particle_boundary_buffer("ions", "eb", "w", lev))
nx = concat(buffer.get_particle_boundary_buffer("ions", "eb", "nx", lev))
ny = concat(buffer.get_particle_boundary_buffer("ions", "eb", "ny", lev))
nz = concat(buffer.get_particle_boundary_buffer("ions", "eb", "nz", lev))
delta_t = concat(
buffer.get_particle_boundary_buffer("ions", "eb", "deltaTimeScraped", lev)
)
energy_ions = 0.5 * proton_mass * w * (ux**2 + uy**2 + uz**2)
energy_ions_in_kEv = energy_ions / (1.602176634e-19 * 1000)
sigma_nascap_ions = sigma_nascap(energy_ions_in_kEv, delta_H, E_HMax)
# Loop over all ions in the EB buffer
for i in range(0, n):
sigma = sigma_nascap_ions[i]
sigma_int = int(sigma)
rn = np.random.uniform(sigma_int, sigma_int + 1 + np.finfo(float).eps)
if rn < sigma:
Ne_sec = (
sigma_int + 1
) # number of the secondary electrons to be emitted
for j in [0, Ne_sec - 1]:

Check failure

Code scanning / CodeQL

Suspicious unused loop iteration variable Error

For loop variable 'j' is not used in the loop body.
xe = np.array([])
ye = np.array([])
ze = np.array([])
we = np.array([])
delta_te = np.array([])
uxe = np.array([])
uye = np.array([])
uze = np.array([])

# Random thermal momenta distribution
ux_th = np.random.normal(0, dist_th)
uy_th = np.random.normal(0, dist_th)
uz_th = np.random.normal(0, dist_th)

un_th = nx[i] * ux_th + ny[i] * uy_th + nz[i] * uz_th

if un_th < 0:
ux_th_reflect = (
-2 * un_th * nx[i] + ux_th
) # for a "mirror reflection" u(sym)=-2(u.n)n+u
uy_th_reflect = -2 * un_th * ny[i] + uy_th
uz_th_reflect = -2 * un_th * nz[i] + uz_th

uxe = np.append(uxe, ux_th_reflect)
uye = np.append(uye, uy_th_reflect)
uze = np.append(uze, uz_th_reflect)
else:
uxe = np.append(uxe, ux_th)
uye = np.append(uye, uy_th)
uze = np.append(uze, uz_th)

xe = np.append(xe, x[i])
ye = np.append(ye, y[i])
ze = np.append(ze, z[i])
we = np.append(we, w[i])
delta_te = np.append(delta_te, delta_t[i])

elect_pc.add_particles(
x=xe + (dt - delta_te) * uxe,
y=ye + (dt - delta_te) * uye,
z=ze + (dt - delta_te) * uze,
ux=uxe,
uy=uye,
uz=uze,
w=we,
)
buffer.clear_buffer() # reinitialise the boundary buffer


# using the new particle container modified at the last step
callbacks.installafterstep(secondary_emission)
##########################
# simulation run
##########################
sim.step(max_steps) # the whole process is done "max_steps" times
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"electrons": {
"particle_momentum_x": 6.863524723051401e-26,
"particle_momentum_y": 1.0324224192517369e-25,
"particle_momentum_z": 5.621885683115495e-26,
"particle_position_x": 0.0072217326490580675,
"particle_position_y": 0.001255871169011761,
"particle_position_z": 0.39892796754417836,
"particle_weight": 2.0
},
"ions": {
"particle_momentum_x": 0.0,
"particle_momentum_y": 0.0,
"particle_momentum_z": 0.0,
"particle_position_x": 0.0,
"particle_position_y": 0.0,
"particle_position_z": 0.0,
"particle_weight": 0.0
},
"lev=0": {
"Er": 3.996898574068735e-08,
"Ez": 3.77948124311196e-08,
"phi": 2.359076749421693e-08,
"rho": 4.6171309216540875e-15
}
}

0 comments on commit b8e49e1

Please sign in to comment.