Collimation

Loss location refinement

In Xtrack simulations particles are lost at defined aperture elements (e.g. xtrack.LimitRect, xtrack.LimitEllipse, xtrack.LimitRectEllipse, xtrack.LimitPolygon). A more accurate estimate of the loss locations can be obtained after the tracking is finished using the xtrack.LossLocationRefinement tool . The tool builds an interpolated aperture model between the aperture elements and backtracks the particles in order to identify the impact point. The following example illustrates how to use this feature.

See also: xtrack.LossLocationRefinement

import numpy as np

import xtrack as xt
import xobjects as xo


###################
# Build test line #
###################

ctx = xo.context_default
buf = ctx.new_buffer()

# We build a test line having two aperture elements which are shifted and
# rotated w.r.t. the accelerator reference frame.

# Define aper_0
aper_0 = xt.LimitEllipse(_buffer=buf, a=2e-2, b=1e-2)
shift_aper_0 = (1e-2, 0.5e-2)
rot_deg_aper_0 = 10.

# Define aper_1
aper_1 = xt.LimitRect(_buffer=buf, min_x=-1e-2, max_x=1e-2,
                                   min_y=-2e-2, max_y=2e-2)
shift_aper_1 = (-5e-3, 1e-2)
rot_deg_aper_1 = 10.
aper_1.shift_x = shift_aper_1[0]
aper_1.shift_y = shift_aper_1[1]
aper_1.rot_s_rad = np.deg2rad(rot_deg_aper_1)


# aper_0_sandwitch
line_aper_0 = xt.Line(
    elements=[xt.XYShift(_buffer=buf, dx=shift_aper_0[0], dy=shift_aper_0[1]),
              xt.SRotation(_buffer=buf, angle=rot_deg_aper_0),
              aper_0,
              xt.Multipole(_buffer=buf, knl=[0.001]),
              xt.SRotation(_buffer=buf, angle=-rot_deg_aper_0),
              xt.XYShift(_buffer=buf, dx=-shift_aper_0[0], dy=-shift_aper_0[1])])
line_aper_0.build_tracker(_buffer=buf)

# aper_1_sandwitch
line_aper_1 = xt.Line(
    elements=[aper_1,
              xt.Multipole(_buffer=buf, knl=[0.001])
        ])
line_aper_1.build_tracker(_buffer=buf)

#################
# Build tracker #
#################

line=xt.Line(
    elements = ((xt.Drift(_buffer=buf, length=0.5),)
                + line_aper_0.elements
                + (xt.Drift(_buffer=buf, length=1),
                   xt.Drift(_buffer=buf, length=1),
                   xt.Drift(_buffer=buf, length=1.),)
                + line_aper_1.elements))
line.build_tracker(_buffer=buf)
num_elements = len(line.element_names)

# Generate test particles
particles = xt.Particles(_context=ctx,
            px=np.random.uniform(-0.01, 0.01, 10000),
            py=np.random.uniform(-0.01, 0.01, 10000))

#########
# Track #
#########

line.track(particles)

########################
# Refine loss location #
########################

loss_loc_refinement = xt.LossLocationRefinement(line,
        n_theta = 360, # Angular resolution in the polygonal approximation of the aperture
        r_max = 0.5, # Maximum transverse aperture in m
        dr = 50e-6, # Transverse loss refinement accuracy [m]
        ds = 0.1, # Longitudinal loss refinement accuracy [m]
        save_refine_lines=True # Diagnostics flag
        )


loss_loc_refinement.refine_loss_location(particles)


# Complete source: xtrack/examples/collimation/001_loss_location_refinement.py
_images/loss_location_refinement.png

Generated transition between the defined apertures. Red dots represent the location of the particle-loss events. See the code generating the image.

Beam interaction (generation of secondary particles)

Xtrack includes an interface to ease the modeling of beam-matter interaction (collimators, beam-gas, collisions with another beam), including the loss of the impacting particles and the production of secondary particles, which need to be tracked together with the surviving beam. Such interface can be used to create a link with other programs for the modeling of these effects, e.g. GEANT, FLUKA, K2, GuineaPig.

