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 from the main window¶
Open EPR Endstation Control, leave Dry run checked, click Run protocol…, and choose a YAML protocol. Output and completion status appear in the application log. A second protocol cannot start while one is active.
For a first dummy-data check, choose protocols/preliminary_tuning.yaml and Continue through its five checkpoints. GUI dry runs use canned data and display checkpoint dialogs; ordinary CLI --test auto-continues. Neither dry-run path creates acquisition or handoff files. Invalid YAML is reported in the log.
Checkpoints offer Continue, Skip and Abort. Failure prompts offer Retry, Skip and Abort; a proposed coarse-stage fallback offers Re-run coarse stage or No. Closing a dialog aborts the run. Skipping a prerequisite may cause a later dependent step to fail.
Stop protocol requests interruption and lets the acquisition worker finish its cleanup. The GUI remains responsive and shows a stopping state. The button becomes Force stop; pressing it sends a second interrupt during cleanup. Allow normal cleanup to finish whenever possible. The application refuses to close while the protocol process is active.
During a live ringing ladder, Stop returns RV to 60 dB. Stopping at a checkpoint or in another step leaves RV unchanged. Instrument locks are released when the runner exits. Uncheck Dry run only when ready for a supervised live run.
Running live¶
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.)
- Open the MW bridge control window and let its vane homing finish. It may remain open during the run; manual commands are blocked while the runner owns the bridge.
- 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 the following files:
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. worker_stdout.log— acquisition-worker output, including FPGA library messages, kept out of the main progress log. This file is created when a live acquisition worker starts.- 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.
Preliminary tuning¶
From the ITC checkout, set the sample and scan bounds in protocols/preliminary_tuning.yaml, then validate and dry-run it:
epr-auto validate protocols/preliminary_tuning.yaml
epr-auto run protocols/preliminary_tuning.yaml --test
This covers ringing, the optional resonator scan, echo search, optimization and a simulated fine-tuning handoff. The dry-run uses canned data and writes no acquisition or handoff files. See Preliminary tuning before a supervised live run. Answer live checkpoints in the launcher dialogs or in a real terminal.
For daily use, keep preliminary.yaml and the experiment protocol in one working folder. Run them in this order: preliminary.yaml → tuned/fine_tuning.yaml → the experiment protocol. The preliminary run creates tuned/ beside its YAML; the fine-tuning run updates tuned/echo_cal.phase_awg for the experiment. Use that preset with window: preset and apply_cal: none, keeping the tuned RV and synthesizer settings.
Fine-tuning data are saved under runs/<date>_fine in the working folder. To keep preliminary data and the exported archive there too, use an output template such as output: runs/{date}_preliminary when launching from the working folder, or the equivalent absolute path when launching elsewhere. Relative output paths resolve from the launch directory; publish_dir resolves from the preliminary protocol's directory.