One deposited marCCD sweep carries -2093438692 in its exposure field. Passed on as
written that is a negative count time, DiffractionExperiment refuses it, and the
whole dataset fails to open - over a number that feeds no geometry, no indexing and
no merge. With the field ignored when it is implausible, that sweep processes and
matches its deposition: P2_12_12_1, cell within 0.12%, and 1.375 A against a
deposited 2.0.
Neither format guarantees the field. The same guard is in the SMV reader, whose TIME
key is no better attested, and both say what they did rather than passing over it
silently.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two more of the formats deposited data actually arrives in, found by processing a
corpus of it: every PETRA III EMBL set is .cbf.gz, and NSRRC and the whole ADSC
Quantum era are SMV. Both were previously "no native input".
SMV is an ASCII "KEY=value;" block between braces, then the pixels - no container,
no compression, nothing to decode by offset - so reader/SMV.{h,cpp} and
JFJochSMVReader are a smaller job than the marCCD pair they sit beside, and need no
new dependency at all. Two things the format does not give us, both said out loud
rather than papered over:
* It states no saturation value, so overloads are judged on the 16-bit container
alone. That can only fail to call a pixel saturated, never condemn a good one,
but a CCD at the top of its range does saturate, so the reader warns once.
* Its beam centre is in MILLIMETRES and which of X/Y is the fast direction is a
convention rather than a rule. Measured on one ALS ADSC sweep the file's value is
TRANSPOSED: as stated it indexes 2/60 frames, and the run's own beam-centre
measurement (which adopts the right one automatically) indexes 60/60. Swapping it
here would fit that writer and might break another, so the header is read as the
format defines it and the measurement stays the arbiter. Revisit with a second
vendor's SMV in hand.
.cbf.gz needed only Slurp() in MiniCBF.cpp, through which every read already passes:
it sniffs the two-byte gzip magic - not the file name - and takes a zlib path when it
is there, leaving the plain path free of zlib's buffer copy. zlib-ng is already in the
build, so this is a link line, not a dependency. The sweep template grew a suffix,
because ".cbf" and ".cbf.gz" are separate sweeps and std::filesystem cannot split the
double extension on its own.
The viewer's single cbf_reader becomes three, dispatched by CanRead() in the same
order as rugnux. Dispatch is by CONTENT in both: ".img" is used by miniCBF, marCCD
AND SMV depending on the writer, and a PDB detector label has now been wrong about
the format four times, so an extension decides nothing.
Measured, de novo, no flags: 9fcg (1800 gzipped frames) gives P4 and a cell 0.06%
from the deposited one at 1.37 A against a deposited 1.54; 6oel (ADSC SMV) gives
F4132 - 96 operations, the most a protein space group can have - and a cell 0.05%
out, 100% indexed. Tests cover both formats and the transposed-beam-centre case with
fixtures written byte for byte, so they need no external data.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>