Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
4f9bc46
Add `compt_scale` as a user parameter.
gbrogginess Aug 21, 2025
49b473e
Touschek!
gbrogginess Sep 4, 2025
d366006
Return particle object.
gbrogginess Sep 4, 2025
43c8b87
Return a scalar.
gbrogginess Sep 4, 2025
8300520
Configure correct s-location.
gbrogginess Sep 4, 2025
0b67070
Use ELEGANT's random number generator.
gbrogginess Sep 5, 2025
9642c41
Clean-up.
gbrogginess Sep 7, 2025
204d05d
Include ELEGANT and SDDS credits.
gbrogginess Sep 7, 2025
c828fbe
Move third party notice to `contributors_and_credits.txt`.
gbrogginess Sep 7, 2025
0cbc053
Add myself as contributor.
gbrogginess Sep 7, 2025
ea07b3c
Add README for touschek.
gbrogginess Sep 7, 2025
7069a69
Add link to ELEGANT.
gbrogginess Sep 7, 2025
30cffd1
Fix.
gbrogginess Sep 7, 2025
6903936
Cleanup headers.
gbrogginess Sep 7, 2025
5b59ddb
Cleanup contributors_and_credits.txt
gbrogginess Sep 7, 2025
a411d27
Add NOTICE
gbrogginess Sep 7, 2025
11a3cbf
Add LAPACK license in third_party
gbrogginess Sep 7, 2025
91b7ecc
Cleanup touschek readme.
gbrogginess Sep 7, 2025
74b85a2
Take momentum aperture based on s instead of element_name.
gbrogginess Sep 10, 2025
c345044
Add back momentum_aperture attribute.
gbrogginess Sep 10, 2025
60ad9bc
Line table not suitable when lines come from MAD-X conversion.
gbrogginess Sep 10, 2025
a8483e4
Speed up Piwinski integral.
gbrogginess Sep 15, 2025
2da379b
Momentum aperture selection based on s instead of element_name.
gbrogginess Sep 15, 2025
ca69ce3
Inherit some warning prints from ELEGANT for safety.
gbrogginess Sep 15, 2025
9d6e53e
Remove typo.
gbrogginess Sep 20, 2025
35843b1
Change momentum aperture handling.
gbrogginess Sep 20, 2025
cdb7b6a
Introduce theta_min and theta_max as user_defined parameters.
gbrogginess Sep 20, 2025
3a01e68
theta_min and theta_max must not be set by the user.
gbrogginess Sep 22, 2025
e2329eb
Touschek: clamp δ sampling to LMA (k=0.9).
gbrogginess Sep 22, 2025
6212f50
Touschek: safety factor from LMA 0.9 --> 0.85.
gbrogginess Sep 22, 2025
b9044f5
Touschek: center Touschek scattered particles around the closed orbit.
gbrogginess Sep 22, 2025
60d333f
Touschek: remove ignoredRate.
gbrogginess Sep 22, 2025
48568a3
Restore original epsabs and epsrel in Piwinski integral.
gbrogginess Sep 24, 2025
c43d013
Merge branch 'xsuite:main' into touschek
gbrogginess Nov 5, 2025
a3c6e51
Merge branch 'xsuite:main' into touschek
gbrogginess Nov 18, 2025
413892b
Merge branch 'xsuite:main' into touschek
gbrogginess Dec 10, 2025
43912eb
Merge branch 'xsuite:main' into touschek
gbrogginess Feb 11, 2026
0de6fa2
Use the include header facility of Xobjects
gbrogginess Feb 13, 2026
929efcd
Touschek example!
gbrogginess Feb 13, 2026
37ac2bd
Merge branch 'touschek' of https://github.com/gbrogginess/xfields int…
gbrogginess Feb 13, 2026
f1a473d
Clean up Touschek example.
gbrogginess Feb 13, 2026
728a7be
Touschek test draft.
gbrogginess Feb 13, 2026
879797d
Merge branch 'xsuite:main' into touschek
gbrogginess Feb 25, 2026
e36ea7e
Merge branch 'xsuite:main' into touschek
gbrogginess Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ include pyproject.toml
# Include the README
include *.md

# Include the license file
# Include the license files
include LICENSE.txt
recursive-include xfields/third_party *LICENSE*

