Files
Jungfraujoch/rugnux/LeftoverLattices.cpp
T
leonarski_fandClaude Opus 5.5 ddf718f91b Rugnux: report further lattices in the leftover spots, and the mosaicity
Rotation, report only: once the first pass has its lattice, the spots it leaves over (ice left
out) are indexed again with a fresh rotation indexer, up to three times. A lattice is kept when
the leftover validation spots sit on it far more often than at a displaced spindle angle (z >= 5,
the first pass's own null), and classified against the main lattice: a misoriented domain of the
same cell (split crystal), one turned by 180 deg (non-merohedral twin domain), one concentrated
in part of the sweep (>= 60% of its spots in 2 of 8 blocks), a related cell (volume n or 1/n) or
an unrelated one. Misorientation is the minimum over the bases whose metric matches, so a lattice
symmetry is not read as one. EXTRA_LATTICE_* keys, a summary row, and a MULTIPLE_LATTICES warning
where domains or an unrelated lattice hold >= 10% of the non-ice spot intensity (29 of 200
battery sets in the exploration census). Its own IndexAndRefine instances and indexer: merged
MTZ byte-identical on three sets; 0.2-2.5 s per run.

Also MOSAICITY_DEG (median, P10, P90 over frames) - the frame-order-smoothed sigma_M the merge
computes partiality from, Kabsch's sigma_M as XDS's REFLECTING_RANGE_E.S.D. - in the report.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1G8gJVAy6gp1K5Dz3NE5C
2026-09-25 21:00:51 +02:00

76 lines
3.0 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "LeftoverLattices.h"
#include <algorithm>
#include <cmath>
#include <Eigen/Dense>
#include "../common/JFJochMath.h" // PI
const char *LeftoverLatticeKindName(LeftoverLatticeKind kind) {
switch (kind) {
case LeftoverLatticeKind::Self: return "SELF";
case LeftoverLatticeKind::Domain: return "DOMAIN";
case LeftoverLatticeKind::TwinDomain: return "TWIN_DOMAIN";
case LeftoverLatticeKind::Segmented: return "SEGMENTED";
case LeftoverLatticeKind::RelatedCell: return "RELATED_CELL";
case LeftoverLatticeKind::Foreign: return "FOREIGN";
}
return "FOREIGN";
}
double LatticeMisorientationDeg(const CrystalLattice &a, const CrystalLattice &b) {
auto mat = [](const CrystalLattice &l) {
Eigen::Matrix3d M;
const Coord v[3] = {l.Vec0(), l.Vec1(), l.Vec2()};
for (int c = 0; c < 3; c++)
M.col(c) = Eigen::Vector3d(v[c].x, v[c].y, v[c].z);
return M;
};
const Eigen::Matrix3d A = mat(a), B = mat(b);
const Eigen::Matrix3d GA = A.transpose() * A;
const double scale = GA.diagonal().mean();
// Every unimodular change of basis with entries -1..1: enough for two reduced cells.
double best = NAN;
for (int code = 0; code < 19683; code++) {
Eigen::Matrix3d M;
int c = code;
for (int e = 0; e < 9; e++) {
M(e / 3, e % 3) = c % 3 - 1;
c /= 3;
}
if (std::abs(M.determinant() - 1.0) > 1e-6)
continue;
const Eigen::Matrix3d BM = B * M;
if ((BM.transpose() * BM - GA).cwiseAbs().maxCoeff() > 0.06 * scale)
continue;
// The rotation nearest to the (slightly non-orthogonal) map from A to BM.
Eigen::JacobiSVD<Eigen::Matrix3d> svd(BM * A.inverse(), Eigen::ComputeFullU | Eigen::ComputeFullV);
const Eigen::Matrix3d R = svd.matrixU() * svd.matrixV().transpose();
if (R.determinant() < 0)
continue;
const double ang = std::acos(std::clamp((R.trace() - 1.0) / 2.0, -1.0, 1.0)) * 180.0 / PI;
if (std::isnan(best) || ang < best)
best = ang;
}
return best;
}
LeftoverLatticeKind ClassifyLeftoverLattice(double misorientation_deg, double volume_ratio,
double sweep_concentration) {
if (std::isfinite(misorientation_deg)) {
if (misorientation_deg <= 1.0)
return LeftoverLatticeKind::Self;
if (sweep_concentration >= 0.6)
return LeftoverLatticeKind::Segmented;
if (misorientation_deg >= 177.0)
return LeftoverLatticeKind::TwinDomain;
return LeftoverLatticeKind::Domain;
}
for (int n = 1; n <= 8; n++)
if (std::abs(volume_ratio / n - 1.0) < 0.06 || std::abs(volume_ratio * n - 1.0) < 0.06)
return LeftoverLatticeKind::RelatedCell;
return LeftoverLatticeKind::Foreign;
}