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 species label. |
|
|
|
|
Number of protons per particle; zero for electrons and positrons. |
|
|
|
|
Number of neutrons per particle. |
|
|
|
|
Signed charge number Z (q=Z e), not electron count; nonzero integer. |
|
|
|
|
Transition gamma of the lattice. |
|
|
|
|
Positive ring circumference (m). |
|
|
|
|
Number of simulated turns; positive integer. |
|
|
|
|
Compute backend: cpu or gpu. |
|
|
|
|
Coordinate storage: float32 or float64. |
|
|
|
|
Number of GPU devices. |
|
|
|
|
List of GPU device identifiers. |
|
|
|
|
Output root directory; relative paths resolve against the input JSON directory. Omitted or |
|
|
|
|
Optional task name, used as the run-folder name beneath |
|
|
|
|
Generate monitor figures from saved results after successful tracking; default output is the run’s |
|
|
|
|
TimingConfig: mode=command, log_interval=10, warmup_turns=1, include_io=True. |
|
|
|
|
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:
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
BunchConfigentries inbunchesThe requirement that
harmonic_idvalues 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 |
|---|---|---|---|
|
|
float |
Kinetic energy per nucleon (eV/u) |
|
|
int |
Number of real particles per bunch |
|
|
int |
Number of macro particles per bunch |
|
|
float |
Twiss β function |
|
|
float |
Twiss α function |
|
|
float |
RMS geometric emittance (m rad) |
|
|
float |
Bunch length |
|
|
float |
Momentum spread |
|
|
str |
Transverse distribution: |
|
|
str |
Longitudinal distribution: |
|
|
float |
Signed physical RF voltage (used in matchz/matchdp modes) |
|
|
float |
Physical RF waveform phase at the matching reference event (matchz/matchdp); see RF phase and charge sign in matching |
|
|
int |
Bunch-group index; its nominal slot is \(z_{\mathrm{center}}=h_{\mathrm{id}}C/h_{\mathrm{group}}\) |
|
|
float |
RF-cavity position relative to injection, used to linearly back-propagate a matched distribution to \(s=0\) |
|
|
float |
Mean bunch relative-momentum offset; mutually exclusive with the kinetic-energy offset |
|
|
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 haves=0)TwissItem— twiss transfer pointDriftItem,QuadrupoleItem,SBendItem, etc. — physical elementsStatMonitorItem,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:
The interpolated optical functions are:
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:
Attribute |
Meaning |
|---|---|
|
|
|
|
|
Dictionary describing the reference beam used for the export/calculation. |
|
Import warnings to review alongside the resulting tables. |
|
Version of the MAD-X engine used by |
Choose the reference beam explicitly:
beam_source="file"requires a realBEAMdeclaration 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"requiresmainand one explicitly selectedbunch. Both accept PASS schema instances or dictionaries using Python field names or JSON aliases. This uses the particle identity inMainConfigand 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 length |
|
Imported element sequence |
Monitor position |
|---|---|---|---|
|
|
|
No recording |
|
|
One |
No recording |
|
Selected monitor template |
Selected monitor only |
|
|
Selected monitor template |
|
|
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.