Building and using the library

Building

From the repository root:

cmake -S . -B build
cmake --build build

This builds libyaml_c_wrapper (.dylib/.so), a shared library that other languages can load. CMake fetches the dependencies — rapidyaml, PCRE2, and AtomicAndPhysicalConstantsCLib — automatically. Rebuild after changing any source file, and before running the tests. Put lattice files under lattice_files/.

Expanding a lattice

parse_and_expand_PALS reads a lattice file, resolves its includes and loads, expands the selected lattice, and returns a lattices struct with five independent views — original, combined, expanded, full_expanded, and leftover (see The five trees). Each is a YAMLTreeHandle and must be freed with delete_tree.

#include "yaml_c_wrapper.h"

struct lattices lat = parse_and_expand_PALS("ex.pals.yaml", nullptr);

char* s = tree_to_string(lat.full_expanded);
std::puts(s);
yaml_free_string(s);

// Report any problems met while expanding (see below), then free everything.
for (size_t i = 0; i < lat.problems.count; ++i)
    std::fprintf(stderr, "  - %s\n", lat.problems.items[i]);
free_lattice_problems(lat.problems);

delete_tree(lat.original);
delete_tree(lat.combined);
delete_tree(lat.expanded);
delete_tree(lat.full_expanded);
delete_tree(lat.leftover);

The second argument names the lattice to expand. Pass nullptr (or an empty string) to expand the lattice named by the last use statement, or — if there is none — the last lattice defined in the file.

Problems found during expansion

Expansion does not abort on a recoverable problem. It leaves the offending value as it found it, carries on with the rest of the lattice, and appends a human‑readable message to lat.problems, an owning string_list. The list is empty when expansion was clean. The library never prints — the caller decides whether to report, save, or ignore the messages — and must release the list with free_lattice_problems.

Expansion is therefore always worth reading: a lattice with problems still comes back expanded as far as it could be, with the trees around the fault intact. The one exception is a top‑level file that is not valid YAML, where there is nothing to expand: all five handles come back NULL and the parse error, with its location, is the single problem reported.

What gets reported falls into a few groups.

Structure. A lattice named in the call (or in use) that does not exist, a line reference to an undefined element or line, a missing inherit/repeat/Fork target, an invalid repeat count, a Fork whose to_line cannot be resolved, a branch that never got expanded, and a load or include that cannot be read or that conflicts with what it is merged into.

Expressions. A value that cannot be evaluated — an unknown constant or species, a dangling element‑parameter reference, a reference cycle, a syntax error — is left as text and reported. Only values that look like expressions (an operator, a parenthesis, a > reference, or an explicit expr(...)) are flagged, so a plain name or a label is not mistaken for broken math. See Evaluating expressions.

Controllers. A control_type that is neither ABSOLUTE nor RELATIVE; a controls entry with no parameter or no expression; a control target that is malformed, names no element parameter, or matches nothing in the expanded lattice; a reference to a variable a named controller does not have; a controller variable whose initial value is not a constant expression; a circular control hierarchy; a parameter driven by both an ABSOLUTE and a RELATIVE controller; and a parameter that is both controlled and assigned a delayed (expr(...)) expression.

Sets. A set with no parameter or no value; a target that is malformed, names no element parameter, or matches nothing (for a pre‑expand_lattice set, nothing defined before it); a value that cannot be evaluated; and a value that reads a parameter whose value is still to be derived during expansion.

Bookkeeping. A branch whose first element gives neither a reference species nor a reference energy, so the reference parameters cannot be computed; and a parameter the author wrote that is inconsistent with what the rest of its family or the bend geometry implies — the authored value is kept and the disagreement reported rather than silently overwritten.

Vocabulary. An element kind or parameter group name that is not one the standard defines — a FlorP group is valid YAML and would otherwise go unrecognised in silence — reported with a “did you mean” suggestion where a near match exists.

Deliberately not computed. Where the standard fixes a value’s magnitude but not how to produce it, this library reports rather than guesses: an absolute_error/relative_error on a set (the error distribution is unspecified, so the deterministic value is written and the error is not), and a Foil element’s downstream species change (the stripping model is undefined, so the species is left as the upstream one).

Reading the expanded lattice

Which of the five trees answers a given question, and what the expansion adds to full_expanded that appears nowhere else — element_index, s_position, ReferenceP, FloorP, the derived parameter families, the branch_end cap — is covered in The five trees.

Examples

The examples/ directory contains runnable programs:

  • examples/example_rw.cpp — read a lattice, do basic manipulations (add, remove, rename), print, and write it back out.

    ./example_rw
    
  • examples/print_lattices — expand a lattice and print all five views. It takes a file name and an optional -lat <name> flag naming the lattice to expand:

    ./print_lattices ex.pals.yaml -lat lat2
    

    The file name is resolved under ../lattice_files/, so run it from a directory that sits beside lattice_files/build/, say — and put the lattice in lattice_files/.

Extensions

PALSJulia extends pals-cpp with a Julia interface to the C API and translators from PALS to Bmad and SciBmad lattice files.