Conversion
How to Run the Conversion Now
Currently the OCELOT Python EuXFEL sequences are generated from the "Component List", an Excel spreadsheet generated internally by the group leader as the "single source of truth" for what actually is in the tunnel. These files can be found here.
Clone the repository and install the Python package. Put the
component list you want to convert in src/euxfel/longlists, or use
one that is already there. The converter will look for a file called
conversion-config.yaml to drive the entire conversion process.
Either use the existing one, make a new one, or make a symlink with
the name conversion-config.yaml that points to one of the
explicitly named (i.e. dated) yaml files. Look at one of the other conversion
yaml files for guidance on how to write your own for a given component list.
Further details on the makeup of these conversion-config.yaml files is
follows here.
$ euxfel convert
This will run, and the output will automatically be written to
src/euxfel/subsequences/.
Changing OCELOT Lattice Versions
If you want to use a different version of the lattice, then the easiest way is to check out a different tagged release. Always use the latest version for a given model type:
To use v0.2 (at the time of writing the latest version) of the July 2024 model:
$ git checkout 0.2.0+componentlist.20240704
To use v0.2 of the January 2026 model:
$ git checkout 0.2.0+componentlist.20260121
API changes should be separate from lattice versions and the two packages should be functionally identical except for the lattice files.
Conversion Configuration
The conversion should be as transparent as possible, and absolutely no hand edits of the output Python modules should be necessary. To this end, the configuration is fully described in the file conversion-config.yaml. I'll try to explain some of the sections of this config file here.
The filename is chosen as:
component_list: component_list_2026.01.21.xls
Writer
Configure the writing of the output Python files with the writer
heading. Currently the only setting is writer_types_power_supplies
with which you can configure which element types have their power
supply IDs written.
writer:
write_types_power_supplies: ["Quadrupole", "Sextupole", "Octupole", "Cavity", "RBend", "SBend"]
Rows
Row based edits with rows header. Subheaders include skip for
skipping certain elements altogether based on TYPE, CLASS, GROUP and
NAME1 from the component list. edit provides for a crude update to
the component list row dictionary immediately prior to generating the
OCELOT component. The below skips all of the listed TYPE, CLASS
and GROUP, as well as setting all "CAX", "CAY", "CBX" and "CBY"
lengths to zero (these are the aircoils, which in pairs are directly
on top of each other in the component list, yet with non-zero length,
which is not permitted).
rows:
skip:
TYPE: ["BENDMARK", "RF", "CTBI", "CUX"]
CLASS: ["CRYO", "UNDPLACEH"]
GROUP: ["CRYO",
"VACUUM",
"MOVER",
"PHOTON",
"DUMP"]
NAME1: []
edit:
TYPE:
CAX: { LENGTH: 0.0 }
CAY: { LENGTH: 0.0 }
CBX: { LENGTH: 0.0 }
CBY: { LENGTH: 0.0 }
CLASS: {}
GROUP: {}
Sections
With sections header the modules themselves are defined. The basic
definition requires a name, for example I1D (which would then become
i1d.py), a sheet name from the Component List to use (not LONGLIST, in
order to ensure the correct extraction are powered for each path
through the machine), and two marker names from the Component List,
one for start and one for stop.
New Markers
For the effective inclusion of physics processes, it's necessary to
introduce additional markers. For example, the OCELOT simulation
starts at 3.2m after the cathode, but there is no marker at this point
in the component list. These can all be added with the new_markers
section. For example, in I1:
sections:
I1:
[...]
new_markers:
stop_astra: { s: 3.2 }
start_ocelot: { s: 3.2 }
lh_start: { reference: U74.49.I1, adjacent: before }
lh_stop: { reference: U74.49.I1, adjacent: after }
DUMP.CSR.START: { s: 38.789005 }
This introduces two markers at 3.2m and one at 38.789m (just befor the I1D dump dipole).
⚠️ Warning:: It is up to the user to ensure that these inserted markers are not placed inside of other elements! There is no checking done on this and it may fail cryptically.
Additionaly, the laser heater undulator U74.49.I1 is wrapped with
two markers, one immediately before and one immediately after in
order to attach the laser heater process correctly.
Additional Element Properties
In I1D it is necessary to introduce two additional element properties
which are missing in the component list. Firstly, the TDS must be
rotated by 90 degrees, and the undulator must be closed. This is done
with the extras section of the section definition:
sections:
I1:
[...]
extras:
TDSA.52.I1:
tilt: 1.570796327
U74.49.I1:
Kx: 1.294
Ky: 0
Rematching at Conversion Time
Finally, due to the focusing from the undulator, when the undulator is closed, the matching from the cathode will now be wrong. We need to rematch whilst converting to calculate the correct quadrupole strengths. The snippet below states to rematch at the given market with respect to the reference optics, using hte listed quadrupoles.
sections:
I1:
[...]
matching:
marker: MATCH.52.I1
quadrupoles:
- Q.37.I1
- Q.38.I1
- QI.46.I1
- QI.47.I1
- QI.50.I1
Inserting Additional Elements
The so-called XY-quadrupoles are not in the component list, but they are needed in order to get the correct optics. For example for the TL section, the XY-quadrupole is approximated with 190 slices of a horizontal combined function magnet and 10 slices of a vertical combined function magnet, interleaved with each other. The position is set with reference to the previous dipole, set to start 0.4724m past the end of BZ.1980.TLD.
sections:
[...]
Tl2TLD:
[...]
new_elements:
QK.1982.TL:
position: { reference: MBZ.1980d.TLD, s: 0.4724 }
type: SlicedElement
expression: "(19 * [xslice1982] + [yslice1982]) * 10"
elements:
xslice1982:
type: RBend
l: 0.005276
angle: 1.180477777265855e-05
k1: 0.090359600075815
yslice1982:
type: RBend
l: 0.005276
angle: -7.780804909999998e-05
k1: -0.090359600075815
tilt: 1.570796327
⚠️ Warning: the named refrence element must have no other elements between it and the newly inserted element. Otherwise it will most likely fail and it will fail mysteriously, most likely with cryptic complains about negative drift lengths! I never added special checks on this.
Currently no other element types besides the SlicedElement may be
inserted into the lattice at conversion time.