Working with tables
Several Xsuite methods return information in the form of Table objects. Examples
of such methods are xtrack.Line.get_table(), xtrack.Line.twiss(),
xtrack.Line.survey(), xtrack.Line.vars.get_table(),
xtrack.Environment.vars.get_table().
See Table class in the Reference guide for the base table API.
Table objects offer several capabilities to access data, for example by selecting rows and columns in various ways. This is illustrated in the following example:
import numpy as np
import xtrack as xt
pi = np.pi
lbend = 3
# Build a simple ring
env = xt.Environment()
line = env.new_line(components=[
env.new('mqf.1', xt.Quadrupole, length=0.3, k1=0.1),
env.new('d1.1', xt.Drift, length=1),
env.new('mb1.1', xt.Bend, length=lbend, angle=pi / 2),
env.new('d2.1', xt.Drift, length=1),
env.new('mqd.1', xt.Quadrupole, length=0.3, k1=-0.7),
env.new('d3.1', xt.Drift, length=1),
env.new('mb2.1', xt.Bend, length=lbend, angle=pi / 2),
env.new('d4.1', xt.Drift, length=1),
env.new('mqf.2', xt.Quadrupole, length=0.3, k1=0.1),
env.new('d1.2', xt.Drift, length=1),
env.new('mb1.2', xt.Bend, length=lbend, angle=pi / 2),
env.new('d2.2', xt.Drift, length=1),
env.new('mqd.2', xt.Quadrupole, length=0.3, k1=-0.7),
env.new('d3.2', xt.Drift, length=1),
env.new('mb2.2', xt.Bend, length=lbend, angle=pi / 2),
env.new('d4.2', xt.Drift, length=1),
])
line.set_particle_ref('proton', p0c=1.2e9)
# Get the twiss table of the line with the strengths
tab = line.twiss4d(strengths=True)
# The table can be printed
tab.show()
# prints:
#
# name element_type s betx bety ...
# mqf.1 Quadrupole 0 1.27738 4.79104
# d1.1 Drift 0.3 1.27738 4.79104
# mb1.1 Bend 1.3 2.26749 5.20838
# d2.1 Drift 4.3 2.88391 8.99176
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# d3.1 Drift 5.6 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# d4.1 Drift 9.6 2.26749 5.20838
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# d1.2 Drift 10.9 1.27738 4.79104
# mb1.2 Bend 11.9 2.26749 5.20838
# d2.2 Drift 14.9 2.88391 8.99176
# mqd.2 Quadrupole 15.9 1.72481 11.0967
# d3.2 Drift 16.2 1.72481 11.0967
# mb2.2 Bend 17.2 2.88391 8.99176
# d4.2 Drift 20.2 2.26749 5.20838
# _end_point 21.2 1.27738 4.79104
# Single values can be accessed using the column name and the row name. For example:
tab['s', 'mb2.1'] # is 6.6
# Entire columns can be accessed using the column name. For example:
tab['s'] # is [0.0, 0.3, 1.3, 4.3, 5.3, 5.6, 6.6, 9.6, 10.6, 10.9, 11.9, ...
# The `.cols` attribute can be used to access multiple columns (the output is
# a Table object). For example:
tab.cols['betx bety alfx alfy']
# returns:
#
# TwissTable: 17 rows, 5 cols
# name betx bety alfx alfy
# mqf.1 1.27738 4.79104 0.0997349 0.103198
# d1.1 1.27738 4.79104 -0.0997349 -0.103198
# mb1.1 2.26749 5.20838 -0.890376 -0.314144
# d2.1 2.88391 8.99176 0.890376 -0.946981
# mqd.1 1.72481 11.0967 0.268732 -1.15793
# d3.1 1.72481 11.0967 -0.268732 1.15793
# mb2.1 2.88391 8.99176 -0.890376 0.946981
# d4.1 2.26749 5.20838 0.890376 0.314144
# mqf.2 1.27738 4.79104 0.0997349 0.103198
# d1.2 1.27738 4.79104 -0.0997349 -0.103198
# mb1.2 2.26749 5.20838 -0.890376 -0.314144
# d2.2 2.88391 8.99176 0.890376 -0.946981
# mqd.2 1.72481 11.0967 0.268732 -1.15793
# d3.2 1.72481 11.0967 -0.268732 1.15793
# mb2.2 2.88391 8.99176 -0.890376 0.946981
# d4.2 2.26749 5.20838 0.890376 0.314144
# _end_point 1.27738 4.79104 0.0997349 0.103198
# Simple expressions can be used in the cols
tab.cols['betx dx dx/sqrt(betx)']
# returns:
#
# TwissTable: 17 rows, 4 cols
# name betx dx dx/sqrt(betx)
# mqf.1 1.27738 2.27721 2.01486
# d1.1 1.27738 2.27721 2.01486
# mb1.1 2.26749 2.24303 1.48958
# d2.1 2.88391 1.84457 1.08619
# mqd.1 1.72481 1.67012 1.27168
# d3.1 1.72481 1.67012 1.27168
# mb2.1 2.88391 1.84457 1.08619
# d4.1 2.26749 2.24303 1.48958
# mqf.2 1.27738 2.27721 2.01486
# d1.2 1.27738 2.27721 2.01486
# mb1.2 2.26749 2.24303 1.48958
# d2.2 2.88391 1.84457 1.08619
# mqd.2 1.72481 1.67012 1.27168
# d3.2 1.72481 1.67012 1.27168
# mb2.2 2.88391 1.84457 1.08619
# d4.2 2.26749 2.24303 1.48958
# _end_point 1.27738 2.27721 2.01486
# The .rows attribute can be used access selected rows
tab.rows[['mqf.1', 'mqd.1']]
# returns:
#
# TwissTable: 2 rows, 98 cols
# TwissTable: 2 rows, 98 cols
# name element_type s betx bety ...
# mqf.1 Quadrupole 0 1.27738 4.79104
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# Regular expressions can be used to select rows
tab.rows['mb.*']
# returns:
#
# TwissTable: 4 rows, 98 cols
# name element_type s betx bety ...
# mb1.1 Bend 1.3 2.26749 5.20838
# mb2.1 Bend 6.6 2.88391 8.99176
# mb1.2 Bend 11.9 2.26749 5.20838
# mb2.2 Bend 17.2 2.88391 8.99176
# Elements can be selected by type using the match search (applicable to any column)
tab.rows.match(element_type='Quadrupole')
# returns:
#
# TwissTable: 4 rows, 98 cols
# name element_type s betx bety ...
# mqf.1 Quadrupole 0 1.27738 4.79104
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# mqd.2 Quadrupole 15.9 1.72481 11.0967
# rows.match supports regular expressions
tab.rows.match(element_type='Quad.*|Be.*')
# returns:
# TwissTable: 8 rows, 98 cols
# name element_type s betx bety ...
# mqf.1 Quadrupole 0 1.27738 4.79104
# mb1.1 Bend 1.3 2.26749 5.20838
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# mb1.2 Bend 11.9 2.26749 5.20838
# mqd.2 Quadrupole 15.9 1.72481 11.0967
# mb2.2 Bend 17.2 2.88391 8.99176
# rows.match_not can be used to select rows not matching a given condition.
# For example to select all elements that are not drifts:
tab.rows.match_not(element_type='Drift')
#
# TwissTable: 9 rows, 98 cols
# name element_type s betx bety ...
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# d3.1 Drift 5.6 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# d4.1 Drift 9.6 2.26749 5.20838
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# d1.2 Drift 10.9 1.27738 4.79104
# mb1.2 Bend 11.9 2.26749 5.20838
# d2.2 Drift 14.9 2.88391 8.99176
# mqd.2 Quadrupole 15.9 1.72481 11.0967
# A section of the table can be selected using names
tab.rows['mqd.1':'mqd.2']
# returns:
#
# TwissTable: 9 rows, 98 cols
# name element_type s betx bety ...
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# d3.1 Drift 5.6 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# d4.1 Drift 9.6 2.26749 5.20838
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# d1.2 Drift 10.9 1.27738 4.79104
# mb1.2 Bend 11.9 2.26749 5.20838
# d2.2 Drift 14.9 2.88391 8.99176
# mqd.2 Quadrupole 15.9 1.72481 11.0967
# A section of the table can be selected using any given column. For example,
# to select all elements between s=3 and s=7:
tab.rows[3.0:7.0:'s']
# returns:
#
# TwissTable: 4 rows, 98 cols
# name element_type s betx bety ...
# d2.1 Drift 4.3 2.88391 8.99176
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# d3.1 Drift 5.6 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# A section of the table can be selected using indexes relative one element
# (e.g. to get from three elements upstream of 'mqd.1' to two elements
# downstream of 'mb2.1')
tab.rows['mqd.1<<3':'mb2.1>>2']
# returns:
#
# TwissTable: 8 rows, 98 cols
# name element_type s betx bety ...
# d1.1 Drift 0.3 1.27738 4.79104
# mb1.1 Bend 1.3 2.26749 5.20838
# d2.1 Drift 4.3 2.88391 8.99176
# mqd.1 Quadrupole 5.3 1.72481 11.0967
# d3.1 Drift 5.6 1.72481 11.0967
# mb2.1 Bend 6.6 2.88391 8.99176
# d4.1 Drift 9.6 2.26749 5.20838
# mqf.2 Quadrupole 10.6 1.27738 4.79104
# As rows and column selectors return Table objects they can be chained for example
# in the following we select rows in the range 'd1.1'-'d2.2', which are not of type Drift,
# and that match the regular expression 'mb.*' and we select the columns `betx` and `bety`:
tab.rows['d1.1':'d2.2'].rows.match_not(element_type='Drift').rows.match(name='mb.*').cols['betx bety']
# returns:
#
# TwissTable: 3 rows, 3 cols
# name betx bety
# mb1.1 2.26749 5.20838
# mb2.1 2.88391 8.99176
# mb1.2 2.26749 5.20838
# Complete source: xtrack/examples/toy_ring/004b_work_with_table.py
Writing tables in TFS format
Tables can be exported to TFS with xtrack.Table.to_tfs(), including the
Twiss tables returned by line.twiss(). The optional format_spec argument
controls which columns and scalar headers are written, their order and names,
and the numerical formatting. It accepts a Python dictionary, a JSON or TOML
file path, or a built-in preset. Calls without format_spec retain the default
TFS export behavior.
For the same selected values, types and row order, a specification defines the same output text across Xtrack releases. Additional table fields do not appear unless explicitly selected.
Selecting and formatting fields
A dictionary gives full control over the output. columns and scalars
are required ordered lists; only the listed fields are written. Scalars become
TFS @ headers. An empty list selects no fields in that category.
# A dictionary is the complete output contract. Only listed fields are emitted.
# name is mandatory; source_name defaults to name. Names are looked up before
# case conversion. The input table's rows and row order are preserved.
spec = {
'version': '1',
'case': 'upper',
'float_format': '.16e',
'columns': [
{'name': 'name', 'type': 'string'},
{'name': 's', 'type': 'float', 'format': '.9f'},
{'name': 'betx', 'type': 'float'},
{'name': 'bety', 'type': 'float'},
],
'scalars': [
{'name': 'TYPE', 'value': 'TWISS'},
{'name': 'Q1', 'source_name': 'qx', 'type': 'float', 'format': '.12f'},
{'name': 'Q2', 'source_name': 'qy', 'type': 'float', 'format': '.12f'},
],
}
tw.to_tfs('custom.tfs', format_spec=spec)
Each field requires name, which specifies the output name. source_name
is optional and defaults to name. For example, the entry for Q1 reads
tw.qx and writes a header named Q1. A scalar entry can instead provide
a constant value, as with TYPE above; value and source_name cannot
be combined.
The following options control formatting:
float_formatsets the default for floating-point columns and scalars. A field’sformatoverrides it. Python notation such as.9f(nine decimal places),.16e(scientific notation with sixteen decimal places) and.12g(twelve significant digits) is supported. The default is.16e.casedefaults to"preserve";"upper"and"lower"convert output field names and string values, including element names. Source lookup remains case-sensitive and happens before conversion. Numeric notation is unaffected.typeoptionally fixes a field’s TFS type:string,float,int,boolorcomplex. Explicit types are useful for long-lived specifications and empty object arrays. Otherwise the type is inferred from the data.A column’s
widthsets its minimum character width. The top-leveldefault_column_widthandnumeric_column_widthset minimum widths for all columns and numeric columns respectively (defaults: 0 and 16). Widths expand to fit the output; values are never truncated.
Rows retain their order in the input table. To select or reorder rows, use the
table’s row selection methods before exporting. No additional class, version,
timestamp or other metadata is added automatically. Missing fields, duplicate
output names, unknown keys and invalid formats raise errors before the output
file is opened. A specification cannot be combined with the legacy selection
or formatting arguments such as include, exclude or float_precision.
Saving a specification in a file
A specification can be saved as JSON and passed directly to to_tfs:
# JSON is convenient for saving a specification made in Python.
Path('custom_format.json').write_text(json.dumps(spec, indent=2), encoding='utf-8')
tw.to_tfs('custom_from_json.tfs', format_spec='custom_format.json')
TOML is convenient for hand-maintained files because it supports comments. The
same specification can be saved as tfs_format_spec.toml:
# The same specification as the dictionary in 004_tfs_format_spec.py.
version = "1"
case = "upper"
float_format = ".16e"
columns = [
{ name = "name", type = "string" },
{ name = "s", type = "float", format = ".9f" },
{ name = "betx", type = "float" },
{ name = "bety", type = "float" },
]
scalars = [
{ name = "TYPE", value = "TWISS" },
{ name = "Q1", source_name = "qx", type = "float", format = ".12f" },
{ name = "Q2", source_name = "qy", type = "float", format = ".12f" },
]
The file can then be used as follows:
# TOML is convenient for hand-maintained specifications with comments.
toml_spec = Path('tfs_format_spec.toml')
tw.to_tfs('custom_from_toml.tfs', format_spec=toml_spec)
Built-in format specifications
xt.format_spec provides ready-to-use specifications: optics_orbit_v1
for optics and closed orbit, optics_orbit_strengths_v1 to also include
integrated strengths, and madx_twiss_v1 for a MAD-X-style layout.
For a periodic Twiss table computed with strengths=True:
tw.to_tfs('optics_orbit.tfs', format_spec=xt.format_spec.optics_orbit_v1)
tw.to_tfs('optics_orbit_strengths.tfs',
format_spec=xt.format_spec.optics_orbit_strengths_v1)
tw.to_tfs('madx_layout.tfs', format_spec=xt.format_spec.madx_twiss_v1)