Each post-refinement now records, beside what it commits, what the integration it was handed realises at the geometry it started from: the held-out residual at nominal (the same value the log prints on the left of "held-out ... ->"), its standard error over the held-out residual values, and the cell the pass's own indexing refined. Nothing here changes a decision. Two helpers read them: HeldOutResidualFell (a pass at a new geometry realises a lower residual than another by more than the standard error of the difference) and ReindexPushesCellBack (re-indexing at a committed geometry returns, on the length the fit moved most, a cell on the side the fit moved away from). The geometry walk in RunAllPasses uses both in the next commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
54 lines
2.3 KiB
C++
54 lines
2.3 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <catch2/catch_all.hpp>
|
|
|
|
#include "../image_analysis/geom_refinement/PostRefine.h"
|
|
|
|
namespace {
|
|
PostRefineResult Measured(double residual, double se) {
|
|
PostRefineResult r;
|
|
r.held_out_before = residual;
|
|
r.held_out_before_se = se;
|
|
return r;
|
|
}
|
|
|
|
PostRefineResult Fit(UnitCell before, UnitCell after) {
|
|
PostRefineResult r;
|
|
r.cell_refined = true;
|
|
r.cell_before = before;
|
|
r.cell = after;
|
|
return r;
|
|
}
|
|
|
|
UnitCell Cell(float a, float b, float c) {
|
|
return UnitCell{.a = a, .b = b, .c = c, .alpha = 90.0f, .beta = 90.0f, .gamma = 90.0f};
|
|
}
|
|
}
|
|
|
|
TEST_CASE("PostRefine_HeldOutResidualFell", "[PostRefine]") {
|
|
// Down by more than the standard error of the difference, sqrt(3^2 + 4^2) = 5.
|
|
CHECK(HeldOutResidualFell(Measured(100.0, 3.0), Measured(94.0, 4.0)));
|
|
// Down, but within that noise.
|
|
CHECK_FALSE(HeldOutResidualFell(Measured(100.0, 3.0), Measured(96.0, 4.0)));
|
|
// Up.
|
|
CHECK_FALSE(HeldOutResidualFell(Measured(100.0, 3.0), Measured(110.0, 4.0)));
|
|
// Not measured on either side.
|
|
CHECK_FALSE(HeldOutResidualFell(PostRefineResult{}, Measured(10.0, 1.0)));
|
|
CHECK_FALSE(HeldOutResidualFell(Measured(100.0, 3.0), PostRefineResult{}));
|
|
}
|
|
|
|
TEST_CASE("PostRefine_ReindexPushesCellBack", "[PostRefine]") {
|
|
// The fit shortens b the most; re-indexing at its geometry returns a longer b: pushed back.
|
|
const auto fit = Fit(Cell(96.9f, 107.9f, 112.9f), Cell(96.8f, 106.6f, 112.9f));
|
|
CHECK(ReindexPushesCellBack(fit, Fit(Cell(96.7f, 107.6f, 112.6f), Cell(96.6f, 106.4f, 112.8f))) == -2);
|
|
// A walk that re-indexing follows (and overshoots a little) is not pushed back.
|
|
const auto walk = Fit(Cell(60.48f, 60.48f, 196.7f), Cell(60.05f, 60.05f, 195.2f));
|
|
CHECK(ReindexPushesCellBack(walk, Fit(Cell(60.00f, 60.00f, 195.1f), Cell(59.8f, 59.8f, 194.2f))) == 0);
|
|
// Nothing committed, or no cell measured by the next pass: no evidence either way.
|
|
PostRefineResult refused = fit;
|
|
refused.cell_refined = false;
|
|
CHECK(ReindexPushesCellBack(refused, Fit(Cell(96.7f, 107.6f, 112.6f), Cell(96.6f, 106.4f, 112.8f))) == 0);
|
|
CHECK(ReindexPushesCellBack(fit, PostRefineResult{}) == 0);
|
|
}
|