CASM  1.1.0
A Clusters Approach to Statistical Mechanics
LatticeMap.cc
Go to the documentation of this file.
2 
3 #include <iterator>
4 
10 #include "casm/external/Eigen/src/Core/Matrix.h"
11 
12 namespace CASM {
13 namespace xtal {
15  Eigen::Ref<const Eigen::MatrixXd> const
16  &strain_gram_mat /*= Eigen::MatrixXd::Identity(9,9)*/) {
17  if (strain_gram_mat.size() == 0 || strain_gram_mat.isIdentity(1e-9)) {
18  m_sym_cost = false;
19  } else {
20  m_sym_cost = true;
21  m_gram_mat.resize(6, 6);
22  if (strain_gram_mat.rows() == 6 && strain_gram_mat.cols() == 6) {
23  std::vector<Index> map({0, 5, 4, 1, 3, 2});
24  for (Index i = 0; i < 6; ++i) {
25  for (Index j = 0; j < 6; ++j) {
26  m_gram_mat(i, j) = strain_gram_mat(map[i], map[j]);
27  if (i > 2) m_gram_mat(i, j) *= sqrt(2.);
28  if (j > 2) m_gram_mat(i, j) *= sqrt(2.);
29  }
30  }
31  }
32  if (strain_gram_mat.rows() == 9 && strain_gram_mat.cols() == 9) {
33  Index m = 0;
34  for (Index i = 0; i < 3; ++i) {
35  for (Index j = i; j < 3; ++j, ++m) {
36  Index n = 0;
37  for (Index k = 0; k < 3; ++k) {
38  for (Index l = k; l < 3; ++l, ++n) {
39  m_gram_mat(m, n) = strain_gram_mat(i * 3 + j, k * 3 + l);
40  if (m > 2) m_gram_mat(m, n) *= sqrt(2.);
41  if (n > 2) m_gram_mat(m, n) *= sqrt(2.);
42  }
43  }
44  }
45  }
46  }
47  }
48 }
49 
50 //*******************************************************************************************
51 // static function
53  const Eigen::Matrix3d &_deformation_gradient, double _vol_factor) {
54  Eigen::Matrix3d tmat =
55  polar_decomposition(_deformation_gradient / _vol_factor);
56 
57  // -> epsilon=(_deformation_gradient_deviatoric-identity)
58  return ((tmat - Eigen::Matrix3d::Identity(3, 3)).squaredNorm() +
59  (tmat.inverse() - Eigen::Matrix3d::Identity(3, 3)).squaredNorm()) /
60  6.;
61 }
62 
63 //*******************************************************************************************
64 // static function
66  const Eigen::Matrix3d &_deformation_gradient) {
67  return isotropic_strain_cost(_deformation_gradient,
68  vol_factor(_deformation_gradient));
69 }
70 
71 //*******************************************************************************************
72 
73 // strain_cost is the mean-square displacement of a point on the surface of a
74 // unit sphere when it is deformed by the volume-preserving deformation
75 // _deformation_gradient_deviatoric =
76 // _deformation_gradient/det(_deformation_gradient)^(1/3)
78  const Eigen::Matrix3d &_deformation_gradient, double _vol_factor) const {
79  if (m_sym_cost) {
80  double cost = 0;
81  m_cache = polar_decomposition(_deformation_gradient / _vol_factor);
82  m_cache_inv = m_cache.inverse() - Eigen::Matrix3d::Identity(3, 3);
84  Index m = 0;
85  for (Index i = 0; i < 3; ++i) {
86  for (Index j = i; j < 3; ++j, ++m) {
87  Index n = 0;
88  for (Index k = 0; k < 3; ++k) {
89  for (Index l = k; l < 3; ++l, ++n) {
90  cost += m_gram_mat(m, n) *
91  (m_cache(i, j) * m_cache(j, k) +
92  m_cache_inv(i, j) * m_cache_inv(j, k)) /
93  6.;
94  }
95  }
96  }
97  }
98  // geometric factor: (3*V/(4*pi))^(2/3)/3 = V^(2/3)/7.795554179
99  return cost;
100  }
101 
102  return isotropic_strain_cost(_deformation_gradient, _vol_factor);
103 }
104 
105 //*******************************************************************************************
106 
108  const Eigen::Matrix3d &_deformation_gradient) const {
109  return strain_cost(_deformation_gradient, vol_factor(_deformation_gradient));
110 }
111 
112 //*******************************************************************************************
114  Eigen::Matrix3d const &_deformation_gradient,
115  SymOpVector const &parent_point_group) const {
116  // Apply the sym op to the deformation gradient and average
117  Eigen::Matrix3d stretch_aggregate = Eigen::Matrix3d::Zero();
118  Eigen::Matrix3d stretch = strain::right_stretch_tensor(_deformation_gradient);
119  for (auto const &op : parent_point_group) {
120  stretch_aggregate += op.matrix * stretch * op.matrix.inverse();
121  }
122  stretch_aggregate = stretch_aggregate / double(parent_point_group.size());
123  return strain_cost(stretch - stretch_aggregate + Eigen::Matrix3d::Identity(),
124  1.0);
125 }
126 
127 //*******************************************************************************************
128 
129 LatticeMap::LatticeMap(const Lattice &_parent, const Lattice &_child,
130  Index num_atoms, int _range,
131  SymOpVector const &_parent_point_group,
132  SymOpVector const &_child_point_group,
133  Eigen::Ref<const Eigen::MatrixXd> const &strain_gram_mat,
134  double _init_better_than /* = 1e20 */,
135  bool _symmetrize_strain_cost, double _xtal_tol)
136  : m_calc(strain_gram_mat),
137  m_vol_factor(pow(std::abs(volume(_child) / volume(_parent)), 1. / 3.)),
138  m_range(_range),
139  m_cost(1e20),
140  m_currmat(0),
141  m_symmetrize_strain_cost(_symmetrize_strain_cost),
142  m_xtal_tol(_xtal_tol) {
143  Lattice reduced_parent = _parent.reduced_cell();
144  m_parent = reduced_parent.lat_column_mat();
145 
146  Lattice reduced_child = _child.reduced_cell();
147  m_child = reduced_child.lat_column_mat();
148 
149  m_U = _parent.inv_lat_column_mat() * m_parent;
150  m_V_inv = m_child.inverse() * _child.lat_column_mat();
151 
152  if (_range == 1)
153  m_mvec_ptr = &unimodular_matrices<1>();
154  else if (_range == 2)
155  m_mvec_ptr = &unimodular_matrices<2>();
156  else if (_range == 3)
157  m_mvec_ptr = &unimodular_matrices<3>();
158  else if (_range == 4)
159  m_mvec_ptr = &unimodular_matrices<4>();
160  else
161  throw std::runtime_error(
162  "LatticeMap cannot currently be invoked for range>4");
163 
164  // Construct inverse fractional symops for parent
165  {
166  IsPointGroupOp symcheck(reduced_parent);
167  m_parent_fsym_mats.reserve(_parent_point_group.size());
168  for (auto const &op : _parent_point_group) {
169  if (!symcheck(op)) continue;
170  m_parent_fsym_mats.push_back(
171  iround(reduced_parent.inv_lat_column_mat() * op.matrix.transpose() *
172  reduced_parent.lat_column_mat()));
173  for (Index i = 0; i < (m_parent_fsym_mats.size() - 1); ++i) {
174  if (m_parent_fsym_mats[i] == m_parent_fsym_mats.back()) {
175  m_parent_fsym_mats.pop_back();
176  break;
177  }
178  }
179  }
180  }
181 
182  // Store the parent symmetry operations
183  m_parent_point_group = _parent_point_group;
184 
185  // Construct fractional symops for child
186  {
187  IsPointGroupOp symcheck(reduced_child);
188  m_child_fsym_mats.reserve(_child_point_group.size());
189  for (auto const &op : _child_point_group) {
190  if (!symcheck(op)) continue;
191  m_child_fsym_mats.push_back(
192  iround(reduced_child.inv_lat_column_mat() * op.matrix *
193  reduced_child.lat_column_mat()));
194  for (Index i = 0; i < (m_child_fsym_mats.size() - 1); ++i) {
195  if (m_child_fsym_mats[i] == m_child_fsym_mats.back()) {
196  m_child_fsym_mats.pop_back();
197  break;
198  }
199  }
200  }
201  }
202 
203  reset(_init_better_than);
204 }
205 
206 LatticeMap::LatticeMap(Eigen::Ref<const LatticeMap::DMatType> const &_parent,
207  Eigen::Ref<const LatticeMap::DMatType> const &_child,
208  Index _num_atoms, int _range,
209  SymOpVector const &_parent_point_group,
210  SymOpVector const &_child_point_group,
211  Eigen::Ref<const Eigen::MatrixXd> const &strain_gram_mat,
212  double _init_better_than /* = 1e20 */,
213  bool _symmetrize_strain_cost, double _xtal_tol)
214  : LatticeMap(Lattice(_parent), Lattice(_child), _num_atoms, _range,
215  _parent_point_group, _child_point_group, strain_gram_mat,
216  _init_better_than, _symmetrize_strain_cost, _xtal_tol) {}
217 
218 //*******************************************************************************************
219 void LatticeMap::reset(double _better_than) {
220  m_currmat = 0;
221 
222  // From relation F * parent * inv_mat.inverse() = child
223  m_deformation_gradient = m_child * inv_mat().cast<double>() *
224  m_parent.inverse(); // -> _deformation_gradient
225 
227 
228  // Initialize to first valid mapping
229  if (tcost <= _better_than && _check_canonical()) {
230  m_cost = tcost;
231  // reconstruct correct N for unreduced lattice
232  m_N = m_U * inv_mat().cast<double>().inverse() * m_V_inv;
233  } else
234  next_mapping_better_than(_better_than);
235 }
236 //*******************************************************************************************
237 /*
238  * For L_child = (*this).lat_column_mat() and L_parent =
239  * _parent_lat.lat_column_mat(), we find the mapping:
240  *
241  * L_child = _deformation_gradient * L_parent * N -- (Where 'N' is
242  * an integer matrix of determinant=1)
243  *
244  * That minimizes the cost function:
245  *
246  * C = pow((*this).volume(),2/3) *
247  * trace(E_dev.transpose()*E_dev.transpose()) / 4
248  *
249  * Where E_dev is the non-volumetric component of the green-lagrange strain
250  *
251  * E_dev = E - trace(E/3)*Identity -- (such that E_dev is traceless)
252  *
253  * where the Green-Lagrange strain is
254  *
255  * E =
256  * (_deformation_gradient.transpose()*_deformation_gradient-Identity)/2
257  *
258  * The cost function approximates the mean-square-displacement of a point in a
259  * cube of volume (*this).volume() when it is deformed by deformation matrix
260  * '_deformation_gradient', but neglecting volumetric effects
261  *
262  * The algorithm proceeds by counting over 'N' matrices (integer matrices of
263  * determinant 1) with elements on the interval (-2,2). (we actually count over
264  * N.inverse(), because....)
265  *
266  * The green-lagrange strain for that 'N' is then found using the relation
267  *
268  * _deformation_gradient.transpose()*_deformation_gradient =
269  * L_parent.inverse().transpose()*N.inverse().transpose()*L_child.transpose()*L_child*N.inverse()*L_parent.inverse()
270  *
271  * The minimal 'C' is returned at the end of the optimization.
272  */
273 //*******************************************************************************************
274 
276  m_currmat = 0;
277 
278  // Get an upper bound on the best mapping by starting with no lattice
279  // equivalence
280  m_N = DMatType::Identity(3, 3);
281  // m_dcache -> value of inv_mat() that gives m_N = identity;
282  m_dcache = m_V_inv * m_U;
284 
285  double best_cost = calc_strain_cost(m_deformation_gradient);
286 
287  while (next_mapping_better_than(best_cost).strain_cost() < best_cost) {
288  best_cost = strain_cost();
289  }
290 
291  m_cost = best_cost;
292  return *this;
293 }
294 
295 //*******************************************************************************************
297  const Eigen::Matrix3d &deformation_gradient) const {
300  else
302 }
303 
304 //*******************************************************************************************
305 
306 const LatticeMap &LatticeMap::next_mapping_better_than(double max_cost) const {
307  m_cost = 1e20;
308  return _next_mapping_better_than(max_cost);
309 }
310 
311 //*******************************************************************************************
312 // Implements the algorithm as above, with generalized inputs:
313 // -- m_inv_count saves the state between calls
314 // -- the search breaks when a mapping is found with cost < max_cost
315 const LatticeMap &LatticeMap::_next_mapping_better_than(double max_cost) const {
316  DMatType init_deformation_gradient(m_deformation_gradient);
317  // tcost initial value shouldn't matter unles m_inv_count is invalid
318  double tcost = max_cost;
319 
320  while (++m_currmat < n_mat()) {
321  if (!_check_canonical()) {
322  continue;
323  }
324 
325  // From relation _deformation_gradient * parent * inv_mat.inverse() = child
326  m_deformation_gradient = m_child * inv_mat().cast<double>() *
327  m_parent.inverse(); // -> _deformation_gradient
329  if (std::abs(tcost) < (std::abs(max_cost) + std::abs(xtal_tol()))) {
330  m_cost = tcost;
331 
332  // need to undo the effect of transformation to reduced cell on 'N'
333  // Maybe better to get m_N from m_deformation_gradient instead? m_U and
334  // m_V_inv depend on the lattice reduction that was performed in the
335  // constructor, so we would need to store "non-reduced" parent and child
336  m_N = m_U * inv_mat().cast<double>().inverse() * m_V_inv;
337  // std::cout << "N:\n" << m_N << "\n";
338  // We already have:
339  // m_deformation_gradient = m_child * inv_mat().cast<double>() *
340  // m_parent.inverse();
341  break;
342  }
343  }
344  if (!(std::abs(tcost) < (std::abs(max_cost) + std::abs(xtal_tol())))) {
345  // If no good mappings were found, uncache the starting value of
346  // m_deformation_gradient
347  m_deformation_gradient = init_deformation_gradient;
348  // m_N hasn't changed if tcost>max_cost
349  // m_cost hasn't changed either
350  }
351  // m_N, m_deformation_gradient, and m_cost will describe the best mapping
352  // encountered, even if nothing better than max_cost was encountered
353 
354  return *this;
355 }
356 
357 //*******************************************************************************************
358 
360  // Purpose of jmin is to exclude (i,j)=(0,0) element
361  // jmin is set to 0 at end of i=0 pass;
362  Index jmin = 1;
363  for (Index i = 0; i < m_parent_fsym_mats.size(); ++i, jmin = 0) {
364  auto const &inv_parent_op = m_parent_fsym_mats[i];
365  for (Index j = jmin; j < m_child_fsym_mats.size(); ++j) {
366  auto const &child_op = m_child_fsym_mats[j];
367  m_icache = child_op * inv_mat() * inv_parent_op;
368  // Skip ops that transform matrix out of range; they won't be enumerated
369  if (std::abs(m_icache(0, 0)) > m_range ||
370  std::abs(m_icache(0, 1)) > m_range ||
371  std::abs(m_icache(0, 2)) > m_range ||
372  std::abs(m_icache(1, 0)) > m_range ||
373  std::abs(m_icache(1, 1)) > m_range ||
374  std::abs(m_icache(1, 2)) > m_range ||
375  std::abs(m_icache(2, 0)) > m_range ||
376  std::abs(m_icache(2, 1)) > m_range ||
377  std::abs(m_icache(2, 2)) > m_range)
378  continue;
379  if (std::lexicographical_compare(m_icache.data(), m_icache.data() + 9,
380  inv_mat().data(), inv_mat().data() + 9))
381  return false;
382  }
383  }
384  return true;
385 }
386 } // namespace xtal
387 } // namespace CASM
Checks if operations are point group operations.
const Eigen::Matrix3d & lat_column_mat() const
3x3 matrix with lattice vectors as its columne
Definition: Lattice.hh:110
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
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
Eigen::Matrix< double, 3, 3, Eigen::DontAlign > DMatType
Definition: LatticeMap.hh:80
SymOpVector m_parent_point_group
Definition: LatticeMap.hh:146
double strain_cost() const
Definition: LatticeMap.hh:108
LatticeMap(Lattice const &_parent, Lattice const &_child, Index _num_atoms, int _range, SymOpVector const &_parent_point_group, SymOpVector const &_child_point_group, Eigen::Ref< const Eigen::MatrixXd > const &strain_gram_mat=Eigen::MatrixXd::Identity(9, 9), double _init_better_than=1e20, bool _symmetrize_strain_cost=false, double _xtal_tol=TOL)
Definition: LatticeMap.cc:129
Eigen::Matrix3i const & inv_mat() const
Returns the inverse of the current transformation matrix under consideration.
Definition: LatticeMap.hh:165
double xtal_tol() const
Definition: LatticeMap.hh:122
const DMatType & deformation_gradient() const
Definition: LatticeMap.hh:114
StrainCostCalculator m_calc
Definition: LatticeMap.hh:131
LatticeMap const & next_mapping_better_than(double max_cost) const
Definition: LatticeMap.cc:306
bool symmetrize_strain_cost() const
Definition: LatticeMap.hh:123
Index n_mat() const
Number of unimodular matrices.
Definition: LatticeMap.hh:168
double calc_strain_cost(const Eigen::Matrix3d &deformation_gradient) const
Definition: LatticeMap.cc:296
void reset(double _better_than=1e20)
Definition: LatticeMap.cc:219
LatticeMap const & best_strain_mapping() const
Definition: LatticeMap.cc:275
DMatType m_deformation_gradient
Definition: LatticeMap.hh:158
std::vector< Eigen::Matrix3i > const * m_mvec_ptr
Definition: LatticeMap.hh:140
std::vector< Eigen::Matrix3i > m_child_fsym_mats
Definition: LatticeMap.hh:149
LatticeMap const & _next_mapping_better_than(double max_cost) const
Definition: LatticeMap.cc:315
std::vector< Eigen::Matrix3i > m_parent_fsym_mats
Definition: LatticeMap.hh:143
bool _check_canonical() const
Returns true if current transformation is the canonical equivalent.
Definition: LatticeMap.cc:359
static double isotropic_strain_cost(Eigen::Matrix3d const &_deformation_gradient)
Definition: LatticeMap.cc:65
StrainCostCalculator(Eigen::Ref< const Eigen::MatrixXd > const &strain_gram_mat=Eigen::MatrixXd::Identity(9, 9))
Definition: LatticeMap.cc:14
double strain_cost(Eigen::Matrix3d const &_deformation_gradient) const
Definition: LatticeMap.cc:107
static double vol_factor(Eigen::Matrix3d const &_deformation_gradient)
Definition: LatticeMap.hh:34
double volume(const Lattice &lat)
Returns the volume of a Lattice.
Definition: Lattice.hh:281
Eigen::Matrix3d polar_decomposition(Eigen::Matrix3d const &mat)
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.
Eigen::CwiseUnaryOp< decltype(Local::iround_l< typename Derived::Scalar >), const Derived > iround(const Eigen::MatrixBase< Derived > &val)
Round Eigen::MatrixXd to Eigen::MatrixXi.
IdentitySymRepBuilder Identity()
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
std::vector< SymOp > SymOpVector
Main CASM namespace.
Definition: APICommand.hh:8
Eigen::Matrix3d Matrix3d
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Definition: stream_io.hh:24