Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4a2a5b0
First attempt at xtrack implementation
jgray-19 Jul 2, 2025
269276b
Add xtrack to the toml
jgray-19 Jul 2, 2025
d4e6081
Toml again?
jgray-19 Jul 2, 2025
f594135
Add setuptools to toml
jgray-19 Jul 2, 2025
0e0291e
License update (for xtrack)
jgray-19 Jul 2, 2025
f5453ea
More setuptools xsuite stuff
jgray-19 Jul 2, 2025
af2aa3a
Add xpart
jgray-19 Jul 2, 2025
24d9fde
Fix typo
jgray-19 Jul 2, 2025
ed3adb8
Fix floating-point representation in example_line fixture
jgray-19 Jul 3, 2025
a9e2965
Add platform check for xtrack kernel compilation in test_convert_xsuite
jgray-19 Jul 3, 2025
1804ac0
Enhance MAD-NG and XTRACK modules with improved error handling and ad…
jgray-19 Jul 3, 2025
18ca2e9
Refactor variable names for clarity in MAD-NG and enhance documentati…
jgray-19 Jul 3, 2025
f4ba3f0
Improve documentation in MAD-NG module with clearer descriptions and …
jgray-19 Jul 3, 2025
5f4ad71
Update turn_by_turn/madng.py
jgray-19 Jul 3, 2025
1be3165
Update pyproject.toml
jgray-19 Jul 3, 2025
1655685
Update turn_by_turn/xtrack.py
jgray-19 Jul 3, 2025
0383609
Remove unnecessary TYPE_CHECKING import and adjust type hint for conv…
jgray-19 Jul 3, 2025
612f762
Refactor tests and modules to improve consistency and clarity; update…
jgray-19 Jul 3, 2025
67b80a7
Update documentation and improve code clarity; disable display_versio…
jgray-19 Jul 4, 2025
46c0fc8
Enhance documentation for turn_by_turn; add usage examples for read_t…
jgray-19 Jul 4, 2025
f8278de
Remove load_tbt_data import from package namespace
jgray-19 Jul 4, 2025
3337d79
Fix ImportError handling for tfs package in write_tbt function
jgray-19 Jul 4, 2025
2bd2a14
Enhance docstring for example_line fixture to clarify its purpose and…
jgray-19 Jul 4, 2025
337a340
Some ruff formatting
jgray-19 Jul 5, 2025
c612290
Refactor documentation in index.rst and io.py for clarity and structu…
jgray-19 Jul 5, 2025
aff359a
Reorder import statements in __init__.py for consistency
jgray-19 Jul 5, 2025
d6dfba0
minor stuff
JoschD Jul 8, 2025
24cc58d
added API header
JoschD Jul 8, 2025
1d0b150
Improve formatting in test_xtrack.py and xtrack_line.py.
jgray-19 Jul 8, 2025
283b751
Refactor particle ID handling in convert_to_tbt for clarity and consi…
jgray-19 Jul 8, 2025
49f7d04
Clarify type annotations in convert_to_tbt functions for consistency …
jgray-19 Jul 9, 2025
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
4 changes: 4 additions & 0 deletions doc/readers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,9 @@
:noindex:

.. automodule:: turn_by_turn.madng
:members:
:noindex:

.. automodule:: turn_by_turn.xtrack
:members:
:noindex:
19 changes: 16 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ requires-python = ">=3.10"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
Expand All @@ -48,14 +47,26 @@ dependencies = [
"pandas >= 2.1",
"sdds >= 0.4",
"h5py >= 2.9",
"tfs-pandas >= 4.0.0", # for madng (could be an optional dependency)
]

[project.optional-dependencies]
madng = [
"tfs-pandas >= 4.0.0", # for reading MAD-NG files (Could do everything in memory with just pandas)
]

xtrack = [
"xtrack >= 0.84.7", # for xtrack
"setuptools >= 65", # for xtrack
"xpart >= 0.23.0", # for xtrack
]

test = [
"pytest>=7.0",
"pytest-cov>=2.9",
]
"turn_by_turn[madng]",
"turn_by_turn[xtrack]",
] # ???

