Input File Generation (Command-Line Mode)

PASS reads simulation inputs from JSON files. Use the Python schema classes to configure global parameters, bunches and a lattice sequence, then call generate_input(). For graphical configuration, see Graphical interface.

Schema construction checks declared field types and constraints; JSON input validation additionally checks the complete input, command ordering and external files. generate_input() writes the configuration and does not itself replace that full check.

Quick start

After installing PASS, save the following script as input/generate_beam0.py under the repository root. Create the input directory if needed. This example uses 2,048 macro particles, 64 CPU turns, a smooth linear lattice and Gaussian distributions; no external lattice file is required.

from pathlib import Path

from PASS.para.api import generate_input
from PASS.para.schema.main import MainConfig
from PASS.para.schema.bunch import BunchConfig, InjectionItem
from PASS.para.schema.sequence import Sequence
from PASS.para.schema.monitors import StatMonitorItem
from PASS.para.smooth import generate_smooth_twiss
from PASS.validation import validate_file

main = MainConfig(
    beam_name="proton",
    num_proton=1, num_neutron=0, num_electron=1,
    gamma_t=4.8, circumference=251.327,
    num_turns=64, backend="cpu", output_dir="output", is_plot=False,
)
items, names, circumference = generate_smooth_twiss(
    circumference=main.circumference,
    qx=4.8, qy=4.4, num_points=17,
    longitudinal_transfer="off",
)
bunch = BunchConfig(
    kinetic_energy=45e6,
    num_real_particles=100_000_000_000,
    num_macro_particles=2048,
    beta_x=items[0].beta_x, beta_y=items[0].beta_y,
    alpha_x=0.0, alpha_y=0.0,
    emit_x=2e-6, emit_y=2e-6,
    sigma_z=0.1, dp=0.001,
    dist_trans="gaussian", dist_longi="gaussian",
)
seq = Sequence()
seq.add("injection", InjectionItem(s=0.0, random_seed=2026, bunches=[bunch]))
for name, item in zip(names, items):
    seq.add(name, item)
seq.add("stat1", StatMonitorItem(s=0.0, write_interval_turns=16))

output_path = Path(__file__).resolve().parent / "beam0.json"
generate_input(main, seq, str(output_path))
report = validate_file(str(output_path))
if not report.ok:
    raise ValueError(report.text())
print(f"Validated input: {output_path}")

Run these commands from the repository root:

python input/generate_beam0.py
pass-run input/beam0.json

Installing PASS registers pass-run in that Python environment. After installation, python -m PASS accepts exactly the same arguments and explicitly uses the selected Python interpreter. Neither entry point requires Qt or PySide6. After installation, use pass-run --version or python -m PASS --version to print the version and exit without an input file. Pass JSON inputs, a .passproj file, or a project directory:

pass-run input/beam0.json
pass-run input/beam0.json input/beam1.json
pass-run example.passproj
python -m PASS example.passproj
pass-run path/to/project-directory
pass-run .

Named input options provide the same three modes:

pass-run --beam0 input/beam0.json
pass-run --beam0 input/beam0.json --beam1 input/beam1.json
pass-run --passproj example.passproj

Use either positional inputs or named input options; do not mix them. --beam0 and --beam1 require JSON files, and --beam1 requires --beam0. --passproj accepts a .passproj file or a project directory and cannot be combined with either beam option. Relative input paths use the terminal’s working directory; quote paths containing spaces.

One or two JSON files run the corresponding beams. A .passproj file or project directory runs its saved Beam 0 and optional Beam 1 selections; if Beam 0 has not been saved, it uses the active configuration. It does not execute every configuration in the project. A directory prefers project.passproj, otherwise it opens its only .passproj file; multiple candidates require an explicit file path. New plain JSON and old ZIP project descriptions are both readable. Invalid saved selections or duplicate beams are errors. See Editable projects for project selection and output paths.

JSON parameter overrides

Override input fields for this run without editing the source JSON or project:

pass-run beam0.json --backend=gpu --device-id=1 --task-name=test
pass-run beam0.json --number-of-turns=100 --particle-precision=float32
pass-run beam0.json --timing.log-interval=20
pass-run beam0.json '--sequence[QF1_s1.000].length=0.5'
pass-run beam0.json '--device-id[0]=1'

Fixed field names come from the JSON keys: remove parentheses and their contents, replace spaces with hyphens, and ignore case. Units remain those of the JSON key: Circumference (m) becomes --circumference and still takes metres. Sigma dp/p retains its slash. Nested fields use dots. Named elements and resources use [exact name]; their case, dots and other characters are preserved. Quote arguments containing shell punctuation, particularly $. Arrays use zero-based indices; replacing an entire array is also supported.

Values use JSON types (numbers, true, false, null, arrays and objects). String fields also accept plain text. Use --path=VALUE or --path VALUE; use the equals form when a string begins with --. --device-id=1 and --device-id="[1]" are equivalent; a single integer is normalized to an array before validation and archiving. Booleans and decimal values such as true and 1.0 are rejected. Choosing a device does not switch the backend to GPU. For names containing brackets, or any ambiguous abbreviated field, use exact original keys in a JSON path array:

pass-run beam0.json --set '["Sequence","QF1_s1.000","Length (m)"]' 0.5

Unqualified paths target Beam 0. Prefix paths with beam0. or beam1. to select an input; exact paths may start with "beam0" or "beam1". The existing shared-run checks still apply: turns, backend, precision, devices, timing and plot settings must agree in both inputs. For example:

pass-run beam0.json beam1.json --number-of-turns=100 --beam1.number-of-turns=100

Task name and Output directory apply to the whole run; do not put them under Beam 1. --output-directory and --output are equivalent. Overrides take precedence over saved input/project settings, are applied before preflight and archiving, and are recorded in input/beam0.json and optional beam1.json. Original configurations remain in configuration*.json. Relative file paths supplied by the command line use the caller’s working directory; unchanged paths retain their original input/project base directory.

All schema-declared input fields are eligible, including omitted optional fields. Whole configuration blocks replace rather than merge the previous value. The interface does not edit external file contents or internal computed variables, add/remove named elements or resources, delete keys, or append array entries via indices. Unknown fields, ambiguous normalized names, invalid types or indices, duplicate targets and parent/child overlaps are errors. Full input and physics consistency validation must pass before execution.

The injection field is now RF S Position Refer to Injection Point (m); the former abbreviated spelling is no longer accepted. Regenerate older inputs or rename that key before running them.

