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 beam eigenemittances to reduced diagnostics. #702

Merged
merged 54 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3000016
Add eigenemittance calculation.
cemitch99 Sep 13, 2024
9584c62
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2024
176405a
Update CovarianceMatrixMath.H
cemitch99 Sep 13, 2024
b51293b
Update EmittanceInvariants.cpp
cemitch99 Sep 13, 2024
6b1e911
Update EmittanceInvariants.H
cemitch99 Sep 13, 2024
4b3fdd1
Update EmittanceInvariants.cpp
cemitch99 Sep 13, 2024
62a11c5
Treat special case of vanishing discriminant in Cardano's formula.
cemitch99 Sep 13, 2024
55ec468
Test from ReducedDiagnostics; update ej ordering.
cemitch99 Sep 13, 2024
ecde532
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2024
8f22292
Add diagnostic output.
cemitch99 Sep 13, 2024
2297ae5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2024
db26dde
Comment print statements used for debugging.
cemitch99 Sep 13, 2024
d4f0a14
Update ReducedBeamCharacteristics.cpp
cemitch99 Sep 13, 2024
939909d
Add benchmark example.
cemitch99 Sep 14, 2024
beccb40
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 14, 2024
d12e44c
Add documentation.
cemitch99 Sep 14, 2024
b3fe78a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 14, 2024
7bd8c5a
Update CMakeLists.txt
cemitch99 Sep 14, 2024
8d001eb
Update CMakeLists.txt
cemitch99 Sep 14, 2024
27e9882
Normalize momenta by mc (dynamical units) for eigenemittances.
cemitch99 Sep 14, 2024
f33bd59
Add alternative routine for cubic roots.
cemitch99 Sep 16, 2024
f22eaae
Update EmittanceInvariants.cpp
cemitch99 Sep 16, 2024
e4a9421
Update CovarianceMatrixMath.H
cemitch99 Sep 16, 2024
1a62b70
Temporarily comment pytest test_transformation.
cemitch99 Sep 16, 2024
8fdee82
Update test_transformation.py
cemitch99 Sep 16, 2024
9070654
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 16, 2024
8cfafc6
Delete commented debugging print statements.
cemitch99 Sep 16, 2024
ea114ff
Update EmittanceInvariants.H
cemitch99 Sep 16, 2024
101803c
Update EmittanceInvariants.cpp
cemitch99 Sep 16, 2024
2ee73f4
Apply suggestions from code review
cemitch99 Sep 19, 2024
96818f2
Update src/particles/diagnostics/EmittanceInvariants.cpp
cemitch99 Sep 19, 2024
e6bd898
Update EmittanceInvariants.cpp
cemitch99 Sep 19, 2024
06b63c2
Add emittance_1,2,3 print.
cemitch99 Sep 24, 2024
0e8ecf1
Finalize print of emittance_1,2,3.
cemitch99 Sep 24, 2024
71bb155
Add ParmParse for optional calculation.
cemitch99 Sep 24, 2024
49d5677
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 24, 2024
3684108
Update test_transformation.py
cemitch99 Sep 24, 2024
c883ee8
Apply suggestions from code review
cemitch99 Sep 25, 2024
0172e10
Update EmittanceInvariants.cpp
cemitch99 Sep 25, 2024
587a095
Update CovarianceMatrixMath.H
cemitch99 Sep 26, 2024
851b46a
Update src/particles/diagnostics/CovarianceMatrixMath.H
cemitch99 Sep 26, 2024
45f21b9
Update CovarianceMatrixMath.H
cemitch99 Sep 26, 2024
59f5b4d
Update CovarianceMatrixMath.H
cemitch99 Sep 26, 2024
53024fa
Use amrex:: math for complex functions.
cemitch99 Sep 26, 2024
534e40f
Avoid copying matrices
ax3l Oct 2, 2024
ed52ed7
Formatting: Whitespaces
ax3l Oct 2, 2024
b922bb4
More general odd-ness test
ax3l Oct 2, 2024
41bee2b
Add conditional emittance output columns.
cemitch99 Oct 5, 2024
e27c30e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2024
4f2d689
Add documentation for user-facing input control.
cemitch99 Oct 5, 2024
d8128ac
Update dataanalysis documentation of eigenemittances.
cemitch99 Oct 5, 2024
4b8f7b3
Update CovarianceMatrixMath.H
cemitch99 Oct 8, 2024
6d61630
Apply suggestions from code review
cemitch99 Oct 8, 2024
345f5b1
Update tests/python/test_transformation.py
ax3l Oct 10, 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
7 changes: 6 additions & 1 deletion docs/source/dataanalysis/dataanalysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ The code writes out the values in an ASCII file prefixed ``reduced_beam_characte
* ``sig_px``, ``sig_py``, ``sig_pt``
Standard deviation of the particle momentum deviations (energy difference for ``pt``) normalized by the magnitude of the reference particle momentum (unit: dimensionless)
* ``emittance_x``, ``emittance_y``, ``emittance_t``
Normalized rms beam emittance (unit: meter)
cemitch99 marked this conversation as resolved.
Show resolved Hide resolved
Unnormalized rms beam emittances (unit: meter)
* ``alpha_x``, ``alpha_y``, ``alpha_t``
Courant-Snyder (Twiss) alpha (unit: dimensionless). Transverse Twiss functions are calculated after removing correlations with particle energy.
* ``beta_x``, ``beta_y``, ``beta_t``
Expand All @@ -93,6 +93,11 @@ The code writes out the values in an ASCII file prefixed ``reduced_beam_characte
Horizontal and vertical dispersion (unit: meter)
* ``dispersion_px``, ``dispersion_py``
Derivative of horizontal and vertical dispersion (unit: dimensionless)
* ``emittance_xn``, ``emittance_yn``, ``emittance_tn``
Normalized rms beam emittances (unit: meter)
* ``emittance_1``, ``emittance_2``, ``emittance_3``
cemitch99 marked this conversation as resolved.
Show resolved Hide resolved
Normalized rms beam eigenemittances (aka mode emittances) (unit: meter)
These three diagnostics are written optionally if the flag eigenemittances = True.
* ``charge``
Total beam charge (unit: Coulomb)

