Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
dec4ebb
Added all the final changes from splineboris.
SiBuijs Feb 27, 2026
4b464d8
Removed making a csv file in the 001_fieldfitter_basic_usage.py
SiBuijs Feb 27, 2026
1aa77a2
Removed gitignore
SiBuijs Feb 27, 2026
d529149
Didn't need to remove the entire gitignore, just removed the lines th…
SiBuijs Feb 27, 2026
14505c7
Corrected to use test_data path
SiBuijs Feb 27, 2026
8e24191
Same as before for this example.
SiBuijs Feb 27, 2026
7fe1615
Removed writout to txt file.
SiBuijs Feb 27, 2026
f8d7767
Removed underscores where unneeded.
SiBuijs Feb 27, 2026
aeb4ec6
Refactor of polynomials. No longer convert between bases until the co…
SiBuijs Mar 2, 2026
99658c8
Added field evaluator to SplineBoris based on the python field evalua…
SiBuijs Mar 2, 2026
bb4a6c9
Small change
SiBuijs Mar 2, 2026
ff46ecc
Small change
SiBuijs Mar 3, 2026
a743d7d
Removed file-reading from the field fitter. User has to convert the d…
SiBuijs Mar 3, 2026
6aeaa55
Added plotting in the solenoid example, to demonstrate the Python fie…
SiBuijs Mar 3, 2026
9944b80
Removed some almost duplicate files.
SiBuijs Mar 3, 2026
687feb7
Small change.
SiBuijs Mar 3, 2026
e898d0a
Merge with most recent main.
SiBuijs Mar 3, 2026
a498e6f
Small change that caused errors with prebuild.
SiBuijs Mar 3, 2026
66683d2
Added (time) benchmarking to both solenoid and undulator.
SiBuijs Mar 3, 2026
c55d8eb
removed some file outputs
SiBuijs Mar 5, 2026
d29698a
The FieldFitter already had a field_tol parameter that acted as a noi…
SiBuijs Mar 5, 2026
4b8a8ec
Some places where field_tol was manually set, moved that to their res…
SiBuijs Mar 5, 2026
2ae1350
Splineboris now directly accepts left/right functino values and deriv…
SiBuijs Mar 5, 2026
cb5a88c
Major simplifications.
SiBuijs Mar 5, 2026
3edab6c
Removed ParamFormat class, just part of SplineBoris now.
SiBuijs Mar 5, 2026
c44d5e4
Changed some plots in the example.
SiBuijs Mar 18, 2026
beac8bf
Small changes to the solenoid
SiBuijs Mar 26, 2026
81d7dd5
Fix.
SiBuijs Mar 30, 2026
ae050ef
Trying to get SplineBorisSequence to work with new SplineBoris.
SiBuijs Mar 30, 2026
4321e2d
Moved SplineBorisSequence to _temp
SiBuijs Mar 30, 2026
14af5cd
Small changes
SiBuijs Mar 31, 2026
b8d8b8f
Spin test was not testing spin, does now and passes.
SiBuijs Mar 31, 2026
336fea2
Small changes.
SiBuijs Mar 31, 2026
70b7bfb
Small change, prebuilt kept crashing without default field values, so…
SiBuijs Mar 31, 2026
c80497f
Merge remote-tracking branch 'upstream/main' into merge-upstream-2026…
SiBuijs Mar 31, 2026
155f176
Merge branch 'merge-upstream-2026-03-31' into splineboris_cleaned
SiBuijs Mar 31, 2026
64a7d35
Some examples still used the old API, updated.
SiBuijs Mar 31, 2026
1a97e36
Still some API errors with the FieldFitter, and some erroneous print …
SiBuijs Mar 31, 2026
60bb3fd
Moved all splineboris-adjacent files to beam_elements/splineboris_src
SiBuijs Mar 31, 2026
647c48c
Put the polynomial conversion to C kernel.
SiBuijs Mar 31, 2026
6ff3ba9
Two tests still used par_list, fixed that.
SiBuijs Mar 31, 2026
232ffec
Reshuffled the examples a bit and added an API intro.
SiBuijs Mar 31, 2026
88a6f05
There was a duplicate test somehow.
SiBuijs Mar 31, 2026
04ffc4c
SplineBoris now no longer depends on s_start, that's kept by SplineBo…
SiBuijs Mar 31, 2026
ff2b9dc
Removed some comments.
SiBuijs Mar 31, 2026
6d98a6e
Removed some tracke s_starts from splineboris.
SiBuijs Mar 31, 2026
529a7db
Some renaming and small fixes.
SiBuijs Mar 31, 2026
6cd0b36
Added to_dict and from_dict, made naming convention more consistent, …
SiBuijs Apr 20, 2026
b8095d3
Few fixes, tests now pass.
SiBuijs Apr 20, 2026
477ea49
Changed the field evaluator to accept arrays now.
SiBuijs Apr 20, 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from scipy.constants import electron_volt
from scipy.constants import c as clight

