Merge branch 'c-speed' into pool-C
This commit is contained in:
+103
-84
@@ -2060,12 +2060,100 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) {
|
||||
// Let every refined pass from here on ask the quality guard's question of itself, so one that
|
||||
// is going to be thrown away can skip the report and the files it would have superseded.
|
||||
quality_guard_pass1_ = pass1;
|
||||
logger.Info("Rotation two-pass geometry post-refinement: second pass (refined geometry, canonical) -> {}_*",
|
||||
base_prefix);
|
||||
config_.output_prefix = base_prefix; // the refined pass is the canonical result (no _02 suffix)
|
||||
// The canonical pass measures the post-refinement again where the pre-pass moved the geometry
|
||||
// (it applies nothing), so that the walk below has something to decide on.
|
||||
postrefine_probe_ = prepass_detector_geometry_.has_value();
|
||||
|
||||
const auto current_geometry = [&] {
|
||||
return std::array<float, 5>{experiment_.GetBeamX_pxl(), experiment_.GetBeamY_pxl(),
|
||||
experiment_.GetDetectorDistance_mm(),
|
||||
experiment_.GetPoniRot1_rad(), experiment_.GetPoniRot2_rad()};
|
||||
};
|
||||
const auto set_geometry = [&](const std::array<float, 5> &g) {
|
||||
experiment_.BeamX_pxl(g[0]).BeamY_pxl(g[1]).DetectorDistance_mm(g[2])
|
||||
.PoniRot1_rad(g[3]).PoniRot2_rad(g[4]);
|
||||
};
|
||||
|
||||
// Where the pre-pass could not tell whether its distance move paid (held_arm_beam_px: the
|
||||
// free distance lowered the fit's own held-out residual by less than that residual's noise),
|
||||
// the other hypothesis - the header distance, with the beam the same fit found there - is
|
||||
// measured too, and the two are judged on what they REALISE: the held-out residual a pass
|
||||
// re-integrated and re-indexed at that geometry measures there, the comparison the walk below
|
||||
// makes between its rounds. Where the free arm wins the run goes on at the post-refined
|
||||
// distance; where the header's wins, or the two cannot be told apart, the header's stands -
|
||||
// it is the hypothesis that asserts nothing, so a tie is its - and the distance walk below is
|
||||
// not entered: the data have just said they cannot place the distance. Measured on a sweep at
|
||||
// 820 mm reaching 2theta ~ 9 deg, where the fit's own gain never realises: the free arm's
|
||||
// re-integrated residual sat within 0.6 % of the header's at a noise of 8 %, and the cell it
|
||||
// carried was 1 % too large. On a sweep at 110 mm reaching 57 deg, a 0.14 % distance move the
|
||||
// pre-pass could not decide realised 3.7 % against a noise of 1.5 %, and the cell it carried
|
||||
// matched the reference to 0.04 %.
|
||||
//
|
||||
// Both arms are measured BEFORE the canonical pass, by passes that stop as soon as the
|
||||
// post-refinement has measured (postrefine_probe_only_). The residual is all the comparison
|
||||
// reads, and it is in hand before the scaling engine is built, so neither arm pays for a
|
||||
// merge, a space-group search or a set of correction surfaces - and only the arm that wins is
|
||||
// ever run as a canonical pass. Judging the arms on written passes instead cost the loser's
|
||||
// whole pass and, where the header won, a third pass that reproduced the loser's numbers
|
||||
// digit for digit.
|
||||
std::string distance_arm; // the arm decision, for the pass decision text
|
||||
bool distance_arm_kept_header = false;
|
||||
int arm_passes = 0;
|
||||
if (!cancelled_ && prepass_detector_geometry_ && pass1.post_refine
|
||||
&& pass1.post_refine->held_arm_beam_px) {
|
||||
const std::array<float, 5> free_geometry = current_geometry();
|
||||
std::array<float, 5> held_geometry = free_geometry;
|
||||
held_geometry[0] = static_cast<float>((*pass1.post_refine->held_arm_beam_px)[0]);
|
||||
held_geometry[1] = static_cast<float>((*pass1.post_refine->held_arm_beam_px)[1]);
|
||||
held_geometry[2] = header_geometry[2];
|
||||
logger.Info("Two-pass: the pre-pass could not tell whether moving the distance {:.3f} -> {:.3f} mm "
|
||||
"paid, so the header distance is re-integrated as well and the two are "
|
||||
"judged on the held-out residual each realises", header_geometry[2],
|
||||
free_geometry[2]);
|
||||
postrefine_probe_only_ = true;
|
||||
const auto free_arm = RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false);
|
||||
++arm_passes;
|
||||
set_geometry(held_geometry);
|
||||
const auto held_arm = RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false);
|
||||
++arm_passes;
|
||||
postrefine_probe_only_ = false;
|
||||
const double realised_free = free_arm.post_refine ? free_arm.post_refine->held_out_before : NAN;
|
||||
const double realised_held = held_arm.post_refine ? held_arm.post_refine->held_out_before : NAN;
|
||||
const double se_free = free_arm.post_refine ? free_arm.post_refine->held_out_before_se : NAN;
|
||||
const double se_held = held_arm.post_refine ? held_arm.post_refine->held_out_before_se : NAN;
|
||||
if (!cancelled_ && free_arm.post_refine && held_arm.post_refine
|
||||
&& HeldOutResidualFell(*held_arm.post_refine, *free_arm.post_refine)) {
|
||||
logger.Info("Two-pass: re-integrated at the post-refined distance {:.3f} mm the held-out "
|
||||
"residual is {:.3e} against {:.3e} at the header's {:.3f} mm (standard errors "
|
||||
"{:.1e} and {:.1e}) - the move is realised and stands", free_geometry[2],
|
||||
realised_free, realised_held, held_geometry[2], se_free, se_held);
|
||||
set_geometry(free_geometry);
|
||||
distance_arm = fmt::format(
|
||||
"the header distance {:.3f} mm was run as well and realised a held-out residual of "
|
||||
"{:.3e} against {:.3e} at the post-refined {:.3f} mm, so the post-refined distance "
|
||||
"stands", held_geometry[2], realised_held, realised_free, free_geometry[2]);
|
||||
} else if (!cancelled_) {
|
||||
logger.Info("Two-pass: re-integrated at the header distance {:.3f} mm the held-out residual "
|
||||
"is {:.3e}, which the post-refined {:.3f} mm does not beat ({:.3e}; standard "
|
||||
"errors {:.1e} and {:.1e}) - these data do not determine the distance, and the "
|
||||
"header's stands", held_geometry[2], realised_held, free_geometry[2],
|
||||
realised_free, se_held, se_free);
|
||||
distance_arm_kept_header = true;
|
||||
pass1.post_refine->distance_held = true;
|
||||
pass1.post_refine->beam_x_after_px = held_geometry[0];
|
||||
pass1.post_refine->beam_y_after_px = held_geometry[1];
|
||||
pass1.post_refine->distance_after_mm = held_geometry[2];
|
||||
distance_arm = fmt::format(
|
||||
"the header distance {:.3f} mm kept: re-integrated there the held-out residual is "
|
||||
"{:.3e}, which the post-refined {:.3f} mm does not beat ({:.3e}), so these data do "
|
||||
"not determine the distance", held_geometry[2], realised_held, free_geometry[2],
|
||||
realised_free);
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("Rotation two-pass geometry post-refinement: second pass (refined geometry, canonical) -> {}_*",
|
||||
base_prefix);
|
||||
auto pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
|
||||
|
||||
// Walk the geometry to the fit's fixed point. The post-refinement is fitted to reflections
|
||||
@@ -2099,87 +2187,6 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) {
|
||||
// last - and says it did not converge; the quality guard below judges it against the header
|
||||
// pass as it judges every refined pass.
|
||||
constexpr int MAX_GEOMETRY_ROUNDS = 8;
|
||||
const auto current_geometry = [&] {
|
||||
return std::array<float, 5>{experiment_.GetBeamX_pxl(), experiment_.GetBeamY_pxl(),
|
||||
experiment_.GetDetectorDistance_mm(),
|
||||
experiment_.GetPoniRot1_rad(), experiment_.GetPoniRot2_rad()};
|
||||
};
|
||||
const auto set_geometry = [&](const std::array<float, 5> &g) {
|
||||
experiment_.BeamX_pxl(g[0]).BeamY_pxl(g[1]).DetectorDistance_mm(g[2])
|
||||
.PoniRot1_rad(g[3]).PoniRot2_rad(g[4]);
|
||||
};
|
||||
|
||||
// Where the pre-pass could not tell whether its distance move paid (held_arm_beam_px: the
|
||||
// free distance lowered the fit's own held-out residual by less than that residual's noise),
|
||||
// the other hypothesis - the header distance, with the beam the same fit found there - is run
|
||||
// as a canonical pass of its own, and the two are judged on what they REALISE: the held-out
|
||||
// residual each re-integrated pass measures at its own geometry, the comparison the walk
|
||||
// below makes between its rounds. The free arm ran first and wrote the canonical files, so
|
||||
// where it wins nothing is re-run; where the header's wins, or the two cannot be told apart,
|
||||
// the header's is re-run to write them - it is the hypothesis that asserts nothing, so a tie
|
||||
// is its, and the distance walk below is not entered: the data have just said they cannot
|
||||
// place the distance. Measured on a sweep at 820 mm reaching 2theta ~ 9 deg, where the fit's
|
||||
// own gain never realises: the free arm's re-integrated residual sat within 0.6 % of the
|
||||
// header's at a noise of 8 %, and the cell it carried was 1 % too large. On a sweep at 110 mm
|
||||
// reaching 57 deg, a 0.14 % distance move the pre-pass could not decide realised 3.7 %
|
||||
// against a noise of 1.5 %, and the cell it carried matched the reference to 0.04 %.
|
||||
std::string distance_arm; // the arm decision, for the pass decision text
|
||||
bool distance_arm_kept_header = false;
|
||||
int arm_passes = 0;
|
||||
if (!cancelled_ && prepass_detector_geometry_ && pass1.post_refine
|
||||
&& pass1.post_refine->held_arm_beam_px && pass2.post_refine) {
|
||||
const std::array<float, 5> free_geometry = current_geometry();
|
||||
std::array<float, 5> held_geometry = free_geometry;
|
||||
held_geometry[0] = static_cast<float>((*pass1.post_refine->held_arm_beam_px)[0]);
|
||||
held_geometry[1] = static_cast<float>((*pass1.post_refine->held_arm_beam_px)[1]);
|
||||
held_geometry[2] = header_geometry[2];
|
||||
logger.Info("Two-pass: the pre-pass could not tell whether moving the distance {:.3f} -> {:.3f} mm "
|
||||
"paid, so the canonical pass runs at the header distance as well and the two are "
|
||||
"judged on the held-out residual each realises", header_geometry[2],
|
||||
free_geometry[2]);
|
||||
// The pass at the header distance measures its own post-refinement and integration, and
|
||||
// neither may replace the free pass's where the free pass is the one kept.
|
||||
const auto free_wish = prepass_detector_geometry_;
|
||||
const auto free_starved = bkg_starved_fraction_;
|
||||
set_geometry(held_geometry);
|
||||
auto held = RunPipeline(observer, /*write_output=*/false, /*geometry_prepass=*/false);
|
||||
++arm_passes;
|
||||
const double realised_free = pass2.post_refine->held_out_before;
|
||||
const double realised_held = held.post_refine ? held.post_refine->held_out_before : NAN;
|
||||
const double se_free = pass2.post_refine->held_out_before_se;
|
||||
const double se_held = held.post_refine ? held.post_refine->held_out_before_se : NAN;
|
||||
if (!cancelled_ && held.post_refine && HeldOutResidualFell(*held.post_refine, *pass2.post_refine)) {
|
||||
logger.Info("Two-pass: re-integrated at the post-refined distance {:.3f} mm the held-out "
|
||||
"residual is {:.3e} against {:.3e} at the header's {:.3f} mm (standard errors "
|
||||
"{:.1e} and {:.1e}) - the move is realised and stands", free_geometry[2],
|
||||
realised_free, realised_held, held_geometry[2], se_free, se_held);
|
||||
set_geometry(free_geometry);
|
||||
prepass_detector_geometry_ = free_wish;
|
||||
bkg_starved_fraction_ = free_starved;
|
||||
distance_arm = fmt::format(
|
||||
"the header distance {:.3f} mm was run as well and realised a held-out residual of "
|
||||
"{:.3e} against {:.3e} at the post-refined {:.3f} mm, so the post-refined distance "
|
||||
"stands", held_geometry[2], realised_held, realised_free, free_geometry[2]);
|
||||
} else if (!cancelled_) {
|
||||
logger.Info("Two-pass: re-integrated at the header distance {:.3f} mm the held-out residual "
|
||||
"is {:.3e}, which the post-refined {:.3f} mm does not beat ({:.3e}; standard "
|
||||
"errors {:.1e} and {:.1e}) - these data do not determine the distance, and the "
|
||||
"header's stands", held_geometry[2], realised_held, free_geometry[2],
|
||||
realised_free, se_held, se_free);
|
||||
pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
|
||||
++arm_passes;
|
||||
distance_arm_kept_header = true;
|
||||
pass1.post_refine->distance_held = true;
|
||||
pass1.post_refine->beam_x_after_px = held_geometry[0];
|
||||
pass1.post_refine->beam_y_after_px = held_geometry[1];
|
||||
pass1.post_refine->distance_after_mm = held_geometry[2];
|
||||
distance_arm = fmt::format(
|
||||
"the header distance {:.3f} mm kept: re-integrated there the held-out residual is "
|
||||
"{:.3e}, which the post-refined {:.3f} mm does not beat ({:.3e}), so these data do "
|
||||
"not determine the distance", held_geometry[2], realised_held, free_geometry[2],
|
||||
realised_free);
|
||||
}
|
||||
}
|
||||
int geometry_rounds = 0; // passes run at a geometry the walk moved to
|
||||
int walk_passes = 0; // every pass the walk ran, the return to its best round included
|
||||
int best_round = 0;
|
||||
@@ -5267,8 +5274,20 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
|
||||
// before the engine is built rather than beside it - the gathered observations and their
|
||||
// rocking events are gigabytes on a long axis, and beside the engine's own arrays they set
|
||||
// the pass's memory high-water mark.
|
||||
if (postrefine_probe_ && !geometry_prepass && is_rotation)
|
||||
if (postrefine_probe_ && !geometry_prepass && is_rotation) {
|
||||
post_refine_geometry();
|
||||
// A pass the distance arm runs to compare two geometries has now produced the only thing
|
||||
// the comparison reads - the held-out residual of the fit just measured. Stop here rather
|
||||
// than scale, search, fit surfaces and merge for a result nothing looks at.
|
||||
if (postrefine_probe_only_) {
|
||||
result.processing_time_s = std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - start_time).count();
|
||||
logger.Info("Distance-arm probe: {} images integrated and post-refined in {:.2f} s "
|
||||
"(the arm reads the held-out residual only, so this pass does not merge)",
|
||||
result.images_processed, result.processing_time_s);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (is_rotation) {
|
||||
if (experiment_.GetRefineRotationWedgeInScaling()
|
||||
|| rot_ss.GetRotationWedgeForScaling().has_value())
|
||||
|
||||
@@ -555,6 +555,12 @@ class Rugnux {
|
||||
// whether it has finished walking is a question only reflections integrated after the move can
|
||||
// answer; RunAllPasses turns this on for the passes that answer it.
|
||||
bool postrefine_probe_ = false;
|
||||
// Whether such a pass exists ONLY for that measurement. The distance arm reads the held-out
|
||||
// residual the probe returns and nothing else, so a pass run for it stops as soon as the probe
|
||||
// has measured - which is before the scaling engine is built. Everything below that point (the
|
||||
// merges, the space-group search, the correction surfaces, the reports) is work no comparison
|
||||
// looks at, and on a long sweep it is four fifths of the pass.
|
||||
bool postrefine_probe_only_ = false;
|
||||
|
||||
// Pre-scan: read a spread sample of frames and take three things off them - the shadow of the beam
|
||||
// stop and its holder, added to the pixel mask (config_.detect_beam_stop), the beam centre
|
||||
|
||||
Reference in New Issue
Block a user