jfjoch_writer writes rugnux's .mtz, _maps.mtz and _model.cif from the END blocks

New gemmi-only library JFJochRugnuxFiles (writer/), linked by JFJochWriter, so
the writer needs no analysis library and builds with JFJOCH_WRITER_ONLY:
- WriteMergedMtz / WriteMapCoefficientsMtz / PlacedModelStructure make the
  files from the structured blocks; WriteRugnuxResultFiles writes whichever
  blocks END carries beside the master, each under a temporary name and renamed
  into place, an existing file replaced only when the run's overwrite is set.
  A file that fails costs only itself (logged), not the run.
- FillRugnuxResultBlocks reads the blocks back from the files rugnux wrote at
  a prefix (p.mtz, p_maps.mtz, p_model.cif), for the broker to fill END.

WriteMtzReflections now goes through WriteMergedMtz (MergedReflectionsForWriter
builds the block); its output is byte-identical to before, checked by md5 on a
synthetic set in both the Friedel-merged and the anomalous layout.

StreamWriter writes the files after the master and before the END ACK, so the
ACK means they are in place; END receipt and each rename are logged at INFO
with the wall-clock time, for last-frame-to-files latency. The broker's direct
HDF5 pusher writes them too.

Test: rugnux's three files -> blocks -> CBOR -> writer reproduces all of them
(and _model.pdb) byte for byte; a ~25k-reflection END is 2.7 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-17 12:19:51 +02:00
1 parent 91f6563953
commit aca27be862
14 files changed
+706 -85

No files matched your search

+5 -2
View File
@@ -4,6 +4,7 @@
#include "../common/JFJochException.h"
#include "StreamWriter.h"
#include <chrono>
#include <utility>
#include "FileWriter.h"
@@ -203,14 +204,16 @@ void StreamWriter::ProcessEndMessage() {
if (state == StreamWriterState::Idle || state == StreamWriterState::Finalized)
return;
if (verbose)
logger.Info("Received end message");
logger.Info("Received end message at wall clock {:.3f} s",
std::chrono::duration<double>(std::chrono::system_clock::now().time_since_epoch()).count());
if (state != StreamWriterState::Error) {
try {
if ((image_puller_output.cbor->end_message->max_image_number == 0) && (max_image_number > 0))
image_puller_output.cbor->end_message->max_image_number = max_image_number;
file_writer->WriteHDF5(*image_puller_output.cbor->end_message);
// Before the END ACK below, so that the ACK says these files are in place too.
file_writer->WriteResultFiles(*image_puller_output.cbor->end_message, logger);
} catch (const std::exception &e) {
// std::exception for the same reason as in ProcessDataImage. Here the cost of a leak is the
// highest: the END ACK and the notification below are never sent, the state never reaches