env = xt.load('b075_2024.09.25.madx')
env = xt.load('sls.madx')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check that this uses the file in test_data folder

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked, was incorrect, corrected it.

line = env.ring
line.particle_ref = xt.Particles(energy0=2.7e9, mass0=xt.ELECTRON_MASS_EV)
line.configure_bend_model(num_multipole_kicks=20)
Expand Down
2 changes: 1 addition & 1 deletion examples/radiation_thick/000_sls_radiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from scipy.constants import electron_volt
from scipy.constants import c as clight

env = xt.load('b075_2024.09.25.madx')
env = xt.load('sls.madx')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, was incorrect, corrected it.

line = env.ring
line.particle_ref = xt.Particles(energy0=2.7e9, mass0=xt.ELECTRON_MASS_EV)
line.configure_bend_model(num_multipole_kicks=20)
Expand Down
116 changes: 116 additions & 0 deletions examples/splineboris/000a_sls_no_undulators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
SLS simulation without undulators.

This script loads the SLS MADX file, creates a copy of the ring without wigglers,
computes twiss with radiation integrals, and prints the results.
"""

import xtrack as xt
from pathlib import Path

# Particle reference
p0 = xt.Particles(mass0=xt.ELECTRON_MASS_EV, q0=1, p0c=2.7e9)

# Load SLS MADX file
madx_file = Path(__file__).resolve().parent.parent.parent / 'test_data' / 'sls' / 'sls.madx'
env = xt.load(str(madx_file))
line_sls = env.ring

# Create copy of ring without wigglers
line_no_wiggler = line_sls.copy(shallow=True)
env['ring_no_wiggler'] = line_no_wiggler

# Configure bend model
line_no_wiggler.configure_bend_model(core='mat-kick-mat')

# Set particle reference
line_no_wiggler.particle_ref = p0.copy()

# Compute twiss with radiation integrals
tw_no_wiggler = line_no_wiggler.twiss4d(radiation_integrals=True)

# Plotting:
import matplotlib.pyplot as plt
plt.close('all')
tw_no_wiggler.plot('x y')
tw_no_wiggler.plot('betx bety', 'dx dy')
tw_no_wiggler.plot('betx2 bety2')
plt.show()

#['name', 's', 'x', 'px', 'y', 'py', 'zeta', 'delta', 'ptau', 'W_matrix', 'kin_px', 'kin_py', 'kin_ps', 'kin_xprime',
# 'kin_yprime', 'env_name', 'betx', 'bety', 'alfx', 'alfy', 'gamx', 'gamy', 'dx', 'dpx', 'dy', 'dpy', 'dx_zeta', 'dpx_zeta',
# 'dy_zeta', 'dpy_zeta', 'betx1', 'bety1', 'betx2', 'bety2', 'alfx1', 'alfy1', 'alfx2', 'alfy2', 'gamx1', 'gamy1',
# 'gamx2', 'gamy2', 'mux', 'muy', 'muzeta', 'nux', 'nuy', 'nuzeta', 'phix', 'phiy', 'phizeta', 'dmux', 'dmuy', 'dzeta',
# 'bx_chrom', 'by_chrom', 'ax_chrom', 'ay_chrom', 'wx_chrom', 'wy_chrom', 'ddx', 'ddpx', 'ddy', 'ddpy', 'c_minus_re',
# 'c_minus_im', 'c_r1', 'c_r2', 'c_phi1', 'c_phi2', 'k0l', 'k1l', 'k2l', 'k3l', 'k4l', 'k5l', 'k0sl', 'k1sl', 'k2sl',
# 'k3sl', 'k4sl', 'k5sl', 'angle_rad', 'rot_s_rad', 'hkick', 'vkick', 'ks', 'length', '_angle_force_body', 'element_type', 'isthick', 'parent_name']

# Extract and print results
print("=" * 80)
print("SLS WITHOUT UNDULATORS")
print("=" * 80)
print(f"Tunes:")
print(f" qx = {tw_no_wiggler.qx:.4f}")
print(f" qy = {tw_no_wiggler.qy:.4f}")
print(f" qs = {tw_no_wiggler.qs:.4f}")
print()
print(f"Chromaticity:")
print(f" dqx = {tw_no_wiggler.dqx:.4f}")
print(f" dqy = {tw_no_wiggler.dqy:.4f}")
print()
print(f"Partition numbers:")
print(f" J_x = {tw_no_wiggler.rad_int_partition_number_x:.4f}")
print(f" J_y = {tw_no_wiggler.rad_int_partition_number_y:.4f}")
print(f" J_zeta = {tw_no_wiggler.rad_int_partition_number_zeta:.4f}")
print()
print(f"Damping constants per second:")
print(f" alpha_x = {tw_no_wiggler.rad_int_damping_constant_x_s:.4f}")
print(f" alpha_y = {tw_no_wiggler.rad_int_damping_constant_y_s:.4f}")
print(f" alpha_zeta = {tw_no_wiggler.rad_int_damping_constant_zeta_s:.4f}")
print()
print(f"Equilibrium emittances:")
print(f" eq_gemitt_x = {tw_no_wiggler.rad_int_eq_gemitt_x:.4f}")
print(f" eq_gemitt_y = {tw_no_wiggler.rad_int_eq_gemitt_y:.4f}")
print(f" eq_gemitt_zeta = {tw_no_wiggler.rad_int_eq_gemitt_zeta:.4f}")
print()
print(f"Energy loss per turn: {tw_no_wiggler.rad_int_eneloss_turn:.4f} eV")
print()
print(f"C^-: {tw_no_wiggler.c_minus:.4e}")
print()
print("=" * 80)

# Write results to file
output_dir = Path("/home/simonfan/cernbox/Documents/Presentations/Section_Meeting_Undulators")
output_dir.mkdir(parents=True, exist_ok=True)
output_file = output_dir / "SLS_WITHOUT_UNDULATORS.txt"

with open(output_file, 'w') as f:
f.write("=" * 80 + "\n")
f.write("SLS WITHOUT UNDULATORS\n")
f.write("=" * 80 + "\n")
f.write(f"Tunes:\n")
f.write(f" qx = {tw_no_wiggler.qx:.4e}\n")
f.write(f" qy = {tw_no_wiggler.qy:.4e}\n")
f.write(f" qs = {tw_no_wiggler.qs:.4e}\n")
f.write("\n")
f.write(f"Chromaticity:\n")
f.write(f" dqx = {tw_no_wiggler.dqx:.4e}\n")
f.write(f" dqy = {tw_no_wiggler.dqy:.4e}\n")
f.write("\n")
f.write(f"Partition numbers:\n")
f.write(f" J_x = {tw_no_wiggler.rad_int_partition_number_x:.4e}\n")
f.write(f" J_y = {tw_no_wiggler.rad_int_partition_number_y:.4e}\n")
f.write(f" J_zeta = {tw_no_wiggler.rad_int_partition_number_zeta:.4e}\n")
f.write("\n")
f.write(f"Damping constants per second:\n")
f.write(f" alpha_x = {tw_no_wiggler.rad_int_damping_constant_x_s:.4e}\n")
f.write(f" alpha_y = {tw_no_wiggler.rad_int_damping_constant_y_s:.4e}\n")
f.write(f" alpha_zeta = {tw_no_wiggler.rad_int_damping_constant_zeta_s:.4e}\n")
f.write("\n")
f.write(f"Equilibrium emittances:\n")
f.write(f" eq_gemitt_x = {tw_no_wiggler.rad_int_eq_gemitt_x:.4e}\n")
f.write(f" eq_gemitt_y = {tw_no_wiggler.rad_int_eq_gemitt_y:.4e}\n")
f.write(f" eq_gemitt_zeta = {tw_no_wiggler.rad_int_eq_gemitt_zeta:.4e}\n")
f.write("\n")
f.write(f"Energy loss per turn: {tw_no_wiggler.rad_int_eneloss_turn:.4e} eV\n")
f.write("=" * 80 + "\n")
124 changes: 124 additions & 0 deletions examples/splineboris/000b_sls_no_undulators_closed_spin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""
SLS simulation without undulators.

This script loads the SLS MADX file, creates a copy of the ring without wigglers,
computes twiss with radiation integrals, and prints the results.
"""

