Build Packages / Create release (push) Successful in 7s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 4m56s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m10s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 13m51s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 16m56s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 20m59s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m25s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 21m17s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 14m14s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m59s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 15m52s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 19m33s
Build Packages / Generate python client (push) Successful in 29s
Build Packages / Build documentation (push) Successful in 1m19s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 20m49s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 25m31s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 25m24s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 26m5s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 22m19s
Build Packages / Unit tests (push) Failing after 2h28m24s
Build Packages / Validation Battery (push) Successful in 40m33s
* Rugnux: Processes a collection live while jfjoch_broker writes it (`rugnux <master.h5> <broker URL>`). * Rugnux: Processing starts on the first images and confirms its choices once all are in, so analysis can begin during collection. * Rugnux: Faster processing, especially on servers with several GPUs and CPU sockets and on computers without a GPU (`--no-gpu`, macOS). * Rugnux: Uses much less GPU memory, so large data sets stay on the GPU of smaller cards, and a data set too large for the card is processed on the CPU instead. * Rugnux: On macOS, Bragg integration runs on the Apple GPU. * Rugnux: Better handling of twinning and pseudo-symmetry, with the twin hypothesis written as `<prefix>_twin_<SG>.mtz`, and a wrong `--model` no longer confirms the data or decides the hand. * Rugnux: `--model` accepts predicted (AlphaFold) models and falls back to molecular replacement when the model is not placed as a rigid body (experimental `--mr` forces it), settling space group, hand and twinning and writing a different answer as `<prefix>_mr.mtz`. * Rugnux: Molecular replacement is faster and settles the space group in more cases with several copies in the asymmetric unit. * Rugnux: Deposition-ready mmCIF, a richer report, and numbered steps on the terminal with the full log in `<prefix>_log.txt`; `_P1.mtz`, `--estimate-beam-center` and `--beam-center-search` are removed. * Rugnux: The beam-stop shadow is found correctly on tilted detectors. * Rugnux: Stills files that record a stationary goniometer axis get the default stills geometry refinement. * jfjoch_viewer: "Analyze dataset" uses the same defaults as a bare `rugnux <file>`, and shows the results of processed CBF, marCCD and SMV sweeps. * jfjoch_viewer: The Files panel refreshes on F5 and every few seconds, and the plot type and other selections stay put during live collection. * jfjoch_writer: Memory stays bounded and old data files are closed efficiently on long runs, any failure ends the run in an error state the broker is told about, and the master file appears only after its data files; the ZeroMQ pusher logs when a stalled writer makes it drop images. * Fixes: The TCP image pusher no longer blocks the broker's receiver, GPU out-of-memory failures stop with a clear message, and the XDS plugin masks module and chip edges. --------- Co-authored-by: Filip Leonarski <jungfrau@filips-mac-mini.home> Reviewed-on: #85 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
93 lines
4.4 KiB
C++
93 lines
4.4 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
// SMV: the format ADSC Quantum detectors wrote, and which Rayonix and several others still write.
|
|
// A plain ASCII header of "KEY=value;" lines between braces, then the pixels - no container, no
|
|
// compression, no binary header to decode by offset. The header states its own length, so even
|
|
// that is not assumed.
|
|
//
|
|
// {
|
|
// HEADER_BYTES= 512; DIM=2; BYTE_ORDER=little_endian; TYPE=unsigned_short;
|
|
// SIZE1=3072; SIZE2=3072; PIXEL_SIZE=0.102588; DISTANCE=250.000000;
|
|
// OSC_START=325.000000; OSC_RANGE=0.200000; WAVELENGTH=0.999839;
|
|
// BEAM_CENTER_X=157.500000; BEAM_CENTER_Y=157.500000;
|
|
// }
|
|
//
|
|
// Note the beam centre is in MILLIMETRES, not pixels, and its convention is the one thing writers
|
|
// disagree about - see the comment on beam_x_mm in the struct.
|
|
//
|
|
// Rigaku's d*TREK writes the same container with a different vocabulary: the detector and the
|
|
// goniometer as named circles with their vectors (CCD_GONIO_VALUES, ROTATION_VECTOR, ...), the beam
|
|
// position in pixels, and its own overflow encoding for 16-bit pixels. A header naming
|
|
// DETECTOR_NAMES is read that way.
|
|
namespace smv {
|
|
|
|
struct Header {
|
|
int64_t nx = 0; // SIZE1, the fast dimension
|
|
int64_t ny = 0; // SIZE2, the slow dimension
|
|
size_t data_offset = 0; // HEADER_BYTES
|
|
bool little_endian = true;
|
|
int64_t bytes_per_pixel = 2; // TYPE: unsigned_short
|
|
double pixel_x_m = 0;
|
|
double pixel_y_m = 0;
|
|
double distance_m = 0;
|
|
// BEAM_CENTER_X/Y, converted to pixels here. Writers disagree about this field more than about
|
|
// any other: it is in millimetres, and which of the two is the fast direction is a convention
|
|
// rather than a rule. Where a file also carries the ADSC-era synonyms (BEAM_CENTRE_X, or the
|
|
// MOSFLM-ordered pair) they are read too. A run whose centre is wrong is recoverable - the
|
|
// beam-centre estimator measures it from the data - but a wrong one is not detectable here.
|
|
double beam_x_px = 0;
|
|
double beam_y_px = 0;
|
|
double wavelength_A = 0;
|
|
double start_angle_deg = 0; // OSC_START, falling back to PHI
|
|
double angle_increment_deg = 0; // OSC_RANGE
|
|
double two_theta_deg = 0;
|
|
double exposure_s = 0;
|
|
int64_t saturation = 0; // CCD_IMAGE_SATURATION: the value a saturated pixel carries
|
|
std::string detector; // DETECTOR_SN, where stated
|
|
std::string axis_name = "phi";
|
|
// d*TREK only (Rigaku CrystalClear): the geometry is stated as vectors rather than the ADSC
|
|
// scalars, in the imgCIF laboratory frame - z from the sample to the source, y up. Absent on an
|
|
// ADSC header, where two_theta_deg above carries the one detector circle there is.
|
|
std::optional<std::array<double, 3>> fast_direction;
|
|
std::optional<std::array<double, 3>> slow_direction;
|
|
std::optional<std::array<double, 3>> spindle_axis;
|
|
struct Circle {
|
|
std::array<double, 3> axis;
|
|
double angle_deg;
|
|
};
|
|
std::vector<Circle> detector_circles; // header order: the outermost circle first
|
|
// RAXIS_COMPRESSION_RATIO: a stored value above 32767 is (value - 32768) * ratio. 0 = not used.
|
|
int64_t overflow_ratio = 0;
|
|
std::map<std::string, std::string> raw; // every key, for anything not modelled above
|
|
};
|
|
|
|
// True if the path names something this reader can open: an SMV file, or a directory holding at
|
|
// least one. Reads a few hundred bytes at most.
|
|
bool CanRead(const std::string &path);
|
|
|
|
// The files of one sweep, in collection order. path is a directory, or one frame inside it.
|
|
std::vector<std::string> CollectSweep(const std::string &path);
|
|
|
|
// Header only - reads just the leading brace block. Cheap enough to call per frame.
|
|
Header ReadHeader(const std::string &path);
|
|
|
|
// Header + pixels. out is resized to nx * ny.
|
|
Header Read(const std::string &path, std::vector<int32_t> &out);
|
|
|
|
// The same, widening the stored pixels into memory the caller already has. scratch carries the
|
|
// raw bytes between frames so a worker walking a sweep allocates once - on Windows only; elsewhere the
|
|
// file is mapped and scratch is not used.
|
|
Header ReadInto(const std::string &path, int32_t *out, size_t capacity, std::vector<uint8_t> &scratch);
|
|
|
|
} // namespace smv
|