Use --output DIR to choose the output root for this run. It takes precedence over the JSON or saved project setting and works with one JSON, two JSON files, or one project. Relative command-line output paths are resolved from the working directory at launch; the original JSON and project files remain unchanged:

pass-run input/beam0.json --output results
pass-run input/beam0.json input/beam1.json --output "results/two beams"
python -m PASS example.passproj --output results

Without this option, JSON execution uses the first JSON’s Output directory. Relative configuration paths are resolved beside that JSON. If the field is omitted or set to default (case-insensitive), the output root is output beside the first JSON. Generated inputs still default to ./output. The repository or installation directory is no longer used as a fallback.

The optional Task name field (MainConfig.task_name) names the run folder inside that root. Output directory / output_dir still specifies the output root. For example, output_dir="output", task_name="capture_sc" writes to output/capture_sc/. Omit the field or use null, an empty string, whitespace, or default to select a single folder named YYYY_MM_DD_HH_MM at minute precision, for example output/2026_10_30_18_39/. default is case-insensitive after trimming surrounding whitespace, so " DEFAULT " has the same effect. There is no separate date parent directory and no automatic Beam 0 name fallback. The input/, distribution/, particle/, plot/ and other component subdirectories keep their names and layout. Result filenames retain their existing HHMM_SS time labels, independently of the task folder name. Both automatic and custom names use unpadded collision suffixes _1, _2, _3, and so on: for example, 2026_10_30_18_39_1 or capture_sc_1. Earlier results are never reused or overwritten.

Use a single folder name, not a path: path separators, ./.., invalid filename characters and reserved operating-system names are rejected. Leading and trailing whitespace is removed. Date-shaped custom names are allowed; YYYY_MMDD is no longer reserved. The command-line --task-name NAME overrides the stored name without changing the input. An explicitly empty override or --task-name default selects the single-level timestamp layout:

pass-run input/beam0.json --task-name capture_sc
pass-run example.passproj --output results --task-name capture_sc
pass-run input/beam0.json --task-name ""
pass-run input/beam0.json --task-name default

Compatibility inputs still accept the former JSON fields Output subdirectory and Output name, Python arguments or project settings output_subdirectory and output_name, and CLI options --output-subdirectory and --output-name. New JSON, project files and execution snapshots use Task name or task_name as appropriate. Supplying multiple aliases for the same setting is rejected; use one spelling when migrating. Existing result trees are not moved or renamed. New runs, including reruns of old inputs or snapshots, use the single-level layout.

Use either help command to display input modes, options, path rules, and examples without starting a simulation:

pass-run --help
pass-run -h

The --stop-file option requests a stop when the named file exists, before initialization or at a turn boundary. Exit codes are 0 for completion, 1 for a failure, 2 for invalid command arguments, 3 for a requested stop and 130 for an interruption. Python integrations can call PASS.main.main('input/beam0.json', raise_errors=True) directly. To override output in a Python integration, use PASS.main.main('input/beam0.json', output_dir='results', raise_errors=True). Relative output_dir paths use the working directory when main() is called. This override requires the default archive_inputs=True; it cannot be combined with archive_inputs=False for an already prepared snapshot. The Python task_name argument similarly overrides the configured folder name, for example main('input/beam0.json', task_name='capture_sc'). task_name=None leaves the saved setting in effect; task_name='' or task_name='default' restores the single-level timestamp layout even when the saved name is custom. The same case-insensitive, whitespace-trimming rule applies. An explicit override requires archive_inputs=True.

The script writes input/beam0.json. Its relative output directory resolves to input/output; each run creates a run directory beneath it. The run directory contains a CSV file and an HDF5 file with 64 statistics rows (turns 0–63). Log output gives the exact run path. Read the newest statistics file after the run:

from pathlib import Path

from PASS.utils.table_io import read_table

files = list(Path("input/output").rglob("*stat*.h5"))
latest = max(files, key=lambda path: path.stat().st_mtime)
data = read_table(latest)
print(latest)
print(data[["turn", "sigmaX", "sigmaY", "xEmittance", "yEmittance"]].tail())

For this uncoupled linear model, transverse RMS emittances should remain constant up to numerical rounding. The finite sampled initial values need not equal the requested emittances exactly. Longitudinal transfer is disabled here; this example does not model synchrotron oscillation or collective effects.

Input snapshots for execution

PASS.main.main(beam0_path, beam1_path=None, ...) archives inputs by default (archive_inputs=True). After checking the original configuration, it copies the JSON configuration and referenced input files, validates the resulting snapshot, and initializes tracking from that snapshot. This includes particle distributions, RF programs, offset tables, wakefield models, and magnet ramping tables. Later changes to their original files do not affect the run.

JSON, saved project, and GUI runs all use the same single-level timestamp layout by default. The input snapshot is stored in the run’s input subdirectory:

<output>/<YYYY_MM_DD_HH_MM>/       # simulation results for this run
    input/
        configuration0.json         # original configuration values
        beam0.json                  # execution input; optional beam1.json
        assets/<filename>           # copied input dependencies
        run.json                    # paths, SHA-256 hashes and run status

With Task name: capture_sc, the same contents live beneath <output>/capture_sc/ instead of the automatic timestamp folder. If that folder already exists, a numbered sibling is allocated as described above.

The result directory is allocated before inputs are copied. Both automatic and custom names receive _1, _2, and so on when the requested folder exists. The run ID remains in run.json; it is not another directory level. Copied dependencies are placed directly in assets/, without numbered subdirectories. Filenames are preserved; name conflicts, compared without case sensitivity, are resolved by appending 1, 2, and so on before the extension, for example rf.tfs, rf1.tfs, and rf2.tfs. Execution JSON and run.json record the resulting filenames.

Two-input runs also save configuration1.json. File references in execution JSON point to relative assets/... paths inside the snapshot, so the input directory can be moved together. The output root, including any --output or Python output_dir override, is resolved before copying and stored as an absolute Output directory in the execution snapshot; moving the snapshot does not redirect its results. An override changes the root for both snapshots and results while preserving the selected layout. The original JSON and input files remain unchanged. configurationN.json serializes the original configuration values; copied dependency files preserve their exact bytes. The GUI and command-line project runs use the same input-copying, hashing, and result-directory rules; see Editable projects.

The parameter JSON saved alongside results retains its existing filename and stores absolute input and output paths. It is generated from the configuration already loaded, after resolving paths and before expanding named configurations, without rereading the source JSON. The main snapshot’s beamN.json continues to use relative dependency paths.