import xtrack as xt
from pathlib import Path

# Particle reference
p0 = xt.Particles(mass0=xt.ELECTRON_MASS_EV, q0=1, p0c=2.7e9)

# Load SLS MADX file
madx_file = Path(__file__).resolve().parent.parent.parent / 'test_data' / 'sls' / 'sls.madx'
env = xt.load(str(madx_file))
line_sls = env.ring

# Create copy of ring without wigglers
line_no_wiggler = line_sls.copy(shallow=True)
env['ring_no_wiggler'] = line_no_wiggler

# Configure bend model
line_no_wiggler.configure_bend_model(core='mat-kick-mat')

# Set particle reference
line_no_wiggler.particle_ref = p0.copy()

# Compute twiss with radiation integrals
tw_no_wiggler = line_no_wiggler.twiss4d(radiation_integrals=True, spin=True, polarization=True)

# Plotting:
import matplotlib.pyplot as plt
plt.close('all')
tw_no_wiggler.plot('x y')
tw_no_wiggler.plot('betx bety', 'dx dy')
tw_no_wiggler.plot('betx2 bety2')
tw_no_wiggler.plot('spin_x spin_y spin_z')
plt.show()

#['name', 's', 'x', 'px', 'y', 'py', 'zeta', 'delta', 'ptau', 'W_matrix', 'kin_px', 'kin_py', 'kin_ps', 'kin_xprime',
# 'kin_yprime', 'env_name', 'betx', 'bety', 'alfx', 'alfy', 'gamx', 'gamy', 'dx', 'dpx', 'dy', 'dpy', 'dx_zeta', 'dpx_zeta',
# 'dy_zeta', 'dpy_zeta', 'betx1', 'bety1', 'betx2', 'bety2', 'alfx1', 'alfy1', 'alfx2', 'alfy2', 'gamx1', 'gamy1',
# 'gamx2', 'gamy2', 'mux', 'muy', 'muzeta', 'nux', 'nuy', 'nuzeta', 'phix', 'phiy', 'phizeta', 'dmux', 'dmuy', 'dzeta',
# 'bx_chrom', 'by_chrom', 'ax_chrom', 'ay_chrom', 'wx_chrom', 'wy_chrom', 'ddx', 'ddpx', 'ddy', 'ddpy', 'c_minus_re',
# 'c_minus_im', 'c_r1', 'c_r2', 'c_phi1', 'c_phi2', 'k0l', 'k1l', 'k2l', 'k3l', 'k4l', 'k5l', 'k0sl', 'k1sl', 'k2sl',
# 'k3sl', 'k4sl', 'k5sl', 'angle_rad', 'rot_s_rad', 'hkick', 'vkick', 'ks', 'length', '_angle_force_body', 'element_type', 'isthick', 'parent_name']

