rugnux: two-pass supercell-collapse guard + post-refine fixes
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m54s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m44s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m53s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m0s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m16s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m38s
Build Packages / build:windows:nocuda (push) Successful in 15m45s
Build Packages / build:windows:nocuda (pull_request) Failing after 58s
Build Packages / build:windows:cuda (push) Successful in 18m39s
Build Packages / build:rpm (rocky8) (push) Successful in 11m12s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m50s
Build Packages / XDS test (durin plugin) (push) Successful in 10m5s
Build Packages / Generate python client (push) Successful in 39s
Build Packages / Build documentation (push) Successful in 1m5s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m44s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 14m18s
Build Packages / build:rpm (rocky9) (push) Successful in 14m44s
Build Packages / DIALS test (push) Successful in 14m37s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m17s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m2s
Build Packages / build:windows:cuda (pull_request) Successful in 13m7s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 7m23s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 7m57s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 10m44s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 11m39s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 12m41s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 11m50s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 11m6s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 13m23s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 11m4s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 11m59s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 12m11s
Build Packages / Generate python client (pull_request) Successful in 16s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 12m45s
Build Packages / Create release (pull_request) Skipped
Build Packages / Build documentation (pull_request) Successful in 1m20s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 8m5s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 9m28s
Build Packages / DIALS test (pull_request) Successful in 15m13s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 8m54s
Build Packages / Unit tests (push) Failing after 1h36m51s
Build Packages / Unit tests (pull_request) Failing after 1h32m3s
Build Packages / build:viewer-tgz:cpu (push) Successful in 7m54s
Build Packages / build:viewer-tgz:cuda (push) Successful in 8m44s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m53s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m0s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m16s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m38s
Build Packages / build:windows:nocuda (push) Successful in 15m45s
Build Packages / build:windows:nocuda (pull_request) Failing after 58s
Build Packages / build:windows:cuda (push) Successful in 18m39s
Build Packages / build:rpm (rocky8) (push) Successful in 11m12s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m50s
Build Packages / XDS test (durin plugin) (push) Successful in 10m5s
Build Packages / Generate python client (push) Successful in 39s
Build Packages / Build documentation (push) Successful in 1m5s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (ubuntu2404) (push) Successful in 13m44s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 14m18s
Build Packages / build:rpm (rocky9) (push) Successful in 14m44s
Build Packages / DIALS test (push) Successful in 14m37s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m17s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m2s
Build Packages / build:windows:cuda (pull_request) Successful in 13m7s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 7m23s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 7m57s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 10m44s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 11m39s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 12m41s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 11m50s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 11m6s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 13m23s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 11m4s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 11m59s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 12m11s
Build Packages / Generate python client (pull_request) Successful in 16s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 12m45s
Build Packages / Create release (pull_request) Skipped
Build Packages / Build documentation (pull_request) Successful in 1m20s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 8m5s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 9m28s
Build Packages / DIALS test (pull_request) Successful in 15m13s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 8m54s
Build Packages / Unit tests (push) Failing after 1h36m51s
Build Packages / Unit tests (pull_request) Failing after 1h32m3s
- Guard the two-pass second pass against a bistable de-novo lattice search collapsing onto a spurious supercell after the small post-refine geometry move: when pass 2's cell volume balloons past 1.5x pass 1, re-run pass 2 with pass-1's correct lattice forced (still integrating at the refined geometry). A genuine de-novo demotion to a smaller primitive (pass2 <= pass1) is left untouched. - Keep an empty output prefix empty in the "compute stats, persist nothing" mode so the two-pass first pass does not write stray _01_* files. - Fix the stale ProcessConfig doc-comment to describe the actual output (canonical unsuffixed _* second pass, header-geometry _01_* first pass) and the --rotation-no-postrefine default-on flag, not the removed _02_*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-1
@@ -229,12 +229,15 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
|
||||
const std::string base_prefix = config_.output_prefix;
|
||||
const auto gonio_snapshot = experiment_.GetGoniometer();
|
||||
prepass_detector_geometry_.reset();
|
||||
prepass_lattice_.reset();
|
||||
prepass_sg_reindexed_ = false;
|
||||
prepass_merge_sg_.reset();
|
||||
|
||||
logger.Info("Rotation two-pass geometry post-refinement: first pass (header geometry) -> {}_01_*",
|
||||
base_prefix);
|
||||
config_.output_prefix = base_prefix + "_01";
|
||||
// Keep an empty prefix empty (the "compute stats, persist nothing" mode): the "_01" suffix would
|
||||
// make it non-empty and RunPipeline would then write stray _01_* files despite no output wanted.
|
||||
config_.output_prefix = base_prefix.empty() ? base_prefix : base_prefix + "_01";
|
||||
auto pass1 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/true);
|
||||
if (cancelled_) { config_.output_prefix = base_prefix; return pass1; }
|
||||
if (gonio_snapshot) experiment_.Goniometer(*gonio_snapshot); // undo the pre-pass goniometer shift
|
||||
@@ -257,6 +260,27 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
|
||||
base_prefix);
|
||||
config_.output_prefix = base_prefix; // the refined pass is the canonical result (no _02 suffix)
|
||||
auto pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
|
||||
|
||||
// Supercell-collapse guard for the second pass. The de-novo lattice search can be BISTABLE on a
|
||||
// periodically-modulated dataset: the tiny post-refine geometry move tips it out of the true-cell
|
||||
// basin onto a spurious SUPERCELL (a large multiple of pass-1's cell volume), and the space group
|
||||
// reused from pass-1 is then stamped onto that wrong-shaped cell - catastrophic. Pass 1 (header
|
||||
// geometry, un-perturbed) found the correct lattice, so re-run the second pass with THAT lattice
|
||||
// forced: it still integrates at the refined geometry, but on the right cell. Only fires when pass 2 is
|
||||
// markedly LARGER than pass 1 - a genuine de-novo demotion to a smaller primitive (the pseudo-symmetry
|
||||
// case the second pass re-indexes de novo to catch) has pass2 <= pass1 and is left untouched.
|
||||
if (!cancelled_ && prepass_lattice_ && pass1.consensus_cell && pass2.consensus_cell) {
|
||||
const double v1 = gemmi::UnitCell(*pass1.consensus_cell).volume;
|
||||
const double v2 = gemmi::UnitCell(*pass2.consensus_cell).volume;
|
||||
if (v1 > 0.0 && v2 > 1.5 * v1) {
|
||||
logger.Info("Two-pass: second-pass cell volume {:.0f} A^3 is {:.2f}x pass-1 ({:.0f} A^3) - a "
|
||||
"spurious supercell; re-running the second pass with pass-1's lattice forced",
|
||||
v2, v2 / v1, v1);
|
||||
config_.forced_rotation_lattice = *prepass_lattice_;
|
||||
pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
|
||||
config_.forced_rotation_lattice.reset();
|
||||
}
|
||||
}
|
||||
return pass2;
|
||||
}
|
||||
return RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
|
||||
@@ -1005,6 +1029,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
|
||||
if (outcomes[o].mosaicity_deg) prepass_mosaicity_[o] = *outcomes[o].mosaicity_deg;
|
||||
if (result.consensus_cell.has_value()) {
|
||||
if (const auto rot = indexer->FinalizeRotationIndexing(); rot && rot->axis) {
|
||||
prepass_lattice_ = rot->lattice; // correct lattice, for the second-pass supercell fallback
|
||||
PostRefineSettings prs;
|
||||
prs.crystal_system = rot->search_result.system;
|
||||
prs.num_threads = static_cast<int>(config_.nthreads);
|
||||
|
||||
+13
-6
@@ -68,12 +68,12 @@ struct ProcessConfig {
|
||||
// re-indexes with it. The value is the number of strong frames fed to the bundle (--refine-geometry).
|
||||
std::optional<int> refine_geometry;
|
||||
|
||||
// Rotation two-pass geometry post-refinement (FullAnalysis, rotation only). When set, a first pass
|
||||
// integrates and post-refines the detector distance + beam (from the observed spot positions) and the
|
||||
// cell scale + rotation axis (from the observed rocking centroids phi_obs), each committed only if it
|
||||
// improves a held-out residual; the second pass re-integrates with the refined geometry and lattice.
|
||||
// BOTH passes are written out - "<prefix>_01_*" (first pass, header geometry) and "<prefix>_02_*" (second
|
||||
// pass, refined geometry) - so the two can be compared and the better one kept (--rotation-post-refine).
|
||||
// Rotation two-pass geometry post-refinement (FullAnalysis, rotation only; on by default in the rugnux
|
||||
// CLI, --rotation-no-postrefine disables it). When set, a first pass integrates and post-refines the
|
||||
// detector distance + beam (from the observed spot positions) and the cell scale + rotation axis (from the
|
||||
// observed rocking centroids phi_obs), each committed only for a small, cross-validated move; the second
|
||||
// pass re-indexes de novo and re-integrates with the refined geometry. The refined (second) pass is the
|
||||
// CANONICAL "<prefix>_*" output; the header-geometry first pass is kept alongside as "<prefix>_01_*".
|
||||
bool rotation_postrefine_geometry = false;
|
||||
|
||||
// Scaling / merging (FullAnalysis). reference_data (from a reference MTZ) also drives the
|
||||
@@ -153,6 +153,13 @@ class Rugnux {
|
||||
// committed no change (the second pass then reproduces the first).
|
||||
std::optional<std::array<float, 3>> prepass_detector_geometry_;
|
||||
|
||||
// Two-pass geometry pre-pass: pass-1's indexed lattice (correct cell + orientation, found at the header
|
||||
// geometry). Kept as a fallback seed: if the second pass's de-novo re-index at the refined geometry
|
||||
// collapses onto a spurious supercell (a bistable lattice search that the tiny geometry move tips out of
|
||||
// the true-cell basin - seen on a periodically-modulated dataset), Run() re-runs the second pass with this
|
||||
// lattice FORCED, so it still integrates at the refined geometry but on the correct cell.
|
||||
std::optional<CrystalLattice> prepass_lattice_;
|
||||
|
||||
// Two-pass geometry pre-pass: whether the first pass's space group came from the centred-lattice reindex
|
||||
// test (a conventional setting the indexer's primitive frame does not share), so Run() clears it and lets
|
||||
// the second pass re-search rather than reuse it.
|
||||
|
||||
Reference in New Issue
Block a user