From ae7b68dd68fed12f4ce0e193aec0a4f14ae550f5 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Tue, 20 Oct 2026 16:39:44 +0200 Subject: [PATCH] rugnux stills: a given cell is where the geometry refinement starts, not what it fits the distance to On stills a cell and a detector distance are degenerate: a spot's radius fixes only their ratio. The geometry refinement that -C switches on anchored the cell and refined the distance, so a deposited cell a percent off this crystal moved a correct distance by as much (MPCCD: 50.47 -> 50.14 mm, R-free 0.259 -> 0.318). Now the distance is held and the cell refined from the spots, starting from the given one. Only where the frames have rejected the file's distance (DecideStillsGeometry's grid, which is what -C recovers a bad header distance with) is the cell anchored and the distance refined from it. The grid itself asks the given cell, not one refined at the file's distance. Co-Authored-By: Claude Opus 5.5 --- .../geom_refinement/GeometryRefiner.cpp | 2 ++ .../geom_refinement/GeometryRefiner.h | 2 ++ rugnux/Rugnux.h | 5 +++++ rugnux/RugnuxStills.cpp | 22 +++++++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/image_analysis/geom_refinement/GeometryRefiner.cpp b/image_analysis/geom_refinement/GeometryRefiner.cpp index 45630b291..ded2b3dd5 100644 --- a/image_analysis/geom_refinement/GeometryRefiner.cpp +++ b/image_analysis/geom_refinement/GeometryRefiner.cpp @@ -258,6 +258,8 @@ GeometryRefinerResult RefineGlobalGeometry(const DiffractionGeometry &nominal_ge problem.SetParameterUpperBound(cell_len, j, 1.3 * cell_len0[j]); } + if (!settings.refine_distance) + problem.SetParameterBlockConstant(&distance_mm); problem.SetParameterBlockConstant(detector_rot); // tilt not refined problem.SetParameterBlockConstant(rot_vec); problem.SetParameterBlockConstant(cell_ang); // angles not refined in v1 diff --git a/image_analysis/geom_refinement/GeometryRefiner.h b/image_analysis/geom_refinement/GeometryRefiner.h index 5dce6c73a..ccd18cd4e 100644 --- a/image_analysis/geom_refinement/GeometryRefiner.h +++ b/image_analysis/geom_refinement/GeometryRefiner.h @@ -36,6 +36,8 @@ struct GeometryRefinerSettings { // Cell-scale regularizer strength (anchors the cell lengths to the input cell so the otherwise // degenerate distance is determined). Large => cell effectively fixed (the validated safe regime). double cell_reg_coeff = 30.0; + // With the distance held, the cell is no longer degenerate with it and is refined from the spots. + bool refine_distance = true; int rounds = 3; // hkl-reassignment / tolerance-tightening rounds double beam_range_px = 8.0; // beam bound around the nominal value double distance_range_mm = 8.0; // distance bound around the nominal value diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 4cac16c57..23da36c49 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -1381,6 +1381,11 @@ class Rugnux { const std::vector &frames); // Returns whether the run's geometry was moved off the file's. bool DecideStillsGeometry(int start_image, int images_to_process); + // Stills: the cell the user gave (-C / reference), kept apart from the one the geometry refinement + // writes into experiment_, and whether the detector distance has been taken from it - only once the + // frames have rejected the file's distance (DecideStillsGeometry). See RefineStillsGeometry. + std::optional stills_user_cell_; + bool stills_distance_from_cell_ = false; // Stills: where the frames' pixel values pile up, against the file's saturation limit (see there). void DetectSaturation(int start_image, int images_to_process); diff --git a/rugnux/RugnuxStills.cpp b/rugnux/RugnuxStills.cpp index 440ddbcec..02d516729 100644 --- a/rugnux/RugnuxStills.cpp +++ b/rugnux/RugnuxStills.cpp @@ -95,7 +95,9 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_ return; } // The bundle anchors a known cell; without one there is nothing to refine against. - const auto cell = experiment_.GetUnitCell(); + if (!stills_user_cell_) + stills_user_cell_ = experiment_.GetUnitCell(); + const auto cell = stills_user_cell_; if (!cell.has_value()) { logger.Warning("--refine-geometry needs a reference unit cell (-C / -S / reference MTZ). Skipping."); return; @@ -245,6 +247,16 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_ GeometryRefinerSettings gr_settings; gr_settings.crystal_system = experiment_.GetCrystalSystem(); gr_settings.num_threads = config_.nthreads; + // A cell and a distance are degenerate on stills: a spot's radius fixes only their ratio. Which one + // to believe is a question of which is the better measurement. A deposited or textbook cell is + // routinely a percent off this crystal (another temperature, another batch), and a detector distance + // a facility states is usually better than that - so the distance is held and the cell refined, with + // the user's cell only the starting point. Only where the frames have already shown the file's + // distance to be wrong (DecideStillsGeometry, which is what -C buys on a header with a bad distance) + // is the cell the better of the two, and then it is anchored and the distance refined from it. + gr_settings.refine_distance = stills_distance_from_cell_; + if (!stills_distance_from_cell_) + gr_settings.cell_reg_coeff = 0.0; const GeometryRefinerResult r = RefineGlobalGeometry( experiment_.GetDiffractionGeometry(), *cell, strong, gr_settings); @@ -388,6 +400,8 @@ bool Rugnux::DecideStillsGeometry(int start_image, int images_to_process) { const auto trial = [&](float x, float y, float distance, const std::vector &on) { DiffractionExperiment e = experiment_; e.BeamX_pxl(x).BeamY_pxl(y).DetectorDistance_mm(distance); + if (stills_user_cell_) + e.SetUnitCell(*stills_user_cell_); // the grid asks the user's cell, not the one refined at the file's distance const StillsTrial t = IndexStillsSample(e, pool, on); logger.Debug("Stills geometry trial: centre ({:.2f},{:.2f}) distance {:.2f} mm - {}/{} frames indexed, " "{} of their {} spots on the lattice", x, y, distance, t.indexed, t.frames, @@ -454,8 +468,12 @@ bool Rugnux::DecideStillsGeometry(int start_image, int images_to_process) { best_distance = grid_best * f; } } - if (score_here < FILE_KEPT_ABOVE * best) + if (score_here < FILE_KEPT_ABOVE * best) { distance = best_distance; + stills_distance_from_cell_ = true; + if (stills_user_cell_) + experiment_.SetUnitCell(*stills_user_cell_); + } logger.Info("Stills detector distance: the file's {:.2f} mm puts {} sampled spots on the lattice, the best " "between {:.0f}% and {:.0f}% of it {} at {:.2f} mm - the run uses {:.2f} mm", file_distance, score_here, 100 * MIN_FACTOR, 100 * MAX_FACTOR, best, best_distance, distance);