The format-version-1 run.json records the SHA-256 hashes of execution JSON, comparison configurations, and available dependencies. Dependency records include the original source path and copied byte count. Missing files belonging to disabled resources retain their validation warnings and are listed in unavailable_dependencies; missing required active inputs block execution. Missing references point to uncreated paths inside the snapshot, so restoring an original file later cannot make it an unarchived runtime input. The record follows preparation and execution status. output_root stores the chosen output root, and output_directory stores the output path written into the execution configuration. task_name stores the requested custom name, or an empty string for the single-level timestamp layout. results_directory stores the actual allocated result directory, including any collision suffix, from preparation onward. The on_initialized(cfg) callback can read cfg.input_snapshot_path to locate this run’s run.json. A preparation failure is recorded as preparation_failed when a record has already been created.

For result summaries that need the final particle state, main() also accepts on_completed(sim). This callback receives the Simulation after tracking finishes successfully, before the run record is marked complete. It is not called for stopped or failed runs. A callback exception marks the run as failed and follows the normal raise_errors behavior. Integrations using the command-line parser can pass the same callback to cli_main(argv, on_completed=callback); for example, the injection-painting wrapper uses it to write completed.json.

Running main() on a saved beam0.json starts again from its initial conditions and creates a new input snapshot and result directory. This is input reproduction, not checkpoint continuation. An input snapshot does not restore the previous Python environment, code version or random-generator state; Random Seed: null retains nondeterministic sampling.

For integrations that already prepared an input snapshot, archive_inputs=False disables this additional copy; the GUI child process uses that setting. Automatic dependency archiving belongs to main(); the lower-level Config.load_input() does not archive dependencies itself. The explicit Python API option flat_output=True retains its existing layout for integrations: results remain directly in the specified output directory, and snapshots are placed in its parent’s input_snapshots/<run-id>. If the result directory itself is named input_snapshots, the sibling input_snapshots_archive/<run-id> is used instead, keeping the flat result directory free of snapshot subdirectories. flat_output=True cannot be combined with a custom Task name; clear the configured name or pass task_name='' or task_name='default' for that verification workflow.

JSON File Structure

The abbreviated structure below shows the nesting; empty bunch objects and omitted parameters are placeholders, not a runnable input.

{
    "Beam Name": "proton",
    "Number of Protons": 1,
    "Number of Neutrons": 0,
    "Number of Charges": 1,
    "Transition Gamma": 4.8,
    "Circumference (m)": 251.327,
    "Number of turns": 64,
    "Backend (gpu/cpu)": "cpu",
    "Number of GPU devices": 1,
    "Device Id": [0],
    "Output directory": "./output",
    "Task name": "",
    "Is plot figure": false,
    "Sequence": {
        "injection": {
            "S (m)": 0.0,
            "Command": "Injection",
            "Harmonic Number": 1,
            "Random Seed": 2026,
            "bunch0": {}
        },
        "twiss_0000": {
            "S (m)": 0.0,
            "Command": "Twiss",
            "S previous (m)": 0.0,
            "Beta x (m)": 8.333
        },
        "stat1": {
            "S (m)": 0.0,
            "Command": "StatMonitor"
        }
    }
}

Note

JSON uses the documented field names. The schema classes map Python field names to JSON keys through pydantic aliases.

When reading, the engine first calls convert_keys_to_lower() to convert all keys to lowercase, so the case of JSON keys does not affect reading.

Core Components

MainConfig (Global Parameters)

Python field

JSON key

Type

Default

Description

beam_name

Beam Name

str

'proton'

Beam species label.

num_proton

Number of Protons

int

1

Number of protons per particle; zero for electrons and positrons.

num_neutron

Number of Neutrons

int

0

Number of neutrons per particle.

num_electron

Number of Charges

int

1

Signed charge number Z (q=Z e), not electron count; nonzero integer.

gamma_t

Transition Gamma

float

7.635

Transition gamma of the lattice.

circumference

Circumference (m)

float

569.1

Positive ring circumference (m).

num_turns

Number of turns

int

100

Number of simulated turns; positive integer.

backend

Backend (gpu/cpu)

str

'cpu'

Compute backend: cpu or gpu.

particle_precision

Particle Precision

str

'float64'

Coordinate storage: float32 or float64.

num_gpu

Number of GPU devices

int

1

Number of GPU devices.

gpu_id

Device Id

list[int]

[0]

List of GPU device identifiers.

output_dir

Output directory

str

'./output'

Output root directory; relative paths resolve against the input JSON directory. Omitted or default (case-insensitive) uses output beside the first JSON.

task_name

Task name

str

'default' (displayed)

Optional task name, used as the run-folder name beneath output_dir. Omitted, null, blank or default (case-insensitive after trimming whitespace) selects the single-level YYYY_MM_DD_HH_MM layout and normalizes to an empty string. Both automatic and custom names use collision suffixes _1, _2, and so on. Subdirectories and result filename rules are unchanged.

is_plot

Is plot figure

bool

False

Generate monitor figures from saved results after successful tracking; default output is the run’s plot/ directory.

timing

Timing

TimingConfig

TimingConfig()

TimingConfig: mode=command, log_interval=10, warmup_turns=1, include_io=True.

is_beambeam

Is beam-beam

bool

False

Obsolete placeholder. A true value is rejected; generated input omits it.

Beam-beam collisions use the separate top-level Beam beam block and explicit commands on both beam sequences. Pass beam_beam=BeamBeamConfig(...) to generate_input; see Beam-Beam Interaction (BeamBeam) for source methods, ordering and elements.

Space charge is configured by the separate top-level Space charge block, not by MainConfig. See Space-Charge Effect (SpaceCharge) for its named resource schema and sequence-command references. Each resource selects Method (pic, frozen, quasi-frozen) and Solver; the latter includes the boundary condition in its name. The command’s Aperture type/value defines particle losses and, for Dirichlet solvers, the conducting wall. Supply a complete grid full-width or half-width pair; omitting the command aperture selects a rectangle equal to that grid.

Electron clouds use the independent top-level Electron cloud block. Pass electron_cloud=ElectronCloudConfig(...) to generate_input and reference a named configuration with ElectronCloudItem. The frozen model needs no Slicer; externally driven build_up and PIC coupled require a current z_rel SliceSet and nested ElectronCloudBuildUpConfiguration. Coupled mode includes cloud self-fields and transverse beam response. Physics, parameters and limits are in Electron Cloud (ElectronCloud).

Intrabeam scattering uses the separate top-level Intrabeam scattering block. Pass intrabeam_scattering=IBSConfig(...) to generate_input and reference a named model with IBSItem. Gaussian growth diagnostics and kinetic kicks require explicit local IBSOpticsConfig; binary collisions use a local three-dimensional mesh. See Intrabeam Scattering (IBS) for rates, exposure time and model limits.

Automatic design clock and initialization

PASS derives one shared ideal RF-only design trajectory before tracking, using the initial energy of the harmonic-ID-zero bunch, species mass and charge, circumference, and enabled RFCavity waveforms at their physical positions. Colocated RF components sample the same design passage and their energy gains are summed. Design flights use the reference velocity after the preceding kick. The design revolution frequency is \(f_{rev}=\beta_{design}c/C\); its accumulated phase has the fixed physical-time origin zero:

\[\Psi(t)=\int_0^t f_{rev}(u)\,du.\]

Frequency nodes occur at turn boundaries and just before RF passages. They are linearly interpolated in physical time, with constant endpoint extrapolation and an analytic integral on each segment. RF kicks are simulated only for the requested turns. One additional design passage at the final design energy closes the interpolation interval without applying further RF kicks. With no active RF voltage the design clock is constant. Collective kicks, particle loss and changes to the tracked bunch energies do not redefine this ideal design clock. Physical RF phase samples remain additive waveform modulation; they are not reinterpreted as requested synchronous-particle phases.

There is no public Reference clock input or ReferenceClock schema. Remove that field from older inputs. An arbitrary externally prescribed clock cannot, in general, be inferred uniquely from an initial energy and RF data. If an external generator specifies the physical RF frequency, use each component’s explicit Frequency (Hz) or file FREQUENCY column and preserve its integrated phase, including any nonzero original epoch. The HIAF converter in RFCavity performs that migration. Reconstructing an archived design trajectory is a model calculation, not a promise of bitwise identical frequency or energy samples; compare its timing, phase and energy explicitly.

Initially, \(T_b=\Psi^{-1}(-h_{id}/h_{group})\), unless BunchConfig supplies Reference arrival time (s). Injection on turn n uses \(\Psi^{-1}(n-h_{id}/h_{group})\); an explicit initial arrival time shifts that source schedule by its difference from the nominal initial time. Incoming z and normalized momenta are transformed to the destination bunch reference without changing physical arrival times or momenta.

harmonic_id and harmonic_number describe nominal grouping slots. The nominal slot position harmonic_id*C/harmonic_number is calculated when needed for metadata output. Adding it to z does not reconstruct a physical position or arrival time. RF harmonics are independent of this grouping count. Bunch Regrouping (ReorganizeBunch) explains regrouping by the automatic design-clock phase while retaining unwrapped particle times.

InjectionItem (Injection and Grouping)

InjectionItem declares harmonic_number (JSON key Harmonic Number) once at the injection level. This value is the bunch-grouping count and determines:

  • The number of bunch centers around the ring, separated by \(C/h_{\mathrm{group}}\)

  • The required number of BunchConfig entries in bunches

  • The requirement that harmonic_id values uniquely cover \(0,\ldots,h_{\mathrm{group}}-1\)

It does not constrain RFComponent.harmonic. Represent an unfilled group with a declared bunch whose num_macro_particles is zero.

Set random_seed (JSON key Random Seed) to an integer when the generated particle distribution must be reproducible. Leave it unset, or use JSON null, for the default non-deterministic seed. The seed belongs to the whole Injection command, so its random stream is shared by all declared bunches and injection turns.

BunchConfig (Bunch Parameters)

Property

JSON key

Type

Description

kinetic_energy

Kinetic Energy per Nucleon (eV/u)

float

Kinetic energy per nucleon (eV/u)

num_real_particles

Number of Real Particles

int

Number of real particles per bunch

num_macro_particles

Number of Macro Particles

int

Number of macro particles per bunch

beta_x / beta_y

Beta x (m) / Beta y (m)

float

Twiss β function

alpha_x / alpha_y

Alpha x / Alpha y

float

Twiss α function

emit_x / emit_y

RMS geometric emittance x (m'rad) / RMS geometric emittance y (m'rad)

float

RMS geometric emittance (m rad)

sigma_z

Sigma z (m)

float

Bunch length

dp

Sigma dp/p

float

Momentum spread

dist_trans

Transverse dist

str

Transverse distribution: kv / gaussian / uniform-real / uniform-phase / waterbag / parabolic

dist_longi

Longitudinal dist

str

Longitudinal distribution: gaussian / coasting / matchz / matchdp

rf_voltage

RF Voltage (V)

float

Signed physical RF voltage (used in matchz/matchdp modes)

rf_phase

RF Phase (rad)

float

Physical RF waveform phase at the matching reference event (matchz/matchdp); see RF phase and charge sign in matching

harmonic_id

Harmonic ID of this bunch

int

Bunch-group index; its nominal slot is \(z_{\mathrm{center}}=h_{\mathrm{id}}C/h_{\mathrm{group}}\)

rf_s_position

RF S Position Refer to Injection Point (m)

float

RF-cavity position relative to injection, used to linearly back-propagate a matched distribution to \(s=0\)

momentum_offset_dp

Momentum Offset dp

float

Mean bunch relative-momentum offset; mutually exclusive with the kinetic-energy offset

kinetic_energy_offset

Kinetic Energy Offset (eV)

float

Mean bunch kinetic-energy offset, converted exactly to a relative-momentum offset internally

All generated or manually inserted z values in BunchConfig are bunch-relative coordinates \(z_{\mathrm{rel}}\), not absolute laboratory azimuths.

Sequence (Sequence Container)

Sequence is an ordered container that stores all sequence items arranged by position s . Export sorts items by (s, command priority); ties preserve insertion order. Execution additionally groups nearby positions using its position tolerance.

from PASS.para.schema.sequence import Sequence
from PASS.para.schema.bunch import BunchConfig, InjectionItem
from PASS.para.schema.elements import QuadrupoleItem
from PASS.para.schema.monitors import StatMonitorItem

bunch = BunchConfig(kinetic_energy=45e6, num_real_particles=100000000000,
                    num_macro_particles=2048, emit_x=2e-6, emit_y=2e-6)
seq = Sequence()
seq.add("injection", InjectionItem(s=0.0, bunches=[bunch]))
seq.add("qd1", QuadrupoleItem(s=1.0, k1l=0.2, length=0.5))
seq.add("stat1", StatMonitorItem(s=0.0))

Supported sequence item types:

  • InjectionItem — injection point (must have s=0 )

  • TwissItem — twiss transfer point

  • DriftItem , QuadrupoleItem , SBendItem , etc. — physical elements

  • StatMonitorItem , DistMonitorItem , PhaseAdvanceMonitorItem — monitors

Lattice Sources

PASS supports the following methods for generating lattice sequences, which can be selected or combined as needed:

Method 1: Read from MADX twiss file

Reads a twiss TFS file generated by MADX, converting each element into a TwissItem transfer point. Suitable for element-by-element twiss transport mode.

from PASS.para.madx import read_madx_twiss

items, names, circum = read_madx_twiss(
    twiss_file="lattice.tfs",
    error_file="errors.tfs",       # optional
    muz=0.001,                      # longitudinal tune
    dqx=0.0,                        # chromaticity (or "from_file")
    dqy=0.0,
    is_field_error=False,           # whether to read field errors
    insert_patterns=["QD.*"],      # regex matching, inserted as thin lens elements
)

For absolute field-error units, instance matching and the difference between distributed element errors and Twiss exit kicks, see Element errors.

Both Twiss readers convert native MAD-X momentum derivatives to PASS’s \(\delta=P/P_0-1\) convention. MAD-X TWISS uses \(\mathrm{PT}=(E-E_0)/(P_0c)\simeq\beta_0\delta\), so imported DX, DPX and file-derived DQ1/DQ2 are multiplied by \(\beta_0\). Explicit numeric dqx/dqy arguments already mean \(dQ/d\delta\) and are unchanged. Raw TFS tables retain their original units. Export with twiss, chrom when using the file’s chromaticities.

Direct TFS imports require native TWISS output. Identifiable raw PTC_TWISS tables are rejected: their derivative convention depends on PTC’s TIME mode, which is not recorded in ordinary TFS headers, and their phase columns can also differ. To compute and import PTC optics, use load_madx_sequence(..., optics_method="ptc") as described in Method 4: Load a MAD-X sequence or LINE. Its adapter knows the calculation settings and returns compatible columns. This does not make native TWISS and exact PTC element models equivalent.

The reference speed is derived from GAMMA, or from ENERGY/MASS when GAMMA is absent. If both descriptions are missing, legacy tables remain readable with a warning and the ultrarelativistic assumption \(\beta_0=1\); provide reference-energy headers for nonrelativistic beams. Invalid reference parameters are rejected. Previously generated inputs must be regenerated to apply this correction; at 800 MeV proton kinetic energy, the conversion factor is approximately 0.84181. See the MAD-X coordinate and derivative conventions.

For a uniform base grid, use the resampling reader instead:

from PASS.para.madx import read_madx_twiss_interpolated

items, names, circum = read_madx_twiss_interpolated(
    twiss_file="lattice.tfs",
    num_interp_slice=101,          # 100 segments, including both 0 and C
    dqx="from_file", dqy="from_file",
    longitudinal_transfer="off",
)

This reader uses interp_kind="phase_hermite" (the sole supported method), num_interp_slice counts base points, not segments; it must be an integer at least two. DQx/DQy default to "from_file" and Mu z defaults to zero. The longitudinal phase is used only for longitudinal_transfer="matrix".

In each source interval of length \(h\), define \(t=(s-s_i)/h\) and interpolate \(p(t)=\mu(s)-\mu(s_i)\) with a quintic Hermite polynomial. The endpoint constraints, in phase cycles, are:

\[p_t = \frac{h}{2\pi\beta}, \qquad p_{tt} = \frac{h^2\alpha}{\pi\beta^2}.\]

The interpolated optical functions are:

\[\beta(s)=\frac{h}{2\pi p_t},\qquad \alpha(s)=\frac{p_{tt}}{4\pi p_t^2}.\]

This preserves \(\beta'=-2\alpha\) and \(\mu'=1/(2\pi\beta)\) within each interval, while matching source beta, alpha and cumulative phase at its endpoints. The full phase derivative is checked at its endpoints and all interior extrema to exclude nonpositive beta. DX/DPX use paired cubic Hermite interpolation in the existing uncoupled, on-reference paraxial convention. Interpolation retains the source units internally, then converts dispersion to derivatives with respect to \(\delta\) when constructing TwissItem. This interpolation assumes uncoupled optics and does not transform closed-orbit coordinates. Source precision and spacing limit accuracy.

The table must include S=0 and S=LENGTH, with finite optics, positive beta and unwrapped phases. Its full phase spans must agree with Q1/Q2 within TFS output rounding (relative tolerance \(2\times10^{-8}\), absolute tolerance \(2\times10^{-9}\)). Output phases retain the source values rather than being rescaled to rounded headers. Segment chromaticities are distributed in proportion to the source phase span, preserving the requested DQx/DQy sums.

Original rows are not retained as additional output points. Zero-length BPM markers, selected BPM monitors, matched thin elements, field errors and repeated-S optical jumps add required split positions. Error names are matched against the original table before resampling. At optical jumps, a zero-length Twiss map joins the incoming and outgoing states. Additional kicks/errors execute after the Twiss maps at that position, consistent with command priority. Explicitly inserting design focusing already represented by the source optics adds it again; error insertion does not preserve the perturbed machine’s tune by construction. See Graphical interface for the corresponding import controls.

Method 2: Read from MADX twiss file as elements

Reads the twiss file, but converts each element into its corresponding physical element object ( QuadrupoleItem , SBendItem , etc.). Suitable for element-by-element tracking mode.

from PASS.para.madx import read_madx_elements

items, names, circum = read_madx_elements(
    twiss_file="lattice.tfs",
    is_merge_drift=True,            # merge adjacent drift sections
    is_field_error=True,
    is_alignment_error=True,
    error_file="errors.tfs",
)

The two error switches are independent and default to False. Alignment imports DX, DY, DPSI from the error TFS, moving only magnetic fields while apertures and SC boundaries stay fixed. Nonzero unsupported alignment components raise an error. Twiss transfer and resampled Twiss imports reject alignment; use this element mode. See Element errors for normalization, instance matching and tracking order. generate_from_tfs also accepts is_alignment_error.

Method 3: Smooth approximation twiss

No MADX file required; uses analytical formulas to generate twiss points with constant β function. \(\beta = C / (2\pi Q)\) . Suitable for quick testing.

from PASS.para.smooth import generate_smooth_twiss

items, names, circum = generate_smooth_twiss(
    circumference=569.1,
    qx=9.47, qy=9.43,
    num_points=100,
    longitudinal_transfer="off",
)

Method 4: Load a MAD-X sequence or LINE

PASS.para.madx_sequence.load_madx_sequence loads a .seq or .madx source through cpymad, a required dependency installed automatically with PASS. No separate installation extra is needed. A source may define either a MAD-X SEQUENCE or a LINE. Select its name with sequence_name (default "ring").

The source is executed by MAD-X, including its CALL statements and any explicit calculation or file-output commands. Relative includes resolve from the entry file’s directory. Use an entry that defines the lattice and its dependencies, then let the loader request the desired optics. The loader’s own calculation files use a temporary directory; output commands inside the supplied source retain their MAD-X behavior. Source EALIGN/EFCOMP node errors are rejected because expanding the selected lattice again would clear them. Load an ideal lattice and apply errors through the existing error-TFS import options. Node kicks produced by CORRECT are also rejected; save the intended corrector strengths as explicit element parameters before loading.

Loading a source and converting it to PASS commands are separate steps. optics_method=None exports the expanded element parameters without calculating optics. This is sufficient for physical-element tracking:

from PASS.para.madx import read_madx_elements
from PASS.para.madx_sequence import load_madx_sequence
from PASS.para.schema.sequence import Sequence

loaded = load_madx_sequence(
    "lattice.seq",
    sequence_name="ring",
    beam_source="file",
    optics_method=None,
)
items, names, circumference = read_madx_elements(loaded.elements)
sequence = Sequence()
for name, item in zip(names, items):
    sequence.add(name, item)

The example creates the lattice portion of a PASS sequence. Add the normal Injection and monitor commands and use the returned circumference when preparing the complete simulation input.

The loader returns the following attributes:

Loaded MAD-X data

Attribute

Meaning

elements

TfsDataFrame of expanded element parameters, including source fringe attributes for compatibility checks.

twiss

TfsDataFrame of optics when requested; otherwise None.

beam

Dictionary describing the reference beam used for the export/calculation.

warnings

Import warnings to review alongside the resulting tables.

madx_version

Version of the MAD-X engine used by cpymad.

Choose the reference beam explicitly:

  • beam_source="file" requires a real BEAM declaration in the loaded source or its included files. A missing declaration raises an error; MAD-X’s implicit default beam is not a substitute.

  • beam_source="pass" requires main and one explicitly selected bunch. Both accept PASS schema instances or dictionaries using Python field names or JSON aliases. This uses the particle identity in MainConfig and the selected bunch’s kinetic energy per nucleon. It does not choose a bunch implicitly when several are present.

For example, the second choice can reuse an existing PASS configuration:

loaded = load_madx_sequence(
    "lattice.seq",
    sequence_name="ring",
    beam_source="pass",
    main=main_config,
    bunch=selected_bunch,
    optics_method="ptc",
)

Here main_config and selected_bunch are the actual configuration objects or dictionaries for the intended run. The source’s normalized magnet strengths and geometry remain the input lattice parameters; choosing a different beam does not itself rematch the lattice.

For Twiss-map transport, request optics_method="twiss" for ordinary MAD-X TWISS or optics_method="ptc" for PTC, then pass loaded.twiss to either Twiss reader.

Ordinary TWISS does not implement the PTC/SAD quadrupole F1/F2 fringe branch. Use "ptc" to compute the enabled fringe model’s optics, including fringe_rebuild; selecting ordinary TWISS gives a different physical model.

For example:

from PASS.para.madx import read_madx_twiss

loaded = load_madx_sequence(
    "lattice.seq",
    sequence_name="ring",
    beam_source="file",
    optics_method="ptc",
)
items, names, circumference = read_madx_twiss(
    loaded.twiss,
    dqx="from_file",
    dqy="from_file",
    longitudinal_transfer="off",
)

read_madx_elements, read_madx_twiss and read_madx_twiss_interpolated accept these in-memory tables as well as TFS paths; preserve their headers when passing or saving them.

The PTC calculation explicitly uses MODEL=2, METHOD=6, EXACT=true, global NST=1, TIME=true, FRINGE=false, ICASE=5 and NO=2. Fringe models follow the element parameters; the global switch does not enable intrinsic fringes in otherwise disabled straight magnets. Positive element-specific integration settings remain part of the source model. The adapter converts the transverse optics column names and obtains full tunes from cumulative phase. It retains derivatives with respect to PT in the returned table; the existing Twiss readers apply the reference beta conversion to delta exactly once. Do not multiply those columns by beta before calling a reader.

Only the uncoupled optics supported by TwissItem can be imported: nonzero closed orbit, vertical dispersion and unsupported coupling are rejected. PTC-computed Twiss maps can include the linear optical effect of the fringe_rebuild model. Physical-element conversion preserves quadrupole F1/F2, straight-magnet FRINGE / FRNGMAX and entrance/exit kill flags for nonlinear fringe tracking. Ordinary Twiss tables often lack these parameters, so use loaded.elements from the sequence source. Twiss maps do not reproduce finite-amplitude nonlinear fringe tracking. See Physical entrance and exit fringe maps for parameter and global-switch conventions, and Graphical interface for source and beam selection in the import form.

Physical-element conversion reuses the existing PASS tracking maps. It also rejects unsupported element types and nonzero attributes such as TILT, source apertures, combined-function bend gradients and a bend K0 that differs from ANGLE/L. These checks describe the current conversion scope; successfully computing source optics does not establish physical-element tracking support for every source attribute. MAD-X’s generic MULTIPOLE uses a thin PTC map without the thick straight-magnet fringe attributes; conversion rejects its nonzero fringe bits 2/4. A directly configured PASS thick Multipole supports intrinsic fringes. Solenoid fringe attributes and non-quadrupole F1/F2 remain unsupported. Sequence expansion corrects only shared-boundary position roundoff of up to four floating-point spacings; meaningful overlaps and the strict geometry rules for external TFS tables remain unchanged.

MAD-X BPM markers and monitors

read_madx_elements, read_madx_twiss, read_madx_twiss_interpolated and generate_from_tfs recognize MONITOR, HMONITOR and VMONITOR. Their bpm_monitor argument defaults to None. Element tracking uses the following rules, where TFS S is the element exit and L its length:

BPM import behavior

BPM length

bpm_monitor

Imported element sequence

Monitor position

L = 0

None (default)

Marker

No recording

L > 0

None (default)

One Drift of length L

No recording

L = 0

Selected monitor template

Selected monitor only

S

L > 0

Selected monitor template

Drift(L/2) → monitor → Drift(L/2)

S - L/2 (BPM center)

