diff --git a/image_analysis/lattice_search/LatticeSearch.cpp b/image_analysis/lattice_search/LatticeSearch.cpp index 4c03dccc..55ff68b7 100644 --- a/image_analysis/lattice_search/LatticeSearch.cpp +++ b/image_analysis/lattice_search/LatticeSearch.cpp @@ -5,6 +5,496 @@ #include #include +#include + +#include "../../common/JFJochException.h" + +const std::map kNiggliClassDefinitions = { + { + 1, { + gemmi::Mat33{ + 1, -1, 1, + 1, 1, -1, + -1, 1, 1 + }, + gemmi::CrystalSystem::Cubic, + 'F' + } + }, + { + 2, { + gemmi::Mat33{ + 1, -1, 0, + -1, 0, 1, + -1, -1, -1 + }, + gemmi::CrystalSystem::Trigonal, + 'R' + } + }, + { + 3, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Cubic, + 'P' + } + }, + { + 5, { + gemmi::Mat33{ + 1, 0, 1, + 1, 1, 0, + 0, 1, 1 + }, + gemmi::CrystalSystem::Cubic, + 'I' + } + }, + { + 4, { + gemmi::Mat33{ + 1, -1, 0, + -1, 0, 1, + -1, -1, -1 + }, + gemmi::CrystalSystem::Trigonal, + 'R' + } + }, + { + 6, { + gemmi::Mat33{ + 0, 1, 1, + 1, 0, 1, + 1, 1, 0 + }, + gemmi::CrystalSystem::Tetragonal, + 'I' + } + }, + { + 7, { + gemmi::Mat33{ + 1, 0, 1, + 1, 1, 0, + 0, 1, 1 + }, + gemmi::CrystalSystem::Tetragonal, + 'I' + } + }, + { + 8, { + gemmi::Mat33{ + -1, -1, 0, + -1, 0, -1, + 0, -1, -1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'I' + } + }, + { + 9, { + gemmi::Mat33{ + 1, 0, 0, + -1, 1, 0, + -1, -1, -3 + }, + gemmi::CrystalSystem::Trigonal, + 'R' + } + }, + { + 10, { + gemmi::Mat33{ + 1, 1, 0, + 1, -1, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 11, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Tetragonal, + 'P' + } + }, + { + 12, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Hexagonal, + 'P' + } + }, + { + 13, { + gemmi::Mat33{ + 1, 1, 0, + -1, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'C' + } + }, + { + 15, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 1, 1, 2 + }, + gemmi::CrystalSystem::Tetragonal, + 'I' + } + }, + { + 16, { + gemmi::Mat33{ + -1, -1, 0, + 1, -1, 0, + 1, 1, 2 + }, + gemmi::CrystalSystem::Orthorhombic, + 'F' + } + }, + { + 14, { + gemmi::Mat33{ + 1, 1, 0, + -1, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 17, { + gemmi::Mat33{ + 1, -1, 0, + 1, 1, 0, + -1, 0, -1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 18, { + gemmi::Mat33{ + 0, -1, 1, + 1, -1, -1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Tetragonal, + 'I' + } + }, + { + 19, { + gemmi::Mat33{ + -1, 0, 0, + 0, -1, 1, + -1, 1, 1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'I' + } + }, + { + 20, { + gemmi::Mat33{ + 0, 1, 1, + 0, 1, -1, + -1, 0, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 21, { + gemmi::Mat33{ + 0, 1, 0, + 0, 0, 1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Tetragonal, + 'P' + } + }, + { + 22, { + gemmi::Mat33{ + 0, 1, 0, + 0, 0, 1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Hexagonal, + 'P' + } + }, + { + 23, { + gemmi::Mat33{ + 0, 1, 1, + 0, -1, 1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Orthorhombic, + 'C' + } + }, + { + 24, { + gemmi::Mat33{ + 1, 2, 1, + 0, -1, 1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Trigonal, + 'R' + } + }, + { + 25, { + gemmi::Mat33{ + 0, 1, 1, + 0, -1, 1, + 1, 0, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 26, { + gemmi::Mat33{ + 1, 0, 0, + -1, 2, 0, + -1, 0, 2 + }, + gemmi::CrystalSystem::Orthorhombic, + 'F' + } + }, + { + 27, { + gemmi::Mat33{ + -1, 2, 0, + -1, 0, 0, + 0, -1, 1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 28, { + gemmi::Mat33{ + -1, 0, 0, + -1, 0, 2, + 0, 1, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 29, { + gemmi::Mat33{ + 1, 0, 0, + 1, -2, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 30, { + gemmi::Mat33{ + 0, 1, 0, + 0, 1, -2, + -1, 0, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 31, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Triclinic, + 'P' + } + }, + { + 32, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'P' + } + }, + { + 40, { + gemmi::Mat33{ + 0, -1, 0, + -1, 0, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'C' + } + }, + { + 35, { + gemmi::Mat33{ + 0, -1, 0, + -1, 0, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Monoclinic, + 'P' + } + }, + { + 36, { + gemmi::Mat33{ + 1, 0, 0, + -1, 0, -2, + 0, 1, 0 + }, + gemmi::CrystalSystem::Orthorhombic, + 'C' + } + }, + { + 33, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Monoclinic, + 'P' + } + }, + { + 38, { + gemmi::Mat33{ + -1, 0, 0, + 1, 2, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Orthorhombic, + 'C' + } + }, + { + 34, { + gemmi::Mat33{ + -1, 0, 0, + 0, 0, -1, + 0, -1, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'P' + } + }, + { + 42, { + gemmi::Mat33{ + -1, 0, 0, + 0, -1, 0, + 1, 1, 2 + }, + gemmi::CrystalSystem::Orthorhombic, + 'I' + } + }, + { + 41, { + gemmi::Mat33{ + 0, -1, -2, + 0, -1, 0, + -1, 0, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 37, { + gemmi::Mat33{ + 1, 0, 2, + 1, 0, 0, + 0, 1, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 39, { + gemmi::Mat33{ + -1, -2, 0, + -1, 0, 0, + 0, 0, -1 + }, + gemmi::CrystalSystem::Monoclinic, + 'C' + } + }, + { + 43, { + gemmi::Mat33{ + -1, 0, 0, + -1, -1, -2, + 0, -1, 0 + }, + gemmi::CrystalSystem::Monoclinic, + 'I' + } + }, + { + 44, { + gemmi::Mat33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + }, + gemmi::CrystalSystem::Triclinic, + 'P' + } + }, +}; struct NiggliClass { int number; @@ -16,9 +506,6 @@ struct NiggliClass { double cond_F; bool cond_DEF; bool cond_2DF; - gemmi::Mat33 reindex; - gemmi::CrystalSystem system; - char centering; }; LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance, double angle_tolerance) { @@ -41,269 +528,186 @@ LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance std::vector niggli_classes = { { 1, 1, - true, true, A / 2, A / 2, A / 2, false, false, - gemmi::Mat33{1, -1, 1, 1, 1, -1, -1, 1, 1}, - gemmi::CrystalSystem::Cubic, 'F' + true, true, A / 2, A / 2, A / 2, false, false }, { 2, 1, - true, true, D, D, D, false, false, - {1, -1, 0, -1, 0, 1, -1, -1, -1}, - gemmi::CrystalSystem::Trigonal, 'R' + true, true, D, D, D, false, false }, { 3, 2, - true, true, 0, 0, 0, false, false, - gemmi::Mat33{1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Cubic, 'P' + true, true, 0, 0, 0, false, false }, { 5, 2, - true, true, -A / 3, -A / 3, -A / 3, false, false, - gemmi::Mat33{1, 0, 1, 1, 1, 0, 0, 1, 1}, - gemmi::CrystalSystem::Cubic, 'I' + true, true, -A / 3, -A / 3, -A / 3, false, false }, { 4, 2, - true, true, D, D, D, false, false, - {1, -1, 0, -1, 0, 1, -1, -1, -1}, - gemmi::CrystalSystem::Trigonal, 'R' + true, true, D, D, D, false, false }, { 6, 2, - true, true, D, D, F, true, false, - {0, 1, 1, 1, 0, 1, 1, 1, 0}, - gemmi::CrystalSystem::Tetragonal, 'I' + true, true, D, D, F, true, false }, { 7, 2, - true, true, D, E, E, true, false, - {1, 0, 1, 1, 1, 0, 0, 1, 1}, - gemmi::CrystalSystem::Tetragonal, 'I' + true, true, D, E, E, true, false }, { 8, 2, - true, true, D, E, F, true, false, - {-1, -1, 0, -1, 0, -1, 0, -1, -1}, - gemmi::CrystalSystem::Orthorhombic, 'I' + true, true, D, E, F, true, false }, { 9, 1, - true, false, A / 2, A / 2, A / 2, false, false, - {1, 0, 0, -1, 1, 0, -1, -1, -3}, - gemmi::CrystalSystem::Trigonal, 'R' + true, false, A / 2, A / 2, A / 2, false, false }, { 10, 1, - true, false, D, D, F, false, false, - {1, 1, 0, 1, -1, 0, 0, 0, -1}, - gemmi::CrystalSystem::Monoclinic, 'C' + true, false, D, D, F, false, false }, { 11, 2, - true, false, 0, 0, 0, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Tetragonal, 'P' + true, false, 0, 0, 0, false, false }, { 12, 2, - true, false, 0, 0, -A / 2, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Hexagonal, 'P' + true, false, 0, 0, -A / 2, false, false }, { 13, 2, - true, false, 0, 0, F, false, false, - {1, 1, 0, -1, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Orthorhombic, 'C' + true, false, 0, 0, F, false, false }, { 15, 2, - true, false, -A / 2, -A / 2, 0, false, false, - {1, 0, 0, 0, 1, 0, 1, 1, 2}, - gemmi::CrystalSystem::Tetragonal, 'I' + true, false, -A / 2, -A / 2, 0, false, false }, { 16, 2, - true, false, D, D, F, true, false, - {-1, -1, 0, 1, -1, 0, 1, 1, 2}, - gemmi::CrystalSystem::Orthorhombic, 'F' + true, false, D, D, F, true, false }, { 14, 2, - true, false, D, D, F, false, false, - {1, 1, 0, -1, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Monoclinic, 'C' + true, false, D, D, F, false, false }, { 17, 2, - true, false, D, E, F, true, false, - {1, -1, 0, 1, 1, 0, -1, 0, -1}, - gemmi::CrystalSystem::Monoclinic, 'C' + true, false, D, E, F, true, false }, { 18, 1, - false, true, A / 4, A / 2, A / 2, false, false, - {0, -1, 1, 1, -1, -1, 1, 0, 0}, - gemmi::CrystalSystem::Tetragonal, 'I' + false, true, A / 4, A / 2, A / 2, false, false }, { 19, 1, - false, true, D, A / 2, A / 2, false, false, - {-1, 0, 0, 0, -1, 1, -1, 1, 1}, - gemmi::CrystalSystem::Orthorhombic, 'I' + false, true, D, A / 2, A / 2, false, false }, { 20, 1, - false, true, D, E, E, false, false, - {0, 1, 1, 0, 1, -1, -1, 0, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, true, D, E, E, false, false }, { 21, 2, - false, true, 0, 0, 0, false, false, - {0, 1, 0, 0, 0, 1, 1, 0, 0}, - gemmi::CrystalSystem::Tetragonal, 'P' + false, true, 0, 0, 0, false, false }, { 22, 2, - false, true, -B / 2, 0, 0, false, false, - {0, 1, 0, 0, 0, 1, 1, 0, 0}, - gemmi::CrystalSystem::Hexagonal, 'P' + false, true, -B / 2, 0, 0, false, false }, { 23, 2, - false, true, D, 0, 0, false, false, - {0, 1, 1, 0, -1, 1, 1, 0, 0}, - gemmi::CrystalSystem::Orthorhombic, 'C' + false, true, D, 0, 0, false, false }, { 24, 2, - false, true, D, -A / 3, -A / 3, true, false, - {1, 2, 1, 0, -1, 1, 1, 0, 0}, - gemmi::CrystalSystem::Trigonal, 'R' + false, true, D, -A / 3, -A / 3, true, false }, { 25, 2, - false, true, D, E, E, false, false, - {0, 1, 1, 0, -1, 1, 1, 0, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, true, D, E, E, false, false }, { 26, 1, - false, false, A / 4, A / 2, A / 2, false, false, - {1, 0, 0, -1, 2, 0, -1, 0, 2}, - gemmi::CrystalSystem::Orthorhombic, 'F' + false, false, A / 4, A / 2, A / 2, false, false }, { 27, 1, - false, false, D, A / 2, A / 2, false, false, - {-1, 2, 0, -1, 0, 0, 0, -1, 1}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, D, A / 2, A / 2, false, false }, { 28, 1, - false, false, D, A / 2, 2 * D, false, false, - {-1, 0, 0, -1, 0, 2, 0, 1, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, D, A / 2, 2 * D, false, false }, { 29, 1, - false, false, D, 2 * D, A / 2, false, false, - {1, 0, 0, 1, -2, 0, 0, 0, -1}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, D, 2 * D, A / 2, false, false }, { 30, 1, - false, false, B / 2, E, 2 * E, false, false, - {0, 1, 0, 0, 1, -2, -1, 0, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, B / 2, E, 2 * E, false, false }, { 31, 1, - false, false, D, E, F, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Triclinic, 'P' + false, false, D, E, F, false, false }, { 32, 2, - false, false, 0, 0, 0, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Orthorhombic, 'P' + false, false, 0, 0, 0, false, false }, { 40, 2, - false, false, -B / 2, 0, 0, false, false, - {0, -1, 0, -1, 0, 0, 0, 0, -1}, - gemmi::CrystalSystem::Orthorhombic, 'C' + false, false, -B / 2, 0, 0, false, false }, { 35, 2, - false, false, D, 0, 0, false, false, - {0, -1, 0, -1, 0, 0, 0, 0, -1}, - gemmi::CrystalSystem::Monoclinic, 'P' + false, false, D, 0, 0, false, false }, { 36, 2, - false, false, 0, -A / 2, 0, false, false, - {1, 0, 0, -1, 0, -2, 0, 1, 0}, - gemmi::CrystalSystem::Orthorhombic, 'C' + false, false, 0, -A / 2, 0, false, false }, { 33, 2, - false, false, 0, E, 0, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Monoclinic, 'P' + false, false, 0, E, 0, false, false }, { 38, 2, - false, false, 0, 0, -A / 2, false, false, - {-1, 0, 0, 1, 2, 0, 0, 0, -1}, - gemmi::CrystalSystem::Orthorhombic, 'C' + false, false, 0, 0, -A / 2, false, false }, { 34, 2, - false, false, 0, 0, F, false, false, - {-1, 0, 0, 0, 0, -1, 0, -1, 0}, - gemmi::CrystalSystem::Monoclinic, 'P' + false, false, 0, 0, F, false, false }, { 42, 2, - false, false, -B / 2, -A / 2, 0, false, false, - {-1, 0, 0, 0, -1, 0, 1, 1, 2}, - gemmi::CrystalSystem::Orthorhombic, 'I' + false, false, -B / 2, -A / 2, 0, false, false }, { 41, 2, - false, false, -B / 2, E, 0, false, false, - {0, -1, -2, 0, -1, 0, -1, 0, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, -B / 2, E, 0, false, false }, { 37, 2, - false, false, D, -A / 2, 0, false, false, - {1, 0, 2, 1, 0, 0, 0, 1, 0}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, D, -A / 2, 0, false, false }, { 39, 2, - false, false, D, 0, -A / 2, false, false, - {-1, -2, 0, -1, 0, 0, 0, 0, -1}, - gemmi::CrystalSystem::Monoclinic, 'C' + false, false, D, 0, -A / 2, false, false }, { 44, 2, - false, false, D, E, F, false, false, - {1, 0, 0, 0, 1, 0, 0, 0, 1}, - gemmi::CrystalSystem::Triclinic, 'P' + false, false, D, E, F, false, false } }; const auto uc_reduced = L_niggli.GetUnitCell(); + std::optional first_niggli_class; + std::vector possible_classes = {}; + for (const auto &c: niggli_classes) { if (c.type == 1 && uc_reduced.beta >= 90 - angle_tolerance ) continue; @@ -333,21 +737,25 @@ LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance ok = false; if (ok) { - return LatticeSearchResult{ - .niggli_class = c.number, - .primitive_reduced = L, - .conventional = L_niggli.Multiply(c.reindex), - .system = c.system, - .centering = c.centering, - }; + if (!first_niggli_class.has_value()) + first_niggli_class = c.number; + possible_classes.push_back(c.number); } } + int first_niggli_class_val = first_niggli_class.value_or(44); + + auto it = kNiggliClassDefinitions.find(first_niggli_class_val); + if (it == kNiggliClassDefinitions.end()) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, + "Wrong niggli class number " + std::to_string(first_niggli_class_val)); + return LatticeSearchResult{ - .niggli_class = 44, - .primitive_reduced = L, - .conventional = L, - .system = gemmi::CrystalSystem::Triclinic, - .centering = 'P', + .niggli_class = first_niggli_class_val, + .possible_niggli_classes = possible_classes, + .primitive_reduced = L_niggli, + .conventional = L_niggli.Multiply(it->second.reindex), + .system = it->second.system, + .centering = it->second.centering, }; } diff --git a/image_analysis/lattice_search/LatticeSearch.h b/image_analysis/lattice_search/LatticeSearch.h index 21b26c4c..9066db18 100644 --- a/image_analysis/lattice_search/LatticeSearch.h +++ b/image_analysis/lattice_search/LatticeSearch.h @@ -4,6 +4,7 @@ #ifndef JFJOCH_LATTICESEARCH_H #define JFJOCH_LATTICESEARCH_H +#include #include "../../common/CrystalLattice.h" #include "../../common/UnitCell.h" #include "../../common/Coord.h" @@ -12,12 +13,22 @@ struct LatticeSearchResult { int niggli_class = 44; + std::vector possible_niggli_classes; CrystalLattice primitive_reduced; CrystalLattice conventional; gemmi::CrystalSystem system = gemmi::CrystalSystem::Triclinic; char centering = 'P'; // 'P','A','B','C','I','F','R' }; +struct NiggliClassDefinition { + gemmi::Mat33 reindex; + gemmi::CrystalSystem system; + char centering; +}; + LatticeSearchResult LatticeSearch(const CrystalLattice& L, double dist_tolerance = 0.03, double angle_tolerance_deg = 3); +// Global lookup: niggli class -> definition +extern const std::map kNiggliClassDefinitions; + #endif //JFJOCH_LATTICESEARCH_H \ No newline at end of file