Loading...
Searching...
No Matches
cell_symmetry.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * BerkeleyGW's `cell_symmetry` header field: 1 for a lattice of the hexagonal
5 * family, 0 for every other.
6 *
7 * The BerkeleyGW specification defines it as 0 for cubic and 1 for hexagonal
8 * symmetry (Deslippe et al., Comput. Phys. Commun. 183, 1269 (2012), Appendix
9 * 11.1; see header.hpp). pw2bgw.x takes it from QE's `ibrav` input (in
10 * write_wfng and write_rhog, QE 7.5): 4 (hexagonal) and 5
11 * (trigonal R) give 1, and the cubic, tetragonal, orthorhombic, monoclinic
12 * and triclinic indices give 0. Q3 records the same index when the input names
13 * a Bravais lattice. Every other cell is a free lattice, index 0: a YAML
14 * lattice outside the cubic family, YAML `lattice.vectors`, and QE's
15 * `CELL_PARAMETERS`, which is how Materials Project cells arrive. The export
16 * used to refuse those, because pw2bgw.x settles that case from an input Q3 has
17 * no counterpart of, which shut out every non-cubic crystal.
18 *
19 * The index only names the shape of the lattice, so the shape is read off the
20 * vectors instead:
21 *
22 * - hexagonal (as index 4): two vectors of equal length at 120 or 60
23 * degrees, and the third perpendicular to both;
24 * - trigonal R (as index 5): three vectors of equal length at one common
25 * angle that is none of the three a cubic lattice's primitive cell has,
26 * 90 (simple), 60 (face-centred) and 109.47 degrees (body-centred);
27 * - anything else is in the cubic family's bracket, 0.
28 */
29
30#include <array>
31#include <cmath>
32#include <cstdlib>
33#include <stdexcept>
34#include <string>
35
38
39namespace q3 {
40
41namespace berkeleygw_cell_symmetry_detail {
42
43/**
44 * Relative agreement two lengths, or two angle cosines, need to count as
45 * equal. Loose enough for a cell written to the six or seven decimals
46 * structure databases carry (a "120 degree" angle stored as 120.000006 is
47 * 1e-7 away in cosine), and far tighter than any lattice that is genuinely
48 * not hexagonal.
49 */
50constexpr Real metric_tolerance = 1e-5;
51
52/// The dot product of rows `first` and `second` of `vectors`.
53inline Real dot(const Matrix3x3& vectors, int first, int second) {
54 return vectors[first].dot(vectors[second]);
55}
56
57/// Whether two dimensionless quantities -- length ratios or cosines -- agree to `metric_tolerance`.
58inline bool close(Real first, Real second) {
59 return std::abs(first - second) <= metric_tolerance;
60}
61
62} // namespace berkeleygw_cell_symmetry_detail
63
64/// Whether the rows of `lattice_vectors` span a hexagonal or trigonal-R lattice.
65inline bool lattice_is_in_the_hexagonal_family(const Matrix3x3& lattice_vectors) {
66 using namespace berkeleygw_cell_symmetry_detail;
67 const std::array<Real, 3> length = {std::sqrt(dot(lattice_vectors, 0, 0)),
68 std::sqrt(dot(lattice_vectors, 1, 1)),
69 std::sqrt(dot(lattice_vectors, 2, 2))};
70 if (length[0] <= 0.0 || length[1] <= 0.0 || length[2] <= 0.0)
71 return false;
72 auto cosine = [&](int first, int second) {
73 return dot(lattice_vectors, first, second) / (length[first] * length[second]);
74 };
75 auto same_length = [&](int first, int second) {
76 return close(length[first] / length[second], 1.0);
77 };
78
79 for (int unique = 0; unique < 3; unique++) {
80 const int first = (unique + 1) % 3;
81 const int second = (unique + 2) % 3;
82 if (same_length(first, second) && close(std::abs(cosine(first, second)), 0.5) &&
83 close(cosine(unique, first), 0.0) && close(cosine(unique, second), 0.0))
84 return true;
85 }
86
87 if (same_length(0, 1) && same_length(1, 2)) {
88 const Real common = cosine(0, 1);
89 if (close(cosine(1, 2), common) && close(cosine(0, 2), common)) {
90 const bool cubic_primitive =
91 close(common, 0.0) || close(common, 0.5) || close(common, -1.0 / 3.0);
92 return !cubic_primitive;
93 }
94 }
95 return false;
96}
97
98/**
99 * The `cell_symmetry` value for a cell: from the Bravais index when the input
100 * named one, from the lattice vectors when it is a free lattice (index 0).
101 */
102inline int berkeleygw_cell_symmetry(int bravais_lattice_index, const Matrix3x3& lattice_vectors) {
103 const int absolute_index = std::abs(bravais_lattice_index);
104 if (bravais_lattice_index == 0)
105 return lattice_is_in_the_hexagonal_family(lattice_vectors) ? 1 : 0;
106 if (absolute_index >= 1 && absolute_index <= 3)
107 return 0; // simple, face- and body-centred cubic
108 if (absolute_index >= 4 && absolute_index <= 5)
109 return 1; // hexagonal, trigonal R
110 if (absolute_index >= 6 && absolute_index <= 14)
111 return 0; // tetragonal, orthorhombic, monoclinic, triclinic
112 throw std::runtime_error("BerkeleyGW cell_symmetry: unrecognized bravais_lattice_index " +
113 std::to_string(bravais_lattice_index));
114}
115
116} // namespace q3
Real dot(const Matrix3x3 &vectors, int first, int second)
The dot product of rows first and second of vectors.
bool close(Real first, Real second)
Whether two dimensionless quantities – length ratios or cosines – agree to metric_tolerance.
double Real
bool lattice_is_in_the_hexagonal_family(const Matrix3x3 &lattice_vectors)
Whether the rows of lattice_vectors span a hexagonal or trigonal-R lattice.
int berkeleygw_cell_symmetry(int bravais_lattice_index, const Matrix3x3 &lattice_vectors)
3×3 matrix stored row-major: row i = mat[i]