Particles monitors

The easy way

When starting a tracking simulation with the Xtrack Tracker object, the easiest way of logging the coordinates of all particles for all turns is to enable the default turn-by-turn monitor, as illustrated by the following example. Note: this mode requires that particles.at_turn is 0 for all particles at the beginning of the simulation.

import json

import xtrack as xt
import xpart as xp
import xobjects as xo

context = xo.ContextCpu()

with open('../../test_data/hllhc15_noerrors_nobb/line_and_particle.json') as f:
    dct = json.load(f)
line = xt.Line.from_dict(dct['line'])
line.particle_ref = xp.Particles.from_dict(dct['particle'])

tracker = line.build_tracker()

num_particles = 50
particles = xp.generate_matched_gaussian_bunch(tracker=tracker,
                                               num_particles=num_particles,
                                               nemitt_x=2.5e-6,
                                               nemitt_y=2.5e-6,
                                               sigma_z=9e-2)

num_turns = 30
tracker.track(particles, num_turns=num_turns,
              turn_by_turn_monitor=True # <--
             )
# tracker.record_last_track contains the measured data. For example,
# tracker.record_last_track.x contains the x coordinate for all particles
# and all turns, e.g. tracker.record_last_track.x[3, 5] for the particle
# having particle_id = 3 and for the turn number 5.

Custom particles monitor

In order to record the particles coordinates only in a selected range of turns, a custom monitor object can be built and passed to the Tracker.track function, as illustrated by the following example.

import json

import xtrack as xt
import xpart as xp
import xobjects as xo

context = xo.ContextCpu()

with open('../../test_data/hllhc15_noerrors_nobb/line_and_particle.json') as f:
    dct = json.load(f)
line = xt.Line.from_dict(dct['line'])
line.particle_ref = xp.Particles.from_dict(dct['particle'])

tracker = line.build_tracker()

num_particles = 50
particles = xp.generate_matched_gaussian_bunch(tracker=tracker,
                                               num_particles=num_particles,
                                               nemitt_x=2.5e-6,
                                               nemitt_y=2.5e-6,
                                               sigma_z=9e-2)

num_turns = 30
monitor = xt.ParticlesMonitor(_context=context,
                              start_at_turn=5, stop_at_turn=15,
                              num_particles=num_particles)
tracker.track(particles, num_turns=num_turns,
              turn_by_turn_monitor=monitor
             )
# tracker.record_last_track contains the measured data. For example,
# tracker.record_last_track.x contains the x coordinate for all particles
# and the selected turns, e.g. tracker.record_last_track.x[3, 5] gives the
# x coordinates for the particle having particle_id = 3 and for the fifth
# recorded turn. The turn indeces that are recorded can be inspected in
# tracker.record_last_track.at_turn

The monitoring can also be limited to a selected range of particles IDs, by using the argument particle_id_range of the ParticlesMonitor class to provide a tuple defining the range to be recorded. In that case the num_particles input of the monitor is omitted.

Multi-frame particles monitor

The particles monitor can record periodically spaced intervals of turns (frames) This feature can be activated by providing the arguments n_repetitions and repetition_period when creating the monitor.

import json

import xtrack as xt
import xpart as xp
import xobjects as xo

context = xo.ContextCpu()

with open('../../test_data/hllhc15_noerrors_nobb/line_and_particle.json') as f:
    dct = json.load(f)
line = xt.Line.from_dict(dct['line'])
line.particle_ref = xp.Particles.from_dict(dct['particle'])

tracker = line.build_tracker()

num_particles = 50
particles = xp.generate_matched_gaussian_bunch(tracker=tracker,
                                               num_particles=num_particles,
                                               nemitt_x=2.5e-6,
                                               nemitt_y=2.5e-6,
                                               sigma_z=9e-2)

num_turns = 100
monitor = xt.ParticlesMonitor(_context=context,
                              start_at_turn=5, stop_at_turn=10,
                              n_repetitions=3,      # <--
                              repetition_period=20, # <--
                              num_particles=num_particles)
tracker.track(particles, num_turns=num_turns,
              turn_by_turn_monitor=monitor)

# tracker.record_last_track contains the measured data. For all particles
# variables the first index provides the frame index.
# For example, tracker.record_last_track.x[0, :, :] contains the recorded
# x position for the turns 5 to 10, tracker.record_last_track.x[1, :, :]
# contains the recorded x position for the turns 25 to 30, etc.
# The turn indeces that are recorded can be inspected in
# tracker.record_last_track.at_turn.

Particles monitor as beam elements

Particles monitors can be used as regular beam element to record the particle coordinates at specific locations in the beam line. For this purpose they can be inserted in the line, as illustrated in the following example.

import json

import xtrack as xt
import xpart as xp
import xobjects as xo

context = xo.ContextCpu()

with open('../../test_data/hllhc15_noerrors_nobb/line_and_particle.json') as f:
    dct = json.load(f)
line = xt.Line.from_dict(dct['line'])
line.particle_ref = xp.Particles.from_dict(dct['particle'])

num_particles = 50
monitor_ip5 = xt.ParticlesMonitor(start_at_turn=5, stop_at_turn=15,
                                    num_particles=num_particles)
monitor_ip8 = xt.ParticlesMonitor(start_at_turn=5, stop_at_turn=15,
                                    num_particles=num_particles)
line.insert_element(index='ip5', element=monitor_ip5, name='mymon5')
line.insert_element(index='ip8', element=monitor_ip8, name='mymon8')

tracker = line.build_tracker()

particles = xp.generate_matched_gaussian_bunch(tracker=tracker,
                                               num_particles=num_particles,
                                               nemitt_x=2.5e-6,
                                               nemitt_y=2.5e-6,
                                               sigma_z=9e-2)

num_turns = 30
monitor = xt.ParticlesMonitor(_context=context,
                              start_at_turn=5, stop_at_turn=15,
                              num_particles=num_particles)
tracker.track(particles, num_turns=num_turns)

# monitor_ip5 contains the data recorded in before the element 'ip5', while
# monitor_ip8 contains the data recorded in before the element 'ip8'
# The element index at which the recording is made can be inspected in
# monitor_ip5.at_element.

Particles monitor in stand-alone mode

As all Xtrack elements, the Particles Monitor has a track method and can be used in stand-alone mode as illustrated in the following example.

import json

import xtrack as xt
import xpart as xp
import xobjects as xo

context = xo.ContextCpu()

with open('../../test_data/hllhc15_noerrors_nobb/line_and_particle.json') as f:
    dct = json.load(f)
line = xt.Line.from_dict(dct['line'])
line.particle_ref = xp.Particles.from_dict(dct['particle'])

tracker = line.build_tracker()

num_particles = 50
particles = xp.generate_matched_gaussian_bunch(tracker=tracker,
                                               num_particles=num_particles,
                                               nemitt_x=2.5e-6,
                                               nemitt_y=2.5e-6,
                                               sigma_z=9e-2)

num_turns = 100
monitor = xt.ParticlesMonitor(_context=context,
                              start_at_turn=5, stop_at_turn=10,
                              n_repetitions=3,      # <--
                              repetition_period=20, # <--
                              num_particles=num_particles)
for iturn in range(num_turns):
    monitor.track(particles)
    tracker.track(particles)

# monitor contains the measured data. For all particles
# variables the first index provides the frame index.
# For example, monitor.x[0, :, :] contains the recorded
# x position for the turns 5 to 10, monitor.x[1, :, :]
# contains the recorded x position for the turns 25 to 30, etc.
# The turn indeces that are recorded can be inspected in
# monitor.at_turn.