API Reference¶
Everything below is exported from the top-level palsparserpy package, so
pp.parse_and_expand_pals(...) reaches it after import palsparserpy as pp.
The tree objects¶
- class palsparserpy.YAMLNode(tree, node_id)[source]¶
Bases:
objectA reference to a single node within a
YAMLTree.Holding a
YAMLNodekeeps its tree alive. Node ids are invalidated if the tree is deleted.- tree¶
- id¶
- is_map()[source]¶
Whether this node is a MAP (a collection of key/value pairs).
A node is exactly one of MAP, sequence, or scalar; use this to decide before accessing children by key.
- Return type:
- is_sequence()[source]¶
Whether this node is a sequence (an ordered list of elements).
A node is exactly one of MAP, sequence, or scalar; use this to decide before accessing children by index.
- Return type:
- is_scalar()[source]¶
Whether this node is a scalar (a leaf holding a single string, number or boolean value).
Scalar nodes have no children and their value is read with
value,as_int(),as_float()oras_bool().- Return type:
- parent()[source]¶
The parent of this node. Raises
ValueErrorfor the root, which has no parent.- Return type:
- root()[source]¶
The root of the tree this node belongs to (this node itself if it is the root).
- Return type:
- child(index)[source]¶
The
index-th direct child of a MAP or sequence node, 0-based.Unlike
node[key], this reaches a MAP’s children by position, which is how a single-key map entry is opened without knowing its key.
- get(key, default=None)[source]¶
The child stored under
key, ordefaultif there is none.- Parameters:
key (str)
- __getitem__(key)[source]¶
node[key]looks up a direct child of a MAP by its string key;node[i]returns thei-th direct child of a MAP or sequence.Only direct children are searched (the lookup is not recursive). Raises
KeyErrorif no child has the given key – test withkey in nodeif it may be absent – andIndexErrorif the index is out of bounds.
- __contains__(key)[source]¶
Whether the MAP node has a direct child stored under
key. Only direct children are checked; the search is not recursive.
- __len__()[source]¶
The number of direct children: the number of key/value pairs in a MAP, or the number of elements in a sequence. Scalar nodes report 0.
- Return type:
- __iter__()[source]¶
Iterate the node’s children: a sequence yields its elements, a MAP yields its keys (as
dictdoes; useitems()for pairs), and a scalar yields nothing.- Return type:
- node_key()[source]¶
The key under which this node is stored in its parent MAP, or
Noneif it has none. Sequence elements and the tree root have no key.- Return type:
str | None
- property value: str¶
The scalar value of this node, as raw text.
Raises
ValueErrorif the node has no value (i.e. it is a MAP or a bare sequence). Useas_int(),as_float()oras_bool()for typed values.
- as_int()[source]¶
The scalar value parsed as an
int. Raises if the node is not a scalar or its text is not a valid integer.- Return type:
- as_float()[source]¶
The scalar value parsed as a
float. Raises if the node is not a scalar or its text is not a valid floating-point number.- Return type:
- as_bool()[source]¶
The scalar value parsed as a
bool.Accepts exactly the text
trueorfalse; any other value (or a non-scalar node) raisesValueError.- Return type:
- add_scalar(value, key=None, index=None)[source]¶
Add a scalar child to this node.
Pass
keyfor MAP parents; omit it for sequence elements.indexselects the 0-based position among the existing children; the default appends at the end.
- add_map(key=None, index=None)[source]¶
Add an empty MAP child to this node.
Pass
keyfor MAP parents; omit it for sequence elements.indexselects the 0-based position among the existing children; the default appends at the end.
- add_sequence(key=None, index=None)[source]¶
Add an empty sequence child to this node.
Pass
keyfor MAP parents; omit it for sequence elements.indexselects the 0-based position among the existing children; the default appends at the end.
- __setitem__(key, value)[source]¶
node[key] = valuesets or updates a scalar value in a MAP node.If
keyalready exists its value is updated; otherwise a new scalar child is appended.
- set_scalar(value)[source]¶
Set or replace the scalar value of this node.
Operates on an existing node in place; to set a value by key within a MAP (adding the key if absent), use
node[key] = valueinstead.- Parameters:
value (str)
- Return type:
None
- set_key(key)[source]¶
Set or replace the key under which this node is stored in its parent MAP. Only meaningful inside a MAP; sequence elements are keyless.
- Parameters:
key (str)
- Return type:
None
- remove()[source]¶
Remove this node, together with all of its descendants, from its parent.
After removal the
YAMLNodeis stale and must not be used again. Intended for non-root nodes; the root has no parent to be removed from.- Return type:
None
- deep_copy_node(src)[source]¶
Copy the type, key, value and all descendants of
srcinto this node, overwriting whatever it previously held. Works across trees.- Parameters:
src (YAMLNode)
- Return type:
None
- deep_copy_children(src, index=None)[source]¶
Copy all children of
srcinto this node at the 0-based positionindexamong the existing children; the default appends them at the end. Works across trees.
- to_yaml_string(exclude=())[source]¶
Emit this node and its descendants as a YAML string.
excludeis a key name, or a collection of key names, to be left out of the output: every MAP entry whose key matches, at any depth, is omitted along with its whole subtree. This is a display filter only – the node itself is never modified. For example, to print a lattice without the floor and reference subtrees:print(lat.to_yaml_string(exclude=["FloorP", "ReferenceP"]))
- write_yaml(filename, exclude=())[source]¶
Write the entire tree that contains this node to a YAML file.
Returns
Trueon success.excludeis a key name, or a collection of key names, to be left out of the file: every MAP entry whose key matches, at any depth, is omitted along with its whole subtree. The tree in memory is not modified. For example:lat.write_yaml("out.pals.yaml", exclude=["FloorP", "ReferenceP"])
- class palsparserpy.YAMLTree(handle)[source]¶
Bases:
objectOwns a C
YAMLTreeHandle. Freed automatically when the object is garbage collected. Do not use the handle after the tree has been freed.- handle¶
- exception palsparserpy.PALSParseError[source]¶
Bases:
ValueErrorA YAML document could not be parsed.
The message carries what the C library reported – for a syntax error, prefixed with the offending
line L, column C:– so the fault can be pinpointed instead of reported as a bare failure.
Parsing and building¶
- palsparserpy.parse_file(filename)[source]¶
Parse a YAML file from disk. Returns a node pointing to the tree root.
- Return type:
Function forms of the node operations¶
Each of these is the free-function spelling of the like-named
YAMLNode method above.
- palsparserpy.is_map(node)[source]¶
Whether
nodeis a MAP. SeeYAMLNode.is_map().
- palsparserpy.is_sequence(node)[source]¶
Whether
nodeis a sequence. SeeYAMLNode.is_sequence().
- palsparserpy.is_scalar(node)[source]¶
Whether
nodeis a scalar. SeeYAMLNode.is_scalar().
- palsparserpy.get_parent(node)[source]¶
The parent of
node. SeeYAMLNode.parent().
- palsparserpy.node_key(node)[source]¶
The key
nodeis stored under. SeeYAMLNode.node_key().
- palsparserpy.add_scalar(parent, value, key=None, index=None)[source]¶
Add a scalar child to
parent. SeeYAMLNode.add_scalar().
- palsparserpy.add_map(parent, key=None, index=None)[source]¶
Add an empty MAP child to
parent. SeeYAMLNode.add_map().
- palsparserpy.add_sequence(parent, key=None, index=None)[source]¶
Add an empty sequence child to
parent. SeeYAMLNode.add_sequence().
- palsparserpy.set_scalar(node, value)[source]¶
Set the scalar value of
node. SeeYAMLNode.set_scalar().
- palsparserpy.set_key(node, key)[source]¶
Set the key of
node. SeeYAMLNode.set_key().
- palsparserpy.remove(node)[source]¶
Remove
nodefrom its parent. SeeYAMLNode.remove().- Parameters:
node (YAMLNode)
- Return type:
None
- palsparserpy.deep_copy_node(dst, src)[source]¶
Copy
srcintodst. SeeYAMLNode.deep_copy_node().
- palsparserpy.deep_copy_children(dst, src, index=None)[source]¶
Copy the children of
srcintodst. SeeYAMLNode.deep_copy_children().
- palsparserpy.to_yaml_string(node, exclude=())[source]¶
Emit
nodeas YAML. SeeYAMLNode.to_yaml_string().
Lattices¶
- palsparserpy.parse_and_expand_pals(filename, root_lattice='', *, problems='print')[source]¶
Parse a PALS lattice file and return its five views.
Returns a
Latticesholding theoriginal,combined,expanded,full_expandedandadjunctviews together with the list of expansionproblems.- Parameters:
filename – Path to the top-level YAML lattice file.
root_lattice (str) –
Name of the lattice to expand. If empty (the default), the lattice to expand is chosen with the following priority:
the lattice named by the last
usestatement, orthe last lattice defined in the file if no
usestatement is present.
What to do with the list of problems found while expanding (undefined lattice, dangling element/line references, undefined
inherit/repeat/Forktargets, and expressions that could not be evaluated). One of:"print"(the default) – print the problems tostderr(nothing is printed when there are none);"none"– do nothing (no printing, no file);any other path – write the problems to that file, printing nothing. Those two names are reserved, so a report cannot be written to a file called
printornone.
- Return type:
The same problems handed to
problemsare also returned in theproblemsfield regardless of the reporting mode, so"none"still lets the caller inspect them programmatically. Each entry carries amessage, thepathit was found at, aseverityand anorigin; only aPROBLEM_INPUTcan be cleared by editing the lattice.The five tree views are:
original: the tree as read in, mapping each file (including any file it includes or loads) to its unparsed contents.combined: the tree with allincludedirectives resolved and spliced inline, and everyloadmerged in subnode by subnode.full_expanded: the selected lattice fully expanded, and nothing else – scalars substituted with their full definitions, everyrepeatunrolled, everyinheritmerged in, forks resolved,setcommands executed and ABSOLUTE controllers applied. It is rooted at a map holding the singlename -> Latticeentry, without thePALS/facilityscaffolding the lattice was defined under, so the lattice is reached aslat.full_expanded["lat1"]rather than through["PALS"]["facility"]. Itsbranchesentries are branches, not theBeamLine``s they were built from, and so carry no ``kind; aBeamLinereferenced inside alineis a sub-line whose contents are spliced directly into the enclosing line, so no nestedBeamLinesurvives in the expanded tree. Elements of amultipassline carry amultipass_indexgiving their pass number – how many times a particle will have travelled through that physical element by that point – so every element of one traversal shares an index (the nearest enclosingmultipassline wins when they nest). Every dependent parameter is computed and present: each element carries itselement_index(its position, counting from one, in the branch line that holds it), itsReferenceP,FloorPands_position, the derived members of every parameter family it uses, and the non-zero defaults of the groups it carries; each branch is capped with abranch_endPlaceholderholding its final reference and floor, numbered with the rest.expanded: the same lattice with all of that removed – what the author wrote decides which parameters stay. It isfull_expandedwith nodes pruned rather than an earlier snapshot, so a parameter present in both views holds the same value in both. Use it to see the inputs rather than their consequences, or to write a lattice back out without the computed values.adjunct: everything the expanded views do not carry, keeping itsPALS/facilityscaffolding: element and beamline definitions,usestatements, constants and variables,Controller``s, ``setcommands, and anyLatticethat was not the one expanded. A definition that expansion substituted into the lattice is copied, so it appears in both trees.
Every mathematical expression is evaluated to a number across the expanded views and
adjunct(seeevaluate_pals_expression();random()/random_gauss()are left as text).Controllerelements are evaluated against their own scoped variable tables, with each controlexpressioncomputed and stored back in its control entry; controllers are facility-level, so they are found inadjunct.Each view is backed by its own tree; all five are freed independently when their nodes are garbage collected.
- palsparserpy.evaluate_pals_expression(expr)[source]¶
Evaluate a single PALS mathematical expression to a
float.Supports the full PALS expression grammar: arithmetic (
+ - * / ^), unary signs, parentheses, the built-in constants (pi,c_light,r_electron, …), the math functions (sqrt,log,sin,floor,modulo, …), and the particle-data functionsmass_of,charge_ofandanomalous_moment_of(backed by AtomicAndPhysicalConstantsCLib), whose species-name argument must be quoted, e.g.mass_of("#3He")(a mass number carries a leading#). A leadingexpr(...)wrapper is accepted and unwrapped.This evaluates a standalone string, so user-defined constants and variables are not in scope – use
parse_and_expand_pals()for whole-lattice evaluation, whose expanded trees already have every expression resolved to a number. RaisesValueErrorifexpris not evaluable: a parse error, an unknown identifier or species, arandom()/random_gauss()expression (which is intentionally deferred), or a non-finite result.Example
>>> evaluate_pals_expression("3.75e7 / c_light^2") # 4.172...e-10 >>> evaluate_pals_expression('mass_of("electron")') # 510998.95069... >>> evaluate_pals_expression("expr(2 * pi)") # 6.283...
- palsparserpy.node_correspondence(lat)[source]¶
Map every node of a lattice to the nodes it corresponds to across the
original,combined,full_expandedandadjuncttrees.The correspondence is exact: it is computed from provenance recorded while the trees were derived from one another (
original->combined->full_expandedandadjunct), not by re-matching after the fact. Because expansion can duplicate a node (scalar substitution,repeat,inherit, forks), the correspondence is one-to-many – a singlecombined/originalnode can map to severalfull_expandedcopies – so each field of the returned value is a list of nodes.Expansion splits the document, so a node of
combinedmay land infull_expanded, inadjunct, or in both: a definition that was substituted into the lattice is copied there while its definition stays behind. Those copies share one equivalence class, tied together through thecombinednode they came from.The
expandedview takes no part in the correspondence: it is a pruned copy offull_expandedrather than a step in the derivation chain, so a node in it is found by the path it sits at, not by a recorded link.- Returns:
A
dictkeyed by node. For any node that participates in the correspondence,corr[node]is aNodeCorrespondence–(original, combined, full_expanded, adjunct)– listing every corresponding node grouped by tree. The queried node appears in its own tree’s list, so the four lists together are the full equivalence class ofnode. A list is empty when a tree has no corresponding node (e.g. the synthesiseddestination_pointerscalar exists only infull_expanded, and a constant that the lattice never references exists only inadjunct).- Parameters:
lat (Lattices)
- Return type:
Example
>>> lat = parse_and_expand_pals("lattice.pals.yaml") >>> corr = node_correspondence(lat) >>> a_const = lat.combined["PALS"]["facility"][0]["constants"][0]["a_const"] >>> corr[a_const].original # the same constant in the original tree >>> corr[a_const].adjunct # constants are not part of the lattice >>> corr[a_const].full_expanded # empty unless the lattice referenced it
- palsparserpy.original_node(lat, node)[source]¶
The node of
lat.originalthatnodecame from: the text it was written as.nodemay be inlat.expanded,lat.full_expanded,lat.adjunctorlat.combined.Every value in the expanded trees is a number. This leads back to the expression it was evaluated from, so a program can tell, for instance, whether it was delayed (
expr(...)). A parameter written by asetleads to that set’svalue, the text that gave it its value, not to the value it replaced.Returns
Nonefor a value the parser computed (aFloorPcoordinate, say), and for one an ABSOLUTE controller drives: that value is the controllers’ (several may sum), not any one text’s.Unlike
node_correspondence(), this answers for one node and also takes theexpandedtree.Example
>>> lat = parse_and_expand_pals("lattice.pals.yaml") >>> kn1 = lat.expanded["lat1"]["branches"][0]["ln"]["line"][1]["q2"]["MagneticMultipoleP"]["Kn1"] >>> original_node(lat, kn1).value.startswith("expr(") # was it delayed?
- palsparserpy.controller_expressions(lat, node)[source]¶
The controllers driving a parameter.
nodeis a parameter’s value node inlat.expandedorlat.full_expanded. For eachcontrolsentry reaching it, ABSOLUTE or RELATIVE, the result holds that entry’sexpressionnode inlat.original, in the order the controllers were evaluated. Several controllers may drive one parameter (ABSOLUTE ones sum), which is why this is a list. It is empty when no controller drivesnode.The controller itself is three
get_parent()steps up from an expression: thecontrolsentry, thecontrolslist, then the controller.Example
>>> for e in controller_expressions(lat, kn1): ... ctrl = get_parent(get_parent(get_parent(e))) ... print(node_key(ctrl), ":", e.value) # e.g. "ps1 : 2 * cur"
- palsparserpy.match_names(node, match_string)[source]¶
Every named construct in
node’s tree that is matched bymatch_string, following PALS Name Matching.nodemay be any node of the tree to search (typically a lattice-view root such aslat.full_expanded); the whole tree is searched and the returned nodes belong to that same tree.match_stringhas the form:{element-set}[>{group}.{sub}. ... .{parameter}]
where
{element-set}is a,union of&intersections of:ranges of element names, and an element name is:[{lattice}>>>][{branch}>>][{kind}::]{name}[#{N}]
The operators bind, tightest first:
>>>,>>,::,#,:,&,,,>.{lattice},{branch}and{name}are PCRE2 patterns matched against the whole name; characters inside a(...),[...]or{...}group belong to the pattern. A{name}of digits only is the 1-based index of an element in its line.{kind}is an element kind,Controller, or one of the MetaP componentsalias,ID,labelanddescription.#{N}keeps the N-th match, and a range{a}:{b}runs from eachato the nextbin the same line. The parameter path after the single>is matched exactly, key by key, and applies to every element of the set.In an expanded lattice (
lat.expanded,lat.full_expanded) a name reaches the elements of the lattice. In a tree holding aPALSnode (lat.original,lat.combined,lat.adjunct, or a file read withparse_file) an unqualified name reaches the element definitions underfacilityorpost_expansion, and only a{branch}>>qualified name reaches the elements written in a beamline’sline.The node returned for each match is whatever the string resolves to: the element node (no parameter path), or the parameter-group or parameter node (with a path). Controllers are matched by
Controller::{name}and a controller variable by{controller}>{variable}. A bare name – one element name with no qualifier and no parameter path – additionally matches each constant and variable of that name defined under thePALS,facilityorpost_expansionnode, and each controller of that name. Constants, variables and controllers are found inlat.adjunct, not inlat.full_expanded.Results are de-duplicated and returned in match order. A malformed pattern yields an empty list.
Example
>>> lat = parse_and_expand_pals("lattice.pals.yaml") >>> match_names(lat.full_expanded, "B1.*>BendP.e1") # e1 of every B1... bend >>> match_names(lat.full_expanded, "Quadrupole::.*") # every quadrupole >>> match_names(lat.full_expanded, "inj>>>arc>>Q.*>length") >>> match_names(lat.full_expanded, "Q1:Q5 & Quadrupole::.*") >>> match_names(lat.adjunct, "a_.*") # constants/variables
- palsparserpy.parameter_value(lat, match_string)[source]¶
The value of the lattice parameter named by
match_string, looked up in the expanded latticelat.match_stringuses the same PALS Name Matching syntax asmatch_names(). It names either an element parameter (with a>{group}.{sub}. ... .{parameter}path) or, as a bare name (no lattice/branch/kind qualifier and no path), a constant or variable – the same constructsmatch_names()resolves.Only two of
lat’s five views are searched:lat.full_expanded, which holds the element parameters, and then, if the name is not found there,lat.adjunct, which holds the facility-level constants, variables, and any definitions not spliced into the lattice. The rawlat.originalandlat.combinedviews are not searched – they carry unevaluated, pre-expansion text.lat.expandedis not searched either: a dependent parameter is a legitimate thing to ask for, and onlyfull_expandedcarries one.Because both searched views are post-expansion, values come back already evaluated: a numeric value as a
float, and a non-numeric one (e.g. a species name like"#3He", or an expression expansion left unevaluated such as one usingrandom()) verbatim as astr.The value is resolved as follows:
Element parameter, set: its value – a
float, or astrwhen non-numeric.Element parameter, not set: the parameter’s default is returned (
0.0for every parameter, for now – real per-parameter defaults come later).Constant or variable (bare name): its value, the same way.
Nothing identified:
None, when the name matches nothing in either view, names a bare element (an element has no single scalar value), stops on a whole parameter group, or several matches carry conflicting values.
Example
>>> lat = parse_and_expand_pals("lattice.pals.yaml") >>> parameter_value(lat, "quad1>MagneticMultipoleP.Bn1") # 1.0 >>> parameter_value(lat, "quad1>BendP.g") # 0.0 (unset) >>> parameter_value(lat, "a_const") # from adjunct >>> parameter_value(lat, "quad1>nope.nope") # None
What expansion hands back¶
- class palsparserpy.Lattices(original, combined, expanded, full_expanded, adjunct, problems=<factory>)[source]¶
Bases:
objectFive representations of a lattice, each as a root
YAMLNode, plus the list of problems found while expanding it.expandedandfull_expandedare the same expanded lattice holding the same values;full_expandedadditionally carries every parameter the bookkeeper computed, whileexpandedkeeps only what the author wrote. Seepalsparserpy.parse_and_expand_pals()for what each view holds.problemsis a list ofProblem– one entry per problem encountered during expansion (undefined lattice, dangling element/line references, undefinedinherit/repeat/Forktargets, misspelled names, and expressions that could not be evaluated). It is empty when expansion was clean. Filter it onseverityororiginto decide what is worth acting on:lat = parse_and_expand_pals("ex.pals.yaml", problems="none") mine = [p for p in lat.problems if p.origin is PROBLEM_INPUT]
- Parameters:
- class palsparserpy.Problem(message, path, severity, origin)[source]¶
Bases:
objectOne problem found while reading or expanding a document.
message– human-readable description, always present.path– the logical spot it was found at, such as"q1>ApertureP.shape". Empty when the problem is not tied to one place. This is a location within the document, not a file name or a line number;messagealready names the file where the file is the point.severity– aProblemSeverity: can the trees still be trusted?origin– aProblemOrigin: whose problem is it?
Only a
PROBLEM_INPUTcan be cleared by editing the lattice, which is what makes the last field worth reading: a tool that fails on any problem at all will fail on lattices whose author has nothing left to fix.- Parameters:
message (str)
path (str)
severity (ProblemSeverity)
origin (ProblemOrigin)
- severity: ProblemSeverity¶
- origin: ProblemOrigin¶
- class palsparserpy.ProblemSeverity(*values)[source]¶
Bases:
IntEnumWhether a problem leaves the expanded trees trustworthy.
ERROR– the document is wrong here and expansion could not work around it. Do not trust the affected part of the trees.WARNING– expansion produced a usable result; something was assumed or skipped, but the trees are still sound.
Mirrors
enum problem_severityin PALSParserCpp.h.- ERROR = 0¶
- WARNING = 1¶
- class palsparserpy.ProblemOrigin(*values)[source]¶
Bases:
IntEnumWho has to act on a problem.
INPUT– the document is wrong; the lattice author can fix it.UNSUPPORTED– valid PALS that PALSParserCpp does not implement yet. Editing the lattice will not clear it.UNSPECIFIED– the PALS standard does not define the case, so nothing was invented. Neither the author nor the library is in the wrong.
Mirrors
enum problem_originin PALSParserCpp.h.- INPUT = 0¶
- UNSUPPORTED = 1¶
- UNSPECIFIED = 2¶
- class palsparserpy.NodeCorrespondence(original, combined, full_expanded, adjunct)[source]¶
Bases:
NamedTupleThe nodes one logical entity maps to in each of the four derivation-chain trees, grouped by tree.
expandedtakes no part – it is a pruned copy offull_expanded, not a step in the chain. A field is empty when a tree has no corresponding node.- Parameters:
Translation¶
- palsparserpy.pals_to_bmad(yaml)[source]¶
Translate a parsed PALS lattice
yaml(as returned byparse_file()) into aBmadLattice.The returned structure is an in-memory model of the Bmad lattice (elements, beamlines, parameters), not the input PALS tree. Translation is a three-step process: parse the PALS file with
parse_file, build the target model withpals_to_bmad, then emit the Bmad lattice file withwrite_bmad_file():yaml = parse_file(file_dir) write_bmad_file(pals_to_bmad(yaml), filename)
- Parameters:
yaml (YAMLNode)
- Return type:
- palsparserpy.write_bmad_file(lat, filename)[source]¶
Serialize the
BmadLatticelattofilenameas a Bmad lattice file.Write the constant and variable definitions, the global, beginning-Twiss and particle-start parameters, the element definitions, the
overlay/groupdefinitions, the beamline (line) definitions, and the branch (use) statement, each in its own labelled section. The constants come first because Bmad, unlike PALS, resolves a name against what the file has defined above the point of use.- Parameters:
lat (BmadLattice)
- Return type:
None
- class palsparserpy.BmadLattice(constants=<factory>, parameters=<factory>, beginning=<factory>, particle_start=<factory>, elements=<factory>, controllers=<factory>, beamlines=<factory>, use=<factory>)[source]¶
Bases:
objectAn in-memory model of a Bmad lattice.
Produced by
pals_to_bmad()and serialized to a file bywrite_bmad_file(). The fields mirror the sections of a Bmad lattice file:constants:name = valuedefinitions, in definition order.parameters: globalparameter[...] = ...settings (species, energy, geometry).beginning:beginning[...] = ...initial Twiss, coupling and dispersion settings.particle_start:particle_start[...] = ...initial-coordinate settings.elements: element definitions (BmadEleDef).controllers:overlay/groupdefinitions (BmadController).beamlines:linedefinitions (BmadBeamline).use: branch names for the finaluse, ...statement.
- Parameters:
- elements: List[BmadEleDef]¶
- controllers: List[BmadController]¶
- beamlines: List[BmadBeamline]¶
- class palsparserpy.BmadEleDef(name, type, attrs=<factory>)[source]¶
Bases:
objectA single Bmad element definition.
name: the element name.type: the Bmad element-type name (e.g.Drift,Quadrupole).attrs: already-translated attribute fragments, each an"attribute = value"string.
- class palsparserpy.BmadBeamline(name, members=<factory>)[source]¶
Bases:
objectA Bmad
linedefinition: itsnameand the ordered list of member elementmembers(by name).
- class palsparserpy.BmadController(name, type, slaves=<factory>, vars=<factory>, inits=<factory>)[source]¶
Bases:
objectA Bmad
overlayorgroupelement: what a PALSControllerbecomes.name: the controller name.type:"overlay"forcontrol_type: ABSOLUTE,"group"forRELATIVE. Bmad’s overlay sets the slave parameter and its group adds to it, which is the same split PALS makes.slaves: the controlled parameters, each an"ele[attribute]: expression"string.vars: the variable names, in definition order.inits: the variables’ initial values, each a"name = value"string.
- palsparserpy.pals_to_madx(yaml)[source]¶
Translate a parsed PALS lattice
yaml(as returned byparse_file()) into aMadxLattice.The returned structure is an in-memory model of the MAD-X lattice (elements, lines, beam), not the input PALS tree. Translation is a three-step process: parse the PALS file with
parse_file, build the target model withpals_to_madx, then emit the MAD-X lattice file withwrite_madx_file():yaml = parse_file(file_dir) write_madx_file(pals_to_madx(yaml), filename)
Controllers are translated after the elements, in a second pass: a
control_type: RELATIVEcontroller adds to the value the element already carries, and the only place that value is written down is the element definition this pass has just built.- Parameters:
yaml (YAMLNode)
- Return type:
- palsparserpy.write_madx_file(lat, filename)[source]¶
Serialize the
MadxLatticelattofilenameas a MAD-X lattice file.Write the constant and variable definitions, the
BEAMcommand and initial conditions, the element definitions, the controller variables and their deferred assignments, thelinedefinitions, theusestatement, and theEALIGNmisalignments, each in its own labelled section.The order of the sections is the order MAD-X needs them in, which is stricter than Bmad’s: a name has to be defined above the point of use,
BEAMhas to come beforeUSE, and theSELECT/EALIGNpairs have to come after it, because there is no sequence to apply an error to until one has been expanded.- Parameters:
lat (MadxLattice)
- Return type:
None
- class palsparserpy.MadxLattice(constants=<factory>, beam=<factory>, beta0=<factory>, particle_start=<factory>, elements=<factory>, controllers=<factory>, alignments=<factory>, beamlines=<factory>, use=<factory>, rigidity=False)[source]¶
Bases:
objectAn in-memory model of a MAD-X lattice.
Produced by
pals_to_madx()and serialized to a file bywrite_madx_file(). The fields mirror the sections of a MAD-X lattice file:constants:name = value;definitions, in definition order.beam: the attributes of theBEAMcommand (species and energy).beta0: the attributes of the initial-conditionsBETA0block (Twiss and dispersion).particle_start: initial particle coordinates, which MAD-X takes in theTRACKmodule rather than in a lattice file, and which are written out as a comment.elements: element definitions (MadxEleDef).controllers: variables and deferred assignments (MadxController).alignments:EALIGNmisalignments (MadxAlignment).beamlines:linedefinitions (MadxBeamline).use: the branches, each a(name, periodic)pair, for theusestatement.rigidity: whether anything written refers to the rigidity variable, which then has to be defined ahead of it.
- Parameters:
- elements: List[MadxEleDef]¶
- controllers: List[MadxController]¶
- alignments: List[MadxAlignment]¶
- beamlines: List[MadxBeamline]¶
- class palsparserpy.MadxEleDef(name, type, attrs=<factory>, notes=<factory>)[source]¶
Bases:
objectA single MAD-X element definition.
name: the element name.type: the MAD-X element-type keyword (e.g.drift,quadrupole).attrs: already-translated attribute fragments, each an"attribute = value"string.notes: comment lines to write above the definition. MAD-X elements carry no metadata strings of their own, so a PALSMetaPbecomes a comment here, as does anything else worth saying about the element in the file it is written to.
- class palsparserpy.MadxBeamline(name, members=<factory>)[source]¶
Bases:
objectA MAD-X
linedefinition: itsnameand the ordered list of member elementmembers(by name).
- class palsparserpy.MadxController(name, vars=<factory>, controls=<factory>, notes=<factory>)[source]¶
Bases:
objectA MAD-X rendering of a PALS
Controller.MAD-X has no controller element. What it has instead is the deferred assignment
:=, which makes an element attribute depend on a variable rather than take its value once, and that is what a controller becomes: its variables become ordinary MAD-X variables and each of its controls becomes a deferred assignment to the attribute it drives.name: the controller name, written out as a comment heading.vars: the variables’ initial values, each a"name = value"string.controls: the deferred assignments, each an"ele->attribute := expression"string.notes: comment lines to write above the definitions, holding the controller’sMetaP.
- class palsparserpy.MadxAlignment(name, attrs=<factory>)[source]¶
Bases:
objectThe misalignment of one element: what a PALS
BodyShiftPbecomes.MAD-X keeps a misalignment apart from the element definition, in an
EALIGNcommand applied to whatever the precedingSELECT, FLAG=ERRORpicked out.nameis the element the errors belong to andattrstheEALIGNattribute fragments.
- palsparserpy.pals_to_xsuite(yaml)[source]¶
Translate a parsed PALS lattice
yaml(as returned byparse_file()) into anXsuiteLattice.The returned structure is an in-memory model of the Xsuite lattice (elements, lines, reference particle), not the input PALS tree. Translation is a three-step process: parse the PALS file with
parse_file, build the target model withpals_to_xsuite, then emit the Python script that builds the lattice withwrite_xsuite_file():yaml = parse_file(file_dir) write_xsuite_file(pals_to_xsuite(yaml), filename)
Controllers are translated after the elements, in a second pass: a
control_type: RELATIVEcontroller adds to the value the element already carries, and the only place that value is written down is the element definition this pass has just built.- Parameters:
yaml (YAMLNode)
- Return type:
- palsparserpy.write_xsuite_file(lat, filename)[source]¶
Serialize the
XsuiteLatticelattofilenameas a Python script that builds the lattice in anxtrack.Environment.The script defines the environment
env, the reference particle, every element and line, and ends withline, the first branch of the lattice, ready to track or twiss. It also definestwiss_initandparticle_start, the initial conditions the PALSBeginningElestates, as keyword arguments forLine.twissandLine.build_particles. Run it, or execute it from another script, to get the lattice:python lattice.py exec(open("lattice.py").read()) # env, line, ... are now defined
The sections come in the order Python needs them in: a variable has to exist before an expression names it, and an element before a line holds it.
- Parameters:
lat (XsuiteLattice)
- Return type:
None
- class palsparserpy.XsuiteLattice(constants=<factory>, particle_ref=<factory>, twiss_init=<factory>, particle_start=<factory>, elements=<factory>, controllers=<factory>, beamlines=<factory>, branches=<factory>, use=None, rigidity=False, pals_constants=<factory>)[source]¶
Bases:
objectAn in-memory model of an Xsuite lattice.
Produced by
pals_to_xsuite()and serialized to a Python script bywrite_xsuite_file(). The fields mirror the sections of the script:constants: variable definitions, each a(name, source)pair, in definition order.particle_ref: thext.Particlesarguments of the reference particle (species and energy).twiss_init: the initial Twiss and dispersion, each a(keyword, source)pair forLine.twiss.particle_start: the initial particle coordinates, each a(keyword, source)pair forLine.build_particles.elements: element definitions (XsuiteEle).controllers: variables and deferred expressions (XsuiteController).beamlines: line definitions (XsuiteBeamline).branches: the branches of the lattice, each a(name, periodic)pair.use: the branch the script makes itsline: the first branch of the lattice the last PALSusenames, or of the last lattice defined.rigidity: whether anything written refers to the rigidity variable, which then has to be defined ahead of it.pals_constants: the PALS predefined constants the script refers to, which it has to define because Xsuite does not, each a(name, source)pair.
- Parameters:
controllers (List[XsuiteController])
beamlines (List[XsuiteBeamline])
use (str | None)
rigidity (bool)
- controllers: List[XsuiteController]¶
- beamlines: List[XsuiteBeamline]¶
- class palsparserpy.XsuiteEle(name, type, attrs=<factory>, knl=<factory>, ksl=<factory>, notes=<factory>, before=<factory>, after=<factory>)[source]¶
Bases:
objectA single Xsuite element, which the script creates with
env.new.name: the element name.type: thextrackelement class (e.g.Drift,Quadrupole), or, for an element cloned from another, the name of the element it is cloned from.attrs: the element’s keyword arguments, each a(keyword, source)pair whosesourceis the Python text of the value: a number, or a quoted string Xsuite turns into a deferred expression.knl,ksl: the multipole arrays, entry by entry in the same Python text, kept apart fromattrsso a controller can lengthen them to reach an order the element did not set.notes: comment lines to write above the definition, holding the PALSMetaPand anything else worth saying about the element in the script.before,after: elements the translation adds to stand in for what a single Xsuite element cannot hold – an aperture is an element of its own in Xsuite, placed at the end of the element it bounds, and a PALSPatchthat both shifts and rotates is two. Wherever a line names this element, these come with it.
- Parameters:
- class palsparserpy.XsuiteBeamline(name, members=<factory>, particle_ref=<factory>)[source]¶
Bases:
objectAn Xsuite line, which the script creates with
env.new_line.name: the line name.members: the ordered line items, each a(name, repeat)pair naming an element or another line and how many times it is repeated. A negativerepeatreverses the order of the items, as it does in PALS.particle_ref: thext.Particlesarguments of the reference particle given by the line’s ownBeginningEle, if it has one.
- class palsparserpy.XsuiteController(name, vars=<factory>, controls=<factory>, notes=<factory>)[source]¶
Bases:
objectAn Xsuite rendering of a PALS
Controller.Xsuite has no controller element. What it has instead is the deferred expression: an element attribute assigned a string becomes an expression in the environment’s variables and follows them from then on. So a controller’s variables become Xsuite variables and each of its controls becomes an expression assigned to the attribute it drives.
name: the controller name, written out as a comment heading.vars: the variables’ initial values, each a(name, source)pair.controls: the deferred assignments, each a(target, expression)pair wheretargetis the Python text of the attribute, such asenv['q1'].k1.notes: comment lines to write above the definitions, holding the controller’sMetaP.
- Parameters: