From 7f8d15fb8feae65b7b8f969031eea3d5282e0fcd Mon Sep 17 00:00:00 2001 From: Chad Mitchell <46825199+cemitch99@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:07:54 -0800 Subject: [PATCH] Apply transverse aperture to thick elements. (#788) * Initial files for applying aperture to thick elements. * Update aperture.H Add namespace amrex::literals * Add Python bindings for drift. * Update elements.cpp Add Python class for aperture mixin. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add draft example. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Move if conditional into mixin. * Add drift aperture input documentation. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Small changes (excluding naming and std::optional). Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja> * Update Drift.H Remove "maybe unused". * Change name xmax -> x_aperture... * Fix one missing xmax -> x_aperture in elements.cpp. * Modify naming x_aperture -> aperture_x. * Update src/initialization/InitElement.cpp * Test workflow for adding elements using CFbend. * Add aperture input to all thick elements. * Update thin Aperture element naming. * Update element parameter docs. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename Mixin Class: `PipeAperture` * Python: Mixin Elements w/o Constructor We do not want to create variables from them. * C++: Add `mixin::` Element Namespace Mirror directory structure and separate from "physical" elements as we do in Python. * Input Aperture: Backward Compatible Name For inputs file. * Last Commit: Forgot Else * Rename Mixin File: Pipe Aperture * Document benchmark example. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove comment re idcpu Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja> --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja> --- docs/source/usage/parameters.rst | 34 ++- docs/source/usage/python.rst | 69 ++++-- examples/CMakeLists.txt | 15 ++ examples/aperture/README.rst | 53 +++++ examples/aperture/analysis_aperture_thick.py | 110 +++++++++ examples/aperture/input_absorber.in | 4 +- examples/aperture/input_aperture.in | 4 +- examples/aperture/input_aperture_periodic.in | 4 +- examples/aperture/input_aperture_thick.in | 47 ++++ examples/aperture/run_absorber.py | 4 +- examples/aperture/run_aperture.py | 2 +- examples/aperture/run_aperture_periodic.py | 4 +- examples/aperture/run_aperture_thick.py | 61 +++++ src/initialization/InitElement.cpp | 104 +++++++-- src/particles/elements/Aperture.H | 28 +-- src/particles/elements/Buncher.H | 10 +- src/particles/elements/CFbend.H | 24 +- src/particles/elements/ChrDrift.H | 26 ++- src/particles/elements/ChrPlasmaLens.H | 24 +- src/particles/elements/ChrQuad.H | 24 +- src/particles/elements/ChrUniformAcc.H | 24 +- src/particles/elements/ConstF.H | 24 +- src/particles/elements/DipEdge.H | 10 +- src/particles/elements/Drift.H | 30 ++- src/particles/elements/Empty.H | 4 +- src/particles/elements/ExactDrift.H | 26 ++- src/particles/elements/ExactSbend.H | 24 +- src/particles/elements/Kicker.H | 10 +- src/particles/elements/LinearMap.H | 10 +- src/particles/elements/Marker.H | 6 +- src/particles/elements/Multipole.H | 10 +- src/particles/elements/NonlinearLens.H | 10 +- src/particles/elements/PRot.H | 8 +- src/particles/elements/PlaneXYRot.H | 10 +- src/particles/elements/Programmable.H | 2 +- src/particles/elements/Quad.H | 24 +- src/particles/elements/RFCavity.H | 22 +- src/particles/elements/Sbend.H | 24 +- src/particles/elements/ShortRF.H | 10 +- src/particles/elements/SoftQuad.H | 22 +- src/particles/elements/SoftSol.H | 22 +- src/particles/elements/Sol.H | 24 +- src/particles/elements/TaperedPL.H | 10 +- src/particles/elements/ThinDipole.H | 10 +- src/particles/elements/diagnostics/openPMD.H | 2 +- src/particles/elements/mixin/alignment.H | 4 +- src/particles/elements/mixin/beamoptic.H | 4 +- .../elements/mixin/lineartransport.H | 4 +- src/particles/elements/mixin/named.H | 4 +- src/particles/elements/mixin/nofinalize.H | 4 +- src/particles/elements/mixin/pipeaperture.H | 103 ++++++++ src/particles/elements/mixin/thick.H | 4 +- src/particles/elements/mixin/thin.H | 4 +- src/python/elements.cpp | 220 +++++++++++------- 54 files changed, 1050 insertions(+), 330 deletions(-) create mode 100755 examples/aperture/analysis_aperture_thick.py create mode 100644 examples/aperture/input_aperture_thick.in create mode 100755 examples/aperture/run_aperture_thick.py create mode 100644 src/particles/elements/mixin/pipeaperture.H diff --git a/docs/source/usage/parameters.rst b/docs/source/usage/parameters.rst index ec87c7a29..8da703ac0 100644 --- a/docs/source/usage/parameters.rst +++ b/docs/source/usage/parameters.rst @@ -132,6 +132,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``drift`` for a free drift. This requires these additional parameters: @@ -140,6 +142,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``drift_chromatic`` for a free drift, with chromatic effects included. @@ -150,6 +154,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``drift_exact`` for a free drift, using the exact nonlinear map. This requires these additional parameters: @@ -158,6 +164,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``quad`` for a quadrupole. This requires these additional parameters: @@ -172,6 +180,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``quad_chromatic`` for A Quadrupole magnet, with chromatic effects included. @@ -191,6 +201,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``quadrupole_softedge`` for a soft-edge quadrupole. This requires these additional parameters: @@ -204,6 +216,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.mapsteps`` (``integer``) number of integration steps per slice used for map and reference particle push in applied fields (default: ``1``) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) @@ -222,6 +236,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``sbend`` for a bending magnet. This requires these additional parameters: @@ -231,6 +247,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``sbend_exact`` for a bending magnet using the exact nonlinear map for the bend body. The map corresponds to the map described in: @@ -244,6 +262,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``solenoid`` for an ideal hard-edge solenoid magnet. This requires these additional parameters: @@ -254,6 +274,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``solenoid_softedge`` for a soft-edge solenoid. This requires these additional parameters: @@ -272,6 +294,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.mapsteps`` (``integer``) number of integration steps per slice used for map and reference particle push in applied fields (default: ``1``) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) @@ -294,6 +318,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``rfcavity`` a radiofrequency cavity. @@ -309,6 +335,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.mapsteps`` (``integer``) number of integration steps per slice used for map and reference particle push in applied fields (default: ``1``) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) @@ -352,6 +380,8 @@ Lattice Elements * ``<element_name>.dx`` (``float``, in meters) horizontal translation error * ``<element_name>.dy`` (``float``, in meters) vertical translation error * ``<element_name>.rotation`` (``float``, in degrees) rotation error in the transverse plane + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical) * ``<element_name>.nslice`` (``integer``) number of slices used for the application of space charge (default: ``1``) * ``linear_map`` for a custom, linear transport matrix. @@ -430,8 +460,8 @@ Lattice Elements * ``aperture`` for a thin collimator element applying a transverse aperture boundary. This requires these additional parameters: - * ``<element_name>.xmax`` (``float``, in meters) maximum value of the horizontal coordinate - * ``<element_name>.ymax`` (``float``, in meters) maximum value of the vertical coordinate + * ``<element_name>.aperture_x`` (``float``, in meters) horizontal half-aperture (elliptical or rectangular) + * ``<element_name>.aperture_y`` (``float``, in meters) vertical half-aperture (elliptical or rectangular) * ``<element_name>.repeat_x`` (``float``, in meters) horizontal period for repeated aperture masking (inactive by default) * ``<element_name>.repeat_y`` (``float``, in meters) vertical period for repeated aperture masking (inactive by default) * ``<element_name>.shape`` (``string``) shape of the aperture boundary: ``rectangular`` (default) or ``elliptical`` diff --git a/docs/source/usage/python.rst b/docs/source/usage/python.rst index 889aca74d..4c8c03b81 100644 --- a/docs/source/usage/python.rst +++ b/docs/source/usage/python.rst @@ -507,7 +507,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, name=None) +.. py:class:: impactx.elements.CFbend(ds, rc, k, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) A combined function bending magnet. This is an ideal Sbend with a normal quadrupole field component. @@ -520,10 +520,12 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) 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.ConstF(ds, kx, ky, kt, dx=0, dy=0, rotation=0, nslice=1, name=None) +.. py:class:: impactx.elements.ConstF(ds, kx, ky, kt, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) A linear Constant Focusing element. @@ -534,6 +536,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -570,15 +574,20 @@ 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.Drift(ds, dx=0, dy=0, rotation=0, nslice=1, name=None) +.. py:class:: impactx.elements.Drift(ds, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) A drift. :param ds: Segment length in m + :param dx: horizontal translation error in m + :param dy: vertical translation error in m + :param rotation: rotation error in the transverse plane [degrees] + :param aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) 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, name=None) +.. py:class:: impactx.elements.ChrDrift(ds, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=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 @@ -588,10 +597,12 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) 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.ExactDrift(ds, dx=0, dy=0, rotation=0, nslice=1, name=None) +.. py:class:: impactx.elements.ExactDrift(ds, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) A drift using the exact nonlinear transfer map. @@ -599,6 +610,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -748,7 +761,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, name=None) +.. py:class:: impactx.elements.Quad(ds, k, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) A Quadrupole magnet. @@ -760,10 +773,12 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) 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.ChrQuad(ds, k, unit=0, dx=0, dy=0, rotation=0, nslice=1, name=None) +.. py:class:: impactx.elements.ChrQuad(ds, k, unit=0, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=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 @@ -779,6 +794,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -790,7 +807,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, name=None) +.. py:class:: impactx.elements.ChrPlasmaLens(ds, k, unit=0, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=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 @@ -804,6 +821,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -815,7 +834,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, name=None) +.. py:class:: impactx.elements.ChrAcc(ds, ez, bz, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) Acceleration in a uniform field Ez, with a uniform solenoidal field Bz. @@ -830,6 +849,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -841,7 +862,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, name=None) +.. py:class:: impactx.elements.RFCavity(ds, escale, freq, phase, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, mapsteps=1, nslice=1, name=None) A radiofrequency cavity. @@ -856,11 +877,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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :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, name=None) +.. py:class:: impactx.elements.Sbend(ds, rc, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) An ideal sector bend. @@ -869,10 +892,12 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) 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.ExactSbend(ds, phi, B=0.0, dx=0, dy=0, rotation=0, nslice=1, name=None) +.. py:class:: impactx.elements.ExactSbend(ds, phi, B=0.0, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=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. @@ -888,6 +913,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -913,7 +940,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.SoftSolenoid(ds, bscale, cos_coefficients, sin_coefficients, unit=0, dx=0, dy=0, rotation=0, mapsteps=1, nslice=1, name=None) +.. py:class:: impactx.elements.SoftSolenoid(ds, bscale, cos_coefficients, sin_coefficients, unit=0, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, mapsteps=1, nslice=1, name=None) A soft-edge solenoid. @@ -929,11 +956,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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :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, name=None) +.. py:class:: impactx.elements.Sol(ds, ks, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, nslice=1, name=None) An ideal hard-edge Solenoid magnet. @@ -942,6 +971,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :param nslice: number of slices used for the application of space charge :param name: an optional name for the element @@ -963,12 +994,12 @@ 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.Aperture(xmax, ymax, shape="rectangular", dx=0, dy=0, rotation=0, name=None) +.. py:class:: impactx.elements.Aperture(aperture_x, aperture_y, shape="rectangular", dx=0, dy=0, rotation=0, name=None) A thin collimator element, applying a transverse aperture boundary. - :param xmax: maximum allowed value of the horizontal coordinate (meter) - :param ymax: maximum allowed value of the vertical coordinate (meter) + :param aperture_x: horizontal half-aperture (rectangular or elliptical) in m + :param aperture_y: vertical half-aperture (rectangular or elliptical) in m :param repeat_x: horizontal period for repeated aperture masking (inactive by default) (meter) :param repeat_y: vertical period for repeated aperture masking (inactive by default) (meter) :param shape: aperture boundary shape: ``"rectangular"`` (default) or ``"elliptical"`` @@ -994,7 +1025,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, name=None) +.. py:class:: impactx.elements.SoftQuadrupole(ds, gscale, cos_coefficients, sin_coefficients, dx=0, dy=0, rotation=0, aperture_x=0, aperture_y=0, mapsteps=1, nslice=1, name=None) A soft-edge quadrupole. @@ -1007,6 +1038,8 @@ 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 aperture_x: horizontal half-aperture (elliptical) in m + :param aperture_y: vertical half-aperture (elliptical) in m :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 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 93b429087..4e6137b3a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1110,6 +1110,21 @@ add_impactx_test(linac-segment.py OFF # no plot script yet ) +# Drift with transverse aperture ################################################ +# +# w/o space charge +add_impactx_test(aperture-thick + examples/aperture/input_aperture_thick.in + ON # ImpactX MPI-parallel + examples/aperture/analysis_aperture_thick.py + OFF # no plot script yet +) +add_impactx_test(aperture-thick.py + examples/aperture/run_aperture_thick.py + OFF # ImpactX MPI-parallel + examples/aperture/analysis_aperture_thick.py + OFF # no plot script yet +) # Iteration of a linear one-turn map ######################################### # diff --git a/examples/aperture/README.rst b/examples/aperture/README.rst index edb4247c5..d57da2b1c 100644 --- a/examples/aperture/README.rst +++ b/examples/aperture/README.rst @@ -154,3 +154,56 @@ We run the following script to analyze correctness: .. literalinclude:: analysis_absorber.py :language: python3 :caption: You can copy this file from ``examples/aperture/analysis_absorber.py``. + + +.. _examples-aperture-thick: + +Aperture Collimation for a Thick Element +========================================= + +Proton beam in a drift, undergoing collimation by a rectangular boundary aperture. + +We use a 250 MeV proton beam with a horizontal rms beam size of 1.56 mm and a vertical rms beam size of 2.21 mm. + +The beam is scraped by a 1 mm x 1.5 mm rectangular aperture. For this test, the parameter nslice = 1, so application at the aperture boundary is equivalent to using a thin aperture element located at the exit of the drift. + +In this test, the initial values of :math:`\sigma_x`, :math:`\sigma_y`, :math:`\sigma_t`, :math:`\epsilon_x`, :math:`\epsilon_y`, and :math:`\epsilon_t` must> +The test fails if: + +* any of the final coordinates for the valid (not lost) particles lie outside the aperture boundary or +* any of the lost particles are inside the aperture boundary or +* if the sum of lost and kept particles is not equal to the initial particles or +* if the recorded position :math:`s` for the lost particles does not coincide with the drift distance. + + +Run +--- + +This example can be run as a Python script (``python3 run_aperture_thick.py``) or with an app with an input file (``impactx input_aperture_thick.in``). +Each can also be prefixed with an `MPI executor <https://www.mpi-forum.org>`__, such as ``mpiexec -n 4 ...`` or ``srun -n 4 ...``, depending on the system. + +.. tab-set:: + + .. tab-item:: Python Script + + .. literalinclude:: run_aperture_thick.py + :language: python3 + :caption: You can copy this file from ``examples/aperture/run_aperture_thick.py``. + + .. tab-item:: App Input File + + .. literalinclude:: input_aperture_thick.in + :language: ini + :caption: You can copy this file from ``examples/aperture/input_aperture_thick.in``. + + +Analyze +------- + +We run the following script to analyze correctness: + +.. dropdown:: Script ``analysis_aperture_thick.py`` + + .. literalinclude:: analysis_aperture_thick.py + :language: python3 + :caption: You can copy this file from ``examples/aperture/analysis_aperture_thick.py``. diff --git a/examples/aperture/analysis_aperture_thick.py b/examples/aperture/analysis_aperture_thick.py new file mode 100755 index 000000000..702cb6bc9 --- /dev/null +++ b/examples/aperture/analysis_aperture_thick.py @@ -0,0 +1,110 @@ +#!/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() + +series_lost = io.Series("diags/openPMD/particles_lost.h5", io.Access.read_only) +particles_lost = series_lost.iterations[0].particles["beam"].to_df() + +# compare number of particles +num_particles = 10000 +assert num_particles == len(initial) +# we lost particles in apertures +assert num_particles > len(final) +assert num_particles == len(particles_lost) + len(final) + +print("Initial Beam:") +sigx, sigy, sigt, emittance_x, emittance_y, emittance_t = get_moments(initial) +print(f" sigx={sigx:e} sigy={sigy:e} sigt={sigt:e}") +print( + f" emittance_x={emittance_x:e} emittance_y={emittance_y:e} emittance_t={emittance_t:e}" +) + +atol = 0.0 # ignored +rtol = 1.8 * 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.559531175539e-3, + 2.205510139392e-3, + 1.0e-3, + 1.0e-6, + 2.0e-6, + 1.0e-6, + ], + rtol=rtol, + atol=atol, +) + +# particle-wise comparison against the rectangular aperture boundary +xmax = 1.0e-3 +ymax = 1.5e-3 + +# kept particles +dx = abs(final["position_x"]) - xmax +dy = abs(final["position_y"]) - ymax + +print() +print(f" x_max={final['position_x'].max()}") +print(f" x_min={final['position_x'].min()}") +assert np.less_equal(dx.max(), 0.0) + +print(f" y_max={final['position_y'].max()}") +print(f" y_min={final['position_y'].min()}") +assert np.less_equal(dy.max(), 0.0) + +# lost particles +dx = abs(particles_lost["position_x"]) - xmax +dy = abs(particles_lost["position_y"]) - ymax + +print() +print(f" x_max={particles_lost['position_x'].max()}") +print(f" x_min={particles_lost['position_x'].min()}") +assert np.greater_equal(dx.max(), 0.0) + +print(f" y_max={particles_lost['position_y'].max()}") +print(f" y_min={particles_lost['position_y'].min()}") +assert np.greater_equal(dy.max(), 0.0) + +# check that s is set correctly +lost_at_s = particles_lost["s_lost"] +drift_s = np.ones_like(lost_at_s) * 0.123 +assert np.allclose(lost_at_s, drift_s) diff --git a/examples/aperture/input_absorber.in b/examples/aperture/input_absorber.in index 129229983..a564fd6ba 100644 --- a/examples/aperture/input_absorber.in +++ b/examples/aperture/input_absorber.in @@ -32,8 +32,8 @@ drift.ds = 0.123 collimator.type = aperture collimator.shape = rectangular -collimator.xmax = 1.0e-3 -collimator.ymax = 1.5e-3 +collimator.aperture_x = 1.0e-3 +collimator.aperture_y = 1.5e-3 collimator.action = absorb ############################################################################### diff --git a/examples/aperture/input_aperture.in b/examples/aperture/input_aperture.in index 4a1c32a4d..41218fc79 100644 --- a/examples/aperture/input_aperture.in +++ b/examples/aperture/input_aperture.in @@ -32,8 +32,8 @@ drift.ds = 0.123 collimator.type = aperture collimator.shape = rectangular -collimator.xmax = 1.0e-3 -collimator.ymax = 1.5e-3 +collimator.aperture_x = 1.0e-3 +collimator.aperture_y = 1.5e-3 ############################################################################### diff --git a/examples/aperture/input_aperture_periodic.in b/examples/aperture/input_aperture_periodic.in index eaec8f1ed..c1c707924 100644 --- a/examples/aperture/input_aperture_periodic.in +++ b/examples/aperture/input_aperture_periodic.in @@ -32,8 +32,8 @@ drift.ds = 0.123 pepperpot.type = aperture pepperpot.shape = rectangular -pepperpot.xmax = 1.5e-4 -pepperpot.ymax = 1.0e-4 +pepperpot.aperture_x = 1.5e-4 +pepperpot.aperture_y = 1.0e-4 pepperpot.repeat_x = 1.0e-3 pepperpot.repeat_y = 1.0e-3 diff --git a/examples/aperture/input_aperture_thick.in b/examples/aperture/input_aperture_thick.in new file mode 100644 index 000000000..0921240b0 --- /dev/null +++ b/examples/aperture/input_aperture_thick.in @@ -0,0 +1,47 @@ +############################################################################### +# Particle Beam(s) +############################################################################### +beam.npart = 10000 +beam.units = static +beam.kin_energy = 250.0 +beam.charge = 1.0e-9 +beam.particle = proton +beam.distribution = waterbag +beam.lambdaX = 1.559531175539e-3 +beam.lambdaY = 2.205510139392e-3 +beam.lambdaT = 1.0e-3 +beam.lambdaPx = 6.41218345413e-4 +beam.lambdaPy = 9.06819680526e-4 +beam.lambdaPt = 1.0e-3 +beam.muxpx = 0.0 +beam.muypy = 0.0 +beam.mutpt = 0.0 + + +############################################################################### +# Beamline: lattice elements and segments +############################################################################### +lattice.elements = monitor drift monitor +lattice.nslice = 1 + +monitor.type = beam_monitor +monitor.backend = h5 + +drift.type = drift +drift.ds = 0.123 +drift.aperture_x = 1.0e-3 +drift.aperture_y = 1.5e-3 + + +############################################################################### +# Algorithms +############################################################################### +algo.particle_shape = 2 +algo.space_charge = false + + +############################################################################### +# Diagnostics +############################################################################### +diag.slice_step_diagnostics = true +diag.backend = h5 diff --git a/examples/aperture/run_absorber.py b/examples/aperture/run_absorber.py index e93c46026..12f7a2c6c 100755 --- a/examples/aperture/run_absorber.py +++ b/examples/aperture/run_absorber.py @@ -52,8 +52,8 @@ elements.Drift(name="drift", ds=0.123), elements.Aperture( name="collimator", - xmax=1.0e-3, - ymax=1.5e-3, + aperture_x=1.0e-3, + aperture_y=1.5e-3, shape="rectangular", action="absorb", ), diff --git a/examples/aperture/run_aperture.py b/examples/aperture/run_aperture.py index 1d186db5e..b80922639 100755 --- a/examples/aperture/run_aperture.py +++ b/examples/aperture/run_aperture.py @@ -51,7 +51,7 @@ monitor, elements.Drift(name="drift", ds=0.123), elements.Aperture( - name="collimator", xmax=1.0e-3, ymax=1.5e-3, shape="rectangular" + name="collimator", aperture_x=1.0e-3, aperture_y=1.5e-3, shape="rectangular" ), monitor, ] diff --git a/examples/aperture/run_aperture_periodic.py b/examples/aperture/run_aperture_periodic.py index c0568718d..6e7cc03bf 100755 --- a/examples/aperture/run_aperture_periodic.py +++ b/examples/aperture/run_aperture_periodic.py @@ -52,8 +52,8 @@ elements.Drift(name="drift", ds=0.123), elements.Aperture( name="pepperpot", - xmax=1.5e-4, - ymax=1.0e-4, + aperture_x=1.5e-4, + aperture_y=1.0e-4, repeat_x=1.0e-3, repeat_y=1.0e-3, shape="rectangular", diff --git a/examples/aperture/run_aperture_thick.py b/examples/aperture/run_aperture_thick.py new file mode 100755 index 000000000..6184c7946 --- /dev/null +++ b/examples/aperture/run_aperture_thick.py @@ -0,0 +1,61 @@ +#!/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 +sim.particle_lost_diagnostics_backend = "h5" + +# domain decomposition & space charge mesh +sim.init_grids() + +# load a 250 MeV proton beam with an initial +# horizontal rms emittance of 1 um and an +# initial vertical rms emittance of 2 um +kin_energy_MeV = 250.0 # 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.Waterbag( + lambdaX=1.559531175539e-3, + lambdaY=2.205510139392e-3, + lambdaT=1.0e-3, + lambdaPx=6.41218345413e-4, + lambdaPy=9.06819680526e-4, + lambdaPt=1.0e-3, +) +sim.add_particles(bunch_charge_C, distr, npart) + +# add beam diagnostics +monitor = elements.BeamMonitor("monitor", backend="h5") + +# design the accelerator lattice +sim.lattice.extend( + [ + monitor, + elements.Drift(name="drift", ds=0.123, aperture_x=1.0e-3, aperture_y=1.5e-3), + monitor, + ] +) + +# run simulation +sim.track_particles() + +# clean shutdown +sim.finalize() diff --git a/src/initialization/InitElement.cpp b/src/initialization/InitElement.cpp index da4c24c35..0d19969f3 100644 --- a/src/initialization/InitElement.cpp +++ b/src/initialization/InitElement.cpp @@ -96,6 +96,28 @@ namespace detail return values; } + + /** Read the Aperture parameters aperture_x and aperture_y from inputs + * + * @param pp_element the element being read + * @return key-value pairs for aperture_x and aperture_y + */ + std::map<std::string, amrex::ParticleReal> + query_aperture (amrex::ParmParse& pp_element) + { + amrex::ParticleReal aperture_x = 0; + amrex::ParticleReal aperture_y = 0; + pp_element.query("aperture_x", aperture_x); + pp_element.query("aperture_y", aperture_y); + + std::map<std::string, amrex::ParticleReal> values = { + {"aperture_x", aperture_x}, + {"aperture_y", aperture_y} + }; + + return values; + } + } // namespace detail /** Read a lattice element @@ -122,36 +144,40 @@ namespace detail { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal k; pp_element.getWithParser("k", k); - m_lattice.emplace_back( Quad(ds, k, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( Quad(ds, k, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], 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); + auto b = detail::query_aperture(pp_element); - m_lattice.emplace_back( Drift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( Drift(ds, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "sbend") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal rc; pp_element.getWithParser("rc", rc); - m_lattice.emplace_back( Sbend(ds, rc, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( Sbend(ds, rc, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "cfbend") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal rc, k; pp_element.getWithParser("rc", rc); pp_element.getWithParser("k", k); - m_lattice.emplace_back( CFbend(ds, rc, k, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( CFbend(ds, rc, k, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "dipedge") { auto a = detail::query_alignment(pp_element); @@ -167,13 +193,14 @@ namespace detail { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::Real kx, ky, kt; pp_element.getWithParser("kx", kx); pp_element.getWithParser("ky", ky); pp_element.getWithParser("kt", kt); - m_lattice.emplace_back( ConstF(ds, kx, ky, kt, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ConstF(ds, kx, ky, kt, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "buncher") { auto a = detail::query_alignment(pp_element); @@ -218,6 +245,7 @@ namespace detail { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal escale, freq, phase; int mapsteps = mapsteps_default; @@ -231,16 +259,17 @@ 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, element_name) ); + m_lattice.emplace_back( RFCavity(ds, escale, freq, phase, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], mapsteps, nslice, element_name) ); } else if (element_type == "solenoid") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal ks; pp_element.getWithParser("ks", ks); - m_lattice.emplace_back( Sol(ds, ks, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( Sol(ds, ks, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "prot") { amrex::ParticleReal phi_in, phi_out; @@ -260,6 +289,7 @@ namespace detail { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal bscale; int mapsteps = mapsteps_default; @@ -273,11 +303,12 @@ 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, element_name) ); + m_lattice.emplace_back( SoftSolenoid(ds, bscale, cos_coef, sin_coef, units, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], mapsteps, nslice, element_name) ); } else if (element_type == "quadrupole_softedge") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal gscale; int mapsteps = mapsteps_default; @@ -289,16 +320,18 @@ 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, element_name) ); + m_lattice.emplace_back( SoftQuadrupole(ds, gscale, cos_coef, sin_coef, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], 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); + auto b = detail::query_aperture(pp_element); - m_lattice.emplace_back( ChrDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ChrDrift(ds, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "quad_chromatic") { auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); amrex::ParticleReal k; @@ -306,10 +339,11 @@ namespace detail pp_element.getWithParser("k", k); pp_element.queryAddWithParser("units", units); - m_lattice.emplace_back( ChrQuad(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ChrQuad(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "plasma_lens_chromatic") { auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); amrex::ParticleReal k; @@ -317,7 +351,7 @@ namespace detail pp_element.getWithParser("k", k); pp_element.queryAddWithParser("units", units); - m_lattice.emplace_back( ChrPlasmaLens(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ChrPlasmaLens(ds, k, units, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "tapered_plasma_lens") { auto a = detail::query_alignment(pp_element); @@ -334,29 +368,32 @@ namespace detail { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); - m_lattice.emplace_back( ExactDrift(ds, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ExactDrift(ds, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "sbend_exact") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal phi; amrex::ParticleReal B = 0.0; pp_element.getWithParser("phi", phi); pp_element.queryAddWithParser("B", B); - m_lattice.emplace_back( ExactSbend(ds, phi, B, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ExactSbend(ds, phi, B, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "uniform_acc_chromatic") { auto const [ds, nslice] = detail::query_ds(pp_element, nslice_default); auto a = detail::query_alignment(pp_element); + auto b = detail::query_aperture(pp_element); amrex::ParticleReal ez, bz; pp_element.getWithParser("ez", ez); pp_element.getWithParser("bz", bz); - m_lattice.emplace_back( ChrAcc(ds, ez, bz, a["dx"], a["dy"], a["rotation_degree"], nslice, element_name) ); + m_lattice.emplace_back( ChrAcc(ds, ez, bz, a["dx"], a["dy"], a["rotation_degree"], b["aperture_x"], b["aperture_y"], nslice, element_name) ); } else if (element_type == "thin_dipole") { auto a = detail::query_alignment(pp_element); @@ -386,13 +423,40 @@ namespace detail { auto a = detail::query_alignment(pp_element); - amrex::Real xmax, ymax; + amrex::Real aperture_x, aperture_y; amrex::ParticleReal repeat_x = 0.0; amrex::ParticleReal repeat_y = 0.0; std::string shape_str = "rectangular"; std::string action_str = "transmit"; - pp_element.getWithParser("xmax", xmax); - pp_element.getWithParser("ymax", ymax); + + // In the future, just use this: + // pp_element.getWithParser("aperture_x", aperture_x); + // pp_element.getWithParser("aperture_y", aperture_y); + // Backwards compatibility to ImpactX <= 25.01 + bool const has_old_xmax = pp_element.queryAddWithParser("xmax", aperture_x); + bool const has_old_ymax = pp_element.queryAddWithParser("ymax", aperture_y); + if (has_old_xmax) { + pp_element.queryAddWithParser("aperture_x", aperture_x); + ablastr::warn_manager::WMRecordWarning( + "ImpactX::read_element", + element_name + ".xmax is deprecated. Use " + element_name + ".aperture_x instead.", + ablastr::warn_manager::WarnPriority::high + ); + } else { + pp_element.getWithParser("aperture_x", aperture_x); + } + if (has_old_ymax) { + pp_element.queryAddWithParser("aperture_y", aperture_y); + ablastr::warn_manager::WMRecordWarning( + "ImpactX::read_element", + element_name + ".ymax is deprecated. Use " + element_name + ".aperture_y instead.", + ablastr::warn_manager::WarnPriority::high + ); + } else { + pp_element.getWithParser("aperture_y", aperture_y); + } + + pp_element.queryAddWithParser("repeat_y", repeat_y); pp_element.queryAddWithParser("repeat_x", repeat_x); pp_element.queryAddWithParser("repeat_y", repeat_y); pp_element.queryAdd("shape", shape_str); @@ -408,7 +472,7 @@ namespace detail Aperture::Action::transmit : Aperture::Action::absorb; - m_lattice.emplace_back( Aperture(xmax, ymax, repeat_x, repeat_y, shape, action, a["dx"], a["dy"], a["rotation_degree"], element_name) ); + m_lattice.emplace_back( Aperture(aperture_x, aperture_y, repeat_x, repeat_y, shape, action, a["dx"], a["dy"], a["rotation_degree"], element_name) ); } else if (element_type == "beam_monitor") { std::string openpmd_name = element_name; @@ -465,7 +529,7 @@ namespace detail amrex::ParticleReal ds = 0.0; pp_element.queryAdd("ds", ds); - elements::LinearTransport::Map6x6 transport_map = elements::LinearTransport::Map6x6::Identity(); + elements::mixin::LinearTransport::Map6x6 transport_map = elements::mixin::LinearTransport::Map6x6::Identity(); // safe to ParmParse inputs for reproducibility for (int i=1; i<=6; ++i) { diff --git a/src/particles/elements/Aperture.H b/src/particles/elements/Aperture.H index bf5621793..d9bea0602 100644 --- a/src/particles/elements/Aperture.H +++ b/src/particles/elements/Aperture.H @@ -26,11 +26,11 @@ namespace impactx { struct Aperture - : public elements::Named, - public elements::BeamOptic<Aperture>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Aperture>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "Aperture"; using PType = ImpactXParticleContainer::ParticleType; @@ -58,8 +58,8 @@ namespace impactx * * @param shape aperture shape * @param action specify action of domain (transmit/absorb) - * @param xmax maximum value of horizontal coordinate (m) - * @param ymax maximum value of vertical coordinate (m) + * @param aperture_x horizontal half-aperture (m) + * @param aperture_y vertical half-aperture (m) * @param repeat_x horizontal period for repeated masking, optional (m) * @param repeat_y vertical period for repeated masking, optional (m) * @param dx horizontal translation error in m @@ -68,8 +68,8 @@ namespace impactx * @param name a user defined and not necessarily unique name of the element */ Aperture ( - amrex::ParticleReal xmax, - amrex::ParticleReal ymax, + amrex::ParticleReal aperture_x, + amrex::ParticleReal aperture_y, amrex::ParticleReal repeat_x, amrex::ParticleReal repeat_y, Shape shape, @@ -81,7 +81,7 @@ namespace impactx ) : Named(std::move(name)), Alignment(dx, dy, rotation_degree), - m_shape(shape), m_action(action), m_xmax(xmax), m_ymax(ymax), m_repeat_x(repeat_x), m_repeat_y(repeat_y) + m_shape(shape), m_action(action), m_aperture_x(aperture_x), m_aperture_y(aperture_y), m_repeat_x(repeat_x), m_repeat_y(repeat_y) { } @@ -128,8 +128,8 @@ namespace impactx v = (m_repeat_y==0.0) ? y : (std::fmod(std::abs(y)+dy,m_repeat_y)-dy); // scale horizontal and vertical coordinates - u = u / m_xmax; - v = v / m_ymax; + u = u / m_aperture_x; + v = v / m_aperture_y; // compare against the aperture boundary switch (m_action) @@ -181,8 +181,8 @@ namespace impactx Shape m_shape; //! aperture type (rectangular, elliptical) Action m_action; //! action type (transmit, absorb) - amrex::ParticleReal m_xmax; //! maximum horizontal coordinate - amrex::ParticleReal m_ymax; //! maximum vertical coordinate + amrex::ParticleReal m_aperture_x; //! maximum horizontal coordinate + amrex::ParticleReal m_aperture_y; //! maximum vertical coordinate amrex::ParticleReal m_repeat_x; //! horizontal period for repeated masking amrex::ParticleReal m_repeat_y; //! vertical period for repeated masking diff --git a/src/particles/elements/Buncher.H b/src/particles/elements/Buncher.H index 0ca7efb1e..3079875ee 100644 --- a/src/particles/elements/Buncher.H +++ b/src/particles/elements/Buncher.H @@ -26,11 +26,11 @@ namespace impactx { struct Buncher - : public elements::Named, - public elements::BeamOptic<Buncher>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Buncher>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "Buncher"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/CFbend.H b/src/particles/elements/CFbend.H index 409f70146..6fb71e18b 100644 --- a/src/particles/elements/CFbend.H +++ b/src/particles/elements/CFbend.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -27,11 +28,12 @@ namespace impactx { struct CFbend - : public elements::Named, - public elements::BeamOptic<CFbend>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<CFbend>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "CFbend"; using PType = ImpactXParticleContainer::ParticleType; @@ -48,6 +50,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -58,12 +62,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_rc(rc), m_k(k) { } @@ -79,7 +86,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -90,7 +97,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -177,6 +184,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ChrDrift.H b/src/particles/elements/ChrDrift.H index 6b59d8b06..b5a063241 100644 --- a/src/particles/elements/ChrDrift.H +++ b/src/particles/elements/ChrDrift.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct ChrDrift - : public elements::Named, - public elements::BeamOptic<ChrDrift>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ChrDrift>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ChrDrift"; using PType = ImpactXParticleContainer::ParticleType; @@ -44,6 +46,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -52,12 +56,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), - Alignment(dx, dy, rotation_degree) + Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y) { } @@ -72,7 +79,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -83,7 +90,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -133,6 +140,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ChrPlasmaLens.H b/src/particles/elements/ChrPlasmaLens.H index bfe0fe78c..e588ccf8c 100644 --- a/src/particles/elements/ChrPlasmaLens.H +++ b/src/particles/elements/ChrPlasmaLens.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct ChrPlasmaLens - : public elements::Named, - public elements::BeamOptic<ChrPlasmaLens>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ChrPlasmaLens>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ChrPlasmaLens"; using PType = ImpactXParticleContainer::ParticleType; @@ -50,6 +52,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -60,12 +64,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_k(k), m_unit(unit) { } @@ -81,7 +88,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -92,7 +99,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -171,6 +178,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ChrQuad.H b/src/particles/elements/ChrQuad.H index 5e246341b..a24a07579 100644 --- a/src/particles/elements/ChrQuad.H +++ b/src/particles/elements/ChrQuad.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -27,11 +28,12 @@ namespace impactx { struct ChrQuad - : public elements::Named, - public elements::BeamOptic<ChrQuad>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ChrQuad>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ChrQuad"; using PType = ImpactXParticleContainer::ParticleType; @@ -53,6 +55,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -63,12 +67,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_k(k), m_unit(unit) { } @@ -84,7 +91,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -95,7 +102,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -210,6 +217,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ChrUniformAcc.H b/src/particles/elements/ChrUniformAcc.H index 45d7fd056..2503c56a7 100644 --- a/src/particles/elements/ChrUniformAcc.H +++ b/src/particles/elements/ChrUniformAcc.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct ChrAcc - : public elements::Named, - public elements::BeamOptic<ChrAcc>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ChrAcc>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ChrAcc"; using PType = ImpactXParticleContainer::ParticleType; @@ -48,6 +50,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -58,12 +62,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_ez(ez), m_bz(bz) { } @@ -79,7 +86,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -90,7 +97,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -173,6 +180,9 @@ namespace impactx py = py/bgf; pt = pt/bgf; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ConstF.H b/src/particles/elements/ConstF.H index 10ab1077c..3f7dc557f 100644 --- a/src/particles/elements/ConstF.H +++ b/src/particles/elements/ConstF.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct ConstF - : public elements::Named, - public elements::BeamOptic<ConstF>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ConstF>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ConstF"; using PType = ImpactXParticleContainer::ParticleType; @@ -44,6 +46,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -55,12 +59,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_kx(kx), m_ky(ky), m_kt(kt) { } @@ -76,7 +83,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -87,7 +94,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart) const { using namespace amrex::literals; // for _rt and _prt @@ -128,6 +135,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/DipEdge.H b/src/particles/elements/DipEdge.H index 12b528e5f..825bd0112 100644 --- a/src/particles/elements/DipEdge.H +++ b/src/particles/elements/DipEdge.H @@ -26,11 +26,11 @@ namespace impactx { struct DipEdge - : public elements::Named, - public elements::BeamOptic<DipEdge>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<DipEdge>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "DipEdge"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/Drift.H b/src/particles/elements/Drift.H index cdc4b8e1f..001bd2583 100644 --- a/src/particles/elements/Drift.H +++ b/src/particles/elements/Drift.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -29,11 +30,12 @@ namespace impactx { struct Drift - : public elements::Named, - public elements::BeamOptic<Drift>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Drift>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "Drift"; using PType = ImpactXParticleContainer::ParticleType; @@ -44,6 +46,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -52,12 +56,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), - Alignment(dx, dy, rotation_degree) + Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y) { } @@ -72,7 +79,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -83,7 +90,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -123,6 +130,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } @@ -168,7 +178,7 @@ namespace impactx * @returns 6x6 transport matrix */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - elements::LinearTransport::Map6x6 + elements::mixin::LinearTransport::Map6x6 transport_map (RefPart & AMREX_RESTRICT refpart) const { using namespace amrex::literals; // for _rt and _prt @@ -181,7 +191,7 @@ namespace impactx amrex::ParticleReal const betgam2 = std::pow(pt_ref, 2) - 1.0_prt; // assign linear map matrix elements - elements::LinearTransport::Map6x6 R = elements::LinearTransport::Map6x6::Identity(); + elements::mixin::LinearTransport::Map6x6 R = elements::mixin::LinearTransport::Map6x6::Identity(); R(1,2) = slice_ds; R(3,4) = slice_ds; R(5,6) = slice_ds / betgam2; diff --git a/src/particles/elements/Empty.H b/src/particles/elements/Empty.H index 9ce3eece8..30b29d3ac 100644 --- a/src/particles/elements/Empty.H +++ b/src/particles/elements/Empty.H @@ -22,8 +22,8 @@ namespace impactx { struct Empty - : public elements::Thin, - public elements::NoFinalize + : public elements::mixin::Thin, + public elements::mixin::NoFinalize { static constexpr auto type = "None"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/ExactDrift.H b/src/particles/elements/ExactDrift.H index 6f3a4060f..b182e1705 100644 --- a/src/particles/elements/ExactDrift.H +++ b/src/particles/elements/ExactDrift.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct ExactDrift - : public elements::Named, - public elements::BeamOptic<ExactDrift>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ExactDrift>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ExactDrift"; using PType = ImpactXParticleContainer::ParticleType; @@ -41,6 +43,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -49,12 +53,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), - Alignment(dx, dy, rotation_degree) + Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y) { } @@ -70,7 +77,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -81,7 +88,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -124,6 +131,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ExactSbend.H b/src/particles/elements/ExactSbend.H index 083ae0c58..058c41647 100644 --- a/src/particles/elements/ExactSbend.H +++ b/src/particles/elements/ExactSbend.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -28,11 +29,12 @@ namespace impactx { struct ExactSbend - : public elements::Named, - public elements::BeamOptic<ExactSbend>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ExactSbend>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "ExactSbend"; static constexpr amrex::ParticleReal degree2rad = ablastr::constant::math::pi / 180.0; @@ -56,6 +58,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -66,12 +70,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_phi(phi * degree2rad), m_B(B) { } @@ -96,7 +103,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -107,7 +114,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -160,6 +167,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/Kicker.H b/src/particles/elements/Kicker.H index 7bd3c17c0..cd3556ed1 100644 --- a/src/particles/elements/Kicker.H +++ b/src/particles/elements/Kicker.H @@ -26,11 +26,11 @@ namespace impactx { struct Kicker - : public elements::Named, - public elements::BeamOptic<Kicker>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Kicker>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "Kicker"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/LinearMap.H b/src/particles/elements/LinearMap.H index 472ae12be..4d66c9131 100644 --- a/src/particles/elements/LinearMap.H +++ b/src/particles/elements/LinearMap.H @@ -26,11 +26,11 @@ namespace impactx { struct LinearMap - : public elements::Named, - public elements::BeamOptic<LinearMap>, - public elements::Alignment, - public elements::LinearTransport, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<LinearMap>, + public elements::mixin::Alignment, + public elements::mixin::LinearTransport, + public elements::mixin::NoFinalize { static constexpr auto type = "LinearMap"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/Marker.H b/src/particles/elements/Marker.H index 3291aff79..5fd90ab88 100644 --- a/src/particles/elements/Marker.H +++ b/src/particles/elements/Marker.H @@ -22,9 +22,9 @@ namespace impactx { struct Marker - : public elements::Named, - public elements::Thin, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::Thin, + public elements::mixin::NoFinalize { static constexpr auto type = "Marker"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/Multipole.H b/src/particles/elements/Multipole.H index f2db02d58..121c2665b 100644 --- a/src/particles/elements/Multipole.H +++ b/src/particles/elements/Multipole.H @@ -26,11 +26,11 @@ namespace impactx { struct Multipole - : public elements::Named, - public elements::BeamOptic<Multipole>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Multipole>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "Multipole"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/NonlinearLens.H b/src/particles/elements/NonlinearLens.H index 8413d000e..d268e0366 100644 --- a/src/particles/elements/NonlinearLens.H +++ b/src/particles/elements/NonlinearLens.H @@ -26,11 +26,11 @@ namespace impactx { struct NonlinearLens - : public elements::Named, - public elements::BeamOptic<NonlinearLens>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<NonlinearLens>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "NonlinearLens"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/PRot.H b/src/particles/elements/PRot.H index 242a82534..5683c1e6f 100644 --- a/src/particles/elements/PRot.H +++ b/src/particles/elements/PRot.H @@ -28,10 +28,10 @@ namespace impactx { struct PRot - : public elements::Named, - public elements::BeamOptic<PRot>, - public elements::Thin, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<PRot>, + public elements::mixin::Thin, + public elements::mixin::NoFinalize { static constexpr auto type = "PRot"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/PlaneXYRot.H b/src/particles/elements/PlaneXYRot.H index eeb56bfca..250deb4d6 100644 --- a/src/particles/elements/PlaneXYRot.H +++ b/src/particles/elements/PlaneXYRot.H @@ -29,11 +29,11 @@ namespace impactx { struct PlaneXYRot - : public elements::Named, - public elements::BeamOptic<PlaneXYRot>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<PlaneXYRot>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "PlaneXYRot"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/Programmable.H b/src/particles/elements/Programmable.H index 2227fd64b..582612424 100644 --- a/src/particles/elements/Programmable.H +++ b/src/particles/elements/Programmable.H @@ -23,7 +23,7 @@ namespace impactx { struct Programmable - : public elements::Named + : public elements::mixin::Named { static constexpr auto type = "Programmable"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/Quad.H b/src/particles/elements/Quad.H index be0464ea8..e41af5293 100644 --- a/src/particles/elements/Quad.H +++ b/src/particles/elements/Quad.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -26,11 +27,12 @@ namespace impactx { struct Quad - : public elements::Named, - public elements::BeamOptic<Quad>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Quad>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "Quad"; using PType = ImpactXParticleContainer::ParticleType; @@ -45,6 +47,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -54,12 +58,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_k(k) { } @@ -75,7 +82,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -86,7 +93,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -152,6 +159,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/RFCavity.H b/src/particles/elements/RFCavity.H index c9fc02d57..98685a0b5 100644 --- a/src/particles/elements/RFCavity.H +++ b/src/particles/elements/RFCavity.H @@ -13,6 +13,7 @@ #include "particles/ImpactXParticleContainer.H" #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/named.H" #include "mixin/thick.H" @@ -103,10 +104,11 @@ namespace RFCavityData } // namespace RFCavityData struct RFCavity - : public elements::Named, - public elements::BeamOptic<RFCavity>, - public elements::Thick, - public elements::Alignment + : public elements::mixin::Named, + public elements::mixin::BeamOptic<RFCavity>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture { static constexpr auto type = "RFCavity"; using PType = ImpactXParticleContainer::ParticleType; @@ -122,6 +124,8 @@ namespace RFCavityData * @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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture in m * @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 @@ -137,6 +141,8 @@ namespace RFCavityData amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int mapsteps = 1, int nslice = 1, std::optional<std::string> name = std::nullopt @@ -144,6 +150,7 @@ namespace RFCavityData : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_escale(escale), m_freq(freq), m_phase(phase), m_mapsteps(mapsteps), m_id(RFCavityData::next_id) { // next created RF cavity has another id for its data @@ -188,7 +195,7 @@ namespace RFCavityData * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -199,7 +206,7 @@ namespace RFCavityData amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, [[maybe_unused]] RefPart const & refpart ) const { @@ -229,6 +236,9 @@ namespace RFCavityData t = out[5]; pt = out[6]; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/Sbend.H b/src/particles/elements/Sbend.H index c6f67d9ad..f896d1fa4 100644 --- a/src/particles/elements/Sbend.H +++ b/src/particles/elements/Sbend.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -27,11 +28,12 @@ namespace impactx { struct Sbend - : public elements::Named, - public elements::BeamOptic<Sbend>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Sbend>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "Sbend"; using PType = ImpactXParticleContainer::ParticleType; @@ -43,6 +45,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -52,12 +56,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_rc(rc) { } @@ -73,7 +80,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -84,7 +91,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -138,6 +145,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/ShortRF.H b/src/particles/elements/ShortRF.H index 64546503a..024e007fb 100644 --- a/src/particles/elements/ShortRF.H +++ b/src/particles/elements/ShortRF.H @@ -26,11 +26,11 @@ namespace impactx { struct ShortRF - : public elements::Named, - public elements::BeamOptic<ShortRF>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ShortRF>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "ShortRF"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/SoftQuad.H b/src/particles/elements/SoftQuad.H index 187f55803..06beccab1 100644 --- a/src/particles/elements/SoftQuad.H +++ b/src/particles/elements/SoftQuad.H @@ -13,6 +13,7 @@ #include "particles/ImpactXParticleContainer.H" #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/named.H" #include "mixin/thick.H" @@ -112,10 +113,11 @@ namespace SoftQuadrupoleData } // namespace SoftQuadrupoleData struct SoftQuadrupole - : public elements::Named, - public elements::BeamOptic<SoftQuadrupole>, - public elements::Thick, - public elements::Alignment + : public elements::mixin::Named, + public elements::mixin::BeamOptic<SoftQuadrupole>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture { static constexpr auto type = "SoftQuadrupole"; using PType = ImpactXParticleContainer::ParticleType; @@ -129,6 +131,8 @@ namespace SoftQuadrupoleData * @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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture in m * @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 @@ -142,6 +146,8 @@ namespace SoftQuadrupoleData amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int mapsteps = 1, int nslice = 1, std::optional<std::string> name = std::nullopt @@ -149,6 +155,7 @@ namespace SoftQuadrupoleData : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_gscale(gscale), m_mapsteps(mapsteps), m_id(SoftQuadrupoleData::next_id) { // next created soft quad has another id for its data @@ -193,7 +200,7 @@ namespace SoftQuadrupoleData * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -204,7 +211,7 @@ namespace SoftQuadrupoleData amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, [[maybe_unused]] RefPart const & refpart ) const { @@ -234,6 +241,9 @@ namespace SoftQuadrupoleData t = out[5]; pt = out[6]; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/SoftSol.H b/src/particles/elements/SoftSol.H index 5152f604c..db0516304 100644 --- a/src/particles/elements/SoftSol.H +++ b/src/particles/elements/SoftSol.H @@ -13,6 +13,7 @@ #include "particles/ImpactXParticleContainer.H" #include "particles/integrators/Integrators.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/named.H" #include "mixin/thick.H" @@ -117,10 +118,11 @@ namespace SoftSolenoidData } // namespace SoftSolenoidData struct SoftSolenoid - : public elements::Named, - public elements::BeamOptic<SoftSolenoid>, - public elements::Thick, - public elements::Alignment + : public elements::mixin::Named, + public elements::mixin::BeamOptic<SoftSolenoid>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture { static constexpr auto type = "SoftSolenoid"; using PType = ImpactXParticleContainer::ParticleType; @@ -139,6 +141,8 @@ namespace SoftSolenoidData * @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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture in m * @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 @@ -153,6 +157,8 @@ namespace SoftSolenoidData amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int mapsteps = 1, int nslice = 1, std::optional<std::string> name = std::nullopt @@ -160,6 +166,7 @@ namespace SoftSolenoidData : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_bscale(bscale), m_unit(unit), m_mapsteps(mapsteps), m_id(SoftSolenoidData::next_id) { // next created soft solenoid has another id for its data @@ -204,7 +211,7 @@ namespace SoftSolenoidData * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -215,7 +222,7 @@ namespace SoftSolenoidData amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, [[maybe_unused]] RefPart const & refpart ) const { @@ -245,6 +252,9 @@ namespace SoftSolenoidData t = out[5]; pt = out[6]; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/Sol.H b/src/particles/elements/Sol.H index 8573648a0..265b651c3 100644 --- a/src/particles/elements/Sol.H +++ b/src/particles/elements/Sol.H @@ -12,6 +12,7 @@ #include "particles/ImpactXParticleContainer.H" #include "mixin/alignment.H" +#include "mixin/pipeaperture.H" #include "mixin/beamoptic.H" #include "mixin/thick.H" #include "mixin/named.H" @@ -27,11 +28,12 @@ namespace impactx { struct Sol - : public elements::Named, - public elements::BeamOptic<Sol>, - public elements::Thick, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<Sol>, + public elements::mixin::Thick, + public elements::mixin::Alignment, + public elements::mixin::PipeAperture, + public elements::mixin::NoFinalize { static constexpr auto type = "Sol"; using PType = ImpactXParticleContainer::ParticleType; @@ -44,6 +46,8 @@ 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 aperture_x horizontal half-aperture in m + * @param aperture_y vertical half-aperture 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 */ @@ -53,12 +57,15 @@ namespace impactx amrex::ParticleReal dx = 0, amrex::ParticleReal dy = 0, amrex::ParticleReal rotation_degree = 0, + amrex::ParticleReal aperture_x = 0, + amrex::ParticleReal aperture_y = 0, int nslice = 1, std::optional<std::string> name = std::nullopt ) : Named(std::move(name)), Thick(ds, nslice), Alignment(dx, dy, rotation_degree), + PipeAperture(aperture_x, aperture_y), m_ks(ks) { } @@ -74,7 +81,7 @@ namespace impactx * @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 idcpu particle global index * @param refpart reference particle */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -85,7 +92,7 @@ namespace impactx amrex::ParticleReal & AMREX_RESTRICT px, amrex::ParticleReal & AMREX_RESTRICT py, amrex::ParticleReal & AMREX_RESTRICT pt, - [[maybe_unused]] uint64_t & AMREX_RESTRICT idcpu, + uint64_t & AMREX_RESTRICT idcpu, RefPart const & refpart ) const { @@ -145,6 +152,9 @@ namespace impactx py = pyout; pt = ptout; + // apply transverse aperture + apply_aperture(x, y, idcpu); + // undo shift due to alignment errors of the element shift_out(x, y, px, py); } diff --git a/src/particles/elements/TaperedPL.H b/src/particles/elements/TaperedPL.H index 2f7dabad4..8039e2a97 100644 --- a/src/particles/elements/TaperedPL.H +++ b/src/particles/elements/TaperedPL.H @@ -26,11 +26,11 @@ namespace impactx { struct TaperedPL - : public elements::Named, - public elements::BeamOptic<TaperedPL>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<TaperedPL>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "TaperedPL"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/ThinDipole.H b/src/particles/elements/ThinDipole.H index 097cb85c4..14ed4f062 100644 --- a/src/particles/elements/ThinDipole.H +++ b/src/particles/elements/ThinDipole.H @@ -24,11 +24,11 @@ namespace impactx { struct ThinDipole - : public elements::Named, - public elements::BeamOptic<ThinDipole>, - public elements::Thin, - public elements::Alignment, - public elements::NoFinalize + : public elements::mixin::Named, + public elements::mixin::BeamOptic<ThinDipole>, + public elements::mixin::Thin, + public elements::mixin::Alignment, + public elements::mixin::NoFinalize { static constexpr auto type = "ThinDipole"; using PType = ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/diagnostics/openPMD.H b/src/particles/elements/diagnostics/openPMD.H index f5ee0d4a8..6d47ecee6 100644 --- a/src/particles/elements/diagnostics/openPMD.H +++ b/src/particles/elements/diagnostics/openPMD.H @@ -63,7 +63,7 @@ namespace detail * same series name as an existing instance. */ struct BeamMonitor - : public elements::Thin + : public elements::mixin::Thin { static constexpr auto type = "BeamMonitor"; using PType = typename ImpactXParticleContainer::ParticleType; diff --git a/src/particles/elements/mixin/alignment.H b/src/particles/elements/mixin/alignment.H index d108c4e1b..283e85147 100644 --- a/src/particles/elements/mixin/alignment.H +++ b/src/particles/elements/mixin/alignment.H @@ -19,7 +19,7 @@ #include <AMReX_REAL.H> -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements with horizontal/vertical alignment errors */ @@ -147,6 +147,6 @@ namespace impactx::elements amrex::ParticleReal m_rotation = 0; //! rotation error in the transverse plane [rad] }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_ALIGNMENT_H diff --git a/src/particles/elements/mixin/beamoptic.H b/src/particles/elements/mixin/beamoptic.H index 0616b5374..bd1d5f314 100644 --- a/src/particles/elements/mixin/beamoptic.H +++ b/src/particles/elements/mixin/beamoptic.H @@ -19,7 +19,7 @@ #include <type_traits> -namespace impactx::elements +namespace impactx::elements::mixin { namespace detail { @@ -190,6 +190,6 @@ namespace detail } }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_BEAMOPTIC_H diff --git a/src/particles/elements/mixin/lineartransport.H b/src/particles/elements/mixin/lineartransport.H index 9fce44ff6..add40df61 100644 --- a/src/particles/elements/mixin/lineartransport.H +++ b/src/particles/elements/mixin/lineartransport.H @@ -20,7 +20,7 @@ #include <AMReX_SmallMatrix.H> -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements that can be expressed as linear transport maps. */ @@ -47,6 +47,6 @@ namespace impactx::elements // Map6x6 m_transport_map; ///< linearized map }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_LINEAR_TRANSPORT_H diff --git a/src/particles/elements/mixin/named.H b/src/particles/elements/mixin/named.H index f06a6d86c..1ebaea4fc 100644 --- a/src/particles/elements/mixin/named.H +++ b/src/particles/elements/mixin/named.H @@ -21,7 +21,7 @@ #include <string> -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements with a user-defined name */ @@ -148,6 +148,6 @@ namespace impactx::elements char * m_name = nullptr; //! a user defined and not necessarily unique name of the element }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_NAMED_H diff --git a/src/particles/elements/mixin/nofinalize.H b/src/particles/elements/mixin/nofinalize.H index 3ab5a9607..7ea6e4da0 100644 --- a/src/particles/elements/mixin/nofinalize.H +++ b/src/particles/elements/mixin/nofinalize.H @@ -11,7 +11,7 @@ #define IMPACTX_ELEMENTS_MIXIN_NOFINALIZE_H -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements that need no finalize function. * @@ -29,6 +29,6 @@ namespace impactx::elements } }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_NOFINALIZE_H diff --git a/src/particles/elements/mixin/pipeaperture.H b/src/particles/elements/mixin/pipeaperture.H new file mode 100644 index 000000000..be336bc08 --- /dev/null +++ b/src/particles/elements/mixin/pipeaperture.H @@ -0,0 +1,103 @@ +/* Copyright 2022-2025 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, Chad Mitchell + * License: BSD-3-Clause-LBNL + */ +#ifndef IMPACTX_ELEMENTS_MIXIN_PIPE_APERTURE_H +#define IMPACTX_ELEMENTS_MIXIN_PIPE_APERTURE_H + +#include <AMReX_Extension.H> +#include <AMReX_GpuQualifiers.H> +#include <AMReX_Math.H> +#include <AMReX_Particle.H> +#include <AMReX_REAL.H> + + +namespace impactx::elements::mixin +{ + /** This is a helper class for applying a transverse aperture restriction to thick lattice elements + */ + struct PipeAperture + { + + /** A finite-length element with a constant elliptical aperture over s. + * + * @param aperture_x horizontal half-aperture size in m + * @param aperture_y vertical half-aperture size in m + */ + PipeAperture ( + amrex::ParticleReal aperture_x, + amrex::ParticleReal aperture_y + ) + : m_aperture_x(aperture_x), m_aperture_y(aperture_y) + { + } + + PipeAperture () = default; + PipeAperture (PipeAperture const &) = default; + PipeAperture& operator= (PipeAperture const &) = default; + PipeAperture (PipeAperture&&) = default; + PipeAperture& operator= (PipeAperture&& rhs) = default; + + ~PipeAperture () = default; + + /** Apply the transverse aperture + * + * @param[inout] x horizontal position relative to reference particle + * @param[inout] y vertical position relative to reference particle + * @param idcpu particle global index + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + void apply_aperture ( + amrex::ParticleReal & AMREX_RESTRICT x, + amrex::ParticleReal & AMREX_RESTRICT y, + uint64_t & AMREX_RESTRICT idcpu + ) const { + using namespace amrex::literals; // for _rt and _prt + + // skip aperture application if aperture_x <= 0 or aperture_y <= 0 + if (m_aperture_x > 0 && m_aperture_y > 0) { + + // scale horizontal and vertical coordinates + amrex::ParticleReal const u = x / m_aperture_x; + amrex::ParticleReal const v = y / m_aperture_y; + + // compare against the aperture boundary + if (std::pow(u,2) + std::pow(v,2) > 1_prt) { + amrex::ParticleIDWrapper{idcpu}.make_invalid(); + } + + } + } + + /** Horizontal aperture size + * + * @return horizontal aperture size in m + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + amrex::ParticleReal aperture_x () const + { + return m_aperture_x; + } + + /** Vertical aperture size + * + * @return vertical aperture size in m + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + amrex::ParticleReal aperture_y () const + { + return m_aperture_y; + } + + amrex::ParticleReal m_aperture_x = 0; //! horizontal aperture size [m] + amrex::ParticleReal m_aperture_y = 0; //! vertical aperture size [m] + }; + +} // namespace impactx::elements::mixin + +#endif // IMPACTX_ELEMENTS_MIXIN_PIPE_APERTURE_H diff --git a/src/particles/elements/mixin/thick.H b/src/particles/elements/mixin/thick.H index 4572d2a67..85a9917d5 100644 --- a/src/particles/elements/mixin/thick.H +++ b/src/particles/elements/mixin/thick.H @@ -16,7 +16,7 @@ #include <AMReX_REAL.H> -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements with finite length */ @@ -59,6 +59,6 @@ namespace impactx::elements int m_nslice; //! number of slices used for the application of space charge }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_THICK_H diff --git a/src/particles/elements/mixin/thin.H b/src/particles/elements/mixin/thin.H index b9c165e64..87138a95c 100644 --- a/src/particles/elements/mixin/thin.H +++ b/src/particles/elements/mixin/thin.H @@ -16,7 +16,7 @@ #include <AMReX_REAL.H> -namespace impactx::elements +namespace impactx::elements::mixin { /** This is a helper class for lattice elements with zero length */ @@ -54,6 +54,6 @@ namespace impactx::elements } }; -} // namespace impactx::elements +} // namespace impactx::elements::mixin #endif // IMPACTX_ELEMENTS_MIXIN_THIN_H diff --git a/src/python/elements.cpp b/src/python/elements.cpp index aeb0e06d9..3e601c580 100644 --- a/src/python/elements.cpp +++ b/src/python/elements.cpp @@ -154,93 +154,87 @@ void init_elements(py::module& m) "Mixin classes for accelerator lattice elements in ImpactX" ); - py::class_<elements::Named>(mx, "Named") + py::class_<elements::mixin::Named>(mx, "Named") .def_property("name", - [](elements::Named & nm) { return nm.name(); }, - [](elements::Named & nm, std::string new_name) { nm.set_name(new_name); }, + [](elements::mixin::Named & nm) { return nm.name(); }, + [](elements::mixin::Named & nm, std::string new_name) { nm.set_name(new_name); }, "segment length in m" ) - .def_property_readonly("has_name", &elements::Named::has_name) + .def_property_readonly("has_name", &elements::mixin::Named::has_name) ; - py::class_<elements::Thick>(mx, "Thick") - .def(py::init< - amrex::ParticleReal, - amrex::ParticleReal - >(), - py::arg("ds"), - py::arg("nslice") = 1, - "Mixin class for lattice elements with finite length." - ) + py::class_<elements::mixin::Thick>(mx, "Thick") .def_property("ds", - [](elements::Thick & th) { return th.m_ds; }, - [](elements::Thick & th, amrex::ParticleReal ds) { th.m_ds = ds; }, + [](elements::mixin::Thick & th) { return th.m_ds; }, + [](elements::mixin::Thick & th, amrex::ParticleReal ds) { th.m_ds = ds; }, "segment length in m" ) .def_property("nslice", - [](elements::Thick & th) { return th.m_nslice; }, - [](elements::Thick & th, int nslice) { th.m_nslice = nslice; }, + [](elements::mixin::Thick & th) { return th.m_nslice; }, + [](elements::mixin::Thick & th, int nslice) { th.m_nslice = nslice; }, "number of slices used for the application of space charge" ) ; - py::class_<elements::Thin>(mx, "Thin") - .def(py::init<>(), - "Mixin class for lattice elements with zero length." - ) + py::class_<elements::mixin::Thin>(mx, "Thin") .def_property_readonly("ds", - &elements::Thin::ds, + &elements::mixin::Thin::ds, "segment length in m" ) .def_property_readonly("nslice", - &elements::Thin::nslice, + &elements::mixin::Thin::nslice, "number of slices used for the application of space charge" ) ; - py::class_<elements::Alignment>(mx, "Alignment") - .def(py::init<>(), - "Mixin class for lattice elements with horizontal/vertical alignment errors." - ) + py::class_<elements::mixin::Alignment>(mx, "Alignment") .def_property("dx", - [](elements::Alignment & a) { return a.dx(); }, - [](elements::Alignment & a, amrex::ParticleReal dx) { a.m_dx = dx; }, + [](elements::mixin::Alignment & a) { return a.dx(); }, + [](elements::mixin::Alignment & a, amrex::ParticleReal dx) { a.m_dx = dx; }, "horizontal translation error in m" ) .def_property("dy", - [](elements::Alignment & a) { return a.dy(); }, - [](elements::Alignment & a, amrex::ParticleReal dy) { a.m_dy = dy; }, + [](elements::mixin::Alignment & a) { return a.dy(); }, + [](elements::mixin::Alignment & a, amrex::ParticleReal dy) { a.m_dy = dy; }, "vertical translation error in m" ) .def_property("rotation", - [](elements::Alignment & a) { return a.rotation(); }, - [](elements::Alignment & a, amrex::ParticleReal rotation_degree) + [](elements::mixin::Alignment & a) { return a.rotation(); }, + [](elements::mixin::Alignment & a, amrex::ParticleReal rotation_degree) { - a.m_rotation = rotation_degree * elements::Alignment::degree2rad; + a.m_rotation = rotation_degree * elements::mixin::Alignment::degree2rad; }, "rotation error in the transverse plane in degree" ) ; - py::class_<elements::LinearTransport>(mx, "LinearTransport") - .def(py::init<>(), - "Mixin class for linear transport approximation via matrices." + py::class_<elements::mixin::PipeAperture>(mx, "PipeAperture") + .def_property_readonly("aperture_x", + &elements::mixin::PipeAperture::aperture_x, + "horizontal aperture in m" ) + .def_property_readonly("aperture_y", + &elements::mixin::PipeAperture::aperture_y, + "vertical aperture in m" + ) + ; + + py::class_<elements::mixin::LinearTransport>(mx, "LinearTransport") // type of map .def_property_readonly_static("Map6x6", - [](py::object /* lt */){ return py::type::of<elements::LinearTransport::Map6x6>(); }, + [](py::object /* lt */){ return py::type::of<elements::mixin::LinearTransport::Map6x6>(); }, "1-indexed, Fortran-ordered, 6x6 linear transport map type" ) // values of the map //.def_property_readonly("R", - // [](elements::LinearTransport const & lt) { return lt.m_transport_map; }, + // [](elements::mixin::LinearTransport const & lt) { return lt.m_transport_map; }, // "1-indexed, Fortran-ordered, 6x6 linear transport map values" //) ; // diagnostics - py::class_<diagnostics::BeamMonitor, elements::Thin> py_BeamMonitor(me, "BeamMonitor"); + py::class_<diagnostics::BeamMonitor, elements::mixin::Thin> py_BeamMonitor(me, "BeamMonitor"); py_BeamMonitor .def(py::init<std::string, std::string, std::string, int>(), py::arg("name"), @@ -301,7 +295,7 @@ void init_elements(py::module& m) // beam optics - py::class_<Aperture, elements::Named, elements::Thin, elements::Alignment> py_Aperture(me, "Aperture"); + py::class_<Aperture, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_Aperture(me, "Aperture"); py_Aperture .def("__repr__", [](Aperture const & ap) { @@ -313,8 +307,8 @@ void init_elements(py::module& m) } ) .def(py::init([]( - amrex::ParticleReal xmax, - amrex::ParticleReal ymax, + amrex::ParticleReal aperture_x, + amrex::ParticleReal aperture_y, amrex::ParticleReal repeat_x, amrex::ParticleReal repeat_y, std::string const & shape, @@ -337,10 +331,10 @@ void init_elements(py::module& m) Aperture::Action const a = action == "transmit" ? Aperture::Action::transmit : Aperture::Action::absorb; - return new Aperture(xmax, ymax, repeat_x, repeat_y, s, a, dx, dy, rotation_degree, name); + return new Aperture(aperture_x, aperture_y, repeat_x, repeat_y, s, a, dx, dy, rotation_degree, name); }), - py::arg("xmax"), - py::arg("ymax"), + py::arg("aperture_x"), + py::arg("aperture_y"), py::arg("repeat_x") = 0, py::arg("repeat_y") = 0, py::arg("shape") = "rectangular", @@ -383,14 +377,14 @@ void init_elements(py::module& m) }, "action type (transmit, absorb)" ) - .def_property("xmax", - [](Aperture & ap) { return ap.m_xmax; }, - [](Aperture & ap, amrex::ParticleReal xmax) { ap.m_xmax = xmax; }, + .def_property("aperture_x", + [](Aperture & ap) { return ap.m_aperture_x; }, + [](Aperture & ap, amrex::ParticleReal aperture_x) { ap.m_aperture_x = aperture_x; }, "maximum horizontal coordinate" ) - .def_property("ymax", - [](Aperture & ap) { return ap.m_ymax; }, - [](Aperture & ap, amrex::ParticleReal ymax) { ap.m_ymax = ymax; }, + .def_property("aperture_y", + [](Aperture & ap) { return ap.m_aperture_y; }, + [](Aperture & ap, amrex::ParticleReal aperture_y) { ap.m_aperture_y = aperture_y; }, "maximum vertical coordinate" ) .def_property("repeat_x", @@ -406,7 +400,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Aperture); - py::class_<ChrDrift, elements::Named, elements::Thick, elements::Alignment> py_ChrDrift(me, "ChrDrift"); + py::class_<ChrDrift, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ChrDrift(me, "ChrDrift"); py_ChrDrift .def("__repr__", [](ChrDrift const & chr_drift) { @@ -421,6 +415,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -428,6 +424,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A Drift with chromatic effects included." @@ -435,7 +433,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrDrift); - py::class_<ChrQuad, elements::Named, elements::Thick, elements::Alignment> py_ChrQuad(me, "ChrQuad"); + py::class_<ChrQuad, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ChrQuad(me, "ChrQuad"); py_ChrQuad .def("__repr__", [](ChrQuad const & chr_quad) { @@ -453,6 +451,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -462,6 +462,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A Quadrupole magnet with chromatic effects included." @@ -479,7 +481,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrQuad); - py::class_<ChrPlasmaLens, elements::Named, elements::Thick, elements::Alignment> py_ChrPlasmaLens(me, "ChrPlasmaLens"); + py::class_<ChrPlasmaLens, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ChrPlasmaLens(me, "ChrPlasmaLens"); py_ChrPlasmaLens .def("__repr__", [](ChrPlasmaLens const & chr_pl_lens) { @@ -497,6 +499,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -506,6 +510,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "An active Plasma Lens with chromatic effects included." @@ -523,7 +529,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrPlasmaLens); - py::class_<ChrAcc, elements::Named, elements::Thick, elements::Alignment> py_ChrAcc(me, "ChrAcc"); + py::class_<ChrAcc, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment> py_ChrAcc(me, "ChrAcc"); py_ChrAcc .def("__repr__", [](ChrAcc const & chr_acc) { @@ -542,6 +548,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -551,6 +559,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A region of Uniform Acceleration, with chromatic effects included." @@ -568,7 +578,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ChrAcc); - py::class_<ConstF, elements::Named, elements::Thick, elements::Alignment> py_ConstF(me, "ConstF"); + py::class_<ConstF, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ConstF(me, "ConstF"); py_ConstF .def("__repr__", [](ConstF const & constf) { @@ -589,6 +599,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -599,6 +611,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A linear Constant Focusing element." @@ -621,7 +635,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ConstF); - py::class_<DipEdge, elements::Named, elements::Thin, elements::Alignment> py_DipEdge(me, "DipEdge"); + py::class_<DipEdge, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_DipEdge(me, "DipEdge"); py_DipEdge .def("__repr__", [](DipEdge const & dip_edge) { @@ -677,7 +691,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_DipEdge); - py::class_<Drift, elements::Named, elements::Thick, elements::Alignment> py_Drift(me, "Drift"); + py::class_<Drift, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_Drift(me, "Drift"); py_Drift .def("__repr__", [](Drift const & drift) { @@ -692,6 +706,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -699,6 +715,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A drift." @@ -706,7 +724,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Drift); - py::class_<ExactDrift, elements::Named, elements::Thick, elements::Alignment> py_ExactDrift(me, "ExactDrift"); + py::class_<ExactDrift, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ExactDrift(me, "ExactDrift"); py_ExactDrift .def("__repr__", [](ExactDrift const & exact_drift) { @@ -721,6 +739,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -728,6 +748,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A Drift using the exact nonlinear map." @@ -735,7 +757,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ExactDrift); - py::class_<ExactSbend, elements::Named, elements::Thick, elements::Alignment> py_ExactSbend(me, "ExactSbend"); + py::class_<ExactSbend, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_ExactSbend(me, "ExactSbend"); py_ExactSbend .def("__repr__", [](ExactSbend const & exact_sbend) { @@ -754,6 +776,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -763,6 +787,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 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." @@ -780,7 +806,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ExactSbend); - py::class_<Kicker, elements::Named, elements::Thin, elements::Alignment> py_Kicker(me, "Kicker"); + py::class_<Kicker, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_Kicker(me, "Kicker"); py_Kicker .def("__repr__", [](Kicker const & kicker) { @@ -832,7 +858,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Kicker); - py::class_<Multipole, elements::Named, elements::Thin, elements::Alignment> py_Multipole(me, "Multipole"); + py::class_<Multipole, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_Multipole(me, "Multipole"); py_Multipole .def("__repr__", [](Multipole const & multipole) { @@ -881,7 +907,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Multipole); - py::class_<Empty, elements::Thin> py_Empty(me, "Empty"); + py::class_<Empty, elements::mixin::Thin> py_Empty(me, "Empty"); py_Empty .def("__repr__", [](Empty const & /* empty */) { @@ -894,7 +920,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Empty); - py::class_<Marker, elements::Named, elements::Thin> py_Marker(me, "Marker"); + py::class_<Marker, elements::mixin::Named, elements::mixin::Thin> py_Marker(me, "Marker"); py_Marker .def("__repr__", [](Marker const & marker) { @@ -907,7 +933,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Marker); - py::class_<NonlinearLens, elements::Named, elements::Thin, elements::Alignment> py_NonlinearLens(me, "NonlinearLens"); + py::class_<NonlinearLens, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_NonlinearLens(me, "NonlinearLens"); py_NonlinearLens .def("__repr__", [](NonlinearLens const & nl) { @@ -947,7 +973,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_NonlinearLens); - py::class_<PlaneXYRot, elements::Named, elements::Thin, elements::Alignment> py_PlaneXYRot(me, "PlaneXYRot"); + py::class_<PlaneXYRot, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_PlaneXYRot(me, "PlaneXYRot"); py_PlaneXYRot .def("__repr__", [](PlaneXYRot const & plane_xyrot) { @@ -979,7 +1005,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_PlaneXYRot); - py::class_<Programmable, elements::Named>(me, "Programmable", py::dynamic_attr()) + py::class_<Programmable, elements::mixin::Named>(me, "Programmable", py::dynamic_attr()) .def("__repr__", [](Programmable const & prg) { return element_name( @@ -1034,7 +1060,7 @@ void init_elements(py::module& m) ) ; - py::class_<Quad, elements::Named, elements::Thick, elements::Alignment> py_Quad(me, "Quad"); + py::class_<Quad, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_Quad(me, "Quad"); py_Quad .def("__repr__", [](Quad const & quad) { @@ -1051,6 +1077,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -1059,6 +1087,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "A Quadrupole magnet." @@ -1071,7 +1101,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Quad); - py::class_<RFCavity, elements::Named, elements::Thick, elements::Alignment> py_RFCavity(me, "RFCavity"); + py::class_<RFCavity, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_RFCavity(me, "RFCavity"); py_RFCavity .def("__repr__", [](RFCavity const & rfc) { @@ -1094,6 +1124,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, int, std::optional<std::string> @@ -1107,6 +1139,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, py::arg("name") = py::none(), @@ -1137,7 +1171,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_RFCavity); - py::class_<Sbend, elements::Named, elements::Thick, elements::Alignment> py_Sbend(me, "Sbend"); + py::class_<Sbend, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_Sbend(me, "Sbend"); py_Sbend .def("__repr__", [](Sbend const & sbend) { @@ -1154,6 +1188,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -1162,6 +1198,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "An ideal sector bend." @@ -1174,7 +1212,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Sbend); - py::class_<CFbend, elements::Named, elements::Thick, elements::Alignment> py_CFbend(me, "CFbend"); + py::class_<CFbend, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_CFbend(me, "CFbend"); py_CFbend .def("__repr__", [](CFbend const & cfbend) { @@ -1193,6 +1231,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -1202,6 +1242,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "An ideal combined function bend (sector bend with quadrupole component)." @@ -1219,7 +1261,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_CFbend); - py::class_<Buncher, elements::Named, elements::Thin, elements::Alignment> py_Buncher(me, "Buncher"); + py::class_<Buncher, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_Buncher(me, "Buncher"); py_Buncher .def("__repr__", [](Buncher const & buncher) { @@ -1259,7 +1301,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Buncher); - py::class_<ShortRF, elements::Named, elements::Thin, elements::Alignment> py_ShortRF(me, "ShortRF"); + py::class_<ShortRF, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_ShortRF(me, "ShortRF"); py_ShortRF .def("__repr__", [](ShortRF const & short_rf) { @@ -1307,7 +1349,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ShortRF); - py::class_<SoftSolenoid, elements::Named, elements::Thick, elements::Alignment> py_SoftSolenoid(me, "SoftSolenoid"); + py::class_<SoftSolenoid, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_SoftSolenoid(me, "SoftSolenoid"); py_SoftSolenoid .def("__repr__", [](SoftSolenoid const & soft_sol) { @@ -1327,6 +1369,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, int, std::optional<std::string> @@ -1339,6 +1383,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, py::arg("name") = py::none(), @@ -1364,7 +1410,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_SoftSolenoid); - py::class_<Sol, elements::Named, elements::Thick, elements::Alignment> py_Sol(me, "Sol"); + py::class_<Sol, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_Sol(me, "Sol"); py_Sol .def("__repr__", [](Sol const & sol) { @@ -1381,6 +1427,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, std::optional<std::string> >(), @@ -1389,6 +1437,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("nslice") = 1, py::arg("name") = py::none(), "An ideal hard-edge Solenoid magnet." @@ -1401,7 +1451,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_Sol); - py::class_<PRot, elements::Named, elements::Thin> py_PRot(me, "PRot"); + py::class_<PRot, elements::mixin::Named, elements::mixin::Thin> py_PRot(me, "PRot"); py_PRot .def("__repr__", [](PRot const & prot) { @@ -1435,7 +1485,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_PRot); - py::class_<SoftQuadrupole, elements::Named, elements::Thick, elements::Alignment> py_SoftQuadrupole(me, "SoftQuadrupole"); + py::class_<SoftQuadrupole, elements::mixin::Named, elements::mixin::Thick, elements::mixin::Alignment, elements::mixin::PipeAperture> py_SoftQuadrupole(me, "SoftQuadrupole"); py_SoftQuadrupole .def("__repr__", [](SoftQuadrupole const & soft_quad) { @@ -1454,6 +1504,8 @@ void init_elements(py::module& m) amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, + amrex::ParticleReal, + amrex::ParticleReal, int, int, std::optional<std::string> @@ -1465,6 +1517,8 @@ void init_elements(py::module& m) py::arg("dx") = 0, py::arg("dy") = 0, py::arg("rotation") = 0, + py::arg("aperture_x") = 0, + py::arg("aperture_y") = 0, py::arg("mapsteps") = 1, py::arg("nslice") = 1, py::arg("name") = py::none(), @@ -1485,7 +1539,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_SoftQuadrupole); - py::class_<ThinDipole, elements::Named, elements::Thin, elements::Alignment> py_ThinDipole(me, "ThinDipole"); + py::class_<ThinDipole, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_ThinDipole(me, "ThinDipole"); py_ThinDipole .def("__repr__", [](ThinDipole const & thin_dp) { @@ -1525,7 +1579,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_ThinDipole); - py::class_<TaperedPL, elements::Named, elements::Thin, elements::Alignment> py_TaperedPL(me, "TaperedPL"); + py::class_<TaperedPL, elements::mixin::Named, elements::mixin::Thin, elements::mixin::Alignment> py_TaperedPL(me, "TaperedPL"); py_TaperedPL .def("__repr__", [](TaperedPL const & taperedpl) { @@ -1579,7 +1633,7 @@ void init_elements(py::module& m) ; register_beamoptics_push(py_TaperedPL); - py::class_<LinearMap, elements::Named, elements::Alignment, elements::LinearTransport> py_LinearMap(me, "LinearMap"); + py::class_<LinearMap, elements::mixin::Named, elements::mixin::Alignment, elements::mixin::LinearTransport> py_LinearMap(me, "LinearMap"); py_LinearMap .def("__repr__", [](LinearMap const & linearmap) { @@ -1589,7 +1643,7 @@ void init_elements(py::module& m) } ) .def(py::init< - elements::LinearTransport::Map6x6, + elements::mixin::LinearTransport::Map6x6, amrex::ParticleReal, amrex::ParticleReal, amrex::ParticleReal, @@ -1606,7 +1660,7 @@ void init_elements(py::module& m) ) .def_property("R", [](LinearMap & linearmap) { return linearmap.m_transport_map; }, - [](LinearMap & linearmap, elements::LinearTransport::Map6x6 R) { linearmap.m_transport_map = R; }, + [](LinearMap & linearmap, elements::mixin::LinearTransport::Map6x6 R) { linearmap.m_transport_map = R; }, "linear map as a 6x6 transport matrix" ) .def_property("ds",