# Extract and print results
print("=" * 80)
print("SLS WITHOUT UNDULATORS")
print("=" * 80)
print(f"Tunes:")
print(f" qx = {tw_no_wiggler.qx:.4f}")
print(f" qy = {tw_no_wiggler.qy:.4f}")
print(f" qs = {tw_no_wiggler.qs:.4f}")
print()
print(f"Chromaticity:")
print(f" dqx = {tw_no_wiggler.dqx:.4f}")
print(f" dqy = {tw_no_wiggler.dqy:.4f}")
print()
print(f"Partition numbers:")
print(f" J_x = {tw_no_wiggler.rad_int_partition_number_x:.4f}")
print(f" J_y = {tw_no_wiggler.rad_int_partition_number_y:.4f}")
print(f" J_zeta = {tw_no_wiggler.rad_int_partition_number_zeta:.4f}")
print()
print(f"Damping constants per second:")
print(f" alpha_x = {tw_no_wiggler.rad_int_damping_constant_x_s:.4f}")
print(f" alpha_y = {tw_no_wiggler.rad_int_damping_constant_y_s:.4f}")
print(f" alpha_zeta = {tw_no_wiggler.rad_int_damping_constant_zeta_s:.4f}")
print()
print(f"Equilibrium emittances:")
print(f" eq_gemitt_x = {tw_no_wiggler.rad_int_eq_gemitt_x:.4f}")
print(f" eq_gemitt_y = {tw_no_wiggler.rad_int_eq_gemitt_y:.4f}")
print(f" eq_gemitt_zeta = {tw_no_wiggler.rad_int_eq_gemitt_zeta:.4f}")
print()
print(f"Energy loss per turn: {tw_no_wiggler.rad_int_eneloss_turn:.4f} eV")
print()
print(f"C^-: {tw_no_wiggler.c_minus:.4e}")
print()
print(f"Spin polarization: {tw_no_wiggler.spin_polarization_eq:.4e}")
print()
print("=" * 80)

