Terminology and conventions#
Documentation is the generic guide, and states what belongs in each
form of documentation and how it should be written. This page holds the
conventions specific to pulserver, and governs every docstring, documentation
page, code comment and user-facing diagnostic string in this repository,
including the C and C++ doc comments under src/. It is binding on human
contributors and on automated agents alike; AGENTS.md requires compliance
with both.
The target register is reference documentation for MRI researchers and sequence developers who run acquisitions on clinical scanners. The reader is assumed to know MR physics, Pulseq and the MRD raw-data format. The documentation’s job is to state what an object is, in the field’s own vocabulary, together with the semantics a signature cannot carry: units, frames, the layer a statement belongs to, state and invariants.
The failure mode this guide exists to prevent is the replacement of an established MRI, Pulseq or MRD term by a paraphrase of what the thing does. A reader then has to reconstruct the standard concept from a description of it. Repeating the correct technical term is always preferable to varying the wording.
1. Terminology is the substance#
Use the established term. Do not explain around it.
Do not write |
Write |
|---|---|
what the operator asks for |
the requested protocol, the prescription |
what the sequence will actually play |
the resolved protocol |
the numbers the scanner keeps |
the scanner parameters |
a design written earlier |
a stored design, by its identifier |
the folder both sides look at |
the design store |
the scanner program |
the interpreter |
the host process for one sequence |
the interpreter host process |
the compiled form of a sequence |
the IR, the IR cache |
a distinct block, amplitudes aside |
a base block |
the part that repeats, the repeating unit |
the repetition, the TR |
pieces of a TR, a segment definition |
a virtual segment |
one occurrence of such a piece |
a segment instance |
the order the pieces are played in |
the execution stream |
filling in the raw data |
MRD enrichment |
what the reconstruction runs on |
the reconstruction worker |
the reconstruction’s code |
the reconstruction plugin |
the sequence’s code |
the scanner-sequence plugin, the sequence function |
Distinctions that must not be blurred#
Protocol. A prescription or requested protocol is what the operator enters. The resolved protocol is what the design achieves and what the interpreter plays. A listing carries entries with their UI schema; a value block carries values only. A preset is a negative time value standing for a design choice, not a time.
Design. A sequence function is a function of the scanner limits and the
protocol arguments that returns the designed sequences, a chain with the
prescans first and the main sequence last. A scanner sequence is the
pulserver SequencePlugin that binds a sequence function, its app
attribute, to protocol entries. “App” names that attribute only; in prose the
bound function is the sequence function. A plugin is the file a scanner sequence or a reconstruction is loaded
from; say which kind. The evaluation of a prescription is what the scanner
sequence’s evaluate returns: the resolved protocol, the scan time, a note and,
optionally, the RF layout. An RF layout is the RF definitions of a sequence,
the RF instances of one TR in play order with the TR as its period, and for
each instance the control its amplitude follows, the flip angle or a float
user entry. The list and validate replies carry it when asked, as an
estimate; the design the interpreter plays is the stored one.
Storage. A design is one generated or imported chain with its IR cache,
immutable once written. Its identity is the hash of what it depends on; its
identifier is the first 18 hexadecimal digits of the identity. The design
store is the directory holding every design. A design call is one list,
validate, generate or import of an interpreter host process; no call
keeps state beyond the designs it stores. A design is pushed to the design intake, the
proxy’s HTTP endpoint writing its store, as a bundle of the files of its
directory.
IR. The IR follows the model of PulSeg, whose terms are used for its
concepts. A subsequence is one file of a NextSequence chain. An event
definition is a distinct RF, gradient or ADC event after deduplication; an
event instance is its occurrence in a block, with its own amplitude. A base
block is a block with its waveform amplitudes normalised; blocks that differ
only in amplitudes, offsets, rotation or, for a pure delay, duration share one.
A virtual segment is an ordered list of base blocks, a reusable structural
unit that need not be periodic; a segment instance is one occurrence of it,
with the amplitudes, offsets, rotations and durations it plays; the execution
stream is the order of segment instances over the scan, which accounts for
every block. The repetition of a subsequence is the period
pypulseqpp.Sequence.repetition() reports, the whole subsequence where it
does not repeat; the conversion finds virtual segments within it. Do not write
“repeating unit”, which implies that every sequence is periodic, nor “segment
definition” for a virtual segment. A TRID label marks a safety group here,
not a segment boundary. The IR cache is the file; the collection is what a
reader loads from it.
Virtual scanner. The stand-ins for the scanner in tests: the virtual interpreter, which plays a cache with the C library’s cursor, the phantom acquired along the trajectory it plays, and the virtual reconstruction client, which sends the samples as the scanner’s reconstruction client does. The design calls, the IR, the proxy and the plugins are not stand-ins.
Raw data. A series is one MRD stream from the reconstruction client. An
acquisition is one MRD readout record; a readout is the ADC event of the
sequence it corresponds to. Encoding counters are the MRD idx fields;
flags are the MRD acquisition flags; an encoding space is an entry of the
header’s encoding list. Enrichment replaces these with what the sequence
states.
Layers. The Pulseq representation is the content of a .seq file. The
engines are pypulseqpp (design) and the reconstruction a plugin imports.
Orchestration is pulserver. Scanner execution is what the interpreter does
on the hardware. Do not attribute a property of one layer to another: a
design is not a .seq file, and the IR cache is not the design of record.
Fixed vocabulary#
design calls, warm server, reconstruction proxy, reconstruction server;
pulserver.hostandpulserver.proxywhen the module is meant.interpreter host process, not “host interpreter” or “interpreter process”.
interpreter for the scanner-side program; reconstruction client for the scanner-side sender of raw data.
IR and IR cache;
.psegis a file extension, not a name for the representation.MRD for the format; ISMRMRD for the library and the HDF5 file.
2. Register#
Write dry, declarative technical prose.
No personification. A server does not decide, a design does not know, a proxy does not wait for anything it is not blocked on, a plugin does not ask. Objects have properties and functions have behaviour.
No literary compression. Do not describe an object by a relative clause where a noun exists: not “the directory the design calls write into” but “the design store”; not “what the design managed” but “the resolved protocol”.
No taglines. An API summary classifies its object.
No conversational or tutorial voice. No “simply”, “just”, “note that”, “under the hood”, “powerful”, “seamless”. No rhetorical questions.
No history. Describe the code as it is. No “used to”, “previously”, “this replaces”, no named fixed bugs, no comparison with removed designs.
Variation is not a virtue. Use the same term for the same concept every time it appears.
3. Summary lines#
The first line is one sentence, on one line, ending in a period.
Functions and methods take an imperative or third-person declarative verb that classifies the operation: “Return…”, “Read…”, “Format…”, “Segment…”.
Classes take a noun phrase naming what the class represents.
Modules and packages take one line naming the responsibility.
Do not restate the signature, the annotations or the parameter names in prose.
4. Units and frames#
Every documented quantity carries its unit.
Quantity |
Unit |
|---|---|
Time entry of a protocol, on the wire and in a scanner parameter |
integer µs |
Time argument of a sequence function |
s |
Float protocol entry |
the entry’s |
Scan time in a |
s; |
Times, frequencies and flip angles of an RF block |
s, Hz and degrees; the amplitude of an instance in an |
Rasters passed to the IR conversion |
s in |
Gyromagnetic ratio, field strength |
Hz/T, T |
Field-of-view offset in a protocol or an import block |
mm |
Field-of-view offset passed to |
m |
Prescription rotation ( |
unitless, element (i, j) of an orthonormal matrix |
PNS limit ( |
fraction of the nerve model’s threshold |
Forbidden band |
Hz; amplitude allowed in it in mT/m |
SAR ratio ( |
unitless: energy per pulse over that of the reference pulse, the local term scaled by the head limit over the local limit |
SAR limits ( |
W/kg |
Default channel weights ( |
unitless magnitude, phase in rad |
VOP safety factor ( |
unitless, at least 1, on local SAR |
Transmit configuration ( |
an opaque string the scanner reports |
Dwell time in an enriched acquisition ( |
µs |
k-space trajectory |
1/m, as pypulseqpp reports it |
Grid trajectory ( |
k times the reconstructed field of view; an N-point matrix spans [-N/2, N/2) |
Design identifier |
18 hexadecimal digits: three 24-bit integers, each exact in a float32 |
State the coordinate frame wherever a position or a k-space quantity appears. The field-of-view offset is expressed along the logical readout, phase and slice axes. The prescription rotation maps the logical axes to the physical x, y and z gradient axes, physical = R logical. The trajectory is expressed along the sequence’s x, y and z gradient axes, with the sequence’s block rotations applied and no prescription rotation. The host’s checks are made in the physical frame, with the prescription rotation composed after the block rotations.
State the precision a value is exchanged at when it matters: time entries are rounded to the nearest microsecond, ties to even, and float entries to six significant digits.
5. Safety language#
Pulserver runs the timing, gradient, PNS, mechanical-resonance and sound pressure checks, which pypulseqpp implements, under the limits the interpreter passes, before it writes an IR cache, and refuses a design that fails one. It writes each subsequence’s sound pressure levels into the cache, and from pypulseqpp’s SAR check each subsequence’s SAR relative to a reference pulse. The interpreter computes the SAR and the gradient and RF heating with the scanner’s own routines. The checks compute estimates; passing them does not establish scanner or patient safety.
Never write “safe”, “validated”, “compliant” or “approved” of a sequence, a protocol or a design.
A valid
VALIDATEreply means the plugin’s evaluation accepted the prescription under the scanner limits it was given. State that, not more.
6. Source of truth#
Existing documentation is not evidence. Before writing or changing a substantive statement, verify it against, in order of authority:
the implementation being documented, including
src/cpp/andsrc/c/;the tests that protect it;
pypulseqpp, for sequence design and the Pulseq format;
the ISMRMRD specification and library, for MRD;
the Pulseq specification and the primary literature, cited by DOI.
Do not infer semantics from a name, a type annotation or another docstring. If a contract cannot be established from these sources, say what is known and leave the rest undocumented rather than guessing.
Do not print measured constants that are not guaranteed across releases or hardware.
7. What to document, and what not to#
The Docstrings section of AGENTS.md governs docstrings: document what the
name, signature and annotations do not already state, and write no docstring
for a private helper whose behaviour is evident. Use local comments for
implementation detail and for a choice a reader would otherwise undo.
8. Format#
NumPy-style docstrings throughout, rendered by
sphinx.ext.napoleon.Documentation pages are Markdown with MyST roles and directives.
C and C++ documentation comments are Doxygen-style and follow this guide’s terminology and register rules in full.
9. Do not churn#
Rewriting correct technical prose for stylistic preference wastes review effort and risks introducing errors. Leave alone documentation that already uses the conventional term, states its units and frame, and classifies its object. When modifying code, clean up nearby documentation that clearly violates this guide; do not broaden a focused change into a repository-wide rewrite unless that is the task.