Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ca47387
2D: ForceFromSelfFields
ax3l Mar 19, 2025
b02e732
[Draft] GatherAndPush
ax3l Mar 19, 2025
273c812
3D Force & Push
ax3l Jul 15, 2025
ee8485d
2D Poisson Solve: Flattened Rho
ax3l Jul 15, 2025
5478a5b
2D Force & Push
ax3l Aug 28, 2025
3896b3e
Read current for 2D PIC, add input for benchmark example.
cemitch99 Sep 5, 2025
d0b5eba
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 6, 2025
76b80b5
2D phi field
ax3l Sep 15, 2025
2f1dfd0
Multi-Level rho_2d
ax3l Sep 15, 2025
36599f1
Python: `flatten_charge_to_2D`
ax3l Sep 15, 2025
111ab86
Add modified push constants and expanding beam example.
cemitch99 Sep 16, 2025
a3cc21c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 16, 2025
3790b71
Update Tests for Phi
ax3l Sep 17, 2025
19b3fce
HandleSpaceCharge: 2D Support
ax3l Sep 23, 2025
edc516e
Test: Deposit Again
ax3l Sep 23, 2025
5c80023
Merge remote-tracking branch 'mainline/development' into topic-space-…
ax3l Sep 24, 2025
334f95f
Draft: update candidate push.
cemitch99 Sep 25, 2025
86b87bd
Comment unused variable in GatherAndPush.cpp to fix CI.
cemitch99 Oct 1, 2025
26c985e
Remove num_guards_phi[2]=0 outside conditional in AmrCoreData.cpp.
cemitch99 Oct 3, 2025
d73ce28
Fix push constants.
cemitch99 Oct 14, 2025
cf34656
Add analysis scripts for tests.
cemitch99 Oct 14, 2025
0565103
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 14, 2025
f608b30
Update ncells in test_charge_deposition_2D.py
cemitch99 Oct 14, 2025
0ce3537
Add Python equivalent tests.
cemitch99 Oct 14, 2025
e96b9a2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 14, 2025
6cfefb7
Update documentation.
cemitch99 Oct 14, 2025
8da14c6
Update examples/CMakeLists.txt
cemitch99 Oct 16, 2025
ba7764e
Relax tolerance in analysis_expanding_fft_2D.py
cemitch99 Oct 16, 2025
376ebee
Relax tolerance in analysis_fodo_2d_sc.py
cemitch99 Oct 16, 2025
1926720
Relax tolerance for analysis_expanding_fft_2D.py
cemitch99 Oct 16, 2025
7db9d76
Relax tolerance in analysis_fodo_2d_sc.py
cemitch99 Oct 16, 2025
5e0bce6
Relax analysis_fodo_2d_sc.py tolerance again.
cemitch99 Oct 16, 2025
20ff802
Relax tolerance in analysis_fodo_2d_sc.py
cemitch99 Oct 17, 2025
bbbbb8d
Relax tolerance in analysis_fodo_2d_sc.py
cemitch99 Oct 17, 2025
a20b595
Merge development into 2D space charge PR (#9)
cemitch99 Oct 17, 2025
6859b3d
Same Grid, Same Mass
ax3l Oct 18, 2025
954251d
Same grid, same mass for FODO.
cemitch99 Oct 20, 2025
8d77c8e
Add plotting script.
cemitch99 Oct 20, 2025
9d2b3fc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 20, 2025
e8d4e5b
Add Weiqun's fix for duplicate values on shared nodes.
cemitch99 Oct 20, 2025
f7b6e14
Merge branch 'development' into topic-space-charge-particles-2D
cemitch99 Oct 24, 2025
0b4c71f
Doc: NAPAC25 Preprint Link Text (#10)
cemitch99 Oct 30, 2025
864b8fa
Build phi and force with flatten boxes in True_2D case
WeiqunZhang Nov 3, 2025
033dadb
Update PoissonSolve for the new layout of phi and rho
WeiqunZhang Nov 3, 2025
d95f987
Fix index type
WeiqunZhang Nov 3, 2025
739619d
Update examples/fodo_space_charge/run_fodo_2d_sc.py
cemitch99 Nov 6, 2025
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
2 changes: 1 addition & 1 deletion cmake/dependencies/ABLASTR.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ set(ImpactX_openpmd_src ""
set(ImpactX_ablastr_repo "https://github.com/BLAST-WarpX/warpx.git"
CACHE STRING
"Repository URI to pull and build ABLASTR from if(ImpactX_ablastr_internal)")
set(ImpactX_ablastr_branch "a76eecf1553f5d0e72ec948c9a67db4111a1544f"
set(ImpactX_ablastr_branch "5dc12e9de9fc1428d4ad4d6c132043abedc8c531"
CACHE STRING
"Repository branch for ImpactX_ablastr_repo if(ImpactX_ablastr_internal)")

Expand Down
2 changes: 1 addition & 1 deletion cmake/dependencies/pyAMReX.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ option(ImpactX_pyamrex_internal "Download & build pyAMReX" ON)
set(ImpactX_pyamrex_repo "https://github.com/AMReX-Codes/pyamrex.git"
CACHE STRING
"Repository URI to pull and build pyamrex from if(ImpactX_pyamrex_internal)")
set(ImpactX_pyamrex_branch "25.09"
set(ImpactX_pyamrex_branch "de24942c76ebc61ffaa184204c2491aebc357a07"
CACHE STRING
"Repository branch for ImpactX_pyamrex_repo if(ImpactX_pyamrex_internal)")

Expand Down
2 changes: 1 addition & 1 deletion docs/source/usage/how_to_run.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tracking of the beam envelope (6x6 covariance matrix) through linearized transpo
================== =============== =============== ====================
Mode Use Case Generality Space Charge Effects
================== =============== =============== ====================
Particle Tracking Full Dynamics Most general Supported (3D only)
Particle Tracking Full Dynamics Most general Supported (2D or 3D)
Envelope Tracking Rapid Scans Linearized Supported (2D or 3D)
Reference Tracking Early Design Reference orbit No
================== =============== =============== ====================
Expand Down
6 changes: 2 additions & 4 deletions docs/source/usage/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -873,8 +873,6 @@ See there ``nslice`` option on lattice elements for slicing.

* ``"2D"``: Space charge forces are computed in the plane ``(x,y)`` transverse to the reference particle velocity, assuming the beam is long and unbunched.

Currently, this model is supported only in envelope mode (when ``algo.track = "envelope"``).

* ``"3D"``: Space charge forces are computed in three dimensions, assuming the beam is bunched.

When running in envelope mode (when ``algo.track = "envelope"``), this model currently assumes that ``<xy> = <yt> = <tx> = 0``.
Expand Down Expand Up @@ -940,8 +938,8 @@ See there ``nslice`` option on lattice elements for slicing.
* ``algo.poisson_solver`` (``string``, optional, default: ``"fft"``)

The numerical solver to solve the Poisson equation when calculating space charge effects.
Currently, this is a 3D solver.
An additional `2D/2.5D solver <https://github.com/BLAST-ImpactX/impactx/issues/401>`__ will be added in the near future.
Currently, the multigrid solver supports only 3D space charge. The fft solver supports either 2D or 3D space charge.
An additional `2.5D solver <https://github.com/BLAST-ImpactX/impactx/issues/401>`__ will be added in the near future.

Options:

Expand Down
6 changes: 2 additions & 4 deletions docs/source/usage/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ Collective Effects & Overall Simulation Parameters

* ``"2D"``: Space charge forces are computed in the plane ``(x,y)`` transverse to the reference particle velocity, assuming the beam is long and unbunched.

Currently, this model is supported only in envelope mode (when ``algo.track = "envelope"``).

* ``"3D"``: Space charge forces are computed in three dimensions, assuming the beam is bunched.

When running in envelope mode (when ``algo.track = "envelope"``), this model currently assumes that ``<xy> = <yt> = <tx> = 0``.
Expand All @@ -86,8 +84,8 @@ Collective Effects & Overall Simulation Parameters
The numerical solver to solve the Poisson equation when calculating space charge effects.
Either ``"fft"`` (default) or ``"multigrid"``.

Currently, this is a 3D solver.
An additional `2D/2.5D solver <https://github.com/BLAST-ImpactX/impactx/issues/401>`__ will be added in the near future.
Currently, the multigrid solver supports only 3D space charge. The fft solver supports either 2D or 3D space charge.
An additional `2.5D solver <https://github.com/BLAST-ImpactX/impactx/issues/401>`__ will be added in the near future.

* ``fft``: Poisson's equation is solved using an Integrated Green Function method (which requires FFT calculations).
See these references for more details `Qiang et al. (2006) <https://doi.org/10.1103/PhysRevSTAB.9.044204>`__ (+ `Erratum <https://doi.org/10.1103/PhysRevSTAB.10.129901>`__).
Expand Down
32 changes: 32 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1675,3 +1675,35 @@ add_impactx_test(solenoid-softedge-solvable.py
examples/solenoid_softedge/analysis_solenoid_softedge_solvable.py
OFF # no plot script yet
)

# Expanding unbunched beam in free space with 2D space charge ######################
#
# with space charge
add_impactx_test(expanding-fft-2d
examples/expanding_beam/input_expanding_fft_2D.in
ON # ImpactX MPI-parallel
examples/expanding_beam/analysis_expanding_fft_2D.py
OFF # no plot script yet
)
add_impactx_test(expanding-fft-2d.py
examples/expanding_beam/run_expanding_fft_2D.py
OFF # ImpactX MPI-parallel
Copy link
Member Author

Choose a reason for hiding this comment

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

If we want 100% the same AMReX blocks ("grids output"):

Suggested change
OFF # ImpactX MPI-parallel
ON # ImpactX MPI-parallel

examples/expanding_beam/analysis_expanding_fft_2D.py
OFF # no plot script yet
)

# FODO cell with 2D space charge using particle tracking ######################
#
# with space charge
add_impactx_test(fodo-2d-sc
examples/fodo_space_charge/input_fodo_2d_sc.in
ON # ImpactX MPI-parallel
examples/fodo_space_charge/analysis_fodo_2d_sc.py
OFF # no plot script yet
)
add_impactx_test(fodo-2d-sc.py
examples/fodo_space_charge/run_fodo_2d_sc.py
OFF # ImpactX MPI-parallel
examples/fodo_space_charge/analysis_fodo_2d_sc.py
OFF # no plot script yet
)
60 changes: 58 additions & 2 deletions examples/expanding_beam/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

.. _examples-expanding:

Expanding Beam in Free Space
============================
Expanding Beam in Free Space with 3D Space Charge
==================================================

A coasting bunch expanding freely in free space under its own space charge.

Expand Down Expand Up @@ -69,3 +70,58 @@ We run the following script to analyze correctness:
.. literalinclude:: analysis_expanding.py
:language: python3
:caption: You can copy this file from ``examples/expanding/analysis_expanding.py``.



.. _examples-expanding-fft-2d:

Expanding Beam in Free Space with 2D Space Charge
==================================================

A long, coasting unbunched beam expanding freely in free space under its own 2D space charge.

We use a cold (zero emittance) 250 MeV electron bunch whose
initial distribution is a uniformly-populated cylinder of radius R0 = 1 mm.

In the laboratory frame, the beam expands to twice its original transverse size. This is tested using the second moments of the distribution.

In this test, the initial and final values of :math:`\sigma_x`, :math:`\sigma_y`, :math:`\sigma_t`, :math:`\epsilon_x`, :math:`\epsilon_y`, and :math:`\epsilon_t` must agree with nominal values.


Run
---

This example can be run **either** as:

* **Python** script: ``python3 run_expanding_fft_2D.py`` or
* ImpactX **executable** using an input file: ``impactx input_expanding_fft_2D.in``

For `MPI-parallel <https://www.mpi-forum.org>`__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system.

We also provide the same example with the multi-grid (MLMG) Poisson solver.

.. tab-set::

.. tab-item:: Python: Script (FFT)

.. literalinclude:: run_expanding_fft_2D.py
:language: python3
:caption: You can copy this file from ``examples/expanding/run_expanding_fft_2D.py``.

.. tab-item:: Executable: Input File (FFT)

.. literalinclude:: input_expanding_fft_2D.in
:language: ini
:caption: You can copy this file from ``examples/expanding/input_expanding_fft_2D.in``.


Analyze
-------

We run the following script to analyze correctness:

.. dropdown:: Script ``analysis_expanding_fft_2D.py``

.. literalinclude:: analysis_expanding_fft_2D.py
:language: python3
:caption: You can copy this file from ``examples/expanding/analysis_expanding_fft_2D.py``.
106 changes: 106 additions & 0 deletions examples/expanding_beam/analysis_expanding_fft_2D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
#
# Copyright 2022-2023 ImpactX contributors
# Authors: Axel Huebl, Chad Mitchell
# License: BSD-3-Clause-LBNL
#

import numpy as np
import openpmd_api as io
from scipy.stats import moment


def get_moments(beam):
"""Calculate standard deviations of beam position & momenta
and emittance values

Returns
-------
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t
"""
sigx = moment(beam["position_x"], moment=2) ** 0.5 # variance -> std dev.
sigpx = moment(beam["momentum_x"], moment=2) ** 0.5
sigy = moment(beam["position_y"], moment=2) ** 0.5
sigpy = moment(beam["momentum_y"], moment=2) ** 0.5
sigt = moment(beam["position_t"], moment=2) ** 0.5
sigpt = moment(beam["momentum_t"], moment=2) ** 0.5

epstrms = beam.cov(ddof=0)
emittance_x = (sigx**2 * sigpx**2 - epstrms["position_x"]["momentum_x"] ** 2) ** 0.5
emittance_y = (sigy**2 * sigpy**2 - epstrms["position_y"]["momentum_y"] ** 2) ** 0.5
emittance_t = (sigt**2 * sigpt**2 - epstrms["position_t"]["momentum_t"] ** 2) ** 0.5

return (sigx, sigy, sigt, emittance_x, emittance_y, emittance_t)


# initial/final beam
series = io.Series("diags/openPMD/monitor.h5", io.Access.read_only)
last_step = list(series.iterations)[-1]
initial = series.iterations[1].particles["beam"].to_df()
final = series.iterations[last_step].particles["beam"].to_df()

# compare number of particles
num_particles = 10000
assert num_particles == len(initial)
assert num_particles == len(final)

print("Initial Beam:")
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t = get_moments(initial)
print(f" sigx={sigx:e} sigy={sigy:e} sigt={sigt:e}")
print(
f" emittance_x={emittance_x:e} emittance_y={emittance_y:e} emittance_t={emittance_t:e}"
)

atol = 0.0 # ignored
rtol = 1.5 * num_particles**-0.5 # from random sampling of a smooth distribution
print(f" rtol={rtol} (ignored: atol~={atol})")

assert np.allclose(
[sigx, sigy, sigt, emittance_x, emittance_y, emittance_t],
[
5.0e-004,
5.0e-004,
1.0e-003,
0.0e-006,
0.0e-006,
0.0e-006,
],
rtol=rtol,
atol=atol,
)


print("")
print("Final Beam:")
sigx, sigy, sigt, emittance_x, emittance_y, emittance_t = get_moments(final)
print(f" sigx={sigx:e} sigy={sigy:e} sigt={sigt:e}")
print(
f" emittance_x={emittance_x:e} emittance_y={emittance_y:e} emittance_t={emittance_t:e}"
)

atol = 0.0 # ignored
rtol = 2.0 * num_particles**-0.5 # from random sampling of a smooth distribution
print(f" rtol={rtol} (ignored: atol~={atol})")

assert np.allclose(
[sigx, sigy, sigt],
[
1.0e-003,
1.0e-003,
1.0e-003,
],
rtol=rtol,
atol=atol,
)
atol = 1.0e-8
rtol = 0.0 # ignored
assert np.allclose(
[emittance_x, emittance_y, emittance_t],
[
0.0,
0.0,
0.0,
],
rtol=rtol,
atol=atol,
)
49 changes: 49 additions & 0 deletions examples/expanding_beam/input_expanding_fft_2D.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
###############################################################################
# Particle Beam(s)
###############################################################################
beam.npart = 10000 # outside tests, use 1e5 or more
beam.units = static
beam.kin_energy = 250.0
beam.current = 0.15
beam.particle = proton
beam.distribution = kvdist
beam.lambdaX = 5.0e-4
beam.lambdaY = beam.lambdaX
beam.lambdaT = 1.0e-3 #result should not depend on this value
beam.lambdaPx = 0.0
beam.lambdaPy = 0.0
beam.lambdaPt = 0.0


###############################################################################
# Beamline: lattice elements and segments
###############################################################################
lattice.elements = monitor drift1 monitor
lattice.nslice = 100

drift1.type = drift
drift1.ds = 10.612823669911099 #doubling-distance

monitor.type = beam_monitor
monitor.backend = h5


###############################################################################
# Algorithms
###############################################################################
algo.particle_shape = 2
#algo.track = "envelope"
algo.space_charge = 2D
algo.poisson_solver = "fft"

amr.n_cell = 32 32 1
amr.blocking_factor_x = 16
amr.blocking_factor_y = 16
amr.blocking_factor_z = 1

geometry.prob_relative = 1.1

###############################################################################
# Diagnostics
###############################################################################
diag.slice_step_diagnostics = true
68 changes: 68 additions & 0 deletions examples/expanding_beam/run_expanding_fft_2D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
#
# Copyright 2022-2023 ImpactX contributors
# Authors: Axel Huebl, Chad Mitchell
# License: BSD-3-Clause-LBNL
#
# -*- coding: utf-8 -*-

from impactx import ImpactX, distribution, elements

sim = ImpactX()

# set numerical parameters and IO control
sim.max_level = 0
sim.n_cell = [32, 32, 1]
sim.blocking_factor_x = [16]
sim.blocking_factor_y = [16]
sim.blocking_factor_z = [1]

sim.particle_shape = 2 # B-spline order
sim.space_charge = "2D"
sim.poisson_solver = "fft"
sim.dynamic_size = True
sim.prob_relative = [1.1]

# beam diagnostics
# sim.diagnostics = False # benchmarking
sim.slice_step_diagnostics = True

# domain decomposition & space charge mesh
sim.init_grids()

# load a 2 GeV electron beam with an initial
# unnormalized rms emittance of 2 nm
kin_energy_MeV = 250 # reference energy
beam_current_A = 0.15 # beam current
npart = 10000 # number of macro particles (outside tests, use 1e5 or more)

# reference particle
ref = sim.particle_container().ref_particle()
ref.set_charge_qe(1.0).set_mass_MeV(938.27208816).set_kin_energy_MeV(kin_energy_MeV)

# particle bunch
distr = distribution.KVdist(
lambdaX=5.0e-4,
lambdaY=5.0e-4,
lambdaT=1.0e-3,
lambdaPx=0.0,
lambdaPy=0.0,
lambdaPt=0.0,
)
sim.add_particles(beam_current_A, distr, npart)

# add beam diagnostics
monitor = elements.BeamMonitor("monitor", backend="h5")

# design the accelerator lattice
doubling_distance = 10.612823669911099

sim.lattice.extend(
[monitor, elements.Drift(name="d1", ds=doubling_distance, nslice=100), monitor]
)

# run simulation
sim.track_particles()

# clean shutdown
sim.finalize()
Loading
Loading