# Include the notice
include NOTICE

recursive-include xfields *.h
recursive-include xfields *.clh
recursive-include xfields *.clh
7 changes: 7 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This package incorporates portions adapted from Argonne National Laboratory’s “Elegant”
and from the SDDS Toolkit. © 2002 The University of Chicago; © 2002 The Regents of the
University of California. Distributed under their respective Software License Agreements.
See: xfields/third_party/elegant/LICENSE and xfields/third_party/SDDS/LICENSE.

This package also incorporates LAPACK’s DLARAN algorithm (modified BSD license).
See: xfields/third_party/lapack/LICENSE.
6 changes: 6 additions & 0 deletions contributors_and_credits.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ The following people contributed to the development of this package:

CERN contributors:
- H. Bartosik
- G. Broggi
- X. Buffat
- R. De Maria
- L. Giacomel
Expand All @@ -18,3 +19,8 @@ External contributors:


The Particle In Cell implementation is largely based on the PyPIC package (https://github.com/pycomplete/pypic)

The Touschek scattering routine contains portions adapted from Elegant and the SDDS Toolkit.
© 2002 The University of Chicago; © 2002 The Regents of the University of California.
Distributed subject to their Software License Agreements. See third_party/elegant/LICENSE and third_party/SDDS/LICENSE.
The RNG core used in the Touschek scattering routine incorporates LAPACK’s DLARAN (modified BSD license). See third_party/lapack/LICENSE.
222 changes: 222 additions & 0 deletions examples/006_touschek/000_touschek_toy_ring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# copyright ############################### #
# This file is part of the Xtrack Package. #
# Copyright (c) CERN, 2025. #
# ######################################### #
import numpy as np
import matplotlib.pyplot as plt

import xobjects as xo
import xtrack as xt
import xfields as xf

######################################################
# Beam parameters
######################################################
nemitt_x = 1e-5
nemitt_y = 1e-7

sigma_z = 4e-3
sigma_delta = 1e-3

bunch_population = 4e9

######################################################
# Build a toy ring
######################################################
lbend = 3
angle = np.pi / 2

lquad = 0.3
k1qf = 0.1
k1qd = 0.7

# Create environment
env = xt.Environment()

# Define the line (toy ring)
line = env.new_line(components=[
env.new('mqf.1', xt.Quadrupole, length=lquad, k1=k1qf),
env.new('d1.1', xt.Drift, length=1),
env.new('mb1.1', xt.Bend, length=lbend, angle=angle),
env.new('d2.1', xt.Drift, length=1),

env.new('mqd.1', xt.Quadrupole, length=lquad, k1=-k1qd),
env.new('d3.1', xt.Drift, length=1),
env.new('mb2.1', xt.Bend, length=lbend, angle=angle),
env.new('d4.1', xt.Drift, length=1),

env.new('mqf.2', xt.Quadrupole, length=lquad, k1=k1qf),
env.new('d1.2', xt.Drift, length=1),
env.new('mb1.2', xt.Bend, length=lbend, angle=angle),
env.new('d2.2', xt.Drift, length=1),

env.new('mqd.2', xt.Quadrupole, length=lquad, k1=-k1qd),
env.new('d3.2', xt.Drift, length=1),
env.new('mb2.2', xt.Bend, length=lbend, angle=angle),
env.new('d4.2', xt.Drift, length=1),
])

# Set the reference particle
line.set_particle_ref('electron', p0c=1e9)

# Configure the bend model
line.configure_bend_model(core='full', edge=None)

######################################################
# Insert Touschek scattering centers
######################################################
# We insert Touschek scattering centers in the middle of each magnet
# to have good coverage of variations of the optical functions
tab = line.get_table()
tab_bends_quads = tab.rows[(tab.element_type == 'Bend') | (tab.element_type == 'Quadrupole')]

for ii, nn in enumerate(tab_bends_quads.name):
tscatter_name = f'TScatter_{ii}'
env.elements[tscatter_name] = xf.TouschekScattering()
line.insert(tscatter_name, at=0.0, from_=nn)

# The last TouschekScattering element has to be placed at the end of the line
tscatter_name = f'TScatter_{ii+1}'
env.elements[tscatter_name] = xf.TouschekScattering()
line.insert(tscatter_name, at=tab.s[-1])

######################################################
# Install apertures
######################################################
tab = line.get_table()
needs_aperture = np.unique(tab.element_type)[
~np.isin(np.unique(tab.element_type), ["", "Drift", "Marker"])
]

aper_size = 0.040 # m

placements = []
for nn, ee in zip(tab.name, tab.element_type):
if ee not in needs_aperture:
continue

env.new(
f'{nn}_aper_entry', xt.LimitRect,
min_x=-aper_size, max_x=aper_size,
min_y=-aper_size, max_y=aper_size
)
placements.append(env.place(f'{nn}_aper_entry', at=f'{nn}@start'))

env.new(
f'{nn}_aper_exit', xt.LimitRect,
min_x=-aper_size, max_x=aper_size,
min_y=-aper_size, max_y=aper_size
)
placements.append(env.place(f'{nn}_aper_exit', at=f'{nn}@end'))

line.insert(placements)

######################################################
# Evaluate momentum aperture profile
######################################################
# Evaluate local momentum aperture at the touschek scattering centers
momentum_aperture = line.momentum_aperture(
# twiss=tw,
include_type_pattern="TouschekScattering",
nemitt_x=nemitt_x,
nemitt_y=nemitt_y,
y_offset=1e-9,
delta_negative_limit=-0.012,
delta_positive_limit=0.012,
delta_step_size=1e-4,
n_turns=1000,
method="4d"
)

df_momentum_aperture = momentum_aperture.to_pandas()

######################################################
# Plot
######################################################
plt.plot(momentum_aperture.s, momentum_aperture.deltan*100, c='r')
plt.plot(momentum_aperture.s, momentum_aperture.deltap*100, c='r')
plt.title('Toy ring: local momentum aperture profile')
plt.xlabel('s [m]')
plt.ylabel(r'$\delta$ [%]')
plt.grid()
plt.show()

######################################################
# Touschek simulation
######################################################
# Parameters
momentum_aperture_scale = 0.85 # scaling factor for momentum aperture
n_simulated = 5e6 # number of simulated scattering events with delta > delta_min
nturns = 1000 # number of turns to simulate

touschek_manager = xf.TouschekManager(
line,
momentum_aperture=df_momentum_aperture,
momentum_aperture_scale=momentum_aperture_scale,
nemitt_x=nemitt_x,
nemitt_y=nemitt_y,
sigma_z=sigma_z,
sigma_delta=sigma_delta,
bunch_population=bunch_population,
n_simulated=n_simulated,
nx=3, ny=3, nz=3,
ignored_portion=0.01,
seed=1997,
method='4d'
)

touschek_manager.initialise_touschek()

touschek_elements = tab.rows[tab.element_type == 'TouschekScattering'].name

line.discard_tracker()
line.build_tracker(_context=xo.ContextCpu(omp_num_threads='auto'))

particles_list = []
for ii in range(len(touschek_elements)):
element = touschek_elements[ii] # xf.TouschekScattering
s_start_elem = tab.rows[tab.name == element].s[0]

# Touschek!
particles = line[element].scatter()

# Track!
print(f"\nTrack particles scattered at {element} at s = {s_start_elem}")
line.track(particles, ele_start=element, ele_stop=element, num_turns=nturns, with_progress=1)

particles_list.append(particles)

particles = xt.Particles.merge(particles_list)

# 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]
)

loss_loc_refinement.refine_loss_location(particles)

######################################################
# Compute Touschek lifetime
######################################################
# Keep lost particles only
particles = particles.filter(particles.state == 0)
# Compute total Touschek loss rate
loss_rate = sum(particles.weight)
# Compute Touschek lifetime
touschek_lifetime = bunch_population / loss_rate

######################################################
# Plot: Toy ring Touschek loss map
######################################################
circumference = line.get_length()
binwidth = 0.1 # m

plt.title(f'Toy ring Touschek loss map (Touschek lifetime: {touschek_lifetime/60:.2f} min)')
plt.hist(particles.s, bins=np.arange(0, circumference + binwidth, binwidth), weights=particles.weight*1e-3)
plt.xlabel('s [m]')
plt.ylabel('Loss rate [kHz]')
plt.grid()
plt.show()
Loading