doc = [
"sphinx >= 7.0",
"sphinx_rtd_theme >= 2.0",
Expand All @@ -64,6 +75,8 @@ doc = [
all = [
"turn_by_turn[test]",
"turn_by_turn[doc]",
"turn_by_turn[madng]",
"turn_by_turn[xtrack]",
]

[project.urls]
Expand Down
41 changes: 41 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import numpy as np
import pandas as pd
import pytest
from turn_by_turn.structures import TbtData, TransverseData

@pytest.fixture(scope="session")
def example_tbt():
"""
Returns a TbtData object with the original simulation data.
This fixture is session-scoped and will only be created once per test run.
"""
names = np.array(["BPM1", "BPM3", "BPM2"])
# First BPM
bpm1_p1_x = np.array([ 1e-3, 0.002414213831,-0.0009999991309])
bpm1_p1_y = np.array([-1e-3, 0.0004142133507, 0.001000000149])
bpm1_p2_x = np.array([-1e-3,-0.002414213831, 0.0009999991309])
bpm1_p2_y = np.array([ 1e-3,-0.0004142133507,-0.001000000149])

# Second BPM
bpm3_p1_x = np.array([ 0.002414213831,-0.0009999991309,-0.002414214191])
bpm3_p1_y = np.array([ 0.0004142133507, 0.001000000149,-0.0004142129907])
bpm3_p2_x = np.array([-0.002414213831, 0.0009999991309, 0.002414214191])
bpm3_p2_y = np.array([-0.0004142133507,-0.001000000149, 0.0004142129907])

# Third BPM
bpm2_p1_x = np.array([-0.0009999999503,-0.0004142138307, 0.0009999998012])
bpm2_p1_y = np.array([ 0.00100000029,-0.002414213351,-0.001000001159])
bpm2_p2_x = np.array([ 0.0009999999503, 0.0004142138307,-0.0009999998012])
bpm2_p2_y = np.array([-0.00100000029, 0.002414213351, 0.001000001159])

matrix = [
TransverseData( # first particle
X=pd.DataFrame(index=names, data=[bpm1_p1_x, bpm2_p1_x, bpm3_p1_x]),
Y=pd.DataFrame(index=names, data=[bpm1_p1_y, bpm2_p1_y, bpm3_p1_y]),
),
TransverseData( # second particle
X=pd.DataFrame(index=names, data=[bpm1_p2_x, bpm2_p2_x, bpm3_p2_x]),
Y=pd.DataFrame(index=names, data=[bpm1_p2_y, bpm2_p2_y, bpm3_p2_y]),
),
]
return TbtData(matrices=matrix, bunch_ids=[0, 1], nturns=3)
58 changes: 10 additions & 48 deletions tests/test_madng.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@

from datetime import datetime

import numpy as np
import pandas as pd
import pytest
from pathlib import Path

from tests.test_lhc_and_general import INPUTS_DIR, compare_tbt
from turn_by_turn import madng, read_tbt, write_tbt
from turn_by_turn.structures import TbtData, TransverseData
from turn_by_turn.structures import TbtData


def test_read_ng(_ng_file):
original = _original_simulation_data()

def test_read_ng(_ng_file: Path, example_tbt: TbtData):
# Check directly from the module
new = madng.read_tbt(_ng_file)
compare_tbt(original, new, no_binary=True)
compare_tbt(example_tbt, new, no_binary=True)

# Check from the main function
new = read_tbt(_ng_file, datatype="madng")
compare_tbt(original, new, no_binary=True)
compare_tbt(example_tbt, new, no_binary=True)

def test_write_ng(_ng_file, tmp_path):
original_tbt = _original_simulation_data()
def test_write_ng(_ng_file: Path, tmp_path: Path, example_tbt: TbtData):
original_tbt = example_tbt

# Write the data
from_tbt = tmp_path / "from_tbt.tfs"
Expand All @@ -40,47 +34,15 @@ def test_write_ng(_ng_file, tmp_path):
compare_tbt(original_tbt, new_tbt, no_binary=True)
assert original_tbt.date == new_tbt.date

def test_error_ng(_error_file):
def test_error_ng(_error_file: Path):
with pytest.raises(ValueError):
read_tbt(_error_file, datatype="madng")

# ---- Helpers ---- #
def _original_simulation_data() -> TbtData:
# Create a TbTData object with the original data
names = np.array(["BPM1", "BPM3", "BPM2"])
bpm1_p1_x = np.array([ 1e-3, 0.002414213831,-0.0009999991309])
bpm1_p1_y = np.array([-1e-3, 0.0004142133507, 0.001000000149])
bpm1_p2_x = np.array([-1e-3,-0.002414213831, 0.0009999991309])
bpm1_p2_y = np.array([ 1e-3,-0.0004142133507,-0.001000000149])

bpm2_p1_x = np.array([-0.0009999999503,-0.0004142138307, 0.0009999998012])
bpm2_p1_y = np.array([ 0.00100000029,-0.002414213351,-0.001000001159])
bpm2_p2_x = np.array([ 0.0009999999503, 0.0004142138307,-0.0009999998012])
bpm2_p2_y = np.array([-0.00100000029, 0.002414213351, 0.001000001159])

bpm3_p1_x = np.array([ 0.002414213831,-0.0009999991309,-0.002414214191])
bpm3_p1_y = np.array([ 0.0004142133507, 0.001000000149,-0.0004142129907])
bpm3_p2_x = np.array([-0.002414213831, 0.0009999991309, 0.002414214191])
bpm3_p2_y = np.array([-0.0004142133507,-0.001000000149, 0.0004142129907])

matrix = [
TransverseData( # first particle
X=pd.DataFrame(index=names, data=[bpm1_p1_x, bpm2_p1_x, bpm3_p1_x]),
Y=pd.DataFrame(index=names, data=[bpm1_p1_y, bpm2_p1_y, bpm3_p1_y]),
),
TransverseData( # second particle
X=pd.DataFrame(index=names, data=[bpm1_p2_x, bpm2_p2_x, bpm3_p2_x]),
Y=pd.DataFrame(index=names, data=[bpm1_p2_y, bpm2_p2_y, bpm3_p2_y]),
),
]
return TbtData(matrices=matrix, bunch_ids=[1, 2], nturns=3)


# ---- Fixtures ---- #
@pytest.fixture
def _ng_file(tmp_path):
def _ng_file(tmp_path: Path):
return INPUTS_DIR / "madng" / "fodo_track.tfs"

@pytest.fixture
def _error_file(tmp_path):
def _error_file(tmp_path: Path):
return INPUTS_DIR / "madng" / "fodo_track_error.tfs"
62 changes: 62 additions & 0 deletions tests/test_xtrack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import numpy as np
import xtrack as xt
import pytest
import sys

from tests.test_lhc_and_general import compare_tbt
from turn_by_turn.structures import TbtData
from turn_by_turn import xtrack
from turn_by_turn.io import convert_to_tbt

@pytest.mark.skipif(sys.platform == "win32", reason="xtrack kernel compilation not supported on Windows CI")
def test_convert_xsuite(example_line: xt.Line, example_tbt: TbtData):
# Build the particles
particles = example_line.build_particles(x=[1e-3,-1e-3], y=[-1e-3, 1e-3])

# Track the particles through the line
example_line.track(particles, num_turns=3)

# Convert to TbtData using xtrack
tbt_data = xtrack.convert_to_tbt(example_line)
compare_tbt(example_tbt, tbt_data, no_binary=True)

# Now convert using the generic function
tbt_data = convert_to_tbt(example_line, datatype="xtrack")
compare_tbt(example_tbt, tbt_data, no_binary=True)

# --- Fixtures ---- #
@pytest.fixture(scope="module")
def example_line():
lcell = 20.
f = lcell/np.sin(np.pi/4.)/4.
k = 1/f
nturns = 3
qf = xt.Multipole(knl=[0., k], ksl=[0.,0.])
qd = xt.Multipole(knl=[0.,-k], ksl=[0.,0.])
drift = xt.Drift(length=10.)
line = xt.Line(
elements=[
xt.ParticlesMonitor(start_at_turn=0, stop_at_turn=nturns, num_particles=2),
qf, drift,
qd, drift,
qf, drift,
xt.ParticlesMonitor(start_at_turn=0, stop_at_turn=nturns, num_particles=2),
qd, drift,
qf, drift,
qd, drift,
xt.ParticlesMonitor(start_at_turn=0, stop_at_turn=nturns, num_particles=2),
],
element_names=[
'BPM1',
'qf_0', 'drift_0',
'qd_0', 'drift_1',
'qf_1', 'drift_2',
'BPM3', # Deliberately not in order to test the conversion
'qd_1', 'drift_3',
'qf_2', 'drift_4',
'qd_2', 'drift_5',
'BPM2'
]
)
line.particle_ref = xt.Particles(p0c=1e9, q0=1., mass0=xt.ELECTRON_MASS_EV)
return line
4 changes: 3 additions & 1 deletion turn_by_turn/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Exposes TbtData, read_tbt and write_tbt directly in the package's namespace."""
from .io import read_tbt, write_tbt
from .io import read_tbt, write_tbt, convert_to_tbt, load_tbt_data
from .structures import TbtData, TransverseData

__title__ = "turn_by_turn"
Expand All @@ -13,6 +13,8 @@
# aliases
write = write_tbt
read = read_tbt
convert = convert_to_tbt
load = load_tbt_data

# Importing * is a bad practice and you should be punished for using it
__all__ = []
Loading