The interaction is defined as an object that provides a .interact(particles) method, which sets to zero or negative the state flag for the particles that are lost and returns a dictionary with the coordinates of the secondary particles that are emitted. The interaction process is embedded in one or multiple xtrack.BeamInteraction beam elements that can be included in Xtrack line.

This is illustrated by the following example:

import numpy as np

import xtrack as xt

######################################
# Create a dummy collimation process #
######################################

class DummyInteractionProcess:
    '''
    I kill some particles. I kick some others by an given angle
    and I generate some secondaries with the opposite angles.
    '''
    def __init__(self, fraction_lost, fraction_secondary, length, kick_x):

        self.fraction_lost = fraction_lost
        self.fraction_secondary = fraction_secondary
        self.kick_x = kick_x
        self.length = length

        self.drift = xt.Drift(length=self.length)


    def interact(self, particles):

        self.drift.track(particles)

        n_part = particles._num_active_particles

        # Kill some particles
        mask_kill = np.random.uniform(size=n_part) < self.fraction_lost
        particles.state[:n_part][mask_kill] = -1 # special flag`


        # Generate some more particles
        mask_secondary = np.random.uniform(size=n_part) < self.fraction_secondary
        n_products = np.sum(mask_secondary)
        if n_products>0:
            products = {
                's': particles.s[:n_part][mask_secondary],
                'x': particles.x[:n_part][mask_secondary],
                'px': particles.px[:n_part][mask_secondary] + self.kick_x,
                'y': particles.y[:n_part][mask_secondary],
                'py': particles.py[:n_part][mask_secondary],
                'zeta': particles.zeta[:n_part][mask_secondary],
                'delta': particles.delta[:n_part][mask_secondary],

                'mass_ratio': particles.x[:n_part][mask_secondary] *0 + 1.,
                'charge_ratio': particles.x[:n_part][mask_secondary] *0 + 1.,

                'parent_particle_id': particles.particle_id[:n_part][mask_secondary],
                'at_element': particles.at_element[:n_part][mask_secondary],
                'at_turn': particles.at_turn[:n_part][mask_secondary],
                }
        else:
            products = None

        return products

############################################################
# Create a beam interaction from the process defined above #
############################################################

interaction_process=DummyInteractionProcess(length=1., kick_x=4e-3,
                                            fraction_lost=0.0,
                                            fraction_secondary=0.2)
collimator = xt.BeamInteraction(length=interaction_process.length,
                                      interaction_process=interaction_process)

########################################################
# Create a line including the collimator defined above #
########################################################

line = xt.Line(elements=[
    xt.Multipole(knl=[0,0]),
    xt.LimitEllipse(a=2e-2, b=2e-2),
    xt.Drift(length=1.),
    xt.Multipole(knl=[0,0]),
    xt.LimitEllipse(a=2e-2, b=2e-2),
    xt.Drift(length=2.),
    collimator,
    xt.Multipole(knl=[0,0]),
    xt.LimitEllipse(a=2e-2, b=2e-2),
    xt.Drift(length=10.),
    xt.LimitEllipse(a=2e-2, b=2e-2),
    xt.Drift(length=10.),
    ])

#################
# Build tracker #
#################

line.build_tracker()
line.config.XTRACK_GLOBAL_XY_LIMIT = 1e3

##########################
# Build particles object #
##########################

# We prepare empty slots to store the product particles that will be
# generated during the tracking.
particles = xt.Particles(
        _capacity=200000,
        x=np.zeros(100000))

#########
# Track #
#########

line.track(particles)

############################
# Loss location refinement #
############################

loss_loc_refinement = xt.LossLocationRefinement(line,
                                            n_theta = 360,
                                            r_max = 0.5, # m
                                            dr = 50e-6,
                                            ds = 0.05,
                                            save_refine_lines=True)

loss_loc_refinement.refine_loss_location(particles)


# Complete source: xtrack/examples/collimation/003_all_together.py