stills: detector tilt refined in the geometry pre-pass, kept where held-out frames agree
The stills geometry bundle (RefineGlobalGeometry) can now refine the detector tilt together with the beam, the distance, the cell and the modules. With the tilt free the beam block is the direct beam, from which the PONI is worked out inside the residual: in PONI terms a tilt moves the whole pattern as a moved beam does (D tan(tilt)/pixel), more than the beam's 8 px box holds, while the direct beam does not move with it and the tilt is left with the keystone. The two tilt-like rows of ModuleGauge are gone: within a module a tilt is a gradient no rigid move makes, and the rows cost held-out accuracy. The shift, turn and outward gauges stay. The tilt is a hypothesis (DecideStillsTilt): the strong frames of the last geometry refinement are split in two halves, each fitted with the tilt held and refined (modules placed in both where the run placed them), each fit scored on the other half, and the tilt is kept where the refined fits predict the held-out spots better. No threshold. It is decided after the stills beam-centre trial and its re-refinement, on frames indexed at the settled centre: a centre error on frames indexed at it reads as a tilt. --rot1/--rot2 or --no-refine-tilt hold the tilt on stills. The stills beam-centre trial now compares direct beams. Its hypotheses (the file's centre, the background walk, the point-symmetry capture) are taken as the point the beam lands on and each trial puts the PONI where its distance and the run's tilt land the direct beam there; the walk, fitted through the geometry's tilt, returns a PONI and is converted. The pre-scan beam-centre check log compares the walk's direct beam, not its PONI, with the file's direct beam. Without a tilt nothing changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
1 parent
f72950612a
commit
a39e18b80f
9 files changed
+319
-64
No files matched your search
@@ -149,3 +149,53 @@ TEST_CASE("ScanModuleShifts_FindsShiftedModule") {
|
||||
CHECK(many[m].on_lattice_at_zero == one[m].on_lattice_at_zero);
|
||||
}
|
||||
}
|
||||
|
||||
// A tilted detector, one panel: started from the untilted geometry with the beam where the beam really lands
|
||||
// (the direct beam, which is what a beam-centre measurement gives), the refinement with the tilt must find it
|
||||
// and put the PONI where the tilt has it - 10 px from the direct beam here, further than the beam's 8 px box
|
||||
// would reach in PONI terms - and the fit with the tilt held must be worse. With the image mirrored and turned
|
||||
// as well, the direct beam has to be taken through the image orientation.
|
||||
TEST_CASE("RefineGlobalGeometry_Tilt") {
|
||||
for (const auto &orientation : {DetectorOrientation(), DetectorOrientation(true, 1)}) {
|
||||
DiffractionExperiment exp;
|
||||
exp.IncidentEnergy_keV(WVL_1A_IN_KEV).BeamX_pxl(1000).BeamY_pxl(1000).DetectorDistance_mm(150)
|
||||
.PoniRot1_rad(0.3 * PI / 180.0).PoniRot2_rad(-0.2 * PI / 180.0);
|
||||
exp.Detector().ImageOrientation(orientation);
|
||||
const DiffractionGeometry truth = exp.GetDiffractionGeometry();
|
||||
const auto [direct_x, direct_y] = truth.GetDirectBeam_pxl();
|
||||
REQUIRE(std::hypot(direct_x - 1000, direct_y - 1000) > 10);
|
||||
|
||||
// One module covering the whole image: the frames' spots, each where the tilted detector shows it.
|
||||
ModuleShift whole;
|
||||
whole.x0 = 1000;
|
||||
whole.y0 = 1000;
|
||||
const CrystalLattice latt(50, 60, 70, 90, 90, 90);
|
||||
std::vector<GeomRefineFrame> frames = FramesOnMovedModules(exp, latt, {whole, whole, whole, whole});
|
||||
REQUIRE(frames.size() > 30);
|
||||
for (auto &f: frames)
|
||||
for (auto &s: f.spots)
|
||||
s.module = -1;
|
||||
|
||||
DiffractionGeometry nominal = truth;
|
||||
nominal.PoniRot1_rad(0).PoniRot2_rad(0).BeamX_pxl(direct_x).BeamY_pxl(direct_y);
|
||||
GeometryRefinerSettings st;
|
||||
st.crystal_system = gemmi::CrystalSystem::Orthorhombic;
|
||||
st.num_threads = 2;
|
||||
|
||||
const auto held = RefineGlobalGeometry(nominal, latt.GetUnitCell(), frames, st);
|
||||
REQUIRE(held.ok);
|
||||
CHECK(held.rot1_rad == 0);
|
||||
CHECK(held.rot2_rad == 0);
|
||||
|
||||
st.refine_tilt = true;
|
||||
const auto tilted = RefineGlobalGeometry(nominal, latt.GetUnitCell(), frames, st);
|
||||
REQUIRE(tilted.ok);
|
||||
CHECK(tilted.rot1_rad * 180.0 / PI == Catch::Approx(0.3).margin(0.01));
|
||||
CHECK(tilted.rot2_rad * 180.0 / PI == Catch::Approx(-0.2).margin(0.01));
|
||||
CHECK(tilted.beam_x_px == Catch::Approx(1000).margin(0.2));
|
||||
CHECK(tilted.beam_y_px == Catch::Approx(1000).margin(0.2));
|
||||
CHECK(tilted.distance_mm == Catch::Approx(150).margin(0.05));
|
||||
CHECK(tilted.median_residual_px < 0.1);
|
||||
CHECK(tilted.capped_rms_px < held.capped_rms_px);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user