// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "../gemmi_gph/gemmi/math.hpp" #include "../gemmi_gph/gemmi/symmetry.hpp" #include #include #include "Coord.h" #include "UnitCell.h" // Below this a basis counts as coplanar. 0.02 is 1.1 deg off flat: an order of magnitude below the // flattest basis a real indexing candidate has been seen to reach, and three orders above the point // where float cell angles stop carrying even the SIGN of the metric determinant. constexpr float MIN_BASIS_VOLUME_FRACTION = 0.02f; class CrystalLattice { Coord vec[3]; void FixHandedness(); // Throws if the basis is too flat to have a reciprocal cell. Everything that builds a lattice // rejects one this flat, so reaching it means a degenerate basis got through somewhere. void CheckHasReciprocal() const; public: void ReorderABEqual(); void ReorderMonoclinic(); CrystalLattice() = default; explicit CrystalLattice(const UnitCell &cell); explicit CrystalLattice(const std::vector& input); CrystalLattice(float a, float b, float c, float alpha, float beta, float gamma); CrystalLattice(const Coord &a, const Coord &b, const Coord &c); [[nodiscard]] float CalcVolume() const; // |V| / (|a| |b| |c|) - the cell volume made scale-free: 1 for an orthogonal basis, 0 for three // coplanar rows. This is the test for a degenerate basis, not CalcVolume(): three nearly-coplanar // 300 A rows still enclose hundreds of A^3, so an absolute volume cannot see them, and a basis // that flat has no usable reciprocal cell (1/V blows up) whatever its size. [[nodiscard]] float VolumeFraction() const; [[nodiscard]] const Coord &Vec0() const; [[nodiscard]] const Coord &Vec1() const; [[nodiscard]] const Coord &Vec2() const; [[nodiscard]] Coord Astar() const; [[nodiscard]] Coord Bstar() const; [[nodiscard]] Coord Cstar() const; [[nodiscard]] UnitCell GetUnitCell() const; [[nodiscard]] std::vector GetVector() const; CrystalLattice FromPrimitive(char centering) const; CrystalLattice ToPrimitive(char centering) const; CrystalLattice Multiply(const RotMatrix &input) const; CrystalLattice Multiply(const gemmi::Mat33 &input) const; void Sort(); void Regularize(const gemmi::CrystalSystem &input); [[nodiscard]] std::vector GetUBMatrix() const; CrystalLattice NiggliReduce() const; void FlipSign(size_t i1); }; inline std::ostream &operator<<( std::ostream &output, const CrystalLattice &in ) { output << in.Vec0() << std::endl << in.Vec1() << std::endl << in.Vec2() << std::endl; return output; }