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

Add WarpX example for FEL simulation #5337

Merged
merged 58 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
a2df74a
General moving-window transformations in boosted-frame simulations
bnara Sep 8, 2024
5ea727d
Default speed of moving window to speed of boosted frame
bnara Sep 9, 2024
09d4ed7
Extend simulation volume enough so that particles don't exit
bnara Sep 9, 2024
3df0002
Add inputs script for FEL
RemiLehe Sep 26, 2024
868d1e4
Add positrons
RemiLehe Sep 26, 2024
0dcd093
Add absorbing boundary conditions
RemiLehe Sep 26, 2024
0fa13a0
Use correct gamm_f
RemiLehe Sep 26, 2024
580e236
Correct box size
RemiLehe Sep 26, 2024
0f1da2c
Prepare moving window and BTD
RemiLehe Sep 26, 2024
d7297c7
Add CMake file for free-electron laser test
RemiLehe Sep 26, 2024
2af7c20
Add automated test
RemiLehe Sep 26, 2024
031decd
Add check for the wavelength
RemiLehe Sep 26, 2024
201472c
Add backtransformed diagnostics
RemiLehe Sep 26, 2024
6fb9bac
Check lab-frame files
RemiLehe Sep 26, 2024
da3bb83
Add 3D script
RemiLehe Sep 26, 2024
d19e4a7
Add ramps and compensating kicks
RemiLehe Sep 26, 2024
bd6abc5
Correct the density in order to take into account the two beams
RemiLehe Sep 26, 2024
3abd31b
Remove kicks in 1D ; higher tolerances
RemiLehe Sep 26, 2024
6c699d3
Remove 3D example for now
RemiLehe Sep 27, 2024
32e5e84
Add new example
RemiLehe Sep 27, 2024
a6f9ab7
Add example for free-electron laser
RemiLehe Sep 27, 2024
e037329
Add checksum
RemiLehe Sep 27, 2024
9e8f9f7
Include moving window speed in diag_lo and diag_hi transformations
bnara Sep 27, 2024
388c93e
More specific citation
RemiLehe Sep 27, 2024
9c85737
Update Examples/Physics_applications/free_electron_laser/README.rst
RemiLehe Sep 27, 2024
e49dfa4
Add benchmark file
RemiLehe Sep 27, 2024
8cf934e
Merge branch 'fel' of github.com:RemiLehe/WarpX into fel
RemiLehe Sep 27, 2024
478f3cf
Modify bounds so as to produce the same results as in previous versions
RemiLehe Sep 27, 2024
7af8f79
Update checksums
RemiLehe Sep 28, 2024
b4b9e0b
Merge branch 'boosted_mw' into fel
RemiLehe Sep 28, 2024
71c616c
Update simulation bounds
RemiLehe Sep 28, 2024
9264525
Update simulation script
RemiLehe Sep 28, 2024
947f223
Update checksums
RemiLehe Sep 28, 2024
c815eeb
Merge branch 'development' into fel
RemiLehe Sep 28, 2024
216a7e3
Update size of the moving window
RemiLehe Sep 28, 2024
3d4c608
Update boosted-frame for arbitrary mobing window
RemiLehe Sep 28, 2024
5a2b025
Update beam position
RemiLehe Sep 28, 2024
bf534c6
Correct boosted-frame for arbitrary moving window
RemiLehe Sep 28, 2024
6da3106
Update time to final timestep
RemiLehe Sep 28, 2024
828e3e4
Let simulation determine how many steps to take
RemiLehe Sep 28, 2024
02c1dee
Reach staturation
RemiLehe Sep 29, 2024
dcc0dc2
Avoid interpolating from guard cells in BTD
RemiLehe Sep 29, 2024
15208c6
Avoid interpolating from guard cells in BTD
RemiLehe Sep 29, 2024
2e3bcfa
Change max_grid_size
RemiLehe Sep 29, 2024
4ced35f
Evaluate checksum from BTD
RemiLehe Sep 29, 2024
6aa1382
Update scripts
RemiLehe Sep 29, 2024
c935ae3
Add plot script
RemiLehe Sep 29, 2024
3e905d2
Update documentation page
RemiLehe Sep 29, 2024
97447e6
Update checksum
RemiLehe Sep 29, 2024
dfc77ad
Merge branch 'avoid_btd_guard_cells' into fel
RemiLehe Sep 29, 2024
d532b02
Reactivate checksum
RemiLehe Sep 30, 2024
703fa33
Update checksum
RemiLehe Sep 30, 2024
44aa5d8
Rely on rigid injection for injection into the undulator
RemiLehe Oct 1, 2024
5421ff4
Use rigid advance
RemiLehe Oct 1, 2024
5438ac2
Merge branch 'development' into fel
RemiLehe Oct 11, 2024
9ed44f6
Apply suggestions from code review
RemiLehe Oct 11, 2024
cc95001
Revert "Rely on rigid injection for injection into the undulator"
RemiLehe Oct 11, 2024
7209799
Revert "Use rigid advance"
RemiLehe Oct 11, 2024
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
35 changes: 35 additions & 0 deletions Docs/source/refs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,38 @@ @article{Vranic2015
issn = {0010-4655},
doi = {https://doi.org/10.1016/j.cpc.2015.01.020},
}

@misc{Fallahi2020,
title={MITHRA 2.0: A Full-Wave Simulation Tool for Free Electron Lasers},
author={Arya Fallahi},
year={2020},
eprint={2009.13645},
archivePrefix={arXiv},
primaryClass={physics.acc-ph},
url={https://arxiv.org/abs/2009.13645},
}

@article{VayFELA2009,
title = {FULL ELECTROMAGNETIC SIMULATION OF FREE-ELECTRON LASER AMPLIFIER PHYSICS VIA THE LORENTZ-BOOSTED FRAME APPROACH},
author = {Fawley, William M and Vay, Jean-Luc},
abstractNote = {Numerical simulation of some systems containing charged particles with highly relativistic directed motion can by speeded up by orders of magnitude by choice of the proper Lorentz-boosted frame[1]. A particularly good example is that of short wavelength free-electron lasers (FELs) in which a high energy electron beam interacts with a static magnetic undulator. In the optimal boost frame with Lorentz factor gamma_F , the red-shifted FEL radiation and blue shifted undulator have identical wavelengths and the number of required time-steps (presuming the Courant condition applies) decreases by a factor of 2(gamma_F)**2 for fully electromagnetic simulation. We have adapted the WARP code [2]to apply this method to several FEL problems involving coherent spontaneous emission (CSE) from pre-bunched ebeams, including that in a biharmonic undulator.},
url = {https://www.osti.gov/biblio/964405},
place = {United States},
year = {2009},
month = {4},
}

@article{VayFELB2009,
author = {Fawley, W. M. and Vay, J.‐L.},
title = "{Use of the Lorentz‐Boosted Frame Transformation to Simulate Free‐Electron Laser Amplifier Physics}",
journal = {AIP Conference Proceedings},
volume = {1086},
number = {1},
pages = {346-350},
year = {2009},
month = {01},
abstract = "{Recently [1] it has been pointed out that numerical simulation of some systems containing charged particles with highly relativistic directed motion can by speeded up by orders of magnitude by choice of the proper Lorentz boosted frame. A particularly good example is that of short wavelength free‐electron lasers (FELs) in which a high energy (E0⩾250 MeV) electron beam interacts with a static magnetic undulator. In the optimal boost frame with Lorentz factor γF, the red‐shifted FEL radiation and blue shifted undulator have identical wavelengths and the number of required time‐steps (presuming the Courant condition applies) decreases by a factor of γF2 for fully electromagnetic simulation.We have adapted the WARP code [2] to apply this method to several FEL problems including coherent spontaneous emission (CSE) from pre‐bunched e‐beams, and strong exponential gain in a single pass amplifier configuration. We discuss our results and compare with those from the “standard” FEL simulation approach which adopts the eikonal approximation for propagation of the radiation field.}",
issn = {0094-243X},
doi = {10.1063/1.3080930},
url = {https://doi.org/10.1063/1.3080930},
}
2 changes: 1 addition & 1 deletion Docs/source/usage/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Particle Accelerator & Beam Physics

examples/gaussian_beam/README.rst
examples/beam_beam_collision/README.rst

examples/free_electron_laser/README.rst

High Energy Astrophysical Plasma Physics
----------------------------------------
Expand Down
1 change: 1 addition & 0 deletions Docs/source/usage/examples/free_electron_laser
2 changes: 1 addition & 1 deletion Docs/source/usage/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Overall simulation parameters
``warpx.self_fields_absolute_tolerance``).

* ``fft``: Poisson's equation is solved using an Integrated Green Function method (which requires FFT calculations).
See these references for more details :cite:t:`QiangPhysRevSTAB2006`, :cite:t:`QiangPhysRevSTAB2006err`.
See these references for more details :cite:t:`param-QiangPhysRevSTAB2006`, :cite:t:`param-QiangPhysRevSTAB2006err`.
EZoni marked this conversation as resolved.
Show resolved Hide resolved
It only works in 3D and it requires the compilation flag ``-DWarpX_FFT=ON``.
If mesh refinement is enabled, this solver only works on the coarsest level.
On the refined patches, the Poisson equation is solved with the multigrid solver.
Expand Down
1 change: 1 addition & 0 deletions Examples/Physics_applications/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

add_subdirectory(beam_beam_collision)
add_subdirectory(capacitive_discharge)
add_subdirectory(free_electron_laser)
add_subdirectory(laser_acceleration)
add_subdirectory(laser_ion)
add_subdirectory(plasma_acceleration)
Expand Down
12 changes: 12 additions & 0 deletions Examples/Physics_applications/free_electron_laser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Add tests (alphabetical order) ##############################################
#

add_warpx_test(
test_1d_fel # name
1 # dims
2 # nprocs
inputs_test_1d_fel # inputs
analysis_fel.py # analysis
diags/diag_boostedframe # output
OFF # dependency
)
30 changes: 30 additions & 0 deletions Examples/Physics_applications/free_electron_laser/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.. _examples-free-electron-laser:

Free-electron laser
===================

This example shows how to simulate the physics of a free-electron laser (FEL) using WarpX.
In this example, a relativistic electron beam is sent through an undulator (represented by an external,
oscillating magnetic field). The radiation emitted by the beam grows exponentially
as the beam travels through the undulator, due to the Free-Electron-Laser instability.

The parameters of the simulation are taken from section 5.1 of :cite:t:`ex-Fallahi2020`.
EZoni marked this conversation as resolved.
Show resolved Hide resolved

The simulation is performed in 1D, and uses the boosted-frame technique as described in
:cite:t:`ex-VayFELA2009` and :cite:t:`ex-VayFELB2009`, to reduce the computational cost of the simulation.
(The Lorentz frame of the simulation is moving at the average speed of the beam in the undulator.)
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved
Even though the simulation is run in this boosted frame, the results are reconstructed in the
laboratory frame, using WarpX's ``BackTransformedDiagnostic``.

The effect of space-charge is intentionally turned off in this example, as it may not be properly modeled in 1D.
This is achieved by initializing two species of opposite charge (electrons and positrons) to
represent the physical electron beam, as discussed in :cite:t:`ex-VayFELB2009`.

Run
---

For `MPI-parallel <https://www.mpi-forum.org>`__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system.
EZoni marked this conversation as resolved.
Show resolved Hide resolved
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved

.. literalinclude:: inputs_test_1d_fel
:language: ini
:caption: You can copy this file from ``Examples/Physics_applications/beam_beam_collision/inputs_test_1d_fel``.
137 changes: 137 additions & 0 deletions Examples/Physics_applications/free_electron_laser/analysis_fel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python

"""
This script tests that the FEL is correctly modelled in the simulation.

The physical parameters are the same as the ones from section 5.1
of https://arxiv.org/pdf/2009.13645

The simulation uses the boosted-frame technique as described in
https://www.osti.gov/servlets/purl/940581
In particular, the effect of space-charge is effectively turned off
by initializing an electron and positron beam on top of each other,
each having half the current of the physical beam.

The script checks that the radiation wavelength and gain length
are the expected ones. The check is performed both in the
lab-frame diagnostics and boosted-frame diagnostics.
"""

import os
import sys

import numpy as np
from openpmd_viewer import OpenPMDTimeSeries
from scipy.constants import c, e, m_e

sys.path.insert(1, "../../../../warpx/Regression/Checksum/")
import checksumAPI
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved

# Physical parameters of the test
gamma_bunch = 100.6
Bu = 0.5
lambda_u = 3e-2
k_u = 2 * np.pi / lambda_u
K = e * Bu / (m_e * c * k_u) # Undulator parameter
gamma_boost = (
gamma_bunch / (1 + K * K / 2) ** 0.5
) # Lorentz factor of the ponderomotive frame
beta_boost = (1 - 1.0 / gamma_boost**2) ** 0.5

# Analyze the diagnostics showing quantities in the boosted frame
ts = OpenPMDTimeSeries("diags/diag_boostedframe")


# Extract the growth of the peak electric field
def extract_peak_E_boost(iteration):
"""
Extract the peak electric field in a *boosted-frame* snapshot.
Also return the position of the peak in the lab frame.
"""
Ex, info = ts.get_field("E", "x", iteration=iteration)
By, info = ts.get_field("B", "y", iteration=iteration)
E_lab = gamma_boost * (Ex + c * beta_boost * By)
E_lab_peak = E_lab.max()
z_boost_peak = info.z[E_lab.argmax()]
t_boost_peak = ts.current_t
z_lab_peak = gamma_boost * (z_boost_peak + beta_boost * c * t_boost_peak)
return z_lab_peak, E_lab_peak


# Loop through all iterations
z_lab_peak, E_lab_peak = ts.iterate(extract_peak_E_boost)
log_P_peak = np.log(E_lab_peak**2)
# Since the radiation power is proportional to the square of the peak electric field,
# the log of the power is equal to the log of the square of the peak electric field,
# up to an additive constant.

# Pick the iterations between which the growth of the log of the power is linear
# (i.e. the growth of the power is exponential) and fit a line to extract the
# gain length.
i_start = 15
i_end = 23
# Perform linear fit
p = np.polyfit(z_lab_peak[i_start:i_end], log_P_peak[i_start:i_end], 1)
# Extract the gain length
Lg = 1 / p[0]
Lg_expected = 0.22 # Expected gain length from https://arxiv.org/pdf/2009.13645
print("Gain length: ", Lg)
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved
assert abs(Lg - Lg_expected) / Lg_expected < 0.1

# Check that the radiation wavelength is the expected one
iteration_check = 2000
Ex, info = ts.get_field("E", "x", iteration=iteration_check)
By, info = ts.get_field("B", "y", iteration=iteration_check)
E_lab = gamma_boost * (Ex + c * beta_boost * By)
Nz = len(info.z)
fft_E = abs(np.fft.fft(E_lab))
lambd = 1.0 / np.fft.fftfreq(Nz, d=info.dz)
lambda_radiation_boost = lambd[fft_E[:Nz].argmax()]
lambda_radiation_lab = lambda_radiation_boost / (2 * gamma_boost)
lambda_expected = lambda_u / (2 * gamma_boost**2)
assert abs(lambda_radiation_lab - lambda_expected) / lambda_expected < 0.01

# Analyze the diagnostics showing quantities in the lab frame
ts_lab = OpenPMDTimeSeries("diags/diag_labframe")


# Extract the growth of the peak electric field
def extract_peak_E_lab(iteration):
"""
Extract the position of the peak electric field
"""
Ex, info = ts_lab.get_field("E", "x", iteration=iteration)
return info.zmax, np.nanmax(Ex)


# Loop through all iterations
z_lab_peak, E_lab_peak = ts_lab.iterate(extract_peak_E_lab)
log_P_peak = np.log(E_lab_peak**2)

# Pick the iterations between which the growth of the log of the power is linear
# (i.e. the growth of the power is exponential) and fit a line to extract the
# gain length.
i_start = 6
i_end = 20
# Perform linear fit
p = np.polyfit(z_lab_peak[i_start:i_end], log_P_peak[i_start:i_end], 1)
# Extract the gain length
Lg = 1 / p[0]
Lg_expected = 0.22 # Expected gain length from https://arxiv.org/pdf/2009.13645
print("Gain length: ", Lg)
RemiLehe marked this conversation as resolved.
Show resolved Hide resolved
assert abs(Lg - Lg_expected) / Lg_expected < 0.15

# Check that the radiation wavelength is the expected one
iteration_check = 18
Ex, info = ts_lab.get_field("E", "x", iteration=iteration_check)
Nz = len(info.z)
fft_E = abs(np.fft.fft(E_lab))
lambd = 1.0 / np.fft.fftfreq(Nz, d=info.dz)
lambda_radiation_lab = lambd[fft_E[:Nz].argmax()]
lambda_expected = lambda_u / (2 * gamma_boost**2)
# assert abs(lambda_radiation_lab - lambda_expected) / lambda_expected < 0.01
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be updated after #5226


# this will be the name of the plot file
filename = sys.argv[1]
test_name = os.path.split(os.getcwd())[1]
checksumAPI.evaluate_checksum(test_name, filename, output_format="openpmd")
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
my_constants.gamma_bunch=100.6
my_constants.Bu = 0.5
my_constants.lambda_u = 3e-2
my_constants.k_u= 2*pi/lambda_u
my_constants.K = q_e*Bu/(m_e*clight*k_u) # Undulator parameter

warpx.gamma_boost = gamma_bunch/sqrt(1+K*K/2) # Lorentz factor of the ponderomotive frame
warpx.boost_direction = z
algo.maxwell_solver = yee
algo.particle_shape = 2
algo.particle_pusher = vay

# geometry
geometry.dims = 1
geometry.prob_hi = 0
geometry.prob_lo = -150e-6

amr.max_grid_size = 64
amr.max_level = 0
amr.n_cell = 1600

# boundary
boundary.field_hi = absorbing_silver_mueller
boundary.field_lo = absorbing_silver_mueller
boundary.particle_hi = absorbing
boundary.particle_lo = absorbing

# diagnostics
diagnostics.diags_names = diag_labframe diag_boostedframe

# Diagnostic that show quantities in the frame
# of the simulation (boosted-frame)
diag_boostedframe.diag_type = Full
diag_boostedframe.format = openpmd
diag_boostedframe.intervals = 100

# Diagnostic that show quantities
# reconstructed in the lab frame
diag_labframe.diag_type = BackTransformed
diag_labframe.num_snapshots_lab = 20
diag_labframe.dz_snapshots_lab = 0.1
diag_labframe.format = openpmd

# Run the simulation long enough for
# all backtransformed diagnostic to be complete
max_step = 2500

particles.species_names = electrons positrons
particles.rigid_injected_species= electrons positrons

electrons.charge = -q_e
electrons.injection_style = nuniformpercell
electrons.mass = m_e
electrons.momentum_distribution_type = constant
electrons.num_particles_per_cell_each_dim = 20
electrons.profile = constant
electrons.density = 2.7e19/2
electrons.ux = 0.0
electrons.uy = 0.0
electrons.uz = gamma_bunch
electrons.zmax = -50e-6
electrons.zmin = -150e-6
electrons.zinject_plane=0.0
electrons.rigid_advance=0

positrons.charge = q_e
positrons.injection_style = nuniformpercell
positrons.mass = m_e
positrons.momentum_distribution_type = constant
positrons.num_particles_per_cell_each_dim = 20
positrons.profile = constant
positrons.density = 2.7e19/2
positrons.ux = 0.0
positrons.uy = 0.0
positrons.uz = gamma_bunch
positrons.zmax = -50e-6
positrons.zmin = -150e-6
positrons.zinject_plane=0.0
positrons.rigid_advance=0

warpx.do_moving_window = 1
warpx.moving_window_dir = z
warpx.moving_window_v = sqrt(1-(1+K*K/2)/(gamma_bunch*gamma_bunch))

# Undulator field
particles.B_ext_particle_init_style = parse_B_ext_particle_function
particles.Bx_external_particle_function(x,y,z,t) = 0
particles.By_external_particle_function(x,y,z,t) = if( z>0, Bu*cos(k_u*z), 0 )
particles.Bz_external_particle_function(x,y,z,t) =0.0

warpx.cfl = 0.99
Loading