CASM  1.1.0
A Clusters Approach to Statistical Mechanics
Niggli.cc
Go to the documentation of this file.
2 
3 #include <set>
4 
7 #include "casm/misc/CASM_math.hh"
8 
9 namespace CASM {
10 namespace xtal {
11 
12 std::vector<Eigen::Matrix3d> _cell_invariant_transforms() {
13  std::vector<Eigen::Matrix3d> result(24, Eigen::Matrix3d::Zero());
14  Index i = 0;
15  result[i].setIdentity();
16 
17  ++i;
18  result[i](0, 1) = result[i](1, 2) = result[i](2, 0) = 1;
19 
20  ++i;
21  result[i](0, 2) = result[i](1, 0) = result[i](2, 1) = 1;
22 
23  ++i;
24  result[i](0, 1) = result[i](1, 0) = result[i](2, 2) = -1;
25 
26  ++i;
27  result[i](0, 2) = result[i](1, 1) = result[i](2, 0) = -1;
28 
29  ++i;
30  result[i](0, 0) = result[i](1, 2) = result[i](2, 1) = -1;
31 
33 
34  for (Index j = 0; j < 3; ++j) {
35  tmat(j, j) = 1;
36  for (Index k = 0; k < 6; ++k) {
37  result[++i] = result[k] * tmat;
38  }
39  tmat(j, j) = -1;
40  }
41 
42  return result;
43 }
44 
45 std::vector<Eigen::Matrix3d> const &NiggliRep::cell_invariant_transforms() {
46  static std::vector<Eigen::Matrix3d> result = _cell_invariant_transforms();
47  return result;
48 }
49 
50 NiggliRep::NiggliRep(const Eigen::Matrix3d &init_lat_col_mat)
51  : m_metrical_matrix(init_lat_col_mat.transpose() * init_lat_col_mat),
52  m_scale_factor(cuberoot(init_lat_col_mat.determinant())) {}
53 
54 NiggliRep::NiggliRep(const Lattice &init_lat)
55  : NiggliRep(init_lat.lat_column_mat()) {}
56 
57 double NiggliRep::A() const { return m_metrical_matrix(0, 0); }
58 
59 double NiggliRep::B() const { return m_metrical_matrix(1, 1); }
60 
61 double NiggliRep::C() const { return m_metrical_matrix(2, 2); }
62 
63 double NiggliRep::ksi() const { return m_metrical_matrix(2, 1) * 2; }
64 
65 double NiggliRep::eta() const { return m_metrical_matrix(2, 0) * 2; }
66 
67 double NiggliRep::zeta() const { return m_metrical_matrix(1, 0) * 2; }
68 
70  return m_metrical_matrix;
71 }
72 
73 bool NiggliRep::meets_criteria_1(double compare_tol) const {
74  bool does_meet = true;
75  compare_tol = m_scale_factor * compare_tol;
76  // For niggli you need A<=B
77  if (CASM::compare(B(), A(), compare_tol)) {
78  does_meet = false;
79  }
80 
81  // If A==B, then niggli requires |ksi|<=|eta|
82  else if (almost_equal(A(), B(), compare_tol) &&
83  compare(std::abs(eta()), std::abs(ksi()), compare_tol)) {
84  does_meet = false;
85  }
86 
87  return does_meet;
88 }
89 
90 bool NiggliRep::meets_criteria_2(double compare_tol) const {
91  bool does_meet = true;
92 
93  compare_tol = m_scale_factor * compare_tol;
94  // For niggli you need B<=C
95  if (compare(C(), B(), compare_tol)) {
96  does_meet = false;
97  }
98 
99  // If B==C, then niggli requires |eta|<=|zeta|
100  else if (almost_equal(B(), C(), compare_tol) &&
101  compare(std::abs(zeta()), std::abs(eta()), compare_tol)) {
102  does_meet = false;
103  }
104 
105  return does_meet;
106 }
107 
108 bool NiggliRep::meets_criteria_3(double compare_tol) const {
109  compare_tol = m_scale_factor * compare_tol;
110  bool does_meet = false;
111  // For type one niggli cells, the angles are all less than 90 degrees
112  if (compare(0.0, ksi(), compare_tol) && compare(0.0, eta(), compare_tol) &&
113  compare(0.0, zeta(), compare_tol)) {
114  does_meet = true;
115  }
116 
117  return does_meet;
118 }
119 
120 bool NiggliRep::meets_criteria_4(double compare_tol) const {
121  compare_tol = m_scale_factor * compare_tol;
122  bool does_meet = false;
123  // For type two niggli cells, the angles are all more than or equal to 90
124  // degrees
125  if (!compare(0.0, ksi(), compare_tol) && !compare(0.0, eta(), compare_tol) &&
126  !compare(0.0, zeta(), compare_tol)) {
127  does_meet = true;
128  }
129 
130  return does_meet;
131 }
132 
133 bool NiggliRep::meets_criteria_5(double compare_tol) const {
134  compare_tol = m_scale_factor * compare_tol;
135  bool does_meet = true;
136 
137  // For niggli you need |ksi|<=B
138  if (compare(B(), std::abs(ksi()), compare_tol)) {
139  does_meet = false;
140  }
141 
142  // If ksi==B, then niggli requires zeta<=2*eta
143  else if (almost_equal(ksi(), B(), compare_tol) &&
144  compare(2 * eta(), zeta(), compare_tol)) {
145  }
146 
147  // If ksi==-B, then niggli requires zeta==0
148  else if (almost_equal(ksi(), (-1) * B(), compare_tol) &&
149  !almost_zero(zeta(), compare_tol)) {
150  does_meet = false;
151  }
152 
153  return does_meet;
154 }
155 
156 bool NiggliRep::meets_criteria_6(double compare_tol) const {
157  compare_tol = m_scale_factor * compare_tol;
158  bool does_meet = true;
159 
160  // For niggli you need |eta|<=A
161  if (compare(A(), std::abs(eta()), compare_tol)) {
162  does_meet = false;
163  }
164 
165  // If ksi==A, then niggli requires zeta<=2*ksi
166  else if (almost_equal(eta(), A(), compare_tol) &&
167  compare(2 * ksi(), zeta(), compare_tol)) {
168  does_meet = false;
169  }
170 
171  // If ksi==-A, then niggli requires zeta==0
172  else if (almost_equal(eta(), (-1) * A(), compare_tol) &&
173  !almost_zero(zeta(), compare_tol)) {
174  does_meet = false;
175  }
176 
177  return does_meet;
178 }
179 
180 bool NiggliRep::meets_criteria_7(double compare_tol) const {
181  compare_tol = m_scale_factor * compare_tol;
182  bool does_meet = true;
183 
184  // For niggli you need |zeta|<=A
185  if (compare(A(), std::abs(zeta()), compare_tol)) {
186  does_meet = false;
187  }
188 
189  // If zeta==A, then you need eta<=2*ksi
190  else if (almost_equal(zeta(), A(), compare_tol) &&
191  compare(2 * ksi(), eta(), compare_tol)) {
192  does_meet = false;
193  }
194 
195  // If zeta==-A, then you need eta==0
196  else if (almost_equal(zeta(), (-1) * A(), compare_tol) &&
197  !almost_zero(eta(), compare_tol)) {
198  does_meet = false;
199  }
200 
201  return does_meet;
202 }
203 
204 bool NiggliRep::meets_criteria_8(double compare_tol) const {
205  compare_tol = m_scale_factor * compare_tol;
206  bool does_meet = true;
207 
208  double clobber = A() + B() + C() + ksi() + eta() + zeta();
209 
210  // For niggli you need C<=A+B+C+ksi+eta+zeta
211  if (compare(clobber, C(), compare_tol)) {
212  does_meet = false;
213  }
214 
215  // If C==A+B+C+ksi+eta+zeta, then 2*A+2*eta+zeta<=0
216  else if (almost_equal(clobber, C(), compare_tol) &&
217  compare(0.0, 2 * A() + 2 * eta() + zeta(), compare_tol)) {
218  does_meet = false;
219  }
220 
221  return does_meet;
222 }
223 
224 bool NiggliRep::is_niggli_type1(double compare_tol) const {
225  return (is_niggli(compare_tol) && meets_criteria_3(compare_tol));
226 }
227 
228 bool NiggliRep::is_niggli_type2(double compare_tol) const {
229  return (is_niggli(compare_tol) && meets_criteria_4(compare_tol));
230 }
231 
232 bool NiggliRep::is_niggli(double compare_tol) const {
233  bool totally_niggli = false;
234  if (meets_criteria_1(compare_tol) && meets_criteria_2(compare_tol) &&
235  (meets_criteria_3(compare_tol) != meets_criteria_4(compare_tol)) &&
236  meets_criteria_5(compare_tol) && meets_criteria_6(compare_tol) &&
237  meets_criteria_7(compare_tol) && meets_criteria_8(compare_tol)) {
238  totally_niggli = true;
239  }
240 
241  return totally_niggli;
242 }
243 
244 void NiggliRep::debug_criteria(double compare_tol) const {
245  std::cout << meets_criteria_1(compare_tol) << meets_criteria_2(compare_tol)
246  << meets_criteria_3(compare_tol) << meets_criteria_4(compare_tol)
247  << meets_criteria_5(compare_tol) << meets_criteria_6(compare_tol)
248  << meets_criteria_7(compare_tol) << meets_criteria_8(compare_tol)
249  << std::endl;
250 
251  std::cout << A() << " " << B() << " " << C() << " " << ksi() << " " << eta()
252  << " " << zeta() << std::endl;
253 
254  return;
255 }
256 
257 Index NiggliRep::niggli_index(double compare_tol) const {
258  return Index(meets_criteria_1(compare_tol)) +
259  Index(meets_criteria_2(compare_tol)) +
260  Index(meets_criteria_3(compare_tol) || meets_criteria_4(compare_tol)) +
261  Index(meets_criteria_5(compare_tol)) +
262  Index(meets_criteria_6(compare_tol)) +
263  Index(meets_criteria_7(compare_tol)) +
264  Index(meets_criteria_8(compare_tol));
265 }
266 
267 Index niggli_index(const Eigen::Matrix3d &test_lat_mat, double compare_tol) {
268  return NiggliRep(test_lat_mat).niggli_index(compare_tol);
269 }
270 
271 bool is_niggli(const Eigen::Matrix3d &test_lat_mat, double compare_tol) {
272  NiggliRep ntest(test_lat_mat);
273 
274  // ntest.debug_criteria(compare_tol);
275  return ntest.is_niggli(compare_tol);
276 }
277 
278 bool is_niggli(const Lattice &test_lat, double compare_tol) {
279  return is_niggli(test_lat.lat_column_mat(), compare_tol);
280 }
281 
282 // Helper function, declared only in this file. Returns all niggli cells of
283 // 'in_lat'
284 std::set<Eigen::Matrix3d, StandardOrientationCompare> _niggli_set(
285  const Lattice &in_lat, double compare_tol, bool keep_handedness) {
286  Lattice reduced_in = in_lat.reduced_cell();
287 
288  if (!keep_handedness) {
289  reduced_in.make_right_handed();
290  }
291 
292  std::set<Eigen::Matrix3d, StandardOrientationCompare> result(
293  (StandardOrientationCompare(compare_tol)));
294 
295  // The parallelepipiped of the reduced cell already has the shape of the
296  // finally niggli cell, but we must select which edges will form the lattice
297  // vectors (satisfying the Niggli property) and which orientation of the
298  // lattice vectors to use (satisfying the CASM-canonical property) For now, we
299  // optimize both properties simultaneously by looping over all transformation
300  // matrices with determinant 1 and elements -1, 0 or 1. There are 3480 such
301  // matrices We could vastly improve this to maximum 24 + 24 transformations if
302  // we knew the (CRYSTAL) point-group There are 24 possible lattice vector
303  // choices to screen for Niggli, starting from the reduced cell Once the
304  // Niggli condition is achieved, there are maximum 24 proper point group
305  // rotations to consider to screen for canonical orientation
306  const std::vector<Eigen::Matrix3i> &candidate_trans_mats =
308  Eigen::Matrix3d candidate_lat_mat;
309  for (auto it = candidate_trans_mats.begin(); it != candidate_trans_mats.end();
310  ++it) {
311  candidate_lat_mat = reduced_in.lat_column_mat() * it->cast<double>();
312  if (is_niggli(candidate_lat_mat, compare_tol)) {
313  result.insert(candidate_lat_mat);
314  }
315  }
316  return result;
317 }
318 
330 Lattice niggli(const Lattice &in_lat, double compare_tol,
331  bool keep_handedness) {
332  auto lat_set = _niggli_set(in_lat, compare_tol, keep_handedness);
333  if (lat_set.empty()) {
334  throw std::runtime_error(
335  "In niggli(), did not find any lattices matching niggli criteria!");
336  }
337  return Lattice(*lat_set.rbegin(), in_lat.tol());
338 }
339 
355  double compare_tol) {
356  // We want to give a preference to lattices with more positive values than
357  // negative ones Count how many non-negative entries there are
358  int non_negatives = 0;
359  for (int i = 0; i < 3; i++) {
360  for (int j = 0; j < 3; j++) {
361  if (float_sgn(lat_mat(i, j), compare_tol) != -1) {
362  non_negatives++;
363  }
364  }
365  }
366 
367  Eigen::VectorXd lat_spatial_descriptor(16);
368 
369  lat_spatial_descriptor <<
370  // Favor positive values
371  non_negatives,
372 
373  // Favor large diagonal values
374  lat_mat(0, 0), lat_mat(1, 1), lat_mat(2, 2),
375 
376  // Favor small off diagonal values
377  -std::abs(lat_mat(2, 1)), -std::abs(lat_mat(2, 0)),
378  -std::abs(lat_mat(1, 0)), -std::abs(lat_mat(1, 2)),
379  -std::abs(lat_mat(0, 2)), -std::abs(lat_mat(0, 1)),
380 
381  // Favor upper triangular
382  float_sgn(lat_mat(2, 1), compare_tol),
383  float_sgn(lat_mat(2, 0), compare_tol),
384  float_sgn(lat_mat(1, 0), compare_tol),
385  float_sgn(lat_mat(1, 2), compare_tol),
386  float_sgn(lat_mat(0, 2), compare_tol),
387  float_sgn(lat_mat(0, 1), compare_tol);
388 
389  return lat_spatial_descriptor;
390 }
391 
401  const Eigen::Matrix3d &low_score_lat_mat,
402  const Eigen::Matrix3d &high_score_lat_mat, double compare_tol) {
403  Eigen::VectorXd low_score_lat_unroll =
404  spatial_unroll(low_score_lat_mat, compare_tol);
405  Eigen::VectorXd high_score_lat_unroll =
406  spatial_unroll(high_score_lat_mat, compare_tol);
407 
408  return float_lexicographical_compare(low_score_lat_unroll,
409  high_score_lat_unroll, compare_tol);
410 }
411 
427 bool standard_orientation_compare(const Eigen::Matrix3d &low_score_lat_mat,
428  const Eigen::Matrix3d &high_score_lat_mat,
429  double compare_tol) {
430  bool low_score_is_symmetric =
431  Eigen::is_symmetric(low_score_lat_mat, compare_tol);
432  bool low_score_is_bisymmetric =
433  Eigen::is_bisymmetric(low_score_lat_mat, compare_tol);
434 
435  bool high_score_is_symmetric =
436  Eigen::is_symmetric(high_score_lat_mat, compare_tol);
437  bool high_score_is_bisymmetric =
438  Eigen::is_bisymmetric(high_score_lat_mat, compare_tol);
439 
440  // The high_score is less symmetric than the low score, therefore high<low
441  if ((low_score_is_bisymmetric && !high_score_is_bisymmetric) ||
442  (low_score_is_symmetric && !high_score_is_symmetric)) {
443  return false;
444  }
445 
446  // The high_score is more symmetric than the low_score, therefore high>low
447  // automatically (matrix symmetry trumps spatial orientation)
448  else if ((!low_score_is_bisymmetric && high_score_is_bisymmetric) ||
449  (!low_score_is_symmetric && high_score_is_symmetric)) {
450  return true;
451  }
452 
453  // If you made it here then the high_score and the low_score have the same
454  // symmetry level, so we check for the spatial orientation
455  else {
457  low_score_lat_mat, high_score_lat_mat, compare_tol);
458  }
459 }
460 
461 } // namespace xtal
462 } // namespace CASM
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:110
double tol() const
Definition: Lattice.hh:195
Lattice reduced_cell() const
Find the lattice vectors which give most compact unit cell Compactness is measured by how close lat_c...
Definition: Lattice.cc:302
Lattice & make_right_handed()
Flip c vector if it's on the wrong side of a-b plane – return (*this)
Definition: Lattice.cc:466
double zeta() const
2ab*cos(gamma)
Definition: Niggli.cc:67
double ksi() const
2bc*cos(alpha)
Definition: Niggli.cc:63
const Eigen::Matrix3d & metrical_matrix() const
Definition: Niggli.cc:69
NiggliRep(const Lattice &init_lat)
Definition: Niggli.cc:54
bool meets_criteria_3(double compare_tol) const
For type I: ksi>0 && eta>0 && zeta>0 (all angles < 90)
Definition: Niggli.cc:108
bool is_niggli(double compare_tol) const
True if all conditions are true, and either 4 OR 3 is false.
Definition: Niggli.cc:232
bool meets_criteria_7(double compare_tol) const
|zeta|<=A OR (zeta==A, eta<=2*ksi) OR (zeta==-A, eta==0)
Definition: Niggli.cc:180
bool meets_criteria_5(double compare_tol) const
|ksi|<=B OR (ksi==B, zeta<=2*eta) OR (ksi==-B, zeta==0)
Definition: Niggli.cc:133
double eta() const
2ac*cos(beta)
Definition: Niggli.cc:65
Index niggli_index(double compare_tol) const
Number of criteria met.
Definition: Niggli.cc:257
bool meets_criteria_4(double compare_tol) const
For type II: ksi<=0 && eta<=0 && zeta<=0 (all angles >= 90)
Definition: Niggli.cc:120
bool meets_criteria_6(double compare_tol) const
|eta|<=A OR (eta==A, zeta<=2*ksi) OR (eta==-A, zeta==0)
Definition: Niggli.cc:156
bool meets_criteria_1(double compare_tol) const
A<=B OR (A==B, |ksi| <= |eta|)
Definition: Niggli.cc:73
bool meets_criteria_8(double compare_tol) const
Definition: Niggli.cc:204
double C() const
Square of lattice length c.
Definition: Niggli.cc:61
static std::vector< Eigen::Matrix3d > const & cell_invariant_transforms()
Definition: Niggli.cc:45
double A() const
Square of lattice length a.
Definition: Niggli.cc:57
bool is_niggli_type1(double compare_tol) const
True if all conditions except 4 are true.
Definition: Niggli.cc:224
bool is_niggli_type2(double compare_tol) const
True if all conditions except 3 are true.
Definition: Niggli.cc:228
void debug_criteria(double compare_tol) const
Definition: Niggli.cc:244
bool meets_criteria_2(double compare_tol) const
B<=C OR (B==C, |eta| <= |zeta|)
Definition: Niggli.cc:90
const Eigen::Matrix3d m_metrical_matrix
Transpose of initialization lattice dotted with itself.
Definition: Niggli.hh:123
const double m_scale_factor
Scaling factor for niggli comparisons.
Definition: Niggli.hh:126
double B() const
Square of lattice length b.
Definition: Niggli.cc:59
const std::vector< Eigen::Matrix3i > & positive_unimodular_matrices()
IdentitySymRepBuilder Identity()
Index niggli_index(const Eigen::Matrix3d &test_lat_mat, double compare_tol)
Number of niggli criteria met.
Definition: Niggli.cc:267
bool standard_orientation_spatial_compare(const Eigen::Matrix3d &low_score_lat_mat, Eigen::Matrix3d &high_score_lat_mat, double compare_tol)
Compare the spatial orientation (ignoring matrix symmetry) and determine which one is oriented more s...
bool is_niggli(const Eigen::Matrix3d &test_lat_mat, double compare_tol)
Definition: Niggli.cc:271
Eigen::VectorXd spatial_unroll(const Eigen::Matrix3d &lat_mat, double compare_tol)
Generate a vector whose lexicographical value determines how well it's oriented in space.
Definition: Niggli.cc:354
Lattice niggli(const Lattice &in_lat, double compare_tol, bool keep_handedness=false)
Definition: Niggli.cc:330
std::set< Eigen::Matrix3d, StandardOrientationCompare > _niggli_set(const Lattice &in_lat, double compare_tol, bool keep_handedness)
Definition: Niggli.cc:284
std::vector< Eigen::Matrix3d > _cell_invariant_transforms()
Definition: Niggli.cc:12
bool standard_orientation_compare(const Eigen::Matrix3d &low_score_lat_mat, const Eigen::Matrix3d &high_score_lat_mat, double compare_tol)
Determine whether high_score has a more standard format than low_score.
Definition: Niggli.cc:427
Main CASM namespace.
Definition: APICommand.hh:8
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
bool compare(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Compare ClusterInvariants.
double cuberoot(double number)
Definition: CASM_math.cc:272
Eigen::Matrix3d Matrix3d
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:104
int float_sgn(T val, double compare_tol=TOL)
Definition: CASM_math.hh:188
bool float_lexicographical_compare(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, double tol)
Floating point lexicographical comparison with tol.
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd
bool is_symmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
bool is_bisymmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)