CASM  1.1.0
A Clusters Approach to Statistical Mechanics
Lattice.cc
Go to the documentation of this file.
2 
5 #include "casm/global/eigen.hh"
7 #include "casm/misc/algorithm.hh"
8 
9 namespace CASM {
10 namespace xtal {
11 
12 Lattice::Lattice(Eigen::Ref<const Eigen::Vector3d> const &vec1,
13  Eigen::Ref<const Eigen::Vector3d> const &vec2,
14  Eigen::Ref<const Eigen::Vector3d> const &vec3, double xtal_tol,
15  bool force)
16  : m_tol(xtal_tol) {
17  m_lat_mat << vec1, vec2, vec3;
18  if (!force && m_lat_mat.determinant() < 0) {
19  // this->make_right_handed();
20  // throw std::runtime_error("Attempted to construct a left-handed lattice.
21  // Try again or override if you know what you're doing");
22  }
23  m_inv_lat_mat = m_lat_mat.inverse();
24 }
25 
26 std::vector<Eigen::Matrix3d> _skew_transforms() {
27  // Creates 12 skew matrices + 12 "double skew" matrices
28  std::vector<Eigen::Matrix3d> skew(24, Eigen::Matrix3d::Identity());
29  Index i, j, k;
30  Index l = 0;
31  for (i = 0; i < 3; i++) {
32  for (j = 0; j < 3; j++) {
33  if (i != j) {
34  // Checks to make sure we're not at a diagonal
35  skew[l](i, j) = 1;
36  skew[l + 12](i, j) = -1;
37  ++l;
38  }
39  }
40  }
41 
42  // the 12 "double skew" matrices corresponding to body diagonal substitution
43  for (i = 0; i < 3; i++) {
44  // column
45 
46  j = (i + 1) % 3;
47  k = (i + 2) % 3;
48 
49  skew[l](j, i) = 1;
50  skew[l](k, i) = 1;
51 
52  skew[l + 12](j, i) = -1;
53  skew[l + 12](k, i) = -1;
54 
55  ++l;
56 
57  skew[l](j, i) = 1;
58  skew[l](k, i) = -1;
59 
60  skew[l + 12](j, i) = -1;
61  skew[l + 12](k, i) = 1;
62 
63  ++l;
64  }
65  return skew;
66 }
67 
68 std::vector<Eigen::Matrix3d> const &Lattice::skew_transforms() {
69  static std::vector<Eigen::Matrix3d> result = _skew_transforms();
70  return result;
71 }
72 
76 Lattice::Lattice(const Eigen::Ref<const Eigen::Matrix3d> &lat_mat,
77  double xtal_tol, bool force)
78  : m_lat_mat(lat_mat), m_inv_lat_mat(lat_mat.inverse()), m_tol(xtal_tol) {
79  if (!force && m_lat_mat.determinant() < 0) {
80  // this->make_right_handed();
81  // throw std::runtime_error("Attempted to construct a left-handed lattice.
82  // Try again or override if you know what you're doing");
83  }
84 }
85 
86 Lattice Lattice::fcc(double tol) {
87  Eigen::Matrix3d latmat;
88  // clang-format off
89  latmat << 0, 1, 1,
90  1, 0, 1,
91  1, 1, 0;
92  // clang-format on
93  latmat /= pow(latmat.determinant(), 1.0 / 3.0);
94  return Lattice(latmat, tol);
95 }
96 
97 Lattice Lattice::bcc(double tol) {
98  Eigen::Matrix3d latmat;
99  // clang-format off
100  latmat << -1, 1, 1,
101  1, -1, 1,
102  1, 1, -1;
103  // clang-format on
104  latmat /= pow(latmat.determinant(), 1.0 / 3.0);
105  return Lattice(latmat, tol);
106 }
107 
108 Lattice Lattice::cubic(double tol) {
110 }
111 
113  Eigen::Matrix3d latmat;
114  // clang-format off
115  latmat << 1, -1.0 / sqrt(3.0),
116  0, 0, 2.0 / sqrt(3.0),
117  0, 0, 0, sqrt(3.0);
118  //clang-format off
119 
120  return Lattice(latmat.transpose(), tol);
121  }
122 
123  Lattice Lattice::scaled_lattice(double scale) const {
124  return Lattice(scale * lat_column_mat(), tol());
125  }
126 
127  double Lattice::length(Index i) const {
128  // Calculates Lengths
129  return m_lat_mat.col(i).norm();
130  }
131 
132  double Lattice::angle(Index i) const {
133  return (180.0 / M_PI) * CASM::angle(m_lat_mat.col((i + 1) % 3), m_lat_mat.col((i + 2) % 3));
134  }
135 
136  void Lattice::read(std::istream &stream) {
137  double scale;
138  stream >> scale;
139  stream >> m_lat_mat;
140  m_lat_mat *= scale;
141  m_lat_mat.transposeInPlace();
142  m_inv_lat_mat = m_lat_mat.inverse();
143 
144  return;
145  }
146 
147  void Lattice::print(std::ostream &stream, int _prec) const {
148  int tprec = stream.precision();
149  std::ios::fmtflags tflags = stream.flags();
150  stream.precision(_prec);
151  stream.flags(std::ios::showpoint | std::ios::fixed | std::ios::right);
152  Eigen::IOFormat format(_prec);
153 
154  stream << 1.0 << '\n';
155  stream << m_lat_mat.transpose().format(format);
156 
157  stream.precision(tprec);
158  stream.flags(tflags);
159 
160  return;
161  }
162 
164  return Lattice(2 * M_PI * inv_lat_column_mat().transpose(), tol());
165  }
166 
167  double Lattice::boxiness() const {
168  return 1 / (this->inv_lat_column_mat().colwise().norm().sum());
169  }
170 
171  std::vector<int> Lattice::calc_kpoints(std::vector<int> prim_kpoints, Lattice prim_lat) {
172  std::vector<int> super_kpoints = prim_kpoints;
173  // Lattice prim_recip_lat = (lattice.primitive->reciprocal());
174  Lattice prim_recip_lat = (prim_lat.reciprocal());
175  Lattice recip_lat = (*this).reciprocal();
176  double prim_density = (prim_kpoints[0] * prim_kpoints[1] * prim_kpoints[2]) / (prim_recip_lat.volume());
177  double super_density = 0;
178 
179  std::vector<double> prim_vec_lengths;
180 
181  for(int i = 0; i < 3; i++) {
182  prim_vec_lengths.push_back(prim_recip_lat.length(i));
183  }
184 
185  double shortest = *std::min_element(prim_vec_lengths.begin(), prim_vec_lengths.end());
186  int short_ind = find_index(prim_vec_lengths, shortest);
187 
188  double scale = (prim_kpoints[short_ind] / shortest);
189 
190  for(int i = 0; i < 3; i++) {
191  super_kpoints[i] = int(ceil(scale * recip_lat.length(i)));
192  }
193 
194  super_density = (super_kpoints[0] * super_kpoints[1] * super_kpoints[2]) / (recip_lat.volume());
195 
196  while(super_density < prim_density) {
197  prim_kpoints[short_ind]++;
198 
199  scale = (prim_kpoints[short_ind] / shortest);
200 
201  for(int i = 0; i < 3; i++) {
202  super_kpoints[i] = int(ceil(scale * recip_lat.length(i)));
203  }
204 
205  super_density = (super_kpoints[0] * super_kpoints[1] * super_kpoints[2]) / (recip_lat.volume());
206  }
207 
208  return super_kpoints;
209  }
210 
230  std::vector<Eigen::Matrix3d> const &skew(skew_transforms());
231  std::vector<Eigen::Matrix3d> const &ntrans(NiggliRep::cell_invariant_transforms());
232 
233  Eigen::Matrix3d trans;
234  trans.setIdentity();
235 
236  Eigen::Matrix3d t_metric, reduced_metric, t_imetric, reduced_imetric;
237 
238  reduced_metric = lat_column_mat().transpose() * lat_column_mat();
239  reduced_imetric = reduced_metric.inverse();
240 
241  bool minimized = false;
242  while(!minimized) {
243  minimized = true;
244  for(Index i = 0; i < skew.size(); ++i) {
245  // Lattice vectors times their transpose give length and angle info
246  t_metric = skew[i].transpose() * reduced_metric * skew[i];
247  t_imetric = skew[(i + 12) % 12].transpose() * reduced_imetric * skew[(i + 12) % 12];
248 
249  if(almost_equal(t_metric.trace(), reduced_metric.trace(), tol() * tol())) {
250  if(almost_equal(t_imetric.trace(), reduced_imetric.trace(), tol() * tol())) {
251  if(niggli_index(lat_column_mat() * trans * skew[i], tol()) > niggli_index(lat_column_mat() * trans, tol())) {
252  /*if(fabs(t_metric(0,1)) < fabs(reduced_metric(0,1))
253  || fabs(t_metric(0,2)) < fabs(reduced_metric(0,2))
254  || fabs(t_metric(1,2)) < fabs(reduced_metric(1,2))) {*/
255 
256  reduced_metric = t_metric;
257  reduced_imetric = t_imetric;
258  trans *= skew[i];
259  minimized = false;
260  }
261  }
262  else if(t_imetric.trace() < reduced_imetric.trace()) {
263  reduced_metric = t_metric;
264  reduced_imetric = t_imetric;
265  trans *= skew[i];
266  minimized = false;
267  }
268  }
269  else if(t_metric.trace() < reduced_metric.trace()) {
270  reduced_metric = t_metric;
271  reduced_imetric = t_imetric;
272  trans *= skew[i];
273  minimized = false;
274  }
275  }
276 
277  Index b_index = niggli_index(lat_column_mat() * trans, tol());
278  for(Index i = 0; i < ntrans.size(); ++i) {
279  Index tb_index = niggli_index(lat_column_mat() * trans * ntrans[i], tol());
280  if(tb_index > b_index) {
281  b_index = tb_index;
282  reduced_metric = ntrans[i].transpose() * reduced_metric * ntrans[i];
283  reduced_imetric = reduced_metric.inverse();
284  trans *= ntrans[i];
285  minimized = false;
286  }
287  }
288  }
289 
290  return Lattice(lat_column_mat() * trans, tol());
291  }
292 
303  Eigen::Matrix3d reduced_lat = lat_column_mat();
304  bool right_handed = (reduced_lat.determinant() > 0);
305  Eigen::HouseholderQR<Eigen::Matrix3d> qr(reduced_lat);
306  Eigen::Matrix3d Q = qr.householderQ();
307  Eigen::Matrix3d R = Q.inverse() * reduced_lat;
308  Eigen::Matrix3d ortho = Q;
309  ortho.col(0) = ortho.col(0) * R(0, 0);
310  ortho.col(1) = ortho.col(1) * R(1, 1);
311  ortho.col(2) = ortho.col(2) * R(2, 2);
312  Index k = 1;
313  while(k < 3) {
314  for(int j = k - 1; j >= 0; j--) {
315  double mu = reduced_lat.col(k).dot(ortho.col(j)) / ortho.col(j).squaredNorm();
316  if(fabs(mu) > 0.5000001) {
317  reduced_lat.col(k) = reduced_lat.col(k) - round(mu) * reduced_lat.col(j);
318  Eigen::HouseholderQR<Eigen::Matrix3d> qr2(reduced_lat);
319  Q = qr2.householderQ();
320  R = Q.inverse() * reduced_lat;
321  ortho = Q;
322  ortho.col(0) = ortho.col(0) * R(0, 0);
323  ortho.col(1) = ortho.col(1) * R(1, 1);
324  ortho.col(2) = ortho.col(2) * R(2, 2);
325  }
326  }
327  double mu2 = reduced_lat.col(k).dot(ortho.col(k - 1)) / ortho.col(k - 1).squaredNorm();
328  if((ortho.col(k) + mu2 * ortho.col(k - 1)).squaredNorm() > (0.75) * ortho.col(k - 1).squaredNorm()) {
329  k = k + 1;
330  }
331  else {
332  Eigen::Vector3d tmp = reduced_lat.col(k);
333  reduced_lat.col(k) = reduced_lat.col(k - 1);
334  reduced_lat.col(k - 1) = tmp;
335  Eigen::HouseholderQR<Eigen::Matrix3d> qr3(reduced_lat);
336  Q = qr3.householderQ();
337  R = Q.inverse() * reduced_lat;
338  ortho = Q;
339  ortho.col(0) = ortho.col(0) * R(0, 0);
340  ortho.col(1) = ortho.col(1) * R(1, 1);
341  ortho.col(2) = ortho.col(2) * R(2, 2);
342  if(k > 1) {
343  k = k - 1;
344  }
345  }
346  }
347  if(right_handed) {
348  return Lattice(reduced_lat).make_right_handed();
349  }
350  return Lattice(reduced_lat);
351  }
352 
353  double Lattice::max_voronoi_measure(const Eigen::Vector3d &pos, Eigen::Vector3d &lattice_trans) const {
355  double maxproj = (voronoi_table() * pos).maxCoeff(&maxv);
356 
357  lattice_trans = (2. * floor(maxproj / 2. + (0.5 - TOL / 2.)) / m_voronoi_table.row(maxv).squaredNorm()) * m_voronoi_table.row(maxv);
358 
359  return maxproj;
360  }
361 
362  int Lattice::voronoi_number(const Eigen::Vector3d &pos) const {
363 
364  int tnum = 0;
365  double tproj = 0;
366 
367  Eigen::MatrixXd const &vtable = voronoi_table();
368 
369  for(Index nv = 0; nv < vtable.rows(); nv++) {
370  tproj = vtable.row(nv) * pos;
371  if(almost_equal(tproj, 1.0)) {
372  tnum++;
373  }
374  else if(tproj > 1.0) {
375  return -1;
376  }
377  }
378 
379  return tnum;
380  }
381 
383  // There are no fewer than 12 points in the voronoi table
384  m_voronoi_table.resize(12, 3);
385 
386  m_inner_voronoi_radius = 1e20;
387 
388  Eigen::Vector3d tpoint;
389  int i;
390  int nrows = 1;
391  Lattice tlat_reduced(reduced_cell());
392 
393  // Count over all lattice vectors, face diagonals, and body diagonals
394  // originating from origin;
395  EigenCounter<Eigen::Vector3i> combo_count(Eigen::Vector3i(-1, -1, -1), Eigen::Vector3i(1, 1, 1), Eigen::Vector3i(1, 1, 1));
396 
397  // For each linear combination, check to see if it is on a face, edge, or vertex of the voronoi cell
398  for(; combo_count.valid(); ++combo_count) {
399  if(combo_count().isZero())
400  continue;
401  // A linear combination does not fall on the voronoi boundary if the angle between
402  // any two of the vectors forming that combination are acute
403  for(i = 0; i < 3; i++) {
404  if(combo_count[(i + 1) % 3] == 0 || combo_count[(i + 2) % 3] == 0)
405  continue;
406  if((180. / M_PI) * CASM::angle(combo_count[(i + 1) % 3] * tlat_reduced[(i + 1) % 3],
407  combo_count[(i + 2) % 3] * tlat_reduced[(i + 2) % 3]) +
408  TOL <
409  90.) {
410  break;
411  }
412  }
413 
414  if(i == 3) {
415  if(nrows > m_voronoi_table.rows())
416  m_voronoi_table.conservativeResize(nrows, Eigen::NoChange);
417 
418  tpoint = tlat_reduced.lat_column_mat() * combo_count().cast<double>();
419 
420  double t_rad = tpoint.norm();
421  if((t_rad / 2.) < m_inner_voronoi_radius)
422  m_inner_voronoi_radius = t_rad / 2.;
423 
424  m_voronoi_table.row(nrows - 1) = (2. / (t_rad * t_rad)) * tpoint;
425  nrows++;
426  }
427  }
428  return;
429  }
430 
444  Eigen::Vector3i Lattice::enclose_sphere(double radius) const {
445 
446  // reciprocal vectors
447  Eigen::Matrix3d recip(inv_lat_column_mat().transpose());
448 
449  for(int i = 0; i < 3; i++) {
450  recip.col(i) *= radius / recip.col(i).norm();
451  }
452  // recip contains three column vectors of length 'radius' pointed along plane normals
453  auto lambda = [](double val) {
454  return ceil(val);
455  };
456  return (inv_lat_column_mat() * recip).cwiseAbs().unaryExpr(lambda).colwise().maxCoeff().cast<int>();
457  }
458 
467 
468  if(lat_column_mat().determinant() < 0) {
469  m_lat_mat = -m_lat_mat;
470  m_inv_lat_mat = m_lat_mat.inverse();
471  }
472 
473  return *this;
474  }
475 
476  Eigen::Vector3i Lattice::millers(Eigen::Vector3d plane_normal) const {
477  // Get fractional coordinates of plane_normal in recip_lattice
478  // These are h, k, l
479  // For miller indeces h, k and l plane_normal[CART]=h*a.recip+k*b.recip+l*c.recip
480  return scale_to_int(lat_column_mat().transpose() * plane_normal, tol());
481  }
482 
496  Lattice Lattice::lattice_in_plane(Eigen::Vector3i millers, int max_vol) const {
497  // John G 121030
498  // Hold new lattice vectors in these. Then at the end we make an actual Lattice out of it
499  Eigen::Matrix3d surface_cell, last_surface_cell; // Holds new lattice vectors, two of which are in the surface plane
500 
501  // Miller indeces of 100, 010 or 001 mean you don't need a new cell to expose the plane, however
502  // you may want to reorient the vectors so that the ab plane is exposed (useful for Structure::stitch)
503 
504  if(millers == Eigen::Vector3i(0, 1, 0)) {
505  return Lattice(lat_column_mat().col(2), lat_column_mat().col(0), lat_column_mat().col(1), tol());
506  }
507 
508  else if(millers == Eigen::Vector3i(1, 0, 0)) {
509  return Lattice(lat_column_mat().col(1), lat_column_mat().col(2), lat_column_mat().col(0), tol());
510  }
511 
512  else if(millers == Eigen::Vector3i(0, 0, 1)) {
513  // || millers==Eigen::Vector3i(0,1,0) || millers==Eigen::Vector3i(1,0,0))
514  return *this;
515  }
516 
517  // Miller indeces of xx0, 0xx or x0x mean one of the lattice vectors is the same as
518  // one of the primitive lattice vectors. This means we only need to get TWO
519  // points that are on the plane, which we connect to get the second lattice vector
520  else if(millers[0] == 0 || millers[1] == 0 || millers[2] == 0) {
521  int zero = 0;
522 
523  // Find out which miller index is 0
524  while(true) {
525  if(millers[zero] == 0) {
526  break;
527  }
528 
529  else {
530  zero++;
531  }
532  }
533 
534  surface_cell.col(0) = lat_column_mat().col(zero);
535 
536  Eigen::Vector3d H_miller_point, K_miller_point;
537  Eigen::Vector3d HK;
538  Eigen::Vector3d millers_dubs;
539 
540  // Turn integer millers into doubles for mathematical purposes (inverse)
541  millers_dubs = millers.cast<double>();
542 
543  Eigen::Vector3d inv_miller_dubs;
544  Eigen::Vector3i inv_miller;
545 
546  // In actualility, the inverse miller of 0 is infinity, we set it to 0 here so that we can use
547  // scale_to_int without going crazy. Since we won't be using this inverse miller index it's OK
548  inv_miller_dubs[zero] = 0;
549  inv_miller_dubs[(zero + 1) % 3] = 1.0 / millers_dubs[(zero + 1) % 3];
550  inv_miller_dubs[(zero + 2) % 3] = 1.0 / millers_dubs[(zero + 2) % 3];
551 
552  inv_miller = scale_to_int(inv_miller_dubs, TOL);
553  H_miller_point = inv_miller[(zero + 1) % 3] * lat_column_mat().col((zero + 1) % 3);
554  K_miller_point = inv_miller[(zero + 2) % 3] * lat_column_mat().col((zero + 2) % 3);
555 
556  HK = K_miller_point - H_miller_point;
557  surface_cell.col(1) = HK;
558  }
559 
560  else {
561  // Get three points that lie on the plane
562  // We'll want to find points that lie on the plane AND land of lattice points. In order to do
563  // this we need the miller inverses multiplied by a factor that makes them integers
564  Eigen::Vector3d H_miller_point, K_miller_point, L_miller_point;
565  Eigen::Vector3d inv_miller_dubs;
566  Eigen::Vector3i inv_miller;
567  // Turn integer millers into doubles for mathematical purposes (inverse)
568  Eigen::Vector3d millers_dubs;
569  millers_dubs = millers.cast<double>();
570 
571  inv_miller_dubs[0] = 1.0 / millers_dubs[0];
572  inv_miller_dubs[1] = 1.0 / millers_dubs[1];
573  inv_miller_dubs[2] = 1.0 / millers_dubs[2];
574 
575  inv_miller = scale_to_int(inv_miller_dubs, TOL);
576 
577  H_miller_point = inv_miller[0] * lat_column_mat().col(0);
578  K_miller_point = inv_miller[1] * lat_column_mat().col(1);
579  L_miller_point = inv_miller[2] * lat_column_mat().col(2);
580 
581  // Get three vectors that connect the three points on the plane to each other. Any two of the following
582  // vectors could be used for constructing the new lattice, but it's convenient to pick the two
583  // most orthogonal vectors
584  Eigen::Vector3d HK, KL, LH;
585  Eigen::Vector3d tangles;
586 
587  HK = K_miller_point - H_miller_point;
588  KL = L_miller_point - K_miller_point;
589  LH = H_miller_point - L_miller_point;
590 
591  // John G 121212
592  // The vectors that we got at this point are valid, but sometimes larger than they need to be.
593  Eigen::Matrix3d templat;
594  templat.col(0) = HK;
595  templat.col(1) = KL;
596  templat.col(2) = LH;
597 
598  // Find shortest vector
599  int s = 0;
600  for(int i = 1; i < 3; i++) {
601  if(lat_column_mat().col(i).norm() < lat_column_mat().col(s).norm()) {
602  s = i;
603  }
604  }
605 
606  // Try dividing by integers and see if they're still lattice combinations. If they are
607  // shorten and continue
608  for(int i = 0; i < 3; i++) {
609  int maxval = round(templat.col(i).norm() / lat_column_mat().col(i).norm() + 1);
610  for(int j = maxval; j > 1; j--) {
611  Eigen::Vector3d shortened = inv_lat_column_mat() * templat.col(i) / j; // Shorten and convert to fractional
612  bool combo = true;
613 
614  for(int k = 0; k < 3; k++) {
615  if(!almost_zero(std::abs(round(shortened[k]) - shortened[k]))) {
616  combo = false;
617  break;
618  }
619  }
620 
621  if(combo) {
622  templat.col(i) = lat_column_mat() * shortened;
623  break;
624  }
625  }
626  }
627 
628  HK = templat.col(0);
629  KL = templat.col(1);
630  LH = templat.col(2);
631 
632  // We select the two vectors that spawn the smallest area
633 
634  double HKKL, KLLH, LHHK;
635  HKKL = HK.cross(KL).norm();
636  KLLH = KL.cross(LH).norm();
637  LHHK = LH.cross(HK).norm();
638 
639  if(HKKL <= KLLH && HKKL <= LHHK) {
640  surface_cell.col(0) = HK;
641  surface_cell.col(1) = KL;
642  }
643 
644  else if(KLLH <= HKKL && KLLH <= LHHK) {
645  surface_cell.col(0) = KL;
646  surface_cell.col(1) = LH;
647  }
648 
649  else {
650  surface_cell.col(0) = LH;
651  surface_cell.col(1) = HK;
652  }
653  }
654  //\John G 121212
655  // We now have lattice vectors a and b. The c vector can be any vector that is a linear combination
656  // of the original primitive cell lattice vectors. Ideally the vector will be short and orthogonal
657  // to the plane.
658  Eigen::Vector3d normal;
659  Eigen::Vector3i L_combination;
660  int factor;
661 
662  normal = inv_lat_column_mat() * surface_cell.col(0).cross(surface_cell.col(1)); // 101112
663  factor = 1;
664 
665  // Divide by largest value in normal vector. We'll do something similar to when finding the miller
666  // indeces. We'll approximate the real normal vector with an integer normal vector, increasing
667  // the "resolution" of the integer vector in steps, until the desired accuary is reached.
668  if(fabs(normal[0]) >= fabs(normal[1]) && fabs(normal[0]) >= fabs(normal[2]) && normal[0] != 0) {
669  normal = normal / normal[0];
670  }
671 
672  else if(fabs(normal[1]) >= fabs(normal[2]) && fabs(normal[1]) >= fabs(normal[0]) && fabs(normal[1]) != 0) {
673  normal = normal / normal[1];
674  }
675 
676  else {
677  normal = normal / normal[2];
678  }
679 
680  Eigen::Vector3d tnormal;
681  // orthoscore represents how close the linear combination is to the plane normal. 1 is perfect, 0 is stupid.
682  double orthoscore = 1;
683  double torthoscore = 0;
684 
685  tnormal = normal;
686 
687  double new_vol = 0;
688  bool one_found = false;
689 
690  do {
691  normal = tnormal * factor;
692 
693  // Get linear combinations by rounding the normal to integers
694  L_combination[0] = int(floor(normal[0] + 0.5));
695  L_combination[1] = int(floor(normal[1] + 0.5));
696  L_combination[2] = int(floor(normal[2] + 0.5));
697 
698  // After getting the normal vector in terms of integers, make a linear combination of the initial
699  // lattice vectors to get third new lattice vector
700  surface_cell.col(2) = lat_column_mat() * L_combination.cast<double>();
701  orthoscore = fabs(cos(CASM::angle(lat_column_mat() * normal, surface_cell.col(2))));
702  // Only use new linear combination if it's more orthogonal than the previous one
703  if(orthoscore > torthoscore + TOL) {
704  torthoscore = orthoscore;
705 
706  last_surface_cell = surface_cell; // Remember currect generated cell, in case we can't find anything better later
707  one_found = true;
708 
709  new_vol = fabs(surface_cell.col(2).dot(surface_cell.col(0).cross(surface_cell.col(1))));
710  }
711 
712  factor++;
713 
714  if(factor == 100) {
715  if(!one_found) {
716  throw std::runtime_error("failed get_lattice_in_plane");
717  }
718  std::cerr << "Reached an outrageous size. Returning last generated cell" << std::endl;
719  surface_cell = last_surface_cell;
720  break;
721  }
722 
723  }
724  while(new_vol / this->volume() < max_vol && orthoscore < 1); // John G 121030
725 
726  Lattice surface_lat(surface_cell.col(0), surface_cell.col(1), surface_cell.col(2));
727  surface_lat.make_right_handed();
728 
729  return surface_lat;
730  }
731 
736  bool Lattice::operator<(const Lattice &RHS) const {
737  bool A_is_niggli = is_niggli(*this, TOL);
738  bool B_is_niggli = is_niggli(RHS, TOL);
739  if(A_is_niggli != B_is_niggli) {
740  return B_is_niggli;
741  }
742 
744  }
745 
747  bool Lattice::_eq(const Lattice &RHS) const {
748  return almost_equal(RHS.lat_column_mat(), lat_column_mat());
749  }
750 
752  if(this->volume() < 0) {
753  return false;
754  }
755  else {
756  return true;
757  }
758  }
759 
762  //*******************************************************************************************
763  //
764  // Finds "superduper" Lattice L_{sd} (represented as a matrix with lattice vectors as its columns
765  // such that L_{sd} satisfies
766  // L_{sd} = L_1 * N_1 = L_2 * N_2, (*1*)
767  // where N_1 and N_2 are integer matrices such that Eq.(*1*) is satisfied and det(N_1) and det(N_2) are minimized.
768  //
769  // It is assumed that L_1 = L * M_1 and L_2 = L * M_2 (i.e., L_1 and L_2 are superlattices of PRIM lattice L having
770  // integer transformation matrices M_1 and M_2, respectively).
771  //
772  // Algorithm proceeds by noting inv(L_2)*L_1 = N_2*inv(N_1) = inv(M_2)*inv(M_1) = A/n, where A is an integer matrix and n = det(M_2).
773  // Assuming that 'n' is small (n<10000), we can attempt to find 'n' and 'A'.
774  //
775  // Solution: N_2 = A*N_1/n, s.t. det(N_1) is minimized and N_2 is integer
776  //
777  // To minimize det(N_1), find smith normal form A = U*S*V, where U and V have det(1), S is diagonal,
778  // and all entries are integer. Then choose N_1 = inv(V)*R, where R is a diagonal integer matrix with entries
779  // R(i,i)=n/gcf(S(i,i),n)
780  // The resulting solution will have det(M_1*N_1)>=lcm(det(M_1),det(M_2))
781  //
782  //*******************************************************************************************
783  Lattice make_superduperlattice(const Lattice &lat1, const Lattice &lat2) {
784 
786  long N = 1, num, denom;
787  for(Index i = 0; i < 3; i++) {
788  for(Index j = 0; j < 3; j++) {
789  nearest_rational_number(dA(i, j), num, denom);
790  dA *= double(denom);
791  N *= denom;
792  }
793  }
794 
795  Eigen::Matrix3l U, S, V;
796  smith_normal_form(lround(dA), U, S, V);
797 
798  denom = N;
799 
800  // reuse matrix S for matrix 'R', as above
801  for(Index i = 0; i < 3; i++) {
802  S(i, i) = N / gcf(S(i, i), N);
803  }
804  // Matrix 'N_1', as above is now equal to inverse(V) * S
805 
806  Lattice tlat(lat1.lat_column_mat() * (inverse(V) * S).cast<double>());
807  return tlat.reduced_cell();
808  }
809 
810  //*******************************************************************************************
811 
812  Lattice replace_vector(const Lattice &lat, const Eigen::Vector3d &new_vector, double tol) {
813 
814  // replace a lattice vector with translation
815  Lattice new_lat{lat};
816  double min_vol = std::abs(volume(new_lat));
817 
818  for(int i = 0; i < 3; i++) {
819 
820  Lattice tmp_lat = lat;
821  tmp_lat[i] = new_vector;
822  double vol = std::abs(volume(tmp_lat));
823 
824  if(vol < min_vol && vol > tol) {
825  min_vol = vol;
826  new_lat = tmp_lat;
827  }
828  }
829 
830  return new_lat;
831  }
832 
834  // First, find unit*Matrix3i approximation of 'scel', then check if reconstructing 'unit' from this approximation
835  // results in residual vectors less than length 'tol'
836  std::pair<bool, Eigen::Matrix3d> is_superlattice(const Lattice &scel, const Lattice &unit, double tol) {
837  // check scel = unit*T, with integer T
838  std::pair<bool, Eigen::Matrix3d> result(true, lround(unit.inv_lat_column_mat() * scel.lat_column_mat()).cast<double>());
839 
840  Eigen::Matrix3d diff = unit.lat_column_mat() - scel.lat_column_mat() * result.second.inverse();
841 
842  result.first = almost_zero((diff.transpose() * diff).diagonal().eval(), 2 * tol) && !almost_zero(result.second, tol);
843 
844  return result;
845  }
846 
847 
848  Eigen::Matrix3l make_transformation_matrix_to_super(const Lattice &tiling_unit, const Lattice &superlattice, double tol) {
849 
850  Eigen::Matrix3d transformation_matrix;
851  bool is_integer_transformation;
852 
853  //TODO: convention is usually "prim" always goes first, but this is contradicte by is_superlattice. Which should change?
854  std::tie(is_integer_transformation, transformation_matrix) = is_superlattice(superlattice, tiling_unit, tol);
855 
856  if(!is_integer_transformation) {
857  throw std::runtime_error("The provided tiling unit and superlattice are not related by a non-singular integer transformation.");
858  }
859 
860  return lround(transformation_matrix);
861  }
862  } // namespace xtal
863 
864 } // namespace CASM
std::set< std::string > & s
bool valid() const
Definition: BaseCounter.hh:160
A Counter allows looping over many incrementing variables in one loop.
Definition: Counter.hh:109
bool is_right_handed() const
Check if the lattice is right handed.
Definition: Lattice.cc:751
Eigen::Matrix3d m_inv_lat_mat
Definition: Lattice.hh:217
void print(std::ostream &stream, int _prec=8) const
Definition: Lattice.cc:147
int voronoi_number(const Eigen::Vector3d &pos) const
Definition: Lattice.cc:362
Eigen::Vector3i millers(Eigen::Vector3d plane_normal) const
Definition: Lattice.cc:476
std::vector< int > calc_kpoints(std::vector< int > prim_kpoints, Lattice prim_lat)
Definition: Lattice.cc:171
double length(Index i) const
Return length of i'th lattice vector.
Definition: Lattice.cc:127
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:110
double m_inner_voronoi_radius
Definition: Lattice.hh:208
Eigen::Vector3i enclose_sphere(double radius) const
Definition: Lattice.cc:444
double max_voronoi_measure(const Eigen::Vector3d &pos, Eigen::Vector3d &lattice_trans) const
Definition: Lattice.cc:353
double tol() const
Definition: Lattice.hh:195
Lattice(Eigen::Ref< const Eigen::Vector3d > const &vec1, Eigen::Ref< const Eigen::Vector3d > const &vec2, Eigen::Ref< const Eigen::Vector3d > const &vec3, double xtal_tol=TOL, bool force=false)
Definition: Lattice.cc:12
static Lattice bcc(double tol=TOL)
Construct BCC primitive cell of unit volume.
Definition: Lattice.cc:97
Lattice lattice_in_plane(Eigen::Vector3i millers, int max_vol=0) const
Definition: Lattice.cc:496
bool _eq(const Lattice &RHS) const
Are lattice vectors identical for two lattices, within TOL.
Definition: Lattice.cc:747
double boxiness() const
Return boxiness factor directly proportional to volume/SA ratio.
Definition: Lattice.cc:167
Lattice reduced_cell2() const
Find the lattice vectors which give most compact unit cell Compactness is measured by how close lat_c...
Definition: Lattice.cc:229
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
Eigen::MatrixXd m_voronoi_table
Definition: Lattice.hh:209
Lattice scaled_lattice(double scale) const
Return scaled copy of this lattice (Note: Volume will be scaled by scale^3)
Definition: Lattice.cc:123
double angle(Index i) const
Return angle between lattice vectors (*this)[(i+1)%3] and (*this)[(i+2)%3], in degrees.
Definition: Lattice.cc:132
static std::vector< Eigen::Matrix3d > const & skew_transforms()
Definition: Lattice.cc:68
double volume() const
Return signed volume of this lattice.
Definition: Lattice.hh:88
const Eigen::Matrix3d & inv_lat_column_mat() const
Inverse of Lattice::lat_column_mat() It is the transformation matrix 'C2F', such that f = C2F * c whe...
Definition: Lattice.hh:117
bool operator<(const Lattice &RHS) const
Compare two Lattice.
Definition: Lattice.cc:736
static Lattice cubic(double tol=TOL)
Construct simple cubic primitive cell of unit volume.
Definition: Lattice.cc:108
static Lattice hexagonal(double tol=TOL)
Construct cubic primitive cell of unit volume.
Definition: Lattice.cc:112
Eigen::Matrix3d m_lat_mat
Definition: Lattice.hh:217
Eigen::MatrixXd const & voronoi_table() const
Return voronoi table, which specifies outward-pointing normals of Lattice Voronoi cell....
Definition: Lattice.hh:95
Lattice & make_right_handed()
Flip c vector if it's on the wrong side of a-b plane – return (*this)
Definition: Lattice.cc:466
Lattice reciprocal() const
Return reciprocal lattice.
Definition: Lattice.cc:163
static Lattice fcc(double tol=TOL)
Construct FCC primitive cell of unit volume.
Definition: Lattice.cc:86
void _generate_voronoi_table() const
populate voronoi information.
Definition: Lattice.cc:382
void read(std::istream &stream)
Definition: Lattice.cc:136
static std::vector< Eigen::Matrix3d > const & cell_invariant_transforms()
Definition: Niggli.cc:45
double volume(const Lattice &lat)
Returns the volume of a Lattice.
Definition: Lattice.hh:281
std::pair< bool, Eigen::Matrix3d > is_superlattice(const Lattice &scel, const Lattice &unit, double tol)
Check if scel is a superlattice of unitcell unit and some integer transformation matrix T.
Definition: Lattice.cc:836
Lattice make_superduperlattice(const Lattice &lat1, const Lattice &lat2)
returns Lattice that is smallest possible superlattice of both input Lattice
Definition: Lattice.cc:783
void smith_normal_form(const Eigen::MatrixBase< DerivedIn > &M, Eigen::MatrixBase< DerivedOut > &U, Eigen::MatrixBase< DerivedOut > &S, Eigen::MatrixBase< DerivedOut > &V)
Return the smith normal form, M == U*S*V.
Eigen::CwiseUnaryOp< decltype(Local::lround_l< typename Derived::Scalar >), const Derived > lround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXl.
double angle(const Eigen::Ref< const Eigen::Vector3d > &a, const Eigen::Ref< const Eigen::Vector3d > &b)
Get angle, in radians, between two vectors on range [0,pi].
Eigen::CwiseUnaryOp< decltype(Local::round_l< typename Derived::Scalar >), const Derived > round(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd.
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > inverse(const Eigen::MatrixBase< Derived > &M)
Return the integer inverse matrix of an invertible integer matrix.
T norm(const Tensor< T > &ttens)
Definition: Tensor.hh:932
IdentitySymRepBuilder Identity()
Index niggli_index(const Eigen::Matrix3d &test_lat_mat, double compare_tol)
Number of niggli criteria met.
Definition: Niggli.cc:267
Eigen::Matrix3l make_transformation_matrix_to_super(const Lattice &tiling_unit, const Lattice &superlattice, double tol)
Definition: Lattice.cc:848
bool is_niggli(const Eigen::Matrix3d &test_lat_mat, double compare_tol)
Definition: Niggli.cc:271
std::vector< Eigen::Matrix3d > _skew_transforms()
Definition: Lattice.cc:26
Lattice replace_vector(const Lattice &lat, const Eigen::Vector3d &new_vector, double tol)
Definition: Lattice.cc:812
Index find_index(const std::vector< Site > &basis, const Site &test_site, double tol)
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.
Eigen::MatrixXd MatrixXd
void nearest_rational_number(double val, long &numerator, long &denominator, double tol=TOL)
Definition: CASM_math.cc:165
Eigen::Matrix3d Matrix3d
const double TOL
Definition: definitions.hh:30
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:104
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
int gcf(int i1, int i2)
Find greatest common factor.
Definition: CASM_math.cc:94
Matrix< long int, 3, 3 > Matrix3l
Definition: eigen.hh:12
Eigen::Matrix< int, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > scale_to_int(const Eigen::MatrixBase< Derived > &val, double _tol=CASM::TOL)