Thus a finite BPM preserves its full flight distance without an extra marker. Default finite-length BPM drifts may merge with adjacent drifts. With recording enabled, merging preserves the center boundary occupied by the monitor. The two Twiss readers retain Twiss transport instead of adding physical drifts; they insert or split a transport point at the exact monitor position, including the center of a finite BPM in the interpolated grid. Zero-length BPMs retain a marker only when recording is disabled. Legacy Twiss tables without an L column treat BPMs as points; export L to place monitors at finite-length BPM centers.

To record data at every imported BPM, supply a monitor template with type and that monitor’s schema fields. The same settings are applied at every BPM:

from PASS.para.madx import read_madx_elements

items, names, circum = read_madx_elements(
    twiss_file="lattice.tfs",
    bpm_monitor={
        "type": "StatMonitor",
        "output_format": "hdf5",
        "write_interval_turns": 16,
    },
)

The same bpm_monitor dictionary works with either Twiss reader and generate_from_tfs. Choose StatMonitor, ParticleMonitor, DistMonitor or PhaseAdvanceMonitor; fields may use their Python names or JSON aliases. Position, command type and ordering are supplied by the importer, so do not include s, command or order (or their aliases). The MAD-X horizontal/vertical BPM keyword does not restrict the selected PASS monitor’s recorded coordinates.

Generated PASS command names omit a terminal numeric MAD-X occurrence suffix: bpm:1 at 422.0936762 m becomes bpm_s422.094. Position distinguishes repeated element names; only identical generated labels receive _2, _3, etc. Raw source names remain unchanged for BPM selection and magnetic-error instance matching.

To monitor only part of the lattice, pass bpm_names (exact names) and/or bpm_patterns (regular expressions). Both match the original MAD-X NAME case-insensitively, before generated name suffixes or drift merging. Regular expressions use search matching; use ^ and $ to anchor a whole name. The two lists are combined by union. With both arguments None all BPMs are selected; an explicitly empty selection, such as bpm_names=[] with no patterns, selects none. No matches is valid and inserts no monitors. Selectors must be lists of strings; a bare string, non-string entry or invalid regular expression raises ValueError. Duplicate source names select every occurrence. The monitor template and selector lists are not modified.

bpm_options = {
    "bpm_monitor": {"type": "StatMonitor"},
    "bpm_names": ["BPM.01", "BPM.07"],
    "bpm_patterns": [r"^BPM\.ARC[12]\."],
}
items, names, circum = read_madx_elements(
    "lattice.tfs", is_merge_drift=True, strict_geometry=True, **bpm_options,
)

Unselected BPMs follow the default rule: a finite-length drift or a zero-length marker. Twiss imports do not add a center point for an unselected finite BPM. Without bpm_monitor, selectors do not enable recording. The same selector arguments are accepted by all three readers and generate_from_tfs.

All four entry points also accept strict_geometry=False. Enabling it checks finite, ordered source positions and valid lengths, the imported transport intervals, and the final circumference. Element mode additionally checks that each entrance/exit agrees with the accumulated physical lengths, so equal total lengths alone cannot hide local gaps or overlaps. Twiss mode checks its transport intervals instead of requiring sampled-row L values to sum to the circumference. Actual S values must lie in [0, C] and remain nondecreasing without a tolerance allowance. Length/cumulative-length and ring-coverage comparisons, including computed entrances S-L, use an absolute tolerance of 1e-6 m. In Twiss mode, point commands must share the runtime position-sorting key with a transport endpoint; being within 1e-6 m alone does not suffice. A failed strict check raises ValueError describing the inconsistency; numeric comparisons include the expected and actual values, their difference and tolerance. The default retains permissive import behavior for compatibility. In either mode, adjacent drifts can merge only when their physical intervals are contiguous; a monitor or marker still prevents merging across its position.

To inspect the conversion after merging, use the original TFS file and the returned items/names with the same BPM options:

from PASS.para.bpm import describe_madx_bpms

bpm_report = describe_madx_bpms(
    "lattice.tfs", items, names, element_mode=True, **bpm_options,
)

The report contains one dictionary per original BPM occurrence, including source_name, occurrence, keyword, length, source_s, selected, measurement_s, conversion and outputs. Occurrence numbers start at 1 and count all rows with that exact original name, including non-BPM rows. selected means a monitor was enabled and the BPM matched. measurement_s is None when no monitor is inserted. Each output lists its final name, command, s, length and merged flag, so a BPM that became part of a larger drift can still be identified. Use element_mode=False for either Twiss reader. Source identities used by the report do not add fields to generated tracking JSON.

ParticleMonitor requires max_tag. Set DistMonitor.save_turns to select saved turns; its empty default saves no distributions. Set PhaseAdvanceMonitor.turn_ranges to enable analysis. For that monitor, omitted beta/alpha fields come from each BPM’s BETX, BETY, ALFX and ALFY. For a finite BPM, source exit optics and dispersion are propagated backward through half its drift length to obtain the monitor-center values. Closed-orbit offsets remain zero unless explicitly configured; source X, PX, Y and PY are not imported as monitor offsets, because the generated tracking model may not reproduce that closed orbit. Imported DX and DPX are multiplied by \(\beta_0\) to use PASS’s momentum-deviation convention. Explicit optical settings override these per-BPM defaults; explicit dispersion already uses PASS units. See Monitors and output for each monitor’s parameters and output.

Mixed Mode

Twiss transfer points and physical elements can be mixed within the same sequence. For example, inserting an RF cavity into a twiss sequence:

from PASS.para.schema.elements import RFCavityItem

from PASS.para.schema.sequence import Sequence
from PASS.para.schema.bunch import BunchConfig, InjectionItem
from PASS.para.schema.elements import QuadrupoleItem
from PASS.para.schema.monitors import StatMonitorItem

bunch = BunchConfig(kinetic_energy=45e6, num_real_particles=100000000000,
                    num_macro_particles=2048, emit_x=2e-6, emit_y=2e-6)
seq = Sequence()
seq.add("injection", InjectionItem(s=0.0, bunches=[bunch]))

from PASS.para.smooth import generate_smooth_twiss
twiss_items, twiss_names, circumference = generate_smooth_twiss(251.327, 4.8, 4.4, 17)

# Twiss transfer points
for i, item in enumerate(twiss_items):
    seq.add(f"twiss_{i:04d}", item)

# insert RF cavity (at s=0)
seq.add("rf1", RFCavityItem(s=0.0, components=[dict(voltage=100e3, harmonic=1, phase=0.5236)]))

External Data File Conversion

