MTables
The MAD Tables (MTables) — also named Table File System (TFS) — are objects convenient to store, read and write a large amount of heterogeneous information organized as columns and header. The MTables are also containers that provide fast access to their rows, columns, and cells by referring to their indexes, or some values of the designated reference column, or by running iterators constrained with ranges and predicates.
The mtable
object is the root object of the TFS tables that store information relative to tables.
The mtable
module extends the typeid module with the is_mtable
function, which returns true
if its argument is a mtable
object, false
otherwise.
Attributes
The mtable
object provides the following attributes:
- type
A string specifying the type of the mtable (often) set to the name of the command that created it, like
survey
,track
ortwiss
. (default:'user'
).- title
A string specifying the title of the mtable (often) set to the attribute
title
of the command that created it. (default:'no-title'
).- origin
A string specifying the origin of the mtable. (default:
"MAD version os arch"
).- date
A string specifying the date of creation of the mtable. (default:
"day/month/year"
).- time
A string specifying the time of creation of the mtable. (default:
"hour:min:sec"
).- refcol
A string specifying the name of the reference column used to build the dictionary of the mtable, and to mangle values with counts. (default:
nil
).- header
A list specifying the augmented attributes names (and their order) used by default for the header when writing the mtable to files. Augmented meaning that the list is concatenated to the list held by the parent mtable during initialization. (default:
{'name', 'type', 'title', 'origin', 'date', 'time', 'refcol'}
).- column
A list specifying the augmented columns names (and their order) used by default for the columns when writing the mtable to files. Augmented meaning that the list is concatenated to the list held by the parent mtable during initialization. (default:
nil
).- novector
A logical specifying to not convert (
novector == true
) columns containing only numbers to vectors during the insertion of the second row. The attribute can also be a list specifying the columns names to remove from the specialization. If the list is empty ornovector ~= true
, all numeric columns will be converted to vectors, and support all methods and operations from the linear algebra module. (default:nil
).- owner
A logical specifying if an empty mtable is a view with no data (
owner ~= true
), or a mtable holding data (owner == true
). (default:nil
).- reserve
A number specifying an estimate of the maximum number of rows stored in the mtable. If the value is underestimated, the mtable will still expand on need. (default:
8
).
Warning: the following private and read-only attributes are present in all mtables and should never be used, set or changed; breaking this rule would lead to an undefined behavior:
- __dat
A table containing all the private data of mtables.
- __seq
A sequence attached to the mtable by the
survey
andtrack
commands and used by the methods receiving a reference to an element as argument. (default:nil
).- __cycle
A reference to the row registered with the
:cycle
method. (default:nil
).
Methods
The mtable
object provides the following methods:
- nrow
A method
()
returning the number of rows in the mtable.- ncol
A method
()
returning the number of columns in the mtable.- ngen
A method
()
returning the number of columns generators in the mtable. The number of columns with data is given by:ncol() - :ngen()
.- colname
A method
(idx)
returning the string name of theidx
-th column in the mtable ornil
.- colnames
A method
([lst])
returning the listlst
(default:{}
) filled with all the columns names of the mtable.- index
A method
(idx)
returning a positive index, ornil
. Ifidx
is negative, it is reflected versus the size of the mtable, e.g.-1
becomes#self
, the index of the last row.- name_of
A method
(idx, [ref])
returning a string corresponding to the (mangled) value from the reference column of the row at the indexidx
, ornil
. A row value appearing more than once in the reference column will be mangled with an absolute count, e.g.mq[3]
, or a relative count versus the reference row determined by:index_of(ref)
, e.g.mq{-2}
.- index_of
A method
(a, [ref], [dir])
returning a number corresponding to the positive index of the row determined by the first argument ornil
. Ifa
is a number (or a string representing a number), it is interpreted as the index of the row and returned as a second number. Ifa
is a string, it is interpreted as the (mangled) value of the row in the reference column as returned by:name_of
. Finally,a
can be a reference to an element to search for if the mtable has both, an attached sequence, and a column named'eidx'
mapping the indexes of the elements to the attached sequence. [1] The argumentref
(default:nil
) specifies the reference row determined by:index_of(ref)
to use for relative indexes, for decoding mangled values with relative counts, or as the reference row to start searching from. The argumentdir
(default:1
) specifies the direction of the search with values1
(forward),-1
(backward), or0
(no direction), which correspond respectively to the rounding methodsceil
,floor
andround
from the lua math module.- range_of
A method
([rng], [ref], [dir])
returning three numbers corresponding to the positive indexes start and end of the range and its direction dir (default:1
), ornil
for an empty range. Ifrng
is omitted, it returns1
,#self
,1
, or#self
,1
,-1
ifdir
is negative. Ifrng
is a number or a string with no'/'
separator, it is interpreted as start and end, both determined by:index_of
. Ifrng
is a string containing the separator'/'
, it is split in two strings interpreted as start and end, both determined by:index_of
. Ifrng
is a list, it will be interpreted as { start, end,[ref]
,[dir]
}, both determined by:index_of
. The argumentsref
anddir
are forwarded to all invocations of:index_of
with a higher precedence than ones in the listrng
, and a runtime error is raised if the method returnsnil
, i.e. to disambiguate between a valid empty range and an invalid range.- length_of
A method
([rng], [ntrn], [dir])
returning a number specifying the length of the range optionally includingntrn
extra turns (default:0
), and calculated from the indexes returned by:range_of([rng], nil, [dir])
.- get
A method
(row, col, [cnt])
returning the value stored in the mtable at the cell(row,col)
, ornil
. Ifrow
is a not a row index determined by:index(row)
, it is interpreted as a (mangled) value to search in the reference column, taking into account the countcnt
(default:1
). Ifcol
is not a column index, it is interpreted as a column name.- set
A method
(row, col, val, [cnt])
returning the mtable itself after updating the cell(row,col)
to the valueval
, or raising an error if the cell does not exist. Ifrow
is a not a row index determined by:index(row)
, it is interpreted as a (mangled) value to search in the reference column, taking into account the countcnt
(default:1
). Ifcol
is not a column index, it is interpreted as a column name.- getcol
A method
(col)
returning the columncol
, ornil
. Ifcol
is not a column index, it is interpreted as a column name.- setcol
A method
(col, val)
returning the mtable itself after updating the columncol
with the values ofval
, or raising an error if the column does not exist. Ifcol
is not a column index, it is interpreted as a column name. If the column is a generator, so must beval
or an error will be raised. If the column is not a generator andval
is a callable(ri)
, it will be invoked with the row indexri
as its sole argument, using its returned value to update the column cell. Otherwiseval
must be an iterable or an error will be raised. If the column is already a specialized vector, the iterable must provide enough numbers to fill it entirely asnil
is not a valid value.- inscol
A method
([ref], col, val, [nvec])
returning the mtable itself after inserting the column dataval
with the string namecol
at indexref
(default::ncol()+1
). Ifref
is not a column index, it is interpreted as a column name. Ifval
is a callable(ri)
, it will be added as a column generator. Otherwiseval
must be an iterable or an error will be raised. The iterable will used to fill the new column that will be specialized to a vector if its first value is a number andnvec ~= true
(default:nil
).- addcol
A method
(col, val, [nvec])
equivalent to:inscol(nil, col, val, [nvec])
.- remcol
A method
(col)
returning the mtable itself after removing the columncol
, or raising an error if the column does not exist. Ifcol
is not a column index, it is interpreted as a column name.- rencol
A method
(col, new)
returning the mtable itself after renaming the columncol
to the stringnew
, or raising an error if the column does not exist. Ifcol
is not a column index, it is interpreted as a column name.- getrow
A method
(row, [ref])
returning the mappable (proxy) of the row determined by the method:index_of(row, [ref])
, ornil
.- setrow
A method
(row, val, [ref])
returning the mtable itself after updating the row at index determined by:index_of(row, [ref])
using the values provided by the mappableval
, which can be a list iterated as pairs of (index, value) or a set iterated as pairs of (key, value) with key being the column names, or a combination of the two. An error is raised if the column does not exist.- insrow
A method
(row, val, [ref])
returning the mtable itself after inserting a new row at index determined by:index_of(row, [ref])
and filled with the values provided by the mappableval
, which can be a list iterated as pairs of (index, value) or a set iterated as pairs of (key, value) with key being the column names or a combination of the two.- addrow
A method
(val)
equivalent to:insrow(#self+1, val)
.- remrow
A method
(row, [ref])
returning the mtable itself after removing the row determined by the method:index_of(row, [ref])
, or raising an error if the row does not exist.- swprow
A method
(row1, row2, [ref1], [ref2])
returning the mtable itself after swapping the content of the rows, both determined by the method:index_of(row, [ref])
, or raising an error if one of the row does not exist.- clrrow
A method
(row, [ref])
returning the mtable itself after clearing the row determined by the method:index_of(row, [ref])
, or raising an error if the row does not exist; where clearing the row means to set vector value to0
andnil
otherwise.- clear
A method
()
returning the mtable itself after clearing all the rows, i.e.#self == 0
, with an opportunity for new columns specialization.- iter
A method
([rng], [ntrn], [dir])
returning an iterator over the mtable rows. The optional range is determined by:range_of([rng], [dir])
, optionally includingntrn
turns (default:0
). The optional directiondir
specifies the forward1
or the backward-1
direction of the iterator. Ifrng
is not provided and the mtable is cycled, the start and end indexes are determined by:index_of(self.__cycle)
. When used with a genericfor
loop, the iterator returns at each rows the index and the row mappable (proxy).- foreach
A method
(act, [rng], [sel], [not])
returning the mtable itself after applying the actionact
on the selected rows. Ifact
is a set representing the arguments in the packed form, the missing arguments will be extracted from the attributesaction
,range
,select
anddefault
. The actionact
must be a callable(row, idx)
applied to a row passed as first argument and its index as second argument. The optional range is used to generate the loop iterator:iter([rng])
. The optional selectorsel
is a callable(row, idx)
predicate selecting eligible rows for the action from the row itself passed as first argument and its index as second argument. The selectorsel
can be specified in other ways, see row selections for details. The optional logicalnot
(default:false
) indicates how to interpret default selection, as all or none, depending on the semantic of the action. [2]- select
A method
([rng], [sel], [not])
returning the mtable itself after selecting the rows using:foreach(sel_act, [rng], [sel], [not])
. By default mtable have all their rows deselected, the selection being stored as boolean in the column at index0
and namedis_selected
.- deselect
A method
([rng], [sel], [not])
returning the mtable itself after deselecting the rows using:foreach(desel_act, [rng], [sel], [not])
. By default mtable have all their rows deselected, the selection being stored as boolean in the column at index0
and namedis_selected
.- filter
A method
([rng], [sel], [not])
returning a list containing the positive indexes of the rows determined by:foreach(filt_act, [rng], [sel], [not])
, and its size.- insert
A method
(row, [rng], [sel])
returning the mtable itself after inserting the rows in the listrow
at the indexes determined by:filter([rng], [sel], true)
. If the arguments are passed in the packed form, the extra attributerows
will be used as a replacement for the argumentrow
, and if the attributewhere="after"
is defined then the rows will be inserted after the selected indexes. The insertion scheme depends on the number \(R\) of rows in the listrow
versus the number \(S\) of rows selected by:filter
; \(1\times 1\) (one row inserted at one index), \(R\times 1\) (\(R\) rows inserted at one index), \(1\times S\) (one row inserted at \(S\) indexes) and \(R\times S\) (\(R\) rows inserted at \(S\) indexes). Hence, the insertion schemes insert respectively \(1\), \(R\), \(S\), and \(\min(R, S)\) rows.- remove
A method
([rng], [sel])
returning the mtable itself after removing the rows determined by:filter([rng], [sel], true)
.- sort
A method
(cmp, [rng], [sel])
returning the mtable itself after sorting the rows at the indexes determined by:filter([rng], [sel], true)
using the ordering callablecmp(row1, row2)
. The argumentsrow1
androw2
are mappable (proxies) referring to the current rows being compared and providing access to the columns values for the comparison. [3] The argumentcmp
can be specified in a compact ordering form as a string that will be converted to an ordering callable by the functionstr2cmp
from the utility module. For example, the string “-y,x” will be converted by the method to the following lambda\r1,r2 -> r1.y > r2.y or r1.y == r2.y and r1.x < r2.x
, wherey
andx
are the columns used to sort the mtable in descending (-) and ascending (+
) order respectively. The compact ordering form is not limited in the number of columns and avoids making mistakes in the comparison logic when many columns are involved.- cycle
A method
(a)
returning the mtable itself after checking thata
is a valid reference using:index_of(a)
, and storing it in the__cycle
attribute, itself erased by the methods editing the mtable like:insert
,:remove
or:sort
.- copy
A method
([name], [owner])
returning a new mtable from a copy ofself
, with the optionalname
and the optional attributeowner
set. If the mtable is a view, so will be the copy unlessowner == true
.- is_view
A method
()
returningtrue
if the mtable is a view over another mtable data,false
otherwise.- set_readonly
Set the mtable as read-only, including the columns and the rows proxies.
- read
A method
([filname])
returning a new instance ofself
filled with the data read from the file determined byopenfile(filename, 'r', {'.tfs','.txt','.dat'})
from the utility module. This method can read columns containing the data types nil, boolean, number, complex number, (numerical) range, and (quoted) string. The header can also contain tables saved as string and decoded with functionstr2tbl
from the utility module.- write
A method
([filname], [clst], [hlst], [rsel])
returning the mtable itself after writing its content to the file determined byopenfile(filename, 'w', {'.tfs', '.txt', '.dat'})
from the utility module. The columns to write and their order is determined byclst
orself.column
(default:nil
\(\equiv\) all columns). The attributes to write in the header and their order is determined byhlst
orself.header
. The logicalrsel
indicates to save all rows or only rows selected by the:select
method (rsel == true
). This method can write columns containing the data types nil, boolean, number, complex number, (numerical) range, and (quoted) string. The header can also contain tables saved as string and encoded with functiontbl2str
from the utility module.A method
([clst], [hlst], [rsel])
equivalent to:write(nil, [clst], [hlst], [rsel])
.- save_sel
A method
([sel])
saving the rows selection to the optional iterablesel
(default:{}
) and return it.- restore_sel
A method
(sel)
restoring the rows selection from the iterablesel
. The indexes ofsel
must match the indexes of the rows in the mtable.- make_dict
A method
([col])
returning the mtable itself after building the rows dictionnary from the values of the reference column determined bycol
(default:refcol
) for fast row access. Ifcol
is not a column index, it is interpreted as a column name except for the special name'none'
that disables the rows dictionnary and resetrefcol
tonil
.- check_mtbl
A method
()
checking the integrity of the mtable and its dictionary (if any), for debugging purpose only.
Metamethods
The mtable
object provides the following metamethods:
- __len
A metamethod
()
called by the length operator#
to return the number of rows in the mtable.- __add
A metamethod
(val)
called by the plus operator+
returning the mtable itself after appending the rowval
at its end, similiar to the:addrow
method.- __index
A metamethod
(key)
called by the indexing operator[key]
to return the value of an attribute determined by key. The key is interpreted differently depending on its type with the following precedence:A number is interpreted as a row index and returns an iterable on the row (proxy) or
nil
.Other key types are interpreted as object attributes subject to object model lookup.
If the value associated with key is
nil
, then key is interpreted as a column name and returns the column if it exists, otherwise…If key is not a column name, then key is interpreted as a value in the reference column and returns either an iterable on the row (proxy) determined by this value or an iterable on the rows (proxies) holding this non-unique value. [4]
Otherwise returns
nil
.
- __newindex
A metamethod
(key, val)
called by the assignment operator[key]=val
to create new attributes for the pairs (key, value). If key is a number or a value specifying a row in the reference column or a string specifying a column name, the following error is raised:"invalid mtable write access (use 'set' methods)"
- __init
A metamethod
()
called by the constructor to build the mtable from the column names stored in its list part and some attributes, likeowner
,reserve
andnovector
.- __copy
A metamethod
()
similar to the methodcopy
.
The following attribute is stored with metamethods in the metatable, but has different purpose:
- __mtbl
A unique private reference that characterizes mtables.
MTables creation
During its creation as an object, an mtable can defined its attributes as any object, and the list of its column names, which will be cleared after its initialization. Any column name in the list that is enclosed by braces is designated as the refererence column for the dictionnary that provides fast row indexing, and the attribute refcol
is set accordingly.
Some attributes are considered during the creation by the metamethod __init
, like owner
, reserve
and novector
, and some others are initialized with defined values like type
, title
, origin
, date
, time
, and refcol
. The attributes header
and column
are concatenated with the the parent ones to build incrementing list of attributes names and columns names used by default when writing the mtable to files, and these lists are not provided as arguments.
The following example shows how to create a mtable form a list of column names add rows:
local mtable in MAD
local tbl = mtable 'mytable' {
{'name'}, 'x', 'y' } -- column 'name' is the refcol
+ { 'p11', 1.1, 1.2 }
+ { 'p12', 2.1, 2.2 }
+ { 'p13', 2.1, 3.2 }
+ { 'p11', 3.1, 4.2 }
print(tbl.name, tbl.refcol, tbl:getcol'name')
-- display: mytable name mtable reference column: 0x010154cd10
Pitfall: When a column is named 'name'
, it must be explicitly accessed, e.g. with the :getcol
method, as the indexing operator []
gives the precedence to object’s attributes and methods. Hence, tbl.name
returns the table name 'mytable'
, not the column 'name'
.
Rows selections
The row selection in mtable use predicates in combination with iterators. The mtable iterator manages the range of rows where to apply the selection, while the predicate says if a row in this range is illegible for the selection. In order to ease the use of methods based on the :foreach
method, the selector predicate sel
can be built from different types of information provided in a set with the following attributes:
- selected
A boolean compared to the rows selection stored in column
'is_selected'
.- pattern
A string interpreted as a pattern to match the string in the reference column, which must exist, using
string.match
from the standard library, see Lua 5.2 §6.4 for details. If the reference column does not exist, it can be built using themake_dict
method.- list
An iterable interpreted as a list used to build a set and select the rows by their name, i.e. the built predicate will use
tbl[row.name]
as a logical, meaning that columnname
must exists. An alternate column name can be provided through the keycolname
, i.e. used astbl[row[colname]]
. If the iterable is a single item, e.g. a string, it will be converted first to a list.- table
A mappable interpreted as a set used to select the rows by their name, i.e. the built predicate will use
tbl[row.name]
as a logical, meaning that columnname
must exists. If the mappable contains a list or is a single item, it will be converted first to a list and its set part will be discarded.- kind
An iterable interpreted as a list used to build a set and select the rows by their kind, i.e. the built predicate will use
tbl[row.kind]
as a logical, meaning that columnkind
must exists. If the iterable is a single item, e.g. a string, it will be converted first to a list. This case is equivalent tolist
withcolname='kind'
.- select
A callable interpreted as the selector itself, which allows to build any kind of predicate or to complete the restrictions already built above.
All these attributes are used in the aforementioned order to incrementally build predicates that are combined with logical conjunctions, i.e. and
’ed, to give the final predicate used by the :foreach
method. If only one of these attributes is needed, it is possible to pass it directly in sel
, not as an attribute in a set, and its type will be used to determine the kind of predicate to build. For example, tbl:foreach(act, "^MB")
is equivalent to tbl:foreach{action=act, pattern="^MB"}
.
Indexes, names and counts
Indexing a mtable triggers a complex look up mechanism where the arguments will be interpreted in various ways as described in the metamethod __index
. A number will be interpreted as a relative row index in the list of rows, and a negative index will be considered as relative to the end of the mtable, i.e. -1
is the last row. Non-number will be interpreted first as an object key (can be anything), looking for mtable methods or attributes; then as a column name or as a row value in the reference column if nothing was found.
If a row exists but its value is not unique in the reference column, an iterable is returned. An iterable supports the length #
operator to retrieve the number of rows with the same value, the indexing operator []
waiting for a count \(n\) to retrieve the \(n\)-th row from the start with that value, and the iterator ipairs
to use with generic for
loops.
The returned iterable is in practice a proxy, i.e. a fake intermediate object that emulates the expected behavior, and any attempt to access the proxy in another manner should raise a runtime error.
Note: Compared to the sequence, the indexing operator []
and the method :index_of
of the mtable always interprets a number as a (relative) row index. To find a row from a \(s\)-position [m] in the mtable if the column exists, use the functions lsearch
or bsearch
(if they are monotonic) from the utility module.
The following example shows how to access to the rows through indexing and the iterable:
local mtable in MAD
local tbl = mtable { {'name'}, 'x', 'y' } -- column 'name' is the refcol
+ { 'p11', 1.1, 1.2 }
+ { 'p12', 2.1, 2.2 }
+ { 'p13', 2.1, 3.2 }
+ { 'p11', 3.1, 4.2 }
print(tbl[ 1].y) -- display: 1.2
print(tbl[-1].y) -- display: 4.2
print(#tbl.p11, tbl.p12.y, tbl.p11[2].y) -- display: 2 2.2 4.2
for _,r in ipairs(tbl.p11) do io.write(r.x," ") end -- display: 1.1 3.1
for _,v in ipairs(tbl.p12) do io.write(v, " ") end -- display: 'p12' 2.1 2.2
-- print name of point with name p11 in absolute and relative to p13.
print(tbl:name_of(4)) -- display: p11[2] (2nd p11 from start)
print(tbl:name_of(1, -2)) -- display: p11{-1} (1st p11 before p13)
The last two lines of code display the name of the same row but mangled with absolute and relative counts.
Iterators and ranges
Ranging a mtable triggers a complex look up mechanism where the arguments will be interpreted in various ways as described in the method :range_of
, itself based on the methods :index_of
and :index
. The number of rows selected by a mtable range can be computed by the :length_of
method, which accepts an extra number of turns to consider in the calculation.
The mtable iterators are created by the method :iter
, based on the method :range_of
as mentioned in its description and includes an extra number of turns as for the method :length_of
, and a direction 1
(forward) or -1
(backward) for the iteration.
The method :foreach
uses the iterator returned by :iter
with a range as its sole argument to loop over the rows where to apply the predicate before executing the action. The methods :select
, :deselect
, :filter
, :insert
, and :remove
are all based directly or indirectly on the :foreach
method. Hence, to iterate backward over a mtable range, these methods have to use either its list form or a numerical range. For example the invocation tbl:foreach(\r -> print(r.name), {-2, 2, nil, -1})
will iterate backward over the entire mtable excluding the first and last rows, equivalently to the invocation tbl:foreach(\r -> print(r.name), -2..2..-1)
.
The following example shows how to access to the rows with the :foreach
method:
local mtable in MAD
local tbl = mtable { {'name'}, 'x', 'y' }
+ { 'p11', 1.1, 1.2 }
+ { 'p12', 2.1, 2.2 }
+ { 'p13', 2.1, 3.2 }
+ { 'p11', 3.1, 4.2 }
local act = \r -> print(r.name, r.y)
tbl:foreach(act, -2..2..-1)
-- display: p13 3.2
! p12 2.2
tbl:foreach(act, "p11[1]/p11[2]")
-- display: p11 1.2
! p12 2.2
! p13 3.2
! p11 4.2
tbl:foreach{action=act, range="p11[1]/p13"}
-- display: p11 1.2
! p12 2.2
! p13 3.2
tbl:foreach{action=act, pattern="[^1]$"}
-- display: p12 2.2
! p13 3.2
local act = \r -> print(r.name, r.y, r.is_selected)
tbl:select{pattern="p.1"}:foreach{action=act, range="1/-1"}
-- display: p11 1.2 true
! p12 2.2 nil
! p13 3.2 nil
! p11 4.2 true
Examples
Creating a MTable
The following example shows how the track
command, i.e. self
hereafter, creates its MTable:
local header = { -- extra attributes to save in track headers
'direction', 'observe', 'implicit', 'misalign', 'deltap', 'lost' }
local function make_mtable (self, range, nosave)
local title, dir, observe, implicit, misalign, deltap, savemap in self
local sequ, nrow = self.sequence, nosave and 0 or 16
return mtable(sequ.name, { -- keep column order!
type='track', title=title, header=header,
direction=dir, observe=observe, implicit=implicit, misalign=misalign,
deltap=deltap, lost=0, range=range, reserve=nrow, __seq=sequ,
{'name'}, 'kind', 's', 'l', 'id', 'x', 'px', 'y', 'py', 't', 'pt',
'slc', 'turn', 'tdir', 'eidx', 'status', savemap and '__map' or nil })
end
Extending a MTable
The following example shows how to extend the MTable created by a twiss
command with the elements tilt, angle and integrated strengths from the attached sequence:
-- The prelude creating the sequence seq is omitted.
local tws = twiss { sequence=seq, method=4, cofind=true }
local is_integer in MAD.typeid
tws:addcol('angle', \ri => -- add angle column
local idx = tws[ri].eidx
return is_integer(idx) and tws.__seq[idx].angle or 0 end)
:addcol('tilt', \ri => -- add tilt column
local idx = tws[ri].eidx
return is_integer(idx) and tws.__seq[idx].tilt or 0 end)
for i=1,6 do -- add kil and kisl columns
tws:addcol('k'..i-1..'l', \ri =>
local idx = tws[ri].eidx
if not is_integer(idx) then return 0 end -- implicit drift
local elm = tws.__seq[idx]
return (elm['k'..i-1] or 0)*elm.l + ((elm.knl or {})[i] or 0)
end)
:addcol('k'..i-1..'sl', \ri =>
local idx = tws[ri].eidx
if not is_integer(idx) then return 0 end -- implicit drift
local elm = tws.__seq[idx]
return (elm['k'..i-1..'s'] or 0)*elm.l + ((elm.ksl or {})[i] or 0)
end)
end
local cols = {'name', 'kind', 's', 'l', 'angle', 'tilt',
'x', 'px', 'y', 'py', 't', 'pt',
'beta11', 'beta22', 'alfa11', 'alfa22', 'mu1', 'mu2', 'dx', 'ddx',
'k1l', 'k2l', 'k3l', 'k4l', 'k1sl', 'k2sl', 'k3sl', 'k4sl'}
tws:write("twiss", cols) -- write header and columns to file twiss.tfs
Hopefully, the physics module provides the function melmcol(mtbl, cols)
to achieve the same task easily:
-- The prelude creating the sequence seq is omitted.
local tws = twiss { sequence=seq, method=4, cofind=true }
-- Add element properties as columns
local melmcol in MAD.gphys
local melmcol(tws, {'angle', 'tilt', 'k1l' , 'k2l' , 'k3l' , 'k4l',
'k1sl', 'k2sl', 'k3sl', 'k4sl'})
-- write TFS table
tws:write("twiss", {
'name', 'kind', 's', 'l', 'angle', 'tilt',
'x', 'px', 'y', 'py', 't', 'pt',
'beta11', 'beta22', 'alfa11', 'alfa22', 'mu1', 'mu2', 'dx', 'ddx',
'k1l', 'k2l', 'k3l', 'k4l', 'k1sl', 'k2sl', 'k3sl', 'k4sl'})
Footnotes