From e784039bd1c0be48d84b63d7d8bc6df55f63b614 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 4 Sep 2024 22:25:35 -0700 Subject: [PATCH 01/30] Doc: Intro Update (#692) * Doc: Intro Update Updated the intro paragraphs with ideas we discussed in the weekly meeting. * Add applications Co-authored-by: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> * Improve wording (Dev Meeting) --------- Co-authored-by: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> --- docs/source/index.rst | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 6ab1b75e0..967641494 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,23 +3,34 @@ ImpactX ------- -ImpactX is an s-based beam dynamics code including space charge effects. +ImpactX enables high-performance modeling of beam dynamics in particle accelerators with collective effects. + This is the next generation of the `IMPACT-Z `__ code. +ImpactX runs on modern GPUs or CPUs alike, provides user-friendly interfaces suitable for AI/ML workflows, has many :ref:`benchmarks ` to ensure its correctness, and an extensive documentation. + +As a beam dynamics code, ImpactX uses the reference trajectory path length :math:`s` as the independent variable of motion to achieve large speedups. +The code includes the effects of externally applied fields from magnets and accelerating cavities as well as the effect of self-fields (space charge fields, CSR, wakefields, ...). +All particle tracking models are symplectic, and space charge is included by solving the Poisson equation in the beam rest frame. +The code may be used to model the dynamics of beams in both linear and ring accelerators. +See our :ref:`theory chapter ` for details on our models, assumptions and concepts. + +ImpactX is part of the `Beam, Plasma & Accelerator Simulation Toolkit (BLAST) `__. +Please see this page for more information to select the code most appropriate for your application. -Please contact us with any questions on it or if you like to contribute to its development. .. _contact: Contact us ^^^^^^^^^^ -If you are starting using ImpactX, or if you have a user question, please pop in our `discussions page `__ and get in touch with the community. +We organize support as an open community, helping each other. -The `ImpactX GitHub repo `__ is the main communication platform. +The `ImpactX GitHub repository `__ is our open communication platform. Have a look at the action icons on the top right of the web page: feel free to watch the repo if you want to receive updates, or to star the repo to support the project. For bug reports or to request new features, you can also open a new `issue `__. -We also have a `discussion page `__ on which you can find already answered questions, add new questions, get help with installation procedures, discuss ideas or share comments. +If you are starting to use ImpactX and have questions not answered by this documentation, have a look on our `discussion page `__. +There, you can find already answered questions, add your own, get help with installation procedures, help others and share ideas. .. raw:: html From b43b397fdf205fb815394477a157e144e3183d0d Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Sun, 8 Sep 2024 11:38:05 -0700 Subject: [PATCH 02/30] Remove additional string for Twiss input (#698) Improve maintainability by removing additional string "from_cs" that allows for beam initialization from Twiss parameters. --- docs/source/usage/parameters.rst | 18 +++++++++--------- examples/fodo/input_fodo_twiss.in | 2 +- src/initialization/InitDistribution.cpp | 7 +------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/source/usage/parameters.rst b/docs/source/usage/parameters.rst index fb1dfb330..d21b1f2ab 100644 --- a/docs/source/usage/parameters.rst +++ b/docs/source/usage/parameters.rst @@ -43,8 +43,8 @@ Initial Beam Distributions * ``beam.muypy`` (``float``, dimensionless, default: ``0``) correlation Y-Py * ``beam.mutpt`` (``float``, dimensionless, default: ``0``) correlation T-Pt - * Courant-Snyder / Twiss parameters. - To enable input via CS / Twiss parameters, add the suffix ``_from_cs`` or ``from_twiss`` to the name of the distribution. + * Courant-Snyder (Twiss) parameters. + To enable input via Courant-Snyder (Twiss) parameters, add the suffix ``from_twiss`` to the name of the distribution. Use the following parameters to characterize it: * ``beam.alphaX`` (``float``, dimensionless, default: ``0``) CS / Twiss :math:`\alpha` for X @@ -59,21 +59,21 @@ Initial Beam Distributions The following distributions are available: - * ``waterbag`` or ``waterbag_from_cs``/``waterbag_from_twiss`` for initial Waterbag distribution. + * ``waterbag`` or ``waterbag_from_twiss`` for initial Waterbag distribution. - * ``kurth6d`` or ``kurth6d_from_cs``/``kurth6d_from_twiss`` for initial 6D Kurth distribution. + * ``kurth6d`` or ``kurth6d_from_twiss`` for initial 6D Kurth distribution. - * ``gaussian`` or ``gaussian_from_cs``/``gaussian_from_twiss`` for initial 6D Gaussian (normal) distribution. + * ``gaussian`` or ``gaussian_from_twiss`` for initial 6D Gaussian (normal) distribution. - * ``kvdist`` or ``kvdist_from_cs``/``kvdist_from_twiss`` for initial K-V distribution in the transverse plane. + * ``kvdist`` or ``kvdist_from_twiss`` for initial K-V distribution in the transverse plane. The distribution is uniform in t and Gaussian in pt. - * ``kurth4d`` or ``kurth4d_from_cs``/``kurth4d_from_twiss`` for initial 4D Kurth distribution in the transverse plane. + * ``kurth4d`` or ``kurth4d_from_twiss`` for initial 4D Kurth distribution in the transverse plane. The distribution is uniform in t and Gaussian in pt. - * ``semigaussian`` or ``semigaussian_from_cs``/``semigaussian_from_twiss`` for initial Semi-Gaussian distribution. The distribution is uniform within a cylinder in (x,y,z) and Gaussian in momenta (px,py,pt). + * ``semigaussian`` or ``semigaussian_from_twiss`` for initial Semi-Gaussian distribution. The distribution is uniform within a cylinder in (x,y,z) and Gaussian in momenta (px,py,pt). - * ``triangle`` or ``triangle_from_cs``/``triangle_from_twiss`` a triangle distribution for laser-plasma acceleration related applications. + * ``triangle`` or ``triangle_from_twiss`` a triangle distribution for laser-plasma acceleration related applications. A ramped, triangular current profile with a Gaussian energy spread (possibly correlated). The transverse distribution is a 4D waterbag. diff --git a/examples/fodo/input_fodo_twiss.in b/examples/fodo/input_fodo_twiss.in index a94f19e4a..5e8fcd670 100644 --- a/examples/fodo/input_fodo_twiss.in +++ b/examples/fodo/input_fodo_twiss.in @@ -6,7 +6,7 @@ beam.units = static beam.kin_energy = 2.0e3 beam.charge = 1.0e-9 beam.particle = electron -beam.distribution = waterbag_from_cs +beam.distribution = waterbag_from_twiss beam.alphaX = -1.5905003499999992 beam.alphaY = 1.5905003499999992 beam.alphaT = 0.0 diff --git a/src/initialization/InitDistribution.cpp b/src/initialization/InitDistribution.cpp index 50f7b852e..61cdd77aa 100644 --- a/src/initialization/InitDistribution.cpp +++ b/src/initialization/InitDistribution.cpp @@ -266,19 +266,14 @@ namespace impactx pp_dist.get("distribution", distribution_type); std::string base_dist_type = distribution_type; - // Position of the underscore for splitting off the suffix in case the distribution name either ends in "_from_twiss" or "_from_cs" + // Position of the underscore for splitting off the suffix in case the distribution name either ends in "_from_twiss" std::size_t str_pos_from_twiss = distribution_type.rfind("_from_twiss"); - std::size_t str_pos_from_cs = distribution_type.rfind("_from_cs"); bool initialize_from_twiss = false; if (str_pos_from_twiss != std::string::npos) { // "_from_twiss" is found // Calculate suffix and base type, consider length of "_from_twiss" = 12 base_dist_type = distribution_type.substr(0, str_pos_from_twiss); initialize_from_twiss = true; - } else if (str_pos_from_cs != std::string::npos) { // "_from_cs" is found - // Calculate suffix and base type, consider length of "_from_cs" = 8 - base_dist_type = distribution_type.substr(0, str_pos_from_cs); - initialize_from_twiss = true; } /* After separating a potential suffix from its base type, check if the base distribution type is in the set of From d470732e1764a33daf444479a6514f243c1a3a5b Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Sun, 8 Sep 2024 11:41:03 -0700 Subject: [PATCH 03/30] Add helper function for Python Twiss input (#697) * Add helper function for Python Twiss input - added helper function `twiss` - added FODO example run script - added CTest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update examples and add Python Twiss input docs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix failing CI test The CI complained that our import of the `twiss` helper function is unused. But we need it to be available to use it in our examples. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/usage/python.rst | 12 +++ examples/CMakeLists.txt | 56 +++++++++-- examples/distgen/README.rst | 95 ++++++------------- examples/distgen/input_gaussian.in | 51 ---------- ...nput_kvdist.in => input_gaussian_twiss.in} | 20 ++-- examples/distgen/run_gaussian_twiss.py | 73 ++++++++++++++ examples/distgen/run_kurth4d.py | 64 +++++++++++++ examples/distgen/run_kvdist_twiss.py | 73 ++++++++++++++ examples/distgen/run_semigaussian.py | 71 ++++++++++++++ examples/fodo/run_fodo_twiss.py | 73 ++++++++++++++ src/python/impactx/__init__.py | 1 + .../impactx/distribution_input_helpers.py | 66 +++++++++++++ 12 files changed, 517 insertions(+), 138 deletions(-) delete mode 100644 examples/distgen/input_gaussian.in rename examples/distgen/{input_kvdist.in => input_gaussian_twiss.in} (78%) create mode 100755 examples/distgen/run_gaussian_twiss.py create mode 100755 examples/distgen/run_kurth4d.py create mode 100755 examples/distgen/run_kvdist_twiss.py create mode 100755 examples/distgen/run_semigaussian.py create mode 100755 examples/fodo/run_fodo_twiss.py create mode 100644 src/python/impactx/distribution_input_helpers.py diff --git a/docs/source/usage/python.rst b/docs/source/usage/python.rst index 09a327a53..52acfe210 100644 --- a/docs/source/usage/python.rst +++ b/docs/source/usage/python.rst @@ -401,6 +401,18 @@ Initial Beam Distributions This module provides particle beam distributions that can be used to initialize particle beams in an :py:class:`impactx.ParticleContainer`. +.. note:: + + For additional information, consult the documentation on :ref:`theory-collective-beam-distribution-input`. + For **all** except the ``thermal`` distribution we allow input in two forms: + + 1. Phase space ellipse axis intersections (ImpactX native) + 2. Courant-Snyder (Twiss) parameters + +For the input from Twiss parameters in Python, please use the helper function ``twiss``: + +.. autofunction:: impactx.twiss + .. py:class:: impactx.distribution.Gaussian(lambdax, lambday, lambdat, lambdapx, lambdapy, lambdapt, muxpx=0.0, muypy=0.0, mutpt=0.0) A 6D Gaussian distribution. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cc00eb47c..d33221240 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -179,6 +179,15 @@ add_impactx_test(FODO.py examples/fodo/plot_fodo.py ) +# Python: FODO Cell w/ Twiss input ############################################ +# +add_impactx_test(FODO.Twiss.py + examples/fodo/run_fodo_twiss.py + OFF # ImpactX MPI-parallel + examples/fodo/analysis_fodo.py + examples/fodo/plot_fodo.py +) + # Python: FODO Cell w/ programmed element for the Drifts ###################### # add_impactx_test(FODO.programmable.py @@ -292,33 +301,42 @@ add_impactx_test(cfchannel.py OFF # no plot script yet ) -# 6D Gaussian Distribution Test ############################################### +# 6D Gaussian Distribution from Twiss Test #################################### # -add_impactx_test(gaussian - examples/distgen/input_gaussian.in +add_impactx_test(gaussian_twiss + examples/distgen/input_gaussian_twiss.in OFF # ImpactX MPI-parallel examples/distgen/analysis_gaussian.py OFF # no plot script yet ) -# K-V Distribution Test ####################################################### +# Python: 6D Gaussian Distribution w/ Twiss input ############################ # -add_impactx_test(kvdist - examples/distgen/input_kvdist.in - OFF # ImpactX MPI-parallel - examples/distgen/analysis_kvdist.py - OFF # no plot script yet +add_impactx_test(gaussian_twiss.py + examples/distgen/run_gaussian_twiss.py + OFF # ImpactX MPI-parallel + examples/distgen/analysis_gaussian.py + OFF # no plot script yet ) # K-V Distribution from Twiss Test ############################################ # -add_impactx_test(kvdist_from_twiss +add_impactx_test(kvdist_twiss examples/distgen/input_kvdist_twiss.in OFF # ImpactX MPI-parallel examples/distgen/analysis_kvdist.py OFF # no plot script yet ) +# Python: 6D Gaussian Distribution w/ Twiss input ############################ +# +add_impactx_test(kvdist_twiss.py + examples/distgen/run_kvdist_twiss.py + OFF # ImpactX MPI-parallel + examples/distgen/analysis_kvdist.py + OFF # no plot script yet +) + # FODO Cell with RF ########################################################### # add_impactx_test(FODO_RF @@ -346,6 +364,15 @@ add_impactx_test(kurth4d OFF # no plot script yet ) +# Python: 4D Kurth Distribution Test ########################################## +# +add_impactx_test(kurth4d.py + examples/distgen/run_kurth4d.py + OFF # ImpactX MPI-parallel + examples/distgen/analysis_kurth4d.py + OFF # no plot script yet +) + # Semi-Gaussian Distribution Test ############################################# # add_impactx_test(semigaussian @@ -355,6 +382,15 @@ add_impactx_test(semigaussian OFF # no plot script yet ) +# Python: 6D Gaussian Distribution w/ Twiss input ############################ +# +add_impactx_test(semigaussian.py + examples/distgen/run_semigaussian.py + OFF # ImpactX MPI-parallel + examples/distgen/analysis_semigaussian.py + OFF # no plot script yet +) + # Chain of Multipoles Test #################################################### # add_impactx_test(multipole diff --git a/examples/distgen/README.rst b/examples/distgen/README.rst index 77c3ce0c9..cb778348d 100644 --- a/examples/distgen/README.rst +++ b/examples/distgen/README.rst @@ -4,6 +4,17 @@ Generation of beam distributions The following examples are tests of beam initialization for distributions of various types. +.. note:: + + Please consult :ref:`theory-collective-beam-distribution-input` for our conventions on beam distribution input. + We currently offer two different methods: + + 1. Phase space ellipse axis intersections (ImpactX native) + 2. Courant-Snyder (Twiss) parameters + + For illustration purposes, some of the following examples use Twiss initialization while some use the ImpactX native initialization. + Both are equivalent. + In each example, we use a 2 GeV electron beam with initial unnormalized rms emittance of 2 nm. The matched Twiss parameters are the same as those used in the FODO example: @@ -20,10 +31,10 @@ In this test, the initial and final values of :math:`\lambda_x`, :math:`\lambda_ .. _examples-distgen-gaussian: -A 6d Gaussian distribution -============================= +A 6d Gaussian distribution from Twiss functions +=============================================== -A Gaussian distribution in all 6 phase space variables. +A Gaussian distribution in all 6 phase space variables, initialized using Courant-Snyder (Twiss) functions. 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. @@ -33,8 +44,8 @@ Run This example can be run **either** as: -* **Python** script: ``python3 run_gaussian.py`` or -* ImpactX **executable** using an input file: ``impactx input_gaussian.in`` +* **Python** script: ``python3 run_gaussian_twiss.py`` or +* ImpactX **executable** using an input file: ``impactx input_gaussian_twiss.in`` For `MPI-parallel `__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system. @@ -42,15 +53,15 @@ For `MPI-parallel `__ runs, prefix these lines with ` .. tab-item:: Python: Script - .. literalinclude:: run_gaussian.py + .. literalinclude:: run_gaussian_twiss.py :language: python3 - :caption: You can copy this file from ``examples/distgen/run_gaussian.py``. + :caption: You can copy this file from ``examples/distgen/run_gaussian_twiss.py``. .. tab-item:: Executable: Input File - .. literalinclude:: input_gaussian.in + .. literalinclude:: input_gaussian_twiss.in :language: ini - :caption: You can copy this file from ``examples/distgen/input_gaussian.in``. + :caption: You can copy this file from ``examples/distgen/input_gaussian_twiss.in``. Analyze @@ -65,62 +76,12 @@ We run the following script to analyze correctness: :caption: You can copy this file from ``examples/distgen/analysis_gaussian.py``. - -.. _examples-distgen-kvdist: - -A Kapchinskij-Vladimirskij (K-V) distribution -=============================================== - -A 4D K-V distribution in the transverse phase space variables ( + a longitudinally uniform distribution in :math:`t` + a Gaussian distribution in :math:`p_t` ). - -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. - - -Run ---- - -This example can be run **either** as: - -* **Python** script: ``python3 run_kvdist.py`` or -* ImpactX **executable** using an input file: ``impactx input_kvdist.in`` - -For `MPI-parallel `__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system. - -.. tab-set:: - - .. tab-item:: Python: Script - - .. literalinclude:: run_kvdist.py - :language: python3 - :caption: You can copy this file from ``examples/distgen/run_kvdist.py``. - - .. tab-item:: Executable: Input File - - .. literalinclude:: input_kvdist.in - :language: ini - :caption: You can copy this file from ``examples/distgen/input_kvdist.in``. - - -Analyze -------- - -We run the following script to analyze correctness: - -.. dropdown:: Script ``analysis_kvdist.py`` - - .. literalinclude:: analysis_kvdist.py - :language: python3 - :caption: You can copy this file from ``examples/distgen/analysis_kvdist.py``. - - - - .. _examples-distgen-kvdist_from_twiss: A K-V distribution initialized from Twiss functions ====================================================== -Identical to the previous example (examples-kvdist), but initialized using Courant-Snyder Twiss functions. +This example is initialized using Courant-Snyder (Twiss) functions. 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. @@ -130,8 +91,8 @@ Run This example can be run **either** as: -* **Python** script: ``python3 run_kvdist_from_twiss.py`` or -* ImpactX **executable** using an input file: ``impactx input_kvdist_from_twiss.in`` +* **Python** script: ``python3 run_kvdist_twiss.py`` or +* ImpactX **executable** using an input file: ``impactx input_kvdist_twiss.in`` For `MPI-parallel `__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system. @@ -141,13 +102,13 @@ For `MPI-parallel `__ runs, prefix these lines with ` .. literalinclude:: run_kvdist_from_twiss.py :language: python3 - :caption: You can copy this file from ``examples/distgen/run_kvdist_from_twiss.py``. + :caption: You can copy this file from ``examples/distgen/run_kvdist_twiss.py``. .. tab-item:: Executable: Input File .. literalinclude:: input_kvdist_from_twiss.in :language: ini - :caption: You can copy this file from ``examples/distgen/input_kvdist_from_twiss.in``. + :caption: You can copy this file from ``examples/distgen/input_kvdist_twiss.in``. Analyze @@ -155,11 +116,11 @@ Analyze We run the following script to analyze correctness: -.. dropdown:: Script ``analysis_kvdist_from_twiss.py`` +.. dropdown:: Script ``analysis_kvdist.py`` - .. literalinclude:: analysis_kvdist_from_twiss.py + .. literalinclude:: analysis_kvdist.py :language: python3 - :caption: You can copy this file from ``examples/distgen/analysis_kvdist_from_twiss.py``. + :caption: You can copy this file from ``examples/distgen/analysis_kvdist.py``. diff --git a/examples/distgen/input_gaussian.in b/examples/distgen/input_gaussian.in deleted file mode 100644 index 99ccac82d..000000000 --- a/examples/distgen/input_gaussian.in +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################### -# Particle Beam(s) -############################################################################### -beam.npart = 10000 -beam.units = static -beam.kin_energy = 2.0e3 -beam.charge = 1.0e-9 -beam.particle = electron -beam.distribution = gaussian -beam.lambdaX = 3.9984884770e-5 -beam.lambdaY = 3.9984884770e-5 -beam.lambdaT = 1.0e-3 -beam.lambdaPx = 2.6623538760e-5 -beam.lambdaPy = 2.6623538760e-5 -beam.lambdaPt = 2.0e-3 -beam.muxpx = -0.846574929020762 -beam.muypy = 0.846574929020762 -beam.mutpt = 0.0 - - -############################################################################### -# Beamline: lattice elements and segments -############################################################################### -lattice.elements = monitor drift1 quad1 drift2 quad2 drift3 monitor - -monitor.type = beam_monitor -monitor.backend = h5 - -drift1.type = drift -drift1.ds = 0.25 - -quad1.type = quad -quad1.ds = 1.0 -quad1.k = 1.0 - -drift2.type = drift -drift2.ds = 0.5 - -quad2.type = quad -quad2.ds = 1.0 -quad2.k = -1.0 - -drift3.type = drift -drift3.ds = 0.25 - - -############################################################################### -# Algorithms -############################################################################### -algo.particle_shape = 2 -algo.space_charge = false diff --git a/examples/distgen/input_kvdist.in b/examples/distgen/input_gaussian_twiss.in similarity index 78% rename from examples/distgen/input_kvdist.in rename to examples/distgen/input_gaussian_twiss.in index e1e551f4e..71884d95d 100644 --- a/examples/distgen/input_kvdist.in +++ b/examples/distgen/input_gaussian_twiss.in @@ -6,16 +6,16 @@ beam.units = static beam.kin_energy = 2.0e3 beam.charge = 1.0e-9 beam.particle = electron -beam.distribution = kvdist -beam.lambdaX = 3.9984884770e-5 -beam.lambdaY = 3.9984884770e-5 -beam.lambdaT = 1.0e-3 -beam.lambdaPx = 2.6623538760e-5 -beam.lambdaPy = 2.6623538760e-5 -beam.lambdaPt = 2.0e-3 -beam.muxpx = -0.846574929020762 -beam.muypy = 0.846574929020762 -beam.mutpt = 0.0 +beam.distribution = gaussian_from_twiss +beam.alphaX = -1.5905003499999992 +beam.alphaY = 1.5905003499999992 +beam.alphaT = 0.0 +beam.betaX = 2.8216194100262637 +beam.betaY = 2.8216194100262637 +beam.betaT = 0.5 +beam.emittX = 2e-09 +beam.emittY = 2e-09 +beam.emittT = 2e-06 ############################################################################### diff --git a/examples/distgen/run_gaussian_twiss.py b/examples/distgen/run_gaussian_twiss.py new file mode 100755 index 000000000..e317bc19d --- /dev/null +++ b/examples/distgen/run_gaussian_twiss.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2024 ImpactX contributors +# Authors: Marco Garten, Axel Huebl, CHad Mitchell +# License: BSD-3-Clause-LBNL +# +# -*- coding: utf-8 -*- + +from impactx import ImpactX, distribution, elements, twiss + +sim = ImpactX() + +# set numerical parameters and IO control +sim.particle_shape = 2 # B-spline order +sim.space_charge = False +# 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 = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.Gaussian( + **twiss( + beta_x=2.8216194100262637, + beta_y=2.8216194100262637, + beta_t=0.5, + emitt_x=2e-09, + emitt_y=2e-09, + emitt_t=2e-06, + alpha_x=-1.5905003499999992, + alpha_y=1.5905003499999992, + alpha_t=0.0, + ) +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice) +ns = 25 # number of slices per ds in the element +fodo = [ + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=1.0, nslice=ns), + monitor, + elements.Drift(ds=0.5, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=-1.0, nslice=ns), + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, +] +# assign a fodo segment +sim.lattice.extend(fodo) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/examples/distgen/run_kurth4d.py b/examples/distgen/run_kurth4d.py new file mode 100755 index 000000000..84bdc4d54 --- /dev/null +++ b/examples/distgen/run_kurth4d.py @@ -0,0 +1,64 @@ +#!/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.particle_shape = 2 # B-spline order +sim.space_charge = False +# sim.diagnostics = False # benchmarking +sim.slice_step_diagnostics = True + +# domain decomposition & space charge mesh +sim.init_grids() + +# load a 2 GeV proton beam with an initial +# normalized transverse rms emittance of 1 um +kin_energy_MeV = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# 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.Kurth4D( + lambdaX=1.0e-3, + lambdaY=1.0e-3, + lambdaT=1.0e-3, + lambdaPx=1.0e-3, + lambdaPy=1.0e-3, + lambdaPt=2.0e-3, + muxpx=-0.0, + muypy=0.0, + mutpt=0.0, +) + +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice) +constf = [ + monitor, + elements.ConstF(ds=2.0, kx=1.0, ky=1.0, kt=1.0e-4), + monitor, +] + +# assign a constf segment +sim.lattice.extend(constf) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/examples/distgen/run_kvdist_twiss.py b/examples/distgen/run_kvdist_twiss.py new file mode 100755 index 000000000..beaaec0de --- /dev/null +++ b/examples/distgen/run_kvdist_twiss.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2024 ImpactX contributors +# Authors: Marco Garten, Axel Huebl, CHad Mitchell +# License: BSD-3-Clause-LBNL +# +# -*- coding: utf-8 -*- + +from impactx import ImpactX, distribution, elements, twiss + +sim = ImpactX() + +# set numerical parameters and IO control +sim.particle_shape = 2 # B-spline order +sim.space_charge = False +# 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 = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.KVdist( + **twiss( + beta_x=2.8216194100262637, + beta_y=2.8216194100262637, + beta_t=0.5, + emitt_x=2e-09, + emitt_y=2e-09, + emitt_t=2e-06, + alpha_x=-1.5905003499999992, + alpha_y=1.5905003499999992, + alpha_t=0.0, + ) +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice) +ns = 25 # number of slices per ds in the element +fodo = [ + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=1.0, nslice=ns), + monitor, + elements.Drift(ds=0.5, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=-1.0, nslice=ns), + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, +] +# assign a fodo segment +sim.lattice.extend(fodo) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/examples/distgen/run_semigaussian.py b/examples/distgen/run_semigaussian.py new file mode 100755 index 000000000..3542d33b6 --- /dev/null +++ b/examples/distgen/run_semigaussian.py @@ -0,0 +1,71 @@ +#!/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.particle_shape = 2 # B-spline order +sim.space_charge = False +# 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 = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.Semigaussian( + lambdaX=3.9984884770e-5, + lambdaY=3.9984884770e-5, + lambdaT=1.0e-3, + lambdaPx=2.6623538760e-5, + lambdaPy=2.6623538760e-5, + lambdaPt=2.0e-3, + muxpx=-0.846574929020762, + muypy=0.846574929020762, + mutpt=0.0, +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice) +ns = 25 # number of slices per ds in the element +fodo = [ + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=1.0, nslice=ns), + monitor, + elements.Drift(ds=0.5, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=-1.0, nslice=ns), + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, +] +# assign a fodo segment +sim.lattice.extend(fodo) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/examples/fodo/run_fodo_twiss.py b/examples/fodo/run_fodo_twiss.py new file mode 100755 index 000000000..bd7b7c51d --- /dev/null +++ b/examples/fodo/run_fodo_twiss.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2024 ImpactX contributors +# Authors: Axel Huebl, Chad Mitchell, Marco Garten +# License: BSD-3-Clause-LBNL +# +# -*- coding: utf-8 -*- + +from impactx import ImpactX, distribution, elements, twiss + +sim = ImpactX() + +# set numerical parameters and IO control +sim.particle_shape = 2 # B-spline order +sim.space_charge = False +# 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 = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.Waterbag( + **twiss( + beta_x=2.8216194100262637, + beta_y=2.8216194100262637, + beta_t=0.5, + emitt_x=2e-09, + emitt_y=2e-09, + emitt_t=2e-06, + alpha_x=-1.5905003499999992, + alpha_y=1.5905003499999992, + alpha_t=0.0, + ) +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice) +ns = 25 # number of slices per ds in the element +fodo = [ + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=1.0, nslice=ns), + monitor, + elements.Drift(ds=0.5, nslice=ns), + monitor, + elements.Quad(ds=1.0, k=-1.0, nslice=ns), + monitor, + elements.Drift(ds=0.25, nslice=ns), + monitor, +] +# assign a fodo segment +sim.lattice.extend(fodo) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/src/python/impactx/__init__.py b/src/python/impactx/__init__.py index 420de40ce..34cac419a 100644 --- a/src/python/impactx/__init__.py +++ b/src/python/impactx/__init__.py @@ -18,6 +18,7 @@ # import core bindings to C++ from . import impactx_pybind as cxx +from .distribution_input_helpers import twiss # noqa from .extensions.ImpactXParIter import register_ImpactXParIter_extension from .extensions.ImpactXParticleContainer import ( register_ImpactXParticleContainer_extension, diff --git a/src/python/impactx/distribution_input_helpers.py b/src/python/impactx/distribution_input_helpers.py new file mode 100644 index 000000000..2b0ab4b42 --- /dev/null +++ b/src/python/impactx/distribution_input_helpers.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# Copyright 2024 ImpactX contributors +# Authors: Marco Garten +# License: BSD-3-Clause-LBNL +# +# -*- coding: utf-8 -*- + +import numpy as np + + +def twiss( + beta_x: np.float64, + beta_y: np.float64, + beta_t: np.float64, + emitt_x: np.float64, + emitt_y: np.float64, + emitt_t: np.float64, + alpha_x: np.float64 = 0.0, + alpha_y: np.float64 = 0.0, + alpha_t: np.float64 = 0.0, +): + """ + Helper function to convert Courant-Snyder / Twiss input into phase space ellipse input. + + :param beta_x: Beta function value (unit: meter) in the x dimension, must be a non-zero positive value. + :param beta_y: Beta function value (unit: meter) in the y dimension, must be a non-zero positive value. + :param beta_t: Beta function value (unit: meter) in the t dimension (arrival time differences multiplied by light speed), must be a non-zero positive value. + :param emitt_x: Emittance value (unit: meter times radian) in the x dimension, must be a non-zero positive value. + :param emitt_y: Emittance value (unit: meter times radian) in the y dimension, must be a non-zero positive value. + :param emitt_t: Emittance value (unit: meter times radian) in the t dimension (arrival time differences multiplied by light speed), must be a non-zero positive value. + :param alpha_x: Alpha function value () in the x dimension, default is 0.0. + :param alpha_y: Alpha function value in the y dimension, default is 0.0. + :param alpha_t: Alpha function value in the t dimension, default is 0.0. + :return: A dictionary containing calculated phase space input: 'lambdaX', 'lambdaY', 'lambdaT', 'lambdaPx', 'lambdaPy', 'lambdaPt', 'muxpx', 'muypy', 'mutpt'. + """ + if beta_x <= 0.0 or beta_y <= 0.0 or beta_t <= 0.0: + raise ValueError( + "Input Error: The beta function values need to be non-zero positive values in all dimensions." + ) + + if emitt_x <= 0.0 or emitt_y <= 0.0 or emitt_t <= 0.0: + raise ValueError( + "Input Error: Emittance values need to be non-zero positive values in all dimensions." + ) + + betas = [beta_x, beta_y, beta_t] + alphas = [alpha_x, alpha_y, alpha_t] + + gammas = [] + # calculate Courant-Snyder gammas + for i in range(3): + gammas.append((1 + alphas[i] ** 2) / betas[i]) + gamma_x, gamma_y, gamma_t = gammas + + return { + "lambdaX": np.sqrt(emitt_x / gamma_x), + "lambdaY": np.sqrt(emitt_y / gamma_y), + "lambdaT": np.sqrt(emitt_t / gamma_t), + "lambdaPx": np.sqrt(emitt_x / beta_x), + "lambdaPy": np.sqrt(emitt_y / beta_y), + "lambdaPt": np.sqrt(emitt_t / beta_t), + "muxpx": alpha_x / np.sqrt(beta_x * gamma_x), + "muypy": alpha_y / np.sqrt(beta_y * gamma_y), + "mutpt": alpha_t / np.sqrt(beta_t * gamma_t), + } From 71d0e5b3f7a5e27f337c303c1fde61700f5ea702 Mon Sep 17 00:00:00 2001 From: ax3l Date: Sun, 8 Sep 2024 18:51:49 +0000 Subject: [PATCH 04/30] Update Stub Files --- src/python/impactx/__init__.pyi | 11 +++++- .../impactx/distribution_input_helpers.pyi | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/python/impactx/distribution_input_helpers.pyi diff --git a/src/python/impactx/__init__.pyi b/src/python/impactx/__init__.pyi index c3d925f89..b92a295d7 100644 --- a/src/python/impactx/__init__.pyi +++ b/src/python/impactx/__init__.pyi @@ -17,6 +17,7 @@ from __future__ import annotations import os as os from amrex import space3d as amr +from impactx.distribution_input_helpers import twiss from impactx.extensions.ImpactXParIter import register_ImpactXParIter_extension from impactx.extensions.ImpactXParticleContainer import ( register_ImpactXParticleContainer_extension, @@ -37,7 +38,13 @@ from impactx.impactx_pybind import ( ) from impactx.madx_to_impactx import read_beam, read_lattice -from . import MADXParser, extensions, impactx_pybind, madx_to_impactx +from . import ( + MADXParser, + distribution_input_helpers, + extensions, + impactx_pybind, + madx_to_impactx, +) __all__ = [ "Config", @@ -52,6 +59,7 @@ __all__ = [ "coordinate_transformation", "cxx", "distribution", + "distribution_input_helpers", "elements", "extensions", "impactx_pybind", @@ -64,6 +72,7 @@ __all__ = [ "register_ImpactXParticleContainer_extension", "s", "t", + "twiss", "wakeconvolution", ] __author__: str = ( diff --git a/src/python/impactx/distribution_input_helpers.pyi b/src/python/impactx/distribution_input_helpers.pyi new file mode 100644 index 000000000..2586f8afb --- /dev/null +++ b/src/python/impactx/distribution_input_helpers.pyi @@ -0,0 +1,34 @@ +from __future__ import annotations + +import numpy +import numpy as np + +__all__ = ["np", "twiss"] + +def twiss( + beta_x: numpy.float64, + beta_y: numpy.float64, + beta_t: numpy.float64, + emitt_x: numpy.float64, + emitt_y: numpy.float64, + emitt_t: numpy.float64, + alpha_x: numpy.float64 = 0.0, + alpha_y: numpy.float64 = 0.0, + alpha_t: numpy.float64 = 0.0, +): + """ + + Helper function to convert Courant-Snyder / Twiss input into phase space ellipse input. + + :param beta_x: Beta function value (unit: meter) in the x dimension, must be a non-zero positive value. + :param beta_y: Beta function value (unit: meter) in the y dimension, must be a non-zero positive value. + :param beta_t: Beta function value (unit: meter) in the t dimension (arrival time differences multiplied by light speed), must be a non-zero positive value. + :param emitt_x: Emittance value (unit: meter times radian) in the x dimension, must be a non-zero positive value. + :param emitt_y: Emittance value (unit: meter times radian) in the y dimension, must be a non-zero positive value. + :param emitt_t: Emittance value (unit: meter times radian) in the t dimension (arrival time differences multiplied by light speed), must be a non-zero positive value. + :param alpha_x: Alpha function value () in the x dimension, default is 0.0. + :param alpha_y: Alpha function value in the y dimension, default is 0.0. + :param alpha_t: Alpha function value in the t dimension, default is 0.0. + :return: A dictionary containing calculated phase space input: 'lambdaX', 'lambdaY', 'lambdaT', 'lambdaPx', 'lambdaPy', 'lambdaPt', 'muxpx', 'muypy', 'mutpt'. + + """ From fa36b9afa6280a440692bccc0cffa7ebd55c8ee4 Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Mon, 16 Sep 2024 19:40:26 -0700 Subject: [PATCH 05/30] Fix literal includes of test files (#701) --- examples/distgen/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/distgen/README.rst b/examples/distgen/README.rst index cb778348d..ec23b2a3d 100644 --- a/examples/distgen/README.rst +++ b/examples/distgen/README.rst @@ -100,13 +100,13 @@ For `MPI-parallel `__ runs, prefix these lines with ` .. tab-item:: Python: Script - .. literalinclude:: run_kvdist_from_twiss.py + .. literalinclude:: run_kvdist_twiss.py :language: python3 :caption: You can copy this file from ``examples/distgen/run_kvdist_twiss.py``. .. tab-item:: Executable: Input File - .. literalinclude:: input_kvdist_from_twiss.in + .. literalinclude:: input_kvdist_twiss.in :language: ini :caption: You can copy this file from ``examples/distgen/input_kvdist_twiss.in``. From ed95725955e31dc74f5c517ab5ef3c43a08bbedb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:40:47 -0700 Subject: [PATCH 06/30] [pre-commit.ci] pre-commit autoupdate (#700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f78e3be0a..61e45cee3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: # Python: Ruff linter & formatter # https://docs.astral.sh/ruff/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.6.5 hooks: # Run the linter - id: ruff From 6559ddba647a67aa46f8760c47ea14c437ffe742 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 16 Sep 2024 19:41:39 -0700 Subject: [PATCH 07/30] Release: 24.09 (#695) --- CMakeLists.txt | 2 +- cmake/dependencies/ABLASTR.cmake | 4 ++-- cmake/dependencies/pyAMReX.cmake | 4 ++-- docs/source/conf.py | 4 ++-- setup.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cadda4eb..a552d5fd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Preamble #################################################################### # cmake_minimum_required(VERSION 3.24.0) -project(ImpactX VERSION 24.08) +project(ImpactX VERSION 24.09) include(${ImpactX_SOURCE_DIR}/cmake/ImpactXFunctions.cmake) diff --git a/cmake/dependencies/ABLASTR.cmake b/cmake/dependencies/ABLASTR.cmake index 7e900e00a..5ac8b4672 100644 --- a/cmake/dependencies/ABLASTR.cmake +++ b/cmake/dependencies/ABLASTR.cmake @@ -127,7 +127,7 @@ macro(find_ablastr) set(COMPONENT_DIM 3D) set(COMPONENT_PRECISION ${ImpactX_PRECISION} P${ImpactX_PRECISION}) - find_package(ABLASTR 24.08 CONFIG REQUIRED COMPONENTS ${COMPONENT_DIM}) + find_package(ABLASTR 24.09 CONFIG REQUIRED COMPONENTS ${COMPONENT_DIM}) message(STATUS "ABLASTR: Found version '${ABLASTR_VERSION}'") endif() @@ -161,7 +161,7 @@ set(ImpactX_openpmd_src "" set(ImpactX_ablastr_repo "https://github.com/ECP-WarpX/WarpX.git" CACHE STRING "Repository URI to pull and build ABLASTR from if(ImpactX_ablastr_internal)") -set(ImpactX_ablastr_branch "0838941a6693df711769a90a8a989c9a920b0abe" +set(ImpactX_ablastr_branch "24.09" CACHE STRING "Repository branch for ImpactX_ablastr_repo if(ImpactX_ablastr_internal)") diff --git a/cmake/dependencies/pyAMReX.cmake b/cmake/dependencies/pyAMReX.cmake index fbaeb5a8d..fd91b71ae 100644 --- a/cmake/dependencies/pyAMReX.cmake +++ b/cmake/dependencies/pyAMReX.cmake @@ -59,7 +59,7 @@ function(find_pyamrex) endif() elseif(NOT ImpactX_pyamrex_internal) # TODO: MPI control - find_package(pyAMReX 24.08 CONFIG REQUIRED) + find_package(pyAMReX 24.09 CONFIG REQUIRED) message(STATUS "pyAMReX: Found version '${pyAMReX_VERSION}'") endif() endfunction() @@ -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 "abdf332e25bfeef2b4d613d7adbe93fb8cf3e2f7" +set(ImpactX_pyamrex_branch "24.09" CACHE STRING "Repository branch for ImpactX_pyamrex_repo if(ImpactX_pyamrex_internal)") diff --git a/docs/source/conf.py b/docs/source/conf.py index bdb3f8639..e553e8d4d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -73,9 +73,9 @@ # built documents. # # The short X.Y version. -version = "24.08" +version = "24.09" # The full version, including alpha/beta/rc tags. -release = "24.08" +release = "24.09" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 308a4a0c6..5a398953f 100644 --- a/setup.py +++ b/setup.py @@ -223,7 +223,7 @@ def build_extension(self, ext): setup( name="impactx", # note PEP-440 syntax: x.y.zaN but x.y.z.devN - version="24.08", + version="24.09", packages=["impactx"], # Python sources: package_dir={"": "src/python"}, From b8345b3dba394e6bf8e525af5a0f1a3c33fc816f Mon Sep 17 00:00:00 2001 From: ax3l Date: Tue, 17 Sep 2024 02:55:27 +0000 Subject: [PATCH 08/30] Update Stub Files --- src/python/impactx/__init__.pyi | 2 +- src/python/impactx/impactx_pybind/__init__.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/impactx/__init__.pyi b/src/python/impactx/__init__.pyi index b92a295d7..f0b3b94d6 100644 --- a/src/python/impactx/__init__.pyi +++ b/src/python/impactx/__init__.pyi @@ -79,7 +79,7 @@ __author__: str = ( "Axel Huebl, Chad Mitchell, Ryan Sandberg, Marco Garten, Ji Qiang, et al." ) __license__: str = "BSD-3-Clause-LBNL" -__version__: str = "24.08" +__version__: str = "24.09" s: impactx_pybind.CoordSystem # value = t: impactx_pybind.CoordSystem # value = cxx = impactx_pybind diff --git a/src/python/impactx/impactx_pybind/__init__.pyi b/src/python/impactx/impactx_pybind/__init__.pyi index e9220f358..b6587969b 100644 --- a/src/python/impactx/impactx_pybind/__init__.pyi +++ b/src/python/impactx/impactx_pybind/__init__.pyi @@ -706,6 +706,6 @@ __author__: str = ( "Axel Huebl, Chad Mitchell, Ryan Sandberg, Marco Garten, Ji Qiang, et al." ) __license__: str = "BSD-3-Clause-LBNL" -__version__: str = "24.08" +__version__: str = "24.09" s: CoordSystem # value = t: CoordSystem # value = From 738d7151f127945d20d49a72944336a790c54ee6 Mon Sep 17 00:00:00 2001 From: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:02:59 -0700 Subject: [PATCH 09/30] Fix special case of pt_ms=0 in dispersive diagnostics. (#707) --- src/particles/diagnostics/ReducedBeamCharacteristics.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/particles/diagnostics/ReducedBeamCharacteristics.cpp b/src/particles/diagnostics/ReducedBeamCharacteristics.cpp index 0b6ed024d..3b4d19ffd 100644 --- a/src/particles/diagnostics/ReducedBeamCharacteristics.cpp +++ b/src/particles/diagnostics/ReducedBeamCharacteristics.cpp @@ -262,10 +262,10 @@ namespace impactx::diagnostics amrex::ParticleReal const emittance_y = std::sqrt(y_ms*py_ms-ypy*ypy); amrex::ParticleReal const emittance_t = std::sqrt(t_ms*pt_ms-tpt*tpt); // Dispersion and dispersive beam moments - amrex::ParticleReal const dispersion_x = - xpt / pt_ms; - amrex::ParticleReal const dispersion_px = - pxpt / pt_ms; - amrex::ParticleReal const dispersion_y = - ypt / pt_ms; - amrex::ParticleReal const dispersion_py = - pypt / pt_ms; + amrex::ParticleReal const dispersion_x = ((pt_ms > 0.0) ? (- xpt / pt_ms) : 0.0); + amrex::ParticleReal const dispersion_px = ((pt_ms > 0.0) ? (- pxpt / pt_ms) : 0.0); + amrex::ParticleReal const dispersion_y = ((pt_ms > 0.0) ? (- ypt / pt_ms) : 0.0); + amrex::ParticleReal const dispersion_py = ((pt_ms > 0.0) ? (- pypt / pt_ms) : 0.0); amrex::ParticleReal const x_msd = x_ms - pt_ms*dispersion_x*dispersion_x; amrex::ParticleReal const px_msd = px_ms - pt_ms*dispersion_px*dispersion_px; amrex::ParticleReal const xpx_d = xpx - pt_ms*dispersion_x*dispersion_px; From 24ec19f375b5eb687bde39dd734e314e850afe9b Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 20 Sep 2024 16:30:21 -0700 Subject: [PATCH 10/30] LUMI (CSC): September Upgrade (#708) Update the LUMI documentation to the latest major system upgrade from last week. --- .../install/hpc/lumi-csc/lumi_cpu_impactx.profile.example | 8 ++++---- .../install/hpc/lumi-csc/lumi_gpu_impactx.profile.example | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/install/hpc/lumi-csc/lumi_cpu_impactx.profile.example b/docs/source/install/hpc/lumi-csc/lumi_cpu_impactx.profile.example index 63b6f43d1..9eb779980 100644 --- a/docs/source/install/hpc/lumi-csc/lumi_cpu_impactx.profile.example +++ b/docs/source/install/hpc/lumi-csc/lumi_cpu_impactx.profile.example @@ -2,10 +2,10 @@ #export proj="project_..." # required dependencies -module load LUMI/23.09 partition/C +module load LUMI/24.03 partition/C module load PrgEnv-aocc -module load buildtools/23.09 -module load cray-fftw/3.3.10.5 +module load buildtools/24.03 +module load cray-fftw/3.3.10.7 # optional: just an additional text editor module load nano @@ -19,7 +19,7 @@ export PATH=${SW_DIR}/adios2-2.8.3/bin:${PATH} export PATH=${SW_DIR}/hdf5-1.14.1.2/bin:${PATH} # optional: for Python bindings or libEnsemble -module load cray-python/3.10.10 +module load cray-python/3.11.7 if [ -d "${SW_DIR}/venvs/impactx-cpu-lumi" ] then diff --git a/docs/source/install/hpc/lumi-csc/lumi_gpu_impactx.profile.example b/docs/source/install/hpc/lumi-csc/lumi_gpu_impactx.profile.example index 8d6958bdb..c060f7d6c 100644 --- a/docs/source/install/hpc/lumi-csc/lumi_gpu_impactx.profile.example +++ b/docs/source/install/hpc/lumi-csc/lumi_gpu_impactx.profile.example @@ -2,9 +2,9 @@ #export proj="project_..." # required dependencies -module load LUMI/23.09 partition/G -module load rocm/5.2.3 -module load buildtools/23.09 +module load LUMI/24.03 partition/G +module load rocm/6.0.3 +module load buildtools/24.03 # optional: just an additional text editor module load nano @@ -18,7 +18,7 @@ export PATH=${SW_DIR}/adios2-2.8.3/bin:${PATH} export PATH=${SW_DIR}/hdf5-1.14.1.2/bin:${PATH} # optional: for Python bindings or libEnsemble -module load cray-python/3.10.10 +module load cray-python/3.11.7 if [ -d "${SW_DIR}/venvs/impactx-gpu-lumi" ] then From ea151e0547fd5564df32c3de7f86072c294b53e6 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sun, 22 Sep 2024 22:01:00 -0700 Subject: [PATCH 11/30] Rename element `name` to `type` (#710) This string just names the type of the element. We will introduce per-element, user-defined names in a separate PR, which we will use the `name` field for. --- docs/source/usage/workflows/add_element.rst | 2 +- src/particles/PushAll.H | 2 +- src/particles/elements/Aperture.H | 2 +- src/particles/elements/Buncher.H | 2 +- src/particles/elements/CFbend.H | 2 +- src/particles/elements/ChrDrift.H | 2 +- src/particles/elements/ChrPlasmaLens.H | 2 +- src/particles/elements/ChrQuad.H | 2 +- src/particles/elements/ChrUniformAcc.H | 2 +- src/particles/elements/ConstF.H | 2 +- src/particles/elements/DipEdge.H | 2 +- src/particles/elements/Drift.H | 2 +- src/particles/elements/Empty.H | 2 +- src/particles/elements/ExactDrift.H | 2 +- src/particles/elements/ExactSbend.H | 2 +- src/particles/elements/Kicker.H | 2 +- src/particles/elements/Multipole.H | 2 +- src/particles/elements/NonlinearLens.H | 2 +- src/particles/elements/PRot.H | 2 +- src/particles/elements/Programmable.H | 2 +- src/particles/elements/Quad.H | 2 +- src/particles/elements/RFCavity.H | 2 +- src/particles/elements/Sbend.H | 2 +- src/particles/elements/ShortRF.H | 2 +- src/particles/elements/SoftQuad.H | 2 +- src/particles/elements/SoftSol.H | 2 +- src/particles/elements/Sol.H | 2 +- src/particles/elements/TaperedPL.H | 2 +- src/particles/elements/ThinDipole.H | 2 +- src/particles/elements/diagnostics/AdditionalProperties.cpp | 2 +- src/particles/elements/diagnostics/openPMD.H | 2 +- src/particles/elements/diagnostics/openPMD.cpp | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/source/usage/workflows/add_element.rst b/docs/source/usage/workflows/add_element.rst index a4efae65e..db80e21eb 100644 --- a/docs/source/usage/workflows/add_element.rst +++ b/docs/source/usage/workflows/add_element.rst @@ -53,7 +53,7 @@ To simplify the logic, we use so-called `mixin classes ; diff --git a/src/particles/elements/diagnostics/openPMD.cpp b/src/particles/elements/diagnostics/openPMD.cpp index db35bcbab..163cbbe11 100644 --- a/src/particles/elements/diagnostics/openPMD.cpp +++ b/src/particles/elements/diagnostics/openPMD.cpp @@ -315,7 +315,7 @@ namespace detail ) { #ifdef ImpactX_USE_OPENPMD - std::string profile_name = "impactx::Push::" + std::string(BeamMonitor::name); + std::string profile_name = "impactx::Push::" + std::string(BeamMonitor::type); BL_PROFILE(profile_name); // preparing to access reference particle data: RefPart From f8a033fa350f30e2d42a01b2bd3ea2a7836dee48 Mon Sep 17 00:00:00 2001 From: Alexander Sinn <64009254+AlexanderSinn@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:27:58 +0200 Subject: [PATCH 12/30] [Test] Fix amrex::Finalize (#711) * Fix amrex::Finalize * Use old finalize location --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4d6aeb315..aa3c973f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,16 +28,16 @@ int main(int argc, char* argv[]) // it here so users can pass command line arguments impactx::initialization::default_init_AMReX(argc, argv); - BL_PROFILE_VAR("main()", pmain); { + BL_PROFILE_VAR("main()", pmain); impactx::ImpactX impactX; impactX.init_grids(); impactX.initBeamDistributionFromInputs(); impactX.initLatticeElementsFromInputs(); impactX.evolve(); + BL_PROFILE_VAR_STOP(pmain); impactX.finalize(); } - BL_PROFILE_VAR_STOP(pmain); #if defined(AMREX_USE_MPI) AMREX_ALWAYS_ASSERT(MPI_SUCCESS == MPI_Finalize()); From 3a8ab8dc981aafccc9ab3de167e222d986f8b374 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:25:14 -0700 Subject: [PATCH 13/30] [pre-commit.ci] pre-commit autoupdate (#712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61e45cee3..7124110c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: # Python: Ruff linter & formatter # https://docs.astral.sh/ruff/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.5 + rev: v0.6.7 hooks: # Run the linter - id: ruff From 4c675858c531db749abc4d9905a646733fba04f4 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 26 Sep 2024 13:07:59 -0700 Subject: [PATCH 14/30] ``: Use `std::` C++ Functions (#716) * ``: Use `std::` C++ Functions Only the `std::` math functions have overloads for `double` and `float`. Without the `std::` prefix, we use the C functions, which will when we run ImpactX in single precision cause up and down casts to `double` (performance penalty). * Leftover --- src/particles/ReferenceParticle.H | 10 ++-- src/particles/distribution/Gaussian.H | 24 +++++----- src/particles/distribution/KVdist.H | 24 +++++----- src/particles/distribution/Kurth4D.H | 28 +++++------ src/particles/distribution/Kurth6D.H | 32 ++++++------- src/particles/distribution/Semigaussian.H | 24 +++++----- src/particles/distribution/Thermal.H | 46 +++++++++---------- src/particles/distribution/Triangle.H | 28 +++++------ src/particles/elements/Aperture.H | 4 +- src/particles/elements/Buncher.H | 2 +- src/particles/elements/CFbend.H | 24 +++++----- src/particles/elements/ChrDrift.H | 14 +++--- src/particles/elements/ChrPlasmaLens.H | 18 ++++---- src/particles/elements/ChrQuad.H | 42 ++++++++--------- src/particles/elements/ChrUniformAcc.H | 36 +++++++-------- src/particles/elements/ConstF.H | 16 +++---- src/particles/elements/DipEdge.H | 6 +-- src/particles/elements/Drift.H | 4 +- src/particles/elements/ExactDrift.H | 6 +-- src/particles/elements/ExactSbend.H | 8 ++-- src/particles/elements/Multipole.H | 2 +- src/particles/elements/NonlinearLens.H | 10 ++-- src/particles/elements/PRot.H | 4 +- src/particles/elements/RFCavity.H | 40 ++++++++-------- src/particles/elements/Sbend.H | 6 +-- src/particles/elements/ShortRF.H | 14 +++--- src/particles/elements/SoftQuad.H | 18 ++++---- src/particles/elements/SoftSol.H | 24 +++++----- src/particles/elements/Sol.H | 4 +- src/particles/elements/ThinDipole.H | 4 +- src/particles/integrators/Integrators.H | 2 +- .../CoordinateTransformation.cpp | 2 +- src/particles/transformation/ToFixedS.H | 8 ++-- src/particles/transformation/ToFixedT.H | 8 ++-- 34 files changed, 271 insertions(+), 271 deletions(-) diff --git a/src/particles/ReferenceParticle.H b/src/particles/ReferenceParticle.H index 7f1b7ae19..226025b74 100644 --- a/src/particles/ReferenceParticle.H +++ b/src/particles/ReferenceParticle.H @@ -66,7 +66,7 @@ namespace impactx using namespace amrex::literals; amrex::ParticleReal const ref_gamma = -pt; - amrex::ParticleReal const ref_beta = sqrt(1.0_prt - 1.0_prt/pow(ref_gamma,2)); + amrex::ParticleReal const ref_beta = std::sqrt(1.0_prt - 1.0_prt/pow(ref_gamma,2)); return ref_beta; } @@ -81,7 +81,7 @@ namespace impactx using namespace amrex::literals; amrex::ParticleReal const ref_gamma = -pt; - amrex::ParticleReal const ref_betagamma = sqrt(pow(ref_gamma, 2) - 1.0_prt); + amrex::ParticleReal const ref_betagamma = std::sqrt(std::pow(ref_gamma, 2) - 1.0_prt); return ref_betagamma; } @@ -118,7 +118,7 @@ namespace impactx if (pt != 0.0_prt) { pt = -kin_energy_MeV() / massE - 1.0_prt; - pz = sqrt(pow(pt, 2) - 1.0_prt); + pz = std::sqrt(std::pow(pt, 2) - 1.0_prt); } return *this; @@ -155,7 +155,7 @@ namespace impactx px = 0.0; py = 0.0; pt = -kin_energy / mass_MeV() - 1.0_prt; - pz = sqrt(pow(pt, 2) - 1.0_prt); + pz = std::sqrt(std::pow(pt, 2) - 1.0_prt); return *this; } @@ -171,7 +171,7 @@ namespace impactx using namespace amrex::literals; amrex::ParticleReal const ref_gamma = -pt; - amrex::ParticleReal const ref_betagamma = sqrt(pow(ref_gamma, 2) - 1.0_prt); + amrex::ParticleReal const ref_betagamma = std::sqrt(std::pow(ref_gamma, 2) - 1.0_prt); //amrex::ParticleReal const ref_rigidity = mass*ref_betagamma*(ablastr::constant::SI::c)/charge; //fails due to "charge" amrex::ParticleReal const ref_rigidity = mass*ref_betagamma*(ablastr::constant::SI::c)/(ablastr::constant::SI::q_e); return ref_rigidity; diff --git a/src/particles/distribution/Gaussian.H b/src/particles/distribution/Gaussian.H index d6baf9640..24397c0b0 100644 --- a/src/particles/distribution/Gaussian.H +++ b/src/particles/distribution/Gaussian.H @@ -100,34 +100,34 @@ namespace impactx::distribution u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - x = ln1*cos(2_prt*pi*u2); - px = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + x = ln1 * std::cos(2_prt*pi*u2); + px = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - y = ln1*cos(2_prt*pi*u2); - py = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + y = ln1 * std::cos(2_prt*pi*u2); + py = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - t = ln1*cos(2_prt*pi*u2); - pt = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + t = ln1 * std::cos(2_prt*pi*u2); + pt = ln1 * std::sin(2_prt*pi*u2); // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/distribution/KVdist.H b/src/particles/distribution/KVdist.H index 87e343399..3554013eb 100644 --- a/src/particles/distribution/KVdist.H +++ b/src/particles/distribution/KVdist.H @@ -101,27 +101,27 @@ namespace impactx::distribution v = amrex::Random(engine); phi = amrex::Random(engine); phi = 2_prt*pi*phi; - r = sqrt(v); - x = r*cos(phi); - y = r*sin(phi); + r = std::sqrt(v); + x = r * std::cos(phi); + y = r * std::sin(phi); // Sample and transform to define (px,py): beta = amrex::Random(engine); beta = 2_prt*pi*beta; - p = sqrt(1_prt-pow(r,2)); - px = p*cos(beta); - py = p*sin(beta); + p = std::sqrt(1_prt-pow(r,2)); + px = p * std::cos(beta); + py = p * std::sin(beta); // Sample and transform to define (t,pt): t = amrex::Random(engine); t = 2.0_prt*(t-0.5_prt); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - pt = ln1*cos(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + pt = ln1 * std::cos(2_prt*pi*u2); // Scale to produce the identity covariance matrix: - amrex::ParticleReal const c = sqrt(3.0_prt); + amrex::ParticleReal const c = std::sqrt(3.0_prt); x = 2_prt*x; y = 2_prt*y; t = c*t; @@ -130,17 +130,17 @@ namespace impactx::distribution // pt = pt; // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/distribution/Kurth4D.H b/src/particles/distribution/Kurth4D.H index cd254d2f9..d5b40e2d7 100644 --- a/src/particles/distribution/Kurth4D.H +++ b/src/particles/distribution/Kurth4D.H @@ -102,9 +102,9 @@ namespace impactx::distribution v = amrex::Random(engine); phi = amrex::Random(engine); phi = 2_prt*pi*phi; - r = sqrt(v); - x = r*cos(phi); - y = r*sin(phi); + r = std::sqrt(v); + x = r * std::cos(phi); + y = r * std::sin(phi); // Random samples used to define Lz: u = amrex::Random(engine); @@ -113,25 +113,25 @@ namespace impactx::distribution // Random samples used to define pr: alpha = amrex::Random(engine); alpha = pi*alpha; - pmax = 1.0_prt - pow((Lz/r),2) - pow(r,2) + pow(Lz,2); - pmax = sqrt(pmax); - pr = pmax*cos(alpha); + pmax = 1.0_prt - std::pow((Lz/r),2) - std::pow(r,2) + std::pow(Lz,2); + pmax = std::sqrt(pmax); + pr = pmax * std::cos(alpha); pphi = Lz/r; // Transformations used to obtain (px,py): - px = pr*cos(phi)-pphi*sin(phi); - py = pr*sin(phi)+pphi*cos(phi); + px = pr * std::cos(phi)-pphi * std::sin(phi); + py = pr * std::sin(phi)+pphi * std::cos(phi); // Sample and transform to define (t,pt): t = amrex::Random(engine); t = 2.0_prt*(t-0.5_prt); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - pt = ln1*cos(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + pt = ln1 * std::cos(2_prt*pi*u2); // Scale to produce the identity covariance matrix: - amrex::ParticleReal const c = sqrt(3.0_prt); + amrex::ParticleReal const c = std::sqrt(3.0_prt); x = 2_prt*x; y = 2_prt*y; t = c*t; @@ -140,17 +140,17 @@ namespace impactx::distribution // pt = pt; // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/distribution/Kurth6D.H b/src/particles/distribution/Kurth6D.H index e3d1b8c42..b0a1da425 100644 --- a/src/particles/distribution/Kurth6D.H +++ b/src/particles/distribution/Kurth6D.H @@ -104,40 +104,40 @@ namespace impactx::distribution v = amrex::Random(engine); costheta = amrex::Random(engine); costheta = 2_prt*(costheta-0.5_prt); - sintheta = sqrt(1_prt-pow(costheta,2)); + sintheta = std::sqrt(1_prt-pow(costheta,2)); phi = amrex::Random(engine); phi = 2_prt*pi*phi; // Transformations for (x,y,t): - r = pow(v,1_prt/3_prt); - x = r*sintheta*cos(phi); - y = r*sintheta*sin(phi); + r = std::pow(v,1_prt/3_prt); + x = r*sintheta * std::cos(phi); + y = r*sintheta * std::sin(phi); t = r*costheta; // Random samples used to define L: L = amrex::Random(engine); - L = r*sqrt(L); + L = r*std::sqrt(L); // Random samples used to define pr: alpha = amrex::Random(engine); alpha = pi*alpha; - pmax = 1_prt - pow(L/r,2) - pow(r,2) + pow(L,2); - pmax = sqrt(pmax); - pr = pmax*cos(alpha); + pmax = 1_prt - std::pow(L/r,2) - std::pow(r,2) + std::pow(L,2); + pmax = std::sqrt(pmax); + pr = pmax * std::cos(alpha); // Random samples used to define ptangent: beta = amrex::Random(engine); beta = 2_prt*pi*beta; - p1 = L/r*cos(beta); // This is phi component - p2 = L/r*sin(beta); // This is theta component + p1 = L/r * std::cos(beta); // This is phi component + p2 = L/r * std::sin(beta); // This is theta component // Transformation from spherical to Cartesian coord.: - px = pr*sintheta*cos(phi) + p2*costheta*cos(phi) - p1*sin(phi); - py = pr*sintheta*sin(phi) + p2*costheta*sin(phi) + p1*cos(phi); + px = pr*sintheta * std::cos(phi) + p2*costheta * std::cos(phi) - p1 * std::sin(phi); + py = pr*sintheta * std::sin(phi) + p2*costheta * std::sin(phi) + p1 * std::cos(phi); pt = pr*costheta - p2*sintheta; // Scale to produce the identity covariance matrix: - amrex::ParticleReal const c = sqrt(5.0_prt); + amrex::ParticleReal const c = std::sqrt(5.0_prt); x = c*x; y = c*y; t = c*t; @@ -146,17 +146,17 @@ namespace impactx::distribution pt = c*pt; // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/distribution/Semigaussian.H b/src/particles/distribution/Semigaussian.H index 63d985657..f6c3deaa1 100644 --- a/src/particles/distribution/Semigaussian.H +++ b/src/particles/distribution/Semigaussian.H @@ -103,14 +103,14 @@ namespace impactx::distribution phi = amrex::Random(engine); phi = 2_prt*pi*phi; v = amrex::Random(engine); - r = sqrt(v); - x = r*cos(phi); - y = r*sin(phi); + r = std::sqrt(v); + x = r * std::cos(phi); + y = r * std::sin(phi); t = amrex::Random(engine); t = 2_prt*(t-0.5_prt); // Scale to produce the identity covariance matrix: - amrex::ParticleReal const c = sqrt(3.0_prt); + amrex::ParticleReal const c = std::sqrt(3.0_prt); x = 2_prt*x; y = 2_prt*y; t = c*t; @@ -119,27 +119,27 @@ namespace impactx::distribution u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - px = ln1*cos(2_prt*pi*u2); - py = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + px = ln1 * std::cos(2_prt*pi*u2); + py = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - pt = ln1*cos(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + pt = ln1 * std::cos(2_prt*pi*u2); // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/distribution/Thermal.H b/src/particles/distribution/Thermal.H index 63169332a..898f86a4a 100644 --- a/src/particles/distribution/Thermal.H +++ b/src/particles/distribution/Thermal.H @@ -97,7 +97,7 @@ namespace distribution amrex::ParticleReal q_e = refpart.charge_qe(); // Set space charge intensity - m_Cintensity = q_e*bunch_charge/(pow(bg,2)*Erest*ablastr::constant::SI::ep0); + m_Cintensity = q_e*bunch_charge/(std::pow(bg,2)*Erest*ablastr::constant::SI::ep0); // Set minimum and maximum radius amrex::ParticleReal r_scale = matched_scale_radius(); @@ -106,8 +106,8 @@ namespace distribution // amrex::PrintToFile("equilibrium_params.out") << r_scale << " " << data.Cintensity << "\n"; // Scale the parameters p1 and p2 - amrex::ParticleReal rt2pi = sqrt(2.0_prt*pi); - amrex::ParticleReal p_scale = pow(r_scale*rt2pi,-3); + amrex::ParticleReal rt2pi = std::sqrt(2.0_prt*pi); + amrex::ParticleReal p_scale = std::pow(r_scale*rt2pi,-3); m_p1 = m_p1*p_scale; m_p2 = m_p2*p_scale; @@ -161,8 +161,8 @@ namespace distribution amrex::ParticleReal k = m_k; amrex::ParticleReal kT = (1.0_prt - m_w) * m_T1 + m_w * m_T2; - amrex::ParticleReal a = m_Cintensity/(4.0_prt*pi*5.0_prt*sqrt(5.0_prt)); - amrex::ParticleReal rscale = sqrt(kT + pow(a*k,2.0/3.0))/k; + amrex::ParticleReal a = m_Cintensity/(4.0_prt*pi*5.0_prt*std::sqrt(5.0_prt)); + amrex::ParticleReal rscale = std::sqrt(kT + std::pow(a*k,2.0/3.0))/k; return rscale; } @@ -252,17 +252,17 @@ namespace distribution // Define intermediate quantities amrex::ParticleReal potential = 0.0_prt; - potential = pow(k*r,2.0)/2.0_prt + c1*phi1 + c2*phi2; - amrex::ParticleReal Pdensity1 = m_p1*exp(-potential/T1); - amrex::ParticleReal Pdensity2 = m_p2*exp(-potential/T2); + potential = std::pow(k*r,2.0)/2.0_prt + c1*phi1 + c2*phi2; + amrex::ParticleReal Pdensity1 = m_p1 * std::exp(-potential/T1); + amrex::ParticleReal Pdensity2 = m_p2 * std::exp(-potential/T2); // amrex::ParticleReal Pdensity_tot = (1.0_prt-w)*Pdensity1 + w*Pdensity2; // amrex::PrintToFile("Pdensity.out") << reval << " " << Pdensity_tot << "\n"; // Apply map to update f1 and f2: m_phi1 = phi1; m_phi2 = phi2; - m_f1 = f1 + tau*4.0_prt*pi*pow(r,2.0)*Pdensity1; - m_f2 = f2 + tau*4.0_prt*pi*pow(r,2.0)*Pdensity2; + m_f1 = f1 + tau*4.0_prt*pi * std::pow(r,2.0)*Pdensity1; + m_f2 = f2 + tau*4.0_prt*pi * std::pow(r,2.0)*Pdensity2; reval = r; } }; @@ -371,28 +371,28 @@ namespace distribution // Generate six standard normal random variables using Box-Muller: u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g1 = ln1*cos(2_prt*pi*u2); - g2 = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g1 = ln1 * std::cos(2_prt*pi*u2); + g2 = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g3 = ln1*cos(2_prt*pi*u2); - g4 = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g3 = ln1 * std::cos(2_prt*pi*u2); + g4 = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g5 = ln1*cos(2_prt*pi*u2); - g6 = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g5 = ln1 * std::cos(2_prt*pi*u2); + g6 = ln1 * std::sin(2_prt*pi*u2); // Scale the last three variables to produce the momenta: amrex::ParticleReal kT = (uhalo > m_w) ? m_T1 : m_T2; // select core or halo value - px = sqrt(kT)*g4; - py = sqrt(kT)*g5; - pz = sqrt(kT)*g6; + px = std::sqrt(kT)*g4; + py = std::sqrt(kT)*g5; + pz = std::sqrt(kT)*g6; // Normalize the first three variables to produce uniform samples on the unit 3-sphere: - norm = sqrt(g1*g1+g2*g2+g3*g3); + norm = std::sqrt(g1*g1+g2*g2+g3*g3); g1 /= norm; g2 /= norm; g3 /= norm; diff --git a/src/particles/distribution/Triangle.H b/src/particles/distribution/Triangle.H index a1f40dc14..631dd5338 100644 --- a/src/particles/distribution/Triangle.H +++ b/src/particles/distribution/Triangle.H @@ -99,29 +99,29 @@ namespace impactx::distribution // Sample the t coordinate for a ramped triangular profile (unit // variance): u0 = amrex::Random(engine); - t = sqrt(2_prt)*(2_prt-3_prt*sqrt(u0)); + t = std::sqrt(2_prt)*(2_prt-3_prt*std::sqrt(u0)); // Generate five standard normal random variables using Box-Muller: u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g1 = ln1*cos(2_prt*pi*u2); - g2 = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g1 = ln1 * std::cos(2_prt*pi*u2); + g2 = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g3 = ln1*cos(2_prt*pi*u2); - g4 = ln1*sin(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g3 = ln1 * std::cos(2_prt*pi*u2); + g4 = ln1 * std::sin(2_prt*pi*u2); u1 = amrex::Random(engine); u2 = amrex::Random(engine); - ln1 = sqrt(-2_prt*log(u1)); - g5 = ln1*cos(2_prt*pi*u2); + ln1 = std::sqrt(-2_prt*std::log(u1)); + g5 = ln1 * std::cos(2_prt*pi*u2); // Use one of these normal random variables for pt: pt = g5; // Normalize the rest to produce uniform samples on the unit sphere: - norm = sqrt(g1*g1+g2*g2+g3*g3+g4*g4); + norm = std::sqrt(g1*g1+g2*g2+g3*g3+g4*g4); g1 /= norm; g2 /= norm; g3 /= norm; @@ -130,24 +130,24 @@ namespace impactx::distribution // Scale to produce uniform samples in a 4D ball (unit variance): d = 4_prt; // unit ball dimension u1 = amrex::Random(engine); // uniform sample - u2 = sqrt(d+2_prt)*pow(u1,1_prt/d); + u2 = std::sqrt(d+2_prt) * std::pow(u1,1_prt/d); x = g1*u2; y = g2*u2; px = g3*u2; py = g4*u2; // Transform to produce the desired second moments/correlations: - root = sqrt(1.0_prt-m_muxpx*m_muxpx); + root = std::sqrt(1.0_prt-m_muxpx*m_muxpx); a1 = m_lambdaX * x / root; a2 = m_lambdaPx * (-m_muxpx * x / root + px); x = a1; px = a2; - root = sqrt(1.0_prt-m_muypy*m_muypy); + root = std::sqrt(1.0_prt-m_muypy*m_muypy); a1 = m_lambdaY * y / root; a2 = m_lambdaPy * (-m_muypy * y / root + py); y = a1; py = a2; - root = sqrt(1.0_prt-m_mutpt*m_mutpt); + root = std::sqrt(1.0_prt-m_mutpt*m_mutpt); a1 = m_lambdaT * t / root; a2 = m_lambdaPt * (-m_mutpt * t / root + pt); t = a1; diff --git a/src/particles/elements/Aperture.H b/src/particles/elements/Aperture.H index 2f1e6e9eb..199712fb9 100644 --- a/src/particles/elements/Aperture.H +++ b/src/particles/elements/Aperture.H @@ -102,13 +102,13 @@ namespace impactx switch (m_shape) { case Shape::rectangular : // default - if (pow(u,2)>1 || pow(v,2) > 1_prt) { + if (std::pow(u,2)>1 || std::pow(v,2) > 1_prt) { amrex::ParticleIDWrapper{idcpu}.make_invalid(); } break; case Shape::elliptical : - if (pow(u,2)+pow(v,2) > 1_prt) { + if (std::pow(u,2) + std::pow(v,2) > 1_prt) { amrex::ParticleIDWrapper{idcpu}.make_invalid(); } break; diff --git a/src/particles/elements/Buncher.H b/src/particles/elements/Buncher.H index 1dc83d4ed..731682c0f 100644 --- a/src/particles/elements/Buncher.H +++ b/src/particles/elements/Buncher.H @@ -86,7 +86,7 @@ namespace impactx // access reference particle values to find (beta*gamma)^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // intialize output values of momenta amrex::ParticleReal pxout = px; diff --git a/src/particles/elements/CFbend.H b/src/particles/elements/CFbend.H index 2db863ce0..b9007986c 100644 --- a/src/particles/elements/CFbend.H +++ b/src/particles/elements/CFbend.H @@ -109,18 +109,18 @@ namespace impactx // access reference particle values to find beta*gamma^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; - amrex::ParticleReal const bet = sqrt(betgam2/(1.0_prt + betgam2)); + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const bet = std::sqrt(betgam2/(1.0_prt + betgam2)); // update horizontal and longitudinal phase space variables - amrex::ParticleReal const gx = m_k + pow(m_rc,-2); - amrex::ParticleReal const omegax = sqrt(std::abs(gx)); + amrex::ParticleReal const gx = m_k + std::pow(m_rc,-2); + amrex::ParticleReal const omegax = std::sqrt(std::abs(gx)); if(gx > 0.0) { // calculate expensive terms once auto const [sinx, cosx] = amrex::Math::sincos(omegax * slice_ds); amrex::ParticleReal const r56 = slice_ds/betgam2 - + (sinx - omegax*slice_ds)/(gx*omegax*pow(bet,2)*pow(m_rc,2)); + + (sinx - omegax*slice_ds)/(gx*omegax * std::pow(bet,2) * std::pow(m_rc,2)); // advance position and momentum (focusing) x = cosx*xout + sinx/omegax*px - (1.0_prt - cosx)/(gx*bet*m_rc)*pt; @@ -131,10 +131,10 @@ namespace impactx ptout = pt; } else { // calculate expensive terms once - amrex::ParticleReal const sinhx = sinh(omegax * slice_ds); - amrex::ParticleReal const coshx = cosh(omegax * slice_ds); + amrex::ParticleReal const sinhx = std::sinh(omegax * slice_ds); + amrex::ParticleReal const coshx = std::cosh(omegax * slice_ds); amrex::ParticleReal const r56 = slice_ds/betgam2 - + (sinhx - omegax*slice_ds)/(gx*omegax*pow(bet,2)*pow(m_rc,2)); + + (sinhx - omegax*slice_ds)/(gx*omegax * std::pow(bet,2) * std::pow(m_rc,2)); // advance position and momentum (defocusing) x = coshx*xout + sinhx/omegax*px - (1.0_prt - coshx)/(gx*bet*m_rc)*pt; @@ -147,7 +147,7 @@ namespace impactx // update vertical phase space variables amrex::ParticleReal const gy = -m_k; - amrex::ParticleReal const omegay = sqrt(std::abs(gy)); + amrex::ParticleReal const omegay = std::sqrt(std::abs(gy)); if(gy > 0.0) { // calculate expensive terms once @@ -159,8 +159,8 @@ namespace impactx } else { // calculate expensive terms once - amrex::ParticleReal const sinhy = sinh(omegay * slice_ds); - amrex::ParticleReal const coshy = cosh(omegay * slice_ds); + amrex::ParticleReal const sinhy = std::sinh(omegay * slice_ds); + amrex::ParticleReal const coshy = std::cosh(omegay * slice_ds); // advance position and momentum (defocusing) y = coshy*yout + sinhy/omegay*py; @@ -201,7 +201,7 @@ namespace impactx // assign intermediate parameter amrex::ParticleReal const theta = slice_ds/m_rc; - amrex::ParticleReal const B = sqrt(pow(pt,2)-1.0_prt)/m_rc; + amrex::ParticleReal const B = std::sqrt(std::pow(pt,2)-1.0_prt)/m_rc; // calculate expensive terms once auto const [sin_theta, cos_theta] = amrex::Math::sincos(theta); diff --git a/src/particles/elements/ChrDrift.H b/src/particles/elements/ChrDrift.H index 35edd625a..3080b41cf 100644 --- a/src/particles/elements/ChrDrift.H +++ b/src/particles/elements/ChrDrift.H @@ -106,7 +106,7 @@ namespace impactx // compute particle momentum deviation delta + 1 amrex::ParticleReal delta1; - delta1 = sqrt(1_prt - 2_prt*pt/bet + pow(pt,2)); + delta1 = std::sqrt(1_prt - 2_prt*pt/bet + std::pow(pt,2)); // advance transverse position and momentum (drift) x = xout + slice_ds * px / delta1; @@ -115,12 +115,12 @@ namespace impactx // pyout = py; // the corresponding symplectic update to t - amrex::ParticleReal term = 2_prt*pow(pt,2)+pow(px,2)+pow(py,2); - term = 2_prt - 4_prt*bet*pt + pow(bet,2)*term; - term = -2_prt + pow(gam,2)*term; + amrex::ParticleReal term = 2_prt * std::pow(pt,2) + std::pow(px,2) + std::pow(py,2); + term = 2_prt - 4_prt*bet*pt + std::pow(bet,2)*term; + term = -2_prt + std::pow(gam,2)*term; term = (-1_prt+bet*pt)*term; - term = term/(2_prt*pow(bet,3)*pow(gam,2)); - t = tout - slice_ds * (1_prt / bet + term / pow(delta1, 3)); + term = term/(2_prt * std::pow(bet,3) * std::pow(gam,2)); + t = tout - slice_ds * (1_prt / bet + term /std::pow(delta1, 3)); // ptout = pt; // assign updated momenta @@ -156,7 +156,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt,2)-1.0_prt); // advance position and momentum (drift) refpart.x = x + step*px; diff --git a/src/particles/elements/ChrPlasmaLens.H b/src/particles/elements/ChrPlasmaLens.H index 0d95d7583..ca389b2e4 100644 --- a/src/particles/elements/ChrPlasmaLens.H +++ b/src/particles/elements/ChrPlasmaLens.H @@ -110,12 +110,12 @@ namespace impactx // compute particle momentum deviation delta + 1 amrex::ParticleReal delta1; - delta1 = sqrt(1_prt - 2_prt*pt/bet + pow(pt,2)); + delta1 = std::sqrt(1_prt - 2_prt*pt/bet + std::pow(pt,2)); amrex::ParticleReal const delta = delta1 - 1_prt; // compute phase advance per unit length in s (in rad/m) // chromatic dependence on delta is included - amrex::ParticleReal const omega = sqrt(std::abs(g)/delta1); + amrex::ParticleReal const omega = std::sqrt(std::abs(g)/delta1); // initialize output values amrex::ParticleReal xout = x; @@ -148,13 +148,13 @@ namespace impactx amrex::ParticleReal const t0 = t - term*slice_ds/delta1; amrex::ParticleReal const w = omega*delta1; - amrex::ParticleReal const term1 = -(pow(p2,2)-pow(q2,2)*pow(w,2))*sin(2_prt*slice_ds*omega); - amrex::ParticleReal const term2 = -(pow(p1,2)-pow(q1,2)*pow(w,2))*sin(2_prt*slice_ds*omega); - amrex::ParticleReal const term3 = -2_prt*q2*p2*w*cos(2_prt*slice_ds*omega); - amrex::ParticleReal const term4 = -2_prt*q1*p1*w*cos(2_prt*slice_ds*omega); + amrex::ParticleReal const term1 = -(std::pow(p2,2)-pow(q2,2) * std::pow(w,2)) * std::sin(2_prt*slice_ds*omega); + amrex::ParticleReal const term2 = -(std::pow(p1,2)-pow(q1,2) * std::pow(w,2)) * std::sin(2_prt*slice_ds*omega); + amrex::ParticleReal const term3 = -2_prt*q2*p2*w * std::cos(2_prt*slice_ds*omega); + amrex::ParticleReal const term4 = -2_prt*q1*p1*w * std::cos(2_prt*slice_ds*omega); amrex::ParticleReal const term5 = 2_prt*omega*(q1*p1*delta1 + q2*p2*delta1 - -(pow(p1,2)+pow(p2,2))*slice_ds - (pow(q1,2)+pow(q2,2))*pow(w,2)*slice_ds); - t = t0 + (-1_prt+bet*pt)/(8_prt*bet*pow(delta1,3)*omega) + -(std::pow(p1,2) + std::pow(p2,2))*slice_ds - (std::pow(q1,2) + std::pow(q2,2)) * std::pow(w,2)*slice_ds); + t = t0 + (-1_prt+bet*pt)/(8_prt*bet * std::pow(delta1,3)*omega) *(term1+term2+term3+term4+term5); // ptout = pt; @@ -194,7 +194,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt,2)-1.0_prt); // advance position and momentum (straight element) refpart.x = x + step*px; diff --git a/src/particles/elements/ChrQuad.H b/src/particles/elements/ChrQuad.H index 2c86cc809..04a0787f4 100644 --- a/src/particles/elements/ChrQuad.H +++ b/src/particles/elements/ChrQuad.H @@ -118,12 +118,12 @@ namespace impactx // compute particle momentum deviation delta + 1 amrex::ParticleReal delta1; - delta1 = sqrt(1_prt - 2_prt*pt/bet + pow(pt,2)); + delta1 = std::sqrt(1_prt - 2_prt*pt/bet + std::pow(pt,2)); amrex::ParticleReal const delta = delta1 - 1_prt; // compute phase advance per unit length in s (in rad/m) // chromatic dependence on delta is included - amrex::ParticleReal const omega = sqrt(std::abs(g)/delta1); + amrex::ParticleReal const omega = std::sqrt(std::abs(g)/delta1); // intialize output values of momenta amrex::ParticleReal pxout = px; @@ -138,23 +138,23 @@ namespace impactx if(g > 0.0) { // advance transverse position and momentum (focusing quad) - x = cos(omega*slice_ds) * xout + - sin(omega*slice_ds)/(omega*delta1)*px; - pxout = -omega * delta1 * sin(omega*slice_ds) * xout + cos(omega * slice_ds) * px; + x = std::cos(omega*slice_ds) * xout + + std::sin(omega*slice_ds)/(omega*delta1)*px; + pxout = -omega * delta1 *std::sin(omega*slice_ds) * xout + std::cos(omega * slice_ds) * px; - y = cosh(omega*slice_ds) * yout + - sinh(omega*slice_ds)/(omega*delta1)*py; - pyout = omega * delta1 * sinh(omega*slice_ds) * yout + cosh(omega * slice_ds) * py; + y = std::cosh(omega*slice_ds) * yout + + std::sinh(omega*slice_ds)/(omega*delta1)*py; + pyout = omega * delta1 * std::sinh(omega*slice_ds) * yout + std::cosh(omega * slice_ds) * py; } else { // advance transverse position and momentum (defocusing quad) - x = cosh(omega*slice_ds) * xout + - sinh(omega*slice_ds)/(omega*delta1)*px; - pxout = omega * delta1 * sinh(omega*slice_ds) * xout + cosh(omega * slice_ds) * px; + x = std::cosh(omega*slice_ds) * xout + + std::sinh(omega*slice_ds)/(omega*delta1)*px; + pxout = omega * delta1 * std::sinh(omega*slice_ds) * xout + std::cosh(omega * slice_ds) * px; - y = cos(omega*slice_ds) * yout + - sin(omega*slice_ds)/(omega*delta1)*py; - pyout = -omega * delta1 * sin(omega*slice_ds) * yout + cos(omega * slice_ds) * py; + y = std::cos(omega*slice_ds) * yout + + std::sin(omega*slice_ds)/(omega*delta1)*py; + pyout = -omega * delta1 *std::sin(omega*slice_ds) * yout + std::cos(omega * slice_ds) * py; q1 = yout; q2 = xout; @@ -169,13 +169,13 @@ namespace impactx amrex::ParticleReal const t0 = tout - term * slice_ds / delta1; amrex::ParticleReal const w = omega*delta1; - amrex::ParticleReal const term1 = -(pow(p2,2)+pow(q2,2)*pow(w,2))*sinh(2_prt*slice_ds*omega); - amrex::ParticleReal const term2 = -(pow(p1,2)-pow(q1,2)*pow(w,2))*sin(2_prt*slice_ds*omega); - amrex::ParticleReal const term3 = -2_prt*q2*p2*w*cosh(2_prt*slice_ds*omega); - amrex::ParticleReal const term4 = -2_prt*q1*p1*w*cos(2_prt*slice_ds*omega); + amrex::ParticleReal const term1 = -(std::pow(p2,2) + std::pow(q2,2) * std::pow(w,2))*std::sinh(2_prt*slice_ds*omega); + amrex::ParticleReal const term2 = -(std::pow(p1,2)-pow(q1,2) * std::pow(w,2)) * std::sin(2_prt*slice_ds*omega); + amrex::ParticleReal const term3 = -2_prt*q2*p2*w*std::cosh(2_prt*slice_ds*omega); + amrex::ParticleReal const term4 = -2_prt*q1*p1*w * std::cos(2_prt*slice_ds*omega); amrex::ParticleReal const term5 = 2_prt*omega*(q1*p1*delta1 + q2*p2*delta1 - -(pow(p1,2)+pow(p2,2))*slice_ds - (pow(q1,2)-pow(q2,2))*pow(w,2)*slice_ds); - t = t0 + (-1_prt+bet*pt)/(8_prt*bet*pow(delta1,3)*omega) + -(std::pow(p1,2) + std::pow(p2,2))*slice_ds - (std::pow(q1,2)-pow(q2,2)) * std::pow(w,2)*slice_ds); + t = t0 + (-1_prt+bet*pt)/(8_prt*bet * std::pow(delta1,3)*omega) *(term1+term2+term3+term4+term5); // ptout = pt; @@ -213,7 +213,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt,2)-1.0_prt); // advance position and momentum (straight element) refpart.x = x + step*px; diff --git a/src/particles/elements/ChrUniformAcc.H b/src/particles/elements/ChrUniformAcc.H index 465b480c4..458f955a1 100644 --- a/src/particles/elements/ChrUniformAcc.H +++ b/src/particles/elements/ChrUniformAcc.H @@ -100,8 +100,8 @@ namespace impactx // access reference particle values (final, initial): amrex::ParticleReal const ptf_ref = refpart.pt; amrex::ParticleReal const pti_ref = ptf_ref + m_ez*slice_ds; - amrex::ParticleReal const bgf = sqrt(pow(ptf_ref, 2) - 1.0_prt); - amrex::ParticleReal const bgi = sqrt(pow(pti_ref, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf_ref, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pti_ref, 2) - 1.0_prt); // initial conversion from static to dynamic units: px = px*bgi; @@ -111,17 +111,17 @@ namespace impactx // compute intermediate quantities related to acceleration amrex::ParticleReal const pti_tot = pti_ref + pt; amrex::ParticleReal const ptf_tot = ptf_ref + pt; - amrex::ParticleReal const pzi_tot = sqrt(pow(pti_tot,2)-1_prt); - amrex::ParticleReal const pzf_tot = sqrt(pow(ptf_tot,2)-1_prt); - amrex::ParticleReal const pzi_ref = sqrt(pow(pti_ref,2)-1_prt); - amrex::ParticleReal const pzf_ref = sqrt(pow(ptf_ref,2)-1_prt); + amrex::ParticleReal const pzi_tot = std::sqrt(std::pow(pti_tot,2)-1_prt); + amrex::ParticleReal const pzf_tot = std::sqrt(std::pow(ptf_tot,2)-1_prt); + amrex::ParticleReal const pzi_ref = std::sqrt(std::pow(pti_ref,2)-1_prt); + amrex::ParticleReal const pzf_ref = std::sqrt(std::pow(ptf_ref,2)-1_prt); amrex::ParticleReal const numer = -ptf_tot + pzf_tot; amrex::ParticleReal const denom = -pti_tot + pzi_tot; // compute focusing constant (1/m) and rotation angle (in rad) amrex::ParticleReal const alpha = m_bz/2.0_prt; - amrex::ParticleReal const theta = alpha/m_ez*log(numer/denom); + amrex::ParticleReal const theta = alpha/m_ez*std::log(numer/denom); // intialize output values amrex::ParticleReal xout = x; @@ -132,15 +132,15 @@ namespace impactx amrex::ParticleReal ptout = pt; // advance positions and momenta using map for focusing - xout = cos(theta)*x + sin(theta)/alpha*px; - pxout = -alpha*sin(theta)*x + cos(theta)*px; + xout = std::cos(theta)*x + std::sin(theta)/alpha*px; + pxout = -alpha * std::sin(theta)*x + std::cos(theta)*px; - yout = cos(theta)*y + sin(theta)/alpha*py; - pyout = -alpha*sin(theta)*y + cos(theta)*py; + yout = std::cos(theta)*y + std::sin(theta)/alpha*py; + pyout = -alpha * std::sin(theta)*y + std::cos(theta)*py; // the correct symplectic update for t tout = t + (pzf_tot - pzf_ref - pzi_tot + pzi_ref)/m_ez; - tout = tout + (1_prt/pzi_tot - 1_prt/pzf_tot)*(pow(py-alpha*x,2)+pow(px+alpha*y,2))/(2_prt*m_ez); + tout = tout + (1_prt/pzi_tot - 1_prt/pzf_tot)*(std::pow(py-alpha*x,2) + std::pow(px+alpha*y,2))/(2_prt*m_ez); ptout = pt; // assign intermediate momenta @@ -149,11 +149,11 @@ namespace impactx pt = ptout; // advance positions and momenta using map for rotation - x = cos(theta)*xout + sin(theta)*yout; - pxout = cos(theta)*px + sin(theta)*py; + x = std::cos(theta)*xout + std::sin(theta)*yout; + pxout = std::cos(theta)*px + std::sin(theta)*py; - y = -sin(theta)*xout + cos(theta)*yout; - pyout = -sin(theta)*px + cos(theta)*py; + y = -std::sin(theta)*xout + std::cos(theta)*yout; + pyout = -std::sin(theta)*px + std::cos(theta)*py; t = tout; ptout = pt; @@ -196,14 +196,14 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // compute intial value of beta*gamma - amrex::ParticleReal const bgi = sqrt(pow(pt, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pt, 2) - 1.0_prt); // advance pt (uniform acceleration) refpart.pt = pt - m_ez*slice_ds; // compute final value of beta*gamma amrex::ParticleReal const ptf = refpart.pt; - amrex::ParticleReal const bgf = sqrt(pow(ptf, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf, 2) - 1.0_prt); // update t refpart.t = t + (bgf - bgi)/m_ez; diff --git a/src/particles/elements/ConstF.H b/src/particles/elements/ConstF.H index ae1009e08..c8af6217e 100644 --- a/src/particles/elements/ConstF.H +++ b/src/particles/elements/ConstF.H @@ -92,7 +92,7 @@ namespace impactx // access reference particle values to find beta*gamma^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // intialize output values amrex::ParticleReal xout = x; @@ -106,14 +106,14 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // advance position and momentum - xout = cos(m_kx*slice_ds)*x + sin(m_kx*slice_ds)/m_kx*px; - pxout = -m_kx*sin(m_kx*slice_ds)*x + cos(m_kx*slice_ds)*px; + xout = std::cos(m_kx*slice_ds)*x + std::sin(m_kx*slice_ds)/m_kx*px; + pxout = -m_kx * std::sin(m_kx*slice_ds)*x + std::cos(m_kx*slice_ds)*px; - yout = cos(m_ky*slice_ds)*y + sin(m_ky*slice_ds)/m_ky*py; - pyout = -m_ky*sin(m_ky*slice_ds)*y + cos(m_ky*slice_ds)*py; + yout = std::cos(m_ky*slice_ds)*y + std::sin(m_ky*slice_ds)/m_ky*py; + pyout = -m_ky * std::sin(m_ky*slice_ds)*y + std::cos(m_ky*slice_ds)*py; - tout = cos(m_kt*slice_ds)*t + sin(m_kt*slice_ds)/(betgam2*m_kt)*pt; - ptout = -(m_kt*betgam2)*sin(m_kt*slice_ds)*t + cos(m_kt*slice_ds)*pt; + tout = std::cos(m_kt*slice_ds)*t + std::sin(m_kt*slice_ds)/(betgam2*m_kt)*pt; + ptout = -(m_kt*betgam2) * std::sin(m_kt*slice_ds)*t + std::cos(m_kt*slice_ds)*pt; // assign updated values x = xout; @@ -151,7 +151,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt, 2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt, 2)-1.0_prt); // advance position and momentum (straight element) refpart.x = x + step*px; diff --git a/src/particles/elements/DipEdge.H b/src/particles/elements/DipEdge.H index 15dba97bf..ca0bc3b9e 100644 --- a/src/particles/elements/DipEdge.H +++ b/src/particles/elements/DipEdge.H @@ -96,13 +96,13 @@ namespace impactx shift_in(x, y, px, py); // edge focusing matrix elements (zero gap) - amrex::ParticleReal const R21 = tan(m_psi)/m_rc; + amrex::ParticleReal const R21 = std::tan(m_psi)/m_rc; amrex::ParticleReal R43 = -R21; amrex::ParticleReal vf = 0; // first-order effect of nonzero gap - vf = (1.0_prt + pow(sin(m_psi),2))/(pow(cos(m_psi),3)); - vf *= m_g * m_K2/(pow(m_rc,2)); + vf = (1.0_prt + std::pow(sin(m_psi),2))/(std::pow(cos(m_psi),3)); + vf *= m_g * m_K2/(std::pow(m_rc,2)); R43 += vf; // apply edge focusing diff --git a/src/particles/elements/Drift.H b/src/particles/elements/Drift.H index c04c6d230..d20f466c1 100644 --- a/src/particles/elements/Drift.H +++ b/src/particles/elements/Drift.H @@ -97,7 +97,7 @@ namespace impactx // access reference particle values to find beta*gamma^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // advance position and momentum (drift) xout = x + slice_ds * px; @@ -143,7 +143,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt,2)-1.0_prt); // advance position and momentum (drift) refpart.x = x + step*px; diff --git a/src/particles/elements/ExactDrift.H b/src/particles/elements/ExactDrift.H index cf10c12b8..83d7384e7 100644 --- a/src/particles/elements/ExactDrift.H +++ b/src/particles/elements/ExactDrift.H @@ -103,8 +103,8 @@ namespace impactx amrex::ParticleReal const betgam = refpart.beta_gamma(); // compute the radical in the denominator (= pz): - amrex::ParticleReal const pzden = sqrt(pow(pt-1_prt/bet,2) - - 1_prt/pow(betgam,2) - pow(px,2) - pow(py,2)); + amrex::ParticleReal const pzden = std::sqrt(std::pow(pt-1_prt/bet,2) - + 1_prt / std::pow(betgam,2) - std::pow(px,2) - std::pow(py,2)); // advance position and momentum (exact drift) x = xout + slice_ds * px / pzden; @@ -147,7 +147,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds /std::sqrt(std::pow(pt,2)-1.0_prt); // advance position and momentum (drift) refpart.x = x + step*px; diff --git a/src/particles/elements/ExactSbend.H b/src/particles/elements/ExactSbend.H index dbef6bc58..ad2c6a5fd 100644 --- a/src/particles/elements/ExactSbend.H +++ b/src/particles/elements/ExactSbend.H @@ -131,8 +131,8 @@ namespace impactx amrex::ParticleReal ptout = pt; // assign intermediate quantities - amrex::ParticleReal const pperp = sqrt(pow(pt,2)-2.0_prt/bet*pt-pow(py,2)+1.0_prt); - amrex::ParticleReal const pzi = sqrt(pow(pperp,2)-pow(px,2)); + amrex::ParticleReal const pperp = std::sqrt(std::pow(pt,2)-2.0_prt/bet*pt-pow(py,2)+1.0_prt); + amrex::ParticleReal const pzi = std::sqrt(std::pow(pperp,2) - std::pow(px,2)); amrex::ParticleReal const rho = rc + xout; auto const [sin_phi, cos_phi] = amrex::Math::sincos(slice_phi); @@ -142,8 +142,8 @@ namespace impactx ptout = pt; // angle of momentum rotation - amrex::ParticleReal const pzf = sqrt(pow(pperp,2)-pow(pxout,2)); - amrex::ParticleReal const theta = slice_phi + asin(px/pperp) - asin(pxout/pperp); + amrex::ParticleReal const pzf = std::sqrt(std::pow(pperp,2)-pow(pxout,2)); + amrex::ParticleReal const theta = slice_phi + std::asin(px/pperp) - std::asin(pxout/pperp); // update position coordinates x = -rc + rho*cos_phi + rc*(pzf + px*sin_phi - pzi*cos_phi); diff --git a/src/particles/elements/Multipole.H b/src/particles/elements/Multipole.H index 04791d482..c0e9fa693 100644 --- a/src/particles/elements/Multipole.H +++ b/src/particles/elements/Multipole.H @@ -98,7 +98,7 @@ namespace impactx // access reference particle values to find (beta*gamma)^2 //amrex::ParticleReal const pt_ref = refpart.pt; - //amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + //amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // intialize output values amrex::ParticleReal xout = x; diff --git a/src/particles/elements/NonlinearLens.H b/src/particles/elements/NonlinearLens.H index 93940b067..9dfa0df40 100644 --- a/src/particles/elements/NonlinearLens.H +++ b/src/particles/elements/NonlinearLens.H @@ -95,7 +95,7 @@ namespace impactx // access reference particle values to find (beta*gamma)^2 //amrex::ParticleReal const pt_ref = refpart.pt; - //amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + //amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // intialize output values amrex::ParticleReal xout = x; @@ -117,12 +117,12 @@ namespace impactx croot = amrex::sqrt(croot); // compute carcsin = arcsin(zeta) - Complex carcsin = im1*zeta + croot; - carcsin = -im1*amrex::log(carcsin); + Complex carcsin = im1 * zeta + croot; + carcsin = -im1 * amrex::log(carcsin); // compute complex function F'(zeta) - Complex dF = zeta/amrex::pow(croot, 2); - dF = dF + carcsin/amrex::pow(croot,3); + Complex dF = zeta / amrex::pow(croot, 2); + dF = dF + carcsin / amrex::pow(croot, 3); // compute momentum kick amrex::ParticleReal const kick = -m_knll/m_cnll; diff --git a/src/particles/elements/PRot.H b/src/particles/elements/PRot.H index ed21e74be..d08e7c009 100644 --- a/src/particles/elements/PRot.H +++ b/src/particles/elements/PRot.H @@ -97,8 +97,8 @@ namespace impactx auto const [sin_theta, cos_theta] = amrex::Math::sincos(theta); auto const [sin_phi_in, cos_phi_in] = amrex::Math::sincos(m_phi_in); - amrex::ParticleReal const pz = sqrt(1.0_prt - 2.0_prt*pt/beta - + pow(pt,2) - pow(py,2) - pow(px + sin_phi_in,2)); + amrex::ParticleReal const pz = std::sqrt(1.0_prt - 2.0_prt*pt/beta + + std::pow(pt,2) - std::pow(py,2) - std::pow(px + sin_phi_in,2)); amrex::ParticleReal const pzf = pz*cos_theta - (px + sin_phi_in)*sin_theta; // advance position and momentum diff --git a/src/particles/elements/RFCavity.H b/src/particles/elements/RFCavity.H index 6db8c9a12..2a83a5d66 100644 --- a/src/particles/elements/RFCavity.H +++ b/src/particles/elements/RFCavity.H @@ -282,7 +282,7 @@ namespace RFCavityData amrex::ParticleReal const slice_ds = m_ds / nslice(); // compute intial value of beta*gamma - amrex::ParticleReal const bgi = sqrt(pow(pt, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pt, 2) - 1.0_prt); // call integrator to advance (t,pt) amrex::ParticleReal const zin = s - sedge; @@ -298,7 +298,7 @@ namespace RFCavityData refpart.z = z + slice_ds*pz/bgi; // compute final value of beta*gamma - amrex::ParticleReal const bgf = sqrt(pow(ptf, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf, 2) - 1.0_prt); // advance momentum (px,py,pz) refpart.px = px*bgf/bgi; @@ -367,14 +367,14 @@ namespace RFCavityData efieldint = z*efield; for (int j=1; j < m_ncoef; ++j) { - efield = efield + cos_data[j]*cos(j*2*pi*z/zlen) + - sin_data[j]*sin(j*2*pi*z/zlen); - efieldp = efieldp-j*2*pi*cos_data[j]*sin(j*2*pi*z/zlen)/zlen + - j*2*pi*sin_data[j]*cos(j*2*pi*z/zlen)/zlen; - efieldpp = efieldpp- pow(j*2*pi*cos_data[j]/zlen,2) *cos(j*2*pi*z/zlen) - - pow(j*2*pi*sin_data[j]/zlen,2) *sin(j*2*pi*z/zlen); - efieldint = efieldint + zlen*cos_data[j]*sin(j*2*pi*z/zlen)/(j*2*pi) - - zlen*sin_data[j]*cos(j*2*pi*z/zlen)/(j*2*pi); + efield = efield + cos_data[j] * std::cos(j*2*pi*z/zlen) + + sin_data[j] * std::sin(j*2*pi*z/zlen); + efieldp = efieldp-j*2*pi*cos_data[j] * std::sin(j*2*pi*z/zlen)/zlen + + j*2*pi*sin_data[j] * std::cos(j*2*pi*z/zlen)/zlen; + efieldpp = efieldpp- std::pow(j*2*pi*cos_data[j]/zlen,2) * std::cos(j*2*pi*z/zlen) - + std::pow(j*2*pi*sin_data[j]/zlen,2) * std::sin(j*2*pi*z/zlen); + efieldint = efieldint + zlen*cos_data[j] * std::sin(j*2*pi*z/zlen)/(j*2*pi) - + zlen*sin_data[j] * std::cos(j*2*pi*z/zlen)/(j*2*pi); } } else // endpoint of the RF, outsize zlen @@ -382,7 +382,7 @@ namespace RFCavityData efieldint = std::copysign(z, z)*zmid*0.5_prt*cos_data[0];; for (int j=1; j < m_ncoef; ++j) { - efieldint = efieldint - zlen*sin_data[j]*cos(j*pi)/(j*2*pi); + efieldint = efieldint - zlen*sin_data[j] * std::cos(j*pi)/(j*2*pi); } } return std::make_tuple(efield, efieldp, efieldint); @@ -408,7 +408,7 @@ namespace RFCavityData amrex::ParticleReal const pt = refpart.pt; if (pt < -1.0_prt) { - refpart.t = t + tau/sqrt(1.0_prt - pow(pt, -2)); + refpart.t = t + tau/std::sqrt(1.0_prt - std::pow(pt, -2)); refpart.pt = pt; } else { @@ -459,17 +459,17 @@ namespace RFCavityData // push the linear map equations amrex::Array2D const R = refpart.map; amrex::ParticleReal const s = tau/refpart.beta_gamma(); - amrex::ParticleReal const L = E0*ezp*sin(k*t+phi)/(2.0_prt*k); + amrex::ParticleReal const L = E0*ezp * std::sin(k*t+phi)/(2.0_prt*k); refpart.map(1,1) = (1.0_prt-s*L)*R(1,1) + s*R(2,1); refpart.map(1,2) = (1.0_prt-s*L)*R(1,2) + s*R(2,2); - refpart.map(2,1) = -s*pow(L,2)*R(1,1) + (1.0_prt+s*L)*R(2,1); - refpart.map(2,2) = -s*pow(L,2)*R(1,2) + (1.0_prt+s*L)*R(2,2); + refpart.map(2,1) = -s * std::pow(L,2)*R(1,1) + (1.0_prt+s*L)*R(2,1); + refpart.map(2,2) = -s * std::pow(L,2)*R(1,2) + (1.0_prt+s*L)*R(2,2); refpart.map(3,3) = (1.0_prt-s*L)*R(3,3) + s*R(4,3); refpart.map(3,4) = (1.0_prt-s*L)*R(3,4) + s*R(4,4); - refpart.map(4,3) = -s*pow(L,2)*R(3,3) + (1.0_prt+s*L)*R(4,3); - refpart.map(4,4) = -s*pow(L,2)*R(3,4) + (1.0_prt+s*L)*R(4,4); + refpart.map(4,3) = -s * std::pow(L,2)*R(3,3) + (1.0_prt+s*L)*R(4,3); + refpart.map(4,4) = -s * std::pow(L,2)*R(3,4) + (1.0_prt+s*L)*R(4,4); } /** This pushes the reference particle and the linear map matrix @@ -506,12 +506,12 @@ namespace RFCavityData amrex::ignore_unused(ezf); refpart.t = t; - refpart.pt = pt - E0*(ezintf-ezint)*cos(k*t+phi); + refpart.pt = pt - E0*(ezintf-ezint) * std::cos(k*t+phi); // push the linear map equations amrex::Array2D const R = refpart.map; - amrex::ParticleReal const M = E0*(ezintf-ezint)*k*sin(k*t+phi); - amrex::ParticleReal const L = E0*(ezpf-ezp)*sin(k*t+phi)/(2.0_prt*k)+M/2.0_prt; + amrex::ParticleReal const M = E0*(ezintf-ezint)*k * std::sin(k*t+phi); + amrex::ParticleReal const L = E0*(ezpf-ezp) * std::sin(k*t+phi)/(2.0_prt*k)+M/2.0_prt; refpart.map(2,1) = L*R(1,1) + R(2,1); refpart.map(2,2) = L*R(1,2) + R(2,2); diff --git a/src/particles/elements/Sbend.H b/src/particles/elements/Sbend.H index 605be632b..e4e5c75be 100644 --- a/src/particles/elements/Sbend.H +++ b/src/particles/elements/Sbend.H @@ -103,8 +103,8 @@ namespace impactx // access reference particle values to find beta*gamma^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; - amrex::ParticleReal const bet = sqrt(betgam2/(1.0_prt + betgam2)); + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const bet = std::sqrt(betgam2/(1.0_prt + betgam2)); // calculate expensive terms once amrex::ParticleReal const theta = slice_ds/m_rc; @@ -162,7 +162,7 @@ namespace impactx // assign intermediate parameter amrex::ParticleReal const theta = slice_ds/m_rc; - amrex::ParticleReal const B = sqrt(pow(pt,2)-1.0_prt)/m_rc; + amrex::ParticleReal const B = std::sqrt(std::pow(pt,2)-1.0_prt)/m_rc; // calculate expensive terms once auto const [sin_theta, cos_theta] = amrex::Math::sincos(theta); diff --git a/src/particles/elements/ShortRF.H b/src/particles/elements/ShortRF.H index b7fa316f4..543a66e27 100644 --- a/src/particles/elements/ShortRF.H +++ b/src/particles/elements/ShortRF.H @@ -99,9 +99,9 @@ namespace impactx // access reference particle values (final, initial): amrex::ParticleReal const ptf_ref = refpart.pt; - amrex::ParticleReal const pti_ref = ptf_ref + m_V*cos(phi); - amrex::ParticleReal const bgf = sqrt(pow(ptf_ref, 2) - 1.0_prt); - amrex::ParticleReal const bgi = sqrt(pow(pti_ref, 2) - 1.0_prt); + amrex::ParticleReal const pti_ref = ptf_ref + m_V * std::cos(phi); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf_ref, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pti_ref, 2) - 1.0_prt); // initial conversion from static to dynamic units: px = px*bgi; @@ -124,7 +124,7 @@ namespace impactx pyout = py; // tout = t; - ptout = pt - m_V*cos(k*t + phi) + m_V*cos(phi); + ptout = pt - m_V * std::cos(k*t + phi) + m_V * std::cos(phi); // assign updated values x = xout; @@ -170,14 +170,14 @@ namespace impactx amrex::ParticleReal const phi = m_phase*(pi/180.0_prt); // compute intial value of beta*gamma - amrex::ParticleReal const bgi = sqrt(pow(pt, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pt, 2) - 1.0_prt); // advance pt - refpart.pt = pt - m_V*cos(phi); + refpart.pt = pt - m_V * std::cos(phi); // compute final value of beta*gamma amrex::ParticleReal const ptf = refpart.pt; - amrex::ParticleReal const bgf = sqrt(pow(ptf, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf, 2) - 1.0_prt); // advance position (x,y,z,t) refpart.x = x; diff --git a/src/particles/elements/SoftQuad.H b/src/particles/elements/SoftQuad.H index 89a6c4755..cfd50eb44 100644 --- a/src/particles/elements/SoftQuad.H +++ b/src/particles/elements/SoftQuad.H @@ -285,7 +285,7 @@ namespace SoftQuadrupoleData amrex::ParticleReal const slice_ds = m_ds / nslice(); // compute intial value of beta*gamma - amrex::ParticleReal const bgi = sqrt(pow(pt, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pt, 2) - 1.0_prt); // call integrator to advance (t,pt) amrex::ParticleReal const zin = s - sedge; @@ -312,7 +312,7 @@ namespace SoftQuadrupoleData refpart.z = z + slice_ds*pz/bgi; // compute final value of beta*gamma - amrex::ParticleReal const bgf = sqrt(pow(ptf, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf, 2) - 1.0_prt); // advance momentum (px,py,pz) refpart.px = px*bgf/bgi; @@ -362,12 +362,12 @@ namespace SoftQuadrupoleData bfieldint = z*bfield; for (int j=1; j < m_ncoef; ++j) { - bfield = bfield + cos_data[j] * cos(j * 2 * pi * z / zlen) + - sin_data[j] * sin(j * 2 * pi * z / zlen); - bfieldp = bfieldp - j * 2 * pi * cos_data[j] * sin(j * 2 * pi * z / zlen) / zlen + - j * 2 * pi * sin_data[j] * cos(j * 2 * pi * z / zlen) / zlen; - bfieldint = bfieldint + zlen * cos_data[j] * sin(j * 2 * pi * z / zlen) / (j * 2 * pi) - - zlen * sin_data[j] * cos(j * 2 * pi * z / zlen) / (j * 2 * pi); + bfield = bfield + cos_data[j] *std::cos(j * 2 * pi * z / zlen) + + sin_data[j] *std::sin(j * 2 * pi * z / zlen); + bfieldp = bfieldp - j * 2 * pi * cos_data[j] *std::sin(j * 2 * pi * z / zlen) / zlen + + j * 2 * pi * sin_data[j] *std::cos(j * 2 * pi * z / zlen) / zlen; + bfieldint = bfieldint + zlen * cos_data[j] *std::sin(j * 2 * pi * z / zlen) / (j * 2 * pi) - + zlen * sin_data[j] *std::cos(j * 2 * pi * z / zlen) / (j * 2 * pi); } } return std::make_tuple(bfield, bfieldp, bfieldint); @@ -394,7 +394,7 @@ namespace SoftQuadrupoleData amrex::ParticleReal const z = zeval; if (pt < -1.0_prt) { - refpart.t = t + tau/sqrt(1.0_prt - pow(pt, -2)); + refpart.t = t + tau/std::sqrt(1.0_prt - std::pow(pt, -2)); refpart.pt = pt; } else { diff --git a/src/particles/elements/SoftSol.H b/src/particles/elements/SoftSol.H index e3f79efc2..0151fc9c6 100644 --- a/src/particles/elements/SoftSol.H +++ b/src/particles/elements/SoftSol.H @@ -296,7 +296,7 @@ namespace SoftSolenoidData amrex::ParticleReal const slice_ds = m_ds / nslice(); // compute intial value of beta*gamma - amrex::ParticleReal const bgi = sqrt(pow(pt, 2) - 1.0_prt); + amrex::ParticleReal const bgi = std::sqrt(std::pow(pt, 2) - 1.0_prt); // call integrator to advance (t,pt) amrex::ParticleReal const zin = s - sedge; @@ -321,7 +321,7 @@ namespace SoftSolenoidData refpart.z = z + slice_ds*pz/bgi; // compute final value of beta*gamma - amrex::ParticleReal const bgf = sqrt(pow(ptf, 2) - 1.0_prt); + amrex::ParticleReal const bgf = std::sqrt(std::pow(ptf, 2) - 1.0_prt); // advance momentum (px,py,pz) refpart.px = px*bgf/bgi; @@ -371,12 +371,12 @@ namespace SoftSolenoidData bfieldint = z*bfield; for (int j=1; j < m_ncoef; ++j) { - bfield = bfield + cos_data[j]*cos(j*2*pi*z/zlen) + - sin_data[j]*sin(j*2*pi*z/zlen); - bfieldp = bfieldp-j*2*pi*cos_data[j]*sin(j*2*pi*z/zlen)/zlen + - j*2*pi*sin_data[j]*cos(j*2*pi*z/zlen)/zlen; - bfieldint = bfieldint + zlen*cos_data[j]*sin(j*2*pi*z/zlen)/(j*2*pi) - - zlen*sin_data[j]*cos(j*2*pi*z/zlen)/(j*2*pi); + bfield = bfield + cos_data[j] * std::cos(j*2*pi*z/zlen) + + sin_data[j] * std::sin(j*2*pi*z/zlen); + bfieldp = bfieldp-j*2*pi*cos_data[j] * std::sin(j*2*pi*z/zlen)/zlen + + j*2*pi*sin_data[j] * std::cos(j*2*pi*z/zlen)/zlen; + bfieldint = bfieldint + zlen*cos_data[j] * std::sin(j*2*pi*z/zlen)/(j*2*pi) - + zlen*sin_data[j] * std::cos(j*2*pi*z/zlen)/(j*2*pi); } } return std::make_tuple(bfield, bfieldp, bfieldint); @@ -403,7 +403,7 @@ namespace SoftSolenoidData amrex::ParticleReal const z = zeval; if (pt < -1.0_prt) { - refpart.t = t + tau/sqrt(1.0_prt - pow(pt, -2)); + refpart.t = t + tau/std::sqrt(1.0_prt - std::pow(pt, -2)); refpart.pt = pt; } else { @@ -466,7 +466,7 @@ namespace SoftSolenoidData // push the linear map equations amrex::Array2D const R = refpart.map; amrex::ParticleReal const alpha = B0*bz/2.0_prt; - amrex::ParticleReal const alpha2 = pow(alpha,2); + amrex::ParticleReal const alpha2 = std::pow(alpha,2); refpart.map(2,1) = R(2,1) - tau*alpha2*R(1,1); refpart.map(2,2) = R(2,2) - tau*alpha2*R(1,2); @@ -515,8 +515,8 @@ namespace SoftSolenoidData // push the linear map equations amrex::Array2D const R = refpart.map; amrex::ParticleReal const theta = tau*B0*bz/2.0_prt; - amrex::ParticleReal const cs = cos(theta); - amrex::ParticleReal const sn = sin(theta); + amrex::ParticleReal const cs = std::cos(theta); + amrex::ParticleReal const sn = std::sin(theta); refpart.map(1,1) = R(1,1)*cs + R(3,1)*sn; refpart.map(1,2) = R(1,2)*cs + R(3,2)*sn; diff --git a/src/particles/elements/Sol.H b/src/particles/elements/Sol.H index 3753aafff..ab772f808 100644 --- a/src/particles/elements/Sol.H +++ b/src/particles/elements/Sol.H @@ -94,7 +94,7 @@ namespace impactx // access reference particle values to find beta*gamma^2 amrex::ParticleReal const pt_ref = refpart.pt; - amrex::ParticleReal const betgam2 = pow(pt_ref, 2) - 1.0_prt; + amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // compute phase advance per unit length (in rad/m) and // rotation angle (in rad) @@ -168,7 +168,7 @@ namespace impactx amrex::ParticleReal const slice_ds = m_ds / nslice(); // assign intermediate parameter - amrex::ParticleReal const step = slice_ds / sqrt(pow(pt,2)-1.0_prt); + amrex::ParticleReal const step = slice_ds / std::sqrt(std::pow(pt,2) - 1.0_prt); // advance position and momentum (straight element) refpart.x = x + step*px; diff --git a/src/particles/elements/ThinDipole.H b/src/particles/elements/ThinDipole.H index ecfd78e0d..5bd1e6633 100644 --- a/src/particles/elements/ThinDipole.H +++ b/src/particles/elements/ThinDipole.H @@ -104,7 +104,7 @@ namespace impactx amrex::ParticleReal ptout = pt; // compute the function expressing dp/p in terms of pt (labeled f in Ripken etc.) - amrex::ParticleReal f = -1.0_prt + sqrt(1.0_prt - 2.0_prt*pt/beta_ref + pow(pt,2)); + amrex::ParticleReal f = -1.0_prt + std::sqrt(1.0_prt - 2.0_prt*pt/beta_ref + std::pow(pt,2)); amrex::ParticleReal fprime = (1.0_prt - beta_ref*pt)/(beta_ref*(1.0_prt + f)); // compute the effective (equivalent) arc length and curvature @@ -113,7 +113,7 @@ namespace impactx // advance position and momentum x = xout; - pxout = px - pow(kx,2) * ds * xout + kx * ds * f; //eq (3.2b) + pxout = px - std::pow(kx,2) * ds * xout + kx * ds * f; //eq (3.2b) y = yout; pyout = py; diff --git a/src/particles/integrators/Integrators.H b/src/particles/integrators/Integrators.H index 636f04384..1bd770baf 100644 --- a/src/particles/integrators/Integrators.H +++ b/src/particles/integrators/Integrators.H @@ -137,7 +137,7 @@ namespace impactx::integrators // initialize numerical integration parameters amrex::ParticleReal const dz = (zout-zin)/nsteps; - amrex::ParticleReal const alpha = 1.0_prt - pow(2.0_prt,1.0/3.0); + amrex::ParticleReal const alpha = 1.0_prt - std::pow(2.0_prt,1.0/3.0); amrex::ParticleReal const tau2 = dz/(1.0_prt + alpha); amrex::ParticleReal const tau1 = tau2/2.0_prt; amrex::ParticleReal const tau3 = alpha*tau1; diff --git a/src/particles/transformation/CoordinateTransformation.cpp b/src/particles/transformation/CoordinateTransformation.cpp index cbf105533..299c95972 100644 --- a/src/particles/transformation/CoordinateTransformation.cpp +++ b/src/particles/transformation/CoordinateTransformation.cpp @@ -62,7 +62,7 @@ namespace impactx::transformation amrex::ParticleReal *const AMREX_RESTRICT part_pz = soa_real[RealSoA::pz].dataPtr(); // Design value of pz/mc = beta*gamma - amrex::ParticleReal const pzd = sqrt(pow(pd, 2) - 1.0); + amrex::ParticleReal const pzd = std::sqrt(std::pow(pd, 2) - 1.0); ToFixedS const to_s(pzd); amrex::ParallelFor(np, [=] AMREX_GPU_DEVICE(long i) { diff --git a/src/particles/transformation/ToFixedS.H b/src/particles/transformation/ToFixedS.H index 10cb1b297..e18d4b136 100644 --- a/src/particles/transformation/ToFixedS.H +++ b/src/particles/transformation/ToFixedS.H @@ -59,9 +59,9 @@ namespace impactx::transformation using namespace amrex::literals; // compute value of reference ptd = -gamma - amrex::ParticleReal const argd = 1.0_prt + pow(m_pzd, 2); + amrex::ParticleReal const argd = 1.0_prt + std::pow(m_pzd, 2); AMREX_ASSERT_WITH_MESSAGE(argd > 0.0_prt, "invalid ptd arg (<=0)"); - amrex::ParticleReal const ptdf = argd > 0.0_prt ? -sqrt(argd) : -1.0_prt; + amrex::ParticleReal const ptdf = argd > 0.0_prt ? -std::sqrt(argd) : -1.0_prt; // transform momenta to dynamic units (e.g., so that momenta are // normalized by mc): @@ -70,9 +70,9 @@ namespace impactx::transformation pz = pz * m_pzd; // compute value of particle pt = -gamma - amrex::ParticleReal const arg = 1.0_prt + pow(m_pzd + pz, 2) + pow(px, 2) + pow(py, 2); + amrex::ParticleReal const arg = 1.0_prt + std::pow(m_pzd + pz, 2) + std::pow(px, 2) + std::pow(py, 2); AMREX_ASSERT_WITH_MESSAGE(arg > 0.0_prt, "invalid pt arg (<=0)"); - amrex::ParticleReal const ptf = arg > 0.0_prt ? -sqrt(arg) : -1.0_prt; + amrex::ParticleReal const ptf = arg > 0.0_prt ? -std::sqrt(arg) : -1.0_prt; // transform position and momentum (from fixed t to fixed s) x = x - px * z / (m_pzd + pz); diff --git a/src/particles/transformation/ToFixedT.H b/src/particles/transformation/ToFixedT.H index 577436ea3..af90cc132 100644 --- a/src/particles/transformation/ToFixedT.H +++ b/src/particles/transformation/ToFixedT.H @@ -62,9 +62,9 @@ namespace impactx::transformation constexpr amrex::ParticleReal tol = 1.0e-8_prt; // compute value of reference pzd = beta*gamma - amrex::ParticleReal const argd = -1.0_prt + pow(m_ptd, 2); + amrex::ParticleReal const argd = -1.0_prt + std::pow(m_ptd, 2); // AMREX_ASSERT_WITH_MESSAGE(argd > 0.0_prt, "invalid pzd arg (<=0)"); - amrex::ParticleReal const pzdf = argd > 0.0_prt ? sqrt(argd) : tol; + amrex::ParticleReal const pzdf = argd > 0.0_prt ? std::sqrt(argd) : tol; // transform momenta to dynamic units (eg, so that momenta are // normalized by mc): @@ -73,9 +73,9 @@ namespace impactx::transformation pt = pt * pzdf; // compute value of particle pz = beta*gamma - amrex::ParticleReal const arg = -1.0_prt + pow(m_ptd+pt, 2) - pow(px, 2) - pow(py, 2); + amrex::ParticleReal const arg = -1.0_prt + std::pow(m_ptd+pt, 2) - std::pow(px, 2) - std::pow(py, 2); // AMREX_ASSERT_WITH_MESSAGE(arg > 0.0_prt, "invalid pz arg (<=0)"); - amrex::ParticleReal const pzf = arg > 0.0_prt ? sqrt(arg) : tol; + amrex::ParticleReal const pzf = arg > 0.0_prt ? std::sqrt(arg) : tol; // transform position and momentum (from fixed s to fixed t) x = x + px*t/(m_ptd+pt); From b42e5f9d730e4295554856fda984e82b02d45654 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 26 Sep 2024 14:41:00 -0700 Subject: [PATCH 15/30] Optionally Name all Elements, Add Marker (#705) * New Element: Marker * Add a `name` to all Elements * Python Bindings: Update for `name` * MAD-X Parser: Support User-Defined Name * Element `name`: Update all Python Examples * `Named` Mixing Class: 40 -> 8 Bytes Make smaller, because we will always copy the whole class to GPU, even though we do not access the name member on it. * Elements: `name=None` last * Marker: Name is not optional * Python Elements: Consistently add `Named` Baseclass This binds the name getters/setters in Python. * `Named`: Enable Name Reset after Constructor * Doc: Python Elements have `name=None` now --- docs/source/usage/python.rst | 75 ++- .../run_spectrometer.py | 8 +- examples/aperture/run_aperture.py | 6 +- examples/apochromatic/run_apochromatic.py | 20 +- examples/apochromatic/run_apochromatic_pl.py | 32 +- examples/cfchannel/run_cfchannel.py | 2 +- examples/cfchannel/run_cfchannel_10nC_fft.py | 2 +- examples/cfchannel/run_cfchannel_10nC_mlmg.py | 2 +- examples/chicane/run_chicane.py | 14 +- examples/chicane/run_chicane_csr.py | 14 +- examples/compression/run_compression.py | 4 +- examples/cyclotron/run_cyclotron.py | 8 +- examples/distgen/run_gaussian_twiss.py | 10 +- examples/distgen/run_kurth4d.py | 2 +- examples/distgen/run_kvdist_twiss.py | 10 +- examples/distgen/run_semigaussian.py | 10 +- examples/dogleg/run_dogleg.py | 12 +- examples/epac2004_benchmarks/run_bithermal.py | 1 + .../epac2004_benchmarks/run_fodo_rf_SC.py | 8 +- examples/expanding_beam/run_expanding_fft.py | 2 +- examples/expanding_beam/run_expanding_mlmg.py | 2 +- examples/fodo/run_fodo.py | 10 +- examples/fodo/run_fodo_twiss.py | 10 +- examples/fodo_chromatic/run_fodo_chr.py | 10 +- .../run_fodo_programmable.py | 14 +- examples/fodo_rf/run_fodo_rf.py | 8 +- examples/fodo_tune/run_fodo_tune.py | 10 +- .../initialize_from_array/run_from_array.py | 2 +- examples/iota_lattice/run_iotalattice.py | 86 +-- examples/iota_lattice/run_iotalattice_sdep.py | 90 +-- examples/iota_lens/run_iotalens.py | 6 +- examples/iota_lens/run_iotalens_sdep.py | 11 +- examples/kicker/run_kicker.py | 4 +- examples/kurth/run_kurth_10nC_periodic.py | 4 +- examples/kurth/run_kurth_periodic.py | 4 +- examples/multipole/run_multipole.py | 8 +- examples/optimize_triplet/run_triplet.py | 14 +- examples/positron_channel/run_positron.py | 18 +- .../run_quadrupole_softedge.py | 6 +- examples/rfcavity/run_rfcavity.py | 5 +- examples/rotation/run_rotation.py | 6 +- examples/solenoid/run_solenoid.py | 2 +- .../run_solenoid_softedge.py | 1 + examples/thin_dipole/run_thin_dipole.py | 10 +- src/initialization/InitElement.cpp | 50 +- src/particles/elements/All.H | 2 + src/particles/elements/Aperture.H | 15 +- src/particles/elements/Aperture.cpp | 28 + src/particles/elements/Buncher.H | 11 +- src/particles/elements/CFbend.H | 11 +- src/particles/elements/CMakeLists.txt | 1 + src/particles/elements/ChrDrift.H | 11 +- src/particles/elements/ChrPlasmaLens.H | 11 +- src/particles/elements/ChrQuad.H | 11 +- src/particles/elements/ChrUniformAcc.H | 11 +- src/particles/elements/ConstF.H | 11 +- src/particles/elements/DipEdge.H | 11 +- src/particles/elements/Drift.H | 11 +- src/particles/elements/Empty.H | 21 +- src/particles/elements/ExactDrift.H | 11 +- src/particles/elements/ExactSbend.H | 11 +- src/particles/elements/Kicker.H | 11 +- src/particles/elements/Marker.H | 89 +++ src/particles/elements/Multipole.H | 11 +- src/particles/elements/NonlinearLens.H | 11 +- src/particles/elements/PRot.H | 12 +- src/particles/elements/Programmable.H | 16 +- src/particles/elements/Quad.H | 11 +- src/particles/elements/RFCavity.H | 11 +- src/particles/elements/Sbend.H | 11 +- src/particles/elements/ShortRF.H | 11 +- src/particles/elements/SoftQuad.H | 11 +- src/particles/elements/SoftSol.H | 11 +- src/particles/elements/Sol.H | 11 +- src/particles/elements/TaperedPL.H | 11 +- src/particles/elements/ThinDipole.H | 12 +- src/particles/elements/mixin/named.H | 153 +++++ src/python/elements.cpp | 581 +++++++++++------- src/python/impactx/madx_to_impactx.py | 24 +- tests/python/test_dataframe.py | 10 +- tests/python/test_impactx.py | 16 +- tests/python/test_particle_tiles.py | 10 +- tests/python/test_push.py | 18 +- tests/python/test_xopt.py | 14 +- 84 files changed, 1241 insertions(+), 656 deletions(-) create mode 100644 src/particles/elements/Aperture.cpp create mode 100644 src/particles/elements/Marker.H create mode 100644 src/particles/elements/mixin/named.H diff --git a/docs/source/usage/python.rst b/docs/source/usage/python.rst index 52acfe210..948de5a6b 100644 --- a/docs/source/usage/python.rst +++ b/docs/source/usage/python.rst @@ -496,7 +496,7 @@ This module provides elements for the accelerator lattice. :param madx_file: file name to MAD-X file with beamline elements :param nslice: number of slices used for the application of space charge -.. py:class:: impactx.elements.CFbend(ds, rc, k, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.CFbend(ds, rc, k, dx=0, dy=0, rotation=0, nslice=1, name=None) A combined function bending magnet. This is an ideal Sbend with a normal quadrupole field component. @@ -510,8 +510,9 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ConstF(ds, kx, ky, kt, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ConstF(ds, kx, ky, kt, dx=0, dy=0, rotation=0, nslice=1, name=None) A linear Constant Focusing element. @@ -523,6 +524,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:property:: kx @@ -536,7 +538,7 @@ This module provides elements for the accelerator lattice. focusing t strength in 1/m -.. py:class:: impactx.elements.DipEdge(psi, rc, g, K2, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.DipEdge(psi, rc, g, K2, dx=0, dy=0, rotation=0, name=None) Edge focusing associated with bend entry or exit @@ -555,15 +557,17 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element -.. py:class:: impactx.elements.Drift(ds, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.Drift(ds, dx=0, dy=0, rotation=0, nslice=1, name=None) A drift. :param ds: Segment length in m :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ChrDrift(ds, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ChrDrift(ds, dx=0, dy=0, rotation=0, nslice=1, name=None) A drift with chromatic effects included. The Hamiltonian is expanded through second order in the transverse variables (x,px,y,py), with the exact pt @@ -574,8 +578,9 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ExactDrift(ds, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ExactDrift(ds, dx=0, dy=0, rotation=0, nslice=1, name=None) A drift using the exact nonlinear transfer map. @@ -584,16 +589,18 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.Kicker(xkick, ykick, unit="dimensionless", dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.Kicker(xkick, ykick, unit="dimensionless", dx=0, dy=0, rotation=0, name=None) A thin transverse kicker. :param xkick: horizontal kick strength (dimensionless OR T-m) :param ykick: vertical kick strength (dimensionless OR T-m) :param unit: specification of units (``"dimensionless"`` in units of the magnetic rigidity of the reference particle or ``"T-m"``) + :param name: an optional name for the element -.. py:class:: impactx.elements.Multipole(multipole, K_normal, K_skew, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.Multipole(multipole, K_normal, K_skew, dx=0, dy=0, rotation=0, name=None) A general thin multipole element. @@ -603,12 +610,13 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element .. py::class:: impactx.elements.Empty This element does nothing. -.. py:class:: impactx.elements.NonlinearLens(knll, cnll, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.NonlinearLens(knll, cnll, dx=0, dy=0, rotation=0, name=None) Single short segment of the nonlinear magnetic insert element. @@ -622,6 +630,7 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element .. py:class:: impactx.elements.BeamMonitor(name, backend="default", encoding="g") @@ -667,7 +676,7 @@ This module provides elements for the accelerator lattice. Scale factor (in meters^(1/2)) of the IOTA nonlinear magnetic insert element used for computing H and I. -.. py:class:: impactx.elements.Programmable +.. py:class:: impactx.elements.Programmable(ds=0.0, nslice=1, name=None) A programmable beam optics element. @@ -675,6 +684,7 @@ This module provides elements for the accelerator lattice. :param ds: Segment length in m. :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:property:: push @@ -707,7 +717,7 @@ This module provides elements for the accelerator lattice. This function is called for the reference particle as it passes through the element. The reference particle is updated *before* the beam particles are pushed. -.. py:class:: impactx.elements.Quad(ds, k, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.Quad(ds, k, dx=0, dy=0, rotation=0, nslice=1, name=None) A Quadrupole magnet. @@ -720,8 +730,9 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ChrQuad(ds, k, unit=0, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ChrQuad(ds, k, unit=0, dx=0, dy=0, rotation=0, nslice=1, name=None) A Quadrupole magnet, with chromatic effects included. The Hamiltonian is expanded through second order in the transverse variables (x,px,y,py), with the exact pt @@ -738,6 +749,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:property:: k @@ -747,7 +759,7 @@ This module provides elements for the accelerator lattice. unit specification for quad strength -.. py:class:: impactx.elements.ChrPlasmaLens(ds, k, unit=0, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ChrPlasmaLens(ds, k, unit=0, dx=0, dy=0, rotation=0, nslice=1, name=None) An active cylindrically symmetric plasma lens, with chromatic effects included. The Hamiltonian is expanded through second order in the transverse variables @@ -762,6 +774,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:property:: k @@ -771,7 +784,7 @@ This module provides elements for the accelerator lattice. unit specification for plasma lens focusing strength -.. py:class:: impactx.elements.ChrAcc(ds, ez, bz, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ChrAcc(ds, ez, bz, dx=0, dy=0, rotation=0, nslice=1, name=None) Acceleration in a uniform field Ez, with a uniform solenoidal field Bz. @@ -787,6 +800,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:property:: ez @@ -796,7 +810,7 @@ This module provides elements for the accelerator lattice. magnetic field strength in 1/m -.. py:class:: impactx.elements.RFCavity(ds, escale, freq, phase, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1) +.. py:class:: impactx.elements.RFCavity(ds, escale, freq, phase, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1, name=None) A radiofrequency cavity. @@ -813,8 +827,9 @@ This module provides elements for the accelerator lattice. :param rotation: rotation error in the transverse plane [degrees] :param mapsteps: number of integration steps per slice used for map and reference particle push in applied fields :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.Sbend(ds, rc, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.Sbend(ds, rc, dx=0, dy=0, rotation=0, nslice=1, name=None) An ideal sector bend. @@ -824,8 +839,9 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ExactSbend(ds, phi, B=0.0, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.ExactSbend(ds, phi, B=0.0, dx=0, dy=0, rotation=0, nslice=1, name=None) An ideal sector bend using the exact nonlinear map. The model consists of a uniform bending field B_y with a hard edge. Pole faces are normal to the entry and exit velocity of the reference particle. @@ -842,6 +858,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element .. py:class:: impactx.elements.Buncher(V, k, dx=0, dy=0, rotation=0) @@ -853,7 +870,7 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] -.. py:class:: impactx.elements.ShortRF(V, freq, phase=-90.0, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.ShortRF(V, freq, phase=-90.0, dx=0, dy=0, rotation=0, name=None) A short RF cavity element (MAD-X model). @@ -863,8 +880,9 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element -.. py:class:: impactx.elements.SoftSolenoid(ds, bscale, cos_coefficients, sin_coefficients, unit=0, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1) +.. py:class:: impactx.elements.SoftSolenoid(ds, bscale, cos_coefficients, sin_coefficients, unit=0, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1, name=None) A soft-edge solenoid. @@ -882,8 +900,9 @@ This module provides elements for the accelerator lattice. :param rotation: rotation error in the transverse plane [degrees] :param mapsteps: number of integration steps per slice used for map and reference particle push in applied fields :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.Sol(ds, ks, dx=0, dy=0, rotation=0, nslice=1) +.. py:class:: impactx.elements.Sol(ds, ks, dx=0, dy=0, rotation=0, nslice=1, name=None) An ideal hard-edge Solenoid magnet. @@ -893,15 +912,17 @@ This module provides elements for the accelerator lattice. :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.PRot(phi_in, phi_out) +.. py:class:: impactx.elements.PRot(phi_in, phi_out, name=None) Exact map for a pole-face rotation in the x-z plane. :param phi_in: angle of the reference particle with respect to the longitudinal (z) axis in the original frame in degrees :param phi_out: angle of the reference particle with respect to the longitudinal (z) axis in the rotated frame in degrees + :param name: an optional name for the element -.. py:class:: impactx.elements.Aperture(xmax, ymax, shape="rectangular", dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.Aperture(xmax, ymax, shape="rectangular", dx=0, dy=0, rotation=0, name=None) A thin collimator element, applying a transverse aperture boundary. @@ -911,6 +932,7 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element .. py:property:: shape @@ -924,7 +946,7 @@ This module provides elements for the accelerator lattice. maximum vertical coordinate -.. py:class:: impactx.elements.SoftQuadrupole(ds, gscale, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1) +.. py:class:: impactx.elements.SoftQuadrupole(ds, gscale, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1, name=None) A soft-edge quadrupole. @@ -939,8 +961,9 @@ This module provides elements for the accelerator lattice. :param rotation: rotation error in the transverse plane [degrees] :param mapsteps: number of integration steps per slice used for map and reference particle push in applied fields :param nslice: number of slices used for the application of space charge + :param name: an optional name for the element -.. py:class:: impactx.elements.ThinDipole(theta, rc, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.ThinDipole(theta, rc, dx=0, dy=0, rotation=0, name=None) A general thin dipole element. @@ -949,12 +972,13 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element Reference: * G. Ripken and F. Schmidt, Thin-Lens Formalism for Tracking, CERN/SL/95-12 (AP), 1995. -.. py:class:: impactx.elements.TaperedPL(k, taper, unit=0, dx=0, dy=0, rotation=0) +.. py:class:: impactx.elements.TaperedPL(k, taper, unit=0, dx=0, dy=0, rotation=0, name=None) A thin nonlinear plasma lens with transverse (horizontal) taper @@ -974,6 +998,7 @@ This module provides elements for the accelerator lattice. :param dx: horizontal translation error in m :param dy: vertical translation error in m :param rotation: rotation error in the transverse plane [degrees] + :param name: an optional name for the element .. py:property:: k diff --git a/examples/achromatic_spectrometer/run_spectrometer.py b/examples/achromatic_spectrometer/run_spectrometer.py index 21346676e..3ea598645 100755 --- a/examples/achromatic_spectrometer/run_spectrometer.py +++ b/examples/achromatic_spectrometer/run_spectrometer.py @@ -58,17 +58,17 @@ # drifts appearing the drift-kick sequence ds_half = ds / 2.0 -dr = elements.Drift(ds=ds_half, nslice=ns) +dr = elements.Drift(name="dr", ds=ds_half, nslice=ns) # define the lens segments thick_lens = [] for _ in range(0, num_lenses): - pl = elements.TaperedPL(k=dk, taper=dtaper, unit=0) + pl = elements.TaperedPL(name="pl", k=dk, taper=dtaper, unit=0) segment = [dr, pl, dr] thick_lens.extend(segment) -bend = elements.ExactSbend(ds=1.0, phi=10.0, B=0.0, nslice=ns) -drift = elements.Drift(ds=1.0, nslice=ns) +bend = elements.ExactSbend(name="bend", ds=1.0, phi=10.0, B=0.0, nslice=ns) +drift = elements.Drift(name="drift", ds=1.0, nslice=ns) # specify the lattice sequence sim.lattice.append(monitor) diff --git a/examples/aperture/run_aperture.py b/examples/aperture/run_aperture.py index 7e8cc73fc..39ff1e6fa 100755 --- a/examples/aperture/run_aperture.py +++ b/examples/aperture/run_aperture.py @@ -54,8 +54,10 @@ sim.lattice.extend( [ monitor, - elements.Drift(0.123), - elements.Aperture(xmax=1.0e-3, ymax=1.5e-3, shape="rectangular"), + elements.Drift(name="drift", ds=0.123), + elements.Aperture( + name="collimator", xmax=1.0e-3, ymax=1.5e-3, shape="rectangular" + ), monitor, ] ) diff --git a/examples/apochromatic/run_apochromatic.py b/examples/apochromatic/run_apochromatic.py index b2333edcc..c25802ae3 100644 --- a/examples/apochromatic/run_apochromatic.py +++ b/examples/apochromatic/run_apochromatic.py @@ -50,18 +50,18 @@ ns = 25 # number of slices per ds in the element # Drift elements -dr1 = elements.ChrDrift(ds=1.0, nslice=ns) -dr2 = elements.ChrDrift(ds=10.0, nslice=ns) +dr1 = elements.ChrDrift(name="dr1", ds=1.0, nslice=ns) +dr2 = elements.ChrDrift(name="dr2", ds=10.0, nslice=ns) # Quad elements -q1 = elements.ChrQuad(ds=1.2258333333, k=0.5884, nslice=ns) -q2 = elements.ChrQuad(ds=1.5677083333, k=-0.7525, nslice=ns) -q3 = elements.ChrQuad(ds=1.205625, k=0.5787, nslice=ns) -q4 = elements.ChrQuad(ds=1.2502083333, k=-0.6001, nslice=ns) -q5 = elements.ChrQuad(ds=1.2502083333, k=0.6001, nslice=ns) -q6 = elements.ChrQuad(ds=1.205625, k=-0.5787, nslice=ns) -q7 = elements.ChrQuad(ds=1.5677083333, k=0.7525, nslice=ns) -q8 = elements.ChrQuad(ds=1.2258333333, k=-0.5884, nslice=ns) +q1 = elements.ChrQuad(name="q1", ds=1.2258333333, k=0.5884, nslice=ns) +q2 = elements.ChrQuad(name="q2", ds=1.5677083333, k=-0.7525, nslice=ns) +q3 = elements.ChrQuad(name="q3", ds=1.205625, k=0.5787, nslice=ns) +q4 = elements.ChrQuad(name="q4", ds=1.2502083333, k=-0.6001, nslice=ns) +q5 = elements.ChrQuad(name="q5", ds=1.2502083333, k=0.6001, nslice=ns) +q6 = elements.ChrQuad(name="q6", ds=1.205625, k=-0.5787, nslice=ns) +q7 = elements.ChrQuad(name="q7", ds=1.5677083333, k=0.7525, nslice=ns) +q8 = elements.ChrQuad(name="q8", ds=1.2258333333, k=-0.5884, nslice=ns) lattice_line = [monitor, dr1, q1, q2, q3, dr2, q4, q5, dr2, q6, q7, q8, dr1, monitor] diff --git a/examples/apochromatic/run_apochromatic_pl.py b/examples/apochromatic/run_apochromatic_pl.py index f31a4b401..af78089df 100644 --- a/examples/apochromatic/run_apochromatic_pl.py +++ b/examples/apochromatic/run_apochromatic_pl.py @@ -50,39 +50,39 @@ ns = 25 # number of slices per ds in the element # Drift elements -dr1 = elements.ChrDrift(ds=1.0, nslice=ns) -dr2 = elements.ChrDrift(ds=2.0, nslice=ns) +dr1 = elements.ChrDrift(name="dr1", ds=1.0, nslice=ns) +dr2 = elements.ChrDrift(name="dr2", ds=2.0, nslice=ns) # Plasma lens elements q1 = elements.ChrPlasmaLens( - ds=0.331817852986604588, k=996.147787384348956, unit=1, nslice=ns + name="q1", ds=0.331817852986604588, k=996.147787384348956, unit=1, nslice=ns ) q2 = elements.ChrPlasmaLens( - ds=0.176038957633108457, k=528.485181135649785, unit=1, nslice=ns + name="q2", ds=0.176038957633108457, k=528.485181135649785, unit=1, nslice=ns ) q3 = elements.ChrPlasmaLens( - ds=1.041842576046930486, k=3127.707468391874166, unit=1, nslice=ns + name="q3", ds=1.041842576046930486, k=3127.707468391874166, unit=1, nslice=ns ) q4 = elements.ChrPlasmaLens( - ds=0.334367090894399520, k=501.900417308233112, unit=1, nslice=ns + name="q4", ds=0.334367090894399520, k=501.900417308233112, unit=1, nslice=ns ) q5 = elements.ChrPlasmaLens( - ds=1.041842576046930486, k=3127.707468391874166, unit=1, nslice=ns + name="q5", ds=1.041842576046930486, k=3127.707468391874166, unit=1, nslice=ns ) q6 = elements.ChrPlasmaLens( - ds=0.176038957633108457, k=528.485181135649785, unit=1, nslice=ns + name="q6", ds=0.176038957633108457, k=528.485181135649785, unit=1, nslice=ns ) q7 = elements.ChrPlasmaLens( - ds=0.331817852986604588, k=996.147787384348956, unit=1, nslice=ns + name="q7", ds=0.331817852986604588, k=996.147787384348956, unit=1, nslice=ns ) -# q1 = elements.ChrPlasmaLens(ds=0.331817852986604588, k=2.98636067687944129, unit=0, nslice=ns) -# q2 = elements.ChrPlasmaLens(ds=0.176038957633108457, k=1.584350618697976110, unit=0, nslice=ns) -# q3 = elements.ChrPlasmaLens(ds=1.041842576046930486, k=9.37658318442237437, unit=0, nslice=ns) -# q4 = elements.ChrPlasmaLens(ds=0.334367090894399520, k=1.50465190902479784, unit=0, nslice=ns) -# q5 = elements.ChrPlasmaLens(ds=1.041842576046930486, k=9.37658318442237437, unit=0, nslice=ns) -# q6 = elements.ChrPlasmaLens(ds=0.176038957633108457, k=1.584350618697976110, unit=0, nslice=ns) -# q7 = elements.ChrPlasmaLens(ds=0.331817852986604588, k=2.98636067687944129, unit=0, nslice=ns) +# q1 = elements.ChrPlasmaLens(name="q1", ds=0.331817852986604588, k=2.98636067687944129, unit=0, nslice=ns) +# q2 = elements.ChrPlasmaLens(name="q2", ds=0.176038957633108457, k=1.584350618697976110, unit=0, nslice=ns) +# q3 = elements.ChrPlasmaLens(name="q3", ds=1.041842576046930486, k=9.37658318442237437, unit=0, nslice=ns) +# q4 = elements.ChrPlasmaLens(name="q4", ds=0.334367090894399520, k=1.50465190902479784, unit=0, nslice=ns) +# q5 = elements.ChrPlasmaLens(name="q5", ds=1.041842576046930486, k=9.37658318442237437, unit=0, nslice=ns) +# q6 = elements.ChrPlasmaLens(name="q6", ds=0.176038957633108457, k=1.584350618697976110, unit=0, nslice=ns) +# q7 = elements.ChrPlasmaLens(name="q7", ds=0.331817852986604588, k=2.98636067687944129, unit=0, nslice=ns) lattice_line = [ monitor, diff --git a/examples/cfchannel/run_cfchannel.py b/examples/cfchannel/run_cfchannel.py index b17d738ea..d46428dc0 100755 --- a/examples/cfchannel/run_cfchannel.py +++ b/examples/cfchannel/run_cfchannel.py @@ -47,7 +47,7 @@ sim.lattice.extend( [ monitor, - elements.ConstF(ds=2.0, kx=1.0, ky=1.0, kt=1.0), + elements.ConstF(name="constf1", ds=2.0, kx=1.0, ky=1.0, kt=1.0), monitor, ] ) diff --git a/examples/cfchannel/run_cfchannel_10nC_fft.py b/examples/cfchannel/run_cfchannel_10nC_fft.py index afac1caf7..9b6467929 100755 --- a/examples/cfchannel/run_cfchannel_10nC_fft.py +++ b/examples/cfchannel/run_cfchannel_10nC_fft.py @@ -53,7 +53,7 @@ sim.lattice.extend( [ monitor, - elements.ConstF(ds=2.0, kx=1.0, ky=1.0, kt=1.0, nslice=nslice), + elements.ConstF(name="constf1", ds=2.0, kx=1.0, ky=1.0, kt=1.0, nslice=nslice), monitor, ] ) diff --git a/examples/cfchannel/run_cfchannel_10nC_mlmg.py b/examples/cfchannel/run_cfchannel_10nC_mlmg.py index 771d76f12..c8c46d18a 100755 --- a/examples/cfchannel/run_cfchannel_10nC_mlmg.py +++ b/examples/cfchannel/run_cfchannel_10nC_mlmg.py @@ -52,7 +52,7 @@ sim.lattice.extend( [ monitor, - elements.ConstF(ds=2.0, kx=1.0, ky=1.0, kt=1.0, nslice=nslice), + elements.ConstF(name="constf1", ds=2.0, kx=1.0, ky=1.0, kt=1.0, nslice=nslice), monitor, ] ) diff --git a/examples/chicane/run_chicane.py b/examples/chicane/run_chicane.py index 10ff072eb..7065246ca 100644 --- a/examples/chicane/run_chicane.py +++ b/examples/chicane/run_chicane.py @@ -53,17 +53,17 @@ lb = 0.500194828041958 # bend arc length (meters) # Drift elements -dr1 = elements.Drift(ds=5.0058489435, nslice=ns) -dr2 = elements.Drift(ds=1.0, nslice=ns) -dr3 = elements.Drift(ds=2.0, nslice=ns) +dr1 = elements.Drift(name="dr1", ds=5.0058489435, nslice=ns) +dr2 = elements.Drift(name="dr2", ds=1.0, nslice=ns) +dr3 = elements.Drift(name="dr3", ds=2.0, nslice=ns) # Bend elements -sbend1 = elements.Sbend(ds=lb, rc=-rc, nslice=ns) -sbend2 = elements.Sbend(ds=lb, rc=rc, nslice=ns) +sbend1 = elements.Sbend(name="sbend1", ds=lb, rc=-rc, nslice=ns) +sbend2 = elements.Sbend(name="sbend2", ds=lb, rc=rc, nslice=ns) # Dipole Edge Focusing elements -dipedge1 = elements.DipEdge(psi=-psi, rc=-rc, g=0.0, K2=0.0) -dipedge2 = elements.DipEdge(psi=psi, rc=rc, g=0.0, K2=0.0) +dipedge1 = elements.DipEdge(name="dipedge1", psi=-psi, rc=-rc, g=0.0, K2=0.0) +dipedge2 = elements.DipEdge(name="dipedge2", psi=psi, rc=rc, g=0.0, K2=0.0) lattice_half = [sbend1, dipedge1, dr1, dipedge2, sbend2] # assign a segment with the first half of the lattice diff --git a/examples/chicane/run_chicane_csr.py b/examples/chicane/run_chicane_csr.py index 1ab675af8..9661d256d 100644 --- a/examples/chicane/run_chicane_csr.py +++ b/examples/chicane/run_chicane_csr.py @@ -55,17 +55,17 @@ lb = 0.500194828041958 # bend arc length (meters) # Drift elements -dr1 = elements.Drift(ds=5.0058489435, nslice=ns) -dr2 = elements.Drift(ds=1.0, nslice=ns) -dr3 = elements.Drift(ds=2.0, nslice=ns) +dr1 = elements.Drift(name="dr1", ds=5.0058489435, nslice=ns) +dr2 = elements.Drift(name="dr2", ds=1.0, nslice=ns) +dr3 = elements.Drift(name="dr3", ds=2.0, nslice=ns) # Bend elements -sbend1 = elements.Sbend(ds=lb, rc=-rc, nslice=ns) -sbend2 = elements.Sbend(ds=lb, rc=rc, nslice=ns) +sbend1 = elements.Sbend(name="sbend1", ds=lb, rc=-rc, nslice=ns) +sbend2 = elements.Sbend(name="sbend2", ds=lb, rc=rc, nslice=ns) # Dipole Edge Focusing elements -dipedge1 = elements.DipEdge(psi=-psi, rc=-rc, g=0.0, K2=0.0) -dipedge2 = elements.DipEdge(psi=psi, rc=rc, g=0.0, K2=0.0) +dipedge1 = elements.DipEdge(name="dipedge1", psi=-psi, rc=-rc, g=0.0, K2=0.0) +dipedge2 = elements.DipEdge(name="dipedge2", psi=psi, rc=rc, g=0.0, K2=0.0) lattice_half = [sbend1, dipedge1, dr1, dipedge2, sbend2] # assign a segment with the first half of the lattice diff --git a/examples/compression/run_compression.py b/examples/compression/run_compression.py index 16dc6d046..0552af292 100644 --- a/examples/compression/run_compression.py +++ b/examples/compression/run_compression.py @@ -47,9 +47,9 @@ # design the accelerator lattice sim.lattice.append(monitor) # Short RF cavity element -shortrf1 = elements.ShortRF(V=1000.0, freq=1.3e9, phase=-89.5) +shortrf1 = elements.ShortRF(name="shortrf1", V=1000.0, freq=1.3e9, phase=-89.5) # Drift element -drift1 = elements.Drift(ds=1.7) +drift1 = elements.Drift(name="drift1", ds=1.7) sim.lattice.extend([shortrf1, drift1]) diff --git a/examples/cyclotron/run_cyclotron.py b/examples/cyclotron/run_cyclotron.py index d194dd8e7..63ec87e1b 100755 --- a/examples/cyclotron/run_cyclotron.py +++ b/examples/cyclotron/run_cyclotron.py @@ -49,10 +49,10 @@ ns = 1 # number of slices per ds in the element period = [ monitor, - elements.ChrAcc(ds=0.038, ez=1.12188308693e-4, bz=1.0e-14, nslice=ns), - elements.ExactSbend(ds=0.25, phi=180.0, B=1), - elements.ChrAcc(ds=0.038, ez=1.12188308693e-4, bz=1.0e-14, nslice=ns), - elements.ExactSbend(ds=0.25, phi=180.0, B=1), + elements.ChrAcc(name="acc1", ds=0.038, ez=1.12188308693e-4, bz=1.0e-14, nslice=ns), + elements.ExactSbend(name="sbend1", ds=0.25, phi=180.0, B=1), + elements.ChrAcc(name="acc2", ds=0.038, ez=1.12188308693e-4, bz=1.0e-14, nslice=ns), + elements.ExactSbend(name="sbend2", ds=0.25, phi=180.0, B=1), monitor, ] diff --git a/examples/distgen/run_gaussian_twiss.py b/examples/distgen/run_gaussian_twiss.py index e317bc19d..dbd4c1ce7 100755 --- a/examples/distgen/run_gaussian_twiss.py +++ b/examples/distgen/run_gaussian_twiss.py @@ -52,15 +52,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d1", ds=0.25, nslice=ns), monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="q1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.Drift(ds=0.5, nslice=ns), + elements.Drift(name="d2", ds=0.5, nslice=ns), monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="q2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/distgen/run_kurth4d.py b/examples/distgen/run_kurth4d.py index 84bdc4d54..94194dcdb 100755 --- a/examples/distgen/run_kurth4d.py +++ b/examples/distgen/run_kurth4d.py @@ -50,7 +50,7 @@ # design the accelerator lattice) constf = [ monitor, - elements.ConstF(ds=2.0, kx=1.0, ky=1.0, kt=1.0e-4), + elements.ConstF(name="constf1", ds=2.0, kx=1.0, ky=1.0, kt=1.0e-4), monitor, ] diff --git a/examples/distgen/run_kvdist_twiss.py b/examples/distgen/run_kvdist_twiss.py index beaaec0de..7d67987e5 100755 --- a/examples/distgen/run_kvdist_twiss.py +++ b/examples/distgen/run_kvdist_twiss.py @@ -52,15 +52,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d1", ds=0.25, nslice=ns), monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="q1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.Drift(ds=0.5, nslice=ns), + elements.Drift(name="d2", ds=0.5, nslice=ns), monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="q2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/distgen/run_semigaussian.py b/examples/distgen/run_semigaussian.py index 3542d33b6..b737246f4 100755 --- a/examples/distgen/run_semigaussian.py +++ b/examples/distgen/run_semigaussian.py @@ -50,15 +50,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d1", ds=0.25, nslice=ns), monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="q1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.Drift(ds=0.5, nslice=ns), + elements.Drift(name="d2", ds=0.5, nslice=ns), monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="q2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="d3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/dogleg/run_dogleg.py b/examples/dogleg/run_dogleg.py index 96b9cd91f..f731c76ca 100644 --- a/examples/dogleg/run_dogleg.py +++ b/examples/dogleg/run_dogleg.py @@ -53,16 +53,16 @@ lb = 0.500194828041958 # bend arc length (meters) # Drift elements -dr1 = elements.Drift(ds=5.0058489435, nslice=ns) -dr2 = elements.Drift(ds=0.5, nslice=ns) +dr1 = elements.Drift(name="dr1", ds=5.0058489435, nslice=ns) +dr2 = elements.Drift(name="dr2", ds=0.5, nslice=ns) # Bend elements -sbend1 = elements.Sbend(ds=lb, rc=-rc, nslice=ns) -sbend2 = elements.Sbend(ds=lb, rc=rc, nslice=ns) +sbend1 = elements.Sbend(name="sbend1", ds=lb, rc=-rc, nslice=ns) +sbend2 = elements.Sbend(name="sbend2", ds=lb, rc=rc, nslice=ns) # Dipole Edge Focusing elements -dipedge1 = elements.DipEdge(psi=-psi, rc=-rc, g=0.0, K2=0.0) -dipedge2 = elements.DipEdge(psi=psi, rc=rc, g=0.0, K2=0.0) +dipedge1 = elements.DipEdge(name="dipedge1", psi=-psi, rc=-rc, g=0.0, K2=0.0) +dipedge2 = elements.DipEdge(name="dipedge2", psi=psi, rc=rc, g=0.0, K2=0.0) lattice_dogleg = [sbend1, dipedge1, dr1, dipedge2, sbend2, dr2] diff --git a/examples/epac2004_benchmarks/run_bithermal.py b/examples/epac2004_benchmarks/run_bithermal.py index 0f9a0bd8b..5463cf9e8 100755 --- a/examples/epac2004_benchmarks/run_bithermal.py +++ b/examples/epac2004_benchmarks/run_bithermal.py @@ -52,6 +52,7 @@ sim.lattice.append(monitor) constf = elements.ConstF( + name="constf", ds=10.0, kx=6.283185307179586, ky=6.283185307179586, diff --git a/examples/epac2004_benchmarks/run_fodo_rf_SC.py b/examples/epac2004_benchmarks/run_fodo_rf_SC.py index 03abc6386..a86cd7d06 100755 --- a/examples/epac2004_benchmarks/run_fodo_rf_SC.py +++ b/examples/epac2004_benchmarks/run_fodo_rf_SC.py @@ -50,14 +50,15 @@ sim.lattice.append(monitor) # Quad elements -fquad = elements.Quad(ds=0.15, k=2.4669749766168163, nslice=6) -dquad = elements.Quad(ds=0.3, k=-2.4669749766168163, nslice=12) +fquad = elements.Quad(name="fquad", ds=0.15, k=2.4669749766168163, nslice=6) +dquad = elements.Quad(name="dquad", ds=0.3, k=-2.4669749766168163, nslice=12) # Drift element -dr = elements.Drift(ds=0.1, nslice=4) +dr = elements.Drift(name="dr", ds=0.1, nslice=4) # RF cavity elements gapa1 = elements.RFCavity( + name="gapa1", ds=1.0, escale=0.042631556991578, freq=7.0e8, @@ -121,6 +122,7 @@ ) gapb1 = elements.RFCavity( + name="gapb1", ds=1.0, escale=0.042631556991578, freq=7.0e8, diff --git a/examples/expanding_beam/run_expanding_fft.py b/examples/expanding_beam/run_expanding_fft.py index d27958a5c..c6b9ac53b 100755 --- a/examples/expanding_beam/run_expanding_fft.py +++ b/examples/expanding_beam/run_expanding_fft.py @@ -55,7 +55,7 @@ monitor = elements.BeamMonitor("monitor", backend="h5") # design the accelerator lattice -sim.lattice.extend([monitor, elements.Drift(ds=6.0, nslice=40), monitor]) +sim.lattice.extend([monitor, elements.Drift(name="d1", ds=6.0, nslice=40), monitor]) # run simulation sim.evolve() diff --git a/examples/expanding_beam/run_expanding_mlmg.py b/examples/expanding_beam/run_expanding_mlmg.py index c41ada0e1..42b97904b 100755 --- a/examples/expanding_beam/run_expanding_mlmg.py +++ b/examples/expanding_beam/run_expanding_mlmg.py @@ -54,7 +54,7 @@ monitor = elements.BeamMonitor("monitor", backend="h5") # design the accelerator lattice -sim.lattice.extend([monitor, elements.Drift(ds=6.0, nslice=40), monitor]) +sim.lattice.extend([monitor, elements.Drift(name="d1", ds=6.0, nslice=40), monitor]) # run simulation sim.evolve() diff --git a/examples/fodo/run_fodo.py b/examples/fodo/run_fodo.py index 57703ed33..6a6418eca 100755 --- a/examples/fodo/run_fodo.py +++ b/examples/fodo/run_fodo.py @@ -50,15 +50,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="drift1", ds=0.25, nslice=ns), monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="quad1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.Drift(ds=0.5, nslice=ns), + elements.Drift(name="drift2", ds=0.5, nslice=ns), monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="quad2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="drift3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/fodo/run_fodo_twiss.py b/examples/fodo/run_fodo_twiss.py index bd7b7c51d..700fdd767 100755 --- a/examples/fodo/run_fodo_twiss.py +++ b/examples/fodo/run_fodo_twiss.py @@ -52,15 +52,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="drift1", ds=0.25, nslice=ns), monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="quad1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.Drift(ds=0.5, nslice=ns), + elements.Drift(name="drift2", ds=0.5, nslice=ns), monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="quad2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="drift3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/fodo_chromatic/run_fodo_chr.py b/examples/fodo_chromatic/run_fodo_chr.py index aa4677f75..1c25ff7c4 100755 --- a/examples/fodo_chromatic/run_fodo_chr.py +++ b/examples/fodo_chromatic/run_fodo_chr.py @@ -50,15 +50,15 @@ ns = 25 # number of slices per ds in the element fodo = [ monitor, - elements.ChrDrift(ds=0.25, nslice=ns), + elements.ChrDrift(name="drift1", ds=0.25, nslice=ns), monitor, - elements.ChrQuad(ds=1.0, k=1.0, nslice=ns), + elements.ChrQuad(name="quad1", ds=1.0, k=1.0, nslice=ns), monitor, - elements.ChrDrift(ds=0.5, nslice=ns), + elements.ChrDrift(name="drift2", ds=0.5, nslice=ns), monitor, - elements.ChrQuad(ds=1.0, k=-1.0, nslice=ns), + elements.ChrQuad(name="quad2", ds=1.0, k=-1.0, nslice=ns), monitor, - elements.ChrDrift(ds=0.25, nslice=ns), + elements.ChrDrift(name="drift3", ds=0.25, nslice=ns), monitor, ] # assign a fodo segment diff --git a/examples/fodo_programmable/run_fodo_programmable.py b/examples/fodo_programmable/run_fodo_programmable.py index dd9310608..b74e697f3 100755 --- a/examples/fodo_programmable/run_fodo_programmable.py +++ b/examples/fodo_programmable/run_fodo_programmable.py @@ -112,7 +112,7 @@ def my_ref_drift(pge, refpart): refpart.s = s + slice_ds -pge1 = elements.Programmable() +pge1 = elements.Programmable(name="d1") pge1.nslice = ns pge1.beam_particles = lambda pti, refpart: my_drift(pge1, pti, refpart) pge1.ref_particle = lambda refpart: my_ref_drift(pge1, refpart) @@ -121,7 +121,7 @@ def my_ref_drift(pge, refpart): # attention: assignment is a reference for pge2 = pge1 -pge2 = elements.Programmable() +pge2 = elements.Programmable(name="d2") pge2.nslice = ns pge2.beam_particles = lambda pti, refpart: my_drift(pge2, pti, refpart) pge2.ref_particle = lambda refpart: my_ref_drift(pge2, refpart) @@ -134,15 +134,15 @@ def my_ref_drift(pge, refpart): # design the accelerator lattice fodo = [ monitor, - pge1, # equivalent to elements.Drift(ds=0.25, nslice=ns) + pge1, # equivalent to elements.Drift("d1", ds=0.25, nslice=ns) monitor, - elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Quad(name="q1", ds=1.0, k=1.0, nslice=ns), monitor, - pge2, # equivalent to elements.Drift(ds=0.5, nslice=ns) + pge2, # equivalent to elements.Drift("d2", ds=0.5, nslice=ns) monitor, - elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Quad(name="q2", ds=1.0, k=-1.0, nslice=ns), monitor, - pge1, # equivalent to elements.Drift(ds=0.25, nslice=ns) + pge1, # equivalent to elements.Drift("d1", ds=0.25, nslice=ns) monitor, ] # assign a fodo segment diff --git a/examples/fodo_rf/run_fodo_rf.py b/examples/fodo_rf/run_fodo_rf.py index 1eed9e10b..397e46212 100644 --- a/examples/fodo_rf/run_fodo_rf.py +++ b/examples/fodo_rf/run_fodo_rf.py @@ -47,12 +47,12 @@ # design the accelerator lattice sim.lattice.append(monitor) # Quad elements -quad1 = elements.Quad(ds=0.15, k=2.5) -quad2 = elements.Quad(ds=0.3, k=-2.5) +quad1 = elements.Quad(name="quad1", ds=0.15, k=2.5) +quad2 = elements.Quad(name="quad2", ds=0.3, k=-2.5) # Drift element -drift1 = elements.Drift(ds=1.0) +drift1 = elements.Drift(name="drift1", ds=1.0) # Short RF cavity element -shortrf1 = elements.Buncher(V=0.01, k=15.0) +shortrf1 = elements.Buncher(name="shortrf1", V=0.01, k=15.0) lattice_no_drifts = [quad1, shortrf1, quad2, shortrf1, quad1] # set first lattice element diff --git a/examples/fodo_tune/run_fodo_tune.py b/examples/fodo_tune/run_fodo_tune.py index 2315be15b..864a799d7 100755 --- a/examples/fodo_tune/run_fodo_tune.py +++ b/examples/fodo_tune/run_fodo_tune.py @@ -51,11 +51,11 @@ ns = 1 # number of slices per ds in the element fodo = [ monitor, - elements.Drift(ds=0.25, nslice=ns), - elements.Quad(ds=1.0, k=1.0, nslice=ns), - elements.Drift(ds=0.5, nslice=ns), - elements.Quad(ds=1.0, k=-1.0, nslice=ns), - elements.Drift(ds=0.25, nslice=ns), + elements.Drift(name="drift1", ds=0.25, nslice=ns), + elements.Quad(name="quad1", ds=1.0, k=1.0, nslice=ns), + elements.Drift(name="drift2", ds=0.5, nslice=ns), + elements.Quad(name="quad2", ds=1.0, k=-1.0, nslice=ns), + elements.Drift(name="drift3", ds=0.25, nslice=ns), ] # assign a fodo segment sim.lattice.extend(fodo) diff --git a/examples/initialize_from_array/run_from_array.py b/examples/initialize_from_array/run_from_array.py index c53eb3588..351ff95ab 100644 --- a/examples/initialize_from_array/run_from_array.py +++ b/examples/initialize_from_array/run_from_array.py @@ -98,7 +98,7 @@ sim.lattice.extend( [ monitor, - elements.Drift(ds=0.01), + elements.Drift(name="drift", ds=0.01), monitor, ] ) diff --git a/examples/iota_lattice/run_iotalattice.py b/examples/iota_lattice/run_iotalattice.py index 6cc6af2f0..5488cf1c7 100644 --- a/examples/iota_lattice/run_iotalattice.py +++ b/examples/iota_lattice/run_iotalattice.py @@ -46,57 +46,57 @@ ns = 10 # number of slices per ds in the element # Drift elements -dra1 = elements.Drift(ds=0.9125, nslice=ns) -dra2 = elements.Drift(ds=0.135, nslice=ns) -dra3 = elements.Drift(ds=0.725, nslice=ns) -dra4 = elements.Drift(ds=0.145, nslice=ns) -dra5 = elements.Drift(ds=0.3405, nslice=ns) -drb1 = elements.Drift(ds=0.3205, nslice=ns) -drb2 = elements.Drift(ds=0.14, nslice=ns) -drb3 = elements.Drift(ds=0.1525, nslice=ns) -drb4 = elements.Drift(ds=0.31437095, nslice=ns) -drc1 = elements.Drift(ds=0.42437095, nslice=ns) -drc2 = elements.Drift(ds=0.355, nslice=ns) -dnll = elements.Drift(ds=1.8, nslice=ns) -drd1 = elements.Drift(ds=0.62437095, nslice=ns) -drd2 = elements.Drift(ds=0.42, nslice=ns) -drd3 = elements.Drift(ds=1.625, nslice=ns) -drd4 = elements.Drift(ds=0.6305, nslice=ns) -dre1 = elements.Drift(ds=0.5305, nslice=ns) -dre2 = elements.Drift(ds=1.235, nslice=ns) -dre3 = elements.Drift(ds=0.8075, nslice=ns) +dra1 = elements.Drift(name="dra1", ds=0.9125, nslice=ns) +dra2 = elements.Drift(name="dra2", ds=0.135, nslice=ns) +dra3 = elements.Drift(name="dra3", ds=0.725, nslice=ns) +dra4 = elements.Drift(name="dra4", ds=0.145, nslice=ns) +dra5 = elements.Drift(name="dra5", ds=0.3405, nslice=ns) +drb1 = elements.Drift(name="drb1", ds=0.3205, nslice=ns) +drb2 = elements.Drift(name="drb2", ds=0.14, nslice=ns) +drb3 = elements.Drift(name="drb3", ds=0.1525, nslice=ns) +drb4 = elements.Drift(name="drb4", ds=0.31437095, nslice=ns) +drc1 = elements.Drift(name="drc1", ds=0.42437095, nslice=ns) +drc2 = elements.Drift(name="drc2", ds=0.355, nslice=ns) +dnll = elements.Drift(name="dnll", ds=1.8, nslice=ns) +drd1 = elements.Drift(name="drd1", ds=0.62437095, nslice=ns) +drd2 = elements.Drift(name="drd2", ds=0.42, nslice=ns) +drd3 = elements.Drift(name="drd3", ds=1.625, nslice=ns) +drd4 = elements.Drift(name="drd4", ds=0.6305, nslice=ns) +dre1 = elements.Drift(name="dre1", ds=0.5305, nslice=ns) +dre2 = elements.Drift(name="dre2", ds=1.235, nslice=ns) +dre3 = elements.Drift(name="dre3", ds=0.8075, nslice=ns) # Bend elements rc30 = 0.822230996255981 -sbend30 = elements.Sbend(ds=0.4305191429, rc=rc30) -edge30 = elements.DipEdge(psi=0.0, rc=rc30, g=0.058, K2=0.5) +sbend30 = elements.Sbend(name="sbend30", ds=0.4305191429, rc=rc30) +edge30 = elements.DipEdge(name="edge30", psi=0.0, rc=rc30, g=0.058, K2=0.5) rc60 = 0.772821121503940 -sbend60 = elements.Sbend(ds=0.8092963858, rc=rc60) -edge60 = elements.DipEdge(psi=0.0, rc=rc60, g=0.058, K2=0.5) +sbend60 = elements.Sbend(name="sbend60", ds=0.8092963858, rc=rc60) +edge60 = elements.DipEdge(name="edge60", psi=0.0, rc=rc60, g=0.058, K2=0.5) # Quad elements ds_quad = 0.21 -qa1 = elements.Quad(ds=ds_quad, k=-8.78017699, nslice=ns) -qa2 = elements.Quad(ds=ds_quad, k=13.24451745, nslice=ns) -qa3 = elements.Quad(ds=ds_quad, k=-13.65151327, nslice=ns) -qa4 = elements.Quad(ds=ds_quad, k=19.75138652, nslice=ns) -qb1 = elements.Quad(ds=ds_quad, k=-10.84199727, nslice=ns) -qb2 = elements.Quad(ds=ds_quad, k=16.24844348, nslice=ns) -qb3 = elements.Quad(ds=ds_quad, k=-8.27411104, nslice=ns) -qb4 = elements.Quad(ds=ds_quad, k=-7.45719247, nslice=ns) -qb5 = elements.Quad(ds=ds_quad, k=14.03362243, nslice=ns) -qb6 = elements.Quad(ds=ds_quad, k=-12.23595641, nslice=ns) -qc1 = elements.Quad(ds=ds_quad, k=-13.18863768, nslice=ns) -qc2 = elements.Quad(ds=ds_quad, k=11.50601829, nslice=ns) -qc3 = elements.Quad(ds=ds_quad, k=-11.10445869, nslice=ns) -qd1 = elements.Quad(ds=ds_quad, k=-6.78179218, nslice=ns) -qd2 = elements.Quad(ds=ds_quad, k=5.19026998, nslice=ns) -qd3 = elements.Quad(ds=ds_quad, k=-5.8586173, nslice=ns) -qd4 = elements.Quad(ds=ds_quad, k=4.62460039, nslice=ns) -qe1 = elements.Quad(ds=ds_quad, k=-4.49607687, nslice=ns) -qe2 = elements.Quad(ds=ds_quad, k=6.66737146, nslice=ns) -qe3 = elements.Quad(ds=ds_quad, k=-6.69148177, nslice=ns) +qa1 = elements.Quad(name="qa1", ds=ds_quad, k=-8.78017699, nslice=ns) +qa2 = elements.Quad(name="qa2", ds=ds_quad, k=13.24451745, nslice=ns) +qa3 = elements.Quad(name="qa3", ds=ds_quad, k=-13.65151327, nslice=ns) +qa4 = elements.Quad(name="qa4", ds=ds_quad, k=19.75138652, nslice=ns) +qb1 = elements.Quad(name="qb1", ds=ds_quad, k=-10.84199727, nslice=ns) +qb2 = elements.Quad(name="qb2", ds=ds_quad, k=16.24844348, nslice=ns) +qb3 = elements.Quad(name="qb3", ds=ds_quad, k=-8.27411104, nslice=ns) +qb4 = elements.Quad(name="qb4", ds=ds_quad, k=-7.45719247, nslice=ns) +qb5 = elements.Quad(name="qb5", ds=ds_quad, k=14.03362243, nslice=ns) +qb6 = elements.Quad(name="qb6", ds=ds_quad, k=-12.23595641, nslice=ns) +qc1 = elements.Quad(name="qc1", ds=ds_quad, k=-13.18863768, nslice=ns) +qc2 = elements.Quad(name="qc2", ds=ds_quad, k=11.50601829, nslice=ns) +qc3 = elements.Quad(name="qc3", ds=ds_quad, k=-11.10445869, nslice=ns) +qd1 = elements.Quad(name="qd1", ds=ds_quad, k=-6.78179218, nslice=ns) +qd2 = elements.Quad(name="qd2", ds=ds_quad, k=5.19026998, nslice=ns) +qd3 = elements.Quad(name="qd3", ds=ds_quad, k=-5.8586173, nslice=ns) +qd4 = elements.Quad(name="qd4", ds=ds_quad, k=4.62460039, nslice=ns) +qe1 = elements.Quad(name="qe1", ds=ds_quad, k=-4.49607687, nslice=ns) +qe2 = elements.Quad(name="qe2", ds=ds_quad, k=6.66737146, nslice=ns) +qe3 = elements.Quad(name="qe3", ds=ds_quad, k=-6.69148177, nslice=ns) # build lattice: first half, qe3, then mirror # fmt: off diff --git a/examples/iota_lattice/run_iotalattice_sdep.py b/examples/iota_lattice/run_iotalattice_sdep.py index 49cd567fa..70f50d101 100644 --- a/examples/iota_lattice/run_iotalattice_sdep.py +++ b/examples/iota_lattice/run_iotalattice_sdep.py @@ -56,57 +56,57 @@ ns = 10 # number of slices per ds in the element # Drift elements -dra1 = elements.Drift(ds=0.9125, nslice=ns) -dra2 = elements.Drift(ds=0.135, nslice=ns) -dra3 = elements.Drift(ds=0.725, nslice=ns) -dra4 = elements.Drift(ds=0.145, nslice=ns) -dra5 = elements.Drift(ds=0.3405, nslice=ns) -drb1 = elements.Drift(ds=0.3205, nslice=ns) -drb2 = elements.Drift(ds=0.14, nslice=ns) -drb3 = elements.Drift(ds=0.1525, nslice=ns) -drb4 = elements.Drift(ds=0.31437095, nslice=ns) -drc1 = elements.Drift(ds=0.42437095, nslice=ns) -drc2 = elements.Drift(ds=0.355, nslice=ns) -dnll = elements.Drift(ds=1.8, nslice=ns) -drd1 = elements.Drift(ds=0.62437095, nslice=ns) -drd2 = elements.Drift(ds=0.42, nslice=ns) -drd3 = elements.Drift(ds=1.625, nslice=ns) -drd4 = elements.Drift(ds=0.6305, nslice=ns) -dre1 = elements.Drift(ds=0.5305, nslice=ns) -dre2 = elements.Drift(ds=1.235, nslice=ns) -dre3 = elements.Drift(ds=0.8075, nslice=ns) +dra1 = elements.Drift(name="dra1", ds=0.9125, nslice=ns) +dra2 = elements.Drift(name="dra2", ds=0.135, nslice=ns) +dra3 = elements.Drift(name="dra3", ds=0.725, nslice=ns) +dra4 = elements.Drift(name="dra4", ds=0.145, nslice=ns) +dra5 = elements.Drift(name="dra5", ds=0.3405, nslice=ns) +drb1 = elements.Drift(name="drb1", ds=0.3205, nslice=ns) +drb2 = elements.Drift(name="drb2", ds=0.14, nslice=ns) +drb3 = elements.Drift(name="drb3", ds=0.1525, nslice=ns) +drb4 = elements.Drift(name="drb4", ds=0.31437095, nslice=ns) +drc1 = elements.Drift(name="drc1", ds=0.42437095, nslice=ns) +drc2 = elements.Drift(name="drc2", ds=0.355, nslice=ns) +dnll = elements.Drift(name="dnll", ds=1.8, nslice=ns) +drd1 = elements.Drift(name="drd1", ds=0.62437095, nslice=ns) +drd2 = elements.Drift(name="drd2", ds=0.42, nslice=ns) +drd3 = elements.Drift(name="drd3", ds=1.625, nslice=ns) +drd4 = elements.Drift(name="drd4", ds=0.6305, nslice=ns) +dre1 = elements.Drift(name="dre1", ds=0.5305, nslice=ns) +dre2 = elements.Drift(name="dre2", ds=1.235, nslice=ns) +dre3 = elements.Drift(name="dre3", ds=0.8075, nslice=ns) # Bend elements rc30 = 0.822230996255981 -sbend30 = elements.Sbend(ds=0.4305191429, rc=rc30) -edge30 = elements.DipEdge(psi=0.0, rc=rc30, g=0.058, K2=0.5) +sbend30 = elements.Sbend(name="sbend30", ds=0.4305191429, rc=rc30) +edge30 = elements.DipEdge(name="edge30", psi=0.0, rc=rc30, g=0.058, K2=0.5) rc60 = 0.772821121503940 -sbend60 = elements.Sbend(ds=0.8092963858, rc=rc60) -edge60 = elements.DipEdge(psi=0.0, rc=rc60, g=0.058, K2=0.5) +sbend60 = elements.Sbend(name="sbend60", ds=0.8092963858, rc=rc60) +edge60 = elements.DipEdge(name="edge60", psi=0.0, rc=rc60, g=0.058, K2=0.5) # Quad elements ds_quad = 0.21 -qa1 = elements.Quad(ds=ds_quad, k=-8.78017699, nslice=ns) -qa2 = elements.Quad(ds=ds_quad, k=13.24451745, nslice=ns) -qa3 = elements.Quad(ds=ds_quad, k=-13.65151327, nslice=ns) -qa4 = elements.Quad(ds=ds_quad, k=19.75138652, nslice=ns) -qb1 = elements.Quad(ds=ds_quad, k=-10.84199727, nslice=ns) -qb2 = elements.Quad(ds=ds_quad, k=16.24844348, nslice=ns) -qb3 = elements.Quad(ds=ds_quad, k=-8.27411104, nslice=ns) -qb4 = elements.Quad(ds=ds_quad, k=-7.45719247, nslice=ns) -qb5 = elements.Quad(ds=ds_quad, k=14.03362243, nslice=ns) -qb6 = elements.Quad(ds=ds_quad, k=-12.23595641, nslice=ns) -qc1 = elements.Quad(ds=ds_quad, k=-13.18863768, nslice=ns) -qc2 = elements.Quad(ds=ds_quad, k=11.50601829, nslice=ns) -qc3 = elements.Quad(ds=ds_quad, k=-11.10445869, nslice=ns) -qd1 = elements.Quad(ds=ds_quad, k=-6.78179218, nslice=ns) -qd2 = elements.Quad(ds=ds_quad, k=5.19026998, nslice=ns) -qd3 = elements.Quad(ds=ds_quad, k=-5.8586173, nslice=ns) -qd4 = elements.Quad(ds=ds_quad, k=4.62460039, nslice=ns) -qe1 = elements.Quad(ds=ds_quad, k=-4.49607687, nslice=ns) -qe2 = elements.Quad(ds=ds_quad, k=6.66737146, nslice=ns) -qe3 = elements.Quad(ds=ds_quad, k=-6.69148177, nslice=ns) +qa1 = elements.Quad(name="qa1", ds=ds_quad, k=-8.78017699, nslice=ns) +qa2 = elements.Quad(name="qa2", ds=ds_quad, k=13.24451745, nslice=ns) +qa3 = elements.Quad(name="qa3", ds=ds_quad, k=-13.65151327, nslice=ns) +qa4 = elements.Quad(name="qa4", ds=ds_quad, k=19.75138652, nslice=ns) +qb1 = elements.Quad(name="qb1", ds=ds_quad, k=-10.84199727, nslice=ns) +qb2 = elements.Quad(name="qb2", ds=ds_quad, k=16.24844348, nslice=ns) +qb3 = elements.Quad(name="qb3", ds=ds_quad, k=-8.27411104, nslice=ns) +qb4 = elements.Quad(name="qb4", ds=ds_quad, k=-7.45719247, nslice=ns) +qb5 = elements.Quad(name="qb5", ds=ds_quad, k=14.03362243, nslice=ns) +qb6 = elements.Quad(name="qb6", ds=ds_quad, k=-12.23595641, nslice=ns) +qc1 = elements.Quad(name="qc1", ds=ds_quad, k=-13.18863768, nslice=ns) +qc2 = elements.Quad(name="qc2", ds=ds_quad, k=11.50601829, nslice=ns) +qc3 = elements.Quad(name="qc3", ds=ds_quad, k=-11.10445869, nslice=ns) +qd1 = elements.Quad(name="qd1", ds=ds_quad, k=-6.78179218, nslice=ns) +qd2 = elements.Quad(name="qd2", ds=ds_quad, k=5.19026998, nslice=ns) +qd3 = elements.Quad(name="qd3", ds=ds_quad, k=-5.8586173, nslice=ns) +qd4 = elements.Quad(name="qd4", ds=ds_quad, k=4.62460039, nslice=ns) +qe1 = elements.Quad(name="qe1", ds=ds_quad, k=-4.49607687, nslice=ns) +qe2 = elements.Quad(name="qe2", ds=ds_quad, k=6.66737146, nslice=ns) +qe3 = elements.Quad(name="qe3", ds=ds_quad, k=-6.69148177, nslice=ns) # Special (elliptic) nonlinear element: @@ -120,7 +120,7 @@ # build up the nonlinear lens in segments ds_half = ds / 2.0 -dr = elements.Drift(ds=ds_half, nslice=1) +dr = elements.Drift(name="dr", ds=ds_half, nslice=1) nll = [] for j in range(0, num_lenses): s = -lens_length / 2.0 + ds_half + j * ds @@ -130,7 +130,7 @@ ) knll_s = t_strength * c_parameter**2 * ds / beta cnll_s = c_parameter * math.sqrt(beta) - nllens = elements.NonlinearLens(knll=knll_s, cnll=cnll_s) + nllens = elements.NonlinearLens(name="nllens" + str(j), knll=knll_s, cnll=cnll_s) segment = [dr, nllens, dr] nll.extend(segment) diff --git a/examples/iota_lens/run_iotalens.py b/examples/iota_lens/run_iotalens.py index 96a02118f..3efcd826f 100644 --- a/examples/iota_lens/run_iotalens.py +++ b/examples/iota_lens/run_iotalens.py @@ -48,9 +48,9 @@ monitor.cn = 0.01 # design the accelerator lattice -constEnd = elements.ConstF(ds=0.05, kx=1.0, ky=1.0, kt=1.0e-12) -nllens = elements.NonlinearLens(knll=4.0e-6, cnll=0.01) -const = elements.ConstF(ds=0.1, kx=1.0, ky=1.0, kt=1.0e-12) +constEnd = elements.ConstF(name="constEnd", ds=0.05, kx=1.0, ky=1.0, kt=1.0e-12) +nllens = elements.NonlinearLens(name="nllens", knll=4.0e-6, cnll=0.01) +const = elements.ConstF(name="const", ds=0.1, kx=1.0, ky=1.0, kt=1.0e-12) num_lenses = 18 nllens_lattice = ( diff --git a/examples/iota_lens/run_iotalens_sdep.py b/examples/iota_lens/run_iotalens_sdep.py index a078a27b0..40f147062 100644 --- a/examples/iota_lens/run_iotalens_sdep.py +++ b/examples/iota_lens/run_iotalens_sdep.py @@ -65,7 +65,7 @@ # drift elements ds_half = ds / 2.0 -dr = elements.Drift(ds=ds_half) +dr = elements.Drift(name="dr", ds=ds_half) # define the nonlinear lens segments for j in range(0, num_lenses): @@ -76,13 +76,18 @@ ) knll_s = t_strength * c_parameter**2 * ds / beta cnll_s = c_parameter * math.sqrt(beta) - nllens = elements.NonlinearLens(knll=knll_s, cnll=cnll_s) + nllens = elements.NonlinearLens(name="nllens" + str(j), knll=knll_s, cnll=cnll_s) segments = [dr, nllens, dr] sim.lattice.extend(segments) # focusing lens const = elements.ConstF( - ds=1.0e-8, kx=12060.113295833, ky=12060.113295833, kt=1.0e-12, nslice=1 + name="const", + ds=1.0e-8, + kx=12060.113295833, + ky=12060.113295833, + kt=1.0e-12, + nslice=1, ) sim.lattice.append(const) sim.lattice.append(monitor) diff --git a/examples/kicker/run_kicker.py b/examples/kicker/run_kicker.py index 77cc624ea..c21a00f6c 100644 --- a/examples/kicker/run_kicker.py +++ b/examples/kicker/run_kicker.py @@ -46,8 +46,8 @@ # design the accelerator lattice kicklattice = [ monitor, - elements.Kicker(xkick=2.0e-3, ykick=0.0, unit="dimensionless"), - elements.Kicker(xkick=0.0, ykick=3.0e-3, unit="dimensionless"), + elements.Kicker(name="kick1", xkick=2.0e-3, ykick=0.0, unit="dimensionless"), + elements.Kicker(name="kick2", xkick=0.0, ykick=3.0e-3, unit="dimensionless"), monitor, ] # assign a lattice diff --git a/examples/kurth/run_kurth_10nC_periodic.py b/examples/kurth/run_kurth_10nC_periodic.py index b819a8ecb..62b3be319 100755 --- a/examples/kurth/run_kurth_10nC_periodic.py +++ b/examples/kurth/run_kurth_10nC_periodic.py @@ -47,8 +47,8 @@ # design the accelerator lattice nslice = 20 # use 30 for increased precision -constf1 = elements.ConstF(ds=2.0, kx=0.7, ky=0.7, kt=0.7, nslice=nslice) -drift1 = elements.Drift(ds=1.0, nslice=nslice) +constf1 = elements.ConstF(name="constf1", ds=2.0, kx=0.7, ky=0.7, kt=0.7, nslice=nslice) +drift1 = elements.Drift(name="drift1", ds=1.0, nslice=nslice) sim.lattice.extend([monitor, drift1, constf1, drift1, monitor]) # run simulation diff --git a/examples/kurth/run_kurth_periodic.py b/examples/kurth/run_kurth_periodic.py index 1d4d45aa1..6b6f60f0d 100755 --- a/examples/kurth/run_kurth_periodic.py +++ b/examples/kurth/run_kurth_periodic.py @@ -45,8 +45,8 @@ monitor = elements.BeamMonitor("monitor", backend="h5") # design the accelerator lattice -constf1 = elements.ConstF(ds=2.0, kx=0.7, ky=0.7, kt=0.7) -drift1 = elements.Drift(ds=1.0) +constf1 = elements.ConstF(name="constf1", ds=2.0, kx=0.7, ky=0.7, kt=0.7) +drift1 = elements.Drift(name="drift1", ds=1.0) sim.lattice.extend([monitor, drift1, constf1, drift1, monitor]) # run simulation diff --git a/examples/multipole/run_multipole.py b/examples/multipole/run_multipole.py index 656f751cb..a8a065351 100644 --- a/examples/multipole/run_multipole.py +++ b/examples/multipole/run_multipole.py @@ -46,9 +46,11 @@ # design the accelerator lattice multipole = [ monitor, - elements.Multipole(multipole=2, K_normal=3.0, K_skew=0.0), - elements.Multipole(multipole=3, K_normal=100.0, K_skew=-50.0), - elements.Multipole(multipole=4, K_normal=65.0, K_skew=6.0), + elements.Multipole(name="thin_quadrupole", multipole=2, K_normal=3.0, K_skew=0.0), + elements.Multipole( + name="thin_sextupole", multipole=3, K_normal=100.0, K_skew=-50.0 + ), + elements.Multipole(name="thin_octupole", multipole=4, K_normal=65.0, K_skew=6.0), monitor, ] # assign a fodo segment diff --git a/examples/optimize_triplet/run_triplet.py b/examples/optimize_triplet/run_triplet.py index 0766e315a..dc7466d15 100755 --- a/examples/optimize_triplet/run_triplet.py +++ b/examples/optimize_triplet/run_triplet.py @@ -43,13 +43,13 @@ def build_lattice(parameters: tuple, write_particles: bool) -> list: # enforce a mirror symmetry of the triplet line = [ - elements.Drift(ds=2.7, nslice=ns), - elements.Quad(ds=0.1, k=q1_k, nslice=ns), - elements.Drift(ds=1.4, nslice=ns), - elements.Quad(ds=0.2, k=q2_k, nslice=ns), - elements.Drift(ds=1.4, nslice=ns), - elements.Quad(ds=0.1, k=q1_k, nslice=ns), - elements.Drift(ds=2.7, nslice=ns), + elements.Drift(name="drift1", ds=2.7, nslice=ns), + elements.Quad(name="quad1", ds=0.1, k=q1_k, nslice=ns), + elements.Drift(name="drift2", ds=1.4, nslice=ns), + elements.Quad(name="quad2", ds=0.2, k=q2_k, nslice=ns), + elements.Drift(name="drift3", ds=1.4, nslice=ns), + elements.Quad(name="quad1", ds=0.1, k=q1_k, nslice=ns), + elements.Drift(name="drift4", ds=2.7, nslice=ns), ] if write_particles: diff --git a/examples/positron_channel/run_positron.py b/examples/positron_channel/run_positron.py index 069edcc0f..0f98ae161 100755 --- a/examples/positron_channel/run_positron.py +++ b/examples/positron_channel/run_positron.py @@ -50,14 +50,16 @@ ns = 1 # number of slices per ds in the element period = [ monitor, - elements.ChrQuad(ds=0.1, k=-6.674941, unit=1, nslice=ns), - elements.ChrDrift(ds=0.3, nslice=ns), - elements.ChrQuad(ds=0.2, k=6.674941, unit=1, nslice=ns), - elements.ChrDrift(ds=0.3, nslice=ns), - elements.ChrQuad(ds=0.1, k=-6.674941, unit=1, nslice=ns), - elements.ChrDrift(ds=0.1, nslice=ns), - elements.ChrAcc(ds=1.8, ez=10871.950994502130424, bz=1.0e-12, nslice=ns), - elements.ChrDrift(ds=0.1, nslice=ns), + elements.ChrQuad(name="quad1", ds=0.1, k=-6.674941, unit=1, nslice=ns), + elements.ChrDrift(name="drift1", ds=0.3, nslice=ns), + elements.ChrQuad(name="quad2", ds=0.2, k=6.674941, unit=1, nslice=ns), + elements.ChrDrift(name="drift2", ds=0.3, nslice=ns), + elements.ChrQuad(name="quad3", ds=0.1, k=-6.674941, unit=1, nslice=ns), + elements.ChrDrift(name="drift3", ds=0.1, nslice=ns), + elements.ChrAcc( + name="acc", ds=1.8, ez=10871.950994502130424, bz=1.0e-12, nslice=ns + ), + elements.ChrDrift(name="drift4", ds=0.1, nslice=ns), monitor, ] diff --git a/examples/quadrupole_softedge/run_quadrupole_softedge.py b/examples/quadrupole_softedge/run_quadrupole_softedge.py index 055d19080..8f74a3241 100755 --- a/examples/quadrupole_softedge/run_quadrupole_softedge.py +++ b/examples/quadrupole_softedge/run_quadrupole_softedge.py @@ -50,6 +50,7 @@ ns = 1 # number of slices per ds in the element quad1 = elements.SoftQuadrupole( + name="quad1", ds=1.0, gscale=1.0, cos_coefficients=[2], @@ -59,6 +60,7 @@ ) quad2 = elements.SoftQuadrupole( + name="quad2", ds=1.0, gscale=-1.0, cos_coefficients=[2], @@ -67,8 +69,8 @@ nslice=ns, ) -drift1 = elements.Drift(ds=0.25, nslice=ns) -drift2 = elements.Drift(ds=0.5, nslice=ns) +drift1 = elements.Drift(name="drift1", ds=0.25, nslice=ns) +drift2 = elements.Drift(name="drift2", ds=0.5, nslice=ns) # assign a fodo segment sim.lattice.extend([monitor, drift1, quad1, drift2, quad2, drift1, monitor]) diff --git a/examples/rfcavity/run_rfcavity.py b/examples/rfcavity/run_rfcavity.py index acdba68b4..af73ed12c 100755 --- a/examples/rfcavity/run_rfcavity.py +++ b/examples/rfcavity/run_rfcavity.py @@ -47,10 +47,11 @@ # design the accelerator lattice # Drift elements -dr1 = elements.Drift(ds=0.4, nslice=1) -dr2 = elements.Drift(ds=0.032997, nslice=1) +dr1 = elements.Drift(name="dr1", ds=0.4, nslice=1) +dr2 = elements.Drift(name="dr2", ds=0.032997, nslice=1) # RF cavity element rf = elements.RFCavity( + name="rf", ds=1.31879807, escale=62.0, freq=1.3e9, diff --git a/examples/rotation/run_rotation.py b/examples/rotation/run_rotation.py index a1257b302..0f1f187d1 100644 --- a/examples/rotation/run_rotation.py +++ b/examples/rotation/run_rotation.py @@ -46,9 +46,9 @@ # design the accelerator lattice rotated_drift = [ monitor, - elements.PRot(phi_in=0.0, phi_out=-5.0), - elements.Drift(ds=2.0, nslice=1), - elements.PRot(phi_in=-5.0, phi_out=0.0), + elements.PRot(name="rotation1", phi_in=0.0, phi_out=-5.0), + elements.Drift(name="drift1", ds=2.0, nslice=1), + elements.PRot(name="rotation2", phi_in=-5.0, phi_out=0.0), monitor, ] # assign a lattice segment diff --git a/examples/solenoid/run_solenoid.py b/examples/solenoid/run_solenoid.py index 78f02100a..fc0c517b4 100755 --- a/examples/solenoid/run_solenoid.py +++ b/examples/solenoid/run_solenoid.py @@ -48,7 +48,7 @@ sim.lattice.extend( [ monitor, - elements.Sol(ds=3.820395, ks=0.8223219329893234), + elements.Sol(name="sol1", ds=3.820395, ks=0.8223219329893234), monitor, ] ) diff --git a/examples/solenoid_softedge/run_solenoid_softedge.py b/examples/solenoid_softedge/run_solenoid_softedge.py index 0d93a7f31..c56aa236c 100755 --- a/examples/solenoid_softedge/run_solenoid_softedge.py +++ b/examples/solenoid_softedge/run_solenoid_softedge.py @@ -43,6 +43,7 @@ # design the accelerator lattice sol = elements.SoftSolenoid( + name="sol1", ds=6.0, bscale=1.233482899483985, cos_coefficients=[ diff --git a/examples/thin_dipole/run_thin_dipole.py b/examples/thin_dipole/run_thin_dipole.py index e29b320e7..4df695f9a 100755 --- a/examples/thin_dipole/run_thin_dipole.py +++ b/examples/thin_dipole/run_thin_dipole.py @@ -48,13 +48,15 @@ # design the accelerator lattice) ns = 1 # number of slices per ds in the element segment = [ - elements.Drift(ds=0.003926990816987, nslice=ns), - elements.ThinDipole(theta=0.45, rc=1.0), - elements.Drift(ds=0.003926990816987, nslice=ns), + elements.Drift(name="drift1", ds=0.003926990816987, nslice=ns), + elements.ThinDipole(name="kick", theta=0.45, rc=1.0), + elements.Drift(name="drift2", ds=0.003926990816987, nslice=ns), ] bend = 200 * segment -inverse_bend = elements.ExactSbend(ds=-1.570796326794897, phi=-90.0) +inverse_bend = elements.ExactSbend( + name="inverse_bend", ds=-1.570796326794897, phi=-90.0 +) sim.lattice.append(monitor) sim.lattice.extend(bend) diff --git a/src/initialization/InitElement.cpp b/src/initialization/InitElement.cpp index 9c5afd6b6..dd770e51f 100644 --- a/src/initialization/InitElement.cpp +++ b/src/initialization/InitElement.cpp @@ -123,13 +123,13 @@ namespace detail amrex::ParticleReal k; pp_element.get("k", k); - m_lattice.emplace_back( Quad(ds, k, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( Quad(ds, k, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "drift") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); - m_lattice.emplace_back( Drift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( Drift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "sbend") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -138,7 +138,7 @@ namespace detail amrex::ParticleReal rc; pp_element.get("rc", rc); - m_lattice.emplace_back( Sbend(ds, rc, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( Sbend(ds, rc, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "cfbend") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -148,7 +148,7 @@ namespace detail pp_element.get("rc", rc); pp_element.get("k", k); - m_lattice.emplace_back( CFbend(ds, rc, k, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( CFbend(ds, rc, k, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "dipedge") { auto a = detail::query_alignment(pp_element); @@ -159,7 +159,7 @@ namespace detail pp_element.get("g", g); pp_element.get("K2", K2); - m_lattice.emplace_back( DipEdge(psi, rc, g, K2, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( DipEdge(psi, rc, g, K2, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "constf") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -170,7 +170,7 @@ namespace detail pp_element.get("ky", ky); pp_element.get("kt", kt); - m_lattice.emplace_back( ConstF(ds, kx, ky, kt, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ConstF(ds, kx, ky, kt, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "buncher") { auto a = detail::query_alignment(pp_element); @@ -179,7 +179,7 @@ namespace detail pp_element.get("V", V); pp_element.get("k", k); - m_lattice.emplace_back( Buncher(V, k, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( Buncher(V, k, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "shortrf") { auto a = detail::query_alignment(pp_element); @@ -190,7 +190,7 @@ namespace detail pp_element.get("freq", freq); pp_element.queryAdd("phase", phase); - m_lattice.emplace_back( ShortRF(V, freq, phase, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( ShortRF(V, freq, phase, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "multipole") { auto a = detail::query_alignment(pp_element); @@ -201,7 +201,7 @@ namespace detail pp_element.get("k_normal", k_normal); pp_element.get("k_skew", k_skew); - m_lattice.emplace_back( Multipole(m, k_normal, k_skew, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( Multipole(m, k_normal, k_skew, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "nonlinear_lens") { auto a = detail::query_alignment(pp_element); @@ -210,7 +210,7 @@ namespace detail pp_element.get("knll", knll); pp_element.get("cnll", cnll); - m_lattice.emplace_back( NonlinearLens(knll, cnll, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( NonlinearLens(knll, cnll, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "rfcavity") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -228,7 +228,7 @@ namespace detail detail::queryAddResize(pp_element, "cos_coefficients", cos_coef); detail::queryAddResize(pp_element, "sin_coefficients", sin_coef); - m_lattice.emplace_back( RFCavity(ds, escale, freq, phase, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice) ); + m_lattice.emplace_back( RFCavity(ds, escale, freq, phase, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice, element_name) ); } else if (element_type == "solenoid") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -237,14 +237,14 @@ namespace detail amrex::ParticleReal ks; pp_element.get("ks", ks); - m_lattice.emplace_back( Sol(ds, ks, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( Sol(ds, ks, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "prot") { amrex::ParticleReal phi_in, phi_out; pp_element.get("phi_in", phi_in); pp_element.get("phi_out", phi_out); - m_lattice.emplace_back( PRot(phi_in, phi_out) ); + m_lattice.emplace_back( PRot(phi_in, phi_out, element_name) ); } else if (element_type == "solenoid_softedge") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -262,7 +262,7 @@ namespace detail detail::queryAddResize(pp_element, "cos_coefficients", cos_coef); detail::queryAddResize(pp_element, "sin_coefficients", sin_coef); - m_lattice.emplace_back( SoftSolenoid(ds, bscale, cos_coef, sin_coef, units, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice) ); + m_lattice.emplace_back( SoftSolenoid(ds, bscale, cos_coef, sin_coef, units, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice, element_name) ); } else if (element_type == "quadrupole_softedge") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -278,13 +278,13 @@ namespace detail detail::queryAddResize(pp_element, "cos_coefficients", cos_coef); detail::queryAddResize(pp_element, "sin_coefficients", sin_coef); - m_lattice.emplace_back( SoftQuadrupole(ds, gscale, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice) ); + m_lattice.emplace_back( SoftQuadrupole(ds, gscale, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], mapsteps, nslice, element_name) ); } else if (element_type == "drift_chromatic") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); - m_lattice.emplace_back( ChrDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ChrDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "quad_chromatic") { auto a = detail::query_alignment(pp_element); @@ -295,7 +295,7 @@ namespace detail pp_element.get("k", k); pp_element.queryAdd("units", units); - m_lattice.emplace_back( ChrQuad(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ChrQuad(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "plasma_lens_chromatic") { auto a = detail::query_alignment(pp_element); @@ -306,7 +306,7 @@ namespace detail pp_element.get("k", k); pp_element.queryAdd("units", units); - m_lattice.emplace_back( ChrPlasmaLens(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ChrPlasmaLens(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "tapered_plasma_lens") { auto a = detail::query_alignment(pp_element); @@ -318,13 +318,13 @@ namespace detail pp_element.get("taper", taper); pp_element.queryAdd("units", units); - m_lattice.emplace_back( TaperedPL(k, taper, units, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( TaperedPL(k, taper, units, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "drift_exact") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); - m_lattice.emplace_back( ExactDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ExactDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "sbend_exact") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -335,7 +335,7 @@ namespace detail pp_element.get("phi", phi); pp_element.queryAdd("B", B); - m_lattice.emplace_back( ExactSbend(ds, phi, B, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ExactSbend(ds, phi, B, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "uniform_acc_chromatic") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); @@ -345,7 +345,7 @@ namespace detail pp_element.get("ez", ez); pp_element.get("bz", bz); - m_lattice.emplace_back( ChrAcc(ds, ez, bz, a["dx"], a["dy"], a["rotation_degree"], nslice) ); + m_lattice.emplace_back( ChrAcc(ds, ez, bz, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); } else if (element_type == "thin_dipole") { auto a = detail::query_alignment(pp_element); @@ -354,7 +354,7 @@ namespace detail pp_element.get("theta", theta); pp_element.get("rc", rc); - m_lattice.emplace_back( ThinDipole(theta, rc, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( ThinDipole(theta, rc, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "kicker") { auto a = detail::query_alignment(pp_element); @@ -370,7 +370,7 @@ namespace detail Kicker::UnitSystem::dimensionless : Kicker::UnitSystem::Tm; - m_lattice.emplace_back( Kicker(xkick, ykick, units, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( Kicker(xkick, ykick, units, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "aperture") { auto a = detail::query_alignment(pp_element); @@ -386,7 +386,7 @@ namespace detail Aperture::Shape::rectangular : Aperture::Shape::elliptical; - m_lattice.emplace_back( Aperture(xmax, ymax, shape, a["dx"], a["dy"], a["rotation_degree"]) ); + m_lattice.emplace_back( Aperture(xmax, ymax, shape, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "beam_monitor") { std::string openpmd_name = element_name; diff --git a/src/particles/elements/All.H b/src/particles/elements/All.H index 6f7f72066..ba108c9fe 100644 --- a/src/particles/elements/All.H +++ b/src/particles/elements/All.H @@ -23,6 +23,7 @@ #include "ExactDrift.H" #include "ExactSbend.H" #include "Kicker.H" +#include "Marker.H" #include "Multipole.H" #include "Empty.H" #include "NonlinearLens.H" @@ -60,6 +61,7 @@ namespace impactx ExactDrift, ExactSbend, Kicker, + Marker, Multipole, NonlinearLens, Programmable, diff --git a/src/particles/elements/Aperture.H b/src/particles/elements/Aperture.H index 199712fb9..9c286870e 100644 --- a/src/particles/elements/Aperture.H +++ b/src/particles/elements/Aperture.H @@ -15,6 +15,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Aperture - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -33,12 +35,16 @@ namespace impactx static constexpr auto type = "Aperture"; using PType = ImpactXParticleContainer::ParticleType; + // TODO: make AMREX_ENUM and simplify @see shape_name implementation with it enum Shape { rectangular, elliptical }; + static std::string + shape_name (Shape const & shape); + /** A thin collimator element that applies a transverse aperture boundary. * Particles outside the boundary are considered lost. * @@ -48,6 +54,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ Aperture ( amrex::ParticleReal xmax, @@ -55,9 +62,11 @@ namespace impactx Shape shape, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_shape(shape), m_xmax(xmax), m_ymax(ymax) { } diff --git a/src/particles/elements/Aperture.cpp b/src/particles/elements/Aperture.cpp new file mode 100644 index 000000000..c647bca7c --- /dev/null +++ b/src/particles/elements/Aperture.cpp @@ -0,0 +1,28 @@ +/* Copyright 2022-2023 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Chad Mitchell, Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#include "Aperture.H" + +#include +#include + + +std::string +impactx::Aperture::shape_name (Shape const & shape) +{ + switch (shape) + { + case Aperture::Shape::rectangular : // default + return "rectangular"; + case Aperture::Shape::elliptical : + return "elliptical"; + default: + throw std::runtime_error("Unknown shape"); + } +} diff --git a/src/particles/elements/Buncher.H b/src/particles/elements/Buncher.H index 731682c0f..bccbdab6a 100644 --- a/src/particles/elements/Buncher.H +++ b/src/particles/elements/Buncher.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Buncher - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -40,15 +42,18 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ Buncher ( amrex::ParticleReal V, amrex::ParticleReal k, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_V(V), m_k(k) { } diff --git a/src/particles/elements/CFbend.H b/src/particles/elements/CFbend.H index b9007986c..bd42851ac 100644 --- a/src/particles/elements/CFbend.H +++ b/src/particles/elements/CFbend.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -26,7 +27,8 @@ namespace impactx { struct CFbend - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -47,6 +49,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ CFbend ( amrex::ParticleReal ds, @@ -55,9 +58,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_rc(rc), m_k(k) { diff --git a/src/particles/elements/CMakeLists.txt b/src/particles/elements/CMakeLists.txt index c7acb5785..dceec99c3 100644 --- a/src/particles/elements/CMakeLists.txt +++ b/src/particles/elements/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(lib PRIVATE + Aperture.cpp Programmable.cpp ) diff --git a/src/particles/elements/ChrDrift.H b/src/particles/elements/ChrDrift.H index 3080b41cf..e042bbff2 100644 --- a/src/particles/elements/ChrDrift.H +++ b/src/particles/elements/ChrDrift.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ChrDrift - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -43,15 +45,18 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ChrDrift ( amrex::ParticleReal ds, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree) { } diff --git a/src/particles/elements/ChrPlasmaLens.H b/src/particles/elements/ChrPlasmaLens.H index ca389b2e4..4a4dfbb3b 100644 --- a/src/particles/elements/ChrPlasmaLens.H +++ b/src/particles/elements/ChrPlasmaLens.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ChrPlasmaLens - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -49,6 +51,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ChrPlasmaLens ( amrex::ParticleReal ds, @@ -57,9 +60,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_k(k), m_unit(unit) { diff --git a/src/particles/elements/ChrQuad.H b/src/particles/elements/ChrQuad.H index 04a0787f4..7672fc3a9 100644 --- a/src/particles/elements/ChrQuad.H +++ b/src/particles/elements/ChrQuad.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -26,7 +27,8 @@ namespace impactx { struct ChrQuad - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -52,6 +54,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ChrQuad ( amrex::ParticleReal ds, @@ -60,9 +63,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_k(k), m_unit(unit) { diff --git a/src/particles/elements/ChrUniformAcc.H b/src/particles/elements/ChrUniformAcc.H index 458f955a1..9cb1087ce 100644 --- a/src/particles/elements/ChrUniformAcc.H +++ b/src/particles/elements/ChrUniformAcc.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ChrAcc - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -47,6 +49,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ChrAcc ( amrex::ParticleReal ds, @@ -55,9 +58,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_ez(ez), m_bz(bz) { diff --git a/src/particles/elements/ConstF.H b/src/particles/elements/ConstF.H index c8af6217e..a1b29c676 100644 --- a/src/particles/elements/ConstF.H +++ b/src/particles/elements/ConstF.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ConstF - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -43,6 +45,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ConstF ( amrex::ParticleReal ds, @@ -52,9 +55,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_kx(kx), m_ky(ky), m_kt(kt) { diff --git a/src/particles/elements/DipEdge.H b/src/particles/elements/DipEdge.H index ca0bc3b9e..e05ab0dbf 100644 --- a/src/particles/elements/DipEdge.H +++ b/src/particles/elements/DipEdge.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct DipEdge - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -49,6 +51,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ DipEdge ( amrex::ParticleReal psi, @@ -57,9 +60,11 @@ namespace impactx amrex::ParticleReal K2, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_psi(psi), m_rc(rc), m_g(g), m_K2(K2) { } diff --git a/src/particles/elements/Drift.H b/src/particles/elements/Drift.H index d20f466c1..fcb4ff6f8 100644 --- a/src/particles/elements/Drift.H +++ b/src/particles/elements/Drift.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Drift - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -40,15 +42,18 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ Drift ( amrex::ParticleReal ds, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree) { } diff --git a/src/particles/elements/Empty.H b/src/particles/elements/Empty.H index e0fd61b43..cf0dba130 100644 --- a/src/particles/elements/Empty.H +++ b/src/particles/elements/Empty.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -35,8 +36,8 @@ namespace impactx /** Push all particles - nothing to do here */ void operator() ( - ImpactXParticleContainer & /* pc */, - int /* step */ + ImpactXParticleContainer & /* pc */, + int /* step */ ) { // nothing to do } @@ -62,14 +63,14 @@ namespace impactx */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void operator() ( - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT x, - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT y, - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT t, - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT px, - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT py, - [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, - [[maybe_unused]] RefPart const & refpart + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT x, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT y, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT t, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT px, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT py, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT pt, + [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + [[maybe_unused]] RefPart const & refpart ) const { // nothing to do diff --git a/src/particles/elements/ExactDrift.H b/src/particles/elements/ExactDrift.H index 83d7384e7..2d976c300 100644 --- a/src/particles/elements/ExactDrift.H +++ b/src/particles/elements/ExactDrift.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ExactDrift - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -40,15 +42,18 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ExactDrift ( amrex::ParticleReal ds, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree) { } diff --git a/src/particles/elements/ExactSbend.H b/src/particles/elements/ExactSbend.H index ad2c6a5fd..f34cac473 100644 --- a/src/particles/elements/ExactSbend.H +++ b/src/particles/elements/ExactSbend.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -27,7 +28,8 @@ namespace impactx { struct ExactSbend - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -55,6 +57,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ ExactSbend ( amrex::ParticleReal ds, @@ -63,9 +66,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_phi(phi * degree2rad), m_B(B) { diff --git a/src/particles/elements/Kicker.H b/src/particles/elements/Kicker.H index fd4090a82..3150900ab 100644 --- a/src/particles/elements/Kicker.H +++ b/src/particles/elements/Kicker.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Kicker - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -48,6 +50,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ Kicker ( amrex::ParticleReal xkick, @@ -55,9 +58,11 @@ namespace impactx UnitSystem unit, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_xkick(xkick), m_ykick(ykick), m_unit(unit) { } diff --git a/src/particles/elements/Marker.H b/src/particles/elements/Marker.H new file mode 100644 index 000000000..46eff72a4 --- /dev/null +++ b/src/particles/elements/Marker.H @@ -0,0 +1,89 @@ +/* Copyright 2022-2023 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#ifndef IMPACTX_ELEMENT_MARKER_H +#define IMPACTX_ELEMENT_MARKER_H + +#include "particles/ImpactXParticleContainer.H" +#include "mixin/thin.H" +#include "mixin/named.H" +#include "mixin/nofinalize.H" + +#include +#include + + +namespace impactx +{ + struct Marker + : public elements::Named, + public elements::Thin, + public elements::NoFinalize + { + static constexpr auto type = "Marker"; + using PType = ImpactXParticleContainer::ParticleType; + + /** This named element does nothing. + * + * @param name a user defined and not necessarily unique name of the element + */ + Marker (std::string name) + : Named(name) + { + } + + /** Push all particles - nothing to do here */ + void operator() ( + ImpactXParticleContainer & /* pc */, + int /* step */ + ) { + // nothing to do + } + + /** Push all particles - nothing to do here */ + void operator() ( + ImpactXParticleContainer::iterator & /* pti */, + RefPart & AMREX_RESTRICT /* ref_part */ + ) { + // nothing to do + } + + /** Does nothing to a particle. + * + * @param x particle position in x + * @param y particle position in y + * @param t particle position in t + * @param px particle momentum in x + * @param py particle momentum in y + * @param pt particle momentum in t + * @param idcpu particle global index (unused) + * @param refpart reference particle + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + void operator() ( + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT x, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT y, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT t, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT px, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT py, + [[maybe_unused]] amrex::ParticleReal & AMREX_RESTRICT pt, + [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + [[maybe_unused]] RefPart const & refpart + ) const + { + // nothing to do + } + + /** This pushes the reference particle. */ + using Thin::operator(); + }; + +} // namespace impactx + +#endif // IMPACTX_ELEMENT_MARKER_H diff --git a/src/particles/elements/Multipole.H b/src/particles/elements/Multipole.H index c0e9fa693..6f4814a2c 100644 --- a/src/particles/elements/Multipole.H +++ b/src/particles/elements/Multipole.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Multipole - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -41,6 +43,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ Multipole ( int multipole, @@ -48,9 +51,11 @@ namespace impactx amrex::ParticleReal K_skew, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_multipole(multipole), m_Kn(K_normal), m_Ks(K_skew) { // compute factorial of multipole index diff --git a/src/particles/elements/NonlinearLens.H b/src/particles/elements/NonlinearLens.H index 9dfa0df40..016d6123a 100644 --- a/src/particles/elements/NonlinearLens.H +++ b/src/particles/elements/NonlinearLens.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct NonlinearLens - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -45,15 +47,18 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ NonlinearLens ( amrex::ParticleReal knll, amrex::ParticleReal cnll, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_knll(knll), m_cnll(cnll) { } diff --git a/src/particles/elements/PRot.H b/src/particles/elements/PRot.H index d08e7c009..cbd43118c 100644 --- a/src/particles/elements/PRot.H +++ b/src/particles/elements/PRot.H @@ -13,6 +13,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -27,7 +28,8 @@ namespace impactx { struct PRot - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::NoFinalize { @@ -43,12 +45,16 @@ namespace impactx * * @param phi_in Initial angle of reference trajectory w/r/t z (degrees) * @param phi_out Final angle of reference trajectory w/r/t/ z (degrees) + * @param name a user defined and not necessarily unique name of the element */ PRot ( amrex::ParticleReal phi_in, - amrex::ParticleReal phi_out + amrex::ParticleReal phi_out, + std::optional name = std::nullopt ) - : m_phi_in(phi_in * degree2rad), m_phi_out(phi_out * degree2rad) + : Named(name), + m_phi_in(phi_in * degree2rad), + m_phi_out(phi_out * degree2rad) { } diff --git a/src/particles/elements/Programmable.H b/src/particles/elements/Programmable.H index e679ccb6e..31e8b5fc3 100644 --- a/src/particles/elements/Programmable.H +++ b/src/particles/elements/Programmable.H @@ -10,8 +10,9 @@ #ifndef IMPACTX_ELEMENTS_PROGRAMMABLE_H #define IMPACTX_ELEMENTS_PROGRAMMABLE_H -#include "mixin/thick.H" #include "particles/ImpactXParticleContainer.H" +#include "mixin/named.H" +#include "mixin/thick.H" #include #include @@ -22,14 +23,23 @@ namespace impactx { struct Programmable + : public elements::Named { static constexpr auto type = "Programmable"; using PType = ImpactXParticleContainer::ParticleType; /** This element can be programmed + * + * @param ds Segment length in m + * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ - Programmable (amrex::ParticleReal ds=0.0, int nslice=1) - : m_ds(ds), m_nslice(nslice) + Programmable ( + amrex::ParticleReal ds=0.0, + int nslice=1, + std::optional name = std::nullopt + ) + : Named(name), m_ds(ds), m_nslice(nslice) {} /** Push all particles relative to the reference particle diff --git a/src/particles/elements/Quad.H b/src/particles/elements/Quad.H index d97121ebe..b4178048b 100644 --- a/src/particles/elements/Quad.H +++ b/src/particles/elements/Quad.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct Quad - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -44,6 +46,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ Quad ( amrex::ParticleReal ds, @@ -51,9 +54,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_k(k) { diff --git a/src/particles/elements/RFCavity.H b/src/particles/elements/RFCavity.H index 2a83a5d66..e4aae613e 100644 --- a/src/particles/elements/RFCavity.H +++ b/src/particles/elements/RFCavity.H @@ -14,6 +14,7 @@ #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" #include "mixin/beamoptic.H" +#include "mixin/named.H" #include "mixin/thick.H" #include @@ -102,7 +103,8 @@ namespace RFCavityData } // namespace RFCavityData struct RFCavity - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment { @@ -123,6 +125,7 @@ namespace RFCavityData * @param mapsteps number of integration steps per slice used for * map and reference particle push in applied fields * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ RFCavity ( amrex::ParticleReal ds, @@ -135,9 +138,11 @@ namespace RFCavityData amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, int mapsteps = 1, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_escale(escale), m_freq(freq), m_phase(phase), m_mapsteps(mapsteps) { diff --git a/src/particles/elements/Sbend.H b/src/particles/elements/Sbend.H index e4e5c75be..5fa7c0080 100644 --- a/src/particles/elements/Sbend.H +++ b/src/particles/elements/Sbend.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -26,7 +27,8 @@ namespace impactx { struct Sbend - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -42,6 +44,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ Sbend ( amrex::ParticleReal ds, @@ -49,9 +52,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_rc(rc) { diff --git a/src/particles/elements/ShortRF.H b/src/particles/elements/ShortRF.H index 543a66e27..fb8a4dcbd 100644 --- a/src/particles/elements/ShortRF.H +++ b/src/particles/elements/ShortRF.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct ShortRF - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -44,6 +46,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ ShortRF ( amrex::ParticleReal V, @@ -51,9 +54,11 @@ namespace impactx amrex::ParticleReal phase, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_V(V), m_freq(freq), m_phase(phase) { } diff --git a/src/particles/elements/SoftQuad.H b/src/particles/elements/SoftQuad.H index cfd50eb44..2637d638e 100644 --- a/src/particles/elements/SoftQuad.H +++ b/src/particles/elements/SoftQuad.H @@ -14,6 +14,7 @@ #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" #include "mixin/beamoptic.H" +#include "mixin/named.H" #include "mixin/thick.H" #include @@ -111,7 +112,8 @@ namespace SoftQuadrupoleData } // namespace SoftQuadrupoleData struct SoftQuadrupole - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment { @@ -130,6 +132,7 @@ namespace SoftQuadrupoleData * @param mapsteps number of integration steps per slice used for * map and reference particle push in applied fields * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ SoftQuadrupole ( amrex::ParticleReal ds, @@ -140,9 +143,11 @@ namespace SoftQuadrupoleData amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, int mapsteps = 1, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_gscale(gscale), m_mapsteps(mapsteps), m_id(SoftQuadrupoleData::next_id) { diff --git a/src/particles/elements/SoftSol.H b/src/particles/elements/SoftSol.H index 0151fc9c6..f5263fe20 100644 --- a/src/particles/elements/SoftSol.H +++ b/src/particles/elements/SoftSol.H @@ -14,6 +14,7 @@ #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" #include "mixin/beamoptic.H" +#include "mixin/named.H" #include "mixin/thick.H" #include @@ -116,7 +117,8 @@ namespace SoftSolenoidData } // namespace SoftSolenoidData struct SoftSolenoid - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment { @@ -140,6 +142,7 @@ namespace SoftSolenoidData * @param mapsteps number of integration steps per slice used for * map and reference particle push in applied fields * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ SoftSolenoid ( amrex::ParticleReal ds, @@ -151,9 +154,11 @@ namespace SoftSolenoidData amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, int mapsteps = 1, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_bscale(bscale), m_unit(unit), m_mapsteps(mapsteps), m_id(SoftSolenoidData::next_id) { diff --git a/src/particles/elements/Sol.H b/src/particles/elements/Sol.H index ab772f808..608f0d9ad 100644 --- a/src/particles/elements/Sol.H +++ b/src/particles/elements/Sol.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -26,7 +27,8 @@ namespace impactx { struct Sol - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thick, public elements::Alignment, public elements::NoFinalize @@ -43,6 +45,7 @@ namespace impactx * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] * @param nslice number of slices used for the application of space charge + * @param name a user defined and not necessarily unique name of the element */ Sol ( amrex::ParticleReal ds, @@ -50,9 +53,11 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, - int nslice = 1 + int nslice = 1, + std::optional name = std::nullopt ) - : Thick(ds, nslice), + : Named(name), + Thick(ds, nslice), Alignment(dx, dy, rotation_degree), m_ks(ks) { diff --git a/src/particles/elements/TaperedPL.H b/src/particles/elements/TaperedPL.H index fb618d823..9a107679e 100644 --- a/src/particles/elements/TaperedPL.H +++ b/src/particles/elements/TaperedPL.H @@ -14,6 +14,7 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include @@ -25,7 +26,8 @@ namespace impactx { struct TaperedPL - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -46,6 +48,7 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ TaperedPL ( amrex::ParticleReal k, @@ -53,9 +56,11 @@ namespace impactx int unit, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_k(k), m_taper(taper), m_unit(unit) { } diff --git a/src/particles/elements/ThinDipole.H b/src/particles/elements/ThinDipole.H index 5bd1e6633..84bcc57e5 100644 --- a/src/particles/elements/ThinDipole.H +++ b/src/particles/elements/ThinDipole.H @@ -14,15 +14,18 @@ #include "mixin/alignment.H" #include "mixin/beamoptic.H" #include "mixin/thin.H" +#include "mixin/named.H" #include "mixin/nofinalize.H" #include #include + namespace impactx { struct ThinDipole - : public elements::BeamOptic, + : public elements::Named, + public elements::BeamOptic, public elements::Thin, public elements::Alignment, public elements::NoFinalize @@ -44,15 +47,18 @@ namespace impactx * @param dx horizontal translation error in m * @param dy vertical translation error in m * @param rotation_degree rotation error in the transverse plane [degrees] + * @param name a user defined and not necessarily unique name of the element */ ThinDipole ( amrex::ParticleReal theta, amrex::ParticleReal rc, amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, - amrex::ParticleReal rotation_degree = 0 + amrex::ParticleReal rotation_degree = 0, + std::optional name = std::nullopt ) - : Alignment(dx, dy, rotation_degree), + : Named(name), + Alignment(dx, dy, rotation_degree), m_theta(theta * degree2rad), m_rc(rc) { diff --git a/src/particles/elements/mixin/named.H b/src/particles/elements/mixin/named.H new file mode 100644 index 000000000..f06a6d86c --- /dev/null +++ b/src/particles/elements/mixin/named.H @@ -0,0 +1,153 @@ +/* Copyright 2022-2024 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#ifndef IMPACTX_ELEMENTS_MIXIN_NAMED_H +#define IMPACTX_ELEMENTS_MIXIN_NAMED_H + +#include "particles/ImpactXParticleContainer.H" + +#include +#include + +#include +#include +#include +#include + + +namespace impactx::elements +{ + /** This is a helper class for lattice elements with a user-defined name + */ + struct Named + { + /** Overwrite the name of the element + * + * @param new_name set a new name on the element + */ + AMREX_GPU_HOST + void set_name ( + std::string const & new_name + ) + { + // free old name + if (m_name != nullptr) { + delete[] m_name; + m_name = nullptr; + } + + // set new name + if (new_name.size() > 0) { + m_name = new char[new_name.size() + 1]; + std::strcpy(m_name, new_name.c_str()); + } + } + + /** A user-named element + * + * @param name a user defined and not necessarily unique name of the element + */ + AMREX_GPU_HOST + Named ( + std::optional name + ) + { + if (name.has_value()) { + set_name(*name); + } + } + + AMREX_GPU_HOST_DEVICE + ~Named () + { + AMREX_IF_ON_HOST(( + if (m_name != nullptr) { + delete[] m_name; + m_name = nullptr; + } + )) + } + + AMREX_GPU_HOST + Named (Named const & other) + { + if (other.has_name()) { + m_name = new char[std::strlen(other.m_name) + 1]; + std::strcpy(m_name, other.m_name); + } + } + + AMREX_GPU_HOST + Named& operator=(Named const & other) + { + if (&other != this) { + if (other.has_name()) { + m_name = new char[std::strlen(other.m_name) + 1]; + std::strcpy(m_name, other.m_name); + } + } + return *this; + } + + AMREX_GPU_HOST_DEVICE + Named ([[maybe_unused]] Named && other) noexcept + { + AMREX_IF_ON_HOST(( + std::swap(this->m_name, other.m_name); + other.m_name = nullptr; + )) + } + + AMREX_GPU_HOST_DEVICE + Named& operator=([[maybe_unused]] Named && other) noexcept + { + AMREX_IF_ON_HOST(( + std::swap(this->m_name, other.m_name); + other.m_name = nullptr; + )) + return *this; + } + + /** Return the user provided name of the element + * + * @return user defined and not necessarily unique name + */ + AMREX_FORCE_INLINE + std::string name () const + { + if (!has_name()) { + throw std::runtime_error("Name not set on element!"); + } + return std::string(m_name); + } + + /** Return true if the user provided a name for this element + * + * @return true if the user provided a name + */ + AMREX_FORCE_INLINE + bool has_name () const + { + return m_name != nullptr; + } + + private: + // Implementation note: + // This is used as a mixin class in elements that are copied to GPU. GPU compilers copy + // a whole element by its sizeof(T). + // To save on this copy operation at kernel start, this is implemented + // as a simple C pointer (8 bytes), contrary to a std::string (32 bytes) or + // a std::optional (40 bytes). m_name points to host-side memory and + // must not be dereferenced (used) on GPU. + char * m_name = nullptr; //! a user defined and not necessarily unique name of the element + }; + +} // namespace impactx::elements + +#endif // IMPACTX_ELEMENTS_MIXIN_NAMED_H diff --git a/src/python/elements.cpp b/src/python/elements.cpp index 82f6ec6ee..7d4a33f76 100644 --- a/src/python/elements.cpp +++ b/src/python/elements.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include @@ -62,6 +64,73 @@ namespace "Push first the reference particle, then all other particles." ); } + + /** Helper to format {key, value} pairs + * + * Expected outcome is ", key=value" with key as a string and appropriate formatting for value. + * + * @tparam T value type + * @param arg a key-value pair + * @return a string of the form ", key=value" + */ + template + std::string + format_extra (std::pair const & arg) + { + if constexpr (std::is_floating_point_v) + { + // float + // TODO: format as scientific number + return std::string(", ") + .append(arg.first) + .append("=") + .append(std::to_string(arg.second)); + } + else if constexpr (std::is_integral_v) + { + // int + return std::string(", ") + .append(arg.first) + .append("=") + .append(std::to_string(arg.second)); + } else + { + // already a string + return std::string(", ") + .append(arg.first) + .append("=") + .append(arg.second); + } + } + + /** Helper to build a __repr__ for an Element + * + * @tparam T_Element type for the C++ element type + * @tparam ExtraArgs type for pairs of name, value to add + * @param el the current element + * @param args pars of name, value to add + * @return a string suitable for Python's __repr__ + */ + template + std::string element_name (T_Element const & el, std::pair const &... args) + { + // Fixed element type name, e.g., "SBend" + std::string const type = T_Element::type; + + // User-provided element name, e.g., "name=bend1" + std::string const name = el.has_name() ? ", name=" + el.name() : ""; + + // Noteworthy element parameters, e.g., "ds=2.3, key=value, ..." + std::string extra_args; + ((extra_args.append(format_extra(args))), ...); + + // combine it all together + return ""; + } } void init_elements(py::module& m) @@ -73,6 +142,15 @@ void init_elements(py::module& m) // mixin classes + py::class_(me, "Named") + .def_property("name", + [](elements::Named & nm) { return nm.name(); }, + [](elements::Named & nm, std::string new_name) { nm.set_name(new_name); }, + "segment length in m" + ) + .def_property_readonly("has_name", &elements::Named::has_name) + ; + py::class_(me, "Thick") .def(py::init< amrex::ParticleReal, @@ -194,11 +272,14 @@ void init_elements(py::module& m) // beam optics - py::class_ py_Aperture(me, "Aperture"); + py::class_ py_Aperture(me, "Aperture"); py_Aperture .def("__repr__", - [](Aperture const & /* ap */) { - return std::string(""); + [](Aperture const & ap) { + return element_name( + ap, + std::make_pair("shape", ap.shape_name(ap.m_shape)) + ); } ) .def(py::init([]( @@ -207,7 +288,8 @@ void init_elements(py::module& m) std::string const & shape, amrex::ParticleReal dx, amrex::ParticleReal dy, - amrex::ParticleReal rotation_degree + amrex::ParticleReal rotation_degree, + std::optional name ) { if (shape != "rectangular" && shape != "elliptical") @@ -216,7 +298,7 @@ void init_elements(py::module& m) Aperture::Shape const s = shape == "rectangular" ? Aperture::Shape::rectangular : Aperture::Shape::elliptical; - return new Aperture(xmax, ymax, s, dx, dy, rotation_degree); + return new Aperture(xmax, ymax, s, dx, dy, rotation_degree, name); }), py::arg("xmax"), py::arg("ymax"), @@ -224,20 +306,13 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "A short collimator element applying a transverse aperture boundary." ) .def_property("shape", [](Aperture & ap) { - switch (ap.m_shape) - { - case Aperture::Shape::rectangular : // default - return "rectangular"; - case Aperture::Shape::elliptical : - return "elliptical"; - default: - throw std::runtime_error("Unknown shape"); - } + return ap.shape_name(ap.m_shape); }, [](Aperture & ap, std::string const & shape) { @@ -263,14 +338,14 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Aperture); - py::class_ py_ChrDrift(me, "ChrDrift"); + py::class_ py_ChrDrift(me, "ChrDrift"); py_ChrDrift .def("__repr__", [](ChrDrift const & chr_drift) { - std::string r = ""); - return r; + return element_name( + chr_drift, + std::make_pair("ds", chr_drift.ds()) + ); } ) .def(py::init< @@ -278,28 +353,29 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A Drift with chromatic effects included." ) ; register_beamoptics_push(py_ChrDrift); - py::class_ py_ChrQuad(me, "ChrQuad"); + py::class_ py_ChrQuad(me, "ChrQuad"); py_ChrQuad .def("__repr__", [](ChrQuad const & chr_quad) { - std::string r = ""); - return r; + return element_name( + chr_quad, + std::make_pair("ds", chr_quad.ds()), + std::make_pair("k", chr_quad.m_k) + ); } ) .def(py::init< @@ -309,7 +385,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("k"), @@ -318,6 +395,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A Quadrupole magnet with chromatic effects included." ) .def_property("k", @@ -333,16 +411,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrQuad); - py::class_ py_ChrPlasmaLens(me, "ChrPlasmaLens"); + py::class_ py_ChrPlasmaLens(me, "ChrPlasmaLens"); py_ChrPlasmaLens .def("__repr__", [](ChrPlasmaLens const & chr_pl_lens) { - std::string r = ""); - return r; + return element_name( + chr_pl_lens, + std::make_pair("ds", chr_pl_lens.ds()), + std::make_pair("k", chr_pl_lens.m_k) + ); } ) .def(py::init< @@ -352,7 +429,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("k"), @@ -361,6 +439,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An active Plasma Lens with chromatic effects included." ) .def_property("k", @@ -376,18 +455,16 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrPlasmaLens); - py::class_ py_ChrAcc(me, "ChrAcc"); + py::class_ py_ChrAcc(me, "ChrAcc"); py_ChrAcc .def("__repr__", [](ChrAcc const & chr_acc) { - std::string r = ""); - return r; + return element_name( + chr_acc, + std::make_pair("ds", chr_acc.ds()), + std::make_pair("ez", chr_acc.m_ez), + std::make_pair("bz", chr_acc.m_bz) + ); } ) .def(py::init< @@ -397,7 +474,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("ez"), @@ -406,6 +484,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A region of Uniform Acceleration, with chromatic effects included." ) .def_property("ez", @@ -421,20 +500,17 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrAcc); - py::class_ py_ConstF(me, "ConstF"); + py::class_ py_ConstF(me, "ConstF"); py_ConstF .def("__repr__", [](ConstF const & constf) { - std::string r = ""); - return r; + return element_name( + constf, + std::make_pair("ds", constf.ds()), + std::make_pair("kx", constf.m_kx), + std::make_pair("ky", constf.m_ky), + std::make_pair("kt", constf.m_kt) + ); } ) .def(py::init< @@ -445,7 +521,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("kx"), @@ -455,6 +532,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A linear Constant Focusing element." ) .def_property("kx", @@ -475,20 +553,17 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ConstF); - py::class_ py_DipEdge(me, "DipEdge"); + py::class_ py_DipEdge(me, "DipEdge"); py_DipEdge .def("__repr__", [](DipEdge const & dip_edge) { - std::string r = ""); - return r; + return element_name( + dip_edge, + std::make_pair("psi", dip_edge.m_psi), + std::make_pair("rc", dip_edge.m_rc), + std::make_pair("g", dip_edge.m_g), + std::make_pair("K2", dip_edge.m_K2) + ); } ) .def(py::init< @@ -498,7 +573,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("psi"), py::arg("rc"), @@ -507,6 +583,7 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "Edge focusing associated with bend entry or exit." ) .def_property("psi", @@ -532,14 +609,14 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_DipEdge); - py::class_ py_Drift(me, "Drift"); + py::class_ py_Drift(me, "Drift"); py_Drift .def("__repr__", [](Drift const & drift) { - std::string r = ""); - return r; + return element_name( + drift, + std::make_pair("ds", drift.ds()) + ); } ) .def(py::init< @@ -547,26 +624,28 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A drift." ) ; register_beamoptics_push(py_Drift); - py::class_ py_ExactDrift(me, "ExactDrift"); + py::class_ py_ExactDrift(me, "ExactDrift"); py_ExactDrift .def("__repr__", [](ExactDrift const & exact_drift) { - std::string r = ""); - return r; + return element_name( + exact_drift, + std::make_pair("ds", exact_drift.ds()) + ); } ) .def(py::init< @@ -574,30 +653,30 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A Drift using the exact nonlinear map." ) ; register_beamoptics_push(py_ExactDrift); - py::class_ py_ExactSbend(me, "ExactSbend"); + py::class_ py_ExactSbend(me, "ExactSbend"); py_ExactSbend .def("__repr__", [](ExactSbend const & exact_sbend) { - std::string r = ""); - return r; + return element_name( + exact_sbend, + std::make_pair("ds", exact_sbend.ds()), + std::make_pair("phi", exact_sbend.m_phi), + std::make_pair("B", exact_sbend.m_B) + ); } ) .def(py::init< @@ -607,7 +686,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("phi"), @@ -616,6 +696,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An ideal sector bend using the exact nonlinear map. When B = 0, the reference bending radius is defined by r0 = length / (angle in rad), corresponding to a magnetic field of B = rigidity / r0; otherwise the reference bending radius is defined by r0 = rigidity / B." ) .def_property("phi", @@ -631,16 +712,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ExactSbend); - py::class_ py_Kicker(me, "Kicker"); + py::class_ py_Kicker(me, "Kicker"); py_Kicker .def("__repr__", [](Kicker const & kicker) { - std::string r = ""); - return r; + return element_name( + kicker, + std::make_pair("xkick", kicker.m_xkick), + std::make_pair("ykick", kicker.m_ykick) + ); } ) .def(py::init([]( @@ -649,7 +729,8 @@ void init_elements(py::module& m) std::string const & unit, amrex::ParticleReal dx, amrex::ParticleReal dy, - amrex::ParticleReal rotation_degree + amrex::ParticleReal rotation_degree, + std::optional name ) { if (unit != "dimensionless" && unit != "T-m") @@ -658,14 +739,15 @@ void init_elements(py::module& m) Kicker::UnitSystem const u = unit == "dimensionless" ? Kicker::UnitSystem::dimensionless : Kicker::UnitSystem::Tm; - return new Kicker(xkick, ykick, u, dx, dy, rotation_degree); + return new Kicker(xkick, ykick, u, dx, dy, rotation_degree, name); }), - py::arg("xkick"), + py::arg("xkick"), py::arg("ykick"), py::arg("unit") = "dimensionless", py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), R"(A thin transverse kicker element. Kicks are for unit "dimensionless" or in "T-m".)" ) .def_property("xkick", @@ -682,18 +764,17 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Kicker); - py::class_ py_Multipole(me, "Multipole"); + py::class_ py_Multipole(me, "Multipole"); py_Multipole .def("__repr__", [](Multipole const & multipole) { - std::string r = ""); - return r; + return element_name( + multipole, + std::make_pair("multipole", multipole.m_multipole), + std::make_pair("mfactorial", multipole.m_mfactorial), + std::make_pair("K_normal", multipole.m_Kn), + std::make_pair("K_skew", multipole.m_Ks) + ); } ) .def(py::init< @@ -702,7 +783,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("multipole"), py::arg("K_normal"), @@ -710,6 +792,7 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "A general thin multipole element." ) .def_property("multipole", @@ -730,10 +813,10 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Multipole); - py::class_ py_None(me, "Empty"); - py_None + py::class_ py_Empty(me, "Empty"); + py_Empty .def("__repr__", - [](Empty const & /* none */) { + [](Empty const & /* empty */) { return std::string(""); } ) @@ -741,18 +824,30 @@ void init_elements(py::module& m) "This element does nothing." ) ; - register_beamoptics_push(py_None); + register_beamoptics_push(py_Empty); - py::class_ py_NonlinearLens(me, "NonlinearLens"); + py::class_ py_Marker(me, "Marker"); + py_Marker + .def("__repr__", + [](Marker const & marker) { + return element_name(marker); + } + ) + .def(py::init(), + "This named element does nothing." + ) + ; + register_beamoptics_push(py_Marker); + + py::class_ py_NonlinearLens(me, "NonlinearLens"); py_NonlinearLens .def("__repr__", [](NonlinearLens const & nl) { - std::string r = ""); - return r; + return element_name( + nl, + std::make_pair("knll", nl.m_knll), + std::make_pair("cnll", nl.m_cnll) + ); } ) .def(py::init< @@ -760,13 +855,15 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("knll"), py::arg("cnll"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "Single short segment of the nonlinear magnetic insert element." ) .def_property("knll", @@ -782,20 +879,23 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_NonlinearLens); - py::class_(me, "Programmable", py::dynamic_attr()) + py::class_(me, "Programmable", py::dynamic_attr()) .def("__repr__", [](Programmable const & prg) { - std::string r = ""); - return r; + return element_name( + prg, + std::make_pair("ds", prg.ds()) + ); } ) .def(py::init< amrex::ParticleReal, - int + int, + std::optional >(), - py::arg("ds") = 0.0, py::arg("nslice") = 1, + py::arg("ds") = 0.0, + py::arg("nslice") = 1, + py::arg("name") = py::none(), "A programmable beam optics element." ) .def_property("nslice", @@ -834,16 +934,15 @@ void init_elements(py::module& m) ) ; - py::class_ py_Quad(me, "Quad"); + py::class_ py_Quad(me, "Quad"); py_Quad .def("__repr__", [](Quad const & quad) { - std::string r = ""); - return r; + return element_name( + quad, + std::make_pair("ds", quad.ds()), + std::make_pair("k", quad.m_k) + ); } ) .def(py::init< @@ -852,7 +951,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("k"), @@ -860,6 +960,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A Quadrupole magnet." ) .def_property("k", @@ -870,20 +971,17 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Quad); - py::class_ py_RFCavity(me, "RFCavity"); + py::class_ py_RFCavity(me, "RFCavity"); py_RFCavity .def("__repr__", [](RFCavity const & rfc) { - std::string r = ""); - return r; + return element_name( + rfc, + std::make_pair("ds", rfc.ds()), + std::make_pair("escale", rfc.m_escale), + std::make_pair("freq", rfc.m_freq), + std::make_pair("phase", rfc.m_phase) + ); } ) .def(py::init< @@ -897,7 +995,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, int, - int + int, + std::optional >(), py::arg("ds"), py::arg("escale"), @@ -910,6 +1009,7 @@ void init_elements(py::module& m) py::arg("rotation") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An RF cavity." ) .def_property("escale", @@ -937,16 +1037,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_RFCavity); - py::class_ py_Sbend(me, "Sbend"); + py::class_ py_Sbend(me, "Sbend"); py_Sbend .def("__repr__", [](Sbend const & sbend) { - std::string r = ""); - return r; + return element_name( + sbend, + std::make_pair("ds", sbend.ds()), + std::make_pair("rc", sbend.m_rc) + ); } ) .def(py::init< @@ -955,7 +1054,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("rc"), @@ -963,6 +1063,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An ideal sector bend." ) .def_property("rc", @@ -973,18 +1074,16 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Sbend); - py::class_ py_CFbend(me, "CFbend"); + py::class_ py_CFbend(me, "CFbend"); py_CFbend .def("__repr__", [](CFbend const & cfbend) { - std::string r = ""); - return r; + return element_name( + cfbend, + std::make_pair("ds", cfbend.ds()), + std::make_pair("rc", cfbend.m_rc), + std::make_pair("k", cfbend.m_k) + ); } ) .def(py::init< @@ -994,7 +1093,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("rc"), @@ -1003,6 +1103,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An ideal combined function bend (sector bend with quadrupole component)." ) .def_property("rc", @@ -1018,16 +1119,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_CFbend); - py::class_ py_Buncher(me, "Buncher"); + py::class_ py_Buncher(me, "Buncher"); py_Buncher .def("__repr__", [](Buncher const & buncher) { - std::string r = ""); - return r; + return element_name( + buncher, + std::make_pair("V", buncher.m_V), + std::make_pair("k", buncher.m_k) + ); } ) .def(py::init< @@ -1035,13 +1135,15 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("V"), py::arg("k"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "A short linear RF cavity element at zero-crossing for bunching." ) .def_property("V", @@ -1057,18 +1159,16 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Buncher); - py::class_ py_ShortRF(me, "ShortRF"); + py::class_ py_ShortRF(me, "ShortRF"); py_ShortRF .def("__repr__", [](ShortRF const & short_rf) { - std::string r = ""); - return r; + return element_name( + short_rf, + std::make_pair("V", short_rf.m_V), + std::make_pair("freq", short_rf.m_freq), + std::make_pair("phase", short_rf.m_phase) + ); } ) .def(py::init< @@ -1077,7 +1177,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("V"), py::arg("freq"), @@ -1085,6 +1186,7 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "A short RF cavity element." ) .def_property("V", @@ -1105,16 +1207,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ShortRF); - py::class_ py_SoftSolenoid(me, "SoftSolenoid"); + py::class_ py_SoftSolenoid(me, "SoftSolenoid"); py_SoftSolenoid .def("__repr__", [](SoftSolenoid const & soft_sol) { - std::string r = ""); - return r; + return element_name( + soft_sol, + std::make_pair("ds", soft_sol.ds()), + std::make_pair("bscale", soft_sol.m_bscale) + ); } ) .def(py::init< @@ -1127,7 +1228,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, int, - int + int, + std::optional >(), py::arg("ds"), py::arg("bscale"), @@ -1139,6 +1241,7 @@ void init_elements(py::module& m) py::arg("rotation") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A soft-edge solenoid." ) .def_property("bscale", @@ -1161,16 +1264,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_SoftSolenoid); - py::class_ py_Sol(me, "Sol"); + py::class_ py_Sol(me, "Sol"); py_Sol .def("__repr__", - [](Sol const & soft_sol) { - std::string r = ""); - return r; + [](Sol const & sol) { + return element_name( + sol, + std::make_pair("ds", sol.ds()), + std::make_pair("ks", sol.m_ks) + ); } ) .def(py::init< @@ -1179,7 +1281,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - int + int, + std::optional >(), py::arg("ds"), py::arg("ks"), @@ -1187,6 +1290,7 @@ void init_elements(py::module& m) py::arg("dy") = 0, py::arg("rotation") = 0, py::arg("nslice") = 1, + py::arg("name") = py::none(), "An ideal hard-edge Solenoid magnet." ) .def_property("ks", @@ -1197,24 +1301,25 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Sol); - py::class_ py_PRot(me, "PRot"); + py::class_ py_PRot(me, "PRot"); py_PRot .def("__repr__", [](PRot const & prot) { - std::string r = ""); - return r; + return element_name( + prot, + std::make_pair("phi_in", prot.m_phi_in), + std::make_pair("phi_out", prot.m_phi_out) + ); } ) .def(py::init< amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("phi_in"), py::arg("phi_out"), + py::arg("name") = py::none(), "An exact pole-face rotation in the x-z plane. Both angles are in degrees." ) .def_property("phi_in", @@ -1230,16 +1335,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_PRot); - py::class_ py_SoftQuadrupole(me, "SoftQuadrupole"); + py::class_ py_SoftQuadrupole(me, "SoftQuadrupole"); py_SoftQuadrupole .def("__repr__", [](SoftQuadrupole const & soft_quad) { - std::string r = ""); - return r; + return element_name( + soft_quad, + std::make_pair("ds", soft_quad.ds()), + std::make_pair("gscale", soft_quad.m_gscale) + ); } ) .def(py::init< @@ -1251,7 +1355,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, int, - int + int, + std::optional >(), py::arg("ds"), py::arg("gscale"), @@ -1262,6 +1367,7 @@ void init_elements(py::module& m) py::arg("rotation") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, + py::arg("name") = py::none(), "A soft-edge quadrupole." ) .def_property("gscale", @@ -1279,16 +1385,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_SoftQuadrupole); - py::class_ py_ThinDipole(me, "ThinDipole"); + py::class_ py_ThinDipole(me, "ThinDipole"); py_ThinDipole .def("__repr__", [](ThinDipole const & thin_dp) { - std::string r = ""); - return r; + return element_name( + thin_dp, + std::make_pair("theta", thin_dp.m_theta), + std::make_pair("rc", thin_dp.m_rc) + ); } ) .def(py::init< @@ -1296,13 +1401,15 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("theta"), py::arg("rc"), py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), "A thin kick model of a dipole bend." ) .def_property("theta", @@ -1318,17 +1425,15 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ThinDipole); - py::class_ py_TaperedPL(me, "TaperedPL"); + py::class_ py_TaperedPL(me, "TaperedPL"); py_TaperedPL .def("__repr__", [](TaperedPL const & taperedpl) { - std::string r = ""); - return r; + return element_name( + taperedpl, + std::make_pair("k", taperedpl.m_k), + std::make_pair("taper", taperedpl.m_taper) + ); } ) .def(py::init< @@ -1337,7 +1442,8 @@ void init_elements(py::module& m) int, amrex::ParticleReal, amrex::ParticleReal, - amrex::ParticleReal + amrex::ParticleReal, + std::optional >(), py::arg("k"), py::arg("taper"), @@ -1345,6 +1451,7 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("name") = py::none(), R"doc(A thin nonlinear plasma lens with transverse (horizontal) taper .. math:: diff --git a/src/python/impactx/madx_to_impactx.py b/src/python/impactx/madx_to_impactx.py index 56a8881b9..59c1da1bb 100644 --- a/src/python/impactx/madx_to_impactx.py +++ b/src/python/impactx/madx_to_impactx.py @@ -66,22 +66,27 @@ def lattice(parsed_beamline, nslice=1): # print(d) if d["type"] in [k.casefold() for k in list(madx_to_impactx_dict.keys())]: if d["type"] == "drift": - impactx_beamline.append(elements.Drift(ds=d["l"], nslice=nslice)) + impactx_beamline.append( + elements.Drift(name=d["name"], ds=d["l"], nslice=nslice) + ) elif d["type"] == "quadrupole": impactx_beamline.append( - elements.Quad(ds=d["l"], k=d["k1"], nslice=nslice) + elements.Quad(name=d["name"], ds=d["l"], k=d["k1"], nslice=nslice) ) elif d["type"] == "sbend": impactx_beamline.append( - elements.Sbend(ds=d["l"], rc=d["l"] / d["angle"], nslice=nslice) + elements.Sbend( + name=d["name"], ds=d["l"], rc=d["l"] / d["angle"], nslice=nslice + ) ) elif d["type"] == "solenoid": impactx_beamline.append( - elements.Sol(ds=d["l"], ks=d["ks"], nslice=nslice) + elements.Sol(name=d["name"], ds=d["l"], ks=d["ks"], nslice=nslice) ) elif d["type"] == "dipedge": impactx_beamline.append( elements.DipEdge( + name=d["name"], psi=d["e1"], rc=1.0 / d["h"], # MAD-X is using half the gap height @@ -92,14 +97,21 @@ def lattice(parsed_beamline, nslice=1): elif d["type"] == "kicker": impactx_beamline.append( elements.Kicker( + name=d["name"], xkick=d["hkick"], ykick=d["vkick"], ) ) elif d["type"] == "monitor": if d["l"] > 0: - impactx_beamline.append(elements.Drift(ds=d["l"], nslice=nslice)) - impactx_beamline.append(elements.BeamMonitor("monitor", backend="h5")) + impactx_beamline.append( + elements.Drift( + name=d["name"] + "_drift", ds=d["l"], nslice=nslice + ) + ) + impactx_beamline.append( + elements.BeamMonitor(name="monitor", backend="h5") + ) # TODO: use name=d["name"] ? else: raise NotImplementedError( "The beamline element named ", diff --git a/tests/python/test_dataframe.py b/tests/python/test_dataframe.py index 42dc0bb4c..db73ab530 100644 --- a/tests/python/test_dataframe.py +++ b/tests/python/test_dataframe.py @@ -57,11 +57,11 @@ def test_df_pandas(save_png=True): # init accelerator lattice fodo = [ - elements.Drift(0.25), - elements.Quad(1.0, 1.0), - elements.Drift(0.5), - elements.Quad(1.0, -1.0), - elements.Drift(0.25), + elements.Drift(name="d1", ds=0.25), + elements.Quad(name="q1", ds=1.0, k=1.0), + elements.Drift(name="d2", ds=0.5), + elements.Quad(name="q2", ds=1.0, k=-1.0), + elements.Drift(name="d3", ds=0.25), ] sim.lattice.extend(fodo) diff --git a/tests/python/test_impactx.py b/tests/python/test_impactx.py index d0b086a59..871746770 100755 --- a/tests/python/test_impactx.py +++ b/tests/python/test_impactx.py @@ -112,11 +112,11 @@ def test_impactx_nofile(): # init accelerator lattice fodo = [ - elements.Drift(0.25), - elements.Quad(1.0, 1.0), - elements.Drift(0.5), - elements.Quad(1.0, -1.0), - elements.Drift(0.25), + elements.Drift(name="d1", ds=0.25), + elements.Quad(name="q1", ds=1.0, k=1.0), + elements.Drift(name="d2", ds=0.5), + elements.Quad(name="q2", ds=1.0, k=-1.0), + elements.Drift(name="d3", ds=0.25), ] # assign a fodo segment # sim.lattice = fodo @@ -127,7 +127,7 @@ def test_impactx_nofile(): # add 2 more drifts for i in range(4): - sim.lattice.append(elements.Drift(0.25)) + sim.lattice.append(elements.Drift(name="d" + str(4 + i), ds=0.25)) print(sim.lattice) print(len(sim.lattice)) @@ -158,7 +158,7 @@ def test_impactx_noparticles(): # particle bunch: init intentionally missing # init accelerator lattice - sim.lattice.append(elements.Drift(0.5)) + sim.lattice.append(elements.Drift(ds=0.5)) with pytest.raises( RuntimeError, match="No particles found. Cannot run evolve without a beam." @@ -225,7 +225,7 @@ def test_impactx_resting_refparticle(): ): sim.add_particles(bunch_charge=0.0, distr=gaussian, npart=10) - sim.lattice.append(elements.Drift(0.25)) + sim.lattice.append(elements.Drift(ds=0.25)) with pytest.raises( RuntimeError, diff --git a/tests/python/test_particle_tiles.py b/tests/python/test_particle_tiles.py index da7f2d40f..4460530e8 100644 --- a/tests/python/test_particle_tiles.py +++ b/tests/python/test_particle_tiles.py @@ -50,11 +50,11 @@ def test_particle_tiles(): # init accelerator lattice fodo = [ - elements.Drift(0.25), - elements.Quad(1.0, 1.0), - elements.Drift(0.5), - elements.Quad(1.0, -1.0), - elements.Drift(0.25), + elements.Drift(name="d1", ds=0.25), + elements.Quad(name="q1", ds=1.0, k=1.0), + elements.Drift(name="d2", ds=0.5), + elements.Quad(name="q2", ds=1.0, k=-1.0), + elements.Drift(name="d3", ds=0.25), ] sim.lattice.extend(fodo) diff --git a/tests/python/test_push.py b/tests/python/test_push.py index 3c4cc962a..04fde43bd 100644 --- a/tests/python/test_push.py +++ b/tests/python/test_push.py @@ -47,20 +47,26 @@ def test_element_push(): assert pc.total_number_of_particles() == npart # init accelerator lattice + drift = elements.Drift(name="drift1", ds=0.25) + assert drift.name == "drift1" + # changed my mind on the name + drift.name = "mydrift" + assert drift.name == "mydrift" + fodo = [ - elements.Drift(0.25), + drift, ] sim.lattice.extend(fodo) sim.evolve() - # Push manually through a few elements - elements.Quad(1.0, 1.0).push(pc) - elements.Drift(0.5).push(pc) - elements.Quad(1.0, -1.0).push(pc) + # Push manually through a few (unnamed) elements + elements.Quad(ds=1.0, k=1.0).push(pc) + elements.Drift(ds=0.5).push(pc) + elements.Quad(ds=1.0, k=-1.0).push(pc) # alternative formulation - push(pc, elements.Drift(0.25)) + push(pc, elements.Drift(ds=0.25)) # finalize simulation sim.finalize() diff --git a/tests/python/test_xopt.py b/tests/python/test_xopt.py index c5458576a..2862d6bf9 100644 --- a/tests/python/test_xopt.py +++ b/tests/python/test_xopt.py @@ -45,13 +45,13 @@ def build_lattice(parameters: dict, write_particles: bool) -> list: # enforce a mirror symmetry of the triplet line = [ - elements.Drift(ds=2.7, nslice=ns), - elements.Quad(ds=0.1, k=q1_k, nslice=ns), - elements.Drift(ds=1.4, nslice=ns), - elements.Quad(ds=0.2, k=q2_k, nslice=ns), - elements.Drift(ds=1.4, nslice=ns), - elements.Quad(ds=0.1, k=q1_k, nslice=ns), - elements.Drift(ds=2.7, nslice=ns), + elements.Drift("d1", ds=2.7, nslice=ns), + elements.Quad("q1", ds=0.1, k=q1_k, nslice=ns), + elements.Drift("d2", ds=1.4, nslice=ns), + elements.Quad("q2", ds=0.2, k=q2_k, nslice=ns), + elements.Drift("d3", ds=1.4, nslice=ns), + elements.Quad("q1", ds=0.1, k=q1_k, nslice=ns), + elements.Drift("d4", ds=2.7, nslice=ns), ] if write_particles: From 27517954204dc6205ff7970e77955f95b0a86df8 Mon Sep 17 00:00:00 2001 From: ax3l Date: Thu, 26 Sep 2024 21:51:04 +0000 Subject: [PATCH 16/30] Update Stub Files --- .../impactx/impactx_pybind/__init__.pyi | 1 + .../impactx/impactx_pybind/elements.pyi | 125 +++++++++++++----- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/python/impactx/impactx_pybind/__init__.pyi b/src/python/impactx/impactx_pybind/__init__.pyi index b6587969b..2d0df0e6d 100644 --- a/src/python/impactx/impactx_pybind/__init__.pyi +++ b/src/python/impactx/impactx_pybind/__init__.pyi @@ -683,6 +683,7 @@ def push( | elements.ExactDrift | elements.ExactSbend | elements.Kicker + | elements.Marker | elements.Multipole | elements.NonlinearLens | elements.Programmable diff --git a/src/python/impactx/impactx_pybind/elements.pyi b/src/python/impactx/impactx_pybind/elements.pyi index dd3427062..86ae8b3c8 100644 --- a/src/python/impactx/impactx_pybind/elements.pyi +++ b/src/python/impactx/impactx_pybind/elements.pyi @@ -26,7 +26,9 @@ __all__ = [ "ExactSbend", "Kicker", "KnownElementsList", + "Marker", "Multipole", + "Named", "NonlinearLens", "PRot", "Programmable", @@ -70,7 +72,7 @@ class Alignment: @rotation.setter def rotation(self, arg1: float) -> None: ... -class Aperture(Thin, Alignment): +class Aperture(Named, Thin, Alignment): def __init__( self, xmax: float, @@ -79,6 +81,7 @@ class Aperture(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ A short collimator element applying a transverse aperture boundary. @@ -168,9 +171,15 @@ class BeamMonitor(Thin): @tn.setter def tn(self, arg1: float) -> None: ... -class Buncher(Thin, Alignment): +class Buncher(Named, Thin, Alignment): def __init__( - self, V: float, k: float, dx: float = 0, dy: float = 0, rotation: float = 0 + self, + V: float, + k: float, + dx: float = 0, + dy: float = 0, + rotation: float = 0, + name: str | None = None, ) -> None: """ A short linear RF cavity element at zero-crossing for bunching. @@ -197,7 +206,7 @@ class Buncher(Thin, Alignment): @k.setter def k(self, arg1: float) -> None: ... -class CFbend(Thick, Alignment): +class CFbend(Named, Thick, Alignment): def __init__( self, ds: float, @@ -207,6 +216,7 @@ class CFbend(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ An ideal combined function bend (sector bend with quadrupole component). @@ -233,7 +243,7 @@ class CFbend(Thick, Alignment): @rc.setter def rc(self, arg1: float) -> None: ... -class ChrAcc(Thick, Alignment): +class ChrAcc(Named, Thick, Alignment): def __init__( self, ds: float, @@ -243,6 +253,7 @@ class ChrAcc(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A region of Uniform Acceleration, with chromatic effects included. @@ -269,7 +280,7 @@ class ChrAcc(Thick, Alignment): @ez.setter def ez(self, arg1: float) -> None: ... -class ChrDrift(Thick, Alignment): +class ChrDrift(Named, Thick, Alignment): def __init__( self, ds: float, @@ -277,6 +288,7 @@ class ChrDrift(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A Drift with chromatic effects included. @@ -289,7 +301,7 @@ class ChrDrift(Thick, Alignment): Push first the reference particle, then all other particles. """ -class ChrPlasmaLens(Thick, Alignment): +class ChrPlasmaLens(Named, Thick, Alignment): def __init__( self, ds: float, @@ -299,6 +311,7 @@ class ChrPlasmaLens(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ An active Plasma Lens with chromatic effects included. @@ -325,7 +338,7 @@ class ChrPlasmaLens(Thick, Alignment): @unit.setter def unit(self, arg1: int) -> None: ... -class ChrQuad(Thick, Alignment): +class ChrQuad(Named, Thick, Alignment): def __init__( self, ds: float, @@ -335,6 +348,7 @@ class ChrQuad(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A Quadrupole magnet with chromatic effects included. @@ -361,7 +375,7 @@ class ChrQuad(Thick, Alignment): @unit.setter def unit(self, arg1: int) -> None: ... -class ConstF(Thick, Alignment): +class ConstF(Named, Thick, Alignment): def __init__( self, ds: float, @@ -372,6 +386,7 @@ class ConstF(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A linear Constant Focusing element. @@ -405,7 +420,7 @@ class ConstF(Thick, Alignment): @ky.setter def ky(self, arg1: float) -> None: ... -class DipEdge(Thin, Alignment): +class DipEdge(Named, Thin, Alignment): def __init__( self, psi: float, @@ -415,6 +430,7 @@ class DipEdge(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ Edge focusing associated with bend entry or exit. @@ -455,7 +471,7 @@ class DipEdge(Thin, Alignment): @rc.setter def rc(self, arg1: float) -> None: ... -class Drift(Thick, Alignment): +class Drift(Named, Thick, Alignment): def __init__( self, ds: float, @@ -463,6 +479,7 @@ class Drift(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A drift. @@ -488,7 +505,7 @@ class Empty(Thin): Push first the reference particle, then all other particles. """ -class ExactDrift(Thick, Alignment): +class ExactDrift(Named, Thick, Alignment): def __init__( self, ds: float, @@ -496,6 +513,7 @@ class ExactDrift(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A Drift using the exact nonlinear map. @@ -508,7 +526,7 @@ class ExactDrift(Thick, Alignment): Push first the reference particle, then all other particles. """ -class ExactSbend(Thick, Alignment): +class ExactSbend(Named, Thick, Alignment): def __init__( self, ds: float, @@ -518,6 +536,7 @@ class ExactSbend(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ An ideal sector bend using the exact nonlinear map. When B = 0, the reference bending radius is defined by r0 = length / (angle in rad), corresponding to a magnetic field of B = rigidity / r0; otherwise the reference bending radius is defined by r0 = rigidity / B. @@ -544,7 +563,7 @@ class ExactSbend(Thick, Alignment): @phi.setter def phi(self, arg1: float) -> None: ... -class Kicker(Thin, Alignment): +class Kicker(Named, Thin, Alignment): def __init__( self, xkick: float, @@ -553,6 +572,7 @@ class Kicker(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ A thin transverse kicker element. Kicks are for unit "dimensionless" or in "T-m". @@ -600,6 +620,7 @@ class KnownElementsList: | ExactDrift | ExactSbend | Kicker + | Marker | Multipole | NonlinearLens | Programmable @@ -634,6 +655,7 @@ class KnownElementsList: | ExactDrift | ExactSbend | Kicker + | Marker | Multipole | NonlinearLens | Programmable @@ -669,6 +691,7 @@ class KnownElementsList: | ExactDrift | ExactSbend | Kicker + | Marker | Multipole | NonlinearLens | Programmable @@ -706,7 +729,20 @@ class KnownElementsList: Return and remove the last element of the list. """ -class Multipole(Thin, Alignment): +class Marker(Named, Thin): + def __init__(self, arg0: str) -> None: + """ + This named element does nothing. + """ + def __repr__(self) -> str: ... + def push( + self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + ) -> None: + """ + Push first the reference particle, then all other particles. + """ + +class Multipole(Named, Thin, Alignment): def __init__( self, multipole: int, @@ -715,6 +751,7 @@ class Multipole(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ A general thin multipole element. @@ -748,7 +785,18 @@ class Multipole(Thin, Alignment): @multipole.setter def multipole(self, arg1: float) -> None: ... -class NonlinearLens(Thin, Alignment): +class Named: + @property + def has_name(self) -> bool: ... + @property + def name(self) -> str: + """ + segment length in m + """ + @name.setter + def name(self, arg1: str) -> None: ... + +class NonlinearLens(Named, Thin, Alignment): def __init__( self, knll: float, @@ -756,6 +804,7 @@ class NonlinearLens(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ Single short segment of the nonlinear magnetic insert element. @@ -782,8 +831,8 @@ class NonlinearLens(Thin, Alignment): @knll.setter def knll(self, arg1: float) -> None: ... -class PRot(Thin): - def __init__(self, phi_in: float, phi_out: float) -> None: +class PRot(Named, Thin): + def __init__(self, phi_in: float, phi_out: float, name: str | None = None) -> None: """ An exact pole-face rotation in the x-z plane. Both angles are in degrees. """ @@ -809,10 +858,12 @@ class PRot(Thin): @phi_out.setter def phi_out(self, arg1: float) -> None: ... -class Programmable: +class Programmable(Named): ds: float nslice: int - def __init__(self, ds: float = 0.0, nslice: int = 1) -> None: + def __init__( + self, ds: float = 0.0, nslice: int = 1, name: str | None = None + ) -> None: """ A programmable beam optics element. """ @@ -865,7 +916,7 @@ class Programmable: @threadsafe.setter def threadsafe(self, arg1: bool) -> None: ... -class Quad(Thick, Alignment): +class Quad(Named, Thick, Alignment): def __init__( self, ds: float, @@ -874,6 +925,7 @@ class Quad(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ A Quadrupole magnet. @@ -893,7 +945,7 @@ class Quad(Thick, Alignment): @k.setter def k(self, arg1: float) -> None: ... -class RFCavity(Thick, Alignment): +class RFCavity(Named, Thick, Alignment): def __init__( self, ds: float, @@ -907,6 +959,7 @@ class RFCavity(Thick, Alignment): rotation: float = 0, mapsteps: int = 1, nslice: int = 1, + name: str | None = None, ) -> None: """ An RF cavity. @@ -947,7 +1000,7 @@ class RFCavity(Thick, Alignment): @phase.setter def phase(self, arg1: float) -> None: ... -class Sbend(Thick, Alignment): +class Sbend(Named, Thick, Alignment): def __init__( self, ds: float, @@ -956,6 +1009,7 @@ class Sbend(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ An ideal sector bend. @@ -975,7 +1029,7 @@ class Sbend(Thick, Alignment): @rc.setter def rc(self, arg1: float) -> None: ... -class ShortRF(Thin, Alignment): +class ShortRF(Named, Thin, Alignment): def __init__( self, V: float, @@ -984,6 +1038,7 @@ class ShortRF(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ A short RF cavity element. @@ -1017,7 +1072,7 @@ class ShortRF(Thin, Alignment): @phase.setter def phase(self, arg1: float) -> None: ... -class SoftQuadrupole(Thick, Alignment): +class SoftQuadrupole(Named, Thick, Alignment): def __init__( self, ds: float, @@ -1029,6 +1084,7 @@ class SoftQuadrupole(Thick, Alignment): rotation: float = 0, mapsteps: int = 1, nslice: int = 1, + name: str | None = None, ) -> None: """ A soft-edge quadrupole. @@ -1055,7 +1111,7 @@ class SoftQuadrupole(Thick, Alignment): @mapsteps.setter def mapsteps(self, arg1: int) -> None: ... -class SoftSolenoid(Thick, Alignment): +class SoftSolenoid(Named, Thick, Alignment): def __init__( self, ds: float, @@ -1068,6 +1124,7 @@ class SoftSolenoid(Thick, Alignment): rotation: float = 0, mapsteps: int = 1, nslice: int = 1, + name: str | None = None, ) -> None: """ A soft-edge solenoid. @@ -1101,7 +1158,7 @@ class SoftSolenoid(Thick, Alignment): @unit.setter def unit(self, arg1: float) -> None: ... -class Sol(Thick, Alignment): +class Sol(Named, Thick, Alignment): def __init__( self, ds: float, @@ -1110,6 +1167,7 @@ class Sol(Thick, Alignment): dy: float = 0, rotation: float = 0, nslice: int = 1, + name: str | None = None, ) -> None: """ An ideal hard-edge Solenoid magnet. @@ -1129,7 +1187,7 @@ class Sol(Thick, Alignment): @ks.setter def ks(self, arg1: float) -> None: ... -class TaperedPL(Thin, Alignment): +class TaperedPL(Named, Thin, Alignment): def __init__( self, k: float, @@ -1138,6 +1196,7 @@ class TaperedPL(Thin, Alignment): dx: float = 0, dy: float = 0, rotation: float = 0, + name: str | None = None, ) -> None: """ A thin nonlinear plasma lens with transverse (horizontal) taper @@ -1213,9 +1272,15 @@ class Thin: number of slices used for the application of space charge """ -class ThinDipole(Thin, Alignment): +class ThinDipole(Named, Thin, Alignment): def __init__( - self, theta: float, rc: float, dx: float = 0, dy: float = 0, rotation: float = 0 + self, + theta: float, + rc: float, + dx: float = 0, + dy: float = 0, + rotation: float = 0, + name: str | None = None, ) -> None: """ A thin kick model of a dipole bend. From 7a0b32658c387417a6693bf99ec1e3b712f61da3 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 30 Sep 2024 16:44:08 -0700 Subject: [PATCH 17/30] Update ABLASTR & pyAMReX (#715) * Update ABLASTR & pyAMReX To latest commit on `development` each. * `ablastr::fields::computePhi`: EB Runtime Control * Update AMReX, too Pull in https://github.com/AMReX-Codes/amrex/pull/4164 * Update ABLASTR & AMReX * Simpler ABLASTR Poisson Solver API * Update ABLASTR --- cmake/dependencies/ABLASTR.cmake | 2 +- cmake/dependencies/pyAMReX.cmake | 2 +- src/particles/spacecharge/PoissonSolve.cpp | 20 +++----------------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/cmake/dependencies/ABLASTR.cmake b/cmake/dependencies/ABLASTR.cmake index 5ac8b4672..7eeb0504d 100644 --- a/cmake/dependencies/ABLASTR.cmake +++ b/cmake/dependencies/ABLASTR.cmake @@ -161,7 +161,7 @@ set(ImpactX_openpmd_src "" set(ImpactX_ablastr_repo "https://github.com/ECP-WarpX/WarpX.git" CACHE STRING "Repository URI to pull and build ABLASTR from if(ImpactX_ablastr_internal)") -set(ImpactX_ablastr_branch "24.09" +set(ImpactX_ablastr_branch "d1a338e90ed1ad7ac2f010f47409aa48a2265c88" CACHE STRING "Repository branch for ImpactX_ablastr_repo if(ImpactX_ablastr_internal)") diff --git a/cmake/dependencies/pyAMReX.cmake b/cmake/dependencies/pyAMReX.cmake index fd91b71ae..4158e63a3 100644 --- a/cmake/dependencies/pyAMReX.cmake +++ b/cmake/dependencies/pyAMReX.cmake @@ -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 "24.09" +set(ImpactX_pyamrex_branch "1c66690f83244196c5655293f1381303a7d1589d" CACHE STRING "Repository branch for ImpactX_pyamrex_repo if(ImpactX_pyamrex_internal)") diff --git a/src/particles/spacecharge/PoissonSolve.cpp b/src/particles/spacecharge/PoissonSolve.cpp index 9d928424d..d9c9b3dd2 100644 --- a/src/particles/spacecharge/PoissonSolve.cpp +++ b/src/particles/spacecharge/PoissonSolve.cpp @@ -71,22 +71,6 @@ namespace impactx::spacecharge pp_algo.queryAdd("mlmg_max_iters", mlmg_max_iters); pp_algo.queryAdd("mlmg_verbosity", mlmg_verbosity); - struct PoissonBoundaryHandler { - amrex::Array const lobc = { - amrex::LinOpBCType::Dirichlet, - amrex::LinOpBCType::Dirichlet, - amrex::LinOpBCType::Dirichlet - }; - amrex::Array const hibc = { - amrex::LinOpBCType::Dirichlet, - amrex::LinOpBCType::Dirichlet, - amrex::LinOpBCType::Dirichlet - }; - //bool bcs_set = false; - //std::array dirichlet_flag; - //bool has_non_periodic = false; - } poisson_boundary_handler; - // create a vector to our fields, sorted by level amrex::Vector sorted_rho; amrex::Vector sorted_phi; @@ -97,6 +81,7 @@ namespace impactx::spacecharge } const bool do_single_precision_comms = false; + const bool eb_enabled = false; ablastr::fields::computePhi( sorted_rho, sorted_phi, @@ -109,12 +94,13 @@ namespace impactx::spacecharge pc.GetParGDB()->DistributionMap(), pc.GetParGDB()->boxArray(), ablastr::utils::enums::GridType::Collocated, - poisson_boundary_handler, is_solver_igf_on_lev0, + eb_enabled, do_single_precision_comms, rel_ref_ratio /* post_phi_calculation, + poisson_boundary_handler gett_new(0), eb_farray_box_factory */ From 36ec501426db238dbd57245132d4f6f6486a1c88 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:48:04 -0700 Subject: [PATCH 18/30] [pre-commit.ci] pre-commit autoupdate (#721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.8) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7124110c9..8dfba477f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: # Python: Ruff linter & formatter # https://docs.astral.sh/ruff/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.7 + rev: v0.6.8 hooks: # Run the linter - id: ruff From 3f26348e3a01e1f60c1bf941b01f7558e94f0c74 Mon Sep 17 00:00:00 2001 From: Parthib Roy <159463257+proy30@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:45:16 -0700 Subject: [PATCH 19/30] Added export functionality for user inputs in dashboard (#719) --- .../dashboard/Toolbar/exportTemplate.py | 91 +++++++++++++++++++ .../impactx/dashboard/Toolbar/toolbarMain.py | 22 +++++ 2 files changed, 113 insertions(+) create mode 100644 src/python/impactx/dashboard/Toolbar/exportTemplate.py diff --git a/src/python/impactx/dashboard/Toolbar/exportTemplate.py b/src/python/impactx/dashboard/Toolbar/exportTemplate.py new file mode 100644 index 000000000..005663b8c --- /dev/null +++ b/src/python/impactx/dashboard/Toolbar/exportTemplate.py @@ -0,0 +1,91 @@ +from ..Input.distributionParameters.distributionMain import parameter_input_checker +from ..Input.latticeConfiguration.latticeMain import parameter_input_checker_for_lattice +from ..trame_setup import setup_server + +server, state, ctrl = setup_server() + +# ----------------------------------------------------------------------------- +# Helper Functions +# ----------------------------------------------------------------------------- + + +def build_distribution_list(): + """ + Generates an instance of distribution inputs + as a string for exporting purposes. + """ + distribution_name = state.selectedDistribution + parameters = parameter_input_checker() + + distribution_parameters = ",\n ".join( + f"{key}={value}" for key, value in parameters.items() + ) + + return ( + f"distr = distribution.{distribution_name}(\n {distribution_parameters},\n)" + ) + + +def build_lattice_list(): + """ + Generates a string representation of lattice element + inputs for export purposes. + """ + + lattice_elements = ",\n ".join( + f'elements.{element["name"]}(' + + ", ".join( + f"{key}={value}" + for key, value in parameter_input_checker_for_lattice(element).items() + ) + + ")" + for element in state.selectedLatticeList + ) + + return f"lattice_configuration = [\n {lattice_elements}\n]" + + +# ----------------------------------------------------------------------------- +# Trame setup +# ----------------------------------------------------------------------------- + + +def input_file(): + """ + This function creates the template to export + dashboard user inputs into a python script. + """ + script = f""" +from impactx import ImpactX, distribution, elements + +sim = ImpactX() + +sim.particle_shape = {state.particle_shape} +sim.space_charge = False +sim.csr = False +sim.slice_step_diagnostics = True + +sim.init_grids() + +# Initialize particle beam +kin_energy_MeV = {state.kin_energy_MeV} +bunch_charge_C = {state.bunch_charge_C} +npart = {state.npart} + +# Reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +{build_distribution_list()} +sim.add_particles(bunch_charge_C, distr, npart) + +{build_lattice_list()} +sim.lattice.extend(lattice_configuration) + +# Simulate +sim.evolve() + +sim.finalize() +""" + + return script diff --git a/src/python/impactx/dashboard/Toolbar/toolbarMain.py b/src/python/impactx/dashboard/Toolbar/toolbarMain.py index a842e6e8b..4b71b0aca 100644 --- a/src/python/impactx/dashboard/Toolbar/toolbarMain.py +++ b/src/python/impactx/dashboard/Toolbar/toolbarMain.py @@ -9,11 +9,22 @@ from trame.widgets import vuetify from ..trame_setup import setup_server +from .exportTemplate import input_file server, state, ctrl = setup_server() state.show_dashboard_alert = True +# ----------------------------------------------------------------------------- +# Triggers +# ----------------------------------------------------------------------------- + + +@ctrl.trigger("export") +def on_export_click(): + return input_file() + + # ----------------------------------------------------------------------------- # Common toolbar elements # ----------------------------------------------------------------------------- @@ -25,6 +36,15 @@ class ToolbarElements: Vuetify UI elements for toolbar. """ + @staticmethod + def export_input_data(): + vuetify.VIcon( + "mdi-download", + style="color: #00313C;", + click="utils.download('impactx_simulation.py', trigger('export'), 'text/plain')", + disabled=("disableRunSimulationButton", True), + ) + @staticmethod def plot_options(): vuetify.VSelect( @@ -75,6 +95,8 @@ def input_toolbar(): """ (ToolbarElements.dashboard_info(),) + vuetify.VSpacer() + ToolbarElements.export_input_data() @staticmethod def run_toolbar(): From 4a7da60f72bb5149dca9d23924ee16dca7421d46 Mon Sep 17 00:00:00 2001 From: Parthib Roy <159463257+proy30@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:52:50 -0700 Subject: [PATCH 20/30] Print simulation content after simulation runs (#718) Improvement from current, however it would be ideal that the contents stream out on the terminal view as the simulation runs, not just at once when the simulation is completed. Using wurlitzer library for this --- src/python/impactx/dashboard/Analyze/plotsMain.py | 6 +++--- src/python/impactx/dashboard/requirements.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/python/impactx/dashboard/Analyze/plotsMain.py b/src/python/impactx/dashboard/Analyze/plotsMain.py index 4d71ffb86..1202801f5 100644 --- a/src/python/impactx/dashboard/Analyze/plotsMain.py +++ b/src/python/impactx/dashboard/Analyze/plotsMain.py @@ -7,12 +7,12 @@ """ import asyncio -import contextlib import glob import io import os from trame.widgets import matplotlib, plotly, vuetify +from wurlitzer import pipes from ..trame_setup import setup_server from .analyzeFunctions import AnalyzeFunctions @@ -140,11 +140,11 @@ def update_plot(): def run_simulation_impactX(): buf = io.StringIO() - with contextlib.redirect_stdout(buf), contextlib.redirect_stderr(buf): + with pipes(stdout=buf, stderr=buf): state.simulation_data = run_simulation() buf.seek(0) - lines = [line.strip() for line in buf] + lines = [line.strip() for line in buf.getvalue().splitlines()] # Use $nextTick to ensure the terminal is fully rendered before printing async def print_lines(): diff --git a/src/python/impactx/dashboard/requirements.txt b/src/python/impactx/dashboard/requirements.txt index 7eaac07dc..0726649c2 100644 --- a/src/python/impactx/dashboard/requirements.txt +++ b/src/python/impactx/dashboard/requirements.txt @@ -6,3 +6,4 @@ trame-plotly>=3.0.2 trame-router>=2.2.0 trame-vuetify>=2.6.2 trame-xterm>=0.2.1 +wurlitzer>=3.1.1 From d6f70e4e5d395a9d22c5f8688fcbc02986d1252e Mon Sep 17 00:00:00 2001 From: Parthib Roy <159463257+proy30@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:36:02 -0700 Subject: [PATCH 21/30] Add ref_particle charge & mass as user options in dashboard (#720) * Added ref_particle charge & mass as user options in dashboard * Added validation for ref_particle charge/mass Created additional_conditions variable to handle cases where validation is more than checking value_type * Update export_template for ref.particle charge/mass * Update validation_status function/ref.charge default --- .../plot_PhaseSpaceProjections/phaseSpace.py | 4 +- .../dashboard/Input/generalFunctions.py | 58 +++++++++++++------ .../Input/inputParameters/inputMain.py | 39 ++++++++++++- .../dashboard/Toolbar/exportTemplate.py | 2 +- 4 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py b/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py index ed027ca71..5421b5c14 100644 --- a/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py +++ b/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py @@ -53,7 +53,9 @@ def run_simulation(): # reference particle pc = sim.particle_container() ref = pc.ref_particle() - ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + ref.set_charge_qe(state.charge_qe).set_mass_MeV(state.mass_MeV).set_kin_energy_MeV( + kin_energy_MeV + ) distribution = distribution_parameters() sim.add_particles(bunch_charge_C, distribution, npart) diff --git a/src/python/impactx/dashboard/Input/generalFunctions.py b/src/python/impactx/dashboard/Input/generalFunctions.py index dcb4717f0..42e7f66cd 100644 --- a/src/python/impactx/dashboard/Input/generalFunctions.py +++ b/src/python/impactx/dashboard/Input/generalFunctions.py @@ -64,38 +64,53 @@ def determine_input_type(value): return value, str @staticmethod - def validate_against(input_value, value_type): + def validate_against(input_value, value_type, additional_conditions=None): """ - Returns an error message if the input value type does not match the desired type. + Validates the input value against the desired type and additional conditions. :param input_value: The value to validate. :param value_type: The desired type ('int', 'float', 'str'). + :param conditions: A list of additional conditions to validate. :return: A list of error messages. An empty list if there are no errors. """ + errors = [] + value = None + # value_type checking if value_type == "int": if input_value is None: - return ["Must be an integer"] - try: - int(input_value) - return [] - except ValueError: - return ["Must be an integer"] - + errors.append("Must be an integer") + else: + try: + value = int(input_value) + except ValueError: + errors.append("Must be an integer") elif value_type == "float": if input_value is None: - return ["Must be a float"] - try: - float(input_value) - return [] - except ValueError: - return ["Must be a float"] - + errors.append("Must be a float") + else: + try: + value = float(input_value) + except ValueError: + errors.append("Must be a float") elif value_type == "str": if input_value is None: - return ["Must be a string"] - return [] + errors.append("Must be a string") + else: + value = str(input_value) else: - return ["Unknown type"] + errors.append("Unknown type") + + # addition_conditions checking + if errors == [] and additional_conditions: + for condition in additional_conditions: + if condition == "non_zero" and value == 0: + errors.append("Must be non-zero") + if condition == "positive" and value <= 0: + errors.append("Must be positive") + if condition == "negative" and value >= 0: + errors.append("Must be negative") + + return errors @staticmethod def update_simulation_validation_status(): @@ -128,6 +143,11 @@ def update_simulation_validation_status(): error_details.append(f"Kinetic Energy: {state.kin_energy_validation}") if state.bunch_charge_C_validation: error_details.append(f"Bunch Charge: {state.bunch_charge_C_validation}") + if state.charge_qe_validation: + error_details.append(f"Ref. Particle Charge: {state.charge_qe_validation}") + if state.mass_MeV_validation: + error_details.append(f"Ref. Particle Mass: {state.mass_MeV}") + if state.selectedLatticeList == []: error_details.append("LatticeListIsEmpty") diff --git a/src/python/impactx/dashboard/Input/inputParameters/inputMain.py b/src/python/impactx/dashboard/Input/inputParameters/inputMain.py index 52e7e63ba..1f9699564 100644 --- a/src/python/impactx/dashboard/Input/inputParameters/inputMain.py +++ b/src/python/impactx/dashboard/Input/inputParameters/inputMain.py @@ -21,9 +21,11 @@ @ctrl.add("on_input_change") def validate_and_convert_to_correct_type( - value, desired_type, state_name, validation_name + value, desired_type, state_name, validation_name, conditions=None ): - validation_result = generalFunctions.validate_against(value, desired_type) + validation_result = generalFunctions.validate_against( + value, desired_type, conditions + ) setattr(state, validation_name, validation_result) generalFunctions.update_simulation_validation_status() @@ -70,17 +72,21 @@ def __init__(self): state.bunch_charge_C = 1.0e-9 state.kin_energy_unit = "MeV" state.old_kin_energy_unit = "MeV" + state.charge_qe = -1 + state.mass_MeV = 0.510998950 state.npart_validation = [] state.kin_energy_validation = [] state.bunch_charge_C_validation = [] + state.mass_MeV_validation = [] + state.charge_qe_validation = [] def card(self): """ Creates UI content for beam properties. """ - with vuetify.VCard(style="width: 340px; height: 300px"): + with vuetify.VCard(style="width: 340px; height: 350px"): with vuetify.VCardTitle("Input Parameters"): vuetify.VSpacer() vuetify.VIcon( @@ -96,6 +102,33 @@ def card(self): items=([1, 2, 3],), dense=True, ) + with vuetify.VRow(classes="my-2"): + with vuetify.VCol(cols=6, classes="py-0"): + vuetify.VTextField( + label="Ref. Particle Charge", + v_model=("charge_qe",), + suffix="qe", + type="number", + dense=True, + error_messages=("charge_qe_validation",), + change=( + ctrl.on_input_change, + "[$event, 'int','charge_qe','charge_qe_validation', ['non_zero']]", + ), + ) + with vuetify.VCol(cols=6, classes="py-0"): + vuetify.VTextField( + label="Ref. Particle Mass", + v_model=("mass_MeV",), + suffix="MeV", + type="number", + dense=True, + error_messages=("mass_MeV_validation",), + change=( + ctrl.on_input_change, + "[$event, 'float','mass_MeV','mass_MeV_validation', ['positive']]", + ), + ) with vuetify.VRow(classes="my-0"): with vuetify.VCol(cols=12, classes="py-0"): vuetify.VTextField( diff --git a/src/python/impactx/dashboard/Toolbar/exportTemplate.py b/src/python/impactx/dashboard/Toolbar/exportTemplate.py index 005663b8c..e58e6e02a 100644 --- a/src/python/impactx/dashboard/Toolbar/exportTemplate.py +++ b/src/python/impactx/dashboard/Toolbar/exportTemplate.py @@ -74,7 +74,7 @@ def input_file(): # Reference particle ref = sim.particle_container().ref_particle() -ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) +ref.set_charge_qe({state.charge_qe}).set_mass_MeV({state.mass_MeV}).set_kin_energy_MeV(kin_energy_MeV) {build_distribution_list()} sim.add_particles(bunch_charge_C, distr, npart) From a8d0b7641f297d69218721050f5f8ba4ea7ed3e1 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 2 Oct 2024 22:38:46 -0700 Subject: [PATCH 22/30] `load_inputs_file`: Simplify File Check (#704) AMReX will now check this in a user-friendly and performant manner. --- src/python/ImpactX.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/python/ImpactX.cpp b/src/python/ImpactX.cpp index 0326ca496..359015b76 100644 --- a/src/python/ImpactX.cpp +++ b/src/python/ImpactX.cpp @@ -62,20 +62,6 @@ void init_ImpactX (py::module& m) .def("load_inputs_file", [](ImpactX const & /* ix */, std::string const & filename) { -#if defined(AMREX_DEBUG) || defined(DEBUG) - // note: only in debug, since this is costly for the file - // system for highly parallel simulations with MPI - // possible improvement: - // - rank 0 tests file & broadcasts existence/failure - bool inputs_file_exists = false; - if (FILE *fp = fopen(filename.c_str(), "r")) { - fclose(fp); - inputs_file_exists = true; - } - AMREX_ALWAYS_ASSERT_WITH_MESSAGE(inputs_file_exists, - "load_inputs_file: file does not exist: " + filename); -#endif - amrex::ParmParse::addfile(filename); }) From 085925c028522b32588dcb4b6c8e9e7c3bf98f84 Mon Sep 17 00:00:00 2001 From: Parthib Roy <159463257+proy30@users.noreply.github.com> Date: Thu, 3 Oct 2024 12:53:14 -0700 Subject: [PATCH 23/30] Relocated simulation file (#725) --- .../impactx/dashboard/Analyze/plotsMain.py | 2 +- .../phaseSpace.py => simulation.py} | 160 +++++++++--------- 2 files changed, 82 insertions(+), 80 deletions(-) rename src/python/impactx/dashboard/{Analyze/plot_PhaseSpaceProjections/phaseSpace.py => simulation.py} (81%) diff --git a/src/python/impactx/dashboard/Analyze/plotsMain.py b/src/python/impactx/dashboard/Analyze/plotsMain.py index 1202801f5..5a8a02bb4 100644 --- a/src/python/impactx/dashboard/Analyze/plotsMain.py +++ b/src/python/impactx/dashboard/Analyze/plotsMain.py @@ -14,10 +14,10 @@ from trame.widgets import matplotlib, plotly, vuetify from wurlitzer import pipes +from ..simulation import run_simulation from ..trame_setup import setup_server from .analyzeFunctions import AnalyzeFunctions from .plot_ParameterEvolutionOverS.overS import line_plot_1d -from .plot_PhaseSpaceProjections.phaseSpace import run_simulation server, state, ctrl = setup_server() diff --git a/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py b/src/python/impactx/dashboard/simulation.py similarity index 81% rename from src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py rename to src/python/impactx/dashboard/simulation.py index 5421b5c14..057304ab1 100644 --- a/src/python/impactx/dashboard/Analyze/plot_PhaseSpaceProjections/phaseSpace.py +++ b/src/python/impactx/dashboard/simulation.py @@ -1,79 +1,81 @@ -""" -This file is part of ImpactX - -Copyright 2024 ImpactX contributors -Authors: Parthib Roy, Axel Huebl -License: BSD-3-Clause-LBNL -""" - -from ...trame_setup import setup_server - -server, state, ctrl = setup_server() - -import base64 -import io - -from impactx import Config, ImpactX - -from ...Input.distributionParameters.distributionMain import distribution_parameters -from ...Input.latticeConfiguration.latticeMain import lattice_elements -from ..plot_PhaseSpaceProjections.phaseSpaceSettings import adjusted_settings_plot - -# Call MPI_Init and MPI_Finalize only once: -if Config.have_mpi: - from mpi4py import MPI # noqa - - -def fig_to_base64(fig): - """ - Puts png in trame-compatible form - """ - buf = io.BytesIO() - fig.savefig(buf, format="png") - buf.seek(0) - return base64.b64encode(buf.read()).decode("utf-8") - - -def run_simulation(): - """ - This tests using ImpactX and Pandas Dataframes - """ - sim = ImpactX() - - sim.particle_shape = state.particle_shape - sim.space_charge = False - sim.slice_step_diagnostics = True - sim.init_grids() - - # init particle beam - kin_energy_MeV = state.kin_energy_MeV - bunch_charge_C = state.bunch_charge_C - npart = state.npart - - # reference particle - pc = sim.particle_container() - ref = pc.ref_particle() - ref.set_charge_qe(state.charge_qe).set_mass_MeV(state.mass_MeV).set_kin_energy_MeV( - kin_energy_MeV - ) - - distribution = distribution_parameters() - sim.add_particles(bunch_charge_C, distribution, npart) - - lattice_configuration = lattice_elements() - - sim.lattice.extend(lattice_configuration) - - # simulate - sim.evolve() - - fig = adjusted_settings_plot(pc) - fig_original = pc.plot_phasespace() - - if fig_original is not None: - image_base64 = fig_to_base64(fig_original) - state.image_data = f"data:image/png;base64, {image_base64}" - - sim.finalize() - - return fig +""" +This file is part of ImpactX + +Copyright 2024 ImpactX contributors +Authors: Parthib Roy, Axel Huebl +License: BSD-3-Clause-LBNL +""" + +from .trame_setup import setup_server + +server, state, ctrl = setup_server() + +import base64 +import io + +from impactx import Config, ImpactX + +from .Analyze.plot_PhaseSpaceProjections.phaseSpaceSettings import ( + adjusted_settings_plot, +) +from .Input.distributionParameters.distributionMain import distribution_parameters +from .Input.latticeConfiguration.latticeMain import lattice_elements + +# Call MPI_Init and MPI_Finalize only once: +if Config.have_mpi: + from mpi4py import MPI # noqa + + +def fig_to_base64(fig): + """ + Puts png in trame-compatible form + """ + buf = io.BytesIO() + fig.savefig(buf, format="png") + buf.seek(0) + return base64.b64encode(buf.read()).decode("utf-8") + + +def run_simulation(): + """ + This tests using ImpactX and Pandas Dataframes + """ + sim = ImpactX() + + sim.particle_shape = state.particle_shape + sim.space_charge = False + sim.slice_step_diagnostics = True + sim.init_grids() + + # init particle beam + kin_energy_MeV = state.kin_energy_MeV + bunch_charge_C = state.bunch_charge_C + npart = state.npart + + # reference particle + pc = sim.particle_container() + ref = pc.ref_particle() + ref.set_charge_qe(state.charge_qe).set_mass_MeV(state.mass_MeV).set_kin_energy_MeV( + kin_energy_MeV + ) + + distribution = distribution_parameters() + sim.add_particles(bunch_charge_C, distribution, npart) + + lattice_configuration = lattice_elements() + + sim.lattice.extend(lattice_configuration) + + # simulate + sim.evolve() + + fig = adjusted_settings_plot(pc) + fig_original = pc.plot_phasespace() + + if fig_original is not None: + image_base64 = fig_to_base64(fig_original) + state.image_data = f"data:image/png;base64, {image_base64}" + + sim.finalize() + + return fig From dfe9b28a5d74b9d916984fb46327a5e900b5a510 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 9 Oct 2024 10:37:45 -0700 Subject: [PATCH 24/30] BeamMonitor: Enable Filtering by Cycle (Turn) (#713) * Elements: Pass Cycle, Filter openPMD * Examples: Update Programmable Signature For cycle argument * Documentation * Add Example * Update Docstrings Co-authored-by: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> * Rename `cycle_intervals` -> `period_sample_intervals` * Update `Marker` * Rename: `cycle` -> `period` --------- Co-authored-by: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> --- docs/source/usage/examples.rst | 10 ++ docs/source/usage/parameters.rst | 5 + docs/source/usage/python.rst | 3 +- examples/CMakeLists.txt | 17 +++ examples/fodo_channel/README.rst | 74 ++++++++++ examples/fodo_channel/analysis_fodo.py | 105 ++++++++++++++ examples/fodo_channel/input_fodo.in | 60 ++++++++ examples/fodo_channel/plot_fodo.py | 132 ++++++++++++++++++ examples/fodo_channel/run_fodo.py | 71 ++++++++++ .../run_ml_surrogate_15_stage.py | 4 +- src/ImpactX.cpp | 26 ++-- src/initialization/InitElement.cpp | 4 +- src/particles/Push.H | 10 +- src/particles/Push.cpp | 13 +- src/particles/PushAll.H | 2 + src/particles/elements/Empty.H | 3 +- src/particles/elements/Marker.H | 3 +- src/particles/elements/Programmable.H | 6 +- src/particles/elements/Programmable.cpp | 7 +- src/particles/elements/diagnostics/openPMD.H | 9 +- .../elements/diagnostics/openPMD.cpp | 15 +- src/particles/elements/mixin/beamoptic.H | 12 +- src/python/elements.cpp | 17 +-- 23 files changed, 559 insertions(+), 49 deletions(-) create mode 100644 examples/fodo_channel/README.rst create mode 100755 examples/fodo_channel/analysis_fodo.py create mode 100644 examples/fodo_channel/input_fodo.in create mode 100755 examples/fodo_channel/plot_fodo.py create mode 100755 examples/fodo_channel/run_fodo.py diff --git a/docs/source/usage/examples.rst b/docs/source/usage/examples.rst index 426881b7f..100139584 100644 --- a/docs/source/usage/examples.rst +++ b/docs/source/usage/examples.rst @@ -70,6 +70,16 @@ Beam Distributions examples/distgen/README + +Channels & Rings +---------------- + +.. toctree:: + :maxdepth: 1 + + examples/fodo_channel/README.rst + + Lattice Design & Optimization ----------------------------- diff --git a/docs/source/usage/parameters.rst b/docs/source/usage/parameters.rst index d21b1f2ab..7b6a81a70 100644 --- a/docs/source/usage/parameters.rst +++ b/docs/source/usage/parameters.rst @@ -449,6 +449,11 @@ Lattice Elements openPMD `iteration encoding `__: (v)ariable based, (f)ile based, (g)roup based (default) variable based is an `experimental feature with ADIOS2 `__. + * ``.period_sample_intervals`` (``int``, default value: ``1``) + + for periodic lattice, only output every Nth period (turn). + By default, diagnostics are returned every cycle. + * ``.nonlinear_lens_invariants`` (``boolean``, default value: ``false``) Compute and output the invariants H and I within the nonlinear magnetic insert element (see: ``nonlinear_lens``). diff --git a/docs/source/usage/python.rst b/docs/source/usage/python.rst index 948de5a6b..2f004a7b8 100644 --- a/docs/source/usage/python.rst +++ b/docs/source/usage/python.rst @@ -632,7 +632,7 @@ This module provides elements for the accelerator lattice. :param rotation: rotation error in the transverse plane [degrees] :param name: an optional name for the element -.. py:class:: impactx.elements.BeamMonitor(name, backend="default", encoding="g") +.. py:class:: impactx.elements.BeamMonitor(name, backend="default", encoding="g", period_sample_intervals=1) A beam monitor, writing all beam particles at fixed ``s`` to openPMD files. @@ -649,6 +649,7 @@ This module provides elements for the accelerator lattice. :param name: name of the series :param backend: I/O backend, e.g., ``bp``, ``h5``, ``json`` :param encoding: openPMD iteration encoding: (v)ariable based, (f)ile based, (g)roup based (default) + :param period_sample_intervals: for periodic lattice, only output every Nth period (turn) .. py:property:: name diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d33221240..528eb8290 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -219,6 +219,23 @@ add_impactx_test(FODO.py.MPI examples/fodo/plot_fodo.py ) +# FODO Channel ################################################################ +# +add_impactx_test(FODO_channel + examples/fodo_channel/input_fodo.in + OFF # ImpactX MPI-parallel + examples/fodo_channel/analysis_fodo.py + examples/fodo_channel/plot_fodo.py +) +add_impactx_test(FODO_channel.py + examples/fodo_channel/run_fodo.py + OFF # ImpactX MPI-parallel + examples/fodo_channel/analysis_fodo.py + examples/fodo_channel/plot_fodo.py +) +label_impactx_test(FODO_channel slow) +label_impactx_test(FODO_channel.py slow) + # Chicane ##################################################################### # add_impactx_test(chicane diff --git a/examples/fodo_channel/README.rst b/examples/fodo_channel/README.rst new file mode 100644 index 000000000..6282e8de0 --- /dev/null +++ b/examples/fodo_channel/README.rst @@ -0,0 +1,74 @@ +.. _examples-fodo-channel: + +FODO Channel +============ + +A 300m channel of 100 stable FODO cells (3m each) with a zero-current phase advance of 67.8 degrees. + +The matched Twiss parameters at entry are: + +* :math:`\beta_\mathrm{x} = 2.82161941` m +* :math:`\alpha_\mathrm{x} = -1.59050035` +* :math:`\beta_\mathrm{y} = 2.82161941` m +* :math:`\alpha_\mathrm{y} = 1.59050035` + +We use a 2 GeV electron beam with initial unnormalized rms emittance of 2 nm. + +The second moments of the particle distribution after the FODO cell should coincide with the second moments of the particle distribution before the FODO cell, to within the level expected due to noise due to statistical sampling. + +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. +This test also demonstrates the ``period_sample_intervals`` capability of our beam monitor diagnostics, only creating output every 10th FODO cell + + +Run +--- + +This example can be run **either** as: + +* **Python** script: ``python3 run_fodo.py`` or +* ImpactX **executable** using an input file: ``impactx input_fodo.in`` + +For `MPI-parallel `__ runs, prefix these lines with ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system. + +.. tab-set:: + + .. tab-item:: Python: Script + + .. literalinclude:: run_fodo.py + :language: python3 + :caption: You can copy this file from ``examples/fodo/run_fodo.py``. + + .. tab-item:: Executable: Input File + + .. literalinclude:: input_fodo.in + :language: ini + :caption: You can copy this file from ``examples/fodo/input_fodo.in``. + + +Analyze +------- + +We run the following script to analyze correctness: + +.. dropdown:: Script ``analysis_fodo.py`` + + .. literalinclude:: analysis_fodo.py + :language: python3 + :caption: You can copy this file from ``examples/fodo/analysis_fodo.py``. + + +Visualize +--------- + +You can run the following script to visualize the beam evolution over time: + +.. dropdown:: Script ``plot_fodo.py`` + + .. literalinclude:: plot_fodo.py + :language: python3 + :caption: You can copy this file from ``examples/fodo/plot_fodo.py``. + +.. figure:: https://gist.githubusercontent.com/ax3l/8ae7dcb9e07c361e002fa56d6b16cb16/raw/cc952670bb946cd7a62282bc7aa3f03f3d5faa16/fodo_channel.png + :alt: preserved emittance in the FODO channel. + + FODO transverse emittance evolution (preserved) diff --git a/examples/fodo_channel/analysis_fodo.py b/examples/fodo_channel/analysis_fodo.py new file mode 100755 index 000000000..127a4af0a --- /dev/null +++ b/examples/fodo_channel/analysis_fodo.py @@ -0,0 +1,105 @@ +#!/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) + +# compare beamline length: 300m +assert np.isclose( + 300.0, series.iterations[last_step].particles["beam"].get_attribute("z_ref") +) +# compare beam monitor outputs: 10 (every 10th FODO element + 1) +assert len(series.iterations) == 11 + +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], + [ + 7.5451170454175073e-005, + 7.5441588239210947e-005, + 9.9775878164077539e-004, + 1.9959540393751392e-009, + 2.0175015289132990e-009, + 2.0013820193294972e-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.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], + [ + 7.4790118496224206e-005, + 7.5357525169680140e-005, + 9.9775879288128088e-004, + 1.9959539836392703e-009, + 2.0175014668882125e-009, + 2.0013820380883801e-006, + ], + rtol=rtol, + atol=atol, +) diff --git a/examples/fodo_channel/input_fodo.in b/examples/fodo_channel/input_fodo.in new file mode 100644 index 000000000..e782be7f8 --- /dev/null +++ b/examples/fodo_channel/input_fodo.in @@ -0,0 +1,60 @@ +############################################################################### +# Particle Beam(s) +############################################################################### +beam.npart = 10000 +beam.units = static +beam.kin_energy = 2.0e3 +beam.charge = 1.0e-9 +beam.particle = electron +beam.distribution = waterbag_from_twiss +beam.alphaX = -1.5905003499999992 +beam.alphaY = 1.5905003499999992 +beam.alphaT = 0.0 +beam.betaX = 2.8216194100262637 +beam.betaY = 2.8216194100262637 +beam.betaT = 0.5 +beam.emittX = 2e-09 +beam.emittY = 2e-09 +beam.emittT = 2e-06 + + +############################################################################### +# Beamline: lattice elements and segments +############################################################################### +lattice.elements = monitor drift1 quad1 drift2 quad2 drift3 +lattice.nslice = 5 +lattice.periods = 101 # FODO channel of 101 periods + +monitor.type = beam_monitor +monitor.period_sample_intervals = 10 +monitor.backend = h5 + +drift1.type = drift +drift1.ds = 0.25 + +quad1.type = quad +quad1.ds = 1.0 +quad1.k = 1.0 + +drift2.type = drift +drift2.ds = 0.5 + +quad2.type = quad +quad2.ds = 1.0 +quad2.k = -1.0 + +drift3.type = drift +drift3.ds = 0.25 + + +############################################################################### +# Algorithms +############################################################################### +algo.particle_shape = 2 +algo.space_charge = false + + +############################################################################### +# Diagnostics +############################################################################### +diag.slice_step_diagnostics = false diff --git a/examples/fodo_channel/plot_fodo.py b/examples/fodo_channel/plot_fodo.py new file mode 100755 index 000000000..5ec2b6715 --- /dev/null +++ b/examples/fodo_channel/plot_fodo.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2023 ImpactX contributors +# Authors: Axel Huebl, Chad Mitchell +# License: BSD-3-Clause-LBNL +# + +import argparse +import glob +import re + +import matplotlib.pyplot as plt +import openpmd_api as io +import pandas as pd +from matplotlib.ticker import MaxNLocator +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 read_file(file_pattern): + for filename in glob.glob(file_pattern): + df = pd.read_csv(filename, delimiter=r"\s+") + if "step" not in df.columns: + step = int(re.findall(r"[0-9]+", filename)[0]) + df["step"] = step + yield df + + +def read_time_series(file_pattern): + """Read in all CSV files from each MPI rank (and potentially OpenMP + thread). Concatenate into one Pandas dataframe. + + Returns + ------- + pandas.DataFrame + """ + return pd.concat( + read_file(file_pattern), + axis=0, + ignore_index=True, + ) # .set_index('id') + + +# options to run this script +parser = argparse.ArgumentParser(description="Plot the FODO benchmark.") +parser.add_argument( + "--save-png", action="store_true", help="non-interactive run: save to PNGs" +) +args = parser.parse_args() + + +# 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() +ref_particle = read_time_series("diags/ref_particle.*") + +# scaling to units +millimeter = 1.0e3 # m->mm +mrad = 1.0e3 # ImpactX uses "static units": momenta are normalized by the magnitude of the momentum of the reference particle p0: px/p0 (rad) +# mm_mrad = 1.e6 +nm_rad = 1.0e9 + + +# select a single particle by id +# particle_42 = beam[beam["id"] == 42] +# print(particle_42) + + +# steps & corresponding z +steps = list(series.iterations) + +z = list(map(lambda step: ref_particle[ref_particle["step"] == step].z.values, steps)) +# print(f"z={z}") + + +# beam transversal size & emittance over steps +moments = list( + map( + lambda step: ( + step, + get_moments(series.iterations[step].particles["beam"].to_df()), + ), + steps, + ) +) +# print(moments) +emittance_x = list(map(lambda step_val: step_val[1][3] * nm_rad, moments)) +emittance_y = list(map(lambda step_val: step_val[1][4] * nm_rad, moments)) + +# print(sigx, sigy) + + +# print beam transversal size over steps +f = plt.figure(figsize=(9, 4.8)) +ax1 = f.gca() +im_emittance_x = ax1.plot(z, emittance_x, ".-", label=r"$\epsilon_x$") +im_emittance_y = ax1.plot(z, emittance_y, ".-", label=r"$\epsilon_y$") + +ax1.legend() +ax1.set_xlabel(r"$z$ [m]") +ax1.set_ylabel(r"$\epsilon_{x,y}$ [nm]") +ax1.set_ylim([1.98, 2.03]) +ax1.xaxis.set_major_locator(MaxNLocator(integer=True)) +plt.tight_layout() +if args.save_png: + plt.savefig("fodo_lambda.png") +else: + plt.show() diff --git a/examples/fodo_channel/run_fodo.py b/examples/fodo_channel/run_fodo.py new file mode 100755 index 000000000..9eaf171f5 --- /dev/null +++ b/examples/fodo_channel/run_fodo.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2024 ImpactX contributors +# Authors: Axel Huebl, Chad Mitchell, Marco Garten +# License: BSD-3-Clause-LBNL +# +# -*- coding: utf-8 -*- + +from impactx import ImpactX, distribution, elements, twiss + +sim = ImpactX() + +# set numerical parameters and IO control +sim.particle_shape = 2 # B-spline order +sim.space_charge = False +# 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 = 2.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.Waterbag( + **twiss( + beta_x=2.8216194100262637, + beta_y=2.8216194100262637, + beta_t=0.5, + emitt_x=2e-09, + emitt_y=2e-09, + emitt_t=2e-06, + alpha_x=-1.5905003499999992, + alpha_y=1.5905003499999992, + alpha_t=0.0, + ) +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5", period_sample_intervals=10) + +# design the accelerator lattice) +ns = 5 # number of slices per ds in the element +fodo = [ + monitor, + elements.Drift(ds=0.25, nslice=ns), + elements.Quad(ds=1.0, k=1.0, nslice=ns), + elements.Drift(ds=0.5, nslice=ns), + elements.Quad(ds=1.0, k=-1.0, nslice=ns), + elements.Drift(ds=0.25, nslice=ns), +] +# assign a fodo segment +sim.lattice.extend(fodo) + +# FODO channel of 101 periods +sim.periods = 101 + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/examples/pytorch_surrogate_model/run_ml_surrogate_15_stage.py b/examples/pytorch_surrogate_model/run_ml_surrogate_15_stage.py index 94c205c80..004f79bdb 100644 --- a/examples/pytorch_surrogate_model/run_ml_surrogate_15_stage.py +++ b/examples/pytorch_surrogate_model/run_ml_surrogate_15_stage.py @@ -169,7 +169,7 @@ def __init__(self, stage_i, surrogate_model, surrogate_length, stage_start): self.push = self.surrogate_push self.ds = surrogate_length - def surrogate_push(self, pc, step): + def surrogate_push(self, pc, step, period): ref_part = pc.ref_particle() ref_z_i = ref_part.z ref_z_i_LPA = ref_z_i - self.stage_start @@ -304,7 +304,7 @@ def __init__(self, sim, stage_i, lattice_index, x_or_y): self.x_or_y = x_or_y self.push = self.set_lens - def set_lens(self, pc, step): + def set_lens(self, pc, step, period): # get envelope parameters rbc = pc.reduced_beam_characteristics() alpha = rbc[f"alpha_{self.x_or_y}"] diff --git a/src/ImpactX.cpp b/src/ImpactX.cpp index 7466356af..ed08201f3 100644 --- a/src/ImpactX.cpp +++ b/src/ImpactX.cpp @@ -137,7 +137,7 @@ namespace impactx { // a global step for diagnostics including space charge slice steps in elements // before we start the evolve loop, we are in "step 0" (initial state) - int global_step = 0; + int step = 0; // check typos in inputs after step 1 bool early_params_checked = false; @@ -158,7 +158,7 @@ namespace impactx { diagnostics::DiagnosticOutput(*amr_data->m_particle_container, diagnostics::OutputType::PrintRefParticle, "diags/ref_particle", - global_step); + step); // print the initial values of reduced beam characteristics diagnostics::DiagnosticOutput(*amr_data->m_particle_container, @@ -181,10 +181,10 @@ namespace impactx { } // periods through the lattice - int periods = 1; - amrex::ParmParse("lattice").queryAdd("periods", periods); + int num_periods = 1; + amrex::ParmParse("lattice").queryAdd("periods", num_periods); - for (int cycle=0; cycle < periods; ++cycle) { + for (int period=0; period < num_periods; ++period) { // loop over all beamline elements for (auto &element_variant: m_lattice) { // update element edge of the reference particle @@ -201,9 +201,9 @@ namespace impactx { // sub-steps for space charge within the element for (int slice_step = 0; slice_step < nslice; ++slice_step) { BL_PROFILE("ImpactX::evolve::slice_step"); - global_step++; + step++; if (verbose > 0) { - amrex::Print() << " ++++ Starting global_step=" << global_step + amrex::Print() << " ++++ Starting step=" << step << " slice_step=" << slice_step << "\n"; } @@ -263,7 +263,7 @@ namespace impactx { // assuming that the distribution did not change // push all particles with external maps - Push(*amr_data->m_particle_container, element_variant, global_step); + Push(*amr_data->m_particle_container, element_variant, step, period); // move "lost" particles to another particle container collect_lost_particles(*amr_data->m_particle_container); @@ -282,14 +282,14 @@ namespace impactx { diagnostics::DiagnosticOutput(*amr_data->m_particle_container, diagnostics::OutputType::PrintRefParticle, "diags/ref_particle", - global_step, + step, true); // print slice step reduced beam characteristics to file diagnostics::DiagnosticOutput(*amr_data->m_particle_container, diagnostics::OutputType::PrintReducedBeamCharacteristics, "diags/reduced_beam_characteristics", - global_step, + step, true); } @@ -308,13 +308,13 @@ namespace impactx { diagnostics::DiagnosticOutput(*amr_data->m_particle_container, diagnostics::OutputType::PrintRefParticle, "diags/ref_particle_final", - global_step); + step); // print the final values of the reduced beam characteristics diagnostics::DiagnosticOutput(*amr_data->m_particle_container, diagnostics::OutputType::PrintReducedBeamCharacteristics, "diags/reduced_beam_characteristics_final", - global_step); + step); // output particles lost in apertures if (amr_data->m_particles_lost->TotalNumberOfParticles() > 0) @@ -323,7 +323,7 @@ namespace impactx { pp_diag.queryAdd("backend", openpmd_backend); diagnostics::BeamMonitor output_lost("particles_lost", openpmd_backend, "g"); - output_lost(*amr_data->m_particles_lost, 0); + output_lost(*amr_data->m_particles_lost, 0, 0); output_lost.finalize(); } } diff --git a/src/initialization/InitElement.cpp b/src/initialization/InitElement.cpp index dd770e51f..71ec7e4aa 100644 --- a/src/initialization/InitElement.cpp +++ b/src/initialization/InitElement.cpp @@ -395,6 +395,8 @@ namespace detail pp_element.queryAdd("backend", openpmd_backend); std::string openpmd_encoding{"g"}; pp_element.queryAdd("encoding", openpmd_encoding); + int period_sample_intervals = 1; + pp_element.queryAdd("period_sample_intervals", period_sample_intervals); // optional: add and calculate additional particle properties // property: nonlinear lens invariants @@ -412,7 +414,7 @@ namespace detail pp_element.queryAdd("cn", cn); } - m_lattice.emplace_back(diagnostics::BeamMonitor(openpmd_name, openpmd_backend, openpmd_encoding)); + m_lattice.emplace_back(diagnostics::BeamMonitor(openpmd_name, openpmd_backend, openpmd_encoding, period_sample_intervals)); } else if (element_type == "line") { // Parse the lattice elements for the sub-lattice in the line diff --git a/src/particles/Push.H b/src/particles/Push.H index 7665df7a6..268a65e28 100644 --- a/src/particles/Push.H +++ b/src/particles/Push.H @@ -23,10 +23,14 @@ namespace impactx * @param[inout] pc container of the particles to push * @param[inout] element_variant a single element to push the particles through * @param[in] step global step for diagnostics + * @param[in] period for periodic lattices, this is the current period (turn or cycle) */ - void Push (ImpactXParticleContainer & pc, - KnownElements & element_variant, - int step); + void Push ( + ImpactXParticleContainer & pc, + KnownElements & element_variant, + int step, + int period + ); } // namespace impactx diff --git a/src/particles/Push.cpp b/src/particles/Push.cpp index 5769dd5d8..54f8cd8b4 100644 --- a/src/particles/Push.cpp +++ b/src/particles/Push.cpp @@ -16,17 +16,20 @@ namespace impactx { - void Push (ImpactXParticleContainer & pc, - KnownElements & element_variant, - int step) + void Push ( + ImpactXParticleContainer & pc, + KnownElements & element_variant, + int step, + int period + ) { // here we just access the element by its respective type - std::visit([&pc, step](auto&& element) + std::visit([&pc, step, period](auto&& element) { BL_PROFILE("impactx::Push"); // push reference particle & all particles - element(pc, step); + element(pc, step, period); }, element_variant); } diff --git a/src/particles/PushAll.H b/src/particles/PushAll.H index ab0bacd65..3fec0f3bc 100644 --- a/src/particles/PushAll.H +++ b/src/particles/PushAll.H @@ -26,6 +26,7 @@ namespace impactx * @param[in,out] pc particle container to push * @param[in,out] element the beamline element * @param[in] step global step for diagnostics + * @param[in] period for periodic lattices, this is the current period (turn or cycle) * @param[in] omp_parallel allow threading via OpenMP for the particle iterator loop (note: if OMP backend is active) */ template @@ -33,6 +34,7 @@ namespace impactx ImpactXParticleContainer & pc, T_Element & element, [[maybe_unused]] int step, + [[maybe_unused]] int period, [[maybe_unused]] bool omp_parallel = true ) { diff --git a/src/particles/elements/Empty.H b/src/particles/elements/Empty.H index cf0dba130..9ce3eece8 100644 --- a/src/particles/elements/Empty.H +++ b/src/particles/elements/Empty.H @@ -37,7 +37,8 @@ namespace impactx /** Push all particles - nothing to do here */ void operator() ( ImpactXParticleContainer & /* pc */, - int /* step */ + int /* step */, + int /* period */ ) { // nothing to do } diff --git a/src/particles/elements/Marker.H b/src/particles/elements/Marker.H index 46eff72a4..3291aff79 100644 --- a/src/particles/elements/Marker.H +++ b/src/particles/elements/Marker.H @@ -41,7 +41,8 @@ namespace impactx /** Push all particles - nothing to do here */ void operator() ( ImpactXParticleContainer & /* pc */, - int /* step */ + int /* step */, + int /* period */ ) { // nothing to do } diff --git a/src/particles/elements/Programmable.H b/src/particles/elements/Programmable.H index 31e8b5fc3..89b6afa8e 100644 --- a/src/particles/elements/Programmable.H +++ b/src/particles/elements/Programmable.H @@ -46,10 +46,12 @@ namespace impactx * * @param[in,out] pc particle container to push * @param[in] step global step for diagnostics + * @param[in] period for periodic lattices, this is the current period (turn or cycle) */ void operator() ( ImpactXParticleContainer & pc, - int step + int step, + int period ) const; /** Push all particles relative to the reference particle */ @@ -103,7 +105,7 @@ namespace impactx */ bool m_threadsafe = false; - std::function m_push; //! hook for push of whole container + std::function m_push; //! hook for push of whole container (pc, step, period) std::function m_beam_particles; //! hook for beam particles std::function m_ref_particle; //! hook for reference particle std::function m_finalize; //! hook for finalize cleanup diff --git a/src/particles/elements/Programmable.cpp b/src/particles/elements/Programmable.cpp index 3f73ac413..43f37aa73 100644 --- a/src/particles/elements/Programmable.cpp +++ b/src/particles/elements/Programmable.cpp @@ -19,16 +19,17 @@ namespace impactx void Programmable::operator() ( ImpactXParticleContainer & pc, - int step + int step, + int period ) const { if (m_push == nullptr) { // TODO: print if verbose mode is set - push_all(pc, *this, step, m_threadsafe); + push_all(pc, *this, step, period, m_threadsafe); } else { BL_PROFILE("impactx::Push::Programmable"); - m_push(&pc, step); + m_push(&pc, step, period); } } diff --git a/src/particles/elements/diagnostics/openPMD.H b/src/particles/elements/diagnostics/openPMD.H index 5b6a6014f..f5ee0d4a8 100644 --- a/src/particles/elements/diagnostics/openPMD.H +++ b/src/particles/elements/diagnostics/openPMD.H @@ -76,8 +76,9 @@ namespace detail * @param series_name name of the data series, usually the element name * @param backend file format backend for openPMD, e.g., "bp" or "h5" * @param encoding openPMD iteration encoding: "v"ariable based, "f"ile based, "g"roup based (default) + * @param period_sample_intervals for periodic lattice, only output every Nth period (turn) */ - BeamMonitor (std::string series_name, std::string backend="default", std::string encoding="g"); + BeamMonitor (std::string series_name, std::string backend="default", std::string encoding="g", int period_sample_intervals=1); BeamMonitor (BeamMonitor const & other) = default; BeamMonitor (BeamMonitor && other) = default; @@ -108,10 +109,12 @@ namespace detail * * @param[in,out] pc particle container to push * @param[in] step global step for diagnostics + * @param[in] period for periodic lattices, this is the current period (turn or cycle) */ void operator() ( ImpactXParticleContainer & pc, - int step + int step, + int period ); /** Write a tile of particles @@ -156,6 +159,8 @@ namespace detail int m_file_min_digits = 6; //! minimum number of digits to iteration number in file name + int m_period_sample_intervals = 1; //! only output every Nth period (turn or cycle) of periodic lattices + /** This rank's offset in the MPI-global particle array, by level * * This MUST be updated by prepare() before the next step's output. diff --git a/src/particles/elements/diagnostics/openPMD.cpp b/src/particles/elements/diagnostics/openPMD.cpp index 163cbbe11..8b215d3e2 100644 --- a/src/particles/elements/diagnostics/openPMD.cpp +++ b/src/particles/elements/diagnostics/openPMD.cpp @@ -154,8 +154,8 @@ namespace detail #endif // ImpactX_USE_OPENPMD } - BeamMonitor::BeamMonitor (std::string series_name, std::string backend, std::string encoding) : - m_series_name(std::move(series_name)), m_OpenPMDFileType(std::move(backend)) + BeamMonitor::BeamMonitor (std::string series_name, std::string backend, std::string encoding, int period_sample_intervals) : + m_series_name(std::move(series_name)), m_OpenPMDFileType(std::move(backend)), m_period_sample_intervals(period_sample_intervals) { #ifdef ImpactX_USE_OPENPMD // pick first available backend if default is chosen @@ -179,8 +179,10 @@ namespace detail else if ( "f" == encoding ) series_encoding = openPMD::IterationEncoding::fileBased; - // legacy options from other diagnostics amrex::ParmParse pp_diag("diag"); + // turn filter + pp_diag.queryAdd("period_sample_intervals", m_period_sample_intervals); + // legacy options from other diagnostics pp_diag.queryAdd("file_min_digits", m_file_min_digits); // Ensure m_series is the same for the same names. @@ -311,9 +313,14 @@ namespace detail void BeamMonitor::operator() ( ImpactXParticleContainer & pc, - int step + int step, + int period ) { + // filter out this turn? + if (period % m_period_sample_intervals != 0) + return; + #ifdef ImpactX_USE_OPENPMD std::string profile_name = "impactx::Push::" + std::string(BeamMonitor::type); BL_PROFILE(profile_name); diff --git a/src/particles/elements/mixin/beamoptic.H b/src/particles/elements/mixin/beamoptic.H index fd58ac600..0616b5374 100644 --- a/src/particles/elements/mixin/beamoptic.H +++ b/src/particles/elements/mixin/beamoptic.H @@ -147,10 +147,16 @@ namespace detail template struct BeamOptic { - /** Push first the reference particle, then all other particles */ + /** Push first the reference particle, then all other particles + * + * @param[inout] pc container of the particles to push + * @param[in] step global step for diagnostics + * @param[in] period for periodic lattices, this is the current period (turn or cycle) + */ void operator() ( ImpactXParticleContainer & pc, - int step + int step, + int period ) { static_assert( @@ -159,7 +165,7 @@ namespace detail ); T_Element& element = *static_cast(this); - push_all(pc, element, step); + push_all(pc, element, step, period); } /** This pushes the particles on a particle iterator tile or box. diff --git a/src/python/elements.cpp b/src/python/elements.cpp index 7d4a33f76..b6403e71b 100644 --- a/src/python/elements.cpp +++ b/src/python/elements.cpp @@ -57,10 +57,10 @@ namespace using Element = typename T_PyClass::type; // py::class cl.def("push", - [](Element & el, ImpactXParticleContainer & pc, int step) { - el(pc, step); + [](Element & el, ImpactXParticleContainer & pc, int step, int period) { + el(pc, step, period); }, - py::arg("pc"), py::arg("step")=0, + py::arg("pc"), py::arg("step")=0, py::arg("period")=0, "Push first the reference particle, then all other particles." ); } @@ -214,10 +214,11 @@ void init_elements(py::module& m) py::class_ py_BeamMonitor(me, "BeamMonitor"); py_BeamMonitor - .def(py::init(), + .def(py::init(), py::arg("name"), py::arg("backend") = "default", py::arg("encoding") = "g", + py::arg("period_sample_intervals") = 1, "This element writes the particle beam out to openPMD data." ) .def_property_readonly("name", @@ -914,9 +915,9 @@ void init_elements(py::module& m) .def_property("push", [](Programmable & p) { return p.m_push; }, [](Programmable & p, - std::function new_hook + std::function new_hook ) { p.m_push = std::move(new_hook); }, - "hook for push of whole container (pc, step)" + "hook for push of whole container (pc, step, period)" ) .def_property("beam_particles", [](Programmable & p) { return p.m_beam_particles; }, @@ -1480,9 +1481,9 @@ void init_elements(py::module& m) register_beamoptics_push(py_TaperedPL); - // free-standing push function + // freestanding push function m.def("push", &Push, - py::arg("pc"), py::arg("element"), py::arg("step")=0, + py::arg("pc"), py::arg("element"), py::arg("step")=0, py::arg("period")=0, "Push particles through an element" ); From 80e6e3453a5ca413d8fdc5eaaa81cc5f165be35f Mon Sep 17 00:00:00 2001 From: cemitch99 Date: Wed, 9 Oct 2024 17:48:17 +0000 Subject: [PATCH 25/30] Update Stub Files --- .../impactx/impactx_pybind/__init__.pyi | 1 + .../impactx/impactx_pybind/elements.pyi | 154 ++++++++++++++---- 2 files changed, 123 insertions(+), 32 deletions(-) diff --git a/src/python/impactx/impactx_pybind/__init__.pyi b/src/python/impactx/impactx_pybind/__init__.pyi index 2d0df0e6d..5d18cf746 100644 --- a/src/python/impactx/impactx_pybind/__init__.pyi +++ b/src/python/impactx/impactx_pybind/__init__.pyi @@ -698,6 +698,7 @@ def push( | elements.TaperedPL | elements.ThinDipole, step: int = 0, + period: int = 0, ) -> None: """ Push particles through an element diff --git a/src/python/impactx/impactx_pybind/elements.pyi b/src/python/impactx/impactx_pybind/elements.pyi index 86ae8b3c8..67bb1995d 100644 --- a/src/python/impactx/impactx_pybind/elements.pyi +++ b/src/python/impactx/impactx_pybind/elements.pyi @@ -88,7 +88,10 @@ class Aperture(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -117,13 +120,20 @@ class Aperture(Named, Thin, Alignment): class BeamMonitor(Thin): def __init__( - self, name: str, backend: str = "default", encoding: str = "g" + self, + name: str, + backend: str = "default", + encoding: str = "g", + period_sample_intervals: int = 1, ) -> None: """ This element writes the particle beam out to openPMD data. """ def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -186,7 +196,10 @@ class Buncher(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -223,7 +236,10 @@ class CFbend(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -260,7 +276,10 @@ class ChrAcc(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -295,7 +314,10 @@ class ChrDrift(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -318,7 +340,10 @@ class ChrPlasmaLens(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -355,7 +380,10 @@ class ChrQuad(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -393,7 +421,10 @@ class ConstF(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -437,7 +468,10 @@ class DipEdge(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -486,7 +520,10 @@ class Drift(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -499,7 +536,10 @@ class Empty(Thin): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -520,7 +560,10 @@ class ExactDrift(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -543,7 +586,10 @@ class ExactSbend(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -579,7 +625,10 @@ class Kicker(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -736,7 +785,10 @@ class Marker(Named, Thin): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -758,7 +810,10 @@ class Multipole(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -811,7 +866,10 @@ class NonlinearLens(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -838,7 +896,10 @@ class PRot(Named, Thin): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -888,15 +949,17 @@ class Programmable(Named): @property def push( self, - ) -> typing.Callable[[impactx.impactx_pybind.ImpactXParticleContainer, int], None]: + ) -> typing.Callable[ + [impactx.impactx_pybind.ImpactXParticleContainer, int, int], None + ]: """ - hook for push of whole container (pc, step) + hook for push of whole container (pc, step, period) """ @push.setter def push( self, arg1: typing.Callable[ - [impactx.impactx_pybind.ImpactXParticleContainer, int], None + [impactx.impactx_pybind.ImpactXParticleContainer, int, int], None ], ) -> None: ... @property @@ -932,7 +995,10 @@ class Quad(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -966,7 +1032,10 @@ class RFCavity(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1016,7 +1085,10 @@ class Sbend(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1045,7 +1117,10 @@ class ShortRF(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1091,7 +1166,10 @@ class SoftQuadrupole(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1131,7 +1209,10 @@ class SoftSolenoid(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1174,7 +1255,10 @@ class Sol(Named, Thick, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1209,7 +1293,10 @@ class TaperedPL(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. @@ -1287,7 +1374,10 @@ class ThinDipole(Named, Thin, Alignment): """ def __repr__(self) -> str: ... def push( - self, pc: impactx.impactx_pybind.ImpactXParticleContainer, step: int = 0 + self, + pc: impactx.impactx_pybind.ImpactXParticleContainer, + step: int = 0, + period: int = 0, ) -> None: """ Push first the reference particle, then all other particles. From 00f9693f085ad332cbc11d00a2352cd476fdf773 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 9 Oct 2024 11:32:30 -0700 Subject: [PATCH 26/30] CMake: Fix List of Pip Options (#729) * CMake: Fix List of Pip Options We were not yet able to pass lists of options to `pip` commands in our `pip` CMake helper targets. This fixes it. * CI: Fix Python on macOS --- .github/workflows/macos.yml | 25 +++++++++---------------- CMakeLists.txt | 10 +++++++--- docs/source/install/cmake.rst | 4 ++-- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index e0c6efdc9..ec380186d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,12 +16,15 @@ jobs: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: TRUE steps: - uses: actions/checkout@v4 - - name: install dependencies + - uses: actions/setup-python@v5 + name: Install Python + with: + python-version: '3.x' + - name: install brew dependencies run: | set +e brew unlink gcc brew update - brew install --overwrite python brew install adios2 brew install ccache brew install cmake @@ -32,19 +35,15 @@ jobs: brew install ninja brew install open-mpi brew install pkg-config - - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade virtualenv - - python3 -m venv py-venv - source py-venv/bin/activate + set -e + - name: install pip dependencies + run: | python3 -m pip install --upgrade pip python3 -m pip install --upgrade build packaging setuptools wheel pytest python3 -m pip install --upgrade -r requirements_mpi.txt python3 -m pip install --upgrade -r src/python/impactx/dashboard/requirements.txt python3 -m pip install --upgrade -r examples/requirements.txt python3 -m pip install --upgrade -r tests/python/requirements.txt - set -e python3 -m pip install --upgrade pipx python3 -m pipx install openPMD-validator - name: CCache Cache @@ -65,13 +64,10 @@ jobs: export CCACHE_SLOPPINESS=time_macros ccache -z - source py-venv/bin/activate - cmake -S . -B build \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DImpactX_FFT=ON \ - -DImpactX_PYTHON=ON \ - -DPython_EXECUTABLE=$(which python3) + -DImpactX_PYTHON=ON cmake --build build -j 3 du -hs ~/Library/Caches/ccache @@ -79,15 +75,12 @@ jobs: - name: run tests run: | - source py-venv/bin/activate - ctest --test-dir build --output-on-failure -E pytest.AMReX - name: run installed python module run: | cmake --build build --target pip_install - source py-venv/bin/activate python3 examples/fodo/run_fodo.py - name: run installed app diff --git a/CMakeLists.txt b/CMakeLists.txt index a552d5fd9..2ad0166d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,9 +394,9 @@ install(CODE "file(CREATE_LINK # if(ImpactX_PYTHON) set(PY_PIP_OPTIONS "-v" CACHE STRING - "Additional parameters to pass to `pip`") + "Additional parameters to pass to `pip` as ; separated list") set(PY_PIP_INSTALL_OPTIONS "" CACHE STRING - "Additional parameters to pass to `pip install`") + "Additional parameters to pass to `pip install` as ; separated list") # add a prefix to custom targets so we do not collide if used as a subproject if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) @@ -412,7 +412,8 @@ if(ImpactX_PYTHON) ${CMAKE_COMMAND} -E rm -f -r impactx-whl COMMAND ${CMAKE_COMMAND} -E env PYIMPACTX_LIBDIR=$ - ${Python_EXECUTABLE} -m pip ${PY_PIP_OPTIONS} wheel --no-build-isolation --no-deps --wheel-dir=impactx-whl ${ImpactX_SOURCE_DIR} + ${Python_EXECUTABLE} -m pip ${PY_PIP_OPTIONS} wheel --no-build-isolation --no-deps --wheel-dir=impactx-whl "${ImpactX_SOURCE_DIR}" + COMMAND_EXPAND_LISTS VERBATIM WORKING_DIRECTORY ${ImpactX_BINARY_DIR} DEPENDS @@ -427,6 +428,7 @@ if(ImpactX_PYTHON) endif() add_custom_target(${ImpactX_CUSTOM_TARGET_PREFIX}pip_install_requirements ${Python_EXECUTABLE} -m pip ${PY_PIP_OPTIONS} install ${PY_PIP_INSTALL_OPTIONS} -r "${ImpactX_SOURCE_DIR}/${pyImpactX_REQUIREMENT_FILE}" + COMMAND_EXPAND_LISTS VERBATIM WORKING_DIRECTORY ${ImpactX_BINARY_DIR} ) @@ -444,6 +446,7 @@ if(ImpactX_PYTHON) add_custom_target(${ImpactX_CUSTOM_TARGET_PREFIX}pip_install ${CMAKE_COMMAND} -E env IMPACTX_MPI=${ImpactX_MPI} ${Python_EXECUTABLE} -m pip ${PY_PIP_OPTIONS} install --force-reinstall --no-index --no-deps ${PY_PIP_INSTALL_OPTIONS} --find-links=impactx-whl impactx + COMMAND_EXPAND_LISTS VERBATIM WORKING_DIRECTORY ${ImpactX_BINARY_DIR} DEPENDS @@ -456,6 +459,7 @@ if(ImpactX_PYTHON) add_custom_target(${ImpactX_CUSTOM_TARGET_PREFIX}pip_install_nodeps ${CMAKE_COMMAND} -E env IMPACTX_MPI=${ImpactX_MPI} ${Python_EXECUTABLE} -m pip ${PY_PIP_OPTIONS} install --force-reinstall --no-index --no-deps ${PY_PIP_INSTALL_OPTIONS} --find-links=impactx-whl impactx + COMMAND_EXPAND_LISTS VERBATIM WORKING_DIRECTORY ${ImpactX_BINARY_DIR} DEPENDS diff --git a/docs/source/install/cmake.rst b/docs/source/install/cmake.rst index 193fcbf2b..8fe5a85c7 100644 --- a/docs/source/install/cmake.rst +++ b/docs/source/install/cmake.rst @@ -112,8 +112,8 @@ CMake Option Default & Values Des ``ImpactX_PRECISION`` SINGLE/**DOUBLE** Floating point precision (single/double) ``ImpactX_PYTHON`` ON/**OFF** Python bindings ``Python_EXECUTABLE`` (newest found) Path to Python executable -``PY_PIP_OPTIONS`` ``-v`` Additional options for ``pip``, e.g., ``-vvv`` -``PY_PIP_INSTALL_OPTIONS`` Additional options for ``pip install``, e.g., ``--user`` +``PY_PIP_OPTIONS`` ``-v`` Additional options for ``pip``, e.g., ``-vvv;-q`` +``PY_PIP_INSTALL_OPTIONS`` Additional options for ``pip install``, e.g., ``--user;-q`` =============================== ============================================ =========================================================== ImpactX can be configured in further detail with options from AMReX, which are `documented in the AMReX manual `_. From 556153c09fbeff6891a80c483a00021f04c28dbd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:23:19 -0700 Subject: [PATCH 27/30] [pre-commit.ci] pre-commit autoupdate (#727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.8 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.8...v0.6.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8dfba477f..1d91577fd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] @@ -66,7 +66,7 @@ repos: # Python: Ruff linter & formatter # https://docs.astral.sh/ruff/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.8 + rev: v0.6.9 hooks: # Run the linter - id: ruff From 035ea8209cecba24712bb464bf1799f78886e6c5 Mon Sep 17 00:00:00 2001 From: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> Date: Wed, 9 Oct 2024 21:37:19 -0700 Subject: [PATCH 28/30] Add beam eigenemittances to reduced diagnostics. (#702) * Add eigenemittance calculation. --- docs/source/dataanalysis/dataanalysis.rst | 7 +- docs/source/usage/examples.rst | 2 + docs/source/usage/parameters.rst | 8 +- docs/source/usage/python.rst | 7 + examples/CMakeLists.txt | 16 ++ examples/coupled_optics/README.rst | 51 ++++ .../coupled_optics/analysis_coupled_optics.py | 142 ++++++++++++ .../coupled_optics/input_coupled_optics.in | 72 ++++++ examples/coupled_optics/run_coupled_optics.py | 81 +++++++ src/particles/diagnostics/CMakeLists.txt | 1 + .../diagnostics/CovarianceMatrixMath.H | 219 ++++++++++++++++++ .../diagnostics/DiagnosticOutput.cpp | 58 ++++- .../diagnostics/EmittanceInvariants.H | 77 ++++++ .../diagnostics/EmittanceInvariants.cpp | 146 ++++++++++++ .../ReducedBeamCharacteristics.cpp | 97 +++++++- src/python/ImpactX.cpp | 10 + tests/python/test_transformation.py | 2 +- 17 files changed, 986 insertions(+), 10 deletions(-) create mode 100644 examples/coupled_optics/README.rst create mode 100755 examples/coupled_optics/analysis_coupled_optics.py create mode 100644 examples/coupled_optics/input_coupled_optics.in create mode 100644 examples/coupled_optics/run_coupled_optics.py create mode 100644 src/particles/diagnostics/CovarianceMatrixMath.H create mode 100644 src/particles/diagnostics/EmittanceInvariants.H create mode 100644 src/particles/diagnostics/EmittanceInvariants.cpp diff --git a/docs/source/dataanalysis/dataanalysis.rst b/docs/source/dataanalysis/dataanalysis.rst index 4371f317c..cc170b57a 100644 --- a/docs/source/dataanalysis/dataanalysis.rst +++ b/docs/source/dataanalysis/dataanalysis.rst @@ -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) + 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`` @@ -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`` + 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) diff --git a/docs/source/usage/examples.rst b/docs/source/usage/examples.rst index 100139584..0658cb278 100644 --- a/docs/source/usage/examples.rst +++ b/docs/source/usage/examples.rst @@ -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 ------------------ diff --git a/docs/source/usage/parameters.rst b/docs/source/usage/parameters.rst index 7b6a81a70..1b0f2644b 100644 --- a/docs/source/usage/parameters.rst +++ b/docs/source/usage/parameters.rst @@ -688,8 +688,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. @@ -699,6 +699,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: diff --git a/docs/source/usage/python.rst b/docs/source/usage/python.rst index 2f004a7b8..73e9c0329 100644 --- a/docs/source/usage/python.rst +++ b/docs/source/usage/python.rst @@ -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. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 528eb8290..19c4d33ca 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -976,3 +976,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 +) diff --git a/examples/coupled_optics/README.rst b/examples/coupled_optics/README.rst new file mode 100644 index 000000000..ff17b9e20 --- /dev/null +++ b/examples/coupled_optics/README.rst @@ -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 `__ 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``. diff --git a/examples/coupled_optics/analysis_coupled_optics.py b/examples/coupled_optics/analysis_coupled_optics.py new file mode 100755 index 000000000..04b3eb998 --- /dev/null +++ b/examples/coupled_optics/analysis_coupled_optics.py @@ -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, +) diff --git a/examples/coupled_optics/input_coupled_optics.in b/examples/coupled_optics/input_coupled_optics.in new file mode 100644 index 000000000..f0ee840c4 --- /dev/null +++ b/examples/coupled_optics/input_coupled_optics.in @@ -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 diff --git a/examples/coupled_optics/run_coupled_optics.py b/examples/coupled_optics/run_coupled_optics.py new file mode 100644 index 000000000..c6e6632d1 --- /dev/null +++ b/examples/coupled_optics/run_coupled_optics.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# +# Copyright 2022-2023 ImpactX contributors +# Authors: Marco Garten, 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.particle_shape = 2 # B-spline order +sim.space_charge = False +# sim.diagnostics = False # benchmarking +sim.slice_step_diagnostics = True +sim.eigenemittances = True + +# domain decomposition & space charge mesh +sim.init_grids() + +# load a 5 GeV electron beam with an initial +# normalized transverse rms emittance of 1 um +kin_energy_MeV = 5.0e3 # reference energy +bunch_charge_C = 1.0e-9 # used with space charge +npart = 10000 # number of macro particles + +# reference particle +ref = sim.particle_container().ref_particle() +ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV) + +# particle bunch +distr = distribution.Waterbag( + lambdaX=2.2951017632e-5, + lambdaY=1.3084093142e-5, + lambdaT=5.5555553e-8, + lambdaPx=1.598353425e-6, + lambdaPy=2.803697378e-6, + lambdaPt=2.000000000e-6, + muxpx=0.933345606203060, + muypy=0.933345606203060, + mutpt=0.999999961419755, +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice +ns = 25 # number of slices per ds in the element +rc = 10.3462283686195526 # bend radius (meters) +psi = 0.048345620280243 # pole face rotation angle (radians) +lb = 0.500194828041958 # bend arc length (meters) + +# Drift elements +dr1 = elements.Drift(ds=5.0058489435, nslice=ns) +dr2 = elements.Drift(ds=0.5, nslice=ns) + +# Bend elements +sbend1 = elements.Sbend(ds=lb, rc=-rc, nslice=ns) +sbend2 = elements.Sbend(ds=lb, rc=rc, nslice=ns) + +# Dipole Edge Focusing elements +dipedge1 = elements.DipEdge(psi=-psi, rc=-rc, g=0.0, K2=0.0) +dipedge2 = elements.DipEdge(psi=psi, rc=rc, g=0.0, K2=0.0) + +# Solenoid element +sol = elements.Sol(ds=3.820395, ks=0.8223219329893234) + +lattice_coupled = [sbend1, dipedge1, sol, dr1, dipedge2, sbend2, dr2] + +sim.lattice.append(monitor) +sim.lattice.extend(lattice_coupled) +sim.lattice.append(monitor) + +# run simulation +sim.evolve() + +# clean shutdown +sim.finalize() diff --git a/src/particles/diagnostics/CMakeLists.txt b/src/particles/diagnostics/CMakeLists.txt index 1ff032aad..50ad97560 100644 --- a/src/particles/diagnostics/CMakeLists.txt +++ b/src/particles/diagnostics/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources(lib PRIVATE ReducedBeamCharacteristics.cpp DiagnosticOutput.cpp + EmittanceInvariants.cpp ) diff --git a/src/particles/diagnostics/CovarianceMatrixMath.H b/src/particles/diagnostics/CovarianceMatrixMath.H new file mode 100644 index 000000000..a3b0099cf --- /dev/null +++ b/src/particles/diagnostics/CovarianceMatrixMath.H @@ -0,0 +1,219 @@ +/* Copyright 2022-2023 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Chad Mitchell, Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#ifndef COVARIANCE_MATRIX_MATH_H +#define COVARIANCE_MATRIX_MATH_H + +#include +#include + +#include +#include +#include + +#include +#include + + +namespace impactx::diagnostics +{ + + /** Function to return the roots of a cubic polynomial ax^3 + bx^2 + cx + d. + * The trigonometric form of Cardano's formula is used. + * This implementation expects three real roots, which is verified + * by checking the sign of the discriminant. + * + * @param[in] a coefficient of cubic term + * @param[in] b coefficient of quadratic term + * @param[in] c coefficient of linear term + * @param[in] d coefficient of constant term + * @returns tuple of three real roots + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal + > + CubicRootsTrig ( + amrex::ParticleReal a, + amrex::ParticleReal b, + amrex::ParticleReal c, + amrex::ParticleReal d + ) + { + using namespace amrex::literals; + using ablastr::constant::math::pi; + + std::tuple roots; + amrex::ParticleReal x1 = 0.0_prt; + amrex::ParticleReal x2 = 0.0_prt; + amrex::ParticleReal x3 = 0.0_prt; + + amrex::ParticleReal Q = (3.0_prt*a*c - std::pow(b,2))/(9.0_prt * std::pow(a,2)); + amrex::ParticleReal R = (9.0_prt*a*b*c - 27_prt*std::pow(a,2)*d - 2.0_prt*std::pow(b,3))/(54.0_prt*std::pow(a,3)); + amrex::ParticleReal discriminant = std::pow(Q,3) + std::pow(R,2); + + // Discriminant should be < 0. Otherwise, keep theta at default and throw an error. + amrex::ParticleReal tol = 1.0e-12; //allow for roundoff error + if (discriminant > tol) { + + ablastr::warn_manager::WMRecordWarning( + "Impactx::diagnostics::CubicRootsTrig", + "Polynomial appearing in CubicRootsTrig has one or more complex " + "(non-real) roots. Only the real part is returned. This " + "suggests a loss of numerical precision in computation of the " + "eigenemittances. Treat eigenemittance values with caution.", + ablastr::warn_manager::WarnPriority::medium + ); + + std::cout << "Polynomial in CubicRoots has one or more complex roots." << "\n"; + + } else if (Q == 0.0_prt) { // Special case of a triple root + + x1 = - b/(3.0_prt*a); + x2 = - b/(3.0_prt*a); + x3 = - b/(3.0_prt*a); + + } else { + + //Three real roots in trigonometric form. + amrex::ParticleReal theta = std::acos(R/std::sqrt(-std::pow(Q,3))); + x1 = 2.0_prt*std::sqrt(-Q)*std::cos(theta/3.0_prt) - b/(3.0_prt*a); + x2 = 2.0_prt*std::sqrt(-Q)*std::cos(theta/3.0_prt + 2.0_prt*pi/3.0_prt) - b/(3.0_prt*a); + x3 = 2.0_prt*std::sqrt(-Q)*std::cos(theta/3.0_prt + 4.0_prt*pi/3.0_prt) - b/(3.0_prt*a); + + } + + roots = std::make_tuple(x1,x2,x3); + return roots; + } + + + /** Function to return the roots of a cubic polynomial ax^3 + bx^2 + cx + d. + * The algebraic form of Cardano's formula is used. + * This implementation expects three real roots, which is verified + * by checking the sign of the discriminant. + * + * @param[in] a coefficient of cubic term + * @param[in] b coefficient of quadratic term + * @param[in] c coefficient of linear term + * @param[in] d coefficient of constant term + * @returns tuple of three real roots + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal + > + CubicRootsAlg ( + amrex::ParticleReal a, + amrex::ParticleReal b, + amrex::ParticleReal c, + amrex::ParticleReal d + ) + { + using namespace amrex::literals; + using Complex = amrex::GpuComplex; + + std::tuple roots; + amrex::ParticleReal x1 = 0.0_prt; + amrex::ParticleReal x2 = 0.0_prt; + amrex::ParticleReal x3 = 0.0_prt; + + amrex::ParticleReal Q = (3.0_prt*a*c - std::pow(b,2))/(9.0_prt * std::pow(a,2)); + amrex::ParticleReal R = (9.0_prt*a*b*c - 27_prt*std::pow(a,2)*d - 2.0_prt*std::pow(b,3))/(54.0_prt*std::pow(a,3)); + amrex::ParticleReal discriminant = std::pow(Q,3) + std::pow(R,2); + + // Define complex variable C + Complex Qc(Q,0.0_prt); + Complex Rc(R,0.0_prt); + Complex Dc(discriminant,0.0_prt); + Complex C = amrex::pow(-Rc + amrex::sqrt(Dc),1.0/3.0); + + // Define a cubic root of unity xi + amrex::ParticleReal xire = -1.0/2.0; + amrex::ParticleReal xiim = std::sqrt(3.0)/2.0; + Complex xi(xire,xiim); + + //Three roots in algebraic form. + + if (C.m_real == 0.0_prt && C.m_imag == 0.0_prt) { // Special case of a triple root + + x1 = - b/(3.0_prt*a); + x2 = - b/(3.0_prt*a); + x3 = - b/(3.0_prt*a); + + } else { + + Complex z1 = Qc/C - C; + Complex z2 = Qc/(xi*C) - xi*C; + Complex z3 = Qc/(amrex::pow(xi,2)*C) - amrex::pow(xi,2)*C; + x1 = z2.m_real - b/(3.0*a); + x2 = z1.m_real - b/(3.0*a); + x3 = z3.m_real - b/(3.0*a); + } + + roots = std::make_tuple(x1,x2,x3); + return roots; + } + + + /** Function to take the trace of a square 6x6 matrix. + * + * @param[in] A a square matrix + * @returns the trace of A + */ + amrex::ParticleReal + TraceMat ( + amrex::Array2D const & A + ) + { + int const dim = 6; + amrex::ParticleReal trA = 0.0; + + for (int i = 1; i < dim+1; i++) { + trA += A(i,i); + } + return trA; + } + + + /** Function to multiply two square matrices of dimension 6. + * + * @param[in] A a square matrix + * @param[in] B square matrix + * @returns the matrix C = AB + */ + amrex::Array2D + MultiplyMat ( + amrex::Array2D const & A, + amrex::Array2D const & B + ) + { + amrex::Array2D C; + int const dim = 6; + + for (int i = 1; i < dim+1; i++) { + for (int j = 1; j < dim+1; j++) { + C(i,j) = 0; + + for (int k = 1; k < dim+1; k++) { + C(i,j) += A(i,k) * B(k,j); + } + + } + + } + return C; + } + + +} // namespace impactx::diagnostics + +#endif // COVARIANCE_MATRIX_MATH_H diff --git a/src/particles/diagnostics/DiagnosticOutput.cpp b/src/particles/diagnostics/DiagnosticOutput.cpp index fee8af6eb..1ec35610f 100644 --- a/src/particles/diagnostics/DiagnosticOutput.cpp +++ b/src/particles/diagnostics/DiagnosticOutput.cpp @@ -43,7 +43,33 @@ namespace impactx::diagnostics if (otype == OutputType::PrintRefParticle) { file_handler << "step s beta gamma beta_gamma x y z t px py pz pt\n"; } else if (otype == OutputType::PrintReducedBeamCharacteristics) { - file_handler << "step" << " " << "s" << " " + + // determine whether to output eigenemittances + amrex::ParmParse pp_diag("diag"); + bool compute_eigenemittances = false; + pp_diag.queryAdd("eigenemittances", compute_eigenemittances); + + if (compute_eigenemittances) { + file_handler << "step" << " " << "s" << " " + << "x_mean" << " " << "x_min" << " " << "x_max" << " " + << "y_mean" << " " << "y_min" << " " << "y_max" << " " + << "t_mean" << " " << "t_min" << " " << "t_max" << " " + << "sig_x" << " " << "sig_y" << " " << "sig_t" << " " + << "px_mean" << " " << "px_min" << " " << "px_max" << " " + << "py_mean" << " " << "py_min" << " " << "py_max" << " " + << "pt_mean" << " " << "pt_min" << " " << "pt_max" << " " + << "sig_px" << " " << "sig_py" << " " << "sig_pt" << " " + << "emittance_x" << " " << "emittance_y" << " " << "emittance_t" << " " + << "alpha_x" << " " << "alpha_y" << " " << "alpha_t" << " " + << "beta_x" << " " << "beta_y" << " " << "beta_t" << " " + << "dispersion_x" << " " << "dispersion_px" << " " + << "dispersion_y" << " " << "dispersion_py" << " " + << "emittance_xn" << " " << "emittance_yn" << " " << "emittance_tn" << " " + << "emittance_1" << " " << "emittance_2" << " " << "emittance_3" << " " + << "charge_C" << " " + << "\n"; + } else { + file_handler << "step" << " " << "s" << " " << "x_mean" << " " << "x_min" << " " << "x_max" << " " << "y_mean" << " " << "y_min" << " " << "y_max" << " " << "t_mean" << " " << "t_min" << " " << "t_max" << " " @@ -57,8 +83,10 @@ namespace impactx::diagnostics << "beta_x" << " " << "beta_y" << " " << "beta_t" << " " << "dispersion_x" << " " << "dispersion_px" << " " << "dispersion_y" << " " << "dispersion_py" << " " + << "emittance_xn" << " " << "emittance_yn" << " " << "emittance_tn" << " " << "charge_C" << " " << "\n"; + } } } @@ -92,7 +120,31 @@ namespace impactx::diagnostics amrex::ParticleReal const s = pc.GetRefParticle().s; - file_handler << step << " " << s << " " + // determine whether to output eigenemittances + amrex::ParmParse pp_diag("diag"); + bool compute_eigenemittances = false; + pp_diag.queryAdd("eigenemittances", compute_eigenemittances); + + if (compute_eigenemittances) { + file_handler << step << " " << s << " " + << rbc.at("x_mean") << " " << rbc.at("x_min") << " " << rbc.at("x_max") << " " + << rbc.at("y_mean") << " " << rbc.at("y_min") << " " << rbc.at("y_max") << " " + << rbc.at("t_mean") << " " << rbc.at("t_min") << " " << rbc.at("t_max") << " " + << rbc.at("sig_x") << " " << rbc.at("sig_y") << " " << rbc.at("sig_t") << " " + << rbc.at("px_mean") << " " << rbc.at("px_min") << " " << rbc.at("px_max") << " " + << rbc.at("py_mean") << " " << rbc.at("py_min") << " " << rbc.at("py_max") << " " + << rbc.at("pt_mean") << " " << rbc.at("pt_min") << " " << rbc.at("pt_max") << " " + << rbc.at("sig_px") << " " << rbc.at("sig_py") << " " << rbc.at("sig_pt") << " " + << rbc.at("emittance_x") << " " << rbc.at("emittance_y") << " " << rbc.at("emittance_t") << " " + << rbc.at("alpha_x") << " " << rbc.at("alpha_y") << " " << rbc.at("alpha_t") << " " + << rbc.at("beta_x") << " " << rbc.at("beta_y") << " " << rbc.at("beta_t") << " " + << rbc.at("dispersion_x") << " " << rbc.at("dispersion_px") << " " + << rbc.at("dispersion_y") << " " << rbc.at("dispersion_py") << " " + << rbc.at("emittance_xn") << " " << rbc.at("emittance_yn") << " " << rbc.at("emittance_tn") << " " + << rbc.at("emittance_1") << " " << rbc.at("emittance_2") << " " << rbc.at("emittance_3") << " " + << rbc.at("charge_C") << "\n"; + } else { + file_handler << step << " " << s << " " << rbc.at("x_mean") << " " << rbc.at("x_min") << " " << rbc.at("x_max") << " " << rbc.at("y_mean") << " " << rbc.at("y_min") << " " << rbc.at("y_max") << " " << rbc.at("t_mean") << " " << rbc.at("t_min") << " " << rbc.at("t_max") << " " @@ -106,7 +158,9 @@ namespace impactx::diagnostics << rbc.at("beta_x") << " " << rbc.at("beta_y") << " " << rbc.at("beta_t") << " " << rbc.at("dispersion_x") << " " << rbc.at("dispersion_px") << " " << rbc.at("dispersion_y") << " " << rbc.at("dispersion_py") << " " + << rbc.at("emittance_xn") << " " << rbc.at("emittance_yn") << " " << rbc.at("emittance_tn") << " " << rbc.at("charge_C") << "\n"; + } } // if( otype == OutputType::PrintReducedBeamCharacteristics) // TODO: add as an option to the monitor element diff --git a/src/particles/diagnostics/EmittanceInvariants.H b/src/particles/diagnostics/EmittanceInvariants.H new file mode 100644 index 000000000..177371953 --- /dev/null +++ b/src/particles/diagnostics/EmittanceInvariants.H @@ -0,0 +1,77 @@ +/* Copyright 2022-2023 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Marco Garten, Chad Mitchell, Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#ifndef IMPACTX_EMITTANCE_INVARIANTS +#define IMPACTX_EMITTANCE_INVARIANTS + +#include "particles/ImpactXParticleContainer.H" + +#include +#include + +#include + + + +namespace impactx::diagnostics +{ + + /** Returns the three independent kinematic moment invariants + * denoted I2, I4, and I6 as constructed from the 6x6 + * beam covariance matrix. These three quantities are invariant + * under any linear symplectic transport map, and are used in the + * calculation of the three eigenemittances, as described in: + * + * G. Rangarajan, F. Neri, and A. Dragt, "Generalized Emittance + * Invariants," in Proc. 1989 IEEE Part. Accel. Conf., Chicago, IL, + * 1989, doi:10.1109/PAC.1989.73422. + * A. Dragt, F. Neri, and G. Rangarajan, "General Moment Invariants + * for Linear Hamiltonian Systems," Phys. Rev. A 45, 2572-2585 (1992), + * doi:10.1103/PhysRevA.45.2572. + * + * @param[in] Sigma symmetric 6x6 covariance matrix + * @returns tuple containing invariants I2, I4, and I6 + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal + > + KineticInvariants ( + amrex::Array2D const & Sigma + ); + + /** Returns the three eigenemittances + * denoted e1, e2, and e3 as constructed from the 6x6 + * beam covariance matrix. These three quantities are invariant + * under any linear symplectic transport map, and reduce to + * the projected normalized rms emittances in the limit of + * uncoupled transport. These quantities are described in: + * + * G. Rangarajan, F. Neri, and A. Dragt, "Generalized Emittance + * Invariants," in Proc. 1989 IEEE Part. Accel. Conf., Chicago, IL, + * 1989, doi:10.1109/PAC.1989.73422. + * A. Dragt, F. Neri, and G. Rangarajan, "General Moment Invariants + * for Linear Hamiltonian Systems," Phys. Rev. A 45, 2572-2585 (1992), + * doi:10.1103/PhysRevA.45.2572. + * + * @param[in] Sigma symmetric 6x6 covariance matrix + * @returns tuple containing eigenemittances e1, e2, and e3 + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal> + Eigenemittances ( + amrex::Array2D const & Sigma + ); + +} // namespace impactx::diagnostics + +#endif // IMPACTX_EMITTANCE_INVARIANTS diff --git a/src/particles/diagnostics/EmittanceInvariants.cpp b/src/particles/diagnostics/EmittanceInvariants.cpp new file mode 100644 index 000000000..20c6df32a --- /dev/null +++ b/src/particles/diagnostics/EmittanceInvariants.cpp @@ -0,0 +1,146 @@ +/* Copyright 2022-2023 The Regents of the University of California, through Lawrence + * Berkeley National Laboratory (subject to receipt of any required + * approvals from the U.S. Dept. of Energy). All rights reserved. + * + * This file is part of ImpactX. + * + * Authors: Chad Mitchell, Axel Huebl + * License: BSD-3-Clause-LBNL + */ +#include "CovarianceMatrixMath.H" + +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace impactx::diagnostics +{ + + /** This function returns the three independent kinetic invariants + * denoted I2, I4, and I6 as constructed from the 6x6 + * beam covariance matrix. These three quantities are invariant + * under any linear symplectic transport map, and are used in the + * calculation of the three eigenemittances. + * + * input - Sigma symmetric 6x6 covariance matrix + * returns - tuple containing invarants I2, I4, and I6 + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal + > + KineticInvariants ( + amrex::Array2D const & Sigma + ) + { + using namespace amrex::literals; + + std::tuple invariants; + amrex::ParticleReal I2 = 0.0_prt; + amrex::ParticleReal I4 = 0.0_prt; + amrex::ParticleReal I6 = 0.0_prt; + + // Intermediate matrices used for storage. + amrex::Array2D S1; + amrex::Array2D S2; + amrex::Array2D S4; + amrex::Array2D S6; + + // Construct the matrix S1 = Sigma*J. This is a + // permutation of the columns of Sigma with + // a change of sign. + for (int i = 1; i < 7; i++) { + for (int j = 1; j < 7; j++) { + if (j % 2 != 0) { + S1(i,j) = -Sigma(i,j+1); // if j is odd + } + else { + S1(i,j) = +Sigma(i,j-1); // if j is even + } + } + } + + // Carry out necessary matrix multiplications (3 are needed). + S2 = impactx::diagnostics::MultiplyMat(S1,S1); + S4 = impactx::diagnostics::MultiplyMat(S2,S2); + S6 = impactx::diagnostics::MultiplyMat(S2,S4); + + // Define the three kinematic invariants (should be nonnegative). + I2 = -impactx::diagnostics::TraceMat(S2)/2.0_prt; + I4 = +impactx::diagnostics::TraceMat(S4)/2.0_prt; + I6 = -impactx::diagnostics::TraceMat(S6)/2.0_prt; + + + invariants = std::make_tuple(I2,I4,I6); + return invariants; + } + + + /** This function returns the three eigenemittances + * denoted e1, e2, and e3 as constructed from the 6x6 + * beam covariance matrix. These three quantities are invariant + * under any linear symplectic transport map, and reduce to + * the projected normalized rms emittances in the limit of + * uncoupled transport. + * + * input - Sigma symmetric 6x6 covariance matrix + * returns - tuple containing eigenemittances e1, e2, and e3 + */ + std::tuple< + amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal> + Eigenemittances ( + amrex::Array2D const & Sigma + ) + { + BL_PROFILE("impactx::diagnostics::Eigenemittances"); + + using namespace amrex::literals; + + std::tuple invariants; + std::tuple roots; + std::tuple emittances; + + // Get the invariants I2, I4, and I6 from the covariance matrix. + invariants = KineticInvariants(Sigma); + amrex::ParticleReal I2 = std::get<0>(invariants); + amrex::ParticleReal I4 = std::get<1>(invariants); + amrex::ParticleReal I6 = std::get<2>(invariants); + + // Construct the coefficients of the cubic polynomial. + // This expression for the characteristic polynomial can be found in: + // V. Balandin, W. Decking, and N. Golubeva, "Relations Between Projected + // Emittances and Eigenemittances," in IPAC2013, Shanghai, China, 2013, + // doi:10.48550/arXiv.1305.1532. + amrex::ParticleReal a = 1.0_prt; + amrex::ParticleReal b = -I2; + amrex::ParticleReal c = (pow(I2,2)-I4)/2.0_prt; + amrex::ParticleReal d = -pow(I2,3)/6.0_prt + I2*I4/2.0_prt - I6/3.0_prt; + + // Return the cubic coefficients + //std::cout << "Return a,b,c,d " << a << " " << b << " " << c << " " << d << "\n"; + + // Solve for the roots to obtain the eigenemittances. + // Caution: The order of e1,e2,e3 should be consistent with the + // order ex,ey,et in the limit of uncoupled transport. + // The order below is important and has been checked. + roots = CubicRootsTrig(a,b,c,d); + amrex::ParticleReal e1 = sqrt(std::abs(std::get<1>(roots))); + amrex::ParticleReal e2 = sqrt(std::abs(std::get<2>(roots))); + amrex::ParticleReal e3 = sqrt(std::abs(std::get<0>(roots))); + + emittances = std::make_tuple(e1,e2,e3); + return emittances; + } + + +} // namespace impactx::diagnostics diff --git a/src/particles/diagnostics/ReducedBeamCharacteristics.cpp b/src/particles/diagnostics/ReducedBeamCharacteristics.cpp index 3b4d19ffd..99a11560d 100644 --- a/src/particles/diagnostics/ReducedBeamCharacteristics.cpp +++ b/src/particles/diagnostics/ReducedBeamCharacteristics.cpp @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "particles/ReferenceParticle.H" +#include "EmittanceInvariants.H" #include // for TinyProfiler #include // for AMREX_GPU_DEVICE @@ -33,6 +34,9 @@ namespace impactx::diagnostics RefPart const ref_part = pc.GetRefParticle(); // reference particle charge in C amrex::ParticleReal const q_C = ref_part.charge; + // reference particle relativistic beta*gamma + amrex::ParticleReal const bg = ref_part.beta_gamma(); + amrex::ParticleReal const bg2 = bg*bg; // preparing access to particle data: SoA using PType = typename ImpactXParticleContainer::SuperParticleType; @@ -151,7 +155,7 @@ namespace impactx::diagnostics * https://stackoverflow.com/questions/55136414/constexpr-variable-captured-inside-lambda-loses-its-constexpr-ness */ // number of reduction operations in second concurrent batch - static constexpr std::size_t num_red_ops_2 = 14; + static constexpr std::size_t num_red_ops_2 = 22; // prepare reduction operations for calculation of mean square and correlation values amrex::TypeMultiplier reduce_ops_2; using ReducedDataT2 = amrex::TypeMultiplier; @@ -182,12 +186,20 @@ namespace impactx::diagnostics const amrex::ParticleReal p_xpx = (p_x-x_mean)*(p_px-px_mean)*p_w; const amrex::ParticleReal p_ypy = (p_y-y_mean)*(p_py-py_mean)*p_w; const amrex::ParticleReal p_tpt = (p_t-t_mean)*(p_pt-pt_mean)*p_w; - // prepare correlations for dispersion + // prepare correlations for dispersion (4 required) const amrex::ParticleReal p_xpt = (p_x-x_mean)*(p_pt-pt_mean)*p_w; const amrex::ParticleReal p_pxpt = (p_px-px_mean)*(p_pt-pt_mean)*p_w; const amrex::ParticleReal p_ypt = (p_y-y_mean)*(p_pt-pt_mean)*p_w; const amrex::ParticleReal p_pypt = (p_py-py_mean)*(p_pt-pt_mean)*p_w; - + // prepare additional cross-plane correlations (8 required) + const amrex::ParticleReal p_xy = (p_x-x_mean)*(p_y-y_mean)*p_w; + const amrex::ParticleReal p_xpy = (p_x-x_mean)*(p_py-py_mean)*p_w; + const amrex::ParticleReal p_xt = (p_x-x_mean)*(p_t-t_mean)*p_w; + const amrex::ParticleReal p_pxy = (p_px-px_mean)*(p_y-y_mean)*p_w; + const amrex::ParticleReal p_pxpy = (p_px-px_mean)*(p_py-py_mean)*p_w; + const amrex::ParticleReal p_pxt = (p_px-px_mean)*(p_t-t_mean)*p_w; + const amrex::ParticleReal p_yt = (p_y-y_mean)*(p_t-t_mean)*p_w; + const amrex::ParticleReal p_pyt = (p_py-py_mean)*(p_t-t_mean)*p_w; const amrex::ParticleReal p_charge = q_C*p_w; @@ -195,6 +207,7 @@ namespace impactx::diagnostics p_px_ms, p_py_ms, p_pt_ms, p_xpx, p_ypy, p_tpt, p_xpt, p_pxpt, p_ypt, p_pypt, + p_xy, p_xpy, p_xt, p_pxy, p_pxpy, p_pxt, p_yt, p_pyt, p_charge}; }, reduce_ops_2 @@ -207,6 +220,7 @@ namespace impactx::diagnostics * px_ms, py_ms, pt_ms, * xpx, ypy, tpt, * p_xpt, p_pxpt, p_ypt, p_pypt, + * p_xy, p_xpy, p_xt, p_pxy, p_pxpy, p_pxt, p_yt, p_pyt, * charge */ amrex::constexpr_for<0, num_red_ops_2> ([&](auto i) { @@ -248,7 +262,15 @@ namespace impactx::diagnostics amrex::ParticleReal const pxpt = values_per_rank_2nd.at(10) /= w_sum; amrex::ParticleReal const ypt = values_per_rank_2nd.at(11) /= w_sum; amrex::ParticleReal const pypt = values_per_rank_2nd.at(12) /= w_sum; - amrex::ParticleReal const charge = values_per_rank_2nd.at(13); + amrex::ParticleReal const xy = values_per_rank_2nd.at(13) /= w_sum; + amrex::ParticleReal const xpy = values_per_rank_2nd.at(14) /= w_sum; + amrex::ParticleReal const xt = values_per_rank_2nd.at(15) /= w_sum; + amrex::ParticleReal const pxy = values_per_rank_2nd.at(16) /= w_sum; + amrex::ParticleReal const pxpy = values_per_rank_2nd.at(17) /= w_sum; + amrex::ParticleReal const pxt = values_per_rank_2nd.at(18) /= w_sum; + amrex::ParticleReal const yt = values_per_rank_2nd.at(19) /= w_sum; + amrex::ParticleReal const pyt = values_per_rank_2nd.at(20) /= w_sum; + amrex::ParticleReal const charge = values_per_rank_2nd.at(21); // standard deviations of positions amrex::ParticleReal const sig_x = std::sqrt(x_ms); amrex::ParticleReal const sig_y = std::sqrt(y_ms); @@ -283,6 +305,65 @@ namespace impactx::diagnostics amrex::ParticleReal const alpha_y = - ypy_d / emittance_yd; amrex::ParticleReal const alpha_t = - tpt / emittance_t; + // Calculate normalized emittances + amrex::ParticleReal emittance_xn = emittance_x * bg; + amrex::ParticleReal emittance_yn = emittance_y * bg; + amrex::ParticleReal emittance_tn = emittance_t * bg; + + // Determine whether to calculate eigenemittances, and initialize + amrex::ParmParse pp_diag("diag"); + bool compute_eigenemittances = false; + pp_diag.queryAdd("eigenemittances", compute_eigenemittances); + amrex::ParticleReal emittance_1 = emittance_xn; + amrex::ParticleReal emittance_2 = emittance_yn; + amrex::ParticleReal emittance_3 = emittance_tn; + + if (compute_eigenemittances) { + // Store the covariance matrix in dynamical variables: + amrex::Array2D Sigma; + Sigma(1,1) = x_ms; + Sigma(1,2) = xpx * bg; + Sigma(1,3) = xy; + Sigma(1,4) = xpy * bg; + Sigma(1,5) = xt; + Sigma(1,6) = xpt * bg; + Sigma(2,1) = xpx * bg; + Sigma(2,2) = px_ms * bg2; + Sigma(2,3) = pxy * bg; + Sigma(2,4) = pxpy * bg2; + Sigma(2,5) = pxt * bg; + Sigma(2,6) = pxpt * bg2; + Sigma(3,1) = xy; + Sigma(3,2) = pxy * bg; + Sigma(3,3) = y_ms; + Sigma(3,4) = ypy * bg; + Sigma(3,5) = yt; + Sigma(3,6) = ypt * bg; + Sigma(4,1) = xpy * bg; + Sigma(4,2) = pxpy * bg2; + Sigma(4,3) = ypy * bg; + Sigma(4,4) = py_ms * bg2; + Sigma(4,5) = pyt * bg; + Sigma(4,6) = pypt * bg2; + Sigma(5,1) = xt; + Sigma(5,2) = pxt * bg; + Sigma(5,3) = yt; + Sigma(5,4) = pyt * bg; + Sigma(5,5) = t_ms; + Sigma(5,6) = tpt * bg; + Sigma(6,1) = xpt * bg; + Sigma(6,2) = pxpt * bg2; + Sigma(6,3) = ypt * bg; + Sigma(6,4) = pypt * bg2; + Sigma(6,5) = tpt * bg; + Sigma(6,6) = pt_ms * bg2; + // Calculate eigenemittances + std::tuple emittances = Eigenemittances(Sigma); + emittance_1 = std::get<0>(emittances); + emittance_2 = std::get<1>(emittances); + emittance_3 = std::get<2>(emittances); + } + std::unordered_map data; data["x_mean"] = x_mean; data["x_min"] = x_min; @@ -322,6 +403,14 @@ namespace impactx::diagnostics data["dispersion_y"] = dispersion_y; data["dispersion_py"] = dispersion_py; data["charge_C"] = charge; + data["emittance_xn"] = emittance_xn; + data["emittance_yn"] = emittance_yn; + data["emittance_tn"] = emittance_tn; + if (compute_eigenemittances) { + data["emittance_1"] = emittance_1; + data["emittance_2"] = emittance_2; + data["emittance_3"] = emittance_3; + } return data; } diff --git a/src/python/ImpactX.cpp b/src/python/ImpactX.cpp index 359015b76..b67ab141b 100644 --- a/src/python/ImpactX.cpp +++ b/src/python/ImpactX.cpp @@ -209,6 +209,16 @@ void init_ImpactX (py::module& m) }, "Number of longitudinal bins used for CSR calculations (default: 150)." ) + .def_property("eigenemittances", + [](ImpactX & /* ix */) { + return detail::get_or_throw("diag", "eigenemittances"); + }, + [](ImpactX & /* ix */, bool const enable) { + amrex::ParmParse pp_diag("diag"); + pp_diag.add("eigenemittances", enable); + }, + "Enable or disable eigenemittance diagnostic calculations (default: disabled)." + ) .def_property("space_charge", [](ImpactX & /* ix */) { return detail::get_or_throw("algo", "space_charge"); diff --git a/tests/python/test_transformation.py b/tests/python/test_transformation.py index 93b52966c..ad660564c 100644 --- a/tests/python/test_transformation.py +++ b/tests/python/test_transformation.py @@ -81,7 +81,7 @@ def test_transformation(): for key, val in rbc_s0.items(): if not np.isclose(val, rbc_s[key], rtol=rtol, atol=atol): print(f"initial[{key}]={val}, final[{key}]={rbc_s[key]} not equal") - assert np.isclose(val, rbc_s[key], rtol=rtol, atol=atol) + assert False # assert that the t-based beam is different, at least in the following keys: large_st_diff_keys = [ "beta_x", From a4107e4d8adffa24764217872c2dcd03912f48e0 Mon Sep 17 00:00:00 2001 From: ax3l Date: Thu, 10 Oct 2024 04:47:55 +0000 Subject: [PATCH 29/30] Update Stub Files --- src/python/impactx/impactx_pybind/__init__.pyi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/python/impactx/impactx_pybind/__init__.pyi b/src/python/impactx/impactx_pybind/__init__.pyi index 5d18cf746..8370f8a2d 100644 --- a/src/python/impactx/impactx_pybind/__init__.pyi +++ b/src/python/impactx/impactx_pybind/__init__.pyi @@ -238,6 +238,13 @@ class ImpactX: @dynamic_size.setter def dynamic_size(self, arg1: bool) -> None: ... @property + def eigenemittances(self) -> bool: + """ + Enable or disable eigenemittance diagnostic calculations (default: disabled). + """ + @eigenemittances.setter + def eigenemittances(self, arg1: bool) -> None: ... + @property def finest_level(self) -> int: """ The currently finest level of mesh-refinement used. This is always less or equal to max_level. From 48bb008322baa567cc914a752e0deebd04c3b1c5 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 9 Oct 2024 22:53:43 -0700 Subject: [PATCH 30/30] Fix CI: CodeQL Setup (#732) Fix broken Python setup in CodeQL CI. --- .github/workflows/codeql.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 10b1829cc..b0bed9ad3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,23 +30,29 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + name: Install Python + with: + python-version: '3.x' + - name: Install Packages run: | sudo apt-get update sudo apt-get install --yes cmake openmpi-bin libfftw3-dev libfftw3-mpi-dev libopenmpi-dev libhdf5-openmpi-dev python -m pip install --upgrade pip + python -m pip install --upgrade pipx python -m pip install --upgrade wheel - python -m pip install --upgrade cmake - export CMAKE="$HOME/.local/bin/cmake" && echo "CMAKE=$CMAKE" >> $GITHUB_ENV python -m pip install --upgrade numpy python -m pip install --upgrade mpi4py python -m pip install --upgrade pytest + python -m pip install --upgrade cmake + python -m pipx install cmake - name: Configure (C++) if: ${{ matrix.language == 'cpp' }} run: | - $CMAKE -S . -B build -DImpactX_FFT=ON -DImpactX_PYTHON=OFF + cmake -S . -B build -DImpactX_FFT=ON -DImpactX_PYTHON=OFF - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -64,7 +70,7 @@ jobs: - name: Build (C++) if: ${{ matrix.language == 'cpp' }} run: | - $CMAKE --build build -j 4 + cmake --build build -j 4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3