CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
SymInfo.cc
Go to the documentation of this file.
3 
4 namespace CASM {
5 
6  const std::string traits<symmetry_type>::name = "symmetry_type";
7 
8  const std::multimap<symmetry_type, std::vector<std::string> > traits<symmetry_type>::strval = {
9  {symmetry_type::identity_op, {"identity"} },
10  {symmetry_type::mirror_op, {"mirror"} },
11  {symmetry_type::glide_op, {"glide"} },
12  {symmetry_type::rotation_op, {"rotation"} },
13  {symmetry_type::screw_op, {"screw"} },
14  {symmetry_type::inversion_op, {"inversion"} },
15  {symmetry_type::rotoinversion_op, {"rotoinversion"} },
16  {symmetry_type::invalid_op, {"invalid"} }
17  };
18 
19 
20  SymInfo::SymInfo(const SymOp &op, const Lattice &lat) :
21  axis(lat),
22  screw_glide_shift(lat),
23  location(lat) {
24 
25  auto matrix = op.matrix();
26  auto tau = op.tau();
27 
28  vector_type _axis;
29  vector_type _screw_glide_shift;
30  vector_type _location;
31 
32  // Simplest case is identity: has no axis and no location
33  if(almost_equal(matrix.trace(), 3.)) {
34  angle = 0;
36  _axis = vector_type::Zero();
37  _location = vector_type::Zero();
38  _set(_axis, _screw_glide_shift, _location, lat);
39  return;
40  }
41 
42  // second simplest case is inversion: has no axis and location is tau()/2
43  if(almost_equal(matrix.trace(), -3.)) {
44  angle = 0;
46  _axis = vector_type::Zero();
47  _location = tau / 2.;
48  _set(_axis, _screw_glide_shift, _location, lat);
49  return;
50  }
51 
52  // det is -1 if improper and +1 if proper
53  int det = round(matrix.determinant());
54 
55  // Find eigen decomposition of proper operation (by multiplying by determinant)
56  Eigen::EigenSolver<matrix_type> t_eig(det * matrix);
57 
58  // 'axis' is eigenvector whose eigenvalue is +1
59  for(Index i = 0; i < 3; i++) {
60  if(almost_equal(t_eig.eigenvalues()(i), std::complex<double>(1, 0))) {
61  _axis = t_eig.eigenvectors().col(i).real();
62  break;
63  }
64  }
65 
66  // Sign convention for 'axis': first non-zero element is positive
67  for(Index i = 0; i < 3; i++) {
68  if(!almost_zero(_axis[i])) {
69  _axis *= float_sgn(_axis[i]);
70  break;
71  }
72  }
73 
74  // get vector orthogonal to axis: ortho,
75  // apply matrix: rot
76  // and check angle between ortho and det*rot,
77  // using determinant to get the correct angle for improper
78  // (i.e. want angle before inversion for rotoinversion)
79  vector_type ortho = _axis.unitOrthogonal();
80  vector_type rot = det * (matrix * ortho);
81  angle = fmod((180. / M_PI) * atan2(_axis.dot(ortho.cross(rot)), ortho.dot(rot)) + 360., 360.);
82 
83  /*
84  std::cout << "det: " << det << "\n";
85  std::cout << "y: " << _axis.dot(ortho.cross(rot)) << "\n";
86  std::cout << "x: " << ortho.dot(rot) << "\n";
87  std::cout << "angle: " << angle << std::endl;
88  */
89 
90  if(det < 0) {
91  if(almost_equal(angle, 180.)) {
92 
93  // shift is component of tau perpendicular to axis
94  Coordinate coord(tau - tau.dot(_axis) * _axis, lat, CART);
95  _screw_glide_shift = coord.cart();
96 
97  // location is 1/2 of component of tau parallel to axis:
98  // matrix*location+tau = -location+tau = location
99  _location = tau.dot(_axis) * _axis / 2.;
100 
101  op_type = coord.is_lattice_shift() ? symmetry_type::mirror_op : symmetry_type::glide_op;
102  }
103  else {
104  // shift is component of tau parallel to axis
105  _screw_glide_shift = tau.dot(_axis) * _axis;
106 
107  // rotoinversion is point symmetry, so we can solve matrix*p+tau=p for invariant point p
108  _location = (matrix_type::Identity() - matrix).inverse() * tau;
109 
111  }
112  }
113  else {
114 
115  // shift is component of tau parallel to axis
116  Coordinate coord(tau.dot(_axis) * _axis, lat, CART);
117  _screw_glide_shift = coord.cart();
118 
119  // Can only solve 2d location problem
120  Eigen::MatrixXd tmat(3, 2);
121  tmat << ortho, ortho.cross(_axis);
122 
123  // if A = tmat.transpose()*matrix()*tmat and s=tmat.transpose()*tau()
124  // then 2d invariant point 'v' is solution to A*v+s=v
125  // implies 3d invariant point 'p' is p=tmat*(eye(2)-A).inverse()*s
126  _location = tmat * (Eigen::MatrixXd::Identity(2, 2) - tmat.transpose() * matrix * tmat).inverse() * tmat.transpose() * tau;
127 
128  op_type = coord.is_lattice_shift() ? symmetry_type::rotation_op : symmetry_type::screw_op;
129 
130  }
131  _set(_axis, _screw_glide_shift, _location, lat);
132  return;
133  }
134 
135  void SymInfo::_set(const vector_type &_axis,
136  const vector_type &_screw_glide_shift,
137  const vector_type &_location,
138  const Lattice &lat) {
139  axis = Coordinate(_axis, lat, CART);
140  screw_glide_shift = Coordinate(_screw_glide_shift, lat, CART);
141  location = Coordinate(_location, lat, CART);
142  }
143 
144 
150  std::string to_string(const SymInfo &info, COORD_TYPE mode) {
151 
152  std::stringstream stream;
153  char term(0);
154  int prec(3);
155  int pad(0);
156 
157  auto print_coord = [&](const Coordinate & coord) {
158  coord.print(stream, mode, term, prec, pad);
159  };
160 
161  auto print_axis = [&](const Coordinate & coord) {
162  coord.print_axis(stream, mode, term, prec, pad);
163  };
164 
165  switch(info.op_type) {
166  case symmetry_type::identity_op:
167  stream << "Identity Operation";
168  break;
169 
170  case symmetry_type::mirror_op:
171  stream.setf(std::ios::showpoint);
172  stream << "Mirror Operation with plane normal" << std::setw(7);
173  print_axis(info.axis);
174  break;
175 
176  case symmetry_type::glide_op:
177  stream.setf(std::ios::showpoint);
178  stream << "Glide Operation with plane normal" << std::setw(7);
179  print_axis(info.axis);
180  stream << '\n';
181  stream << "Glide Vector:" << std::setw(7);
182  print_coord(info.screw_glide_shift);
183  break;
184 
185  case symmetry_type::rotation_op:
186  stream.unsetf(std::ios::showpoint);
187  stream << std::setprecision(3) << info.angle << "-degree Rotation Operation about axis";
188  stream.setf(std::ios::showpoint);
189  stream << std::setw(7);
190  print_axis(info.axis);
191  break;
192 
193  case symmetry_type::screw_op:
194  stream.unsetf(std::ios::showpoint);
195  stream << std::setprecision(3) << info.angle << "-degree Screw Operation along axis";
196  stream.setf(std::ios::showpoint);
197  stream << std::setw(7);
198  print_axis(info.axis);
199  stream << "\n Screw Vector:" << std::setw(7);
200  print_coord(info.screw_glide_shift);
201  break;
202 
203  case symmetry_type::inversion_op:
204  stream << "Inversion Operation";
205  break;
206 
207  case symmetry_type::rotoinversion_op:
208  stream.unsetf(std::ios::showpoint);
209  stream << std::setprecision(3) << info.angle << "-degree Rotoinversion Operation about axis";
210  stream.setf(std::ios::showpoint);
211  stream << std::setw(7);
212  print_axis(info.axis);
213  break;
214 
215  case symmetry_type::invalid_op:
216  stream << "Invalid Operation !!!";
217  break;
218 
219  }
220 
221  return stream.str();
222  }
223 
225  std::string description(const SymOp &op, const Lattice &lat, COORD_TYPE mode) {
226  return to_string(SymInfo(op, lat), mode);
227  }
228 
230  void add_sym_info(const SymInfo &info, jsonParser &j) {
232  j["type"] = info.op_type;
233 
234  if(info.op_type == symmetry_type::rotation_op ||
235  info.op_type == symmetry_type::screw_op ||
236  info.op_type == symmetry_type::rotoinversion_op) {
237  to_json_array(info.axis.const_cart().normalized(), j["rotation_axis"]["CART"]);
238  to_json_array(info.axis.const_frac().normalized(), j["rotation_axis"]["FRAC"]);
239  j["rotation_angle"] = info.angle;
240  }
241  else if(info.op_type == symmetry_type::mirror_op ||
242  info.op_type == symmetry_type::glide_op) {
243  to_json_array(info.axis.const_cart().normalized(), j["mirror_normal"]["CART"]);
244  to_json_array(info.axis.const_frac().normalized(), j["mirror_normal"]["FRAC"]);
245  }
246 
247  if(info.op_type == symmetry_type::screw_op ||
248  info.op_type == symmetry_type::glide_op) {
249  to_json_array(info.screw_glide_shift.const_cart(), j["shift"]["CART"]);
250  to_json_array(info.screw_glide_shift.const_frac(), j["shift"]["FRAC"]);
251  }
252 
253  if(info.op_type != symmetry_type::identity_op) {
254  to_json_array(info.location.const_cart(), j["invariant_point"]["CART"]);
255  to_json_array(info.location.const_frac(), j["invariant_point"]["FRAC"]);
256  }
257  }
258 
259 }
Eigen::MatrixXd MatrixXd
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::MatrixXd pad(const Eigen::MatrixXd &M, int n)
Construct a matrix consisting of blocks M and Identity(n,n)
Definition: PCA.hh:88
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:41
Main CASM namespace.
Definition: complete.cpp:8
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: EnumIO.hh:83
SymInfo(const SymOp &op, const Lattice &lat)
EigenIndex Index
For long integer indexing:
int float_sgn(T val, double compare_tol=TOL)
Definition: CASM_math.hh:130
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.
void add_sym_info(const SymInfo &info, jsonParser &j)
Add to existing JSON object.
std::string description(const SymOp &op, const Lattice &lat, COORD_TYPE mode)
Print SymInfo to string.
CASM::jsonParser & to_json_array(const Eigen::MatrixBase< Derived > &value, CASM::jsonParser &json)
Write Eigen Matrix with 1 row or 1 column to JSON array.
Definition: container.hh:191
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)
int round(double val)
Definition: CASM_math.cc:6