Varian / Agilent VnmrJ NMR data directories
Varian and Agilent NMR spectrometers (VNMRS and INOVA consoles, VNMR and VnmrJ software) store an experiment as a .fid directory. OpenReadout returns the FID as a trace with one sweep per stored trace, and the acquisition parameters from procpar. Processed data in datdir is listed, not decoded.
Derived from nmrglue’s documentation and source (BSD-3-Clause, read as prior art and run as a reference reader) and public data directories. Provenance: docs/provenance/varian-nmr.md. Format id varian-nmr, family nmr.
A data set is a directory <name>.fid/, not a file. OpenReadout opens the directory, or a path to its fid, procpar, text or log (resolve_varian_dir). A folder holding several .fid directories (a study as nmrXiv ships it) is detected (find_varian_experiments) but not opened: the error lists the directories (exit 2). A directory with acqus is a Bruker experiment, never a Varian one.
<name>.fid/ fid binary: 32-byte file header, then blocks (block headers + traces) procpar parameter text (optional: without it only the layout is known) text free-text title (sample description) log acquisition log (listed, not interpreted) datdir/phasefile, datdir/data processed data, when present (listed, not decoded)The fid file (FidHeader, BlockHeader)
Everything is big-endian (every corpus file, as nmrglue documents).
File header (FILE_HEADER_BYTES = 32), as nmrglue’s get_fileheader unpacks it (six 32-bit integers, two 16-bit, one 32-bit):
| offset | our name | meaning |
|---|---|---|
| 0 | block_count |
blocks in the file |
| 4 | traces_per_block |
traces in each block (1 in every corpus file) |
| 8 | points |
values per trace, real and imaginary counted separately (np) |
| 12 | element_bytes |
2 or 4 |
| 16 | trace_bytes |
points × element_bytes |
| 20 | block_bytes |
traces_per_block × trace_bytes + block_headers × 28 |
| 24 | version_code |
16-bit software/file version code (0 in the corpus) |
| 26 | status |
16-bit status bits (below) |
| 28 | block_headers |
block headers at the start of each block (1 in the corpus) |
problems lists every inconsistency (negative counts, sizes that do not add up, an element size the status bits contradict); a header with problems is not treated as a Varian file by sniff (read_fid_header), and a directory whose fid fails is not opened.
Status bits (status_names; meanings as nmrglue documents them, names ours): 0x1 data, 0x2 spectrum, 0x4 int32, 0x8 float32, 0x10 complex, 0x20 hypercomplex, 0x80 acquisition_parameters, 0x100 secondary_fourier_transform, 0x200 transposed, 0x800/0x1000/0x2000/0x4000 np_dimension/nf_dimension/ni_dimension/ni2_dimension.
Sample type (VarianSampleType, sample_type): Float32 when 0x8 is set, else Int32 when 0x4 is set, else Int16 (nmrglue find_dtype). Values are converted to f64 exactly (decode_varian). Observed: 0xc9 (float32) in every VNMRS file, 0x49 (float32 without 0x80) on the VnmrJ 4.2 INOVA, 0x45 (int32) on the older INOVA. int16 (dp='n') appears in no corpus file; it is covered by synthetic tests only.
Block header (BLOCK_HEADER_BYTES = 28; four 16-bit, one 32-bit, four float32): scale, status, index (1-based block number), mode, completed_scans (the scan count of that block), left_phase, right_phase, level, tilt (drift correction). With two block headers per block the second is a hypercomplex header (nmrglue get_hyperheader); it is skipped. Block scale is 0 in every corpus file and, as in nmrglue, never applied; check reports blocks with a non-zero scale (block_scale). The INOVA int32 file carries non-zero level/tilt; they are reported in info --view full (first block), not applied.
Traces: each trace holds points / 2 complex points, real and imaginary interleaved (nmrglue uninterleave_data). Sweeps are the traces in disk order: sweep s is trace s mod traces_per_block of block ⌊s / traces_per_block⌋ — nmrglue’s as_2d=True order. Arrayed (array) and multidimensional (ni, ni2, phase arrays) data are not reordered; extra.arrayed_parameters, array_size and indirect_dimensions say how the sweeps are organised. In every corpus file arraydim equals the number of traces (check: array_mismatch otherwise) and the block numbers run 1, 2, 3… (block_index).
A file shorter than declared_len is truncated: info notes it, read_trace of a missing block is a corrupt-file error (exit 4) and check reports truncated at the first incomplete block.
Parameters (procpar: Procpar, ProcparParam, ProcparValues)
Text (UTF-8, else Latin-1 → latin1); one parameter per three or more lines (nmrglue get_parameter):
name subtype basic_type max min step group display_group protection active intptr— we keepname,subtype,basic_typeandactive(0 = switched off).- value count, then the values: reals on the same line (
ProcparValues::Real); strings in double quotes, the first on this line and each further one on its own line (ProcparValues::Text). A string left open continues on the next line;\"and\\are unescaped. - enumeration count, then the allowed values (
enumeration).
Problems (short value lists, non-numeric reals, missing lines) go to issues; check reports them as parameter_syntax. Lookup: get, real (first real value), text (first string, trimmed, non-empty), first_text, len, is_empty; to_json gives info --view full’s vendor tree (procpar). parse_procpar parses bytes.
extra of the trace (our name ← parameter)
| our name | from | notes |
|---|---|---|
kind, file |
– | time_domain, fid |
axis |
sw |
{quantity: time, unit: s, first: 0, step: 1/sw, size} |
nucleus, nucleus_name |
tn |
H1 → 1H, C13 → 13C (inferred rewrite); the name as written |
spectrometer_frequency_mhz |
sfrq |
|
decoupler_nucleus, decoupler_frequency_mhz |
dn, dfrq |
|
spectral_width_hz |
sw |
also the trace’s sample_rate_hz (complex data) |
spectral_width_ppm |
sw / sfrq |
computed |
time_domain_size |
np |
values per trace (real + imaginary) |
scans, completed_scans, dummy_scans |
nt, ct, ss |
|
receiver_gain |
gain |
|
pulse_program |
seqfil |
sequence name |
experiment |
pslabel |
parameter-set label |
solvent |
solvent |
as written (cdcl3, none) |
temperature_c |
temp |
°C as written (0 when no temperature control is recorded) |
acquired_at, completed_at |
time_run, time_complete |
YYYYMMDDThhmmss → ISO-8601 without zone (spectrometer local time, inferred) |
acquisition_date |
date |
as written (Jun 6 2007) |
console, instrument |
console |
vnmrs, inova |
system_name |
systemname_ |
|
probe |
probe_ |
|
operator |
operator_ |
|
sample_name, comment, title |
samplename, comment, the text file |
|
software, software_version |
parver |
VnmrJ VERSION 4.2 REVISION A → VnmrJ, 4.2 REVISION A; format_version is parver as written (else procpar version <parversion>) |
arrayed_parameters, array_size |
array, arraydim |
|
indirect_dimensions[] |
ni/ni2/ni3, sw1/sw2/sw3, phase/phase2/phase3, dn/dn2/dn3 |
{dimension, increments, spectral_width_hz, phase_values, nucleus} for each niN ≥ 1 |
block_count, traces_per_block, block_headers_per_block, status_code, status_flags, sample_type, byte_order |
file header | |
block_scale, block_completed_scans |
first block header |
Experiment facts (Dataset::experiment)
| experiment field | from |
|---|---|
sample.id |
samplename (unless empty or none) |
sample.name |
first line of text |
acquisition.operator |
operator_ |
The derived model adds vendor (format), model (console), software and version (parver), nucleus, pulse program, spectrometer frequency, solvent, scans, start time from the trace extra.
check finding codes
bad_header, truncated, odd_points (errors); extra_bytes, block_index, missing_parameters (no procpar), parameter_syntax, np_mismatch, array_mismatch, missing_parameter (sw) (warnings); block_scale, non_utf8 (info).
Observed corpus values
| id | console / software | fid |
notes |
|---|---|---|---|
nmrglue-agilent-1d |
vnmrs, no parver (2007) |
1 × 3000 float32 | 13C CP, nt 512 |
nmrglue-agilent-2d |
vnmrs | 332 × 3000 float32 | ni 166, array phase |
nmrglue-agilent-2d-tppi |
vnmrs | 600 × 2800 float32 | TPPI, no phase array |
nmrglue-agilent-3d |
vnmrs | 11264 × 2500 float32 | array phase,phase2 |
nmrglue-agilent-4d |
– (no procpar) |
1536 × 2800 float32 | layout only |
nmrpy-test1-fid |
inova, parversion 5.1 |
24 × 31084 int32 | 31P, arrayed nt, non-zero lvl/tlt |
nmrpy-test2-fid |
inova, VnmrJ 4.2 | 1 × 32768 float32 | status 0x49 |
nmrxiv-s325-1h |
vnmrs, VnmrJ 4.0 | 1 × 32768 float32 | samplename, text |
nmrxiv-s501-carbon |
vnmrs, VnmrJ 4.2 | 1 × 65536 float32 | |
nmrxiv-s501-ghsqcad |
vnmrs, VnmrJ 4.2 | 200 × 3232 float32 | ni 100, array phase |
Vocabulary (every public identifier in crates/openreadout-nmr/src/varian_*.rs must appear here)
| identifier | meaning |
|---|---|
VarianReader, VarianDataset, VARIAN_FORMAT_ID, open, procpar |
reader entry points: format reader, opened directory (core Dataset), the id varian-nmr; the parsed procpar |
FidHeader, block_count, traces_per_block, points, element_bytes, trace_bytes, block_bytes, version_code, status, block_headers, parse, sample_type, problems, status_names, declared_len |
the 32-byte file header |
FILE_HEADER_BYTES, BLOCK_HEADER_BYTES |
32 and 28 |
BlockHeader, scale, index, mode, completed_scans, left_phase, right_phase, level, tilt |
one block header (status as above) |
VarianSampleType { Int16, Int32, Float32 }, width, dtype, decode_varian |
stored sample type; big-endian decoding |
read_fid_header, resolve_varian_dir, find_varian_experiments |
detection and path resolution |
Procpar, params, issues, latin1, get, real, text, to_json, parse_procpar |
the parsed procpar |
ProcparParam, name, subtype, basic_type, values, enumeration, active, first_text, len, is_empty |
one parameter |
ProcparValues { Real, Text } |
its values |
How this reader was derived, file by file: provenance log.