Magnet ramping, RF and exciter inputs use TFS tables. Quadrupole, Sextupole, Octupole and Multipole read normalized strengths against physical time in seconds; each nonempty bunch samples once at the element entrance and uses that strength throughout the element. See Magnet ramping for the supported columns and sampling model. RF uses its own TIME, VOLTAGE, FREQUENCY, PHASE interface, documented in RFCavity.

Generate a magnet program

Write a breakpoint table directly; the tracker performs piecewise-linear interpolation and holds endpoint values outside the listed interval:

from PASS.para.tools.ramping import write_magnet_ramping
from PASS.para.schema.elements import QuadrupoleItem

write_magnet_ramping(
    "quadrupole_ramp.tfs",
    times=[0.0, 0.05, 0.10],               # physical seconds
    columns={"K1L": [0.20, 0.25, 0.22], "K1SL": [0.01, 0.0, -0.01]},
)
quad = QuadrupoleItem(
    s=10.0, length=0.5, num_slices=8,
    is_ramping=True, ramping_file="quadrupole_ramp.tfs",
)

The writer validates the table and adds TIME_UNIT="s", STRENGTH_CONVENTION="normalized" and strength-unit metadata. Supply absolute strengths, not multipliers. Non-integrated K1, K2 and similar columns require a positive magnet length; integrated K1L, K2L and similar columns also support thin lenses. Normal and skew components can be supplied independently. Do not specify both K and KL for the same component.

Convert an external file

The dedicated converter reads CSV, whitespace-separated TXT or TFS. Map source column names to the required strength names and explicitly convert the time unit. It preserves the original sample times, without converting to turns or resampling a dense turn grid:

from PASS.para.tools.ramping import convert_magnet_ramping

convert_magnet_ramping(
    input_path="external_ramp.csv",
    output_path="quadrupole_ramp.tfs",
    time_column="time_ms",
    time_scale=1e-3,                       # milliseconds -> seconds
    column_mapping={"normal": "K1L", "skew": "K1SL"},
    delimiter=",",
)

For unusual headers or skipped rows, use read_magnet_ramping_source(input_path, delimiter=None, header=0, skiprows=0) from the same module, extract the selected numeric columns, and call write_magnet_ramping. Time units in source TFS metadata must agree with the selected time conversion. Strength-unit metadata is checked when supplied; strengths are not implicitly converted from physical magnetic fields.

The GUI offers both import/conversion and breakpoint generation under Tools → Data conversion → Magnet ramping; see GUI tools. After exporting, enable ramping and select the file in the supported element.

Legacy convert_external_to_tfs, convert_k1l_ramping and related helpers retain their historical turn-table behavior. The tracker can accept a legacy TIME_S column only when it contains physical seconds; a TURN-only table cannot drive magnet ramping. Do not infer elapsed time from a row number or an instantaneous revolution frequency during acceleration. Use the physical-time writer or converter above for new inputs.

RF files remain separate:

from PASS.para.tools.rf_data import convert_rf_data

convert_rf_data("llrf.csv", "rf_physical_time.tfs")

Parameter scans and validation

model_copy(update=...) does not validate updated values. Rebuild a schema object from a field dictionary, then validate the complete generated input before tracking:

from PASS.para.schema.main import MainConfig

baseline = MainConfig(circumference=251.327)
candidate = {**baseline.model_dump(), "num_turns": 128}
scan_config = MainConfig.model_validate(candidate)

The Python field num_electron is a historical name for the signed charge number, not a bound-electron count. num_proton=1, num_neutron=0, num_electron=1 therefore specifies a proton. Python field names belong in schema constructors; JSON inputs must use the aliases shown in the tables.

Architecture Overview

The parameter modules construct, validate and serialize the input objects:

PASS/para/
├── schema/       Parameter definitions (field definitions and aliases)
│   ├── main.py         MainConfig: global simulation parameters
│   ├── bunch.py        BunchConfig + OffsetConfig + InjectionItem
│   ├── twiss.py        TwissItem: twiss transfer point
│   ├── elements.py     Element configuration classes
│   ├── monitors.py     StatMonitor / DistMonitor / PhaseAdvanceMonitor
│   ├── space_charge.py SpaceChargeConfig + SpaceChargeResourceConfig + SpaceCharge
│   ├── electron_cloud.py ElectronCloudConfig + ElectronCloudConfiguration + ElectronCloudItem
│   ├── ibs.py      IBSConfig + IBSConfiguration + IBSOpticsConfig + IBSItem
│   └── sequence.py     Sequence: ordered container + auto-sorting
├── madx.py        MADX TFS → schema objects (element / twiss / error)
├── madx_sequence.py MAD-X source → element / optics tables through cpymad
├── smooth.py      Analytical smooth approximation twiss
├── tools/        External data → PASS TFS
│   ├── data_converter.py General data conversion pipeline
│   ├── ramping.py         Element ramping file generation
│   ├── rf_data.py         RF data file generation
│   └── exciter_data.py    Exciter data file generation
├── toolkit.py    sort_sequence + class_map + apply_element_settings + build_sequence
└── api.py        High-level API (generate_input / load_input / generate_from_tfs)

The data flow is as follows:

MADX TFS / user parameters / external data files
           │
           ▼
     madx.py / smooth.py + tools/  → schema objects / TFS files
           │
           ▼
      schema/ (pydantic)     ← field definitions and aliases: validation + aliases
           │
           ▼
     api.py (generate_input) → beam0.json
           │
           ▼
      PASS engine (Config → Beam → CommandSequence → Executor)

API entry points

PASS.para.api exports generate_input, build_sequence, generate_from_tfs and load_input. load_input(path) returns (MainConfig, raw_sequence_dict); it does not reconstruct typed commands or return the top-level Space charge/Wake field/Electron cloud/Intrabeam scattering blocks. It validates electron-cloud and IBS configurations and references before returning. Preserve those blocks explicitly when editing a complete existing file.

When generate_from_tfs fills an omitted bunch kinetic_energy, MAD-X ENERGY and MASS are the complete particle’s total and rest energies in GeV. The mass number is A = main_cfg.num_proton + main_cfg.num_neutron after parsing MainConfig; neither beam_name nor the charge count determines it. For A > 0, the imported kinetic energy is (ENERGY - MASS) * 1e9 / A in eV/u. Electrons and positrons have A = 0 and use (ENERGY - MASS) * 1e9 in eV per particle. An explicitly supplied kinetic_energy is preserved in the corresponding PASS units.

This conversion preserves the TFS kinetic energy and leaves PASS’s runtime species-mass convention unchanged; see Injection / Particle Generation. If the TFS rest mass differs from the mass used by PASS, the resulting reference gamma and beta need not equal their TFS values.