From 801faa797c6bf11c3ed281a43eae6de4e05e9daa Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 7 Oct 2026 06:30:20 +0200 Subject: [PATCH] --prepass-fraction: the geometry pre-pass image loop on the first part of the sweep only The two-pass rotation pipeline's first image loop (the geometry pre-pass whose reflections feed post-refinement) stops after the given fraction of the sweep; pass 2 still integrates every frame and is the only one written. Default 1 (unchanged). On the 25 sets ranked by the post-refinement held-out gain, --prepass-fraction 0.5 vs rc174 (REFMAC check): 0 verdict changes, d_min change median 0.000, R-free worse by >0.005 on 1/17; image-loop seconds -17%, myob wall 11.9 -> 10.5 s. Without post-refinement at all (--rotation-no-postrefine) the same sets had 5 verdict changes and 11/17 R-free worse. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi --- rugnux/Rugnux.cpp | 5 ++++- rugnux/Rugnux.h | 2 ++ rugnux/rugnux_cli.cpp | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 50e81e304..15423afad 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -6231,11 +6231,14 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b AzimuthalIntegrationProfile profile(mapping); HarmonicEvidence worker_harmonic; std::vector worker_offsets; + const int loop_end = geometry_prepass + ? start_image + static_cast(std::lround((end_image - start_image) * config_.prepass_fraction)) + : end_image; while (!cancelled_) { const int ordinal = next_ordinal.fetch_add(1); const int image_idx = start_image + ordinal * config_.stride; - if (image_idx >= end_image) break; + if (image_idx >= loop_end) break; // Reuse the raw image frame to frame - a fresh 4-byte 16 Mpx frame is 72 MB of // untouched pages the decoder faults in one by one - except while the writer thread diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 72ade5d92..0a0caf5e6 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -100,6 +100,8 @@ struct ProcessConfig { bool rotation_indexing = false; bool two_pass_rotation = true; int rotation_indexing_image_count = 100; + // The geometry pre-pass image loop stops after this fraction of the sweep (pass 2 reads all of it). + double prepass_fraction = 1.0; std::optional forced_rotation_lattice; // Stills global geometry-refinement two-pass (FullAnalysis, stills only). When set, an extra first diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index c8732e70f..8681d7739 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -142,6 +142,7 @@ void print_usage() { std::cout << " --force-rotation-lattice Force rotation indexer with external lattice (in Angstrom) : \"a0x,a0y,a0z,a1x,a1y,a1z,a2x,a2y,a2z\" (9 floats, skips first pass)" << std::endl; std::cout << " --rotation-scale Goniometer rotation scale: the stage turned k times the angle stored in the file (the commanded one). Applied to both passes; overrides the fitted correction" << std::endl; std::cout << " --rotation-no-postrefine Disable the (default-on) two-pass rotation post-refine (post-refine detector distance/beam + cell/axis, then re-integrate; only the refined pass is written, as the canonical _* output)" << std::endl; + std::cout << " --prepass-fraction Run the two-pass rotation geometry pre-pass on only the first fraction f of the sweep (0.05-1, default 1); pass 2 still reads every frame" << std::endl; std::cout << " -X, --indexing-algorithm Indexing algorithm (FFBIDX|FFT|FFTW|Auto|None)" << std::endl; std::cout << " -S, --space-group Space group number (96) or symbol (P43212) - for indexing and scaling" << std::endl; std::cout << " -C, --unit-cell Fix reference unit cell: \"a,b,c,alpha,beta,gamma\"" << std::endl; @@ -249,6 +250,7 @@ enum { OPT_FORCE_ROTATION_LATTICE, OPT_MIN_INDEXED_SPOTS, OPT_ROTATION_NO_POSTREFINE, + OPT_PREPASS_FRACTION, OPT_ROTATION_SCALE, OPT_BACKGROUND_CLIP, OPT_BACKGROUND_RADIAL, @@ -380,6 +382,7 @@ static option long_options[] = { {"polarization", required_argument, nullptr, OPT_POLARIZATION}, {"force-rotation-lattice", required_argument, nullptr, OPT_FORCE_ROTATION_LATTICE}, {"rotation-no-postrefine", no_argument, nullptr, OPT_ROTATION_NO_POSTREFINE}, + {"prepass-fraction", required_argument, nullptr, OPT_PREPASS_FRACTION}, {"rotation-scale", required_argument, nullptr, OPT_ROTATION_SCALE}, {"refine-geometry", optional_argument, nullptr, OPT_REFINE_GEOMETRY}, {"detect-beam-stop", optional_argument, nullptr, OPT_DETECT_BEAM_STOP}, @@ -752,6 +755,7 @@ static int RunRugnux(int argc, char **argv) { bool rotation_postrefine_geometry = true; // default on; --rotation-no-postrefine disables it std::optional rotation_scale; // --rotation-scale: asserted stage calibration int rotation_indexing_image_count = 100; + double prepass_fraction = 1.0; std::optional rotation_indexing_range; std::optional min_indexed_spots; // --min-indexed-spots: spots on the lattice for a frame to count as indexed bool run_scaling = true; // merge is on by default; --no-merge turns it off @@ -894,6 +898,9 @@ static int RunRugnux(int argc, char **argv) { case OPT_ROTATION_NO_POSTREFINE: rotation_postrefine_geometry = false; break; + case OPT_PREPASS_FRACTION: + prepass_fraction = parse_number_arg(optarg, "--prepass-fraction", logger, 0.05, 1.0); + break; case OPT_ROTATION_SCALE: rotation_scale = parse_number_arg(optarg, "--rotation-scale", logger, 0.9f, 1.1f); break; @@ -2813,6 +2820,7 @@ static int RunRugnux(int argc, char **argv) { config.measure_spot_budget = !max_spot_count_override.has_value(); config.rotation_scale = rotation_scale; config.rotation_indexing_image_count = rotation_indexing_image_count; + config.prepass_fraction = prepass_fraction; config.forced_rotation_lattice = forced_rotation_lattice; config.refine_geometry = refine_geometry; config.run_scaling = run_scaling;