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);