Files
Jungfraujoch/image_analysis/indexing/MultiLatticeSearch.cpp
T
leonarski_f 02ecf5c32e
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 9m24s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 10m30s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 11m2s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 11m43s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 12m39s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 12m51s
Build Packages / build:rpm (rocky8) (push) Successful in 10m21s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 10m4s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 9m31s
Build Packages / Generate python client (push) Successful in 12s
Build Packages / XDS test (durin plugin) (push) Successful in 8m34s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 38s
Build Packages / build:rpm (rocky9) (push) Successful in 11m47s
Build Packages / DIALS test (push) Successful in 12m35s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 6m15s
Build Packages / XDS test (neggia plugin) (push) Successful in 5m12s
Build Packages / Unit tests (push) Successful in 56m53s
MultiLatticeSearch: Explore all valid sign flips when comparing with reference cell
2026-06-05 11:36:23 +02:00

129 lines
4.6 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "MultiLatticeSearch.h"
#include <Eigen/Dense>
namespace {
// Direct lattice vectors as matrix columns: M = [a | b | c]
Eigen::Matrix3d LatticeMatrix(const CrystalLattice &latt) {
const Coord a = latt.Vec0();
const Coord b = latt.Vec1();
const Coord c = latt.Vec2();
Eigen::Matrix3d M;
M.col(0) = Eigen::Vector3d(a.x, a.y, a.z);
M.col(1) = Eigen::Vector3d(b.x, b.y, b.z);
M.col(2) = Eigen::Vector3d(c.x, c.y, c.z);
return M;
}
// Proper rotation mapping reference -> target (target = R * reference).
// R = Mtarget * Mref^-1, projected to the nearest rotation via SVD.
Eigen::Matrix3d RotationRefToTarget(const CrystalLattice &reference, const CrystalLattice &target) {
Eigen::Matrix3d R = LatticeMatrix(target) * LatticeMatrix(reference).inverse();
Eigen::JacobiSVD<Eigen::Matrix3d> svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV);
R = svd.matrixU() * svd.matrixV().transpose();
if (R.determinant() < 0.0) {
Eigen::Matrix3d U = svd.matrixU();
U.col(2) *= -1.0;
R = U * svd.matrixV().transpose();
}
return R;
}
}
CrystalLattice Transform(const CrystalLattice &in_latt, int i) {
CrystalLattice latt = in_latt;
switch (i) {
case 0:
break;
case 1:
latt.FlipSign(0);
latt.FlipSign(1);
break;
case 2:
latt.FlipSign(0);
latt.FlipSign(2);
break;
case 3:
latt.FlipSign(1);
latt.FlipSign(2);
break;
default:
break;
}
return latt;
}
std::vector<MultiLatticeSearchResult> MultiLatticeSearch(const std::vector<CrystalLattice> &lattices,
float dist_tolerance,
float angle_tolerance_deg) {
std::vector<MultiLatticeSearchResult> ret;
if (lattices.empty())
return ret;
const CrystalLattice &reference = lattices[0];
const UnitCell ref_cell = reference.GetUnitCell();
ret.push_back({reference, reference, Coord(0, 0, 0)});
for (size_t i = 1; i < lattices.size(); i++) {
bool found = false;
for (int flip = 0; flip < 4; flip++) {
if (found)
continue;
const CrystalLattice latt = Transform(lattices[i], flip);
if (!latt.GetUnitCell().is_close(ref_cell, dist_tolerance, angle_tolerance_deg))
continue;
const Eigen::Matrix3d R = RotationRefToTarget(reference, latt);
const Eigen::AngleAxisd aa(R);
const Eigen::Vector3d rod = aa.angle() * aa.axis();
const Coord rotation_vector(static_cast<float>(rod.x()),
static_cast<float>(rod.y()),
static_cast<float>(rod.z()));
// output_lattice = R * reference, built straight from the Eigen matrix
// so it is exactly a proper rotation of the reference (full double precision).
const Eigen::Matrix3d out = R * LatticeMatrix(reference);
ret.push_back({
latt,
CrystalLattice(Coord(out(0, 0), out(1, 0), out(2, 0)),
Coord(out(0, 1), out(1, 1), out(2, 1)),
Coord(out(0, 2), out(1, 2), out(2, 2))),
rotation_vector
});
found = true;
}
}
return ret;
}
std::vector<MultiLatticeSearchResult> MultiLatticeSearchReduced(const std::vector<CrystalLattice> &lattices,
float dist_tolerance,
float angle_tolerance_deg) {
if (lattices.empty())
return {};
// Reduce lattice 0 to its primitive Niggli setting; that becomes the reference basis.
std::vector<CrystalLattice> reduced;
reduced.reserve(lattices.size());
// Reduce every candidate the same way so metric comparison and rotation
// are all done in the reduced setting.
for (const auto & lattice : lattices)
reduced.push_back(lattice.NiggliReduce());
auto ret = MultiLatticeSearch(reduced, dist_tolerance, angle_tolerance_deg);
// Keep input_lattice pointing at the original (un-reduced) lattices for debugging.
return ret;
}