Expand Down
2 changes: 2 additions & 0 deletions docs/source/usage/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Single Particle Dynamics
examples/achromatic_spectrometer/README.rst
examples/fodo_programmable/README.rst
examples/dogleg/README.rst
examples/coupled_optics/README.rst


Collective Effects
------------------
Expand Down
8 changes: 6 additions & 2 deletions docs/source/usage/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,8 @@ Diagnostics and output
This option is ignored for the openPMD output elements (remove them from the lattice to disable).

* ``diag.slice_step_diagnostics`` (``boolean``, optional, default: ``false``)
By default, diagnostics is performed at the beginning and end of the simulation.
Enabling this flag will write diagnostics every step and slice step
By default, diagnostics are computed and written at the beginning and end of the simulation.
Enabling this flag will write diagnostics at every step and slice step.

* ``diag.file_min_digits`` (``integer``, optional, default: ``6``)
The minimum number of digits used for the step number appended to the diagnostic file names.
Expand All @@ -694,6 +694,10 @@ Diagnostics and output
Diagnostics for particles lost in apertures, stored as ``diags/openPMD/particles_lost.*`` at the end of the simulation.
See the ``beam_monitor`` element for backend values.

* ``diag.eigenemittances`` (``boolean``, optional, default: ``false``)
If this flag is enabled, the 3 eigenemittances of the 6D beam distribution are computed and written as diagnostics.
This flag is disabled by default to reduce computational cost.


.. _running-cpp-parameters-diagnostics-insitu:

Expand Down
7 changes: 7 additions & 0 deletions docs/source/usage/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ Collective Effects & Overall Simulation Parameters
Diagnostics for particles lost in apertures.
See the ``BeamMonitor`` element for backend values.

.. py:property:: eigenemittances

Enable (``True``) or disable (``False``) output of eigenemittances at every slice step in elements (default: ``False``).

If this flag is enabled, the 3 eigenemittances of the 6D beam distribution are computed and written as diagnostics.
This flag is disabled by default to reduce computational cost.

.. py:method:: init_grids()

Initialize AMReX blocks/grids for domain decomposition & space charge mesh.
Expand Down
16 changes: 16 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -959,3 +959,19 @@ add_impactx_test(dogleg.py
examples/dogleg/analysis_dogleg.py
OFF # no plot script yet
)

# Coupled Optics #############################################################
#
# w/o space charge
add_impactx_test(coupled-optics
examples/coupled_optics/input_coupled_optics.in
ON # ImpactX MPI-parallel
examples/coupled_optics/analysis_coupled_optics.py
OFF # no plot script yet
)
add_impactx_test(coupled-optics.py
examples/coupled_optics/run_coupled_optics.py
OFF # ImpactX MPI-parallel
examples/coupled_optics/analysis_coupled_optics.py
OFF # no plot script yet
)
51 changes: 51 additions & 0 deletions examples/coupled_optics/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.. _examples-coupled-optics:

Coupled Optics
==============

This is a lattice illustrating fully coupled 6D transport. It is obtained from the example "dogleg" by adding a solenoid after the first bending dipole.
The solenoid is identical to that found in the example "solenoid".

Its primary purpose is to benchmark the calculation of the three beam eigenemittances (mode emittances).

In this test, the initial and final values of :math:`\lambda_x`, :math:`\lambda_y`, :math:`\lambda_t`, :math:`\epsilon_x`, :math:`\epsilon_y`, and :math:`\epsilon_t` must
agree with nominal values.

In addition, the initial and final values of :math:`emittance_1`, :math:`emittance_2`, :math:`emittance_3` must coincide.


