CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
Niggli.cc
Go to the documentation of this file.
4 #include "casm/misc/CASM_math.hh"
5 
6 namespace CASM {
7  NiggliRep::NiggliRep(const Eigen::Matrix3d &init_lat_col_mat):
8  m_metrical_matrix(init_lat_col_mat.transpose() * init_lat_col_mat) {
9  }
10 
11  NiggliRep::NiggliRep(const Lattice &init_lat):
12  NiggliRep(init_lat.lat_column_mat()) {
13  }
14 
15  double NiggliRep::A() const {
16  return m_metrical_matrix(0, 0);
17  }
18 
19  double NiggliRep::B() const {
20  return m_metrical_matrix(1, 1);
21  }
22 
23  double NiggliRep::C() const {
24  return m_metrical_matrix(2, 2);
25  }
26 
27  double NiggliRep::ksi() const {
28  return m_metrical_matrix(2, 1) * 2;
29  }
30 
31  double NiggliRep::eta() const {
32  return m_metrical_matrix(2, 0) * 2;
33  }
34 
35  double NiggliRep::zeta() const {
36  return m_metrical_matrix(1, 0) * 2;
37  }
38 
40  return m_metrical_matrix;
41  }
42 
43  bool NiggliRep::meets_criteria_1(double compare_tol) const {
44  bool does_meet = true;
45 
46  //For niggli you need A<=B
47  if(CASM::compare(B(), A(), compare_tol)) {
48  does_meet = false;
49  }
50 
51  //If A==B, then niggli requires |ksi|<=|eta|
52  else if(almost_equal(A(), B(), compare_tol) && compare(std::abs(eta()), std::abs(ksi()), compare_tol)) {
53  does_meet = false;
54  }
55 
56  return does_meet;
57  }
58 
59  bool NiggliRep::meets_criteria_2(double compare_tol) const {
60  bool does_meet = true;
61 
62  //For niggli you need B<=C
63  if(compare(C(), B(), compare_tol)) {
64  does_meet = false;
65  }
66 
67  //If B==C, then niggli requires |eta|<=|zeta|
68  else if(almost_equal(B(), C(), compare_tol) && compare(std::abs(zeta()), std::abs(eta()), compare_tol)) {
69  does_meet = false;
70  }
71 
72  return does_meet;
73  }
74 
75  bool NiggliRep::meets_criteria_3(double compare_tol) const {
76  bool does_meet = false;
77 
78  //For type one niggli cells, the angles are all less than 90 degrees
79  if(compare(0.0, ksi(), compare_tol) && compare(0.0, eta(), compare_tol) && compare(0.0, zeta(), compare_tol)) {
80  does_meet = true;
81  }
82 
83  return does_meet;
84  }
85 
86  bool NiggliRep::meets_criteria_4(double compare_tol) const {
87  bool does_meet = false;
88 
89  //For type two niggli cells, the angles are all more than or equal to 90 degrees
90  if(!compare(0.0, ksi(), compare_tol) && !compare(0.0, eta(), compare_tol) && !compare(0.0, zeta(), compare_tol)) {
91  does_meet = true;
92  }
93 
94  return does_meet;
95  }
96 
97  bool NiggliRep::meets_criteria_5(double compare_tol) const {
98  bool does_meet = true;
99 
100  //For niggli you need |ksi|<=B
101  if(compare(B(), std::abs(ksi()), compare_tol)) {
102  does_meet = false;
103  }
104 
105  //If ksi==B, then niggli requires zeta<=2*eta
106  else if(almost_equal(ksi(), B(), compare_tol) && compare(2 * eta(), zeta(), compare_tol)) {
107  does_meet = false;
108  }
109 
110  //If ksi==-B, then niggli requires zeta==0
111  else if(almost_equal(ksi(), (-1)*B(), compare_tol) && !almost_zero(zeta(), compare_tol)) {
112  does_meet = false;
113  }
114 
115  return does_meet;
116  }
117 
118  bool NiggliRep::meets_criteria_6(double compare_tol) const {
119  bool does_meet = true;
120 
121  //For niggli you need |eta|<=A
122  if(compare(A(), std::abs(eta()), compare_tol)) {
123  does_meet = false;
124  }
125 
126  //If ksi==A, then niggli requires zeta<=2*ksi
127  else if(almost_equal(eta(), A(), compare_tol) && compare(2 * ksi(), zeta(), compare_tol)) {
128  does_meet = false;
129  }
130 
131  //If ksi==-A, then niggli requires zeta==0
132  else if(almost_equal(eta(), (-1)*A(), compare_tol) && !almost_zero(zeta(), compare_tol)) {
133  does_meet = false;
134  }
135 
136  return does_meet;
137  }
138 
139  bool NiggliRep::meets_criteria_7(double compare_tol) const {
140  bool does_meet = true;
141 
142  //For niggli you need |zeta|<=A
143  if(compare(A(), std::abs(zeta()), compare_tol)) {
144  does_meet = false;
145  }
146 
147  //If zeta==A, then you need eta<=2*ksi
148  else if(almost_equal(zeta(), A(), compare_tol) && compare(2 * ksi(), eta(), compare_tol)) {
149  does_meet = false;
150  }
151 
152  //If zeta==-A, then you need eta==0
153  else if(almost_equal(zeta(), (-1)*A(), compare_tol) && !almost_zero(eta(), compare_tol)) {
154  does_meet = false;
155  }
156 
157  return does_meet;
158  }
159 
160  bool NiggliRep::meets_criteria_8(double compare_tol) const {
161  bool does_meet = true;
162 
163  double clobber = A() + B() + C() + ksi() + eta() + zeta();
164 
165  //For niggli you need C<=A+B+C+ksi+eta+zeta
166  if(compare(clobber, C(), compare_tol)) {
167  does_meet = false;
168  }
169 
170  //If C==A+B+C+ksi+eta+zeta, then 2*A+2*eta+zeta<=0
171  else if(almost_equal(clobber, C(), compare_tol) && compare(0.0, 2 * A() + 2 * eta() + zeta(), compare_tol)) {
172  does_meet = false;
173  }
174 
175  return does_meet;
176  }
177 
178  bool NiggliRep::is_niggli_type1(double compare_tol) const {
179  return (is_niggli(compare_tol) && meets_criteria_3(compare_tol));
180  }
181 
182  bool NiggliRep::is_niggli_type2(double compare_tol) const {
183  return (is_niggli(compare_tol) && meets_criteria_4(compare_tol));
184  }
185 
186  bool NiggliRep::is_niggli(double compare_tol) const {
187  bool totally_niggli = false;
188 
189  if(meets_criteria_1(compare_tol) &&
190  meets_criteria_2(compare_tol) &&
191  (meets_criteria_3(compare_tol) != meets_criteria_4(compare_tol)) &&
192  meets_criteria_5(compare_tol) &&
193  meets_criteria_6(compare_tol) &&
194  meets_criteria_7(compare_tol) &&
195  meets_criteria_8(compare_tol)) {
196  totally_niggli = true;
197  }
198 
199  return totally_niggli;
200  }
201 
202  void NiggliRep::debug_criteria(double compare_tol) const {
203  std::cout << meets_criteria_1(compare_tol) <<
204  meets_criteria_2(compare_tol) <<
205  meets_criteria_3(compare_tol) <<
206  meets_criteria_4(compare_tol) <<
207  meets_criteria_5(compare_tol) <<
208  meets_criteria_6(compare_tol) <<
209  meets_criteria_7(compare_tol) <<
210  meets_criteria_8(compare_tol) << std::endl;
211 
212  std::cout << A() << " " << B() << " " << C() << " "
213  << ksi() << " " << eta() << " " << zeta() << std::endl;
214 
215  return;
216  }
217 
218  bool is_niggli(const Eigen::Matrix3d &test_lat_mat, double compare_tol) {
219  NiggliRep niggtest(test_lat_mat);
220 
221  //niggtest.debug_criteria(compare_tol);
222  return niggtest.is_niggli(compare_tol);
223  }
224 
225  bool is_niggli(const Lattice &test_lat, double compare_tol) {
226  return is_niggli(test_lat.lat_column_mat(), compare_tol);
227  }
228 
240  Lattice niggli(const Lattice &in_lat, double compare_tol, bool keep_handedness) {
241 
242  Lattice target_lat(in_lat);
243 
244  if(!keep_handedness) {
245  target_lat.make_right_handed();
246  }
247  const Lattice reduced_in = target_lat.get_reduced_cell();
248 
249  bool first_niggli = true;
250  Eigen::Matrix3d best_lat_mat = Eigen::Matrix3d::Zero();
251 
252  //Like the point group, but brute forcing for every possible transformation matrix ever with determinant 1 and elements -1, 0 or 1
253  const std::vector<Eigen::Matrix3i> &candidate_trans_mats = positive_unimodular_matrices();
254 
255  for(auto it = candidate_trans_mats.begin(); it != candidate_trans_mats.end(); ++it) {
256  Eigen::Matrix3d candidate_lat_mat = reduced_in.lat_column_mat() * it->cast<double>();
257 
258  if(is_niggli(candidate_lat_mat, compare_tol)) {
259  if(first_niggli || standard_orientation_compare(best_lat_mat, candidate_lat_mat, compare_tol)) {
260  best_lat_mat = candidate_lat_mat;
261  first_niggli = false;
262  }
263  }
264  }
265  return Lattice(best_lat_mat);
266  }
267 
276  Lattice canonical_equivalent_lattice(const Lattice &in_lat, const SymGroup &point_grp, double compare_tol) {
277 
278  //Ensure you at least get *something* back that's niggli AND right handed
279  Lattice most_canonical = niggli(in_lat, compare_tol, false);
280  Eigen::Matrix3d most_canonical_lat_mat = most_canonical.lat_column_mat();
281 
282  Eigen::Matrix3d ref_lat_mat = most_canonical.lat_column_mat();
283 
284  for(auto it = point_grp.begin(); it != point_grp.end(); ++it) {
285 
286  Eigen::Matrix3d transformed_lat_mat = it->matrix() * ref_lat_mat;
287  // Eigen::Matrix3d transformed_lat_mat = it->matrix() * in_lat.lat_column_mat();
288 
289  Lattice transformed_lat(transformed_lat_mat);
290  Eigen::Matrix3d candidate_lat_mat = niggli(transformed_lat, compare_tol).lat_column_mat();
291 
292  //Skip operations that change the handedness of the lattice
293  if(candidate_lat_mat.determinant() < 0.0) {
294  continue;
295  }
296 
297  if(is_niggli(candidate_lat_mat, compare_tol) && standard_orientation_compare(most_canonical_lat_mat, candidate_lat_mat, compare_tol)) {
298  most_canonical_lat_mat = candidate_lat_mat;
299  }
300  }
301 
302  return Lattice(most_canonical_lat_mat);
303  }
304 
319  Eigen::VectorXd spatial_unroll(const Eigen::Matrix3d &lat_mat, double compare_tol) {
320  //We want to give a preference to lattices with more positive values than negative ones
321  //Count how many non-negative entries there are
322  int non_negatives = 0;
323  for(int i = 0; i < 3; i++) {
324  for(int j = 0; j < 3; j++) {
325  if(float_sgn(lat_mat(i, j), compare_tol) != -1) {
326  non_negatives++;
327  }
328  }
329  }
330 
331 
332  Eigen::VectorXd lat_spatial_descriptor(16);
333 
334  lat_spatial_descriptor <<
335  //Favor positive values
336  non_negatives,
337 
338  //Favor large diagonal values
339  lat_mat(0, 0),
340  lat_mat(1, 1),
341  lat_mat(2, 2),
342 
343  //Favor small off diagonal values
344  -std::abs(lat_mat(2, 1)),
345  -std::abs(lat_mat(2, 0)),
346  -std::abs(lat_mat(1, 0)),
347  -std::abs(lat_mat(1, 2)),
348  -std::abs(lat_mat(0, 2)),
349  -std::abs(lat_mat(0, 1)),
350 
351  //Favor upper triangular
352  float_sgn(lat_mat(2, 1), compare_tol),
353  float_sgn(lat_mat(2, 0), compare_tol),
354  float_sgn(lat_mat(1, 0), compare_tol),
355  float_sgn(lat_mat(1, 2), compare_tol),
356  float_sgn(lat_mat(0, 2), compare_tol),
357  float_sgn(lat_mat(0, 1), compare_tol);
358 
359  return lat_spatial_descriptor;
360  }
361 
370  bool standard_orientation_spatial_compare(const Eigen::Matrix3d &low_score_lat_mat, const Eigen::Matrix3d &high_score_lat_mat, double compare_tol) {
371  Eigen::VectorXd low_score_lat_unroll = spatial_unroll(low_score_lat_mat, compare_tol);
372  Eigen::VectorXd high_score_lat_unroll = spatial_unroll(high_score_lat_mat, compare_tol);
373 
374  return float_lexicographical_compare(low_score_lat_unroll, high_score_lat_unroll, compare_tol);
375  }
376 
391  bool standard_orientation_compare(const Eigen::Matrix3d &low_score_lat_mat, const Eigen::Matrix3d &high_score_lat_mat, double compare_tol) {
392  bool low_score_is_symmetric = Eigen::is_symmetric(low_score_lat_mat, compare_tol);
393  bool low_score_is_bisymmetric = Eigen::is_bisymmetric(low_score_lat_mat, compare_tol);
394 
395  bool high_score_is_symmetric = Eigen::is_symmetric(high_score_lat_mat, compare_tol);
396  bool high_score_is_bisymmetric = Eigen::is_bisymmetric(high_score_lat_mat, compare_tol);
397 
398  //The high_score is less symmetric than the low score, therefore high<low
399  if((low_score_is_bisymmetric && !high_score_is_bisymmetric) || (low_score_is_symmetric && !high_score_is_symmetric)) {
400  return false;
401  }
402 
403  //The high_score is more symmetric than the low_score, therefore high>low automatically (matrix symmetry trumps spatial orientation)
404  else if((!low_score_is_bisymmetric && high_score_is_bisymmetric) || (!low_score_is_symmetric && high_score_is_symmetric)) {
405  return true;
406  }
407 
408  //If you made it here then the high_score and the low_score have the same symmetry level, so we check for the spatial orientation
409  else {
410  return standard_orientation_spatial_compare(low_score_lat_mat, high_score_lat_mat, compare_tol);
411  }
412  }
413 
414 }
bool meets_criteria_2(double compare_tol) const
B<=C OR (B==C, |eta| <= |zeta|)
Definition: Niggli.cc:59
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:41
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 compare(const T &A, const T &B, double tol)
Floating point comparison with tol, return A < B.
Definition: CASM_math.hh:89
double A() const
Square of lattice length a.
Definition: Niggli.cc:15
bool float_lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, double tol)
Floating point lexicographical comparison with tol.
Definition: CASM_math.hh:111
bool meets_criteria_7(double compare_tol) const
|zeta|<=A OR (zeta==A, eta<=2*ksi) OR (zeta==-A, eta==0)
Definition: Niggli.cc:139
bool is_niggli_type2(double compare_tol) const
True if all conditions except 3 are true.
Definition: Niggli.cc:182
bool is_bisymmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
Definition: CASM_math.hh:532
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:391
Lattice get_reduced_cell() const
Find the lattice vectors which give most compact unit cell Compactness is measured by how close lat_c...
Definition: Lattice.cc:442
Main CASM namespace.
Definition: complete.cpp:8
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:104
double C() const
Square of lattice length c.
Definition: Niggli.cc:23
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:33
bool is_niggli_type1(double compare_tol) const
True if all conditions except 4 are true.
Definition: Niggli.cc:178
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:319
bool meets_criteria_4(double compare_tol) const
For type II: ksi<=0 && eta<=0 && zeta<=0 (all angles >= 90)
Definition: Niggli.cc:86
double zeta() const
2ab*cos(gamma)
Definition: Niggli.cc:35
bool is_niggli(const Eigen::Matrix3d &test_lat_mat, double compare_tol)
Check whether the given lattice (represented as a matrix) is in niggli TYPE ?? reduced form (does not...
Definition: Niggli.cc:218
const std::vector< Eigen::Matrix3i > & positive_unimodular_matrices()
double B() const
Square of lattice length b.
Definition: Niggli.cc:19
int float_sgn(T val, double compare_tol=TOL)
Definition: CASM_math.hh:130
Lattice canonical_equivalent_lattice(const Lattice &in_lat, const SymGroup &point_grp, double compare_tol)
Find the niggli, most standard oriented version of the given orbit (defined by the given SymGroup) of...
Definition: Niggli.cc:276
NiggliRep(const Lattice &init_lat)
Definition: Niggli.cc:11
const Eigen::Matrix3d & metrical_matrix() const
Definition: Niggli.cc:39
Eigen::VectorXd VectorXd
double ksi() const
2bc*cos(alpha)
Definition: Niggli.cc:27
bool meets_criteria_1(double compare_tol) const
A<=B OR (A==B, |ksi| <= |eta|)
Definition: Niggli.cc:43
bool meets_criteria_8(double compare_tol) const
Definition: Niggli.cc:160
const Eigen::Matrix3d m_metrical_matrix
Transpose of initialization lattice dotted with itself.
Definition: Niggli.hh:112
T const * end() const
Definition: Array.hh:197
bool is_symmetric(const Eigen::MatrixBase< Derived > &test_mat, double test_tol=CASM::TOL)
Definition: CASM_math.hh:504
Eigen::Matrix3d Matrix3d
bool is_niggli(double compare_tol) const
True if all conditions are true, and either 4 OR 3 is false.
Definition: Niggli.cc:186
void debug_criteria(double compare_tol) const
Definition: Niggli.cc:202
Lattice & make_right_handed()
Flip c vector if it's on the wrong side of a-b plane – return (*this)
Definition: Lattice.cc:681
bool meets_criteria_3(double compare_tol) const
For type I: ksi>0 && eta>0 && zeta>0 (all angles < 90)
Definition: Niggli.cc:75
T const * begin() const
Definition: Array.hh:185
double eta() const
2ac*cos(beta)
Definition: Niggli.cc:31
bool meets_criteria_6(double compare_tol) const
|eta|<=A OR (eta==A, zeta<=2*ksi) OR (eta==-A, zeta==0)
Definition: Niggli.cc:118
Lattice niggli(const Lattice &in_lat, double compare_tol, bool keep_handedness=false)
Convert the given lattice into it's niggli reduced form, with the most standard orientation possilbe...
Definition: Niggli.cc:240
bool meets_criteria_5(double compare_tol) const
|ksi|<=B OR (ksi==B, zeta<=2*eta) OR (ksi==-B, zeta==0)
Definition: Niggli.cc:97
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)