rugnux: resolve the indexing ambiguity against a reference MTZ (rotation)
A reference MTZ is now allowed for rotation data: it fixes the space group and cell (on the CLI) and resolves the indexing (merohedral) ambiguity, but is NOT used to scale - the rotation merge stays self-consistent. Previously rotation + reference was a hard error. After the space group is determined, ChooseReindex/ReferenceIntensityCC pick the reindexing whose merged intensities best correlate with the reference and, if it is not the identity, the twin-law reindex is baked into the integration outcome (hkl labels only; the cell is unchanged, the reindex is metric- preserving), the rotation scale-merge is re-ingested and the data re-merged in that indexing. No-op for a holohedral crystal (no twin laws), e.g. lysozyme. Stills resolve the per-image ambiguity in ScaleOnTheFly, not here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+37
-2
@@ -30,6 +30,7 @@
|
||||
#include "../image_analysis/scale_merge/Merge.h"
|
||||
#include "../image_analysis/scale_merge/RotationScaleMerge.h"
|
||||
#include "../image_analysis/scale_merge/ResolutionCutoff.h"
|
||||
#include "../image_analysis/scale_merge/ReindexAmbiguity.h"
|
||||
#include "../image_analysis/scale_merge/ScalingResult.h"
|
||||
#include "../image_analysis/scale_merge/SearchSpaceGroup.h"
|
||||
#include "../image_analysis/lattice_search/LatticeSearch.h"
|
||||
@@ -565,12 +566,15 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
|
||||
const bool is_rotation = experiment_.IsRotationIndexing(); // rotation indexing -> rotation scaling/merge
|
||||
std::optional<RotationScaleMerge> rsm;
|
||||
if (is_rotation) {
|
||||
if (!config_.reference_data.empty() || rot_ss.GetRefineB()
|
||||
if (rot_ss.GetRefineB()
|
||||
|| experiment_.GetRefineRotationWedgeInScaling()
|
||||
|| rot_ss.GetRotationWedgeForScaling().has_value())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Rotation scaling/merging (RotationScaleMerge) does not support "
|
||||
"reference scaling, B-factor refinement or wedge refinement");
|
||||
"B-factor refinement or wedge refinement");
|
||||
// A reference MTZ is allowed for rotation: it fixes the space group / cell (on the CLI) and
|
||||
// resolves the indexing ambiguity (below), but is NOT used to scale - the rotation merge stays
|
||||
// self-consistent.
|
||||
rsm.emplace(experiment_, indexer->GetIntegrationOutcome(), result.consensus_cell,
|
||||
static_cast<int>(config_.scaling_iter),
|
||||
config_.spot_finding.ice_ring_width_Q_recipA,
|
||||
@@ -814,6 +818,37 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
|
||||
result.space_group_number = experiment_.GetSpaceGroupNumber();
|
||||
}
|
||||
|
||||
// Reference-based indexing-ambiguity resolution (rotation). When a reference MTZ is supplied and
|
||||
// the crystal has an indexing ambiguity (merohedral: lattice symmetry higher than the Laue group),
|
||||
// pick the reindexing that best correlates with the reference intensities and re-merge in it. The
|
||||
// twin-law reindex is metric-preserving, so only the hkl labels change (the cell is unchanged).
|
||||
// Stills resolve the ambiguity per image in ScaleOnTheFly, not here.
|
||||
if (rsm && !config_.reference_data.empty() && result.consensus_cell
|
||||
&& experiment_.GetSpaceGroupNumber().has_value()) {
|
||||
const int sg_num = static_cast<int>(*experiment_.GetSpaceGroupNumber());
|
||||
const auto choice = ChooseReindex(
|
||||
sm.merged, *result.consensus_cell, sg_num,
|
||||
[&](const std::vector<MergedReflection> &m) {
|
||||
return ReferenceIntensityCC(m, config_.reference_data, sg_num);
|
||||
});
|
||||
if (!choice.is_identity) {
|
||||
logger.Info("Reference: resolved indexing ambiguity by reindexing to match the reference "
|
||||
"(CC {:.3f}, vs {:.3f} unchanged)", choice.score, choice.identity_score);
|
||||
for (auto &io : indexer->GetIntegrationOutcome())
|
||||
for (auto &r : io.reflections) {
|
||||
const gemmi::Op::Miller h = choice.op.apply_to_hkl({{r.h, r.k, r.l}});
|
||||
r.h = h[0]; r.k = h[1]; r.l = h[2];
|
||||
}
|
||||
rsm.emplace(experiment_, indexer->GetIntegrationOutcome(), result.consensus_cell,
|
||||
static_cast<int>(config_.scaling_iter),
|
||||
config_.spot_finding.ice_ring_width_Q_recipA,
|
||||
config_.nthreads, logger, config_.observation_dump_path);
|
||||
rsm->Ingest();
|
||||
phase("Re-merging in the reference indexing");
|
||||
sm = scale_and_merge(experiment_.GetGemmiSpaceGroup()->short_name(), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Ice-ring CC1/2 mask: a hexagonal-ice ring whose merged half-set CC1/2 collapses well below its
|
||||
// resolution shoulders has been decorrelated by ice (its Bragg is unrecoverable) - drop it and
|
||||
// re-merge. Weak/absent rings track their neighbours and stay, so completeness on clean crystals
|
||||
|
||||
Reference in New Issue
Block a user