Run
---

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

* **Python** script: ``python3 run_coupled_optics.py`` or
* ImpactX **executable** using an input file: ``impactx input_coupled_optics.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.

.. tab-set::

.. tab-item:: Python: Script

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

.. tab-item:: Executable: Input File

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


Analyze
-------

We run the following script to analyze correctness:

.. dropdown:: Script ``analysis_coupled_optics.py``

.. literalinclude:: analysis_coupled_optics.py
:language: python3
:caption: You can copy this file from ``examples/coupled_optics/analysis_coupled_optics.py``.
142 changes: 142 additions & 0 deletions examples/coupled_optics/analysis_coupled_optics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/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)


def get_eigenemittances(openpmd_beam):
"""Return eigenemittances from an openPMD particle species

Returns
-------
emittance_1, emittance_2, emittance_3
"""
emittance_1 = openpmd_beam.get_attribute("emittance_1")
emittance_2 = openpmd_beam.get_attribute("emittance_2")
emittance_3 = openpmd_beam.get_attribute("emittance_3")

return (emittance_1, emittance_2, emittance_3)


# initial/final beam
series = io.Series("diags/openPMD/monitor.h5", io.Access.read_only)
last_step = list(series.iterations)[-1]
initial_beam = series.iterations[1].particles["beam"]
initial = initial_beam.to_df()
final_beam = series.iterations[last_step].particles["beam"]
final = final_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 = 2.2 * 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],
[
6.4214719960819659e-005,
3.6603372435649773e-005,
1.9955175623579313e-004,
1.0198263116327677e-010,
1.0308359092878036e-010,
4.0035161705244885e-010,
],
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.2e12 * 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],
[
1.922660e-03,
2.166654e-05,
1.101353e-04,
8.561046e-09,
1.020439e-10,
8.569865e-09,
],
rtol=rtol,
atol=atol,
)

print("")
print("Initial eigenemittances:")
emittance_1i, emittance_2i, emittance_3i = get_eigenemittances(initial_beam)
print(
f" emittance_1={emittance_1i:e} emittance_2={emittance_2i:e} emittance_3={emittance_3i:e}"
)

print("")
print("Final eigenemittances:")
emittance_1f, emittance_2f, emittance_3f = get_eigenemittances(final_beam)
print(
f" emittance_1={emittance_1f:e} emittance_2={emittance_2f:e} emittance_3={emittance_3f:e}"
)

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

assert np.allclose(
[emittance_1f, emittance_2f, emittance_3f],
[
emittance_1i,
emittance_2i,
emittance_3i,
],
rtol=rtol,
atol=atol,
)
72 changes: 72 additions & 0 deletions examples/coupled_optics/input_coupled_optics.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
###############################################################################
# Particle Beam(s)
###############################################################################
beam.npart = 10000
beam.units = static
beam.kin_energy = 5.0e3
beam.charge = 1.0e-9
beam.particle = electron
beam.distribution = waterbag
beam.lambdaX = 2.2951017632e-5
beam.lambdaY = 1.3084093142e-5
beam.lambdaT = 5.5555553e-8
beam.lambdaPx = 1.598353425e-6
beam.lambdaPy = 2.803697378e-6
beam.lambdaPt = 2.000000000e-6
beam.muxpx = 0.933345606203060
beam.muypy = 0.933345606203060
beam.mutpt = 0.999999961419755


###############################################################################
# Beamline: lattice elements and segments
###############################################################################
lattice.elements = monitor sbend1 dipedge1 sol drift1 dipedge2 sbend2 drift2 monitor
lattice.nslice = 25

sbend1.type = sbend
sbend1.ds = 0.500194828041958 # projected length 0.5 m, angle 2.77 deg
sbend1.rc = -10.3462283686195526

drift1.type = drift
drift1.ds = 5.0058489435 # projected length 5 m

sbend2.type = sbend
sbend2.ds = 0.500194828041958 # projected length 0.5 m, angle 2.77 deg
sbend2.rc = 10.3462283686195526

drift2.type = drift
drift2.ds = 0.5

dipedge1.type = dipedge # dipole edge focusing
dipedge1.psi = -0.048345620280243
dipedge1.rc = -10.3462283686195526
dipedge1.g = 0.0
dipedge1.K2 = 0.0

dipedge2.type = dipedge
dipedge2.psi = 0.048345620280243
dipedge2.rc = 10.3462283686195526
dipedge2.g = 0.0
dipedge2.K2 = 0.0

sol.type = solenoid
sol.ds = 3.820395
sol.ks = 0.8223219329893234

monitor.type = beam_monitor
monitor.backend = h5


###############################################################################
# Algorithms
###############################################################################
algo.particle_shape = 2
algo.space_charge = false


###############################################################################
# Diagnostics
###############################################################################
diag.slice_step_diagnostics = true
diag.eigenemittances = true
Loading
Loading