Report the strongest index-2 superstructure class and the cell it doubles to
The report-only supercell probe now reaches the results report: SUPERCELL_CLASS (parity of the primitive indices), its occupancy and Bragg-like (rocking) part against the lattice's own reflections, its <I/sigma>, and SUPERCELL_DOUBLED_CELL, the Niggli-reduced cell to give with -C. No decision is taken on it: correct cells whose half-integer class is diffuse, and cells whose depositor kept the sub-cell, cannot be told from a real doubling by the data alone. On a crystal with a real doubled axis the reported cell matched the deposited one, and processing on it with -C brought R_free against the deposited model from 0.60 to 0.29. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C
This commit is contained in:
1 parent
9e510816e0
commit
6c9205a670
7 files changed
+96
No files matched your search
@@ -9,6 +9,7 @@
|
||||
* Rugnux accepts a detector-modulation or absorption correction surface when held-out data support it on Fisher's z, and fits them in the order modulation, time, goniometer frame; this recovers corrections that were wrongly refused.
|
||||
* Rugnux settles a disagreement between the file's and the measured beam centre over an axis harmonic in either direction, so a file centre off along the spindle no longer leaves the run on a doubled axis.
|
||||
* Rugnux commits a post-refined geometry only when the held-out residual falls by more than its own noise, instead of by a fixed 2 %.
|
||||
* Rugnux reports the strongest index-2 superstructure class of a rotation run (`SUPERCELL_CLASS` and related keys), with the doubled cell to give with `-C`; it does not change the lattice.
|
||||
* Rugnux also reports `R_MEAS_WEIGHTED`, the R_meas with each observation weighted as the merge weights it, so weak frames kept in the merge at low weight can be told from real disagreement; `R_MEAS` is unchanged.
|
||||
* Rugnux no longer stops per-frame scaling as unsettled because of a single frame that is about to be dropped as blank.
|
||||
* `rugnux --polarization` is documented as the polarization degree (XDS `FRACTION_OF_POLARIZATION` = (1 + p)/2); the default 0.99 suits undulators.
|
||||
|
||||
@@ -415,6 +415,25 @@ Rings are detected far more often than they are excluded: exclusion happens only
|
||||
found no usable lattice. The measurement is described in
|
||||
[CPU/GPU data analysis ▸ Resolution and ice-ring handling](CPU_DATA_ANALYSIS_IMAGE.md#33-resolution-and-ice-ring-handling).
|
||||
|
||||
## Index-2 superstructure
|
||||
|
||||
On rotation data, section 4 reports whether the lattice the run adopted has intensity at half-integer
|
||||
positions it does not index. On 60 frames spread over the sweep, after each frame's own integration, the
|
||||
lattice is predicted doubled along all three primitive axes and integrated to 3 Å; the reflections split
|
||||
into eight parity classes of h, k and l, of which `0 0 0` is the lattice itself and each of the other
|
||||
seven is one index-2 superstructure. Nothing is decided on it: whether a superstructure belongs in the
|
||||
cell is as much the depositor's call as the data's, and several crystals whose accepted cell is the
|
||||
sub-cell carry one.
|
||||
|
||||
**`SUPERCELL_CLASS=`** is the parity class with the most intensity over 20–3 Å.
|
||||
**`SUPERCELL_OCCUPANCY_PCT=`** is its mean intensity against the lattice's own reflections on the same
|
||||
frames, and **`SUPERCELL_ROCK_PCT=`** (± **`SUPERCELL_ROCK_SE_PCT=`**) the part of it that follows the
|
||||
partiality the way a Bragg reflection does, from a fit I = a + b p over the class, on the same scale.
|
||||
**`SUPERCELL_I_OVER_SIGMA=`** is its mean I/σ. A class near zero on both is empty. One that is occupied
|
||||
and rocks is a superstructure whose reflections this run did not integrate; **`SUPERCELL_DOUBLED_CELL=`**
|
||||
is the Niggli-reduced cell the lattice would double to, to give with `-C` to process on it. One that is
|
||||
occupied but hardly rocks is diffuse or disordered intensity rather than Bragg reflections.
|
||||
|
||||
## Translational pseudo-symmetry
|
||||
|
||||
Two copies of the contents of the asymmetric unit related by a pure translation that is not a lattice
|
||||
|
||||
@@ -640,6 +640,7 @@ void IndexAndRefine::ProbeSupercell(std::vector<int64_t> image_numbers) {
|
||||
std::sort(image_numbers.begin(), image_numbers.end());
|
||||
supercell_probe_frames_ = std::move(image_numbers);
|
||||
supercell_probe_ = {};
|
||||
supercell_probe_primitive_.reset();
|
||||
}
|
||||
|
||||
SupercellProbe IndexAndRefine::GetSupercellProbe() {
|
||||
@@ -647,6 +648,11 @@ SupercellProbe IndexAndRefine::GetSupercellProbe() {
|
||||
return supercell_probe_;
|
||||
}
|
||||
|
||||
std::optional<CrystalLattice> IndexAndRefine::GetSupercellProbePrimitive() {
|
||||
const std::unique_lock ul(supercell_probe_mutex_);
|
||||
return supercell_probe_primitive_;
|
||||
}
|
||||
|
||||
void IndexAndRefine::ProbeSupercellFrame(const DataMessage &msg, BraggPrediction &prediction,
|
||||
const BraggIntegrateFn &integrate, const IndexingOutcome &outcome,
|
||||
const BraggPredictionSettings &settings) {
|
||||
@@ -684,6 +690,7 @@ void IndexAndRefine::ProbeSupercellFrame(const DataMessage &msg, BraggPrediction
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 2; j++)
|
||||
supercell_probe_[i][j] += frame[i][j];
|
||||
supercell_probe_primitive_ = prim;
|
||||
}
|
||||
|
||||
std::optional<IndexAndRefine::IndexingOutcome>
|
||||
|
||||
@@ -123,6 +123,7 @@ class IndexAndRefine {
|
||||
// Supercell probe: the image numbers it reads, and what it has read (see ProbeSupercell).
|
||||
std::vector<int64_t> supercell_probe_frames_;
|
||||
SupercellProbe supercell_probe_{};
|
||||
std::optional<CrystalLattice> supercell_probe_primitive_;
|
||||
std::mutex supercell_probe_mutex_;
|
||||
void ProbeSupercellFrame(const DataMessage &msg, BraggPrediction &prediction,
|
||||
const BraggIntegrateFn &integrate, const IndexingOutcome &outcome,
|
||||
@@ -189,6 +190,9 @@ public:
|
||||
// The probe's integrations are the caller's to keep out of anything else it counts.
|
||||
void ProbeSupercell(std::vector<int64_t> image_numbers);
|
||||
[[nodiscard]] SupercellProbe GetSupercellProbe();
|
||||
// The primitive lattice the parity classes are indexed in (the last probed frame's; only its metric
|
||||
// is meant to be read), so a class can be turned into the cell it would double to.
|
||||
[[nodiscard]] std::optional<CrystalLattice> GetSupercellProbePrimitive();
|
||||
// Index a single frame (no integration) with the current forced rotation lattice; used to score
|
||||
// first-pass sampling schemes on the real per-image path. Returns whether the frame indexed.
|
||||
bool IndexFrameOnly(DataMessage &msg, const SpotFindingSettings &settings);
|
||||
|
||||
@@ -1075,6 +1075,32 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
|
||||
}
|
||||
}
|
||||
|
||||
// ---- index-2 superstructure
|
||||
// Report only, like the harmonic above: on 60 frames the probe integrates the lattice doubled
|
||||
// along all three primitive axes and reads the seven half-integer parity classes. Whether a
|
||||
// class that is there belongs in the cell is the depositor's call as much as the data's -
|
||||
// several crystals whose accepted cell is the sub-cell carry one - so no verdict is printed.
|
||||
if (result.supercell) {
|
||||
const auto &sc = *result.supercell;
|
||||
Add(s, KeyText("SUPERCELL_CLASS", fmt::format("{} {} {}", sc.h, sc.k, sc.l)));
|
||||
Add(s, KeyReal("SUPERCELL_OCCUPANCY_PCT", sc.occupancy_pct, "{:.1f}"));
|
||||
Add(s, KeyReal("SUPERCELL_ROCK_PCT", sc.rock_pct, "{:.1f}"));
|
||||
Add(s, KeyReal("SUPERCELL_ROCK_SE_PCT", sc.rock_se_pct, "{:.1f}"));
|
||||
Add(s, KeyReal("SUPERCELL_I_OVER_SIGMA", sc.i_over_sigma, "{:.2f}"));
|
||||
Add(s, KeyText("SUPERCELL_DOUBLED_CELL", CellString(sc.doubled_cell)));
|
||||
Add(s, Blank());
|
||||
Add(s, Prose(
|
||||
" SUPERCELL_CLASS is the half-integer class (parity of h, k, l in the primitive cell of the\n"
|
||||
" lattice) with the most intensity over 20-3 A, read on 60 frames. OCCUPANCY is its mean\n"
|
||||
" intensity against the lattice's own reflections; ROCK is the part of it that follows the\n"
|
||||
" partiality the way a Bragg reflection does, on the same scale. A class near zero on both is\n"
|
||||
" empty. One that is occupied and rocks is an index-2 superstructure the lattice does not\n"
|
||||
" index: its reflections are not integrated. To process on the doubled lattice, give\n"
|
||||
" SUPERCELL_DOUBLED_CELL with -C. An occupied class that does not rock is diffuse or\n"
|
||||
" disordered intensity rather than Bragg reflections."));
|
||||
Add(s, Blank());
|
||||
}
|
||||
|
||||
// ---- powder contamination
|
||||
// A crystalline phase other than the crystal, diffracting as rings among its reflections.
|
||||
// Measured in the pre-scan on every run, from the spots found there for the width and the
|
||||
|
||||
@@ -5312,6 +5312,35 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
|
||||
}
|
||||
logger.Info("Supercell probe (the eight parity classes of the doubled primitive cell; <I/sigma>, "
|
||||
"mean intensity, and the fit I = a + b p against the lattice's own):{}", table);
|
||||
|
||||
// The class with the most intensity over 20-3 A goes into the report, with the cell it would
|
||||
// double the lattice to: the vectors t of the primitive lattice with (h,k,l).t even - twice
|
||||
// one axis the class is odd on, and each other axis plus that one where the class is odd
|
||||
// on it too - Niggli-reduced.
|
||||
const auto band = [&](int c) { SupercellProbeClass x = probe[c][0]; x += probe[c][1]; return x; };
|
||||
const SupercellProbeFit f0 = FitSupercellProbe(band(0));
|
||||
const auto prim = indexer->GetSupercellProbePrimitive();
|
||||
int best = 0;
|
||||
for (int c = 1; c < 8; c++)
|
||||
if (best == 0 || FitSupercellProbe(band(c)).mean_i > FitSupercellProbe(band(best)).mean_i)
|
||||
best = c;
|
||||
if (prim && f0.mean_i > 0.0 && f0.b != 0.0) {
|
||||
const SupercellProbeFit f = FitSupercellProbe(band(best));
|
||||
const int p[3] = {(best >> 2) & 1, (best >> 1) & 1, best & 1};
|
||||
const Coord v[3] = {prim->Vec0(), prim->Vec1(), prim->Vec2()};
|
||||
const int i = p[0] ? 0 : p[1] ? 1 : 2;
|
||||
Coord t[3];
|
||||
for (int j = 0; j < 3; j++)
|
||||
t[j] = j == i ? v[i] * 2.0f : v[j] + v[i] * static_cast<float>(p[j]);
|
||||
ProcessResult::SupercellClass sc;
|
||||
sc.h = p[0]; sc.k = p[1]; sc.l = p[2];
|
||||
sc.occupancy_pct = 100.0 * f.mean_i / f0.mean_i;
|
||||
sc.rock_pct = 100.0 * f.b / f0.b;
|
||||
sc.rock_se_pct = 100.0 * f.b_se / f0.b;
|
||||
sc.i_over_sigma = f.mean_i_over_sigma;
|
||||
sc.doubled_cell = CrystalLattice(t[0], t[1], t[2]).NiggliReduce().GetUnitCell();
|
||||
result.supercell = sc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -362,6 +362,16 @@ struct ProcessResult {
|
||||
// Higher-order contamination of the beam, read off the spots the lattice did not take. Measured
|
||||
// on the images themselves, so it is there whether or not anything merged.
|
||||
HarmonicContaminationResult harmonic;
|
||||
// The strongest index-2 superstructure class the supercell probe read (20-3 A, 60 frames): which
|
||||
// parity class of the primitive cell, its mean intensity and the part that rocks like a Bragg
|
||||
// reflection, both against the lattice's own, and the cell the lattice would double to were it
|
||||
// real. Report only - nothing decides on it.
|
||||
struct SupercellClass {
|
||||
int h = 0, k = 0, l = 0; // parity of the primitive indices
|
||||
double occupancy_pct = 0.0, rock_pct = 0.0, rock_se_pct = 0.0, i_over_sigma = 0.0;
|
||||
UnitCell doubled_cell{};
|
||||
};
|
||||
std::optional<SupercellClass> supercell;
|
||||
// Powder contamination measured in the pre-scan: a crystalline phase other than the crystal,
|
||||
// diffracting as rings among its reflections. Measured and reported on every run.
|
||||
PowderRings powder;
|
||||
|
||||
Reference in new issue
Block a user