Quickstart¶
This page takes you from a fresh checkout to a dry-run of a protocol, then describes the prerequisites of a live run on the real spectrometer. It assumes you have already read the overview; the individual steps a protocol can call are catalogued in the step reference.
Install¶
epr_auto ships only with Atomize_ITC — the EPR-endstation variant of
Atomize. It is not part of plain Atomize and is not published to PyPI, so the
only way to get it is a source checkout of the ITC repository. Clone it and
install in editable mode, exactly as for the rest of the endstation build:
The editable install registers the console entry point epr-auto
(defined in pyproject.toml as atomize.epr_auto.cli:main). Everything below
can be typed either way — the two forms are identical:
Note
The machine at the endstation has python3 only — there is no python
on PATH. Use python3 and pip3 throughout.
The three commands¶
The runner exposes three subcommands.
| Command | Purpose |
|---|---|
epr-auto steps |
list every step and its parameters |
epr-auto validate <file>.yaml |
parse and check a protocol without running it |
epr-auto run <file>.yaml [--test] |
execute a protocol (--test = dry-run) |
epr-auto steps¶
Prints the step registry — the same catalogue rendered on the step reference page, but generated live from the code, so it can never drift. Each entry gives the step name, a one-line summary, and every parameter with its type, default (in brackets) and help text:
exp.t2
Hahn echo decay (T2/Tm), linear tau sweep, with fit
preset: preset file [hahn_echo_4s.phase_awg] — Linear Time moving-echo (Hahn) preset
tau_start: time ("300 ns") [300 ns] — first tau; the saved axis is the evolution time 2*tau
tau_step: time ("300 ns") [12 ns] — tau increment per point
points: integer, required — sweep points
scans: integer [1] — scan count — the ceiling when target_snr or max_duration shrink the run
...
epr-auto validate¶
Loads the protocol, resolves every preset path, substitutes the foreach
loop variables and range-checks every parameter — all the load-time checks a
run performs — but stops before executing anything. Every error is reported
as <file>:<line>: message, so a typo in a parameter or a preset that
resolves nowhere is caught at your desk rather than at the bench. On success it
echoes a one-line summary of what it parsed:
OK: field_series_t1t2.yaml — sample 'field_series', autonomy checkpointed, 5 entries (field.edfs, tune.echo_window, tune.auto_phase, tune.pi_calibration, foreach[B])
epr-auto run --test¶
Runs the protocol end-to-end in dry-run mode. This is the pre-flight check you run before every bench session:
=== overnight_t2.yaml | sample: test_sample | autonomy: checkpointed | 4 steps | DRY-RUN (test mode) ===
[1/4] field.edfs (line 8)
preset = /home/.../experiments/ed_4s.phase_awg
range = ['338 mT', '352 mT']
points = 200
scans = 1
pick = max
[checkpoint] would pause here (checkpointed mode) — auto-continuing in dry-run
[PASS] echo_snr: score=inf (note=dry-run, not judged)
-> field=3450.0 G, pick=max, canned=True
[2/4] tune.auto_phase (line 13)
preset = /home/.../experiments/hahn_echo_4s.phase_awg
...
[PASS] phase_coherence: score=inf (note=dry-run, not judged)
-> phase_deg=0.0, zero_order_deg=0.0, temperature_k=200.0, canned=True
...
=== finished: 4/4 steps ran ===
What a dry-run actually does¶
--test is not a syntax check — it exercises the whole framework's test
mode, the same pre-flight path used throughout Atomize. Before importing any
device module, the CLI rewrites sys.argv[1] to 'test'; every device module
reads that flag in its constructor and takes its test branch, so no hardware is
touched and no main-window GUI is needed. Concretely, a dry-run:
- instantiates the pulser, bridge, field and temperature controllers as
test-mode devices that return canned readings instead of talking to the
instruments (each step's result is flagged
canned=Truein the log); - pre-flights every step: the argument range-checks and the sequence overlap/length asserts that only fire in test mode run for real, so an illegal pulse geometry or an out-of-range setting is rejected here rather than at the bench;
- computes and logs every judge but does not gate on it — the log shows
[PASS] ... (note=dry-run, not judged), so a dry-run always runs to the end regardless of the (canned) scores; - flows canned results between steps through the session state, so a calibration produced by an earlier step is consumed by a later one exactly as it would be live;
- auto-continues at every checkpoint (
[checkpoint] would pause here …) instead of prompting, and writes nomanifest.json— the run record is log-only in test mode so a dry-run never litters the data directory.
The result is that a protocol is walked from first step to last, with all its validation and cross-step plumbing exercised, on a machine with no spectrometer attached.
Running live¶
Commissioning status
Live execution is enabled from the CLI, but the automation chain has not
yet been validated on the spectrometer — every step is implemented and
verified in dry-run (--test) only. Always dry-run a protocol first, and
keep the first live sessions in supervised autonomy with an operator
present.
Prepare a live session as follows:
- Launch the main Atomize GUI first, and keep it open. A live run's acquisition worker pushes its traces to the main window's LivePlot server; a real run dies without it. (A dry-run needs no GUI — see above.)
- Close the interactive field and temperature tools. At run start the
runner seizes the
field.paramandtemp_paramcross-process locks asepr_auto, the same discipline the four experiment-runner GUIs use to keep the interactive tools off the GPIB devices. If either lock is already held by another tool, the runner refuses to start; it releases the locks infinallyand onatexit, so a crash never leaves them stranded. - Run from the repository root. The CLI changes into
libs/itself, because the Insys FPGA driver readsbrd.ini/exam_adc.inirelative to the working directory at instantiation time; it also re-provisions the device config store and pre-flights every step in test mode before touching hardware. - Start in supervised autonomy until the chain is trusted. In
autonomy: supervisedthe runner pauses before every step (press Enter to continue), so you can watch each move; move up tocheckpointedand thenautonomousonce you trust the protocol.
Always dry-run a protocol with --test before its first live run.
Stopping a live run¶
Ctrl-C in the terminal is the clean stop. The acquisition worker child
ignores the interrupt; the parent reads the worker out and saves the trace
in flight, then aborts the protocol as an operator interrupt — recorded in the
manifest as aborted: operator interrupt, never as a retryable step failure, so
the run does not try to re-run the interrupted step. A second Ctrl-C bounds the
wait (a 60 s wind-down that still lets the worker close the pulser), and a third
terminates immediately.
Do not close the terminal to stop a run. Killing the terminal sends SIGHUP,
which skips the worker's pulser_close and can strand the FPGA card; use
Ctrl-C so the card is released cleanly.
The run directory¶
A live run writes everything for that session into one directory. By default it is:
where <date> is today's date (YYYY-MM-DD) and <sample> is the protocol's
sample: field with unsafe characters replaced by underscores. A protocol can
override the location with an output: template in which {date} and
{sample} expand (for example output: ~/epr_data/{date}_{sample}); a relative
template resolves against the directory you launched epr-auto from. If that
directory already holds a manifest.json — a same-day re-run of the same
sample — a _run2 / _run3 … suffix is appended so the earlier run is never
overwritten.
The directory holds three kinds of file:
manifest.json— the crash-safe run record, rewritten atomically after every step so an interrupted run still leaves a valid file. It carries the protocol name, sample, autonomy mode, start/finish timestamps and overall status, plus one entry per executed step: its resolved parameters, result, judge reports, attempt count, and — inside aforeach— the loop{var, value, index}.- A copy of the protocol, saved as
protocol_<name>.yaml, so the exact YAML that produced the data always sits next to it. - The acquisition CSVs, named
NNN_tag.csvwith a per-session counter, for example001_t2.csv. Inside aforeachiteration the loop stamp is folded into the name, so a field or temperature series is self-identifying —003_t2_B_3318G.csvis the third saved file, a T2 measurement, at theB = 3318 Gpoint of the loop.
Note
manifest.json and the CSVs are written only on a live run. A dry-run
logs the same information to the terminal but writes nothing to disk.
Next steps¶
- Writing protocols — the YAML schema in full:
autonomy,on_fail/retries,checkpoint, theforeachseries block and value substitution. - The tune-up chain — how the
tune.*steps compose into a calibration sequence and when each calibration is invalidated. - Examples — two annotated protocols walked through end to end.
- Troubleshooting — common failures, quoted verbatim, and what they mean.