CASM  1.1.0
A Clusters Approach to Statistical Mechanics
StrucMapping.cc
Go to the documentation of this file.
2 
14 #include "casm/external/Eigen/src/Core/Map.h"
15 #include "casm/external/Eigen/src/Core/PermutationMatrix.h"
16 #include "casm/external/Eigen/src/Core/util/Constants.h"
17 #include "casm/external/Eigen/src/Core/util/Meta.h"
18 
19 namespace CASM {
20 namespace xtal {
21 
22 namespace Local {
23 
24 static bool lex_lt(Eigen::Matrix<long, 3, 3> const &A,
25  Eigen::Matrix<long, 3, 3> const &B) {
26  return std::lexicographical_compare(A.data(), A.data() + 9, B.data(),
27  B.data() + 9);
28 }
29 } // namespace Local
30 //*******************************************************************************************
31 
32 namespace StrucMapping {
33 double atomic_cost_child(const MappingNode &mapped_result, Index Nsites) {
34  Nsites = max(Nsites, Index(1));
35  // mean square displacement distance in deformed coordinate system
36  double atomic_vol =
37  mapped_result.lattice_node.parent.superlattice().volume() /
38  double(Nsites) / mapped_result.lattice_node.stretch.determinant();
39  return pow(3. * abs(atomic_vol) / (4. * M_PI), -2. / 3.) *
40  (mapped_result.lattice_node.stretch.inverse() *
41  mapped_result.atom_displacement)
42  .squaredNorm() /
43  double(Nsites);
44 }
45 //*******************************************************************************************
46 
47 double atomic_cost_parent(const MappingNode &mapped_result, Index Nsites) {
48  Nsites = max(Nsites, Index(1));
49  // mean square displacement distance in deformed coordinate system
50  double atomic_vol =
51  mapped_result.lattice_node.parent.superlattice().volume() /
52  double(Nsites);
53  return pow(3. * abs(atomic_vol) / (4. * M_PI), -2. / 3.) *
54  (mapped_result.atom_displacement).squaredNorm() / double(Nsites);
55 }
56 
57 //*******************************************************************************************
58 
59 double atomic_cost(const MappingNode &mapped_result, Index Nsites) {
60  // mean square displacement distance in deformed coordinate system
61  return (atomic_cost_child(mapped_result, Nsites) +
62  atomic_cost_parent(mapped_result, Nsites)) /
63  2.;
64 }
65 
66 //*******************************************************************************************
67 double atomic_cost(
68  const MappingNode &basic_mapping_node, SymOpVector &factor_group,
69  const std::vector<Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic,
70  Index>> &permutation_group,
71  Index Nsites) {
72  const auto disp_matrix = basic_mapping_node.atom_displacement;
73  Eigen::MatrixXd symmetry_preserving_displacement =
74  Eigen::MatrixXd::Zero(disp_matrix.rows(), disp_matrix.cols());
75  for (int i = 0; i < factor_group.size(); ++i) {
76  auto transformed_disp = factor_group[i].matrix * disp_matrix;
77  Eigen::MatrixXd transformed_and_permuted_disp =
78  transformed_disp * permutation_group[i];
79  symmetry_preserving_displacement += transformed_and_permuted_disp;
80  }
81  symmetry_preserving_displacement =
82  symmetry_preserving_displacement / factor_group.size();
83  auto new_report = basic_mapping_node;
84  new_report.atom_displacement = disp_matrix - symmetry_preserving_displacement;
85  return atomic_cost_parent(new_report, Nsites);
86 };
87 
88 } // namespace StrucMapping
89 
90 //*******************************************************************************************
91 namespace Local {
92 // Local helper function for StrucMapper::k_best_maps_better_than
93 template <typename OutputIterator>
94 static bool initial_atomic_maps(SimpleStructure child_struc,
95  MappingNode const &seed,
96  StrucMapCalculatorInterface const &calculator,
97  double max_cost,
98  bool const &symmetrize_atomic_cost,
99  OutputIterator it) {
100  // derotate first
101  child_struc.rotate_coords(seed.isometry());
102 
103  // Then undeform by inverse of right stretch
104  child_struc.deform_coords(seed.stretch());
105 
106  // We want to get rid of translations.
107  // define translation such that:
108  // IDEAL = RELAXED + translation
109  // and use it when calculating cost matrix
110 
111  for (Eigen::Vector3d const &translation :
112  calculator.translations(seed, child_struc)) {
113  MappingNode node = seed;
114  node.atomic_node.translation = translation;
115  if (!calculator.populate_cost_mat(node, child_struc)) {
116  // Indicates that structure is incompatible with supercell, regardless of
117  // translation so return false
118  return false;
119  }
120 
121  // The mapping routine is called here
122  node.calc();
123 
124  // if assignment is smaller than child_struc.basis().size(), then
125  // child_struc is incompattible with supercell (assignment.size()==0 if the
126  // hungarian routine detects an incompatibility, regardless of translation)
127  if (!node.is_viable) {
128  return false;
129  }
130  // Now we are filling up displacements
131  calculator.finalize(node, child_struc, symmetrize_atomic_cost);
132 
133  if (node.cost < max_cost) {
134  *it = node;
135  }
136  }
137 
138  return true;
139 }
140 
141 //*******************************************************************************************
142 // Local helper function for StrucMapper::k_best_maps_better_than
143 template <typename OutputIterator>
144 static void partition_node(MappingNode const &_node,
145  StrucMapCalculatorInterface const &_calculator,
146  SimpleStructure child_struc,
147  bool const &symmetrize_atomic_cost,
148  OutputIterator it) {
149  // derotate first
150  child_struc.rotate_coords(_node.isometry());
151 
152  // Then undeform by inverse of right stretch
153  child_struc.deform_coords(_node.stretch());
154 
155  Index cN = _node.lattice_node.child.size() *
156  _calculator.struc_info(child_struc).size();
157 
158  // We will increment from i=0 to number of rows in cost matrix of '_node'
159  // For each (i,j) assignment of '_node', we will will spawn a new node, where
160  // the (i,j) assignment is forced OFF and all (k,l) assignments with k<i will
161  // be forced ON. This involves
162  // (1) setting the cost of element (i,j) to infinity (forccing it off)
163  // (2) striking the first 'k' rows from the staring cost matrix, and all
164  // columns corresponding to
165  // their atomic assignments.
166  // (3) recording the (i,j) pairs that have been turned on
167  // (4) resolving the reduced assignment problem with the new cost matrix
168  Index old_j, new_j, deleted_j;
169  Index n = _node.atomic_node.assignment.size();
170  MappingNode t1(_node), t2(_node);
171 
172  _node.is_partitioned = true;
173  t1.is_partitioned = false;
174  t2.is_partitioned = false;
175 
176  MappingNode *p1 = &t1;
177  MappingNode *p2 = &t2;
178  for (Index m = n; 1 < m && (p1->is_viable || p2->is_viable); --m) {
179  AssignmentNode &n1(p1->atomic_node);
180  AssignmentNode &n2(p2->atomic_node);
181  // clear assignment and cost_mat
182  n2.assignment.clear();
183  n2.irow.clear();
184  n2.icol.clear();
185  n2.cost_mat.resize(m - 1, m - 1);
186  n2.forced_on = n1.forced_on;
187 
188  // We are forcing on the first site assignment in t1: i.e., [0,deleted_j]
189  // this involves striking row 0 and col deleted_j from t2's cost_mat
190  deleted_j = n1.assignment[0];
191 
192  // [0,deleted_j] have local context only. We store the forced assignment in
193  // forced_on using the original indexing from n1
194  n2.forced_on.emplace(n1.irow[0], n1.icol[deleted_j]);
195 
196  // Strike row 0 and col deleted_j to form new AssignmentNode for t2
197  // (i,j) indexes the starting cost_mat, (i-1, new_j) indexes the resulting
198  // cost_mat
199  n2.irow = std::vector<Index>(++n1.irow.begin(), n1.irow.end());
200  // We will also store an updated assignment vector in t2, which will be
201  // used to construct next node of partition
202  n2.assignment =
203  std::vector<Index>(++n1.assignment.begin(), n1.assignment.end());
204  for (old_j = 0, new_j = 0; old_j < m; ++old_j, ++new_j) {
205  if (old_j == deleted_j) {
206  --new_j;
207  continue;
208  }
209  n2.icol.push_back(n1.icol[old_j]);
210 
211  // We will also store an updated assignment vector in t2, which will be
212  // used to construct next node of partition
213  if (n2.assignment[new_j] > deleted_j) n2.assignment[new_j]--;
214 
215  // Fill col new_j of t2's cost mat
216  for (Index i = 1; i < m; ++i)
217  n2.cost_mat(i - 1, new_j) = n1.cost_mat(i, old_j);
218  }
219  // t2 properly initialized; we can now force OFF [0,deleted_j] in t1, and
220  // add it to node list
221  n1.cost_mat(0, deleted_j) = StrucMapping::big_inf();
222  // IMPORTANT: If n1.icol[deleted_j]=cN, it is a virtual vacancy.
223  // If we exclude a single virtual vacancy from occupying this parent site,
224  // the marginal cost of assigning a different virtual vacancy to the same
225  // site is zero, and the mapping is equivalent. So, we need to exclude ALL
226  // virtual vacancies from occupying this parent site:
227  if (n1.icol[deleted_j] >= cN) {
228  for (old_j = n1.icol.size() - 1; old_j >= 0 && n1.icol[old_j] >= cN;
229  --old_j) {
230  n1.cost_mat(0, old_j) = StrucMapping::big_inf();
231  }
232  }
233  n1.assignment.clear();
234  p1->is_viable = true;
235  p1->calc();
236  if (p1->is_viable) {
237  // even if p1 is unviable, p2 may still be viable, so we continue
238  _calculator.finalize(*p1, child_struc, symmetrize_atomic_cost);
239  it = *p1;
240  }
241  std::swap(p1, p2);
242  }
243 }
244 } // namespace Local
245 
246 //*******************************************************************************************
247 
248 LatticeNode::LatticeNode(Lattice const &parent_prim, Lattice const &parent_scel,
249  Lattice const &child_prim, Lattice const &child_scel,
250  Index child_N_atom,
251  double _cost /*=StrucMapping::big_inf()*/)
252  : parent(parent_prim, parent_scel),
253  // Transform child_prim lattice to its idealized state using same
254  // F.inverse as below, but inline:
255  child(Lattice((parent_scel.lat_column_mat() *
256  child_scel.inv_lat_column_mat()) *
257  child_prim.lat_column_mat(),
258  parent_prim.tol()),
259  parent_scel),
260  cost(_cost) {
261  // F is from ideal parent to child ( child_scel = F * parent_scel )
262  Eigen::Matrix3d F =
263  child_scel.lat_column_mat() * parent_scel.inv_lat_column_mat();
264 
265  // stretch is from (de-rotated, strained) child to ideal parent
266  // child_scel = F * parent_scel = isometry.transpose() * stretch.inverse() *
267  // parent_scel OR: parent_scel = stretch * isometry * child_scel
268  stretch = strain::right_stretch_tensor(F).inverse();
269 
270  // isometry is from child to strained parent
271  isometry = (F * stretch).transpose();
272 
275 }
276 
277 //*******************************************************************************************
278 
279 LatticeNode::LatticeNode(LatticeMap const &_lat_map, Lattice const &parent_prim,
280  Lattice const &child_prim)
281  : // stretch is from (de-rotated, strained) child to ideal parent
282  stretch(polar_decomposition(_lat_map.deformation_gradient()).inverse()),
283  // isometry is from child to strained parent
284  isometry((_lat_map.deformation_gradient() * stretch).transpose()),
285  parent(parent_prim, Lattice(_lat_map.parent_matrix(), parent_prim.tol())),
286  child(Lattice(_lat_map.deformation_gradient().inverse() *
287  child_prim.lat_column_mat(),
288  parent_prim.tol()),
289  Lattice(_lat_map.parent_matrix(), parent_prim.tol())),
290  cost(_lat_map.strain_cost()) {}
291 
292 //*******************************************************************************************
293 
295 bool less(LatticeNode const &A, LatticeNode const &B, double cost_tol) {
296  if (!almost_equal(A.cost, B.cost, cost_tol)) return A.cost < B.cost;
305  return false;
306 }
307 
308 //*******************************************************************************************
309 
310 bool identical(LatticeNode const &A, LatticeNode const &B, double cost_tol) {
311  if (!almost_equal(A.cost, B.cost, cost_tol)) return false;
314  return false;
317  return false;
318  return true;
319 }
320 
321 //*******************************************************************************************
322 
324  if (empty() != B.empty()) return empty();
325  if (time_reversal != B.time_reversal) return B.time_reversal;
326  if (!almost_equal(translation, B.translation, 1e-6))
328  return false;
329 }
330 
331 //*******************************************************************************************
332 
333 bool identical(AssignmentNode const &A, AssignmentNode const &B) {
334  if (A.empty() != B.empty()) return false;
335  if (A.time_reversal != B.time_reversal) return false;
336  if (!almost_equal(A.translation, B.translation, 1e-6)) return false;
337  return true;
338 }
339 //*******************************************************************************************
340 
344  0.5);
345  result.is_viable = false;
346  result.is_valid = false;
347  result.is_partitioned = false;
348  return result;
349 }
350 
351 //*******************************************************************************************
352 
354  if (is_viable) {
355  if (atomic_node.irow.empty())
356  atomic_node.irow = sequence<Index>(0, atomic_node.cost_mat.rows() - 1);
357  if (atomic_node.icol.empty())
358  atomic_node.icol = sequence<Index>(0, atomic_node.cost_mat.cols() - 1);
359  double tcost =
361  cost_tol()); // + atomic_node.cost_offset;
362  if (StrucMapping::is_inf(tcost)) {
363  is_viable = false;
365  }
366  } else
368 }
369 
370 //*******************************************************************************************
371 
372 bool MappingNode::operator<(MappingNode const &B) const {
373  double _cost_tol = max(this->cost_tol(), B.cost_tol());
374  if (!almost_equal(this->cost, B.cost, _cost_tol)) {
375  return this->cost < B.cost;
376  }
377  if (!almost_equal(this->lattice_node.cost, B.lattice_node.cost, _cost_tol)) {
378  return this->lattice_node.cost < B.lattice_node.cost;
379  }
380  if (this->atomic_node.empty() != B.atomic_node.empty()) {
381  return this->atomic_node.empty();
382  }
383  if (!identical(this->lattice_node, B.lattice_node, _cost_tol)) {
384  return less(this->lattice_node, B.lattice_node, _cost_tol);
385  }
386  if (!identical(this->atomic_node, B.atomic_node)) {
387  return this->atomic_node < B.atomic_node;
388  }
390  return std::lexicographical_compare(
391  this->atom_permutation.begin(), this->atom_permutation.end(),
392  B.atom_permutation.begin(), B.atom_permutation.end());
393 
394  return false;
395 }
396 
397 //*******************************************************************************************
398 
400  StrucMapCalculatorInterface const &calculator,
401  double _lattice_weight /*= 0.5*/, double _max_volume_change /*= 0.5*/,
402  int _options /*= robust*/, // this should actually be a bitwise-OR of
403  // StrucMapper::Options
404  double _cost_tol /*= TOL*/, double _min_va_frac /*= 0.*/,
405  double _max_va_frac /*= 1.*/)
406  : m_calc_ptr(calculator.clone()),
407  m_max_volume_change(_max_volume_change),
408  m_options(_options),
409  m_cost_tol(max(1e-10, _cost_tol)),
410  m_xtal_tol(TOL),
411  m_lattice_transformation_range(1),
412  m_filtered(false),
413  m_symmetrize_lattice_cost(false),
414  m_symmetrize_atomic_cost(false) {
415  set_min_va_frac(_min_va_frac);
416  set_max_va_frac(_max_va_frac);
417 
418  // squeeze lattice_weight into (0,1] if necessary
419  set_lattice_weight(_lattice_weight);
420 
421  // Make sure that max_volume_change is positive
422  m_max_volume_change = max(3 * xtal_tol(), _max_volume_change);
423 }
424 
425 //*******************************************************************************************
426 
428  return calculator().struc_info(sstruc).size();
429 }
430 
431 //*******************************************************************************************
432 
433 std::pair<Index, Index> StrucMapper::_vol_range(
434  const SimpleStructure &child_struc) const {
435  Index min_vol(0), max_vol(0);
436  // mapped_result.clear();
437  SimpleStructure::Info const &c_info(calculator().struc_info(child_struc));
438 
439  // Using _n_species() instead of parent().n_mol() here.
440  // For mapping molecules, may need to push this entire routine into the
441  // StrucMapCalculator?
442  double N_sites_p = double(_n_species(parent()));
443 
444  double N_sites_c = double(_n_species(child_struc));
445 
446  if (calculator().fixed_species().size() > 0) {
447  std::string tcompon = calculator().fixed_species().begin()->first;
448  int ncompon(0);
449  for (std::string const &sp : c_info.names) {
450  if (sp == tcompon) ncompon++;
451  }
452  min_vol = ncompon / int(calculator().fixed_species().begin()->second);
453  max_vol = min_vol;
454  } else {
455  // Try to narrow the range of supercell volumes -- the best bounds are
456  // obtained from the convex hull of the end-members, but we need to wait for
457  // improvements to convex hull routines
458 
459  int max_n_va = calculator().max_n_va();
460 
461  // Absolute largest Va fraction
462  double max_va_frac_limit = double(max_n_va) / N_sites_p;
463 
464  double t_min_va_frac = min(min_va_frac(), max_va_frac_limit);
465  double t_max_va_frac = min(max_va_frac(), max_va_frac_limit);
466 
467  // Use vacancy range to narrow volume range.
468 
469  // the number of sites implied in order to realize va_frac >= min_va_frac
470  // (TOL is not super important):
471  double min_n_sites = floor(N_sites_c / (1. - t_min_va_frac) - TOL);
472 
473  // the number of sites implied to realize va_frac <= max_va_frac (TOL is
474  // not super important):
475  double max_n_sites = ceil(N_sites_c / (1. - t_max_va_frac) + TOL);
476 
477  // min_vol assumes min number vacancies -- best case scenario (buffer by
478  // half an atom)
479  min_vol = ceil((min_n_sites - 0.5) / N_sites_p);
480 
481  // max_vol assumes min number vacancies -- best case scenario (buffer by
482  // half an atom)
483  max_vol = floor((max_n_sites + 0.5) / N_sites_p);
484 
485  // If volume is not fully determined, use volume ratio of child to parent to
486  // narrow search
487  if (min_vol != max_vol) {
488  // Nvol is approximate integer volume-- assume that actual integer is
489  // within m_max_volume_change*100% of this volume, and use it to tighten
490  // our bounds
491  double Nvol = (std::abs(child_struc.lat_column_mat.determinant() /
492  parent().lat_column_mat.determinant()));
493  min_vol = min(
494  max_vol, max<Index>(round((1.0 - m_max_volume_change) * double(Nvol)),
495  min_vol));
496  max_vol = max(
497  min_vol, min<Index>(round((1.0 + m_max_volume_change) * double(Nvol)),
498  max_vol));
499  }
500  }
501 
502  // make sure volume range is above zero
504  Index smallest_possible_vol = ceil((N_sites_c - 0.5) / N_sites_p);
505  min_vol = max<Index>(min_vol, smallest_possible_vol);
506  max_vol = max<Index>(max_vol, smallest_possible_vol);
507  }
508 
509  return std::pair<Index, Index>(min_vol, max_vol);
510 }
511 
512 //*******************************************************************************************
513 
515  return calculator().parent();
516 }
517 
518 //*******************************************************************************************
519 
520 std::set<MappingNode> StrucMapper::_seed_from_vol_range(
521  SimpleStructure const &child_struc, Index k, Index min_vol, Index max_vol,
522  double max_lattice_cost, double min_lattice_cost,
523  SymOpVector const &child_factor_group) const {
524  if (!valid_index(min_vol) || !valid_index(min_vol) || max_vol < min_vol) {
525  auto vol_range = _vol_range(child_struc);
526  min_vol = vol_range.first;
527  max_vol = vol_range.second;
528  }
529 
530  // There is nothing to enumerate, don't even bother.
531  if (max_vol < 1) {
532  return {};
533  }
534  // Ensure that you don't try to enumerate size zero supercells
535  min_vol = std::max(min_vol, Index{1});
536 
537  Lattice child_lat(child_struc.lat_column_mat, xtal_tol());
538  std::set<MappingNode> mapping_seed;
539  for (Index i_vol = min_vol; i_vol <= max_vol; i_vol++) {
540  std::vector<Lattice> lat_vec;
541  for (Lattice const &lat : _lattices_of_vol(i_vol)) {
542  if (m_filtered && !_filter_lat(lat, child_lat)) {
543  continue;
544  }
545  lat_vec.push_back(lat);
546  }
547 
548  std::set<MappingNode> t_seed = _seed_k_best_from_super_lats(
549  child_struc, lat_vec, {Lattice(child_struc.lat_column_mat, xtal_tol())},
550  k, max_lattice_cost, max(min_lattice_cost, cost_tol()),
551  child_factor_group);
552 
553  mapping_seed.insert(std::make_move_iterator(t_seed.begin()),
554  std::make_move_iterator(t_seed.end()));
555  }
556  return mapping_seed;
557 }
558 
559 //*******************************************************************************************
560 /*
561  * Given a structure and a mapping node, find a perfect supercell of the prim
562  * that is equivalent to structure's lattice and then try to map the structure's
563  * basis onto that supercell
564  *
565  * Returns false if no mapping is possible, or if the lattice is not ideal
566  *
567  * What this does NOT do:
568  * -Check if the imported Structure is the same as one in a smaller Supercell
569  *
570  */
571 //*******************************************************************************************
572 
573 std::set<MappingNode> StrucMapper::map_ideal_struc(
574  const SimpleStructure &child_struc, Index k,
575  double max_cost /*=StrucMapping::big_inf()*/, double min_cost /*=-TOL*/,
576  bool keep_invalid /*=false*/) const {
577  // xtal::is_superlattice isn't very smart right now, and will return
578  // false if the two lattices differ by a rigid rotation
579  // In the future this may not be the case, so we will assume that child_struc
580  // may be rigidly rotated relative to prim
581  Eigen::Matrix3d trans_mat;
582 
583  // c_lat must be an ideal supercell of the parent lattice, but it need not be
584  // canonical We will account for the difference in orientation between c_lat
585  // and the canonical supercell, which must be related by a point group
586  // operation
587  Lattice c_lat(child_struc.lat_column_mat, xtal_tol());
588 
589  bool c_lat_is_supercell_of_parent;
590  std::tie(c_lat_is_supercell_of_parent, trans_mat) = xtal::is_superlattice(
591  c_lat, Lattice(parent().lat_column_mat), xtal_tol());
592  if (!c_lat_is_supercell_of_parent) {
593  return {};
594  }
595 
596  // We know child_struc.lattice() is a supercell of the prim, now we have to
597  // reorient 'child_struc' by a point-group operation of the parent to match
598  // canonical lattice vectors This may not be a rotation in the child
599  // structure's point group
600  Lattice derot_c_lat(canonical::equivalent(
601  Lattice(parent().lat_column_mat * trans_mat, xtal_tol()),
602  calculator().point_group()));
603 
604  // We now find a transformation matrix of c_lat so that, after transformation,
605  // it is related to derot_c_lat by rigid rotation only. Following line finds R
606  // and T such that derot_c_lat = R*c_lat*T
608  derot_c_lat, c_lat, calculator().point_group().begin(),
609  calculator().point_group().end(), xtal_tol());
610  LatticeNode lattice_node(
611  Lattice(parent().lat_column_mat, xtal_tol()), derot_c_lat, c_lat,
612  Lattice(child_struc.lat_column_mat * res.second.cast<double>(),
613  xtal_tol()),
614  _n_species(child_struc), 0. /*strain_cost is zero in ideal case*/);
615 
617  child_struc, lattice_node, k, max_cost, min_cost, keep_invalid);
618 }
619 
620 //*******************************************************************************************
621 
622 std::set<MappingNode> StrucMapper::map_deformed_struc(
623  const SimpleStructure &child_struc, Index k /*=1*/,
624  double max_cost /*=StrucMapping::big_inf()*/, double min_cost /*=-TOL*/,
625  bool keep_invalid /*=false*/, SymOpVector const &child_factor_group) const {
626  auto vols = _vol_range(child_struc);
628  child_struc, vols.first, vols.second, k, max_cost, min_cost, keep_invalid,
629  child_factor_group);
630 }
631 
632 //*******************************************************************************************
633 
635  const SimpleStructure &child_struc, Index min_vol, Index max_vol,
636  Index k /*=1*/, double max_cost /*=StrucMapping::big_inf()*/,
637  double min_cost /*=-TOL*/, bool keep_invalid /*=false*/,
638  SymOpVector const &child_factor_group) const {
639  int seed_k = 10 + 5 * k;
640  std::set<MappingNode> mapping_seed = _seed_from_vol_range(
641  child_struc, seed_k, min_vol, max_vol,
642  max_cost / (this->lattice_weight()),
643  max(min_cost / (this->lattice_weight()), cost_tol()), child_factor_group);
644 
645  bool no_partition = (!(robust & options())) && k <= 1;
646  k_best_maps_better_than(child_struc, mapping_seed, k, max_cost, min_cost,
647  keep_invalid, false, no_partition);
648 
649  return mapping_seed;
650 }
651 
652 //*******************************************************************************************
653 
655  const SimpleStructure &child_struc, const Lattice &imposed_lat,
656  Index k /*=1*/, double max_cost /*=StrucMapping::big_inf()*/,
657  double min_cost /*=-TOL*/, bool keep_invalid /*=false*/,
658  SymOpVector const &child_factor_group) const {
659  std::set<MappingNode> mapping_seed = _seed_k_best_from_super_lats(
660  child_struc, {imposed_lat},
661  {Lattice(child_struc.lat_column_mat, xtal_tol())}, k,
662  max_cost / (this->lattice_weight()),
663  max(min_cost / (this->lattice_weight()), cost_tol()), child_factor_group);
664 
665  bool no_partition = !(robust & options()) && k <= 1;
666  k_best_maps_better_than(child_struc, mapping_seed, k, max_cost, min_cost,
667  keep_invalid, false, no_partition);
668  return mapping_seed;
669 }
670 
671 //*******************************************************************************************
672 
674  const SimpleStructure &child_struc, const LatticeNode &imposed_node,
675  Index k /*=1*/, double max_cost /*=StrucMapping::big_inf()*/,
676  double min_cost /*=-TOL*/, bool keep_invalid /*=false*/) const {
677  std::set<MappingNode> mapping_seed;
678  mapping_seed.emplace(imposed_node, m_lattice_weight);
679  bool no_partition = !(robust & options()) && k <= 1;
680  k_best_maps_better_than(child_struc, mapping_seed, k, max_cost, min_cost,
681  keep_invalid, false, no_partition);
682  return mapping_seed;
683 }
684 
685 //*******************************************************************************************
686 
687 std::vector<Lattice> StrucMapper::_lattices_of_vol(Index prim_vol) const {
688  if (!valid_index(prim_vol)) {
689  throw std::runtime_error("Cannot enumerate lattice of volume " +
690  std::to_string(prim_vol) +
691  ", which is out of bounds.\n");
692  }
693 
694  // If you specified that you wanted certain lattices, return those, otherwise
695  // do the usual enumeration
696  if (this->lattices_constrained()) {
697  // This may very well return an empty vector, saving painful time
698  // enumerating things
699  return m_allowed_superlat_map[prim_vol];
700  }
701 
702  // If we already have candidate lattices for the given volume, return those
703  auto it = m_superlat_map.find(prim_vol);
704  if (it != m_superlat_map.end()) return it->second;
705 
706  // We don't have any lattices for the provided volume, enumerate them all!!!
707  std::vector<Lattice> &lat_vec = m_superlat_map[prim_vol];
708 
709  auto pg = calculator().point_group();
710  SuperlatticeEnumerator enumerator(
711  pg.begin(), pg.end(), Lattice(parent().lat_column_mat, xtal_tol()),
712  ScelEnumProps(prim_vol, prim_vol + 1));
713 
714  for (auto it = enumerator.begin(); it != enumerator.end(); ++it) {
715  Lattice canon_lat = *it;
716  if (canonical::check(canon_lat, calculator().point_group())) {
717  canon_lat = canonical::equivalent(canon_lat, calculator().point_group());
718  }
719  lat_vec.push_back(canon_lat);
720  }
721 
722  return lat_vec;
723 }
724 
725 //*******************************************************************************************
726 /*
727  * Starting from a child structure and a queue of (possibly unfinalized) mapping
728  * nodes, visit each node in the queue, test its fitness, and add any of its
729  * viable derivative nodes to the queue
730  *
731  * The queue is ordered by the lower bound of a node's mapping cost. This lower
732  * cost is less than or equal to its finalized mapping cost or the finalized
733  * mapping costs of any of its derivatives node. As such, the queue only needs
734  * to be iterated over once.
735  *
736  * At the end of the process, the queue is pruned to have at most 'k' valid
737  * nodes that are the best mappings of child_struc onto the parent structure.
738  *
739  * Returns the number of mappings found.
740  *
741  * @param max_cost: maximum allowed mapping cost. Will ignore any mapping
742  * greater than this value
743  * @param min_cost: minimum mapping cost. For each node with a cost less than
744  * min_cost, 'k' will be increased by 1
745  * @param keep_invalid: Invalid nodes are retained in queue if true
746  * @param erase_tail: Tail end of queue is retained if true
747  *
748  * What this does NOT do:
749  * -Check if the imported Structure is the same as one in a smaller Supercell
750  *
751  */
752 //*******************************************************************************************
753 // This is where the magic happens, part 1
755  SimpleStructure const &child_struc, std::set<MappingNode> &queue, Index k,
756  double max_cost /*=StrucMapping::big_inf()*/, double min_cost /*=-TOL*/,
757  bool keep_invalid /*=false*/, bool keep_tail /*= false*/,
758  bool no_partition /*= false*/) const {
759  int nfound = 0;
760  // Track pairs of supercell volumes that are chemically incompatible
761  std::set<std::pair<Index, Index>> vol_mismatch;
762 
763  if (k == 0) {
764  // If k==0, then we only keep values less than min_cost
765  // However, max_cost controls search loop, so we set max_cost to min_cost
766  max_cost = min_cost;
767  }
768 
769  auto it = queue.begin();
770  while (it != queue.end()) {
771  bool erase = true;
772  auto current = it;
773 
774  if (it->cost <= (max_cost + this->cost_tol())) {
775  // If supercell volumes have already been determined incompatible, we do
776  // nothing; current node is deleted
777  if (!vol_mismatch.count(current->vol_pair())) {
778  // Consider two exlusive cases (and base case, in which current node
779  // isn't even viable)
780  if (current->atomic_node.empty()) {
781  // Case 1: Current node only describes a lattice mapping with no basis
782  // mapping
783  // Perform basis mapping, insert result into queue, and erase
784  // current (new node must have cost greather than the current
785  // node, so will
786  // appear later in the queue)
787  if (!Local::initial_atomic_maps(child_struc, *current, calculator(),
788  max_cost, symmetrize_atomic_cost(),
789  std::inserter(queue, current))) {
790  // If no basis maps are viable, it indicates volume mismatch; add to
791  // vol_mismatch
792  vol_mismatch.insert(current->vol_pair());
793  }
794  } else if (current->is_viable) {
795  // Case 2: Current node is a complete mapping and is viable
796  // Either it is a valid node, and thus part of the solution
797  // set, or it is invalid, but we must add its partition to the
798  // queue because it may have a suboptimal derivative mapping
799  // that is valid and is part of the solution set
800 
801  if (current->is_valid && current->cost > min_cost && nfound < k) {
802  // current node is part of solution set
803  ++nfound;
804 
805  // Need to account for case where mapping k+1 is as good as mapping
806  // k A few ways to make this happen, but we will do it by increasing
807  // k when nfound==k, and shrink max_cost to be current cost
808  if (nfound == k) {
809  ++k;
810  max_cost = current->cost; // + tol();
811  }
812  }
813 
814  // Regardless of validity, we partition current node and add the
815  // derivative nodes to queue (but only if we haven't reached stopping
816  // condition) Skip partitioning if node is already partitioned, or if
817  // caller has asked not to
818  if (nfound < k || current->cost <= min_cost) {
819  if (!(no_partition || current->is_partitioned)) {
820  Local::partition_node(*current, calculator(), child_struc,
822  std::inserter(queue, current));
823  }
824 
825  // Keep current node if it is in the solution set if we have been
826  // asked to keep invalids
827  if (current->is_valid || keep_invalid) erase = false;
828  }
829  }
830  // Never keep unviable nodes or incomplete nodes
831  }
832  } else {
833  erase = !keep_tail;
834  }
835  // Safe to increment here:
836  // 1) No continue/break statements
837  // 2) Nothing has been deleted yet
838  // 3) Everything that needs to be inserted has been inserted
839  ++it;
840 
841  // Erase current if no longer needed
842  if (erase) queue.erase(current);
843  }
844 
845  return nfound;
846 }
847 
848 //****************************************************************************************************************
849 
850 // Find all Lattice mappings better than min_cost and at most the k best
851 // mappings in range [min_cost,max_cost]
853  SimpleStructure const &child_struc,
854  std::vector<Lattice> const &_parent_scels,
855  std::vector<Lattice> const &_child_scels, Index k,
856  double max_lattice_cost /*=StrucMapping::small_inf()*/,
857  double min_lattice_cost /*=1e-6*/,
858  SymOpVector const &child_factor_group) const {
859  Lattice p_prim_lat(parent().lat_column_mat, xtal_tol());
860  Lattice c_prim_lat(child_struc.lat_column_mat, xtal_tol());
861  std::set<MappingNode> result;
862 
863  if (k == 0 || !valid_index(k)) {
864  max_lattice_cost = min_lattice_cost;
865  }
866  for (Lattice const &c_lat : _child_scels) {
867  auto pg_indices = invariant_subgroup_indices(c_lat, child_factor_group);
868  SymOpVector c_lat_factor_group;
869  c_lat_factor_group.reserve(pg_indices.size());
870  for (Index i : pg_indices) {
871  c_lat_factor_group.push_back(child_factor_group[i]);
872  }
873 
874  for (Lattice const &p_lat : _parent_scels) {
875  int n_child_atom = round(std::abs(volume(c_lat) / volume(c_prim_lat))) *
876  _n_species(child_struc);
877  LatticeMap lattice_map(
878  p_lat, c_lat, n_child_atom, this->lattice_transformation_range(),
879  calculator().point_group(), c_lat_factor_group, m_strain_gram_mat,
880  max_lattice_cost, symmetrize_lattice_cost());
881 
882  // lattice_map is initialized to first mapping better than
883  // 'max_lattice_cost', if such a mapping exists We will continue checking
884  // possibilities until all such mappings are exhausted
885  while (lattice_map.strain_cost() < (max_lattice_cost + cost_tol())) {
886  // Mappings worse than min_lattice_cost count against k
887  if (lattice_map.strain_cost() > min_lattice_cost) {
888  if (k == 0 || !valid_index(k)) {
889  // If k is already depleted, we will still add this mapping, but
890  // adjust max_lattice_cost to avoid adding anything worse
891  max_lattice_cost = max(min_lattice_cost, lattice_map.strain_cost());
892  } else {
893  // If k is not depleted, decrement k
894  k--;
895  }
896  }
897 
898  result.emplace(LatticeNode(lattice_map, p_prim_lat, c_prim_lat),
899  this->lattice_weight());
900 
901  lattice_map.next_mapping_better_than(max_lattice_cost);
902  }
903  }
904  }
905  return result;
906 }
907 
908 } // namespace xtal
909 } // namespace CASM
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:110
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
static Lattice cubic(double tol=TOL)
Construct simple cubic primitive cell of unit volume.
Definition: Lattice.cc:108
double strain_cost() const
Definition: LatticeMap.hh:108
LatticeMap const & next_mapping_better_than(double max_cost) const
Definition: LatticeMap.cc:306
Data structure for holding supercell enumeration properties.
Representation of a crystal of molecular and/or atomic occupants, and any additional properties....
void deform_coords(Eigen::Ref< const Eigen::Matrix3d > const &_F)
Apply homogeneous deformation gradient tensor _F to lat_column_mat, mol_info, and atom_info.
void rotate_coords(Eigen::Ref< const Eigen::Matrix3d > const &_R)
Apply homogeneous rotation matrix _R to lat_column_mat, mol_info, and atom_info.
static double isotropic_strain_cost(Eigen::Matrix3d const &_deformation_gradient)
Definition: LatticeMap.cc:65
virtual void finalize(MappingNode &_node, SimpleStructure const &child_struc, bool const &symmetrize_atomic_cost=false) const =0
Calculates final mapping score and sets _node.is_valid.
Index max_n_va() const
Return maximum possible number of vacancies in underlying primitive structure.
virtual std::vector< Eigen::Vector3d > translations(MappingNode const &_node, SimpleStructure const &child_struc) const =0
construct list of prospective mapping translations
virtual bool populate_cost_mat(MappingNode &_node, SimpleStructure const &child_struc) const =0
SimpleStructure::Info const & struc_info(SimpleStructure const &_struc) const
SymOpVector const & point_group() const
List of point group operations that map parent onto itself (neglecting internal translation)
StrucMapping::FixedSpecies const & fixed_species() const
bool lattices_constrained() const
returns true if the search of parent superlattices is constrained to a pre-specified list
double max_va_frac() const
Returns the maximum fraction of sites allowed to be vacant in the mapping relation Vacancy fraction i...
std::set< MappingNode > _seed_from_vol_range(SimpleStructure const &child_struc, Index k, Index min_vol, Index max_vol, double max_strain_cost, double min_strain_cost, SymOpVector const &child_factor_group={SymOp::identity()}) const
construct partial mapping nodes (with uninitialized atomic_node) based on current settings considers ...
bool symmetrize_atomic_cost() const
void set_max_va_frac(double _max_va)
Sets the maximum fraction of sites allowed to be vacant in the mapping relation Vacancy fraction is u...
std::set< MappingNode > map_ideal_struc(const SimpleStructure &child_struc, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false) const
k-best mappings of ideal child structure onto parent structure Assumes that child_struc and parent_st...
Index lattice_transformation_range() const
Max element considered for integer unimodular matrix transformations (which define orientation relati...
std::set< MappingNode > map_deformed_struc_impose_lattice(const SimpleStructure &child_struc, const Lattice &imposed_lat, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false, SymOpVector const &child_factor_group={SymOp::identity()}) const
k-best mappings of arbitrary child structure onto parent structure imposes simplifying assumption tha...
bool symmetrize_lattice_cost() const
bool _filter_lat(Lattice const &_parent_lat, Lattice const &_child_lat) const
StrucMapCalculatorInterface const & calculator() const
std::pair< Index, Index > _vol_range(const SimpleStructure &child_struc) const
int options() const
returns bit flag of selected options for this StrucMapper
LatMapType m_allowed_superlat_map
LatMapType m_superlat_map
Maps the supercell volume to a vector of Lattices with that volume.
std::set< MappingNode > _seed_k_best_from_super_lats(SimpleStructure const &child_struc, std::vector< Lattice > const &_parent_scels, std::vector< Lattice > const &_child_scels, Index k, double max_strain_cost, double min_strain_cost, SymOpVector const &child_factor_group={SymOp::identity()}) const
generate list of partial mapping seeds (i.e., LatticeNode only) from a list of supercells of the pare...
std::set< MappingNode > map_deformed_struc_impose_lattice_vols(const SimpleStructure &child_struc, Index min_vol, Index max_vol, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false, SymOpVector const &child_factor_group={SymOp::identity()}) const
k-best mappings of arbitrary child structure onto parent structure imposes simplifying assumption tha...
double xtal_tol() const
Tolerance for initializing lattices. For now it is initialized to CASM::TOL.
Index _n_species(SimpleStructure const &sstruc) const
returns number of species in a SimpleStructure given the current calculator settings....
void set_lattice_weight(double _lw)
std::set< MappingNode > map_deformed_struc(const SimpleStructure &child_struc, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false, SymOpVector const &child_factor_group={SymOp::identity()}) const
k-best mappings of arbitrary child structure onto parent structure, without simplifying assumptions I...
double min_va_frac() const
Returns the minimum fraction of sites allowed to be vacant in the mapping relation Vacancy fraction i...
void set_min_va_frac(double _min_va)
Sets the minimum fraction of sites allowed to be vacant in the mapping relation Vacancy fraction is u...
StrucMapper(StrucMapCalculatorInterface const &_calculator, double _lattice_weight=0.5, double _max_volume_change=0.5, int _options=0, double _cost_tol=TOL, double _min_va_frac=0., double _max_va_frac=1.)
Construct and initialize a StrucMapper.
SimpleStructure const & parent() const
returns reference to parent structure
std::set< MappingNode > map_deformed_struc_impose_lattice_node(const SimpleStructure &child_struc, const LatticeNode &imposed_node, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false) const
k-best mappings of arbitrary child structure onto parent structure imposes simplifying assumption tha...
double lattice_weight() const
double cost_tol() const
Tolerance for determining if two mapping-cost values are identical.
Index k_best_maps_better_than(SimpleStructure const &child_struc, std::set< MappingNode > &queue, Index k=1, double max_cost=StrucMapping::big_inf(), double min_cost=-TOL, bool keep_invalid=false, bool keep_tail=false, bool no_partiton=false) const
low-level function. Takes a queue of mappings and use it to seed a search for k-best total mappings....
std::vector< Lattice > _lattices_of_vol(Index prim_vol) const
Eigen::MatrixXd m_strain_gram_mat
A fake container of supercell matrices.
const Eigen::Matrix3l & transformation_matrix_to_super() const
Definition: Superlattice.hh:24
const Lattice & superlattice() const
Definition: Superlattice.hh:18
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: io_traits.hh:172
const_iterator end() const
A const iterator to the past-the-last volume.
const_iterator begin() const
A const iterator to the beginning volume, specify here how the iterator should jump through the enume...
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
Eigen::Matrix3d polar_decomposition(Eigen::Matrix3d const &mat)
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.
SharedPrimFormatter< jsonParser > factor_group()
Eigen::Matrix3d right_stretch_tensor(const Eigen::Ref< const Eigen::Matrix3d > &deformation_tensor)
Calculates and returns the value of U where F = R*U.
Definition: Strain.cc:104
static bool lex_lt(Eigen::Matrix< long, 3, 3 > const &A, Eigen::Matrix< long, 3, 3 > const &B)
Definition: StrucMapping.cc:24
static void partition_node(MappingNode const &_node, StrucMapCalculatorInterface const &_calculator, SimpleStructure child_struc, bool const &symmetrize_atomic_cost, OutputIterator it)
static bool initial_atomic_maps(SimpleStructure child_struc, MappingNode const &seed, StrucMapCalculatorInterface const &calculator, double max_cost, bool const &symmetrize_atomic_cost, OutputIterator it)
Definition: StrucMapping.cc:94
double big_inf()
Very large value used to denote invalid or impossible mapping.
Definition: StrucMapping.hh:35
double atomic_cost_child(const MappingNode &mapped_result, Index Nsites)
Calculate the basis cost function of a MappingNode as the normalized mean-square displacement of its ...
Definition: StrucMapping.cc:33
double atomic_cost_parent(const MappingNode &mapped_result, Index Nsites)
Calculate the basis cost function of a MappingNode as the normalized mean-square displacement of its ...
Definition: StrucMapping.cc:47
bool is_inf(double _val)
Definition: StrucMapping.hh:41
double atomic_cost(const MappingNode &mapped_config, Index Nsites)
Calculate the basis cost function of a MappingNode as the average of atomic_cost_child and atomic_cos...
Definition: StrucMapping.cc:59
Lattice equivalent(Lattice const &in_lat, SymOpVector const &point_grp, double compare_tol)
bool check(const Lattice &lat)
bool identical(LatticeNode const &A, LatticeNode const &B, double cost_tol)
returns true if cost values and parent/child supercell transformations are same for A and B
std::vector< Index > invariant_subgroup_indices(const Lattice &lat, SymOpVector const &super_grp)
Construct indices of the subgroup that leaves a lattice unchanged.
bool less(LatticeNode const &A, LatticeNode const &B, double cost_tol)
Compare two LatticeMap objects, based on their mapping cost first, followed by PrimGrid transformatio...
std::pair< OpIterator, Eigen::Matrix3d > is_equivalent_superlattice(const Object &scel, const Object &unit, OpIterator begin, OpIterator end, double tol)
Definition: SymTools.hh:110
std::vector< SymOp > SymOpVector
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
Eigen::Matrix3d Matrix3d
void swap(ConfigDoF &A, ConfigDoF &B)
Definition: ConfigDoF.cc:260
double hungarian_method(const Eigen::MatrixXd &cost_matrix, std::vector< Index > &optimal_assignments, const double _tol)
const double TOL
Definition: definitions.hh:30
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.
T min(const T &A, const T &B)
Definition: CASM_math.hh:88
bool valid_index(Index i)
Definition: definitions.cc:5
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
T max(const T &A, const T &B)
Definition: CASM_math.hh:95
std::unique_ptr< T > clone(const T &obj)
Structure to encode the solution of a constrained atomic assignmnet problem This describes the permut...
std::vector< Index > irow
'real' indices of rows in the reduced 'cost_mat' When a site assignment {i,j} is added to forced_on,...
Eigen::Vector3d translation
Mapping translation from child to parent Defined such that translation=parent_coord....
bool empty() const
True if cost matrix and assignment vector are uninitialized.
bool operator<(AssignmentNode const &other) const
Compares time_reversal and translation for time_reversal, false is less than true for translation,...
std::vector< Index > assignment
Solution of the assignment problem for the reduced 'cost_mat' An assignment {k,l} in the reduced prob...
Eigen::MatrixXd cost_mat
Cost matrix for an assignment problem, which may be a reduced assignment problem if forced_on....
std::vector< Index > icol
'real' indices of columns in the reduced 'cost_mat' When a site assignment {i,j} is added to forced_o...
std::set< std::pair< Index, Index > > forced_on
parent->child site assignments that have been forced on at this node for element 'el' such that force...
bool time_reversal
time_reversal relationship between child and parent
Class describing the lattice-mapping portion of a particular mapping A general map for child_struc on...
Definition: StrucMapping.hh:82
LatticeNode(Lattice const &parent_prim, Lattice const &parent_scel, Lattice const &child_prim, Lattice const &child_scel, Index child_N_atom, double _cost=StrucMapping::big_inf())
Construct with ideal parent_scel and deformed child_scel, which are related by a deformation tensor.
double cost
strain_cost of the LatticeNode
Superlattice parent
PrimGrid for supercell of parent structure The parent structure defines the ideal strain state,...
Superlattice child
PrimGrid for supercell of child structure The child lattice is recorded in its idealized state (de-ro...
Eigen::Matrix3d stretch
stretch tensor that takes child superlattice from its de-rotated, deformed state to its ideal,...
Definition: StrucMapping.hh:87
Eigen::Matrix3d isometry
cartesian rotation/reflection/rotoreflection that rotates the child superlattice to its de-rotated,...
Definition: StrucMapping.hh:91
void calc()
non-const calc method solves the assignment problem via hungarian_method sets is_viable -> false if n...
bool operator<(MappingNode const &other) const
static MappingNode invalid()
Static constructor to build an invalid MappingNode, can be used as return value when no valid mapping...
AssignmentNode atomic_node
std::vector< Index > atom_permutation
Eigen::Matrix3d const & stretch() const
convenience method to access MappingNode::lattice_node.stretch
bool is_partitioned
true if node has been partitioned into sub-nodes for generalized k-best assignment problem – default ...
Eigen::MatrixXd atom_displacement
3xN matrix of displacements for all sites in parent supercell (Va are included, but set to Zero)
bool is_viable
true if assignment problem is not yet known to be insoluable – default true
double cost
total, finalized cost, populated by a StrucMapCalculator. Not guaranteed to be a linear function of l...
double cost_tol() const
bool is_valid
true if assignment has been checked for physical validity and passes – default false
Eigen::Matrix3d const & isometry() const
convenience method to access MappingNode::lattice_node.isometry
Struct to encode all information about the crystal basis Info may describe the basis in a atomic cont...
std::vector< std::string > names
names[i] is name of species that occupies sites 'i'
Index size() const
Number of sites is defined as names.size()