# Write results to file
output_dir = Path("/home/simonfan/cernbox/Documents/Presentations/Section_Meeting_Undulators")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path refers to your computer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected!

output_dir.mkdir(parents=True, exist_ok=True)
output_file = output_dir / "SLS_WITHOUT_UNDULATORS.txt"

with open(output_file, 'w') as f:
f.write("=" * 80 + "\n")
f.write("SLS WITHOUT UNDULATORS\n")
f.write("=" * 80 + "\n")
f.write(f"Tunes:\n")
f.write(f" qx = {tw_no_wiggler.qx:.4e}\n")
f.write(f" qy = {tw_no_wiggler.qy:.4e}\n")
f.write(f" qs = {tw_no_wiggler.qs:.4e}\n")
f.write("\n")
f.write(f"Chromaticity:\n")
f.write(f" dqx = {tw_no_wiggler.dqx:.4e}\n")
f.write(f" dqy = {tw_no_wiggler.dqy:.4e}\n")
f.write("\n")
f.write(f"Partition numbers:\n")
f.write(f" J_x = {tw_no_wiggler.rad_int_partition_number_x:.4e}\n")
f.write(f" J_y = {tw_no_wiggler.rad_int_partition_number_y:.4e}\n")
f.write(f" J_zeta = {tw_no_wiggler.rad_int_partition_number_zeta:.4e}\n")
f.write("\n")
f.write(f"Damping constants per second:\n")
f.write(f" alpha_x = {tw_no_wiggler.rad_int_damping_constant_x_s:.4e}\n")
f.write(f" alpha_y = {tw_no_wiggler.rad_int_damping_constant_y_s:.4e}\n")
f.write(f" alpha_zeta = {tw_no_wiggler.rad_int_damping_constant_zeta_s:.4e}\n")
f.write("\n")
f.write(f"Equilibrium emittances:\n")
f.write(f" eq_gemitt_x = {tw_no_wiggler.rad_int_eq_gemitt_x:.4e}\n")
f.write(f" eq_gemitt_y = {tw_no_wiggler.rad_int_eq_gemitt_y:.4e}\n")
f.write(f" eq_gemitt_zeta = {tw_no_wiggler.rad_int_eq_gemitt_zeta:.4e}\n")
f.write("\n")
f.write(f"Energy loss per turn: {tw_no_wiggler.rad_int_eneloss_turn:.4e} eV\n")
f.write("\n")
f.write(f"C^-: {tw_no_wiggler.c_minus:.4e}\n")
f.write("\n")
f.write(f"Spin polarization: {tw_no_wiggler.spin_polarization_eq:.4e}\n")
f.write("\n")
f.write("=" * 80 + "\n")
44 changes: 44 additions & 0 deletions examples/splineboris/001_fieldfitter_basic_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from pathlib import Path

# Simple local import so this file can be run directly (e.g. via IDE "Run" button)
from xtrack._temp.field_fitter import FieldFitter


'''
Basic usage of FieldFitter.

This script fits a field map and saves the fit parameters to a file.

It plots the fit results for each derivative order.
It also plots the integrated field along the longitudinal direction.

The raw data only has three transverse x positions, which means the highest order polynomial that we can fit is 2.
This also means that we can only incorporate up to the second derivative of the field into the fit (sextupole components).
'''

dz = 0.001 # Step size in the z (longitudinal) direction for numerical differentiation

here = Path(__file__).resolve().parent

# Standard 6-column format (X Y Z Bx By Bs)
# Use the shared test-data knot-map file
file_path = Path(__file__).resolve().parent.parent.parent / "test_data" / "sls" / "undulator_field_map.txt"

deg = 2

if __name__ == "__main__":
# Build and run the fitter (file path is parsed inline by FieldFitter)
fitter = FieldFitter(
raw_data=file_path,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still taking a path, let's use a DataFrame

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not Taylor the fitter to a specific format

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the "path acceptance" from the FieldFitter. Now only accepts DataFrames. The user has to convert their data to a DataFrame of the correct format.

xy_point=(0.0, 0.0),
distance_unit=dz,
min_region_size=10,
deg=deg,
)

fitter.fit()

for der in range(0, deg + 1):
fitter.plot_fields(der=der)

fitter.plot_integrated_fields()
Loading