Aperture
========
This module describes the **aperture checking system** (Aperture) in PASS, used to check whether particles exceed the transverse aperture boundaries of the beam pipe during particle tracking. Aperture checking is a core component of beam loss simulation, capable of identifying and recording particles lost due to transverse coordinates exceeding physical pipe limits.
The aperture module is located at ``PASS/utils/aperture.py`` and provides both CPU and GPU implementations (called via the ``check_aperture_cpu`` and ``check_aperture_gpu`` functions, respectively), automatically invoked after each element tracking. The current element's longitudinal position :math:`s` and the current turn number are passed in at call time.
Aperture checking is performed only on the transverse coordinates :math:`(x, y)` of particles and does not involve longitudinal coordinates. Each element can independently set its aperture type and parameters, supporting 10 aperture geometries.
Geometry construction and loss handling are both defined in this one file.
``build_aperture({"Type": ..., "Value": ...})`` constructs a geometry object;
``mask(x, y)`` includes the wall, while ``strict_mask(x, y)`` excludes it.
These predicates accept NumPy or CuPy coordinate arrays and return a mask on
the same backend without modifying particles. ``aperture_bounds(geometry)``
returns the geometric extents for initialization checks.
``check_aperture_gpu`` supports all types below with float32 or float64 particle
coordinates and records losses on the device. It validates shape parameters
before launching a CUDA kernel, accepts integer or floating-point dimensions,
and does nothing for an empty bunch. Previously lost particles retain their
original loss position and turn; particles outside the requested bunch range
are untouched. CPU execution does not require CuPy. This GPU support concerns
the aperture module; PIC and SpaceCharge tracking remain CPU-only.
Interface Parameters
--------------------
The aperture system is controlled by two parameters:
.. list-table::
:header-rows: 1
:widths: 20 25 15 40
* - Property
- JSON key
- Type
- Description
* - ``aperture_type``
- ``Aperture Type``
- str
- Aperture type, case-insensitive; available values are listed below
* - ``aperture_value``
- ``Aperture Value``
- list
- Aperture parameter values; meaning varies by type
.. note::
``aperture_type`` is case-insensitive and is internally converted to lowercase before matching. The ``off`` and ``default`` types ignore ``aperture_value`` .
Lost Particle Handling
----------------------
Only strict aperture interiors survive on both CPU and GPU. Particles touching
any physical wall are lost, including polygon edges and vertices; rectangular
loss does not require both coordinates to reach their limits.
When a particle is determined to be lost, the system performs the following operations:
- **tag negation** : :math:`\text{tag} \leftarrow -|\text{tag}|` , preserving the particle ID information and only negating the sign to mark it as lost
- **lost_position** : records the longitudinal coordinate :math:`s` of the loss location, i.e., the longitudinal position of the current element
- **lost_turn** : records the turn number at the time of loss
Particles already lost ( :math:`\text{tag} < 0` ) are skipped in subsequent aperture checks and are not marked again. Aperture checking is performed only on surviving particles ( :math:`\text{tag} > 0` ).
Detailed Aperture Types
-----------------------
The following describes the parameter definitions and loss conditions for each of the 10 aperture types.
off (Disabled)
~~~~~~~~~~~~~~
**Parameters** : none ( ``aperture_value`` is ignored)
**Description** : No aperture checking is performed; all particles are retained.
.. raw:: html
default (Default Rectangle)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Parameters** : none ( ``aperture_value`` is ignored)
**Description** : Ordinary elements use the default ±1m rectangle, equivalent
to ``aperture_value = [1.0, 1.0]``. ``SpaceCharge`` instead resolves default
to the rectangle defined by its configuration's grid extent; see :doc:`space_charge`.
**Loss condition** :
.. math::
|x| \ge 1.0 \quad \text{or} \quad |y| \ge 1.0
.. raw:: html
circle (Circular)
~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [r]`` , where :math:`r` is the circle radius.
**Loss condition** :
.. math::
x^2 + y^2 \ge r^2
.. raw:: html
rectangle (Rectangular)
~~~~~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [w, h]`` , where :math:`w` is the half-width and :math:`h` is the half-height.
**Loss condition** (particle is lost if either condition is met):
.. math::
|x| \ge w
.. math::
|y| \ge h
.. raw:: html
ellipse (Elliptical)
~~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [a, b]`` , where :math:`a` is the semi-major axis (x direction) and :math:`b` is the semi-minor axis (y direction).
**Loss condition** :
.. math::
\left(\frac{x}{a}\right)^2 + \left(\frac{y}{b}\right)^2 \ge 1
.. raw:: html
rectcircle (Rectangle Inscribed Circle)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [w, h, r]`` , where :math:`w` is the rectangle half-width, :math:`h` is the rectangle half-height, and :math:`r` is the circle radius.
The aperture region is the **intersection** of the rectangle and the circle (particles must be inside both the rectangle and the circle to survive).
**Loss condition** (particle is lost if either condition is met):
.. math::
|x| \ge w \quad \text{or} \quad |y| \ge h
.. math::
x^2 + y^2 \ge r^2
.. raw:: html
.. note::
The red dashed line is the rectangle boundary, and the blue solid line is the circle boundary. The aperture region is the intersection of the two.
rectellipse (Rectangle Inscribed Ellipse)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [w, h, a, b]`` , where :math:`w` is the rectangle half-width, :math:`h` is the rectangle half-height, :math:`a` is the ellipse semi-major axis (x direction), and :math:`b` is the ellipse semi-minor axis (y direction).
The aperture region is the **intersection** of the rectangle and the ellipse (particles must be inside both the rectangle and the ellipse to survive).
**Loss condition** (particle is lost if either condition is met):
.. math::
|x| \ge w \quad \text{or} \quad |y| \ge h
.. math::
\left(\frac{x}{a}\right)^2 + \left(\frac{y}{b}\right)^2 \ge 1
.. raw:: html
.. note::
The red dashed line is the rectangle boundary, and the blue solid line is the ellipse boundary. The aperture region is the intersection of the two.
racetrack (Racetrack)
~~~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [w, h, a, b]`` , where :math:`w` is the rectangle half-width, :math:`h` is the rectangle half-height, :math:`a` is the x-direction semi-axis of the elliptical ends, and :math:`b` is the y-direction semi-axis of the elliptical ends.
The racetrack aperture consists of a central rectangle and two semi-elliptical ends. The centers of the elliptical ends are located at :math:`(\pm w, 0)` .
**Survival condition** (particle survives if either condition is met):
Strictly inside the rectangular region:
.. math::
|x| < w \quad \text{and} \quad |y| < h
Inside the elliptical end region (when :math:`|x| > w` ):
.. math::
\left(\frac{|x| - w}{a}\right)^2 + \left(\frac{y}{b}\right)^2 < 1
At the internal seams :math:`|x|=w`, a particle survives only when
:math:`|y|<\min(h,b)`; these seams are not physical walls. All other points
outside the strict regions are lost.
.. raw:: html
.. note::
The orange dots mark the centers of the elliptical ends at :math:`(\pm w, 0)` .
octagon (Octagonal)
~~~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [w, h, d]`` , where :math:`w` is the half-width, :math:`h` is the half-height, and :math:`d` is the half-diagonal clearance (chamfer distance).
The octagon is the shape obtained by cutting 45° corners off a rectangle. The larger :math:`d` , the larger the chamfer; when :math:`d = 0` , it degenerates into a rectangle.
**Loss condition** (particle is lost if either condition is met):
.. math::
|x| \ge w \quad \text{or} \quad |y| \ge h
.. math::
|x| + |y| \ge w + h - d
.. raw:: html
polygon (Polygon)
~~~~~~~~~~~~~~~~~
**Parameters** : ``aperture_value = [[x1, y1], [x2, y2], ...]`` , a list of vertices, automatically closed (the last vertex connects back to the first vertex).
**Loss condition** : The point lies on an edge or vertex, or outside the polygon.
The edge check precedes ray casting; it uses a small floating-point tolerance
shared by CPU and GPU.
The **ray casting** method is used to determine whether a point is inside the polygon: a ray is cast from the test point in the horizontal direction, and the number of intersections with the polygon edges is counted:
- Odd number of intersections → the point is inside the polygon (survives)
- Even number of intersections → the point is outside the polygon (lost)
.. raw:: html