CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
SymGroup.cc
Go to the documentation of this file.
2 
3 #include "casm/external/Eigen/CASM_AddOns"
4 #include "casm/misc/CASM_math.hh"
11 #include "casm/symmetry/SymInfo.hh"
12 
15 
16 namespace CASM {
17  //INITIALIZE STATIC MEMBER MasterSymGroup::GROUP_COUNT
18  //THIS MUST OCCUR IN A .CC FILE; MAY CAUSE PROBLEMS IF WE
19  //CHANGE COMPILING/LINKING STRATEGY
21 
22  //*******************************************************************************************
23 
25  SymGroup(RHS),
26  m_group_index(RHS.m_group_index),
27  m_coord_rep_ID(RHS.m_coord_rep_ID),
28  m_reg_rep_ID(RHS.m_reg_rep_ID),
29  m_identity_rep_IDs(RHS.m_identity_rep_IDs) {
30 
31  m_rep_array.reserve(RHS.m_rep_array.size());
32 
33  for(Index i = 0; i < RHS.m_rep_array.size(); i++) {
34  _add_representation(RHS.m_rep_array[i]->copy());
35  }
36 
37  for(Index i = 0; i < size(); i++)
38  at(i).set_index(*this, i);
39  }
40 
41  //*******************************************************************************************
42 
44  clear();
45  return;
46  }
47 
48  //*******************************************************************************************
53  m_rep_array.reserve(RHS.m_rep_array.size());
54  for(Index i = 0; i < RHS.m_rep_array.size(); i++)
55  _add_representation(RHS.m_rep_array[i]->copy());
56 
57  for(Index i = 0; i < size(); i++)
58  at(i).set_index(*this, i);
59 
60  return *this;
61  }
62 
63  //*******************************************************************************************
64 
65  void MasterSymGroup::push_back(const SymOp &op) {
67 
68  back().set_index(*this, size() - 1);
69  return;
70  }
71 
72  //*******************************************************************************************
73 
75  if(!m_point_group.size()) {
77  }
78  return m_point_group;
79  }
80 
81  //*******************************************************************************************
82 
86  for(Index i = 0; i < m_rep_array.size(); i++) {
87  if(m_rep_array[i])
88  delete m_rep_array[i];
89  }
90  m_rep_array.clear();
91 
93 
94  m_identity_rep_IDs.clear();
95  return;
96  }
97 
98  //*******************************************************************************************
99 
101  if(m_coord_rep_ID.empty())
102  _add_coord_rep();
103  return m_coord_rep_ID;
104  }
105 
106  //*******************************************************************************************
107 
109  if(m_reg_rep_ID.empty())
110  _add_reg_rep();
111  return m_reg_rep_ID;
112  }
113 
114  //*******************************************************************************************
115 
117  if(m_identity_rep_IDs.size() < dim + 1)
119 
120  if(m_identity_rep_IDs[dim].empty()) {
121  m_rep_array.push_back(new SymGroupRep(*this));
122  for(Index i = 0; i < size(); i++) {
123  m_rep_array.back()->set_rep(i, SymMatrixXd(Eigen::MatrixXd::Identity(dim, dim)));
124  }
126  }
127  return m_identity_rep_IDs[dim];
128  }
129 
130  //*******************************************************************************************
131 
133  if(m_coord_rep_ID.empty())
134  _add_coord_rep();
136  }
137 
138  //*******************************************************************************************
139 
141  if(m_reg_rep_ID.empty())
142  _add_reg_rep();
144  }
145 
146  //*******************************************************************************************
147 
149  SymGroupRep *coordrep(new SymGroupRep(*this));
150  for(Index i = 0; i < size(); i++)
151  coordrep->set_rep(i, SymMatrixXd(at(i).matrix()));
152 
154  return m_coord_rep_ID;
155  }
156 
157  //*******************************************************************************************
159  SymGroupRep *regrep(new SymGroupRep(*this));
160  Eigen::MatrixXd regrep_mat(size(), size());
161 
162  if(get_alt_multi_table().size() != size())
163  return SymGroupRepID();
164 
165 
166  for(Index i = 0; i < size(); i++) {
167  for(Index j = 0; j < size(); j++) {
168  for(Index k = 0; k < size(); k++) {
169  if(alt_multi_table[j][k] == i) {
170  regrep_mat(j, k) = 1.0;
171  }
172  else {
173  regrep_mat(j, k) = 0.0;
174  }
175  }
176  }
177  regrep->set_rep(i, SymMatrixXd(regrep_mat));
178  }
179 
181 
182  return m_reg_rep_ID;
183  }
184 
185  //*******************************************************************************************
186 
188  SymGroupRep const *rep1(_representation_ptr(ID1)), *rep2(_representation_ptr(ID2));
189  if(!(rep1 && rep2))
190  return SymGroupRepID();
191 
192  SymGroupRep *new_rep(new SymGroupRep(*this));
193  Eigen::MatrixXd tmat;
194  for(Index i = 0; i < size(); i++) {
195  //std::cout << "rep1 matrix:\n";
196  //std::cout << *(rep1->get_MatrixXd(i)) << '\n';
197  //std::cout << "rep2 matrix:\n";
198  //std::cout << *(rep2->get_MatrixXd(i)) << '\n';
199 
200  kroneckerProduct(*(rep1->get_MatrixXd(i)), *(rep2->get_MatrixXd(i)), tmat);
201  //std::cout << "Total matrix:\n" << tmat << '\n';
202  new_rep->set_rep(i, SymMatrixXd(tmat));
203  }
204  return _add_representation(new_rep);
205  }
206 
207  //*******************************************************************************************
208 
211  for(Index i = 0; i < rep_IDs.size(); i++) {
212  treps.push_back(_representation_ptr(rep_IDs[i]));
213  if(!treps.back())
214  return SymGroupRepID();
215  }
216  SymGroupRep *new_rep(new SymGroupRep(*this));
217 
218  int dim = 0;
219  for(Index i = 0; i < treps.size(); i++) {
220  if(treps[i]->size() != size())
221  return SymGroupRepID();
222  if(!(treps[i]->get_MatrixXd(0)))
223  return SymGroupRepID();
224 
225  dim += (treps[i]->get_MatrixXd(0))->cols();
226  }
227 
228  Eigen::MatrixXd tmat(Eigen::MatrixXd::Zero(dim, dim));
229  int corner = 0;
230  for(Index i = 0; i < size(); i++) {
231  corner = 0;
232  for(Index j = 0; j < treps.size(); j++) {
233  tmat.block(corner, corner, (treps[j]->get_MatrixXd(i))->cols(), (treps[j]->get_MatrixXd(i))->cols()) = *(treps[j]->get_MatrixXd(i));
234  corner += (treps[j]->get_MatrixXd(i))->cols();
235  }
236  new_rep->set_rep(i, SymMatrixXd(tmat));
237  }
238  return _add_representation(new_rep);
239  }
240 
241  //*******************************************************************************************
242 
244 
245  // make sure coord_rep_ID exists?
246  //Index coord_rep_index((*this).coord_rep_ID());
247  // make a new symmetry representation
248  SymGroupRep *new_rep(new SymGroupRep(*this));
249 
250  // we are going to use our knowledge of how rotation
251  // matrices transform under symmetry (in the coord_rep)
252  // in order to build
253  // up a representation in the rotation basis.
254  for(Index i = 0; i < size(); i++) {
255 
256  // build each 4d representaion for a = 1, b = 1, ...
257  // rp = rotation parameter
258  Eigen::Matrix3d coord_rep_mat = at(i).matrix();
259  if(!almost_equal(coord_rep_mat.determinant(), 1.)) {
260  std::cout << "IN IF" << std::endl;
261  coord_rep_mat *= coord_rep_mat.determinant();
262  //continue;
263  }
264 
265  Eigen::Quaterniond opq(coord_rep_mat);
266 
267  //std::cout << "Quaternion " << opq << std::endl;
268 
269  // this quaternion can be converted to a 4d rotation as follows
270  // q = q0 q1 q2 q3
271  // Right multiplication : r * q = u
272  //
273  // | q0 -q1 -q2 -q3 | | r0 | | u0 |
274  // | q1 q0 q3 -q2 | | r1 | | u1 |
275  // | q2 -q3 q0 q1 | | r2 | = | u2 |
276  // | q3 q2 -q1 q0 | | q3 | | u3 |
277  //
278  // and Left multiplication : q * r = u
279  //
280  // | q0 -q1 -q2 -q3 | | r0 | | u0 |
281  // | q1 q0 -q3 q2 | | r1 | | u1 |
282  // | q2 q3 q0 -q1 | | r2 | = | u2 |
283  // | q3 -q2 q1 q0 | | q3 | | u3 |
284 
285  // building both for now
286  Eigen::MatrixXd right_q(4, 4);
287  Eigen::MatrixXd left_q(4, 4);
288 
289  right_q(0, 0) = opq.w();
290  right_q(0, 1) = -opq.x();
291  right_q(0, 2) = -opq.y();
292  right_q(0, 3) = -opq.z();
293 
294  right_q(1, 0) = opq.x();
295  right_q(1, 1) = opq.w();
296  right_q(1, 2) = opq.z();
297  right_q(1, 3) = -opq.y();
298 
299  right_q(2, 0) = opq.y();
300  right_q(2, 1) = -opq.z();
301  right_q(2, 2) = opq.w();
302  right_q(2, 3) = opq.x();
303 
304  right_q(3, 0) = opq.z();
305  right_q(3, 1) = opq.y();
306  right_q(3, 2) = -opq.x();
307  right_q(3, 3) = opq.w();
308 
309  left_q(0, 0) = opq.w();
310  left_q(0, 1) = -opq.x();
311  left_q(0, 2) = -opq.y();
312  left_q(0, 3) = -opq.z();
313 
314  left_q(1, 0) = opq.x();
315  left_q(1, 1) = opq.w();
316  left_q(1, 2) = -opq.z();
317  left_q(1, 3) = opq.y();
318 
319  left_q(2, 0) = opq.y();
320  left_q(2, 1) = opq.z();
321  left_q(2, 2) = opq.w();
322  left_q(2, 3) = -opq.x();
323 
324  left_q(3, 0) = opq.z();
325  left_q(3, 1) = -opq.y();
326  left_q(3, 2) = opq.x();
327  left_q(3, 3) = opq.w();
328 
329 
330  std::cout << right_q << std::endl;
331 
332  new_rep->set_rep(i, SymMatrixXd(left_q));
333  }
334 
335  for(Index i = 0 ; i < new_rep->size() ; i++) {
336  std::cout << "Rota Rep final mats " << i << "\n" << *(new_rep->get_MatrixXd(i)) << std::endl;
337  }
338 
339  return _add_representation(new_rep);
340  }
341 
342  //*******************************************************************************************
343 
345  SymGroupRep const *trep(_representation_ptr(orig_ID));
346  if(!trep)
347  return SymGroupRepID();
348  return add_representation(trep->coord_transformed_copy(trans_mat));
349  }
350 
351  //*******************************************************************************************
352 
354  SymGroupRepID new_ID(group_index(), m_rep_array.size());
355  m_rep_array.push_back(new SymGroupRep(*this, new_ID));
356  return new_ID;
357  }
358 
359  //*******************************************************************************************
360 
362  return _add_representation(new_rep.copy());
363  }
364 
365  //*******************************************************************************************
366 
368  SymGroupRepID new_ID(group_index(), m_rep_array.size());
369  m_rep_array.push_back(new_rep);
370  m_rep_array.back()->set_master_group(*this, new_ID);
371  return new_ID;
372  }
373 
374  //*******************************************************************************************
376  return *_representation_ptr(_id);
377  }
378  //*******************************************************************************************
380  if(_id.is_identity()) {
381  // _id.rep_index() stores dimension of representation
382  _id = identity_rep_ID(_id.rep_index());
383  }
384  else if(_id.group_index() != group_index()) {
385  throw std::runtime_error("Attempting to access representation from MasterGroup #" + std::to_string(group_index())
386  + " that resides in MasterGroup #" + std::to_string(_id.group_index()));
387  }
388 
389  return m_rep_array[_id.rep_index()];
390  }
391 
392  //*******************************************************************************************
393 
395  SymGroup::sort();
397  bool broken_check(false);
398  Array<Index> perm_array(size(), 0);
399  for(Index i = 0; i < size(); i++) {
400  perm_array[i] = at(i).index();
401  if(at(i).index() != i) {
402  at(i).set_index(*this, i);
403  broken_check = true;
404  }
405  }
406  if(broken_check && m_rep_array.size()) {
407  std::cerr << "WARNING: Order of symmetry operations has been altered by MasterSymGroup::sort_by_class(). Attempting to repair "
408  << m_rep_array.size() << " symmetry representations.\n";
409  for(Index i = 0; i < m_rep_array.size(); i++) {
410  m_rep_array[i]->permute(perm_array);
411  for(Index j = 0; j < m_rep_array[i]->size(); j++) {
412  if(m_rep_array[i]->at(j)) {
413  (m_rep_array[i]->at(j))->set_identifiers(*this, m_rep_array[i]->get_ID(), perm_array[j]);
414  }
415  }
416  }
417  }
418 
419  return;
420  }
421 
422 
423  //*******************************************************************************************
424 
425  SymGroup::SymGroup(const Array<SymOp> &from_array, PERIODICITY_TYPE init_type) :
426  m_group_periodicity(init_type),
427  m_max_error(-1) {
428  for(Index i = 0; i < from_array.size(); i++)
429  push_back(from_array[i]);
430 
431  }
432 
433  //*******************************************************************************************
434 
435  void SymGroup::set_lattice(const Lattice &lat) {
436  m_lat_ptr = &lat;
437  }
438 
439  /*****************************************************************/
440 
441  void SymGroup::push_back(const SymOp &new_op) {
442  Array<SymOp>::push_back(new_op);
443  if(back().map_error() > m_max_error)
444  m_max_error = back().map_error();
445  return;
446  }
447 
448  //*******************************************************************************************
449 
451  multi_table.clear();
454  class_names.clear();
456  m_character_table.clear();
458  irrep_IDs.clear();
459  irrep_names.clear();
460 
461  m_subgroups.clear();
462 
465 
466  name.clear();
467  latex_name.clear();
468  comment.clear();
469 
470  return;
471  }
472 
473  //*****************************************************************
475  Array<Index> ind_array(size());
476  for(Index i = 0; i < size(); i++) {
477  if(!valid_index(at(i).index()) && at(i).has_valid_master()) {
478  ind_array[i] = at(i).master_group().find_periodic(at(i));
479  }
480  else {
481  ind_array[i] = at(i).index();
482  }
483 
484  }
485  return ind_array;
486  }
487 
488  //*****************************************************************
489 
492 
493  clear_tables();
494 
495  m_max_error = -1;
496 
497  return;
498  }
499 
500  //*****************************************************************
501  // Determination of point group type
502  // By counting the number of unique type of rotation symmetry and
503  // total number of rotation symmetry elements, the point group type
504  // of a space group is determined.
505 
506  //
507  // Donghee
508  //*****************************************************************
510  Array<SymOp> E, TF, THF, FF, SF;
511  Array<SymOp> I, ITF, ITHF, IFF, ISF;
512  for(Index i = 0; i < size(); i++) {
513  SymInfo info(at(i), lattice());
514  if(info.op_type == symmetry_type::identity_op) {
515  E.push_back(at(i));
516  }
518  I.push_back(at(i));
519  }
520  if(info.op_type == symmetry_type::rotation_op) {
521  if(std::abs(180 - info.angle) < TOL) {
522  TF.push_back(at(i));
523  }
524  else if(std::abs(120 - info.angle) < TOL) {
525  THF.push_back(at(i));
526  }
527  else if(std::abs(90 - info.angle) < TOL) {
528  FF.push_back(at(i));
529  }
530  else if(std::abs(60 - info.angle) < TOL) {
531  SF.push_back(at(i));
532  }
533  else continue;
534  }
536  if(info.op_type == symmetry_type::mirror_op) {
537  ITF.push_back(at(i));
538  }
539  else if(std::abs(120 - info.angle) < TOL) {
540  ITHF.push_back(at(i));
541  }
542  else if(std::abs(90 - info.angle) < TOL) {
543  IFF.push_back(at(i));
544  }
545  else if(std::abs(60 - info.angle) < TOL) {
546  ISF.push_back(at(i));
547  }
548  else continue;
549  }
550  }
551  rotation_groups.push_back(E);
552  rotation_groups.push_back(I);
553  rotation_groups.push_back(TF);
554  rotation_groups.push_back(ITF);
555  rotation_groups.push_back(THF);
556  rotation_groups.push_back(ITHF);
557  rotation_groups.push_back(FF);
558  rotation_groups.push_back(IFF);
559  rotation_groups.push_back(SF);
560  rotation_groups.push_back(ISF);
561 
562  return;
563  }
564  //*****************************************************************
566  if(!rotation_groups.size()) {
568  }
569  // Calculate total number of rotation elements
570  Index nm = rotation_groups[2].size() + 1;
571  for(Index k = 4; k < rotation_groups.size(); k++) {
572  nm += rotation_groups[k].size();
573  }
574  centric = true;
575  if(!rotation_groups[1].size()) {
576  centric = false;
577  }
578  // naming point gorup
579  group_name.resize(2);
580  group_number.resize(2);
581  if((rotation_groups[4].size() + rotation_groups[5].size()) == 8) {
582  crystal_system = "Cubic";
583  switch(nm) {
584  case 12:
585  if(centric) {
586  group_name[0] = "m-3";
587  group_name[1] = "Th";
588  group_number[0] = 200;
589  group_number[1] = 206;
590  }
591  else {
592  group_name[0] = "23";
593  group_name[1] = "T (Chiral)";
594  group_number[0] = 195;
595  group_number[1] = 199;
596  }
597  break;
598  case 24:
599  if(centric) {
600  group_name[0] = "m-3m";
601  group_name[1] = "Oh";
602  group_number[0] = 221;
603  group_number[1] = 230;
604  }
605  else {
606  if(rotation_groups[6].size() == 6) {
607  group_name[0] = "432";
608  group_name[1] = "O (Chiral)";
609  group_number[0] = 207;
610  group_number[1] = 214;
611  }
612  else if(rotation_groups[7].size() == 6) {
613  group_name[0] = "-43m";
614  group_name[1] = "Td";
615  group_number[0] = 215;
616  group_number[1] = 220;
617  }
618  else {
619  std::cerr << "\n Error Cubic case 24 Acentric \n ";
620  }
621  }
622  break;
623  default:
624  std::cerr << "\n Error Cubic \n";
625  }
626  } // for cubic;
627  else if((rotation_groups[8].size() + rotation_groups[9].size()) == 2) {
628  crystal_system = "Hexagonal";
629  switch(nm) {
630  case 6:
631  if(centric) {
632  group_name[0] = "6/m";
633  group_name[1] = "C6h";
634  group_number[0] = 175;
635  group_number[1] = 176;
636  }
637  else {
638  if(rotation_groups[8].size() == 2) {
639  group_name[0] = "6";
640  group_name[1] = "C6 (Chiral)";
641  group_number[0] = 168;
642  group_number[1] = 173;
643  }
644  else if(rotation_groups[9].size() == 2) {
645  group_name[0] = "-6";
646  group_name[1] = "C3h";
647  group_number[0] = 174;
648  group_number[1] = 174;
649  }
650  else {
651  std::cerr << "\n Error Hexagonal case 6 Acentric \n ";
652  }
653  }
654  break;
655  case 12:
656  if(centric) {
657  group_name[0] = "6/mmm";
658  group_name[1] = "D6h";
659  group_number[0] = 191;
660  group_number[1] = 194;
661 
662  }
663  else {
664  if(rotation_groups[8].size() == 2) {
665  if(rotation_groups[3].size() == 7) {
666  group_name[0] = "622";
667  group_name[1] = "D6 (Chiral)";
668  group_number[0] = 177;
669  group_number[1] = 182;
670  }
671  else if(rotation_groups[4].size() == 6) {
672  group_name[0] = "6mm";
673  group_name[1] = "C6v";
674  group_number[0] = 183;
675  group_number[1] = 186;
676  }
677  else std::cerr << "\n Error Hexagonal case 12 Ancentric #6 \n";
678  }
679  else if(rotation_groups[9].size() == 2) {
680  group_name[0] = "-6m2";
681  group_name[1] = "D3h";
682  group_number[0] = 187;
683  group_number[1] = 190;
684  }
685  else {
686  std::cerr << "\n Error Hexagonal case 12 Acentric \n ";
687  }
688  }
689  break;
690  default:
691  std::cerr << "\n Error Hexagonal \n";
692  }
693  } // for hexagonal
694  else if((rotation_groups[4].size() + rotation_groups[5].size()) == 2) {
695  crystal_system = "Trigonal";
696  switch(nm) {
697  case 3:
698  if(centric) {
699  group_name[0] = "-3";
700  group_name[1] = "S6";
701  group_number[0] = 147;
702  group_number[1] = 148;
703  }
704  else {
705  group_name[0] = "3";
706  group_name[1] = "C3 (Chiral)";
707  group_number[0] = 143;
708  group_number[1] = 146;
709  }
710  break;
711  case 6:
712  if(centric) {
713  group_name[0] = "-3m";
714  group_name[1] = "D3d";
715  group_number[0] = 162;
716  group_number[1] = 167;
717  }
718  else {
719  if(rotation_groups[2].size() == 3) {
720  group_name[0] = "32";
721  group_name[1] = "D3 (Chiral)";
722  group_number[0] = 149;
723  group_number[1] = 155;
724  }
725  else if(rotation_groups[3].size() == 3) {
726  group_name[0] = "3m";
727  group_name[1] = "C3v";
728  group_number[0] = 156;
729  group_number[1] = 161;
730  }
731  else {
732  std::cerr << "\n Error Trigonal case 6 Acentric \n ";
733  }
734  }
735  break;
736  default:
737  std::cerr << "\n Error Trigonal \n";
738  }
739  } // for trigonal
740  else if((rotation_groups[6].size() + rotation_groups[7].size()) == 2) {
741  crystal_system = "Tetragonal";
742  switch(nm) {
743  case 4:
744  if(centric) {
745  group_name[0] = "4/m";
746  group_name[1] = "C4h";
747  group_number[0] = 83;
748  group_number[1] = 88;
749  }
750  else {
751  if(rotation_groups[6].size() == 2) {
752  group_name[0] = "4";
753  group_name[1] = "C4 (Chiral)";
754  group_number[0] = 75;
755  group_number[1] = 80;
756  }
757  else if(rotation_groups[7].size() == 2) {
758  group_name[0] = "-4";
759  group_name[1] = "S4";
760  group_number[0] = 81;
761  group_number[1] = 82;
762  }
763  else {
764  std::cerr << "\n Error Tetragonal case 4 Acentric \n ";
765  }
766  }
767 
768  break;
769  case 8:
770  if(centric) {
771  group_name[0] = "4/mmm";
772  group_name[1] = "D4h";
773  group_number[0] = 123;
774  group_number[1] = 142;
775  }
776  else {
777  if(rotation_groups[6].size() == 2) {
778  if(rotation_groups[3].size() == 5) {
779  group_name[0] = "422";
780  group_name[1] = "D4 (Chiral)";
781  group_number[0] = 89;
782  group_number[1] = 98;
783  }
784  else if(rotation_groups[4].size() == 4) {
785  group_name[0] = "4mm";
786  group_name[1] = "C4v";
787  group_number[0] = 99;
788  group_number[1] = 110;
789  }
790  else std::cerr << "\n Error Tetragonal case 8 Ancentric #4 \n";
791  }
792  else if(rotation_groups[7].size() == 2) {
793  group_name[0] = "-42m";
794  group_name[1] = "D2d";
795  group_number[0] = 111;
796  group_number[1] = 122;
797  }
798  else {
799  std::cerr << "\n Error Tetragonal case 8 Acentric \n ";
800  }
801  }
802  break;
803  default:
804  std::cerr << "\n Error Tetragonal \n";
805  }
806  } // for tetragonal
807  else if((rotation_groups[2].size() + rotation_groups[3].size()) == 3 || ((rotation_groups[2].size() + rotation_groups[3].size()) == 6 && nm == 4)) {
808  crystal_system = "Orthorhomic";
809  if(centric) {
810  group_name[0] = "mmm";
811  group_name[1] = "D2h";
812  group_number[0] = 47;
813  group_number[1] = 74;
814  }
815  else {
816  if(rotation_groups[3].size() == 3) {
817  group_name[0] = "222";
818  group_name[1] = "D2 (Chiral)";
819  group_number[0] = 16;
820  group_number[1] = 24;
821  }
822  else if(rotation_groups[4].size() == 2) {
823  group_name[0] = "mm2";
824  group_name[1] = "C2v";
825  group_number[0] = 25;
826  group_number[1] = 46;
827  }
828  else {
829  std::cerr << "\n Error Orthorhombic Acentric \n ";
830  }
831  }
832  } // for orthorhombic
833 
834  else if((rotation_groups[2].size() + rotation_groups[3].size()) == 1 || nm == 2) {
835  crystal_system = "Monoclinic";
836  if(centric) {
837  group_name[0] = "2/m";
838  group_name[1] = "C2h";
839  group_number[0] = 10;
840  group_number[1] = 15;
841  }
842  else {
843  if(rotation_groups[3].size() == 1) {
844  group_name[0] = "2";
845  group_name[1] = "C2 (Chiral)";
846  group_number[0] = 3;
847  group_number[1] = 5;
848  }
849  else if(rotation_groups[4].size() == 1) {
850  group_name[0] = "m";
851  group_name[1] = "Cs";
852  group_number[0] = 6;
853  group_number[1] = 9;
854  }
855  else {
856  std::cerr << "\n Error Monoclinic Acentric \n ";
857  }
858  }
859  } // for Acentric monoclinic
860  else if(nm == 1) {
861  crystal_system = "Triclinic";
862  if(centric) {
863  group_name[0] = "-1";
864  group_name[1] = "Ci";
865  group_number[0] = 2;
866  group_number[1] = 2;
867  }
868  else {
869  group_name[0] = "1";
870  group_name[1] = "C1 (Chiral)";
871  group_number[0] = 1;
872  group_number[1] = 1;
873  }
874  }
875  else {
876  std::cerr << "Error foind point group type \n";
877  }
878  return;
879  }
880 
881  //*******************************************************************************************
882 
883  void SymGroup::print_space_group_info(std::ostream &out) const {
884  if(!rotation_groups.size())
886 
887  if(!crystal_system.size())
889 
890  out << " Crystal System : " << crystal_system << "\n";
891  out << " Point Group # : " << group_number[0] << " - " << group_number[1] << "\n";
892  out << " Point Group is " ;
893  if(!centric) {
894  out << " Acentric ";
895  }
896  else out << " Centric ";
897  out << " " << group_name[0] << " ( " << group_name[1] << " ) \n";
898  }
899 
900  //*******************************************************************************************
901  /* Loop through SymGroup and populate passed SymGroup
902  * with all the operations but none of the shifts. Unle
903  * explicitly requested, degenerate symmetry operations
904  * will not be added.
905  */
906  //*******************************************************************************************
907  void SymGroup::copy_no_trans(SymGroup &shiftless, bool keep_repeated) const {
908  if(shiftless.size() != 0) {
909  std::cerr << "WARNING in SymGroup::copy_no_trans" << std::endl;
910  std::cerr << "The provided SymGroup wasn't empty and it's about to be erased. Say goodbye." << std::endl;
911  shiftless.clear();
912  }
913  shiftless.m_lat_ptr = m_lat_ptr;
914  for(Index i = 0; i < size(); i++) {
915  SymOp tsymop(at(i).no_trans());
916  if(keep_repeated) {
917  shiftless.push_back(tsymop);
918  }
919  else if(!shiftless.contains(tsymop)) {
920  shiftless.push_back(tsymop);
921  }
922  }
923  return;
924  }
925 
926  //*******************************************************************************************
928  return SymInfo(at(i), lattice());
929  }
930 
931  //*******************************************************************************************
932 
934 
935  return m_max_error;
936  }
937 
938  //*******************************************************************************************
939 
940  const Lattice &SymGroup::lattice() const {
941  assert(m_lat_ptr && "Attempting to access Lattice of a SymGroup, but it has not been initialized!");
942  return *m_lat_ptr;
943  }
944 
945  //***************************************************
946 
947  void SymGroup::_generate_irrep_names() const { //AAB
949 
950  Array<int> repeats;
951 
952  bool inversion = false;
953  bool sigma_h = false;
954  bool sigma_v = false;
955  bool C2p = false;
956 
957  bool sym_wrt_paxis = true;
958  bool sym_wrt_v = true;
959  bool sym_wrt_C2p = true;
960  bool sym_wrt_h = true;
961  bool sym_wrt_inv = true;
962 
963  bool cubic = false;
964 
965  bool all_unique = false;
966 
968 
969  if((name.find("O") != std::string::npos) || (name.find("T") != std::string::npos)) {
970  cubic = true;
971  }
972 
973  for(Index j = 0; j < conjugacy_classes.size(); j++) {
974  if(class_names[j].find("i") != std::string::npos) {
975  inversion = true;
976  }
977  if(class_names[j].find("h") != std::string::npos) {
978  sigma_h = true;
979  }
980  if(class_names[j].find("v") != std::string::npos) {
981  sigma_v = true;
982  }
983  if(class_names[j].find("C2'") != std::string::npos) {
984  C2p = true;
985  }
986  }
987 
988  //std::cout << "STEP 1: Name according to representation dimension...\n";
989  for(Index i = 0; i < irrep_names.size(); i++) {
990  sym_wrt_paxis = true;
991  if(m_character_table[i][0].real() == 1.0) {
992  // Check for symmetry wrt principal axis...
993  // This means finding unprimed proper rotations and checking their
994  // dimension one characters' signs. If they are all +1, the IrRep
995  // is symmetry wrt principal axis. If any of them are -1, then it is
996  // antisymmetric wrt principal axis...
997  // Symmetric => "A"
998  // Antisymmetric => "B"
999 
1000 
1001  // *** IF CUBIC, MUST ONLY CONSIDER THE C3 AXIS HERE!!
1002  // *** THERE SHOULD BE NO "B"
1003 
1004  for(Index j = 0; j < conjugacy_classes.size() && sym_wrt_paxis; j++) {
1005  if(class_names[j].find("C") != std::string::npos) {
1006  if(class_names[j].find("'") != std::string::npos) {
1007  continue;
1008  }
1009  else if(m_character_table[i][j].real() < 0) {
1010  sym_wrt_paxis = false;
1011  }
1012  }
1013  }
1014 
1015  if(sym_wrt_paxis == true) {
1016  irrep_names[i] = "A";
1017  }
1018  else if(!cubic) {
1019  irrep_names[i] = "B";
1020  }
1021  else {
1022  irrep_names[i] = "A";
1023  }
1024 
1025  } //Closes loop over 1-d representations
1026 
1027  else if(m_character_table[i][0].real() == 2.0) {
1028  irrep_names[i] = "E";
1029  }
1030  else if(m_character_table[i][0].real() == 3.0) {
1031  irrep_names[i] = "T";
1032  }
1033  }
1034 
1035  // This checks if each representation has a unique name...
1036  for(Index j = 0; j < irrep_names.size() && all_unique; j++) {
1037  for(Index k = 0; k < irrep_names.size() && all_unique; k++) {
1038  all_unique = true;
1039  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0)) {
1040  // std::cout << irrep_names[j] << " = " << irrep_names[k] << std::endl;
1041  all_unique = false;
1042  }
1043  }
1044  }
1045  //std::cout << irrep_names << std::endl;
1046  //std::cout << "Check if all of the current names are unique... " << all_unique << "\n";
1047 
1048  if(!all_unique) {
1049  repeats.clear();
1050  for(Index j = 0; j < irrep_names.size(); j++) {
1051  for(Index k = 0; k < irrep_names.size(); k++) {
1052  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0) && (m_character_table[j][0].real() == m_character_table[k][0].real())) {
1053  int dim = int(m_character_table[k][0].real());
1054  if(!(repeats.contains(dim))) {
1055  repeats.push_back(dim);
1056  }
1057  }
1058  }
1059  }
1060  }
1061 
1062  // std::cout << "Repeats in ..." << repeats << "\n";
1063  //std::cout << "STEP 2: Name according to sigma_v or C2p symmetry...\n";
1064  //Check for symmetry wrt vertical mirror plane or C2' axis...
1065  //I don't know how to do this for non-1D representations =(
1066 
1067 
1068  for(Index i = 0; i < irrep_names.size(); i++) {
1069  int C2ind = 0;
1070  if((sigma_v == true) && (!all_unique) && (repeats.contains(int(m_character_table[0][i].real()))) && (irrep_names[i] != "E") && (!cubic)) {
1071  sym_wrt_v = true;
1072  for(Index j = 0; j < conjugacy_classes.size() && sym_wrt_v; j++) {
1073  if(class_names[j].find("v") != std::string::npos) {
1074  if(class_names[j].find("'") != std::string::npos) {
1075  continue;
1076  }
1077  else if(m_character_table[i][j].real() < 0) {
1078  sym_wrt_v = false;
1079  }
1080  }
1081  }
1082  if(sym_wrt_v == true) {
1083  irrep_names[i].append("1");
1084  }
1085  else {
1086  irrep_names[i].append("2");
1087  }
1088  }
1089 
1090  else if((sigma_v != true) && (C2p == true) && (!all_unique) && (repeats.contains(int(m_character_table[0][i].real()))) && (irrep_names[i] != "E") && (!cubic)) {
1091  for(Index j = 0; j < conjugacy_classes.size() && sym_wrt_C2p; j++) {
1092  if(class_names[j].find("C2") != std::string::npos) {
1093  if((class_names[j].find("'") != std::string::npos) || (class_names[j].find("''") == std::string::npos)) {
1094  continue;
1095  }
1096  else if(m_character_table[i][j].real() < 0) {
1097  sym_wrt_C2p = false;
1098  }
1099  }
1100  }
1101  if(sym_wrt_C2p == true) {
1102  irrep_names[i].append("1");
1103  }
1104  else {
1105  irrep_names[i].append("2");
1106  }
1107  }
1108 
1109  else if(((sigma_v == true) || (C2p == true)) && (!all_unique) && (!cubic)) { //This only deals with 2d stuff...
1110  for(Index j = 0; j < conjugacy_classes.size(); j++) {
1111  if((class_names[j].find("C2") != std::string::npos) && (class_names[j].find("'") == std::string::npos) && !cubic) {
1112  C2ind = j;
1113  }
1114  }
1115  if(m_character_table[i][C2ind].real() > 0) {
1116  irrep_names[i].append("1");
1117  }
1118  else {
1119  irrep_names[i].append("2");
1120  }
1121  }
1122 
1123  else if(((sigma_v == true) || (C2p == true)) && (!all_unique) && (cubic)) {
1124  for(Index j = 0; j < conjugacy_classes.size(); j++) {
1125  if(((class_names[j].find("C2") != std::string::npos) && (class_names[j].find("'") != std::string::npos)) || (class_names[j].find("d") != std::string::npos)) {
1126  C2ind = j;
1127  }
1128  }
1129 
1130  if(m_character_table[i][C2ind].real() > 0) {
1131  if(irrep_names[i] == "A") {
1132  irrep_names[i].append("1");
1133  }
1134  else if(irrep_names[i] == "T") {
1135  irrep_names[i].append("2");
1136  }
1137  }
1138  else {
1139  if(irrep_names[i] == "A") {
1140  irrep_names[i].append("2");
1141  }
1142  else if(irrep_names[i] == "T") {
1143  irrep_names[i].append("1");
1144  }
1145  }
1146 
1147  }
1148 
1149  else if((name == "D2h") && (irrep_names[i] == "B")) {
1150  int c2z = 0, c2y = 0, c2x = 0;
1151  for(Index j = 0; j < conjugacy_classes.size(); j++) {
1152  if(class_names[j].find("(z)") != std::string::npos) {
1153  c2z = j;
1154  }
1155  else if(class_names[j].find("(y)") != std::string::npos) {
1156  c2y = j;
1157  }
1158  else if(class_names[j].find("(x)") != std::string::npos) {
1159  c2x = j;
1160  }
1161  }
1162 
1163  if(m_character_table[i][c2z].real() > 0) {
1164  irrep_names[i].append("1");
1165  }
1166  else if(m_character_table[i][c2y].real() > 0) {
1167  irrep_names[i].append("2");
1168  }
1169  else if(m_character_table[i][c2x].real() > 0) {
1170  irrep_names[i].append("3");
1171  }
1172  }
1173  }
1174 
1175  // This checks if each representation has a unique name...
1176  for(Index j = 0; j < irrep_names.size() && all_unique; j++) {
1177  for(Index k = 0; k < irrep_names.size() && all_unique; k++) {
1178  all_unique = true;
1179  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0)) {
1180  // std::cout << irrep_names[j] << " = " << irrep_names[k] << std::endl;
1181  all_unique = false;
1182  }
1183  }
1184  }
1185  // std::cout << irrep_names << std::endl;
1186  // std::cout << "Check if all of the current names are unique... " << all_unique << "\n";
1187 
1188  if(!all_unique) {
1189  repeats.clear();
1190  for(Index j = 0; j < irrep_names.size(); j++) {
1191  for(Index k = 0; k < irrep_names.size(); k++) {
1192  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0) && (m_character_table[j][0].real() == m_character_table[k][0].real())) {
1193  int dim = int(m_character_table[k][0].real());
1194  if(!(repeats.contains(dim))) {
1195  repeats.push_back(dim);
1196  }
1197  }
1198  }
1199  }
1200  }
1201  // std::cout << "Repeats in ..." << repeats << "\n";
1202  //std::cout << "STEP 3: Name according to inversion symmetry...\n";
1203  for(Index i = 0; i < irrep_names.size(); i++) {
1204  if((inversion == true) && (!all_unique) && (repeats.contains(int(m_character_table[0][i].real())))) {
1205  sym_wrt_inv = true;
1206  for(Index j = 0; j < conjugacy_classes.size() && sym_wrt_inv; j++) {
1207  if((class_names[j].find("i") != std::string::npos) && (m_character_table[i][j].real() < 0)) {
1208  sym_wrt_inv = false;
1209  }
1210  }
1211 
1212  if(sym_wrt_inv == true) {
1213  irrep_names[i].append("g");
1214  }
1215  else {
1216  irrep_names[i].append("u");
1217  }
1218  }
1219  }
1220 
1221  for(Index j = 0; j < irrep_names.size() && all_unique; j++) {
1222  for(Index k = 0; k < irrep_names.size() && all_unique; k++) {
1223  all_unique = true;
1224  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0)) {
1225  // std::cout << irrep_names[j] << " = " << irrep_names[k] << std::endl;
1226  all_unique = false;
1227  }
1228  }
1229  }
1230  // std::cout << irrep_names << std::endl;
1231  //std::cout << "Check if all of the current names are unique... " << all_unique << "\n";
1232 
1233  if(!all_unique) {
1234  repeats.clear();
1235  for(Index j = 0; j < irrep_names.size(); j++) {
1236  for(Index k = 0; k < irrep_names.size(); k++) {
1237  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0) && (m_character_table[j][0].real() == m_character_table[k][0].real())) {
1238  int dim = int(m_character_table[k][0].real());
1239  if(!(repeats.contains(dim))) {
1240  repeats.push_back(dim);
1241  }
1242  }
1243  }
1244  }
1245  }
1246 
1247  // std::cout << "Repeats in ..." << repeats << "\n";
1248 
1249  // std::cout << "STEP 4: Name according to sigma_h symmetry...\n";
1250  for(Index i = 0; i < irrep_names.size(); i++) {
1251  if((sigma_h == true) && (!all_unique) && (repeats.contains(int(m_character_table[0][i].real())))) {
1252  for(Index j = 0; j < conjugacy_classes.size() && sym_wrt_h; j++) {
1253  if((class_names[j].find("h") != std::string::npos) && (m_character_table[i][j].real() < 0)) {
1254  sym_wrt_h = false;
1255  }
1256  }
1257 
1258  if(sym_wrt_h == true) {
1259  irrep_names[i].append("'");
1260  }
1261  else {
1262  irrep_names[i].append("''");
1263  }
1264  }
1265  }//Close loop over representations
1266 
1267  // std::cout << "At the very end, check if everything is unique...\n";
1268 
1269  all_unique = true;
1270  for(Index j = 0; j < irrep_names.size() && all_unique; j++) {
1271  for(Index k = 0; k < irrep_names.size() && all_unique; k++) {
1272  all_unique = true;
1273  if((j != k) && (irrep_names[j].compare(irrep_names[k]) == 0)) {
1274  std::cout << irrep_names[j] << " = " << irrep_names[k] << std::endl;
1275  all_unique = false;
1276  }
1277  }
1278  }
1279 
1280  // std::cout << "Check if all of the current names are unique... " << all_unique << "\n";
1281 
1282  if(!all_unique) {
1283  std::cerr << "WARNING: Failed to name all irreps uniquely... \n";
1284  }
1285 
1286  std::cout << irrep_names << std::endl;
1287 
1288  return;
1289  }
1290 
1291 
1292 
1293  //*******************************************************************************************
1308  void SymGroup::_generate_class_names() const { //AAB
1309  if(!conjugacy_classes.size()) {
1311  }
1312 
1314 
1315  Array<Eigen::Vector3d > highsym_axes;
1316  Array<int> mult;
1317  double angle = 360;
1318  double dprod;
1319  std::string symtype;
1320  Index to_name = conjugacy_classes.size();
1321  bool cubic = false;
1322 
1323  if((name == "T") || (name == "Td") || (name == "Th") || (name == "O") || (name == "Oh")) {
1324  cubic = true;
1325  }
1326 
1327  class_names[0] = "E";
1328  to_name--;
1329 
1330  std::vector<SymInfo> info;
1331  for(Index i = 0; i < size(); i++)
1332  info.push_back(SymInfo(at(i), lattice()));
1333 
1334  //If the SymGroup includes an inversion operation, name it "i"
1335  //We can just use back() here because inversion is always last
1336  if(info.back().op_type == symmetry_type::inversion_op) {
1337  class_names[class_names.size() - 1] = "i";
1338  to_name--;
1339  }
1340 
1341  if(to_name == 0) {
1342  return;
1343  }
1344 
1345  // C1h contains only the identity element and sigma h mirror plane, so we deal with this specifically...
1346  if(name == "C1h") {
1347  class_names[1] = "h";
1348  to_name--;
1349  }
1350 
1351  if(to_name == 0) {
1352  return;
1353  }
1354 
1355  // This part will loop over all of the proper rotations and look for the axis of highest rotational
1356  // symmetry, which we are going to call the "principal axis."
1357  highsym_axes.clear();
1358  mult.clear();
1359 
1360  for(Index i = 0; i < size(); i++) {
1361  if((info[i].op_type == symmetry_type::rotation_op) || (info[i].op_type == symmetry_type::screw_op)) {
1362  if(highsym_axes.contains(info[i].axis.cart())) { //Otherwise, check if the axis has been found;
1363  mult[highsym_axes.find(info[i].axis.cart())]++;
1364  }
1365  else {
1366  highsym_axes.push_back(info[i].axis.cart());
1367  mult.push_back(1);
1368  }
1369  }
1370  }
1371 
1372  Eigen::Vector3d hs_axis;
1373  bool hs_axis_set = false;
1374  double hangle = 360;
1375 
1376  for(Index i = 0; i < size(); i++) {
1377  if((info[i].angle < hangle) && (info[i].angle > TOL)) {
1378  hangle = info[i].angle;
1379  hs_axis = info[i].axis.cart();
1380  hs_axis_set = true;
1381  }
1382  }
1383 
1384 
1385  int order = mult.max();
1386 
1387  for(int i = (int(mult.size()) - 1); i >= 0; i--) {
1388  if((cubic == false) && (mult[i] != order)) {
1389  highsym_axes.remove(i);
1390  mult.remove(i);
1391  }
1392  else if(mult[i] < (order - 1)) {
1393  highsym_axes.remove(i);
1394  mult.remove(i);
1395  }
1396  }
1397 
1398  //This is kind of a hack... We might want to change this so that the principal axis
1399  //is actually the one with the highest fold rotation, and then treat cases like
1400  //cubic and orthorhombic separately....... but I am not sure that's better...
1401 
1402  if(name == "D3d") {
1403  for(int i = int(mult.size()) - 1; i >= 0; i--) {
1404  if(!hs_axis_set) {
1405  throw std::runtime_error("Error in _generate_class_names: using hs_axis unitialized");
1406  }
1407  if(!almost_zero(highsym_axes[i] - hs_axis)) {
1408  highsym_axes.remove(i);
1409  mult.remove(i);
1410  }
1411  }
1412  }
1413 
1414  int ind = 0;
1415 
1416  std::string cprime = "'";
1417  std::string sprime = "'";
1418  std::string vprime = "";
1419 
1420  for(Index i = 0; i < size(); i++) { //Loop over all SymOps
1421  //ind = conjugacy_corr_table[i][0];
1422  ind = index2conjugacy_class[i];
1423  std::ostringstream s;
1424  if(!class_names[ind].size()) { //Check to see if this has already been named
1425  bool normal = false;
1426  for(Index j = 0; j < highsym_axes.size(); j++) {
1427  dprod = highsym_axes[j].dot(info[i].axis.const_cart());
1428  Eigen::Vector3d xprodvec = highsym_axes[j].cross(info[i].axis.const_cart());
1429  if(almost_zero(xprodvec.norm())) {
1430  normal = true;
1431  break;
1432  }
1433  }
1434 
1435  if(normal) { //Check if the cross product with principal axis is zero
1436  if((info[i].angle < 200) && (info[i].angle > 1)) { //Only bother with angles that 360 is divisible by
1437  if((info[i].op_type == symmetry_type::rotation_op) || (info[i].op_type == symmetry_type::screw_op)) {
1438  angle = info[i].angle;
1439  symtype = "C";
1440  s << conjugacy_classes[ind].size() << symtype << int(360 / angle);
1441  class_names[ind] = s.str();
1442  to_name--;
1443  //std::cout << " rotation\t==> " << s.str() << std::endl;
1444  }
1445  else if(info[i].op_type == symmetry_type::rotoinversion_op) {
1446  angle = info[i].angle;
1447  symtype = "S";
1448  s << conjugacy_classes[ind].size() << symtype << int(360 / angle);
1449  class_names[ind] = s.str();
1450  to_name--;
1451  //std::cout << " rotoinversion\t==> " << s.str() << std::endl;
1452  }
1453  }
1454  else if((info[i].op_type == symmetry_type::mirror_op) || (info[i].op_type == symmetry_type::glide_op)) {
1455  symtype = "h";
1456  s << conjugacy_classes[ind].size() << symtype;
1457  class_names[ind] = s.str();
1458  to_name--;
1459  //std::cout << " mirror\t==> " << s.str() << std::endl;
1460  }
1461  }
1462  else {
1463  if((info[i].angle < 200) && (info[i].angle > 1)) {
1464  if((info[i].op_type == symmetry_type::rotation_op) || (info[i].op_type == symmetry_type::screw_op)) {
1465  angle = info[i].angle;
1466  symtype = "C";
1467  s << conjugacy_classes[ind].size() << symtype << int(360 / angle) << cprime;
1468  class_names[ind] = s.str();
1469  to_name--;
1470  //std::cout << " rotation\t==> " << s.str() << std::endl;
1471  cprime = "''";
1472  }
1473  else if(info[i].op_type == symmetry_type::rotoinversion_op) {
1474  angle = info[i].angle;
1475  symtype = "S";
1476  s << conjugacy_classes[ind].size() << symtype << int(360 / angle) << sprime;
1477  class_names[ind] = s.str();
1478  to_name--;
1479  //std::cout << " rotoinversion\t==> " << s.str() << std::endl;
1480  sprime = "''";
1481  }
1482  }
1483  else if((info[i].op_type == symmetry_type::mirror_op) || (info[i].op_type == symmetry_type::glide_op)) {
1484  if(almost_zero(dprod)) {
1485  symtype = "v";
1486  s << conjugacy_classes[ind].size() << symtype << vprime;
1487  class_names[ind] = s.str();
1488  to_name--;
1489  vprime = "'";
1490  }
1491  else {
1492  symtype = "d";
1493  s << conjugacy_classes[ind].size() << symtype;
1494  class_names[ind] = s.str();
1495  to_name--;
1496  }
1497  //std::cout << " mirror\t==> " << s.str() << std::endl;
1498  }
1499  }
1500  }
1501  }
1502 
1503  for(Index i = 0; i < class_names.size(); i++) {
1504  std::string one = "1";
1505  if(class_names[i].find(one) != std::string::npos) {
1506  class_names[i].erase(class_names[i].find(one), 1);
1507  }
1508  }
1509 
1510  //This is also a hack... but again, not sure there is a better way...
1511 
1512  if(name == "D2h") {
1513  for(Index i = 0; i < conjugacy_classes.size(); i++) {
1514  if(info[conjugacy_classes[i][0]].op_type == symmetry_type::rotation_op) {
1515  if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitX())) {
1516  class_names[i].append("(x)");
1517  }
1518  else if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitY())) {
1519  class_names[i].append("(y)");
1520  }
1521  else if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitY())) {
1522  class_names[i].append("(z)");
1523  }
1524  }
1525  else if(info[conjugacy_classes[i][0]].op_type == symmetry_type::mirror_op) {
1526  if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitX())) {
1527  class_names[i].append("(yz)");
1528  }
1529  else if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitY())) {
1530  class_names[i].append("(xz)");
1531  }
1532  else if(almost_zero(info[conjugacy_classes[i][0]].axis.const_cart() - Eigen::Vector3d::UnitZ())) {
1533  class_names[i].append("(xy)");
1534  }
1535  }
1536  }
1537  }
1538 
1539 
1540  return;
1541  }
1542 
1543 
1544  //*******************************************************************************************
1545  void SymGroup::print_character_table(std::ostream &stream) {
1548 
1549  stream.precision(3);
1550 
1551  for(Index i = 0; i < m_character_table.size(); i++) {
1552  for(Index j = 0; j < m_character_table.size(); j++) {
1553  //This part cleans up the numbers in the table...
1554  if(almost_zero(std::abs(m_character_table[i][j].real()) - 0.5)) {
1555  if(m_character_table[i][j].real() > 0) {
1556  m_character_table[i][j] = std::complex<double>(0.5, m_character_table[i][j].imag());
1557  }
1558  else {
1559  m_character_table[i][j] = std::complex<double>(-0.5, m_character_table[i][j].imag());
1560  }
1561  }
1562  if(almost_zero(std::abs(m_character_table[i][j].imag()) - 0.5)) {
1563  if(m_character_table[i][j].imag() > 0) {
1564  m_character_table[i][j] = std::complex<double>(m_character_table[i][j].real(), 0.5);
1565  }
1566  else {
1567  m_character_table[i][j] = std::complex<double>(m_character_table[i][j].real(), -0.5);
1568  }
1569  }
1570  if(almost_zero(std::abs(m_character_table[i][j].real()) - 0.866)) {
1571  if(m_character_table[i][j].real() > 0) {
1572  m_character_table[i][j] = std::complex<double>(0.866, m_character_table[i][j].imag());
1573  }
1574  else {
1575  m_character_table[i][j] = std::complex<double>(-0.866, m_character_table[i][j].imag());
1576  }
1577  }
1578  if(almost_zero(std::abs(m_character_table[i][j].imag()) - 0.866)) {
1579  if(m_character_table[i][j].imag() > 0) {
1580  m_character_table[i][j] = std::complex<double>(m_character_table[i][j].real(), 0.866);
1581  }
1582  else {
1583  m_character_table[i][j] = std::complex<double>(m_character_table[i][j].real(), -0.866);
1584  }
1585  }
1586  if(almost_zero(m_character_table[i][j].real())) {
1587  m_character_table[i][j] = std::complex<double>(0, m_character_table[i][j].imag());
1588  }
1589  if(almost_zero(m_character_table[i][j].imag())) {
1590  m_character_table[i][j] = std::complex<double>(m_character_table[i][j].real(), 0);
1591  }
1592  }
1593  }
1594 
1595  // This will check if there are any non-integer values in the table.
1596  bool all_int = true;
1597  for(Index i = 0; i < m_character_table.size(); i++) {
1598  for(Index j = 0; j < m_character_table.size(); j++) {
1599  if(!almost_zero(m_character_table[i][j].real() - floor(m_character_table[i][j].real()))) {
1600  all_int = false;
1601  }
1602  if(!almost_zero(m_character_table[i][j].imag() - floor(m_character_table[i][j].imag()))) {
1603  all_int = false;
1604  }
1605  }
1606  }
1607 
1608  //This loop is for printing integer-only tables.
1609  if(all_int) {
1610  stream << " ";
1611  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1612  stream << "-------";
1613  }
1614  stream << "\n| Group: " << name << " (" << comment << ")";
1615  int space = (7 * (m_character_table.size() + 1)) - (name.size() + comment.size() + 11);
1616  for(int i = 0; i < space; i++) {
1617  stream << " ";
1618  }
1619 
1620  stream << "|\n ";
1621  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1622  stream << "-------";
1623  }
1624  stream << std::endl;
1625 
1626  stream << "| |";
1627  for(Index i = 0; i < m_character_table.size(); i++) {
1628  double whitespace = (6 - double(class_names[i].size())) / 2.0;
1629  double leftspace = ceil(whitespace);
1630  double rightspace = floor(whitespace);
1631  for(int j = 0; j < int(leftspace); j++) {
1632  stream << " ";
1633  }
1634  stream << class_names[i];
1635  for(int j = 0; j < int(rightspace); j++) {
1636  stream << " ";
1637  }
1638  stream << "|";
1639 
1640  }
1641  stream << std::endl;
1642 
1643  stream << " ";
1644  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1645  stream << "-------";
1646  }
1647  stream << std::endl;
1648 
1649  for(Index i = 0; i < m_character_table.size(); i++) {
1650  stream << "|";
1651  double whitespace = (7 - double(irrep_names[i].size())) / 2.0;
1652  double leftspace = ceil(whitespace);
1653  double rightspace = floor(whitespace);
1654  for(int j = 0; j < int(leftspace); j++) {
1655  stream << " ";
1656  }
1657  stream << irrep_names[i];
1658  for(int j = 0; j < int(rightspace); j++) {
1659  stream << " ";
1660  }
1661  stream << "|";
1662 
1663  for(Index j = 0; j < m_character_table.size(); j++) {
1664  //This will make sure the positive and negative numbers align
1665  if(m_character_table[i][j].real() > 0) {
1666  stream << " ";
1667  }
1668  else if(m_character_table[i][j].real() < 0) {
1669  stream << " ";
1670  }
1671  else {
1672  if(m_character_table[i][j].imag() >= 0) {
1673  stream << " ";
1674  }
1675  else {
1676  stream << " ";
1677  }
1678  } //End alignment of negative numbers
1679 
1680  if((m_character_table[i][j].imag() > 0) && (m_character_table[i][j].real() > 0)) {
1681  if(m_character_table[i][j].imag() == 1) {
1682  stream << int(m_character_table[i][j].real()) << "i" << " |";
1683  }
1684  else {
1685  stream << int(m_character_table[i][j].real()) << "+" << int(m_character_table[i][j].imag()) << "i" << " |";
1686  }
1687  }
1688  else if((m_character_table[i][j].imag() < 0) && (m_character_table[i][j].real() > 0)) {
1689  if(m_character_table[i][j].imag() == -1) {
1690  stream << int(m_character_table[i][j].real()) << "-" << "i" << " |";
1691  }
1692  else {
1693  stream << int(m_character_table[i][j].real()) << int(m_character_table[i][j].imag()) << "i" << " |";
1694  }
1695  }
1696  else if(m_character_table[i][j].imag() == 0) {
1697  stream << int(m_character_table[i][j].real()) << " |";
1698  }
1699  else if(m_character_table[i][j].real() == 0) {
1700  if(m_character_table[i][j].imag() == 1) {
1701  stream << "i" << " |";
1702  }
1703  else if(m_character_table[i][j].imag() == -1) {
1704  stream << "-" << "i" << " |";
1705  }
1706  else {
1707  stream << int(m_character_table[i][j].imag()) << "i" << " |";
1708  }
1709  }
1710  } //Closes loop over j
1711  stream << std::endl;
1712  } //Closes loop over i
1713 
1714  //Prints the line at the bottom of the table
1715  stream << " ";
1716  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1717  stream << "-------";
1718  }
1719  stream << std::endl;
1720  }//Closes the if all_int loop
1721 
1722  //If not everything is an integer, we use this loop instead...
1723  else {
1724  stream << " ";
1725  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1726  stream << "----------------";
1727  }
1728 
1729  stream << "\n| Group: " << name << " (" << comment << ")";
1730  int space = (16 * (m_character_table.size() + 1)) - (name.size() + comment.size() + 12);
1731  for(int i = 0; i < space; i++) {
1732  stream << " ";
1733  }
1734 
1735  stream << "|\n ";
1736 
1737  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1738  stream << "----------------";
1739  }
1740  stream << std::endl;
1741 
1742  stream << "| |";
1743  for(Index i = 0; i < m_character_table.size(); i++) {
1744  double whitespace = (15 - double(class_names[i].size())) / 2.0;
1745  double leftspace = ceil(whitespace);
1746  double rightspace = floor(whitespace);
1747  for(int j = 0; j < int(leftspace); j++) {
1748  stream << " ";
1749  }
1750  stream << class_names[i];
1751  for(int j = 0; j < int(rightspace); j++) {
1752  stream << " ";
1753  }
1754  stream << "|";
1755  }
1756  stream << std::endl;
1757  stream << " ";
1758  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1759  stream << "----------------";
1760  }
1761  stream << std::endl;
1762 
1763  for(Index i = 0; i < m_character_table.size(); i++) {
1764  stream << "|";
1765  double whitespace = (15 - double(irrep_names[i].size())) / 2.0;
1766  double leftspace = ceil(whitespace);
1767  double rightspace = floor(whitespace);
1768  for(int j = 0; j < int(leftspace); j++) {
1769  stream << " ";
1770  }
1771  stream << irrep_names[i];
1772  for(int j = 0; j < int(rightspace); j++) {
1773  stream << " ";
1774  }
1775  stream << "|";
1776 
1777  for(Index j = 0; j < m_character_table.size(); j++) {
1778  //This will make sure the positive and negative numbers align
1779  if(m_character_table[i][j].real() > 0) {
1780  stream << " ";
1781  }
1782  else if(m_character_table[i][j].real() < 0) {
1783  stream << " ";
1784  }
1785  else {
1786  if(m_character_table[i][j].imag() >= 0) {
1787  stream << " ";
1788  }
1789  else {
1790  stream << " ";
1791  }
1792  } //End alignment of negative numbers
1793 
1794  if((m_character_table[i][j].imag() > 0) && (m_character_table[i][j].real() > 0)) { //Long
1795  if(m_character_table[i][j].imag() == 1) {
1796  stream << m_character_table[i][j].real() << "i" << " |";
1797  }
1798  else {
1799  stream << m_character_table[i][j].real() << "+" << m_character_table[i][j].imag() << "i" << " |";
1800  }
1801  }
1802  else if((m_character_table[i][j].imag() < 0) && (m_character_table[i][j].real() > 0)) { //Long
1803  if(m_character_table[i][j].imag() == -1) {
1804  stream << m_character_table[i][j].real() << "-" << "i" << " |";
1805  }
1806  else {
1807  stream << m_character_table[i][j].real() << m_character_table[i][j].imag() << "i" << " |";
1808  }
1809  }
1810  else if(m_character_table[i][j].imag() == 0) { //Short
1811  stream << int(m_character_table[i][j].real()) << " |";
1812  }
1813  else if(m_character_table[i][j].real() == 0) { //Short
1814  if(m_character_table[i][j].imag() == 1) {
1815  stream << "i" << " |";
1816  }
1817  else if(m_character_table[i][j].imag() == -1) {
1818  stream << "-" << "i" << " |";
1819  }
1820  else {
1821  stream << m_character_table[i][j].imag() << "i" << " |";
1822  }
1823  }
1824  else if((m_character_table[i][j].imag() > 0) && (m_character_table[i][j].real() < 0)) { //Long
1825  stream << m_character_table[i][j].real() << "+" << m_character_table[i][j].imag() << "i" << " |";
1826  }
1827  else { //Long
1828  stream << m_character_table[i][j].real() << m_character_table[i][j].imag() << "i" << " |";
1829  }
1830 
1831  }//Closes the j loop
1832 
1833  //stream << "\n|-";
1834  //for(int x=0; x<m_character_table.size(); x++){
1835  // stream << "--------------|";
1836  //}
1837 
1838  stream << "\n";
1839  } //Closes the i loop
1840  stream << " ";
1841  for(Index i = 0; i < m_character_table.size() + 1; i++) {
1842  stream << "----------------";
1843  }
1844  stream << "\n";
1845  }//Closes else loop (non-integers)
1846 
1847  return;
1848  }
1849 
1850  //*******************************************************************************************
1851 
1862  if(!conjugacy_classes.size()) {
1864  }
1865 
1866  bool all_commute = true;
1867 
1869 
1870  for(Index i = 0; i < conjugacy_classes.size(); i++) {
1871  all_commute = true;
1872  for(Index j = 0; j < conjugacy_classes[i].size() && all_commute; j++) {
1873  for(Index k = 0; k < multi_table.size(); k++) {
1874  int ind = conjugacy_classes[i][j];
1875  if((multi_table[ind][k] == multi_table[k][ind]) && (!centralizer_table[i].contains(k))) {
1877  }
1878  else {
1879  all_commute = false;
1880  }
1881  }
1882  }
1883  }
1884  }
1885 
1886  //*******************************************************************************************
1887 
1889  //std::cout << "Calculating character table of SymGroup " << this << '\n';
1890  if(!size()) {
1891  std::cerr << "WARNING: This is an empty group. It has no character.\n";
1892  name = "EMPTY";
1893  return;
1894  }
1895 
1896  int sigma_h_ind = 0;
1897  Index d1 = 0, d2 = 0, d3 = 0;
1898  Index order1 = 0, order2 = 0;
1899  bool mirror(false);
1900  bool inversion(false);
1901  SymGroup subgroup;
1902  subgroup.m_lat_ptr = m_lat_ptr;
1903 
1904  std::vector<SymInfo> info;
1905  for(Index i = 0; i < size(); i++)
1906  info.push_back(SymInfo(at(i), lattice()));
1907 
1908  if(!size() || get_multi_table().size() != size() || !valid_index(get_multi_table()[0][0])) {
1909  std::cerr << "WARNING: This is not a group!!!\n";
1910  name = "NG";
1911  return;
1912  }
1913 
1915  //_generate_conjugacy_corr_table();
1917 
1918  Index h = multi_table.size(); //This is the dimensionality of the group.
1919  Index nc = conjugacy_classes.size(); //This is the number of conjugacy classes, which is also the number of irreducible representations.
1920 
1921  m_character_table.resize(nc, Array<std::complex<double> >(nc, -7));
1922  complex_irrep.resize(nc, false);
1923  irrep_IDs.resize(nc);
1924 
1925 
1933  for(Index i = 0; i <= nc; i++) {
1934  for(Index j = 0; j <= nc; j++) {
1935  for(Index k = 0; k <= nc; k++) {
1936  if(((1 * i + 4 * j + 9 * k) == h) && ((i + j + k) == nc) && (d1 >= d2) && (d1 >= d3)) {
1937  d1 = i;
1938  d2 = j;
1939  d3 = k;
1940  }
1941  }
1942  }
1943  }
1944 
1945  // Set characters related to identity
1946  for(Index i = 0; i < m_character_table.size(); i++) {
1947  m_character_table[0][i] = 1;
1948  if(i < d1) {
1949  m_character_table[i][0] = 1;
1950  }
1951  else if(i >= d1 && i < (d1 + d2)) {
1952  m_character_table[i][0] = 2;
1953  }
1954  else {
1955  m_character_table[i][0] = 3;
1956  }
1957  }
1958 
1959  // count over all possible 1d representations
1960  Counter<CASM::Array<int> > count(Array<int>(nc, -1), Array<int>(nc, 1), Array<int>(nc, 2));
1961  int sum = 0;
1962  order1 = 1;
1963  std::complex<double> ortho = 0;
1964  int telem = 0, elem1 = 0, elem2 = 0;
1965  int try1 = 0, try2 = 0;
1966 
1967  int try1_ind = 0, try2_ind = 0;
1968  Array<bool> check(nc, false);
1969 
1970 
1971  do {
1972  sum = 0;
1973  ortho = 0;
1974  bool satisfy_multi_table = true;
1975 
1976  if(count[0] == 1) {
1977 
1978  for(Index i = 0; i < nc; i++) {
1979  sum += count[i] * conjugacy_classes[i].size();
1980  check[i] = true;
1981  }
1982 
1983  if(sum == 0) {
1984  for(Index i = 0; i < nc && check[i]; i++) {
1985  for(Index j = 0; j < nc && check[i]; j++) {
1986  telem = (count[i] * count[j]);
1987 
1988  for(Index x = 0; x < conjugacy_classes[i].size() && check[i]; x++) {
1989  for(Index y = 0; y < conjugacy_classes[j].size() && check[i]; y++) {
1990  elem1 = conjugacy_classes[i][x];
1991  elem2 = conjugacy_classes[j][y];
1992 
1993  try1 = multi_table[elem1][elem2];
1994  try2 = multi_table[elem2][elem1];
1995 
1996  //try1_ind = conjugacy_corr_table[0][try1];
1997  //try2_ind = conjugacy_corr_table[0][try2];
1998  try1_ind = index2conjugacy_class[try1];
1999  try2_ind = index2conjugacy_class[try2];
2000 
2001  if((telem != count[try1_ind]) || (telem != count[try2_ind])) {
2002  check[i] = false;
2003  break;
2004  }
2005  }
2006  }
2007  }
2008  }
2009 
2010  Array<std::complex<double> > ortharray;
2011 
2012  for(Index i = 0; i < nc; i++) {
2013  ortho = 0.0;
2014  ortharray.resize(0);
2015  for(Index j = 0; j < nc; j++) {
2016  if((m_character_table[i][1].real() != -7) && (m_character_table[i][1].imag() != 0)) {
2017  std::complex<double> temp = std::complex<double>(double(count[j] * conjugacy_classes[j].size()), 0.0);
2018  ortho += (temp * m_character_table[i][j]);
2019  }
2020  ortharray.push_back(ortho);
2021  }
2022  }
2023 
2024  ortho = 0;
2025 
2026  for(Index i = 0; i < ortharray.size(); i++) {
2027  if(!almost_zero(ortharray[i])) {
2028  ortho = std::complex<double>(1, 1);
2029  }
2030  }
2031 
2032  Index wtf = 0;
2033  for(Index i = 0; i < nc; i++) {
2034  wtf += check[i];
2035  }
2036 
2037  if(wtf == nc) {
2038  satisfy_multi_table = true;
2039  }
2040  else {
2041  satisfy_multi_table = false;
2042  }
2043 
2044  if((ortho == std::complex<double>(0.0, 0.0)) && (satisfy_multi_table == true)) {
2045  for(Index i = 1; i < nc; i++) {
2046  if(order1 < nc) {
2047  m_character_table[order1][i] = count[i];
2048  }
2049  }
2050  order1++;
2051  }
2052  }
2053  }
2054  }
2055  while(++count);
2056 
2057  if(order1 == nc) {
2073  if(nc == 1) {
2074  name = "C1";
2075  latex_name = "C_1";
2076  comment = "#1";
2077  return;
2078  }
2079  else if(nc == 8) {
2080  name = "D2h";
2081  latex_name = "D_{2h}";
2082  comment = "#47-74";
2083  return;
2084  }
2085 
2086  bool inv = false;
2087  bool mir = false;
2088 
2089  for(Index i = 0; i < size(); i++) {
2090  if(info[i].op_type == symmetry_type::inversion_op) {
2091  inv = true;
2092  }
2093  else if(info[i].op_type == symmetry_type::mirror_op || info[i].op_type == symmetry_type::glide_op) {
2094  mir = true;
2095  }
2096  }
2097 
2098  if(nc == 2) {
2099  if(inv == true) {
2100  name = "S2"; //This can also be called Ci
2101  latex_name = "S_2";
2102  comment = "#2";
2103  }
2104  else if(mir == true) {
2105  name = "C1h"; //This can also be called S1 or Cs
2106  latex_name = "C_{1h}";
2107  comment = "#6-9";
2108  }
2109  else {
2110  name = "C2";
2111  latex_name = "C_2";
2112  comment = "#3-5";
2113  }
2114  return;
2115  }
2116 
2117  else if(nc == 4) {
2118  if(inv == true) {
2119  name = "C2h";
2120  latex_name = "C_{2h}";
2121  comment = "#10-15";
2122  }
2123  else if(mir == true) {
2124  name = "C2v";
2125  latex_name = "C_{2v}";
2126  comment = "#25-46";
2127  }
2128  else {
2129  name = "D2";
2130  latex_name = "D_2";
2131  comment = "#16-24";
2132  }
2133  return;
2134  }
2135 
2136  //You're done. Probably don't need to check column ortho, but why not?
2137  return;
2138  }
2139 
2140  if(!centralizer_table.size()) {
2142  }
2143 
2144  Array<std::complex<double> > centralizers(nc, 0);
2145 
2146  // std::cout << "----------------------------------------------------------------------------\n";
2147  for(Index i = 0; i < conjugacy_classes.size(); i++) {
2148  centralizers[i] = centralizer_table[i].size();
2149  // std::cout << centralizer_table[i].size() << "\t";
2150  }
2151  //std::cout << std::endl;
2152 
2153  //std::cout << "----------------------------------------------------------------------------\n";
2154  //std::cout << "----------------------------------------------------------------------------\n";
2155 
2156  //std::cout << order1 << std::endl;
2157 
2158  if(order1 != nc) {
2167  //We need to find sigma_h in our group so we can use it to hit stuff
2168 
2169  for(Index i = 0; i < size(); i++) {
2170  if(info[i].op_type == symmetry_type::inversion_op) {
2171  inversion = true;
2172  }
2173  else if((info[i].op_type == symmetry_type::mirror_op) || (info[i].op_type == symmetry_type::glide_op)) {
2174  //if(conjugacy_classes[conjugacy_corr_table[0][i]].size() == 1) {
2176  sigma_h_ind = i;
2177  mirror = true;
2178 
2179  }
2180  }
2181  }
2182  }
2183 
2192  if((inversion == false) && (mirror == false) && (d1 == conjugacy_classes.size())) {
2193 
2208  double angle = 360;
2209  int generator;
2210  bool generator_found = false;
2211 
2212  for(Index i = 0; i < size(); i++) {
2213  if((info[i].angle < angle) && (info[i].angle > TOL)) {
2214  angle = info[i].angle;
2215  generator = i;
2216  generator_found = true;
2217  }
2218  }
2219  if(!generator_found) {
2220  throw std::runtime_error("Error in _generate_character_table: generator not found");
2221  }
2222 
2228  angle = angle * (M_PI / 180);
2229 
2230  std::complex<double> iangle(cos(angle), sin(angle));
2231 
2233  Array<std::complex<double> > tcharconj;
2234  tchar.resize(nc);
2235  tcharconj.resize(nc);
2236  tchar[0] = std::complex<double>(1, 0);
2237  tchar[generator] = iangle;
2238 
2239  Index cycle = 0;
2240  Index ind2 = generator;
2241 
2242  while(order1 < nc) {
2243  std::complex<double> tangle = iangle;
2244  do {
2245  tangle *= iangle;
2246  tchar[multi_table[ind2][generator]] = tangle;
2247  ind2 = multi_table[ind2][generator];
2248  cycle++;
2249  }
2250  while(cycle < nc);
2251 
2258  for(Index i = 0; i < tchar.size(); i++) {
2259  tcharconj[i] = conj(tchar[i]);
2260  }
2261 
2262  for(Index i = 0; i < tchar.size(); i++) {
2263  if(std::abs(tchar[i].real()) < TOL) {
2264  tchar[i] = std::complex<double>(0, tchar[i].imag());
2265  tcharconj[i] = std::complex<double>(0, tcharconj[i].imag());
2266  }
2267  if(std::abs(tchar[i].imag()) < TOL) {
2268  tchar[i] = std::complex<double>(tchar[i].real(), 0);
2269  tcharconj[i] = std::complex<double>(tcharconj[i].real(), 0);
2270  }
2271  }
2272  m_character_table[order1] = tchar;
2273  complex_irrep[order1] = true;
2274  order1++;
2275  m_character_table[order1] = tcharconj;
2276  complex_irrep[order1] = true;
2277  order1++;
2278  iangle *= iangle;
2279  }
2280 
2281  // Should check for orthogonality here...
2282 
2283  if(nc == 3) {
2284  name = "C3";
2285  latex_name = "C_3";
2286  comment = "#143-146";
2287  }
2288  else if(nc == 4) {
2289  bool inv = false;
2290  for(Index g = 0; g < size() && !inv; g++) {
2291  if(info[g].op_type == symmetry_type::rotoinversion_op) {
2292  inv = true;
2293  }
2294  }
2295 
2296  if(!inv) {
2297  name = "C4";
2298  latex_name = "C_4";
2299  comment = "#75-80";
2300  }
2301  else {
2302  name = "S4";
2303  latex_name = "S_4";
2304  comment = "81-82";
2305  }
2306  }
2307  else if(nc == 6) {
2308  name = "C6";
2309  latex_name = "C_6";
2310  comment = "#168-173";
2311  }
2312 
2313  return;
2314  }
2315 
2316  else if((mirror == true) || (inversion == true) /*&& (size()>3)*/) {
2330  for(Index i = 0; i < size(); i++) {
2331  if(at(i).matrix().determinant() > 0) {
2332  subgroup.push_back(at(i));
2333  }
2334  }
2335 
2343  subgroup._generate_character_table();
2344  subgroup._generate_class_names();
2345  //subgroup.print_character_table(std::cout);
2346 
2360  //std::cout << "Subgroup name is : " << subgroup.name << std::endl;
2361 
2362  for(Index i = 0; i < subgroup.size(); i++) { //Loop over subgroup SymOps
2363  for(Index j = 0; j < size(); j++) { //Loop over big group SymOps
2364  //This loop is for the normal SymOps
2365  if(almost_equal(subgroup[i].matrix(), at(j).matrix(), TOL)) {
2366  //Index ind_i = subgroup.conjugacy_corr_table[0][i];
2367  //Index ind_j = conjugacy_corr_table[0][j];
2368  Index ind_i = subgroup.index2conjugacy_class[i];
2369  Index ind_j = index2conjugacy_class[j];
2370 
2371  for(Index k = 0; k < subgroup.m_character_table.size(); k++) { //rows
2372  m_character_table[k][ind_j] = subgroup.m_character_table[k][ind_i];
2373  m_character_table[subgroup.m_character_table.size() + k][ind_j] = subgroup.m_character_table[k][ind_i];
2374  }
2375  }
2376  //This loop is for the inverted SymOps
2377  if(almost_equal(subgroup[i].matrix()*back().matrix(), at(j).matrix(), TOL) && inversion) {
2378  //Index ind_i = subgroup.conjugacy_corr_table[0][i];
2379  //Index ind_j = conjugacy_corr_table[0][j];
2380  Index ind_i = subgroup.index2conjugacy_class[i];
2381  Index ind_j = index2conjugacy_class[j];
2382  for(Index k = 0; k < subgroup.m_character_table.size(); k++) {
2383  m_character_table[k][ind_j] = subgroup.m_character_table[k][ind_i];
2384  m_character_table[subgroup.m_character_table.size() + k][ind_j] = -subgroup.m_character_table[k][ind_i];
2385  }
2386 
2387  if(nc == 12) {
2388  if(h == 12) {
2389  name = "C6h";
2390  latex_name = "C_{6h}";
2391  comment = "#175-176";
2392  }
2393  else if(h == 24) {
2394  name = "D6h";
2395  latex_name = "D_{6h}";
2396  comment = "#191-194";
2397  }
2398  }
2399  else if(nc == 10) {
2400  if(h == 16) {
2401  name = "D4h";
2402  latex_name = "D_{4h}";
2403  comment = "#123-142";
2404  }
2405  else if(h == 48) {
2406  name = "Oh";
2407  latex_name = "O_h";
2408  comment = "#221-230";
2409  }
2410  }
2411 
2412  else if(nc == 8) {
2413  if(subgroup.name == "C4") {
2414  name = "C4h";
2415  latex_name = "C_{4h}";
2416  comment = "#83-88";
2417  }
2418  else if(subgroup.name == "D2") {
2419  name = "D2h";
2420  latex_name = "D_{2h}";
2421  comment = "#47-74";
2422  }
2423  else if(subgroup.name == "T") {
2424  name = "Th";
2425  latex_name = "T_h";
2426  comment = "#200-206";
2427  }
2428  }
2429  else if(nc == 6) {
2430  if(subgroup.name == "D3") {
2431  name = "D3d";
2432  latex_name = "D_{3d}";
2433  comment = "#162-167";
2434  }
2435  else if(subgroup.name == "C3") {
2436  name = "S6";
2437  latex_name = "S_6";
2438  comment = "#147-148";
2439  }
2440  }
2441  }
2442 
2443  //This loop is for mirrored SymOps when there is no inversion
2444  //This only happens in groups C3h and D3h
2445 
2446  else if(almost_equal(subgroup[i].matrix()*at(sigma_h_ind).matrix(), at(j).matrix(), TOL) && mirror && !inversion) {
2447  //Index ind_i = subgroup.conjugacy_corr_table[0][i];
2448  //Index ind_j = conjugacy_corr_table[0][j];
2449  Index ind_i = subgroup.index2conjugacy_class[i];
2450  Index ind_j = index2conjugacy_class[j];
2451  for(Index k = 0; k < subgroup.m_character_table.size(); k++) {
2452  m_character_table[k][ind_j] = subgroup.m_character_table[k][ind_i];
2453  m_character_table[subgroup.m_character_table.size() + k][ind_j] = -subgroup.m_character_table[k][ind_i];
2454  }
2455 
2456  if(h == 12) {
2457  name = "D3h";
2458  latex_name = "D_{3h}";
2459  comment = "#187-190";
2460  }
2461  else if(h == 6) {
2462  name = "C3h";
2463  latex_name = "C_{3h}";
2464  comment = "#174";
2465  }
2466  }
2467  }
2468  }
2469 
2470  //You're done! Check orthogonality and centralizers just in case.
2471  return;
2472  }
2473 
2474  else if((d3 == 0) && (d2 > 0)) {
2475  Array<std::complex<double> > centrcheck;
2476  centrcheck.resize(nc);
2477 
2478  for(Index i = 0; i < d1; i++) {
2479  for(Index j = 0; j < m_character_table.size(); j++) {
2480  centrcheck[j] += (m_character_table[i][j] * m_character_table[i][j]);
2481  }
2482  }
2483 
2484  if(d2 == 1) {
2485  for(Index i = 1; i < nc; i++) {
2486  if(abs(centrcheck[i] - centralizers[i]) < TOL) {
2487  m_character_table[nc - 1][i] = std::complex<double>(0, 0);
2488  }
2489  else {
2490  //This is a bit of a hack, but it turns out to always be true...
2491  m_character_table[nc - 1][i] = -sqrt(abs(centrcheck[i] - centralizers[i]));
2492  }
2493  }
2494 
2495  bool mir = false;
2496  int mircount = 0;
2497 
2498  for(Index i = 0; i < size(); i++) {
2499  if(info[i].op_type == symmetry_type::mirror_op || info[i].op_type == symmetry_type::glide_op) {
2500  mir = true;
2501  mircount++;
2502  }
2503  }
2504 
2505  if(nc == 3) {
2506  if(mir == true) {
2507  name = "C3v";
2508  latex_name = "C_{3v}";
2509  comment = "#156-161";
2510  }
2511  else {
2512  name = "D3";
2513  latex_name = "D_3";
2514  comment = "#149-155";
2515  }
2516  }
2517  else if(nc == 5) {
2518  if(mircount == 0) {
2519  name = "D4";
2520  latex_name = "D_4";
2521  comment = "#89-98";
2522  }
2523  else if(mircount == 2) {
2524  name = "D2d";
2525  latex_name = "D_{2d}";
2526  comment = "#111-122";
2527  }
2528  else if(mircount == 4) {
2529  name = "C4v";
2530  latex_name = "C_{4v}";
2531  comment = "#99-110";
2532  }
2533  }
2534 
2535  //You're done! Check orthogonality just in case.
2536  return;
2537  }
2538 
2539  else if((d2 == 2) && !subgroup.size()) {
2540  Array<std::complex<double> > remainder;
2541  int nr = 0;
2542  remainder.resize(nc);
2543  for(Index i = 1; i < nc; i++) {
2544  if(abs(centrcheck[i] - centralizers[i]) < TOL) {
2545  for(Index j = 0; j < d2; j++) {
2546  m_character_table[(nc - j) - 1][i] = std::complex<double>(0, 0);
2547  remainder[i] = std::complex<double>(0, 0);
2548  }
2549  }
2550  else {
2551  remainder[i] = sqrt(abs(centralizers[i] - centrcheck[i]) / 2);
2552  nr++;
2553  }
2554  }
2555 
2556  Counter<CASM::Array<int> > signcount(Array<int>(nr, -1), Array<int>(nr, 1), Array<int> (nr, 2));
2558  order2 = (nc - d2);
2560 
2561  do {
2562  trow.resize(nc);
2563  int j = 0;
2564  for(Index i = 0; i < nc; i++) {
2565  if(m_character_table[nc - 1][i].real() != -7) {
2566  trow[i] = m_character_table[nc - 1][i];
2567  }
2568  if(m_character_table[order2][i].real() == -7) {
2569  trow[i] = signcount[j] * remainder[i].real();
2570  j++;
2571  }
2572  }
2573 
2574  int sum = 0;
2575 
2576  for(Index i = 0; i < nc; i++) {
2577  sum += trow[i].real() * conjugacy_classes[i].size();
2578  }
2579  double orthcheck;
2580  Array<double> ortharray;
2581  if(sum == 0) {
2582  ortharray.resize(0);
2583  for(Index i = 0; i < d1; i++) {
2584  orthcheck = 0.0;
2585  for(Index j = 0; j < nc; j++) {
2586  double temp = (trow[j].real()) * (conjugacy_classes[j].size());
2587  orthcheck += (temp * m_character_table[i][j].real());
2588  }
2589  ortharray.push_back(orthcheck);
2590  }
2591 
2592  ortho = 0;
2593 
2594  for(Index i = 0; i < ortharray.size(); i++) {
2595  if(!almost_zero(ortharray[i])) {
2596  ortho = std::complex<double>(1, 1);
2597  }
2598  }
2599  }
2600 
2601  if((sum == 0) && (ortho.real() == 0) && (ortho.imag() == 0)) {
2602  tset.push_back(trow);
2603  }
2604 
2605  }
2606  while(++signcount);
2607 
2608  if(tset.size() == d2) {
2609  for(Index i = 0; i < d2; i++) {
2610  m_character_table[order2 + i] = tset[i];
2611  }
2612  }
2613 
2614  bool mir = false;
2615  for(Index i = 0; i < size(); i++) {
2616  if(info[i].op_type == symmetry_type::mirror_op || info[i].op_type == symmetry_type::glide_op) {
2617  mir = true;
2618  }
2619  }
2620 
2621  if(mir == true) {
2622  name = "C6v";
2623  latex_name = "C_{6v}";
2624  comment = "#183-186";
2625  }
2626  else {
2627  name = "D6";
2628  latex_name = "D_6";
2629  comment = "#177-182";
2630  }
2631 
2632  //You're done. Check orthogonality of columns.
2633  return;
2634  }
2635  }
2636 
2637  else if(d3 != 0) {
2644  if(nc == 4) {
2645  name = "T";
2646  latex_name = "T";
2647  comment = "#195-199";
2648  complex_irrep[1] = true;
2649  complex_irrep[2] = true;
2650  std::complex<double> omega = std::complex<double>(-0.5, 0.866);
2651  for(Index j = 1; j < nc; j++) {
2652  if(conjugacy_classes[j].size() == 3) {
2653  m_character_table[1][j] = std::complex<double>(1, 0);
2654  m_character_table[2][j] = std::complex<double>(1, 0);
2655  m_character_table[3][j] = std::complex<double>(-1, 0);
2656  }
2657  else {
2658  m_character_table[1][j] = omega;
2659  m_character_table[2][j] = omega * omega;
2660  m_character_table[3][j] = std::complex<double>(0, 0);
2661  omega *= omega;
2662  }
2663  }
2664  }
2665 
2666  bool sigma_d = false;
2667 
2668  for(Index i = 0; i < size(); i++) {
2669  if((info[i].op_type == symmetry_type::mirror_op) || (info[i].op_type == symmetry_type::glide_op)) {
2670  sigma_d = true;
2671  }
2672  }
2673 
2674  if(sigma_d == true) {
2675  name = "Td";
2676  latex_name = "T_d";
2677  comment = "#215-220";
2678 
2679  for(Index j = 1; j < nc; j++) { //Loop over columns
2680  if(conjugacy_classes[j].size() == 3) {
2681  m_character_table[1][j] = std::complex<double>(1, 0);
2682  m_character_table[2][j] = std::complex<double>(2, 0);
2683  m_character_table[3][j] = std::complex<double>(-1, 0);
2684  m_character_table[4][j] = std::complex<double>(-1, 0);
2685  }
2686  else if(conjugacy_classes[j].size() == 8) {
2687  m_character_table[1][j] = std::complex<double>(1, 0);
2688  m_character_table[2][j] = std::complex<double>(-1, 0);
2689  m_character_table[3][j] = std::complex<double>(0, 0);
2690  m_character_table[4][j] = std::complex<double>(0, 0);
2691  }
2692  else if((conjugacy_classes[j].size() == 6) && (info[conjugacy_classes[j][0]].angle > 10)) {
2693  m_character_table[1][j] = std::complex<double>(-1, 0);
2694  m_character_table[2][j] = std::complex<double>(0, 0);
2695  m_character_table[3][j] = std::complex<double>(1, 0);
2696  m_character_table[4][j] = std::complex<double>(-1, 0);
2697  }
2698  else {
2699  m_character_table[1][j] = std::complex<double>(-1, 0);
2700  m_character_table[2][j] = std::complex<double>(0, 0);
2701  m_character_table[3][j] = std::complex<double>(-1, 0);
2702  m_character_table[4][j] = std::complex<double>(1, 0);
2703  }
2704  }
2705  }
2706 
2707  else if((sigma_d == false) && (nc == 5)) {
2708  name = "O";
2709  latex_name = "O";
2710  comment = "#207-214";
2711  for(Index j = 1; j < nc; j++) { //Loop over columns
2712  if(conjugacy_classes[j].size() == 3) {
2713  m_character_table[1][j] = std::complex<double>(1, 0);
2714  m_character_table[2][j] = std::complex<double>(2, 0);
2715  m_character_table[3][j] = std::complex<double>(-1, 0);
2716  m_character_table[4][j] = std::complex<double>(-1, 0);
2717  }
2718  else if(conjugacy_classes[j].size() == 8) {
2719  m_character_table[1][j] = std::complex<double>(1, 0);
2720  m_character_table[2][j] = std::complex<double>(-1, 0);
2721  m_character_table[3][j] = std::complex<double>(0, 0);
2722  m_character_table[4][j] = std::complex<double>(0, 0);
2723  }
2724  else if((conjugacy_classes[j].size() == 6) && (almost_equal(info[conjugacy_classes[j][0]].angle, 180.0))) {
2725  m_character_table[1][j] = std::complex<double>(-1, 0);
2726  m_character_table[2][j] = std::complex<double>(0, 0);
2727  m_character_table[3][j] = std::complex<double>(-1, 0);
2728  m_character_table[4][j] = std::complex<double>(1, 0);
2729  }
2730  else {
2731  m_character_table[1][j] = std::complex<double>(-1, 0);
2732  m_character_table[2][j] = std::complex<double>(0, 0);
2733  m_character_table[3][j] = std::complex<double>(1, 0);
2734  m_character_table[4][j] = std::complex<double>(-1, 0);
2735  }
2736  }
2737  }
2738  }
2739 
2740  return;
2741  }
2742 
2743  //*******************************************************************************************
2745 
2746  if(!get_multi_table().size()) {
2747  return;
2748  }
2749 
2751 
2752  for(Index i = 0; i < size(); i++) {
2754  }
2755 
2756  Index telem = 0;
2757  Index ind = 0;
2758 
2759  for(Index i = 0; i < size(); i++) {
2760  telem = i;
2761  ind = 0;
2762  do {
2763  telem = multi_table[i][telem];
2764  ind++;
2765  }
2766  while(telem != i);
2767  elem_order_table[i].push_back(ind);
2768  }
2769 
2770  return;
2771  }
2772 
2773 
2774 
2775  //*******************************************************************************************
2777  Array<Index>::X3 small_sgroups;
2778  int tempind = 0;
2779  Index i, j, k, l;
2780  if(get_alt_multi_table().size() != size()) {
2781  return small_sgroups;
2782  }
2783  // identity is a small subgroup
2784  small_sgroups.resize(1, Array<Index>::X2(1, Array<Index>(1, 0)));
2785 
2786  for(i = 1; i < multi_table.size(); i++) {
2787  Array<Index> tgroup;
2788  tgroup.push_back(0);
2789  j = i;
2790  while(j != 0) {
2791  if(!tgroup.contains(j)) {
2792  tgroup.push_back(j);
2793  }
2794  if(!tgroup.contains(alt_multi_table[j][0])) {
2795  tgroup.push_back(alt_multi_table[j][0]);
2796  }
2797  j = multi_table[i][j];
2798  }
2799 
2800  for(j = 0; j < small_sgroups.size(); j++) {
2801  for(k = 0; k < small_sgroups[j].size(); k++) {
2802  if(small_sgroups[j][k].size() == tgroup.size() && tgroup.all_in(small_sgroups[j][k]))
2803  break;
2804  }
2805  if(k < small_sgroups[j].size())
2806  break;
2807  }
2808  if(j < small_sgroups.size())
2809  continue;
2810 
2811  // use equiv_map to find the equivalent subgroups
2812  Array<Array<Index> > equiv_map(left_cosets(tgroup));
2813  small_sgroups.push_back(Array<Index>::X2());
2814  //push_back tgroup first
2815  small_sgroups.back().push_back(tgroup);
2816  for(k = 1; k < equiv_map.size(); k++) {
2817  small_sgroups.back().push_back(small_sgroups.back()[0]);
2818  for(l = 0; l < small_sgroups.back()[0].size(); l++) {
2819  tempind = ind_prod(small_sgroups.back()[0][l], ind_inverse(equiv_map[k][0]));
2820  small_sgroups.back()[k][l] = ind_prod(equiv_map[k][0], tempind);
2821  }
2822  }
2823  }
2824  return small_sgroups;
2825  }
2826 
2827  //*******************************************************************************************
2828  /* Start with m_subgroups = m_small_subgroups, then add new subgroups by
2829  * finding the closure of a union of a large_group and a
2830  * small_group. If the the new large_group is unique, add it as a
2831  * large_group. Repeat for all (large_group, small_group) pairs,
2832  * until no new m_subgroups are found. This is probably not the
2833  * fastest algorithm, but it is complete
2834  */
2835 
2837  Array<Index>::X3 small_subgroups = _small_subgroups();
2838 
2839  Index i, j, k, l, m, jj, iii, jjj;
2840 
2841  m_subgroups = small_subgroups;
2842 
2843 
2844  for(i = 0; i < m_subgroups.size(); i++) {
2845  //std::cout << "i is " << i << " and m_subgroups.size() is " << m_subgroups.size() << std::endl;
2846  for(j = 0; j < small_subgroups.size(); j++) {
2847  //for(ii=0; ii<m_subgroups[i].size(); ii++){
2848  for(jj = 0; jj < small_subgroups[j].size(); jj++) {
2849  Array<Index> tgroup1(m_subgroups[i][0]);
2850 
2851  // append unique elements from small_subgroups[j][jj]
2852  for(jjj = 0; jjj < small_subgroups[j][jj].size(); jjj++) {
2853  if(!tgroup1.contains(small_subgroups[j][jj][jjj])) {
2854  tgroup1.push_back(small_subgroups[j][jj][jjj]);
2855  }
2856  }
2857 
2858  // find group closure
2859  for(iii = 0; iii < tgroup1.size(); iii++) {
2860  for(jjj = 0; jjj < tgroup1.size(); jjj++) {
2861  if(!tgroup1.contains(multi_table[tgroup1[iii]][tgroup1[jjj]])) {
2862  tgroup1.push_back(multi_table[tgroup1[iii]][tgroup1[jjj]]);
2863  }
2864  }
2865  }
2866 
2867  for(k = 0; k < m_subgroups.size(); k++) {
2868  if(m_subgroups[k][0].size() == tgroup1.size()
2869  && which_unique_combination(tgroup1, m_subgroups[k]) != m_subgroups[k].size()) {
2870  break;
2871  }
2872  }
2873  //std::cout << " k is " << k << " and m_subgroups.size() is " << m_subgroups.size() << std::endl;
2874  if(k < m_subgroups.size())
2875  continue;
2876  // add the new group
2877  //std::cout << "tgroup1 " << tgroup1 << " is unique, so we add it " << std::endl;
2878  //std::cout << "tgroup2 " << tgroup2 << " yields all_in = " << tgroup1.all_in(tgroup2) << std::endl;
2879  Array<Index> tconj(tgroup1);
2880  m_subgroups.push_back(Array< Array<Index> >());
2881  for(l = 0; l < alt_multi_table.size(); l++) {
2882  for(m = 0; m < tgroup1.size(); m++) {
2883  iii = alt_multi_table[l][tgroup1[m]];
2884  tconj[m] = multi_table[iii][l];
2885  }
2886  if(which_unique_combination(tconj, m_subgroups.back()) == m_subgroups.back().size()) {
2887  tconj.sort();
2888  m_subgroups.back().push_back(tconj);
2889  }
2890 
2891  }
2892 
2893  }
2894  }
2895  }
2896 
2897  // Sort subgroups by number of elements and multiplicity
2898  for(Index i = 0; i < m_subgroups.size(); i++) {
2899  for(Index j = i + 1; j < m_subgroups.size(); j++) {
2900  if(m_subgroups[i][0].size() < m_subgroups[j][0].size())
2901  m_subgroups.swap_elem(i, j);
2902  if(m_subgroups[i][0].size() == m_subgroups[j][0].size()
2903  && m_subgroups[i].size() < m_subgroups[j].size())
2904  m_subgroups.swap_elem(i, j);
2905  }
2906  }
2907  return;
2908  }
2909 
2910  //*******************************************************************************************
2911 
2912 
2913  std::vector<SymGroup> SymGroup::unique_subgroups() const {
2914  if(!m_subgroups.size()) _generate_subgroups();
2915 
2916  Array<std::string> sg_names, sg_names_limited;
2917  Array<bool> chosen_flag(m_subgroups.size(), false);
2918  for(Index i = 0; i < m_subgroups.size(); i++) {
2919  SymGroup sgroup;
2920  sgroup.m_lat_ptr = m_lat_ptr;
2921  for(Index j = 0; j < m_subgroups[i][0].size(); j++) {
2922  sgroup.push_back(at(m_subgroups[i][0][j]));
2923  }
2924  sgroup._generate_character_table();
2925  sg_names.push_back(sgroup.name);
2926  std::cout << sgroup.name << "-" << i << " has equivalencies:\n";
2927  for(Index j = 0; j < m_subgroups[i].size(); j++) {
2928  std::cout << " " << m_subgroups[i][j] << "\n";
2929  }
2930  std::cout << "\n";
2931  }
2932 
2933  Array< Array< Index > > sg_tree(m_subgroups.size(), Array<Index>());
2934  for(Index i = 0; i < m_subgroups.size(); i++) {
2935  std::cout << "Subgroup " << sg_names[i] << "-" << i << " is also a subgroup of ";
2936  for(Index j = 0; j < m_subgroups.size(); j++) {
2937  for(Index jj = 0; jj < m_subgroups[j].size(); jj++) {
2938  if(m_subgroups[i][0].all_in(m_subgroups[j][jj])) {
2939  sg_tree[i].push_back(j);
2940  std::cout << sg_names[j] << "-" << j << "-" << jj << " ";
2941  break;
2942  }
2943  }
2944  }
2945  std::cout << "\n";
2946  }
2947 
2948  //attempt to maximize coincidence a
2949  int max_co, t_co;
2950  for(int i = int(m_subgroups.size()) - 1; i >= 0; i--) {
2951  if(chosen_flag[i]) continue;
2952  //chosen_flag[i]=true;
2953  for(int j = 0; j < i; j++) {
2954  if(sg_names[j] == sg_names[i]) {
2955  chosen_flag[j] = true;
2956  continue;
2957  }
2958  max_co = 0;
2959  for(Index jj = 0; jj < m_subgroups[j].size(); jj++) {
2960  t_co = m_subgroups[i][0].coincidence(m_subgroups[j][jj]);
2961  if(t_co > max_co) {
2962  max_co = t_co;
2963  m_subgroups[j].swap_elem(jj, 0);
2964  }
2965  }
2966  }
2967  }
2968 
2969  std::vector<SymGroup> unique_sgroups;
2970 
2971  for(Index i = 0; i < m_subgroups.size(); i++) {
2972  if(chosen_flag[i]) continue;
2973  unique_sgroups.push_back(SymGroup());
2974  for(Index ii = 0; ii < m_subgroups[i][0].size(); ii++) {
2975  unique_sgroups.back().push_back(at(m_subgroups[i][0][ii]));
2976  }
2977  unique_sgroups.back().sort();
2978  unique_sgroups.back()._generate_character_table();
2979 
2980  std::cout << "Added group " << unique_sgroups.back().name << " having bit-string " << m_subgroups[i][0] << std::endl;
2981  }
2982 
2983  return unique_sgroups;
2984  }
2985 
2986  //*******************************************************************************************
2993  //*******************************************************************************************
2994 
2996 
2997  if(get_multi_table().size() != size()) {
2998  return;
2999  }
3000 
3001 
3003 
3004  int k;
3005 
3006  for(Index i = 0; i < size(); i++) {
3007  bool dup_class(false);
3008  for(Index j = 0; j < conjugacy_classes.size(); j++) {
3009  if(conjugacy_classes[j].contains(i)) {
3010  dup_class = true;
3011  break;
3012  }
3013  }
3014  if(dup_class) continue;
3015 
3017 
3018  for(Index j = 0; j < size(); j++) {
3019  //std::cout << "for j=" << j << ", i=" << i << ": j-inverse= " << ind_inverse(j) << ", i*j-inverse= " << ind_prod(i, ind_inverse(j));
3020  //int tk=alt_multi_table[j][i];
3021  //std::cout << "-- compare to amt[j][0]=" << alt_multi_table[j][0] << " and amt[j][i]=" << tk << " and result is k=";
3022  k = ind_prod(j, ind_prod(i, ind_inverse(j)));
3023  //std::cout << k << " -- compare to explicit value " << multi_table[tk][j];
3024 
3025  if(!conjugacy_classes.back().contains(k)) {
3026  //std::cout << " so " << k << " goes in class " << conjugacy_classes.size()-1;
3028  }
3029  //std::cout << "\n";
3030  }
3031  std::sort(conjugacy_classes.back().begin(), conjugacy_classes.back().end());
3032  }
3033 
3035  for(Index i = 0; i < conjugacy_classes.size(); i++) {
3036  for(Index j = 0; j < conjugacy_classes[i].size(); j++) {
3038  }
3039  }
3040  //std::cout << "index2conjugacy is " << index2conjugacy_class << '\n';
3041  return;
3042  }
3043 
3044  //*******************************************************************************************
3045 
3047  if(get_alt_multi_table().size() != size() || !valid_index(i) || i >= size())
3048  return -1;
3049  //std::cout << "Inside ind_inverse. 'i' is " << i << " and alt_multi_table size is " << alt_multi_table.size() << "\n";
3050  return alt_multi_table[i][0];
3051  }
3052 
3053  //*******************************************************************************************
3054 
3056  if(get_multi_table().size() != size()
3057  || !valid_index(i) || i >= size()
3058  || !valid_index(j) || j >= size()) {
3059 
3060 
3061  //std::cerr << "WARNING: SymGroup::ind_prod() failed for " << i << ", " << j << "\n";// and multi_table\n" << get_multi_table() << "\n\n";
3062  //assert(0);
3063  return -1;
3064  }
3065  //std::cout << "Inside ind_prod. 'i' is " << i << " and j is " << j << " and multi_table size is " << multi_table.size() << "\n";
3066  return multi_table[i][j];
3067  }
3068 
3069  //*******************************************************************************************
3070 
3072  if(!get_conjugacy_classes().size() || i > size())
3073  return -1;
3074  return index2conjugacy_class[i];
3075  }
3076 
3077  //*******************************************************************************************
3078 
3080  assert((valid_index(i) && i < irrep_IDs.size()) && "Attempting to set ID for out-of-bounds irrep.");
3081  irrep_IDs[i] = ID;
3082  return;
3083 
3084  }
3085 
3086  //*******************************************************************************************
3087 
3089  if(!valid_index(i) || i >= irrep_IDs.size())
3090  return SymGroupRepID();
3091 
3092  return irrep_IDs[i];
3093 
3094  }
3095 
3096  //*******************************************************************************************
3097 
3099  if(!size() || !at(0).has_valid_master()) {
3100  std::cerr << "CRITICAL ERROR: In SymGroup::get_coord_rep_ID(), SymGroup is improperly initialized.\n"
3101  << " Exiting...\n";
3102  exit(1);
3103  }
3104 
3105  return at(0).master_group().coord_rep_ID();
3106  }
3107  //*******************************************************************************************
3108 
3110  if(!size() || !at(0).has_valid_master()) {
3111  std::cerr << "CRITICAL ERROR: In SymGroup::add_empty_representation(), SymGroup is improperly initialized.\n"
3112  << " Exiting...\n";
3113  exit(1);
3114  }
3115 
3117  }
3118 
3119  //*******************************************************************************************
3120 
3122  if(!size() || !valid_index(i) || i >= irrep_IDs.size())
3123  throw std::runtime_error(std::string("Cannot find irrep ") + std::to_string(i) + " in the current SymGroup\n");
3124 
3125  return at(0).master_group().representation(irrep_IDs[i]);
3126 
3127  }
3128 
3129  //*******************************************************************************************
3130  // The set of left cosets is identical to the equivalence_map formed by partitioning (*this) w.r.t. 'subgroup'
3132  return left_cosets(find_all_periodic(subgroup, tol));
3133  }
3134 
3135 
3136  //*******************************************************************************************
3137  // The set of left cosets is identical to the equivalence_map formed by partitioning (*this) w.r.t. 'subgroup'
3138  // This version is overloaded to take only the indices of the operations that form the subgroup
3140  assert(size() % subgroup_inds.size() == 0 && "In SymGroup::left_cosets(), left cosets must be generated by a subgroup of *this SymGroup.");
3141 
3142  Array<Index>::X2 tcosets;
3143  tcosets.reserve(size() / subgroup_inds.size());
3144  tcosets.push_back(subgroup_inds);
3145  if(tcosets.back().size() == 0) {
3146  std::cerr << "CRITICAL ERROR: In SymGroup::left_cosets(), could not find subgroup within *this group.\n"
3147  << " Exiting...\n";
3148  assert(0);
3149  exit(1);
3150  }
3151  Index j;
3152  for(Index i = 0; i < size(); i++) {
3153  for(j = 0; j < tcosets.size(); j++) {
3154  if(tcosets[j].contains(i))
3155  break;
3156  }
3157  if(j < tcosets.size()) continue;
3158  tcosets.push_back(Array<Index>(subgroup_inds.size()));
3159  for(j = 0; j < subgroup_inds.size(); j++) {
3160  tcosets.back()[j] = ind_prod(i, subgroup_inds[j]);
3161  //std::cout << "PROD " << i << ", " << j << " = " << tcosets.back()[j] << "\n";
3162  }
3163  }
3164  return tcosets;
3165  }
3166 
3167 
3168  //*******************************************************************************************
3169 
3171  if(multi_table.size() != size()) {
3172  //std::cout << "CALCULATING MULTI_TABLE for " << this << ": table size is " << multi_table.size() << " and group size is " << size() << "!!\n";
3174  }
3175  return multi_table;
3176  }
3177 
3178  //*******************************************************************************************
3179 
3181  if(alt_multi_table.size() != size()) {
3182  //std::cout << "CALCULATING ALT_MULTI_TABLE " << this << ": table size is " << alt_multi_table.size() << " and group size is " << size() << "!!\n";
3184  }
3185  return alt_multi_table;
3186  }
3187  //*******************************************************************************************
3188 
3192 
3193  }
3194 
3195  //*******************************************************************************************
3196 
3198  if(conjugacy_classes.size() != size())
3200  return conjugacy_classes;
3201  }
3202  //*******************************************************************************************
3203 
3205  if(!m_character_table.size())
3207  return complex_irrep;
3208  }
3209 
3210  //*******************************************************************************************
3211 
3213  if(!m_character_table.size())
3215  return m_character_table;
3216  }
3217 
3218  //*******************************************************************************************
3219 
3220  const std::string &SymGroup::get_name() const {
3221  if(!name.size()) {
3223  if(!name.size()) {
3224 
3225  std::cerr << "WARNING: In SymGroup::get_name(), unable to get symgroup type.\n";
3226  std::cerr << "group size is " << size() << '\n';
3227  name = "unknown";
3228  }
3229  }
3230 
3231  return name;
3232  }
3233 
3234  //*******************************************************************************************
3235 
3236  const std::string &SymGroup::get_latex_name() const {
3237  character_table();
3238  return latex_name;
3239  }
3240 
3241  //*******************************************************************************************
3242 
3244  if(!m_subgroups.size())
3246  return m_subgroups;
3247  }
3248 
3249  //*******************************************************************************************
3250 
3251  bool SymGroup::_generate_multi_table() const { //AAB
3252  Index i, j;
3254 
3255  for(i = 0; i < size(); i++) {
3256  for(j = 0; j < size(); j++) {
3257  multi_table[i][j] = find_periodic(at(i) * at(j));
3258  if(multi_table[i][j] >= size() || multi_table[i].find(multi_table[i][j]) != j) {
3259  // this is a hack (sort of). If find_periodic doesn't work, we try find_no trans, which *should* work.
3260  // In other words, we are using 'inuition' to determine that user doesn't really care about the translational aspects.
3261  // If our intuition is wrong, there will will probably be an obvious failure later.
3262  multi_table[i][j] = find_no_trans(at(i) * at(j));
3263  if(multi_table[i][j] >= size() || multi_table[i].find(multi_table[i][j]) != j) {
3264 
3265  //if(multi_table[i][j] >= size()) {
3266  //std::cout << "This SymGroup is not a group because the combination of at least two of its elements is not contained in the set.\n";
3267 
3268  //Returning a table of all 1's seems to make the most sense. This will prevent weird recursion from happening.
3269  std::cerr << "Failed to construc multiplication table! Table in progress:\n";
3270  for(Index m = 0; m < multi_table.size(); m++)
3271  std::cerr << multi_table[m] << "\n";
3272  std::cerr << "\n";
3274  //multi_table.clear();
3275  return false;
3276  }
3277  }
3278  }
3279  }
3280 
3281  return true;
3282 
3283  }
3284 
3285  //*******************************************************************************************
3287  //by calling get_multi_table(), we ensure that multi_table is populated
3289 
3290  if(multi_table.size() && !valid_index(multi_table[0][0])) {
3292  return;
3293  }
3294  for(Index i = 0; i < multi_table.size(); i++) {
3295  if(multi_table[i][i] != 0) {
3296  alt_multi_table[multi_table[i].find(0)] = multi_table[i];
3297  }
3298  else {
3299  alt_multi_table[i] = multi_table[i];
3300  }
3301  }
3302 
3303  }
3304 
3305  //*******************************************************************************************
3306  // Please keep in mind that this will only return the FIRST match that is found.
3307  // This does not guarantee that it is the only match.
3308 
3309  Index SymGroup::find_no_trans(const SymOp &test_op) const {
3310  for(Index i = 0; i < size(); i++) {
3311  if(almost_equal(at(i).matrix(), test_op.matrix())) {
3312  return i;
3313  }
3314  }
3315 
3316  return size();
3317  }
3318 
3319  //*******************************************************************************************
3320 
3321  Index SymGroup::find_periodic(const SymOp &test_op, double tol) const {
3322  for(Index i = 0; i < size(); i++) {
3323  if(compare_periodic(at(i), test_op, lattice(), periodicity(), tol)) {
3324  return i;
3325  }
3326  }
3327  return size();
3328  }
3329 
3330  //*******************************************************************************************
3331 
3333  Array<Index> tarray;
3334  for(Index i = 0; i < subgroup.size(); i++) {
3335  tarray.push_back(find_periodic(subgroup[i], tol));
3336  if(tarray.back() == size()) {
3337  tarray.clear();
3338  break;
3339  }
3340  }
3341  return tarray;
3342  }
3343 
3344  //*******************************************************************************************
3345  // Probably need to change this...
3346  bool SymGroup::is_group(double tol) const {
3347 
3348  // This is important because it ensures that things like
3349  // within() and min_dist() work correctly for your group
3350  // regardless of what kind of group it is.
3351 
3352  for(Index i = 0; i < size(); i++) {
3353  for(Index j = 0; j < size(); j++) {
3354  if(!contains_periodic(at(i)*at(j), tol))
3355  return false;
3356  }
3357  if(!contains_periodic(at(i).inverse(), tol))
3358  return false;
3359  }
3360  return true;
3361  }
3362 
3363  //*******************************************************************************************
3364 
3365  void SymGroup::enforce_group(double tol, Index max_size) {
3366  bool new_ops(true);
3367 
3368  while(new_ops && size() < max_size) {
3369  new_ops = false;
3370  for(Index i = 0; i < size() && size() < max_size; i++) {
3371  SymOp A_op(at(i).unregistered_copy());
3372  for(Index j = 0; j < size() && size() < max_size; j++) {
3373  SymOp B_op(at(j).unregistered_copy());
3374 
3375  SymOp tOp(A_op * B_op);
3376 
3377  if(!contains_periodic(tOp, tol)) {
3379  new_ops = true;
3380  // std::cout << "Pushing back a SymOp due to multiplication fail.\n";
3381  }
3382  }
3383 
3384  SymOp tOp(A_op.inverse());
3385  if(!contains_periodic(tOp, tol)) {
3387  new_ops = true;
3388  //std::cout << "Pushing back a SymOp due to inverse fail.\n";
3389  }
3390  }
3391  }
3392  if(size() >= max_size - 1) {
3393  std::cerr << "In SymGroup::enforce_group() -- you have reached the maximum allowed size you specified for your group (the default is 200). Unless you are generating a factor group in a large supercell, you probably need to adjust your tolerances.\n";
3394  assert(0);
3395  }
3396  return;
3397  }
3398 
3399  //*******************************************************************************************
3400 
3401  /*bool SymGroup::contains(const SymOp &test_op) const {
3402  return Array<SymOp> :: contains(test_op);
3403  }*/
3404 
3405  //*******************************************************************************************
3406 
3407  bool SymGroup::contains_periodic(const SymOp &test_op, double tol) const {
3408  return find_periodic(test_op, tol) != size();
3409  }
3410 
3411  //*******************************************************************************************
3412 
3414  for(Index i = 0; i < size(); i++)
3415  at(i).apply_sym(op);
3416  return *this;
3417  }
3418 
3419  //*******************************************************************************************
3420 
3421  void SymGroup::write(std::string filename, COORD_TYPE mode) const {
3422  std::ofstream outfile;
3423  outfile.open(filename.c_str());
3424  print(outfile, mode);
3425  outfile.close();
3426  return;
3427  }
3428 
3429  //*******************************************************************************************
3430 
3431  void SymGroup::print(std::ostream &out, COORD_TYPE mode) const {
3432  out << size() << " # " << COORD_MODE::NAME(mode) << " representation of group containing " << size() << " elements:\n\n";
3433  Eigen::Matrix3d c2f_mat(Eigen::Matrix3d::Identity());
3434  if(mode == FRAC)
3435  c2f_mat = lattice().inv_lat_column_mat();
3436  for(Index i = 0; i < size(); i++) {
3437  out << i << " " << description(at(i), lattice(), mode) << "\n";
3438  at(i).print(out, c2f_mat);
3439  out << std::endl;
3440  }
3441  return;
3442  }
3443 
3444 
3445  //*******************************************************************************************
3446 
3447  void SymGroup::calc_space_group_in_cell(SymGroup &space_group_cell, const Lattice &_cell) const {
3448  if(!size()) return;
3449 
3450  Eigen::Vector3i max_trans(3, 3, 3);
3451  Coordinate trans(Eigen::Vector3d::Zero(), _cell, FRAC);
3452  space_group_cell.clear();
3453 
3454  std::vector<SymInfo> sg_info;
3455  for(Index i = 0; i < size(); i++) {
3456  EigenCounter<Eigen::Vector3i> lat_comb(-max_trans, max_trans, Eigen::Vector3i::Ones());
3457  do {
3458  trans.frac() = lat_comb().cast<double>();
3459  SymOp new_sym(SymOp::translation(trans.cart())*at(i));
3460  SymInfo info(new_sym, lattice());
3461  trans = info.location;
3462  if(!trans.is_within()) {
3463  continue;
3464  }
3465 
3466 
3467  bool new_location = true;
3468  for(Index j = 0; j < space_group_cell.size(); j++) {
3469 
3470  if(almost_equal(new_sym.matrix(), space_group_cell[j].matrix()) && almost_equal(info.location.const_cart(), sg_info[j].location.const_cart())) {
3471  new_location = false;
3472  break;
3473 
3474  }
3475  }
3476  if(new_location) {
3477  space_group_cell.push_back(new_sym);
3478  sg_info.push_back(info);
3479  }
3480  }
3481  while(++lat_comb);
3482  }
3483 
3484  return;
3485  }
3486 
3487  //*******************************************************************************************
3488 
3490  const Lattice &_cell,
3491  Eigen::Vector3i min_trans,
3492  Eigen::Vector3i max_trans) const {
3493  if(!size()) return;
3494 
3495 
3496  Coordinate trans(Eigen::Vector3d::Zero(), _cell, FRAC);
3497 
3498  for(Index i = 0; i < size(); i++) {
3499  EigenCounter<Eigen::Vector3i> lat_comb(min_trans, max_trans, Eigen::Vector3i::Ones());
3500  do {
3501  trans.frac() = lat_comb().cast<double>();
3502 
3503  SymOp new_sym(SymOp::translation(trans.cart())*at(i));
3504 
3505  if(!space_group.contains(new_sym)) {
3506  space_group.push_back(new_sym);
3507 
3508  }
3509 
3510  }
3511  while(++lat_comb);
3512  }
3513 
3514  return;
3515  }
3516 
3517  //***************************************************
3518  void SymGroup::print_locations(std::ostream &stream) const {
3519  //Assumes SymGroup is sorted with clumps of SymOps of common matrix type and eigenvec
3520  //sort();
3521 
3522  bool new_op = true;
3523  stream << "Locations for symmetry operations\n";
3524  SymInfo info(at(0), lattice());
3525  SymInfo next_info(info);
3527  for(Index i = 0; i < size(); i++) {
3528  if(new_op) {
3529  at(i).print(stream, c2f_mat);
3530 
3531  stream << std::endl;
3532  at(i).print(stream, Eigen::Matrix3d::Identity());
3533  stream << std::endl;
3534 
3535  stream << "Location:" << std::endl;
3536  stream << "FRAC\t\t\t\t\tCART" << std::endl;
3537  }
3538  stream << info.location.const_frac();
3539  stream << "\t\t\t";
3540  stream << info.location.const_cart();
3541  stream << std::endl;
3542 
3543  if(i + 1 < size()) {
3544  next_info = SymInfo(at(i + 1), lattice());
3545  if(info.op_type == next_info.op_type && almost_equal(info.axis.const_cart(), next_info.axis.const_cart())) {
3546  //Is this enough to know if it's a new symmetry or not?
3547  new_op = false;
3548  }
3549  else {
3550  new_op = true;
3551  stream << "----------------------------------------------------------------------------------\n\n";
3552  }
3553  }
3554  info = next_info;
3555  }
3556  return;
3557  }
3558 
3559  //*******************************************************************************************
3560 
3575 
3576  // floating point comparison tolerance
3577  double tol = TOL;
3578 
3579  //COORD_TYPE print_mode = CART;
3580 
3581  // compare on vector of '-det', '-trace', 'angle', 'axis', 'tau'
3582  typedef Eigen::Matrix<double, 9, 1> key_type;
3583  auto make_key = [](const SymOp & op, const Lattice & lat) {
3584 
3585  key_type vec;
3586  int offset = 0;
3587 
3588  SymInfo info(op, lat);
3589 
3590  vec[offset] = -op.matrix().determinant();
3591  offset++;
3592 
3593  vec[offset] = -op.matrix().trace();
3594  offset++;
3595 
3596  vec[offset] = info.angle;
3597  offset++;
3598 
3599  vec.segment<3>(offset) = info.axis.const_frac();
3600  offset += 3;
3601 
3602  vec.segment<3>(offset) = Coordinate(op.tau(), lat, CART).const_frac();
3603  offset += 3;
3604 
3605  return vec;
3606  };
3607 
3608  // define symop compare function
3609  auto op_compare = [tol](const key_type & A, const key_type & B) {
3610  return float_lexicographical_compare(A, B, tol);
3611  };
3612 
3613  typedef std::map<key_type, SymOp, std::reference_wrapper<decltype(op_compare)> > map_type;
3614 
3615  // sort conjugacy class using the first symop in the sorted class
3616  auto cclass_compare = [tol](const map_type & A, const map_type & B) {
3617  return float_lexicographical_compare(A.begin()->first, B.begin()->first, tol);
3618  };
3619 
3620  // sort elements in each conjugracy class (or just put all elements in the first map)
3621  std::vector<map_type> sorter;
3622 
3623  // first put identity in position 0
3624  for(int i = 0; i < size(); ++i) {
3625  if(at(i).is_identity()) {
3626  std::swap(at(0), at(i));
3627  break;
3628  }
3629  }
3630 
3631  // if sorting by congujacy classes
3632  if(get_multi_table().size() == size()) {
3633 
3634  // get conjugacy classes
3636 
3637  // insert elements into each conjugacy class to sort the class
3638  for(int i = 0; i < conjugacy_classes.size(); ++i) {
3639  sorter.push_back(map_type(op_compare));
3640  auto &cclass = sorter.back();
3641 
3642  for(int j = 0; j < conjugacy_classes[i].size(); ++j) {
3643  const SymOp &op = at(conjugacy_classes[i][j]);
3644  cclass.insert(std::make_pair(make_key(op, lattice()), op));
3645  }
3646  }
3647 
3648  // sort conjugacy class using the first symop in the sorted class
3649  std::sort(sorter.begin(), sorter.end(), cclass_compare);
3650 
3651  }
3652  else {
3653  // else just sort element
3654  sorter.push_back(map_type(op_compare));
3655  for(auto it = begin(); it != end(); ++it) {
3656  sorter.back().insert(std::make_pair(make_key(*it, lattice()), *it));
3657  }
3658  }
3659 
3660  // copy symop back into group
3661  int j = 0;
3662  for(int i = 0; i < sorter.size(); ++i) {
3663  for(auto it = sorter[i].begin(); it != sorter[i].end(); ++it) {
3664  at(j) = it->second;
3665  ++j;
3666  }
3667  }
3668 
3669  clear_tables();
3670  }
3671 
3672  //*******************************************************************************************
3673 
3675  double tvalue = 0;
3676 
3677  for(Index i = 0; i < size(); i++) {
3678  tvalue += (at(i).matrix().trace()) * (at(i).matrix().trace());
3679  }
3680 
3681  if(Index(std::abs(tvalue)) == size()) {
3682  return true;
3683  }
3684  else {
3685  return false;
3686  }
3687  }
3688 
3689  //*******************************************************************************************
3691  SymGroup &SymGroup::operator+=(const Eigen::Ref<const SymOp::vector_type> &shift) {
3692  for(Index ng = 0; ng < size(); ng++)
3693  at(ng) += shift;
3694  return (*this);
3695  }
3696 
3697  //*******************************************************************************************
3698 
3699  SymGroup &SymGroup::operator-=(const Eigen::Ref<const SymOp::vector_type> &shift) {
3700  for(Index ng = 0; ng < size(); ng++)
3701  at(ng) -= shift;
3702  return (*this);
3703  }
3704 
3705  //*******************************************************************************************
3706 
3708  Array<Index> tdec;
3709  Array<double> repchar;
3710 
3711  tdec.resize(conjugacy_classes.size());
3712  repchar.resize(conjugacy_classes.size());
3713 
3714  for(Index i = 0; i < conjugacy_classes.size(); i++) {
3715  repchar[i] += at(conjugacy_classes[i][0]).matrix().trace();
3716  }
3717 
3718  // std::cout << "The reducible characters are:\n";
3719 
3720  // std::cout << repchar << "\n\n";
3721 
3722  for(Index i = 0; i < m_character_table.size(); i++) { // Loop over irreducible representations
3723  std::complex<double> temp = std::complex<double>(0, 0);
3724  for(Index j = 0; j < m_character_table.size(); j++) { // Loop over conjugacy classes
3725  temp += (m_character_table[i][j] * std::complex<double>(conjugacy_classes[j].size(), 0) * std::complex<double>(repchar[j], 0));
3726  }
3727  // std::cout << temp << "\t";
3728 
3729  tdec[i] = round(temp.real() / size());
3730  }
3731 
3732  // std::cout << "\n\nThe irreducible projection is:\n";
3733  //std::cout << tdec << "\n\n";
3734 
3735  return tdec;
3736  }
3737 
3738  //*******************************************************************************************
3739 
3741  json.put_obj();
3742 
3743  json["symop"].put<Array<SymOp> >(*this);
3744 
3745  for(int i = 0; i < json["symop"].size(); ++i) {
3746  SymInfo info(at(i), lattice());
3747  auto &j = json["symop"][i];
3748  add_sym_info(info, j);
3749  }
3750 
3751  // PERIODICITY_TYPE group_periodicity;
3752  json["group_periodicity"] = periodicity();
3753 
3754  // mutable Array<Array<int> > multi_table;
3755  json["multi_table"] = multi_table;
3756 
3757  // mutable Array<Array<int> > alt_multi_table;
3758  json["alt_multi_table"] = alt_multi_table;
3759 
3760  // mutable Array<Array<int> > conjugacy_classes;
3761  json["conjugacy_classes"] = conjugacy_classes;
3762 
3763  // mutable Array<std::string> class_names;
3764  json["class_names"] = class_names;
3765 
3766  // mutable Array<int> index2conjugacy_class;
3767  json["index2conjugacy_class"] = index2conjugacy_class;
3768 
3769  // mutable Array<Array<std::complex<double> > > m_character_table;
3770  json["character_table"] = m_character_table;
3771  for(int i = 0; i < json["character_table"].size(); i++) {
3772  json["character_table"][i].set_force_row();
3773  for(int j = 0; j < json["character_table"][i].size(); j++) {
3774  json["character_table"][i][j].set_force_row();
3775  json["character_table"][i][j]["real"].set_remove_trailing_zeros();
3776  json["character_table"][i][j]["imag"].set_remove_trailing_zeros();
3777  }
3778  }
3779 
3780  // mutable Array<int> irrep_IDs;
3781  json["irrep_IDs"] = irrep_IDs;
3782 
3783  // mutable Array<bool> complex_irrep;
3784  json["complex_irrep"] = complex_irrep;
3785 
3786  // mutable Array<std::string> irrep_names;
3787  json["irrep_names"] = irrep_names;
3788 
3789  json["unique_subgroups"] = unique_subgroups();
3790 
3791  // mutable Array<Array<Array<int> > > m_subgroups;
3792  json["m_subgroups"] = m_subgroups;
3793 
3794  // mutable Array<Array<int> > centralizer_table;
3795  json["centralizer_table"] = centralizer_table;
3796 
3797  // mutable Array<Array<int> > elem_order_table;
3798  json["elem_order_table"] = elem_order_table;
3799 
3800  // mutable std::string name;
3801  json["name"] = name;
3802 
3803  // mutable std::string latex_name;
3804  json["latex_name"] = latex_name;
3805 
3806  //mutable std::string comment;
3807  json["comment"] = comment;
3808 
3809  // mutable double max_error;
3810  json["max_error"] = m_max_error;
3811 
3812  // mutable Array<Array<SymOp> > rotation_groups;
3813  json["rotation_groups"] = rotation_groups;
3814 
3815  // mutable std::string crystal_system;
3816  json["crystal_system"] = crystal_system;
3817 
3818  // mutable bool centric;
3819  json["centric"] = centric;
3820 
3821  // mutable Array<int> group_number; // space group number (min and max)
3822  json["group_number"] = group_number;
3823 
3824  // mutable Array<std::string> group_name; // 0: International 1: Schonflies
3825  json["group_name"] = group_name;
3826 
3827  return json;
3828  }
3829 
3830  //*******************************************************************************************
3831 
3832  void SymGroup::from_json(const jsonParser &json) {
3833  try {
3834 
3835  // class SymGroup : public Array<SymOp>
3836  clear();
3837 
3838  for(int i = 0; i < json["symop"].size(); i++) {
3839  push_back(json["symop"][i].get<SymOp>());
3840  }
3841 
3842  // PERIODICITY_TYPE group_periodicity;
3843  CASM::from_json(m_group_periodicity, json["group_periodicity"]);
3844 
3845  // mutable Array<Array<int> > multi_table;
3846  CASM::from_json(multi_table, json["multi_table"]);
3847 
3848  // mutable Array<Array<int> > alt_multi_table;
3849  CASM::from_json(alt_multi_table, json["alt_multi_table"]);
3850 
3851  // mutable Array<Array<int> > conjugacy_classes;
3852  CASM::from_json(conjugacy_classes, json["conjugacy_classes"]);
3853 
3854  // mutable Array<std::string> class_names;
3855  CASM::from_json(class_names, json["class_names"]);
3856 
3857  // mutable Array<int> index2conjugacy_class;
3858  CASM::from_json(index2conjugacy_class, json["index2conjugacy_class"]);
3859 
3860  // mutable Array<Array<std::complex<double> > > m_character_table;
3861  CASM::from_json(m_character_table, json["character_table"]);
3862 
3863  // mutable Array<int> irrep_IDs;
3864  CASM::from_json(irrep_IDs, json["irrep_IDs"]);
3865 
3866  // mutable Array<bool> complex_irrep;
3867  CASM::from_json(complex_irrep, json["complex_irrep"]);
3868 
3869  // mutable Array<std::string> irrep_names;
3870  CASM::from_json(irrep_names, json["irrep_names"]);
3871 
3872  // mutable Array<Array<Array<int> > > m_subgroups;
3873  CASM::from_json(m_subgroups, json["m_subgroups"]);
3874 
3875  // mutable Array<Array<int> > centralizer_table;
3876  CASM::from_json(centralizer_table, json["centralizer_table"]);
3877 
3878  // mutable Array<Array<int> > elem_order_table;
3879  CASM::from_json(elem_order_table, json["elem_order_table"]);
3880 
3881  // mutable std::string name;
3882  CASM::from_json(name, json["name"]);
3883 
3884  // mutable std::string latex_name;
3885  CASM::from_json(latex_name, json["latex_name"]);
3886 
3887  //mutable std::string comment;
3888  CASM::from_json(comment, json["comment"]);
3889 
3890  // mutable double max_error;
3891  CASM::from_json(m_max_error, json["max_error"]);
3892 
3893  // mutable Array<Array<SymOp> > rotation_groups;
3894  //CASM::from_json( rotation_groups, json["rotation_groups"]);
3895  rotation_groups.clear();
3896  Array<SymOp> group;
3897  for(int i = 0; i < json["rotation_groups"].size(); i++) {
3898  group.clear();
3899  for(int j = 0; i < json["rotation_groups"][i].size(); j++) {
3900  group.push_back(json["rotation_groups"][i][j].get<SymOp>());
3901  }
3902  rotation_groups.push_back(group);
3903  }
3904 
3905  // mutable std::string crystal_system;
3906  CASM::from_json(crystal_system, json["crystal_system"]);
3907 
3908  // mutable bool centric;
3909  CASM::from_json(centric, json["centric"]);
3910 
3911  // mutable Array<int> group_number; // space group number (min and max)
3912  CASM::from_json(group_number, json["group_number"]);
3913 
3914  // mutable Array<std::string> group_name; // 0: International 1: Schonflies
3915  CASM::from_json(group_name, json["group_name"]);
3916 
3917  }
3918  catch(...) {
3920  throw;
3921  }
3922  }
3923 
3924  //*******************************************************************************************
3925 
3926  jsonParser &to_json(const SymGroup &sym, jsonParser &json) {
3927  return sym.to_json(json);
3928  }
3929 
3930  void from_json(SymGroup &sym, const jsonParser &json) {
3931  try {
3932  sym.from_json(json);
3933  }
3934  catch(...) {
3936  throw;
3937  }
3938  }
3939  //*******************************************************************************************
3940 
3941  SymGroup molecular_point_group(std::map<int, std::vector<Eigen::Vector3d> > coord_map) {
3942  //std::cout << " in mol_pg" << std::endl;
3943  SymGroup mol_pg(LOCAL);
3944 
3945  if(coord_map.empty()) {
3946  std::cerr << "SymGroup molecular_point_group found an empty coord_map\n"
3947  << "Exiting ... " << std::endl;
3948  exit(88);
3949  }
3950 
3951 
3952  // num different species
3953  int map_size = coord_map.size();
3954  // num total coordinates
3955  int num_elements = 0;
3956  for(auto const &map_entry : coord_map) {
3957  num_elements += map_entry.second.size();
3958  }
3959 
3960  //
3961  //
3962  // Building a 3xN matrix with coordinates as column vectors
3963  Eigen::MatrixXd coord_matrix(3, num_elements);
3964  Index col_counter = 0;
3965  Eigen::Vector3d center(0., 0., 0.);
3966  Eigen::Vector3d mol_center(0., 0., 0.);
3967  for(auto map_entry : coord_map) {
3968  for(Index i = 0; i < map_entry.second.size() ; i++) {
3969  //Eigen::Vector3d tmp_coord = map_entry.second.at(i);
3970  coord_matrix.col(col_counter) = map_entry.second.at(i);
3971  mol_center += map_entry.second.at(i);
3972  col_counter++;
3973  }
3974  }
3975  mol_center /= num_elements;
3976  std::cout << " found center at: " << mol_center << std::endl;
3977 
3978  // Cannot have a size() == 1 std::vector < (0.,0.,0.) >
3979  // since this will break algorithm
3980  Eigen::Vector3d origin(0., 0., 0.);
3981  for(auto it = coord_map.begin(); it != coord_map.end();) {
3982  if(it->second.size() == 1 && it->second.at(0) == origin) {
3983  it = coord_map.erase(it);
3984  map_size -= 1;
3985  num_elements -= 1;
3986  }
3987  else
3988  ++it;
3989  }
3990 
3991 
3992  col_counter = 0;
3993  // centerize this thing
3994  if(center != mol_center) {
3995  for(auto &map_entry : coord_map) {
3996  //std::vector<Coordinate> new_coord_vec;
3997  for(Index i = 0; i < map_entry.second.size() ; i++) {
3998  //Coordinate tmp_coord(map_entry.second.at(i));
3999  coord_map[map_entry.first].at(i) -= mol_center;
4000  //new_coord_vec.push_back(tmp_coord);
4001  //map_entry.second.at(i) = tmp_coord;
4002  col_counter++;
4003  }
4004  //map_entry.second.swap(new_coord_vec);
4005  }
4006  }
4007 
4008 
4009  // update coord_matrix to centerized coordinates
4010  // don't like running through all of these loops here
4011  // but a quick fix eludes me
4012  col_counter = 0;
4013  for(auto &map_entry : coord_map) {
4014  for(Index i = 0; i < map_entry.second.size() ; i++) {
4015  //Eigen::Vector3d tmp_coord = map_entry.second.at(i)(CART);
4016  coord_matrix.col(col_counter) = map_entry.second.at(i);
4017  col_counter++;
4018  }
4019  }
4020 
4021 
4022  // the algorithm:
4023  // - first look for linear molecules, these will have either C_{\inf}v or D_{\inf}h groups
4024  // - we have some easy cases like C1 which has no sym ops.
4025  // - C1 can be recognized immiediately if all species are distinct
4026  // - also check for inversion, we will return only E and i representations;
4027  // if a cluster expansion is desired on linear molecule orientation,
4028  // it is up to the user to specify spin-cluster expansion (SCE)
4029  // rather than quaternion-cluster expansion (QCE).
4030  // - to find all sym ops we will consider clusters of 3 unique atoms (if possible)
4031  // -
4032  //
4033  bool is_linear = false;
4034  bool is_planar = false;
4035 
4036  // if we've gotten this far, we already have Identity
4037  mol_pg.push_back(SymOp());
4038 
4039  // Check the rank.
4040  if(coord_matrix.colPivHouseholderQr().rank() == 1) {
4041  // points are on a line.
4042  is_linear = true;
4043  }
4044  else if(coord_matrix.colPivHouseholderQr().rank() == 2) {
4045  // points are coplanar
4046  std::cout << " rank == 2 " << std::endl;
4047  is_planar = true;
4048  }
4049 
4050 
4051  bool is_unique = false;
4052  // Is each atom unique? If so, probably C1 unless linear
4053  if(coord_map.count(map_size - 1) == 0) {
4054  is_unique = true;
4055  }
4056 
4057  if(is_unique) {
4058  // this is C1 or C_\inf
4059  // proceed no further
4060  return mol_pg;
4061  }
4062 
4063  // now begin mapping
4064  // Need to pick 3 atoms, then compute their allowed permutations and inverses
4065  // Perform matrix multiplications and check for Unitary transformations
4066 
4067  // Holder for SymMatrices
4068  std::vector<Eigen::Matrix3d> sym_ops;
4069  // We need to roll through the key-buckets
4070  // Need to take care when choosing clusters,
4071  // limiting cases are: all one bucket (Buckyball)
4072  // or, all unique but one size=2 bucket.
4073  // Therefore, going to take as many coords from each bucket as possible.
4074  Eigen::Matrix3d init_triplet;
4075 
4076  // ijk will exhaust the combinations among buckets
4077  // still need to do permutations between buckets
4078  // *and* within buckets - lots of 'for-loops' :/
4079  // next_permutation to the rescue :D
4080  Index i, j, k; // for combinations
4081  Eigen::Vector3d tmp_coord;
4082 
4083 
4084  // deal with planar molecules
4085  // by finding the normal vector
4086  if(is_planar && !is_linear) {
4087  std::cout << " In is_planar" << std::endl;
4088  Eigen::Vector3d disp1;
4089  disp1 = (coord_map.at(0).at(0) - coord_map.at(0).at(1));
4090  Eigen::Vector3d disp2;
4091  disp2 = (coord_map.at(0).at(2) - coord_map.at(0).at(1));
4092  Eigen::Vector3d normal_vector;
4093  normal_vector = (disp1.cross(disp2));
4094 
4095  // add a bucket with +/- normal vector
4096  std::vector<Eigen::Vector3d> norm_vecs {normal_vector, -normal_vector};
4097  std::cout << "Norm vecs 1 " << norm_vecs.at(0) << std::endl;
4098  std::cout << "Norm vecs 2 " << norm_vecs.at(1) << std::endl;
4099  coord_map[map_size] = norm_vecs;
4100  map_size += 1;
4101  // can probably just use same algorithms
4102  }
4103 
4104  Eigen::Matrix3d S;
4105  Eigen::Matrix3d init_cluster;
4106  Eigen::Matrix3d inv_init;
4107 
4108 
4109 
4110  // NOTE:::::this is only going to work for >= 3 unique atoms
4111  // ok for now might honestly just make 3 ifs
4112  std::vector<std::vector<Eigen::Vector3d> > curr_buckets(3);
4113 
4114 
4115  if(map_size >= 3) {
4116 
4117  for(i = 0; i < map_size - 2; i++) {
4118  for(j = i + 1; j < map_size - 1; j++) {
4119  for(k = j + 1; k < map_size; k++) {
4120 
4121 
4122  // Let's get only unique elements from the sym_ops
4123  // going to try the sort and unique idea
4124  std::sort(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4125  for(Index i = 0; i < 3; ++i) {
4126  for(Index j = 0; j < 3; ++j) {
4127  if(almost_equal(a(i, j), b(i, j))) continue;
4128  if(a(i, j) < b(i, j)) return true;
4129  if(a(i, j) > b(i, j)) return false;
4130  }
4131  }
4132  return false;
4133  });
4134  sym_ops.erase(unique(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4135  return a.isApprox(b, 0.00001);
4136  }), sym_ops.end());
4137 
4138  curr_buckets[0] = coord_map.at(i);
4139  curr_buckets[1] = coord_map.at(j);
4140  curr_buckets[2] = coord_map.at(k);
4141 
4142  for(Index ii = 0; ii < curr_buckets.at(0).size(); ii++) {
4143  for(Index jj = 0; jj < curr_buckets.at(1).size(); jj++) {
4144  for(Index kk = 0; kk < curr_buckets.at(2).size(); kk++) {
4145 
4146  tmp_coord = curr_buckets.at(0)[ii];
4147  init_cluster.col(0) = tmp_coord;
4148  tmp_coord = curr_buckets.at(1)[jj];
4149  init_cluster.col(1) = tmp_coord;
4150  tmp_coord = curr_buckets.at(2)[kk];
4151  init_cluster.col(2) = tmp_coord;
4152  // we have our starting cluster: a0b0c0
4153  // compare against all a(ii)b(jj)c(kk)
4154 
4155  std::cout << "my init_cluster:\n" << init_cluster << std::endl;
4156  // Check if this is full rank!
4157  if(init_cluster.colPivHouseholderQr().rank() == 3) {
4158  inv_init = init_cluster.inverse();
4159  //std::map<int, std::vector<Eigen::Vector3d> > tmp_map;
4160  //tmp_map[0] = curr_buckets.at(0);
4161  //tmp_map[1] = curr_buckets.at(1);
4162  //tmp_map[2] = curr_buckets.at(2);
4163 
4164 
4165  for(Index iii = 0; iii < curr_buckets.at(0).size(); iii++) {
4166  for(Index jjj = 0; jjj < curr_buckets.at(1).size(); jjj++) {
4167  for(Index kkk = 0; kkk < curr_buckets.at(2).size(); kkk++) {
4168  Eigen::Matrix3d test_cluster;
4169 
4170  tmp_coord = curr_buckets.at(0)[iii];
4171  test_cluster.col(0) = tmp_coord;
4172  tmp_coord = curr_buckets.at(1)[jjj];
4173  test_cluster.col(1) = tmp_coord;
4174  tmp_coord = curr_buckets.at(2)[kkk];
4175  test_cluster.col(2) = tmp_coord;
4176 
4177  // Finally, calc S in test_cluster = S * init_cluster
4178  // and if S'S = I.
4179 
4180  S = test_cluster * inv_init;
4181 
4182  std::cout << "my S:\n" << S << std::endl;
4183  if(S.isUnitary(1e-6) && !(S.isIdentity(1e-8))) {
4184  std::cout << "keeper:\n" << S << std::endl;
4185  // we've got a keeper
4186  //S = S * -1;
4187  //S = S * -1;
4188  sym_ops.push_back(S);
4189  //if(sym_ops.size() > 120) exit(88);
4190 
4191 
4192  }
4193 
4194 
4195  } // end iii-for
4196  } // end jjj-for
4197  } // end kkk-for
4198  } // end if rank=3
4199  } // end ii-for
4200  } // end jj-for
4201  } // end kk-for
4202  } // end k-for
4203  } // end j-for
4204  } // end i-for
4205  } // end if
4206 
4207 
4208  //std::cout << "map_size() " << map_size << std::endl;
4209  // things are a little weird when we have to use the same bucket for
4210  // two of the columns. I am doing the easiest approach.
4211  if(map_size <= 2 && num_elements > 2) {
4212 
4213  // this makes coord_map[0].size == 1
4214  if(map_size > 1 && coord_map.at(0).size() != 1) {
4215  std::vector<Eigen::Vector3d> &single_atom = coord_map[1];
4216  std::vector<Eigen::Vector3d> &multi_atom = coord_map[0];
4217  std::swap(single_atom, multi_atom);
4218  }
4219  // 'ordered-ordered' list
4220 
4221 
4222  // There are only 4 combinations to test
4223  // (011), (111), (001), (000)
4224  // and permutations (101),(110)
4225  // as well as (010)(100)
4226  // this is an ugly control flow
4227  // but unclear how to clean it up
4228 
4229  for(Index comb = 0; comb < 4; comb++) {
4230 
4231  std::cout << "in for" << std::endl;
4232 
4233  // (000) on first run
4234  // if at(0) big enough
4235  if(comb == 0 && coord_map.at(0).size() > 2) {
4236  std::cout << "in comb 0" << std::endl;
4237  curr_buckets[0] = coord_map.at(0);
4238  curr_buckets[1] = coord_map.at(0);
4239  curr_buckets[2] = coord_map.at(0);
4240  }
4241  else if(comb == 0) {
4242  continue;
4243  }
4244  // (111) on second run
4245  // if at(1) big enough
4246  if(map_size > 1 && comb == 1 && coord_map.at(1).size() > 2) {
4247  std::cout << "in comb 1" << std::endl;
4248  curr_buckets[0] = coord_map.at(1);
4249  curr_buckets[1] = coord_map.at(1);
4250  curr_buckets[2] = coord_map.at(1);
4251  }
4252  else if(comb == 1) continue;
4253 
4254  // (001) on third run
4255  // if at(0) big enough
4256  if(map_size > 1 && comb == 2 && coord_map.at(0).size() > 1) {
4257  std::cout << "in comb 2" << std::endl;
4258  curr_buckets[0] = coord_map.at(0);
4259  curr_buckets[1] = coord_map.at(0);
4260  curr_buckets[2] = coord_map.at(1);
4261  }
4262  else if(comb == 2) continue;
4263 
4264 
4265  // (110) on third run
4266  // if at(0) big enough
4267  if(map_size > 1 && comb == 3 && coord_map.at(1).size() > 1) {
4268  std::cout << "in comb 3" << std::endl;
4269  curr_buckets[0] = coord_map.at(1);
4270  curr_buckets[1] = coord_map.at(1);
4271  curr_buckets[2] = coord_map.at(0);
4272  }
4273  else if(comb == 3) continue;
4274 
4275 
4276  // we are guaranteed to have at least three elements
4277  //
4278  //
4279 
4280 
4281  for(Index ii = 0; ii < curr_buckets.at(0).size(); ++ii) {
4282 
4283 
4284 
4285  // Due to how slow this is, putting it here
4286  // to avoid repeating it so many times.
4287  //
4288  // Let's get only unique elements from the sym_ops
4289  // going to try the sort and unique idea
4290  std::sort(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4291  for(Index i = 0; i < 3; ++i) {
4292  for(Index j = 0; j < 3; ++j) {
4293  if(almost_equal(a(i, j), b(i, j))) continue;
4294  if(a(i, j) < b(i, j)) return true;
4295  if(a(i, j) > b(i, j)) return false;
4296  }
4297  }
4298  return false;
4299  });
4300  sym_ops.erase(unique(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4301  return a.isApprox(b, 0.00001);
4302  }), sym_ops.end());
4303 
4304 
4305  for(Index jj = 0; jj < curr_buckets.at(1).size(); ++jj) {
4306 
4307  for(Index kk = 0; kk < curr_buckets.at(2).size(); ++kk) {
4308 
4309  tmp_coord = curr_buckets.at(0)[ii];
4310  init_cluster.col(0) = tmp_coord;
4311  tmp_coord = curr_buckets.at(1)[jj];
4312  init_cluster.col(1) = tmp_coord;
4313  tmp_coord = curr_buckets.at(2)[kk];
4314  init_cluster.col(2) = tmp_coord;
4315 
4316  // we have our starting cluster: a0b0c0
4317  // compare against all a(ii)b(jj)c(kk)
4318 
4319  //std::cout << "my init_cluster:\n" << init_cluster << std::endl;
4320 
4321  // Check if this is full rank!
4322  if(init_cluster.colPivHouseholderQr().rank() == 3) {
4323  inv_init = init_cluster.inverse();
4324  //std::map<int, std::vector<Eigen::Vector3d> > tmp_map;
4325  //tmp_map[0] = curr_buckets.at(0);
4326  //tmp_map[1] = curr_buckets.at(1);
4327  //tmp_map[2] = curr_buckets.at(2);
4328 
4329  for(Index iii = 0; iii < curr_buckets.at(0).size(); ++iii) {
4330  for(Index jjj = 0; jjj < curr_buckets.at(1).size(); ++jjj) {
4331  for(Index kkk = 0; kkk < curr_buckets.at(2).size(); ++kkk) {
4332  Eigen::Matrix3d test_cluster;
4333 
4334  tmp_coord = curr_buckets.at(0)[iii];
4335  test_cluster.col(0) = tmp_coord;
4336  tmp_coord = curr_buckets.at(1)[jjj];
4337  test_cluster.col(1) = tmp_coord;
4338  tmp_coord = curr_buckets.at(2)[kkk];
4339  test_cluster.col(2) = tmp_coord;
4340 
4341  // Finally, calc S in test_cluster = S * init_cluster
4342  // and if S'S = I.
4343 
4344  S = test_cluster * inv_init;
4345 
4346  if(S.isUnitary(1e-6) && !(S.isIdentity(1e-8))) {
4347  //std::cout << "my S:\n" << S << std::endl;
4348  // we've got a keeper
4349  sym_ops.push_back(S);
4350  //if(sym_ops.size() > 5000000) {
4351  // std::cout << " I HAD TO EXIT " << std::endl;
4352  // goto comehere;
4353  //}
4354  }
4355 
4356 
4357  } // end iii-for
4358  } // end jjj-for
4359  } // end kkk-for
4360  } // end if rank=3
4361  } // end ii-for
4362  } // end jj-for
4363  } // end kk-for
4364 
4365  } // end combination-for
4366  } // end if-only two kind of atoms
4367 
4368 
4371  //for (i = 0;i < curr_buckets.at(0).size() - 2; i++){
4372  // for(j = i + 1; j < curr_buckets.at(1).size() - 1; j++){
4373  // for(k = j + 1; k < curr_buckets.at(2).size(); k++){
4374 
4375  // // (r1,r2,r3) cluster initial
4376  // Eigen::Matrix3d init_cluster;
4377  // // (AtomXi, AtomXj, AtomXk) to be permuted
4378  // std::vector<Eigen::Vector3d> permute_list(3);
4379 
4380  // tmp_coord = curr_buckets.at(0)[i];
4381  // permute_list.at(0) = curr_buckets.at(0)[i];
4382  // init_cluster.col(0) = tmp_coord;
4383  // tmp_coord = curr_buckets.at(1)[j];
4384  // permute_list.at(1) = curr_buckets.at(1)[j];
4385  // init_cluster.col(1) = tmp_coord;
4386  // tmp_coord = curr_buckets.at(2)[k];
4387  // permute_list.at(2) = curr_buckets.at(2)[k];
4388  // init_cluster.col(2) = tmp_coord;
4389 
4390  // //std::cout << "have init_cluster\n" << init_cluster << std::endl;
4391 
4392  // if(init_cluster.colPivHouseholderQr().rank() == 3){
4393  // // i'd like to do permutations now of the cluster
4394 
4395  // // need to sort the permute_list lexicographically
4396  // // in order for next_permutation to work
4397  // std::sort(permute_list.begin(),permute_list.end(),[](const Eigen::Vector3d &a, const Eigen::Vector3d &b) {
4398  // for(Index i =0; i < 3; i++){
4399  // for(Index j = 0; j < 3; j++){
4400  // if(a(i,j) < b(i,j)) return true;
4401  // if(a(i,j) > b(i,j)) return false;
4402  // }} return false;});
4403  // std::cout << "NEW PERMUTATION\n\n" << std::endl;
4404  // do{
4405  // Eigen::Matrix3d curr_cluster;
4406  // curr_cluster.col(0) = permute_list.at(0);
4407  // curr_cluster.col(1) = permute_list.at(1);
4408  // curr_cluster.col(2) = permute_list.at(2);
4409  // std::cout << "curr_cluster\n" << curr_cluster << std::endl;
4410 
4411 
4412  // Eigen::Matrix3d cluster_inv;
4413  // cluster_inv = curr_cluster.inverse();
4414  //
4415  // for (Index ii = 0;ii < curr_buckets.at(0).size() - 2; ii++){
4416  // for(Index jj = ii + 1; jj < curr_buckets.at(1).size() - 1; jj++){
4417  // for(Index kk = jj + 1; kk < curr_buckets.at(2).size(); kk++){
4418  //
4419  // Eigen::Matrix3d test_cluster;
4420  //
4421  // tmp_coord = curr_buckets.at(0)[ii];
4422  // test_cluster.col(0) = tmp_coord;
4423  // tmp_coord = curr_buckets.at(1)[jj];
4424  // test_cluster.col(1) = tmp_coord;
4425  // tmp_coord = curr_buckets.at(2)[kk];
4426  // test_cluster.col(2) = tmp_coord;
4427 
4428  // // Finally, calc S in test_cluster = S * init_cluster
4429  // // and if S'S = I.
4430  //
4431  // Eigen::Matrix3d S;
4432 
4433 
4434  // S = test_cluster * cluster_inv;
4435  // //std::cout << "test_cluster * cluster_inv = S\n" << test_cluster << "\n" << cluster_inv << "\n" << S << std::endl;
4436  // if(S.isUnitary(TOL) && !(S.isIdentity(TOL))){
4437  // // we've got a keeper
4438  //
4439  // //std::cout << "Keeper! \n" << S << std::endl;
4440  // sym_ops.push_back(S);
4441  // }
4442 
4443 
4444  // } // end kk-for
4445  // } // end jj-for
4446  // } // end ii-for
4447  //
4448  // } while(std::next_permutation(permute_list.begin(),permute_list.end(),[](const Eigen::Vector3d &a,const Eigen::Vector3d &b){
4449  // for(Index t = 0; t < a.size(); t++){
4450  // if(a[t]<b[t]) return true;
4451  // if(a[t]>b[t])return false;
4452  // } return false;}));
4453  // }
4454 
4455  // } // end k-for
4456  // } // end j-for
4457  // } // end i-for
4458 
4459 
4460 
4461  // if only two atoms of same identity
4462  // only really have to test inversion
4463  // which will happen at end.
4464  if(num_elements == 2) {
4465  Eigen::Matrix3d inv_mat;
4466  inv_mat << -1., 0., 0.,
4467  0., -1., 0.,
4468  0., 0., -1.0;
4469  sym_ops.push_back(inv_mat);
4470  }
4471 
4472 
4473  // Let's get only unique elements from the sym_ops
4474  // going to try the sort and unique idea
4475  std::sort(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4476  for(Index i = 0; i < 3; ++i) {
4477  for(Index j = 0; j < 3; ++j) {
4478  if(almost_equal(a(i, j), b(i, j))) continue;
4479  if(a(i, j) < b(i, j)) return true;
4480  if(a(i, j) > b(i, j)) return false;
4481  }
4482  }
4483  return false;
4484  });
4485  //for(Index s = 0; s < sym_ops.size() ; s++){
4486  // std::cout << "S: \n" << sym_ops.at(s) << std::endl;
4487  //}
4488  //std::cout << "Size pre erase: " << sym_ops.size() << std::endl;
4489  //int pre = sym_ops.size();
4490  sym_ops.erase(unique(sym_ops.begin(), sym_ops.end(), [](const Eigen::Matrix3d & a, const Eigen::Matrix3d & b) {
4491  return a.isApprox(b, 0.00001);
4492  }), sym_ops.end());
4493  //int post = sym_ops.size();
4494  //std::cout << "Size post: " << sym_ops.size() << std::endl;
4495 
4496 
4497 
4498 
4499 
4500  // Now test these isometries.
4501  // Apply isometry to every coordinate
4502  // then check if the transformed coordinate
4503  // exists within the original map.
4504  // If they all do, add to final_list if
4505  // if isn't already there.
4506  std::vector<bool> sym_bools(sym_ops.size(), true);
4507  std::cout << " PRE TEST Sym_ops size() " << sym_ops.size() << std::endl;
4508  // so = symmetry operation in sym_ops
4509  for(Index so = 0; so < sym_ops.size(); so++) {
4510  // each map_entry holds all coordinates
4511  // for each species type
4512 
4513  for(auto map_entry : coord_map) {
4514  std::vector<Eigen::Vector3d> sym_species_vec;
4515  // get all of the transformed coords
4516  for(Index v = 0; v < map_entry.second.size(); v++) {
4517  // transform the coordinate
4518  sym_species_vec.push_back(sym_ops[so] * map_entry.second.at(v));
4519  } // end v-for
4520  std::vector<bool> sym_species_bools(sym_species_vec.size(), false);
4521 
4522  // now check if all of these coords were in the original std::vector
4523  // ov = original vector
4524  // this was wrong
4525  for(Index ov = 0 ; ov < map_entry.second.size(); ov++) {
4526  //for(Index sv = 0; sv < sym_species_vec.size(); sv++){
4527  //bool ya;
4528  tmp_coord = map_entry.second.at(ov);
4529  auto found = std::find_if(sym_species_vec.begin(), sym_species_vec.end(), [&tmp_coord](Eigen::Vector3d a) {
4530  return a.isApprox(tmp_coord, 1e-5);
4531  }
4532  );
4533  if(found != sym_species_vec.end()) {
4534  //std::cout << " Found coord" << std::endl;
4535  }
4536  else {
4537  //std::cout << "Didn't find a coordinate in sym_species_vec" << std::endl;
4538  sym_bools[so] = false;
4539  }
4540  //} // end sv-for
4541  } // end ov-for
4542  sym_species_vec.clear(); // clear this
4543 
4544  } // end map_entry-for
4545  } // end so-for
4546 
4547 
4548  int vec_counter = 0;
4549  std::vector<Eigen::Matrix3d> final_ops;
4550  for(auto ind : sym_bools) {
4551  //std::cout << "sym bools: " << ind << std::endl;
4552  if(ind) {
4553  final_ops.push_back(sym_ops.at(vec_counter));
4554  } // end if
4555  vec_counter++;
4556  } // end vec-for
4557 
4558 
4559  for(Index mat = 0; mat < final_ops.size(); mat++) {
4560  mol_pg.push_back(SymOp(final_ops.at(mat)));
4561  }
4562 
4563  // we are done, return sym_ops
4564 
4565 
4566  return mol_pg;
4567  }
4568 
4569  //*******************************************************************************************
4570 
4572 
4573  // class MasterSymGroup : public SymGroup
4574  SymGroup::to_json(json);
4575 
4576  // mutable Array<SymGroupRep *> m_rep_array;
4577  json["m_rep_array"].put_array();
4578  for(Index i = 0; i < m_rep_array.size(); i++) {
4579  json["rep_array"].push_back(m_rep_array[i]);
4580  }
4581 
4582  // mutable int coord_rep_ID, reg_rep_ID;
4583  json["coord_rep_ID"] = m_coord_rep_ID;
4584  json["reg_rep_ID"] = m_reg_rep_ID;
4585 
4586  // mutable SymGroup point_group_internal;
4587  json["point_group"] = m_point_group;
4588 
4589  return json;
4590  }
4591 
4592  //*******************************************************************************************
4593 
4594  // Note: as a hack this expects at(0) to be present and have the right lattice!!!
4595  // it's just used to set the lattice for all the SymOp
4597  try {
4598  clear();
4599 
4600  // class MasterSymGroup : public SymGroup
4601  SymGroup::from_json(json);
4602 
4603  // mutable Array<SymGroupRep *> m_rep_array;
4604  // destruct exisiting
4605  for(Index i = 0; i < m_rep_array.size(); i++) {
4606  delete m_rep_array[i];
4607  }
4608  m_rep_array.resize(json["rep_array"].size());
4609  for(int i = 0; i < json["rep_array"].size(); i++) {
4610  m_rep_array[i] = new SymGroupRep(*this);
4611  m_rep_array[i]->from_json(json["rep_array"][i]);
4612  }
4613 
4614  // mutable int coord_rep_ID, reg_rep_ID;
4615  CASM::from_json(m_coord_rep_ID, json["coord_rep_ID"]);
4616  CASM::from_json(m_reg_rep_ID, json["reg_rep_ID"]);
4617 
4618  // mutable SymGroup m_point_group;
4619  m_point_group.clear();
4620  m_point_group.from_json(json["point_group"]);
4621  }
4622  catch(...) {
4624  throw;
4625  }
4626  }
4627 
4628  //*******************************************************************************************
4629 
4631  return sym.to_json(json);
4632  }
4633 
4634  void from_json(MasterSymGroup &sym, const jsonParser &json) {
4635  try {
4636  sym.from_json(json);
4637  }
4638  catch(...) {
4640  throw;
4641  }
4642  }
4643 
4644  //**********************************************************
4645 
4646  bool compare_periodic(const SymOp &a,
4647  const SymOp &b,
4648  const Lattice &lat,
4649  PERIODICITY_TYPE periodicity,
4650  double _tol) {
4651 
4652  if(!almost_equal(a.matrix(), b.matrix(), _tol))
4653  return false;
4654  //std::cout << "Operations:\n"
4655  //<< a.matrix() << "\n and \n"
4656  //<< b.matrix() << "\n are equal \n"
4657  //<< "a-tau " << a.tau().transpose() << "\n"
4658  //<< "b-tau " << b.tau().transpose() << "\n";
4659  if(periodicity != PERIODIC)
4660  return almost_equal(a.tau(), b.tau(), _tol);
4661 
4662  return Coordinate(a.tau(), lat, CART).min_dist(Coordinate(b.tau(), lat, CART)) < _tol;
4663 
4664  }
4665 
4666  //**********************************************************
4667 
4669  const Lattice &lat,
4670  PERIODICITY_TYPE periodicity) {
4671  if(periodicity != PERIODIC)
4672  return a;
4673 
4674  Coordinate trans(a.tau(), lat, CART);
4675  trans.within();
4676  return SymOp(a.matrix(), trans.cart(), a.map_error());
4677  }
4678 
4679 
4680 }
SymGroupRepID add_rotation_rep() const
Definition: SymGroup.cc:243
Eigen::MatrixXd MatrixXd
size_type size() const
Returns array size if *this is a JSON array, object size if *this is a JSON object, 1 otherwise.
Definition: jsonParser.cc:430
PERIODICITY_TYPE m_group_periodicity
Specifies whether to use lattice periodicity when testing for equivalence.
Definition: SymGroup.hh:212
Coordinate location
A Cartesian coordinate that is invariant to the operation (if one exists)
Definition: SymInfo.hh:59
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].
virtual void sort()
Sort SymOp in the SymGroup.
Definition: SymGroup.cc:3574
jsonParser & to_json(jsonParser &json) const
Definition: SymGroup.cc:4571
Array< Array< Index > > multi_table
multi_table[i][j] gives index of operation that is result of at(i)*at(j)
Definition: SymGroup.hh:215
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:41
Index rep_index() const
Index of SymGroupRep within the master group Used internally to MasterSymGroup to access the correct ...
void calc_space_group_in_cell(SymGroup &space_group, const Lattice &_cell) const
Definition: SymGroup.cc:3447
Coordinate axis
Definition: SymInfo.hh:48
SymInfo info(Index i) const
Definition: SymGroup.cc:927
void _generate_centralizers() const
Definition: SymGroup.cc:1861
static SymOp translation(const Eigen::Ref< const vector_type > &_tau)
static method to create operation that describes pure translation
Definition: SymOp.hh:35
bool contains(const T &test_elem) const
Definition: Array.hh:281
ReturnArray< Array< Index > > left_cosets(const Array< SymOp > &subgroup, double tol=TOL) const
Definition: SymGroup.cc:3131
void from_json(ClexDescription &desc, const jsonParser &json)
Array< Array< Index > > alt_multi_table
alt_multi_table[i][j] gives index of operation that is result of at(i).inverse()*at(j) ...
Definition: SymGroup.hh:218
A Counter allows looping over many incrementing variables in one loop.
Definition: Counter.hh:71
Index size() const
Definition: Array.hh:145
Coordinate_impl::CartCoordinate cart()
Set Cartesian coordinate vector and update fractional coordinate vector.
Definition: Coordinate.hh:593
Index which_unique_combination(const Array< Index > &input, const Array< Index >::X2 &unique, const Array< Index >::X2 &ind_equiv)
Type-safe ID object for communicating and accessing Symmetry representation info. ...
SymGroup & apply_sym(const SymOp &op)
Calls 'apply_sym' on all SymOps in the group.
Definition: SymGroup.cc:3413
bool empty() const
Returns true if SymGroupRepID has not been initialized with valid group_index or rep_index.
void get_rotation_groups() const
Space group (added by Donghee );.
Definition: SymGroup.cc:509
void write(std::string filename, COORD_TYPE mode) const
Write the SymGroup to a file.
Definition: SymGroup.cc:3421
bool has_valid_master() const
check if this representation is registered with a MasterSymGroup
SymGroupRepID add_direct_sum_rep(const Array< SymGroupRepID > &rep_IDs) const
Definition: SymGroup.cc:209
void push_back(const T &toPush)
Definition: Array.hh:513
virtual void clear()
Definition: SymGroup.cc:490
SymGroupRepID add_transformed_rep(SymGroupRepID orig_ID, const Eigen::MatrixXd &trans_mat) const
Definition: SymGroup.cc:344
bool float_lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, double tol)
Floating point lexicographical comparison with tol.
Definition: CASM_math.hh:111
const SymGroup & point_group() const
Definition: SymGroup.cc:74
jsonParser & to_json(const ClexDescription &desc, jsonParser &json)
void set_index(const MasterSymGroup &new_group, Index new_index)
Definition: SymOp.cc:23
bool is_identity() const
Returns true if SymGroupRepID corresponds to an Identity representation.
SymGroupRepID add_empty_representation() const
Add a new empty representation.
Definition: SymGroup.cc:353
void resize(Index new_N)
Definition: Array.hh:468
Array< SymGroupRep * > m_rep_array
Definition: SymGroup.hh:350
Index index() const
Index of this operation within the master_group.
SymGroupRepID m_coord_rep_ID
ID of Cartesian representation.
Definition: SymGroup.hh:353
SymGroup molecular_point_group(std::map< int, std::vector< Eigen::Vector3d > > coord_map)
Definition: SymGroup.cc:3941
Main CASM namespace.
Definition: complete.cpp:8
SymGroupRepID add_kronecker_rep(SymGroupRepID ID1, SymGroupRepID ID2) const
Definition: SymGroup.cc:187
Array< std::string > irrep_names
Definition: SymGroup.hh:231
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: EnumIO.hh:83
Array< Array< Index > > elem_order_table
Definition: SymGroup.hh:239
Array< std::string > group_name
Definition: SymGroup.hh:252
const double TOL
SymGroupRepID coord_rep_ID() const
Get symrep ID of the representation that stores the Cartesian symop matrices.
Definition: SymGroup.cc:3098
Lattice const * m_lat_ptr
Pointer to a lattice for doing periodic comparisons.
Definition: SymGroup.hh:209
void push_back(const SymOp &op)
Definition: SymGroup.cc:65
std::string latex_name
Definition: SymGroup.hh:242
Index ind_inverse(Index i) const
Get index of operation that is inverse of operation at(i)
Definition: SymGroup.cc:3046
const vector_type & const_cart() const
user override to force const Access the Cartesian coordinate vector
Definition: Coordinate.hh:92
Array & operator=(const Array &RHS)
void print(std::ostream &out, COORD_TYPE mode) const
Print the SymGroup to a stream.
Definition: SymGroup.cc:3431
bool is_identity() const
returns true if matrix part of operation is identity
Definition: SymOp.hh:69
const Array< Index >::X2 & get_multi_table() const
Definition: SymGroup.cc:3170
const vector_type & const_frac() const
user override to force const Access the fractional coordinate vector
Definition: Coordinate.hh:66
Array< Index >::X3 _small_subgroups() const
Definition: SymGroup.cc:2776
void print_locations(std::ostream &stream) const
print locations of the symmetry-generating element of each SymOp
Definition: SymGroup.cc:3518
Index group_index() const
Definition: SymGroup.hh:287
jsonParser & to_json(jsonParser &json) const
Definition: SymGroup.cc:3740
Index group_index() const
Index of master group in which the corresponding SymGroupRep is stored Used internally to MasterSymGr...
void swap(ConfigDoF &A, ConfigDoF &B)
Definition: ConfigDoF.cc:195
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:33
MasterSymGroup & operator=(const MasterSymGroup &RHS)
Definition: SymGroup.cc:49
bool is_irreducible() const
Definition: SymGroup.cc:3674
double tol
const matrix_type & matrix() const
Const access of entire cartesian symmetry matrix.
Definition: SymOp.hh:57
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:113
void clear()
Definition: Array.hh:216
SymGroupRep const & coord_rep() const
Definition: SymGroup.cc:132
void _generate_subgroups() const
Definition: SymGroup.cc:2836
const double & map_error() const
Allows access to the map_error.
Definition: SymOp.cc:9
SymOp is the Coordinate representation of a symmetry operation it keeps fraction (FRAC) and Cartesian...
Definition: SymOp.hh:28
SymGroupRep const & get_irrep(Index i) const
Get symrep for a particular irrep.
Definition: SymGroup.cc:3121
Represents cartesian and fractional coordinates.
Definition: Coordinate.hh:34
Array< Array< Index > > centralizer_table
Definition: SymGroup.hh:238
Simple struct to be used as return type for SymOp::info().
Definition: SymInfo.hh:35
void _generate_character_table() const
Definition: SymGroup.cc:1888
symmetry_type op_type
Definition: SymInfo.hh:41
Eigen::MatrixXd const * get_MatrixXd(Index i) const
Definition: SymGroupRep.cc:253
SymOp within_cell(const SymOp &a, const Lattice &lat, PERIODICITY_TYPE periodicity)
Definition: SymGroup.cc:4668
double max_error()
This returns the group's max_error.
Definition: SymGroup.cc:933
std::string comment
Definition: SymGroup.hh:243
EigenIndex Index
For long integer indexing:
SymGroupRepID _add_coord_rep() const
Definition: SymGroup.cc:148
void set_irrep_ID(Index i, SymGroupRepID ID) const
set symrep ID of a particular irrep
Definition: SymGroup.cc:3079
const Array< std::complex< double > >::X2 & character_table() const
Definition: SymGroup.cc:3212
const Array< Index >::X3 & subgroups() const
Definition: SymGroup.cc:3243
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.
Array< Array< Index > > conjugacy_classes
Definition: SymGroup.hh:222
const MasterSymGroup & master_group() const
const access of head group
ReturnArray< Index > get_irrep_decomposition() const
Definition: SymGroup.cc:3707
bool is_within() const
Checks to see if coordinate is in the unit cell, but does not translate it.
Definition: Coordinate.cc:301
const Array< Index >::X2 & get_alt_multi_table() const
Definition: SymGroup.cc:3180
Array< int > group_number
Definition: SymGroup.hh:251
void invalidate_multi_tables() const
Definition: SymGroup.cc:3189
void enforce_group(double tol=TOL, Index max_size=200)
Enforce group property by adding products of operations to the group.
Definition: SymGroup.cc:3365
bool contains_periodic(const SymOp &test_op, double tol=TOL) const
Check to see if a SymOp is contained in in SymGroup.
Definition: SymGroup.cc:3407
SymOp sum() const
std::string name
Definition: SymGroup.hh:241
SymOp inverse() const
get the inverse of this SymOp
Definition: SymOp.cc:82
void _generate_conjugacy_classes() const
Definition: SymGroup.cc:2995
void _generate_elem_order_table() const
Definition: SymGroup.cc:2744
const Array< Index >::X2 & get_conjugacy_classes() const
Definition: SymGroup.cc:3197
SymOp & at(Index ind)
Definition: Array.hh:157
SymGroupRepID _add_representation(SymGroupRep *_rep_ptr) const
Definition: SymGroup.cc:367
Array< Array< std::complex< double > > > m_character_table
Definition: SymGroup.hh:228
SymGroupRepID reg_rep_ID() const
Definition: SymGroup.cc:108
Index find_no_trans(const SymOp &test_op) const
Check to see if a SymOp is contained in in SymGroup and return its index.
Definition: SymGroup.cc:3309
void _generate_alt_multi_table() const
Definition: SymGroup.cc:3286
double m_max_error
Definition: SymGroup.hh:245
std::string description(const SymOp &op, const Lattice &lat, COORD_TYPE mode)
Print SymInfo to string.
Index find(const SymOp &test_elem) const
bool is_group(double tol=TOL) const
Check to see if SymGroup satisfies the group property.
Definition: SymGroup.cc:3346
void from_json(const jsonParser &json)
Definition: SymGroup.cc:4596
MasterSymGroup(PERIODICITY_TYPE init_type=PERIODIC)
Definition: SymGroup.hh:279
std::vector< SymGroup > unique_subgroups() const
Definition: SymGroup.cc:2913
T const * end() const
Definition: Array.hh:197
SymGroupRepID get_irrep_ID(Index i) const
Get symrep ID of a particular irrep.
Definition: SymGroup.cc:3088
const Lattice & lattice() const
Definition: SymGroup.cc:940
Array & append(const Array &new_tail)
Definition: Array.hh:897
Index find_periodic(const SymOp &test_op, double tol=TOL) const
Definition: SymGroup.cc:3321
static std::string NAME()
get a string with the name of the active mode
Generalized symmetry matrix representation for arbitrary dimension Can be used to describe applicatio...
Definition: SymMatrixXd.hh:22
const T & max() const
Definition: Array.hh:604
bool check(std::string test, const jsonParser &expected, const jsonParser &calculated, fs::path test_cases_path, bool quiet, double tol=0.0)
Check expected JSON vs calculated JSON using BOOST_CHECK_EQUAL.
SymGroup m_point_group
Copy of *this with translations removed.
Definition: SymGroup.hh:362
bool all_in(const Array &superset) const
Definition: Array.hh:664
PERIODICITY_TYPE periodicity() const
Definition: SymGroup.hh:160
SymGroupRep const & reg_rep() const
Definition: SymGroup.cc:140
Eigen::Matrix3d Matrix3d
double angle
Definition: SymInfo.hh:52
jsonParser & put_obj()
Puts new empty JSON object.
Definition: jsonParser.hh:276
Array< SymGroupRepID > irrep_IDs
Definition: SymGroup.hh:229
virtual void push_back(const SymOp &new_op)
Definition: SymGroup.cc:441
Array< std::string > class_names
Definition: SymGroup.hh:223
jsonParser & put(const T &value)
Puts data of any type T for which 'jsonParser& to_json( const T &value, jsonParser &json)' is defined (same as 'o...
Definition: jsonParser.hh:761
void reserve(Index new_max)
Definition: Array.hh:491
SymGroupRepID m_reg_rep_ID
ID of 'regular representation', which is (size() X size()) representation constructed from alt_multi_...
Definition: SymGroup.hh:356
void sort(const CompareType &comp)
Definition: Array.hh:766
jsonParser & push_back(const T &value)
Puts new valued element at end of array of any type T for which 'jsonParser& to_json( const T &value...
Definition: jsonParser.hh:696
void copy_no_trans(SymGroup &shiftless, bool keep_repeated=false) const
Fill up a SymGroup with *this minus the shifts.
Definition: SymGroup.cc:907
void print(std::ostream &stream, const Eigen::Ref< const matrix_type > &c2fmat=matrix_type::Identity()) const
Definition: SymOp.cc:118
SymGroup(PERIODICITY_TYPE init_type=PERIODIC)
Initialize by setting periodicity mode (default mode is PERIODIC)
Definition: SymGroup.hh:38
SymGroupRep coord_transformed_copy(const Eigen::MatrixXd &trans_mat) const
Definition: SymGroupRep.cc:280
SymGroupRep * _representation_ptr(SymGroupRepID _id) const
Definition: SymGroup.cc:379
Coordinate_impl::FracCoordinate frac()
Set the fractional coordinate vector.
Definition: Coordinate.hh:581
SymOp & apply_sym(const SymOp &op)
Definition: SymOp.cc:111
const vector_type & tau() const
Const access of the cartesian translation vector, 'tau'.
Definition: SymOp.hh:63
void set_lattice(const Lattice &new_lat)
Definition: SymGroup.cc:435
SymGroupRepID _add_reg_rep() const
Definition: SymGroup.cc:158
SymGroupRep is an alternative representation of a SymGroup for something other than real space...
Definition: SymGroupRep.hh:30
const std::string & get_latex_name() const
Definition: SymGroup.cc:3236
void print_space_group_info(std::ostream &out) const
Definition: SymGroup.cc:883
SymGroupRepID coord_rep_ID() const
Definition: SymGroup.cc:100
Array< Array< SymOp > > rotation_groups
Space group (added by Donghee );.
Definition: SymGroup.hh:248
Index class_of_op(Index i) const
Get conjugacy class index of operation at(i)
Definition: SymGroup.cc:3071
T const * begin() const
Definition: Array.hh:185
void from_json(const jsonParser &json)
Definition: SymGroup.cc:3832
void _generate_class_names() const
Definition: SymGroup.cc:1308
void _generate_irrep_names() const
Definition: SymGroup.cc:947
const std::string & get_name() const
Definition: SymGroup.cc:3220
const Array< bool > & get_complex_irrep_list() const
Definition: SymGroup.cc:3204
Array< Index > index2conjugacy_class
Definition: SymGroup.hh:224
SymGroupRepID identity_rep_ID(Index dim) const
Definition: SymGroup.cc:116
Array< SymGroupRepID > m_identity_rep_IDs
identity representations: m_identity_rep_IDs[dim] refers to the Identity representation of dimention ...
Definition: SymGroup.hh:359
SymOp & back()
Definition: Array.hh:177
bool _generate_multi_table() const
Definition: SymGroup.cc:3251
SymGroupRepID add_representation(const SymGroupRep &new_rep) const
Add a new representation by passing a reference. SymGroup will store a copy.
Definition: SymGroup.cc:361
void print_character_table(std::ostream &stream)
Definition: SymGroup.cc:1545
Index ind_prod(Index i, Index j) const
Get index of operation that is result of multiplication of at(i)*at(j)
Definition: SymGroup.cc:3055
SymGroupRepID add_empty_representation() const
Add a new empty representation.
Definition: SymGroup.cc:3109
SymGroupRep * copy() const
Definition: SymGroupRep.hh:83
Basic std::vector like container (deprecated)
ReturnArray< Index > find_all_periodic(const Array< SymOp > &subgroup, double tol=TOL) const
Definition: SymGroup.cc:3332
jsonParser & put_array()
Puts new empty JSON array.
Definition: jsonParser.hh:285
void calc_space_group_in_range(SymGroup &space_group, const Lattice &_cell, Eigen::Vector3i min_trans, Eigen::Vector3i max_trans) const
Definition: SymGroup.cc:3489
std::string crystal_system
Definition: SymGroup.hh:249
void get_point_group_type() const
Definition: SymGroup.cc:565
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)
SymGroup & operator+=(const Eigen::Ref< const SymOp::vector_type > &shift)
Cartesian translation of SymGroup origin by vector 'shift'.
Definition: SymGroup.cc:3691
Array< bool > complex_irrep
Definition: SymGroup.hh:230
Array< Array< Array< Index > > > m_subgroups
Definition: SymGroup.hh:235
static Index GROUP_COUNT
Counts number of instantiated MasterSymGroups, excluding ones created via copy.
Definition: SymGroup.hh:343
bool compare_periodic(const SymOp &a, const SymOp &b, const Lattice &lat, PERIODICITY_TYPE periodicity, double _tol)
Definition: SymGroup.cc:4646
int round(double val)
Definition: CASM_math.cc:6
SymGroup & operator-=(const Eigen::Ref< const SymOp::vector_type > &shift)
Definition: SymGroup.cc:3699
ReturnArray< Index > op_indices() const
Definition: SymGroup.cc:474
bool valid_index(Index i)
void remove(Index ind)
Definition: Array.hh:546
SymGroupRep const & representation(SymGroupRepID i) const
Const access of alternate Representations of a SymGroup.
Definition: SymGroup.cc:375
void sort()
Sort SymOp in the SymGroup.
Definition: SymGroup.cc:394
virtual void clear_tables()
Definition: SymGroup.cc:450
void set_rep(const SymOpRepresentation &base_rep, const SymOpRepresentation &new_rep) const
Definition: SymGroupRep.cc:82