1 /* Copyright (C) 2008 Atsushi Togo */
2 /* All rights reserved. */
3 
4 /* This file is part of spglib. */
5 
6 /* Redistribution and use in source and binary forms, with or without */
7 /* modification, are permitted provided that the following conditions */
8 /* are met: */
9 
10 /* * Redistributions of source code must retain the above copyright */
11 /*   notice, this list of conditions and the following disclaimer. */
12 
13 /* * Redistributions in binary form must reproduce the above copyright */
14 /*   notice, this list of conditions and the following disclaimer in */
15 /*   the documentation and/or other materials provided with the */
16 /*   distribution. */
17 
18 /* * Neither the name of the phonopy project nor the names of its */
19 /*   contributors may be used to endorse or promote products derived */
20 /*   from this software without specific prior written permission. */
21 
22 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
23 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
24 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
25 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE */
26 /* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
27 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */
28 /* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
29 /* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER */
30 /* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT */
31 /* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */
32 /* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
33 /* POSSIBILITY OF SUCH DAMAGE. */
34 
35 #include <math.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include "cell.h"
39 #include "delaunay.h"
40 #include "mathfunc.h"
41 #include "symmetry.h"
42 #include "overlap.h"
43 
44 #include "debug.h"
45 
46 #define NUM_ATOMS_CRITERION_FOR_OPENMP 1000
47 #define ANGLE_REDUCE_RATE 0.95
48 #define NUM_ATTEMPT 100
49 #define PI 3.14159265358979323846
50 /* Tolerance of angle between lattice vectors in degrees */
51 /* Negative value invokes converter from symprec. */
52 static int relative_axes[][3] = {
53   { 1, 0, 0},
54   { 0, 1, 0},
55   { 0, 0, 1},
56   {-1, 0, 0},
57   { 0,-1, 0}, /* 5 */
58   { 0, 0,-1},
59   { 0, 1, 1},
60   { 1, 0, 1},
61   { 1, 1, 0},
62   { 0,-1,-1}, /* 10 */
63   {-1, 0,-1},
64   {-1,-1, 0},
65   { 0, 1,-1},
66   {-1, 0, 1},
67   { 1,-1, 0}, /* 15 */
68   { 0,-1, 1},
69   { 1, 0,-1},
70   {-1, 1, 0},
71   { 1, 1, 1},
72   {-1,-1,-1}, /* 20 */
73   {-1, 1, 1},
74   { 1,-1, 1},
75   { 1, 1,-1},
76   { 1,-1,-1},
77   {-1, 1,-1}, /* 25 */
78   {-1,-1, 1},
79 };
80 
81 static int identity[3][3] = {{1, 0, 0},
82                              {0, 1, 0},
83                              {0, 0, 1}};
84 
85 static int get_index_with_least_atoms(const Cell *cell);
86 static VecDBL * get_translation(SPGCONST int rot[3][3],
87                                 const Cell *cell,
88                                 const double symprec,
89                                 const int is_identity);
90 static Symmetry * get_operations(const Cell *primitive,
91                                  const double symprec,
92                                  const double angle_symprec);
93 static Symmetry * reduce_operation(const Cell * primitive,
94                                    const Symmetry * symmetry,
95                                    const double symprec,
96                                    const double angle_symprec,
97                                    const int is_pure_trans);
98 static int search_translation_part(int atoms_found[],
99                                    const Cell * cell,
100                                    SPGCONST int rot[3][3],
101                                    const int min_atom_index,
102                                    const double origin[3],
103                                    const double symprec,
104                                    const int is_identity);
105 static int search_pure_translations(int atoms_found[],
106                                     const Cell * cell,
107                                     const double trans[3],
108                                     const double symprec);
109 static int is_overlap_all_atoms(const double test_trans[3],
110                                 SPGCONST int rot[3][3],
111                                 const Cell * cell,
112                                 const double symprec,
113                                 const int is_identity);
114 static PointSymmetry
115 transform_pointsymmetry(SPGCONST PointSymmetry * point_sym_prim,
116                         SPGCONST double new_lattice[3][3],
117                         SPGCONST double original_lattice[3][3]);
118 static Symmetry *
119 get_space_group_operations(SPGCONST PointSymmetry *lattice_sym,
120                            const Cell *primitive,
121                            const double symprec);
122 static void set_axes(int axes[3][3],
123                      const int a1, const int a2, const int a3);
124 static PointSymmetry get_lattice_symmetry(SPGCONST double cell_lattice[3][3],
125                                           const double symprec,
126                                           const double angle_symprec);
127 static int is_identity_metric(SPGCONST double metric_rotated[3][3],
128                               SPGCONST double metric_orig[3][3],
129                               const double symprec,
130                               const double angle_symprec);
131 static double get_angle(SPGCONST double metric[3][3],
132                         const int i,
133                         const int j);
134 
135 /* Return NULL if failed */
sym_alloc_symmetry(const int size)136 Symmetry * sym_alloc_symmetry(const int size)
137 {
138   Symmetry *symmetry;
139 
140   symmetry = NULL;
141 
142   if (size < 1) {
143     return NULL;
144   }
145 
146   if ((symmetry = (Symmetry*) malloc(sizeof(Symmetry))) == NULL) {
147     warning_print("spglib: Memory could not be allocated ");
148     return NULL;
149   }
150 
151   symmetry->size = size;
152   symmetry->rot = NULL;
153   symmetry->trans = NULL;
154 
155   if ((symmetry->rot =
156        (int (*)[3][3]) malloc(sizeof(int[3][3]) * size)) == NULL) {
157     warning_print("spglib: Memory could not be allocated ");
158     warning_print("(line %d, %s).\n", __LINE__, __FILE__);
159     free(symmetry);
160     symmetry = NULL;
161     return NULL;
162   }
163   if ((symmetry->trans =
164        (double (*)[3]) malloc(sizeof(double[3]) * size)) == NULL) {
165     warning_print("spglib: Memory could not be allocated ");
166     warning_print("(line %d, %s).\n", __LINE__, __FILE__);
167     free(symmetry->rot);
168     symmetry->rot = NULL;
169     free(symmetry);
170     symmetry = NULL;
171     return NULL;
172   }
173 
174   return symmetry;
175 }
176 
sym_free_symmetry(Symmetry * symmetry)177 void sym_free_symmetry(Symmetry *symmetry)
178 {
179   if (symmetry->size > 0) {
180     free(symmetry->rot);
181     symmetry->rot = NULL;
182     free(symmetry->trans);
183     symmetry->trans = NULL;
184   }
185   free(symmetry);
186 }
187 
188 /* Return NULL if failed */
sym_get_operation(const Cell * primitive,const double symprec,const double angle_tolerance)189 Symmetry * sym_get_operation(const Cell * primitive,
190                              const double symprec,
191                              const double angle_tolerance)
192 {
193 
194   debug_print("sym_get_operations:\n");
195 
196   return get_operations(primitive, symprec, angle_tolerance);
197 }
198 
199 /* Return NULL if failed */
sym_reduce_operation(const Cell * primitive,const Symmetry * symmetry,const double symprec,const double angle_tolerance)200 Symmetry * sym_reduce_operation(const Cell * primitive,
201                                 const Symmetry * symmetry,
202                                 const double symprec,
203                                 const double angle_tolerance)
204 {
205   return reduce_operation(primitive, symmetry, symprec, angle_tolerance, 0);
206 }
207 
208 /* Return NULL if failed */
sym_get_pure_translation(const Cell * cell,const double symprec)209 VecDBL * sym_get_pure_translation(const Cell *cell,
210                                   const double symprec)
211 {
212   int multi;
213   VecDBL * pure_trans;
214 
215   debug_print("sym_get_pure_translation (tolerance = %f):\n", symprec);
216 
217   multi = 0;
218   pure_trans = NULL;
219 
220   if ((pure_trans = get_translation(identity, cell, symprec, 1)) == NULL) {
221     warning_print("spglib: get_translation failed (line %d, %s).\n",
222                   __LINE__, __FILE__);
223     return NULL;
224   }
225 
226   multi = pure_trans->size;
227   if ((cell->size / multi) * multi == cell->size) {
228     debug_print("  sym_get_pure_translation: pure_trans->size = %d\n", multi);
229   } else {
230     ;
231     warning_print("spglib: Finding pure translation failed (line %d, %s).\n",
232                   __LINE__, __FILE__);
233     warning_print("        cell->size %d, multi %d\n", cell->size, multi);
234   }
235 
236   return pure_trans;
237 }
238 
239 /* Return NULL if failed */
sym_reduce_pure_translation(const Cell * cell,const VecDBL * pure_trans,const double symprec,const double angle_tolerance)240 VecDBL * sym_reduce_pure_translation(const Cell * cell,
241                                      const VecDBL * pure_trans,
242                                      const double symprec,
243                                      const double angle_tolerance)
244 {
245   int i, multi;
246   Symmetry *symmetry, *symmetry_reduced;
247   VecDBL * pure_trans_reduced;
248 
249   symmetry = NULL;
250   symmetry_reduced = NULL;
251   pure_trans_reduced = NULL;
252 
253   multi = pure_trans->size;
254 
255   if ((symmetry = sym_alloc_symmetry(multi)) == NULL) {
256     return NULL;
257   }
258 
259   for (i = 0; i < multi; i++) {
260     mat_copy_matrix_i3(symmetry->rot[i], identity);
261     mat_copy_vector_d3(symmetry->trans[i], pure_trans->vec[i]);
262   }
263 
264   if ((symmetry_reduced =
265        reduce_operation(cell, symmetry, symprec, angle_tolerance, 1)) == NULL) {
266     sym_free_symmetry(symmetry);
267     symmetry = NULL;
268     return NULL;
269   }
270 
271   sym_free_symmetry(symmetry);
272   symmetry = NULL;
273   multi = symmetry_reduced->size;
274 
275   if ((pure_trans_reduced = mat_alloc_VecDBL(multi)) == NULL) {
276     sym_free_symmetry(symmetry_reduced);
277     symmetry_reduced = NULL;
278     return NULL;
279   }
280 
281   for (i = 0; i < multi; i++) {
282     mat_copy_vector_d3(pure_trans_reduced->vec[i], symmetry_reduced->trans[i]);
283   }
284   sym_free_symmetry(symmetry_reduced);
285   symmetry_reduced = NULL;
286 
287   return pure_trans_reduced;
288 }
289 
290 /* 1) Pointgroup operations of the primitive cell are obtained. */
291 /*    These are constrained by the input cell lattice pointgroup, */
292 /*    i.e., even if the lattice of the primitive cell has higher */
293 /*    symmetry than that of the input cell, it is not considered. */
294 /* 2) Spacegroup operations are searched for the primitive cell */
295 /*    using the constrained point group operations. */
296 /* 3) The spacegroup operations for the primitive cell are */
297 /*    transformed to those of original input cells, if the input cell */
298 /*    was not a primitive cell. */
get_operations(const Cell * primitive,const double symprec,const double angle_symprec)299 static Symmetry * get_operations(const Cell *primitive,
300                                  const double symprec,
301                                  const double angle_symprec)
302 {
303   PointSymmetry lattice_sym;
304   Symmetry *symmetry;
305 
306   debug_print("get_operations:\n");
307 
308   symmetry = NULL;
309 
310   lattice_sym = get_lattice_symmetry(primitive->lattice,
311                                      symprec,
312                                      angle_symprec);
313   if (lattice_sym.size == 0) {
314     return NULL;
315   }
316 
317   if ((symmetry = get_space_group_operations(&lattice_sym,
318                                              primitive,
319                                              symprec)) == NULL) {
320     return NULL;
321   }
322 
323   return symmetry;
324 }
325 
326 /* Return NULL if failed */
reduce_operation(const Cell * primitive,const Symmetry * symmetry,const double symprec,const double angle_symprec,const int is_pure_trans)327 static Symmetry * reduce_operation(const Cell * primitive,
328                                    const Symmetry * symmetry,
329                                    const double symprec,
330                                    const double angle_symprec,
331                                    const int is_pure_trans)
332 {
333   int i, j, num_sym;
334   Symmetry * sym_reduced;
335   PointSymmetry point_symmetry;
336   MatINT *rot;
337   VecDBL *trans;
338 
339   debug_print("reduce_operation:\n");
340 
341   sym_reduced = NULL;
342   rot = NULL;
343   trans = NULL;
344 
345   if (is_pure_trans) {
346     point_symmetry.size = 1;
347     mat_copy_matrix_i3(point_symmetry.rot[0], identity);
348   } else {
349     point_symmetry = get_lattice_symmetry(primitive->lattice,
350                                           symprec,
351                                           angle_symprec);
352     if (point_symmetry.size == 0) {
353       return NULL;
354     }
355   }
356 
357   if ((rot = mat_alloc_MatINT(symmetry->size)) == NULL) {
358     return NULL;
359   }
360 
361   if ((trans = mat_alloc_VecDBL(symmetry->size)) == NULL) {
362     mat_free_MatINT(rot);
363     rot = NULL;
364     return NULL;
365   }
366 
367   num_sym = 0;
368   for (i = 0; i < point_symmetry.size; i++) {
369     for (j = 0; j < symmetry->size; j++) {
370       if (mat_check_identity_matrix_i3(point_symmetry.rot[i],
371                                        symmetry->rot[j])) {
372         if (is_overlap_all_atoms(symmetry->trans[j],
373                                  symmetry->rot[j],
374                                  primitive,
375                                  symprec,
376                                  0)) {
377           mat_copy_matrix_i3(rot->mat[num_sym], symmetry->rot[j]);
378           mat_copy_vector_d3(trans->vec[num_sym], symmetry->trans[j]);
379           num_sym++;
380         }
381       }
382     }
383   }
384 
385   if ((sym_reduced = sym_alloc_symmetry(num_sym)) != NULL) {
386     for (i = 0; i < num_sym; i++) {
387       mat_copy_matrix_i3(sym_reduced->rot[i], rot->mat[i]);
388       mat_copy_vector_d3(sym_reduced->trans[i], trans->vec[i]);
389     }
390   }
391 
392   mat_free_MatINT(rot);
393   rot = NULL;
394   mat_free_VecDBL(trans);
395   trans = NULL;
396 
397   return sym_reduced;
398 }
399 
400 /* Look for the translations which satisfy the input symmetry operation. */
401 /* This function is heaviest in this code. */
402 /* Return NULL if failed */
get_translation(SPGCONST int rot[3][3],const Cell * cell,const double symprec,const int is_identity)403 static VecDBL * get_translation(SPGCONST int rot[3][3],
404                                 const Cell *cell,
405                                 const double symprec,
406                                 const int is_identity)
407 {
408   int i, j, k, min_atom_index, num_trans;
409   int *is_found;
410   double origin[3];
411   VecDBL *trans;
412 
413   debug_print("get_translation (tolerance = %f):\n", symprec);
414 
415   num_trans = 0;
416   is_found = NULL;
417   trans = NULL;
418 
419   if ((is_found = (int*) malloc(sizeof(int)*cell->size)) == NULL) {
420     warning_print("spglib: Memory could not be allocated ");
421     return NULL;
422   }
423 
424   for (i = 0; i < cell->size; i++) {
425     is_found[i] = 0;
426   }
427 
428   /* Look for the atom index with least number of atoms within same type */
429   min_atom_index = get_index_with_least_atoms(cell);
430   if (min_atom_index == -1) {
431     debug_print("spglib: get_index_with_least_atoms failed.\n");
432     goto ret;
433   }
434 
435   /* Set min_atom_index as the origin to measure the distance between atoms. */
436   mat_multiply_matrix_vector_id3(origin, rot, cell->position[min_atom_index]);
437 
438   num_trans = search_translation_part(is_found,
439                                       cell,
440                                       rot,
441                                       min_atom_index,
442                                       origin,
443                                       symprec,
444                                       is_identity);
445   if (num_trans == -1 || num_trans == 0) {
446     goto ret;
447   }
448 
449   if ((trans = mat_alloc_VecDBL(num_trans)) == NULL) {
450     goto ret;
451   }
452 
453   k = 0;
454   for (i = 0; i < cell->size; i++) {
455     if (is_found[i]) {
456       for (j = 0; j < 3; j++) {
457         trans->vec[k][j] = cell->position[i][j] - origin[j];
458         trans->vec[k][j] -= mat_Nint(trans->vec[k][j]);
459       }
460       k++;
461     }
462   }
463 
464  ret:
465   free(is_found);
466   is_found = NULL;
467 
468   return trans;
469 }
470 
471 /* Returns -1 on failure. */
search_translation_part(int atoms_found[],const Cell * cell,SPGCONST int rot[3][3],const int min_atom_index,const double origin[3],const double symprec,const int is_identity)472 static int search_translation_part(int atoms_found[],
473                                    const Cell * cell,
474                                    SPGCONST int rot[3][3],
475                                    const int min_atom_index,
476                                    const double origin[3],
477                                    const double symprec,
478                                    const int is_identity)
479 {
480   int i, j, num_trans, is_overlap;
481   double trans[3];
482   OverlapChecker * checker;
483 
484   checker = NULL;
485 
486   if ((checker = ovl_overlap_checker_init(cell)) == NULL) {
487     return -1;
488   }
489 
490   num_trans = 0;
491 
492   for (i = 0; i < cell->size; i++) {
493     if (atoms_found[i]) {
494       continue;
495     }
496 
497     if (cell->types[i] != cell->types[min_atom_index]) {
498       continue;
499     }
500 
501     for (j = 0; j < 3; j++) {
502       trans[j] = cell->position[i][j] - origin[j];
503     }
504 
505     is_overlap = ovl_check_total_overlap(checker,
506                                          trans,
507                                          rot,
508                                          symprec,
509                                          is_identity);
510     if (is_overlap == -1) {
511       goto err;
512     } else if (is_overlap) {
513       atoms_found[i] = 1;
514       num_trans++;
515       if (is_identity) {
516         num_trans += search_pure_translations(atoms_found,
517                                               cell,
518                                               trans,
519                                               symprec);
520       }
521     }
522   }
523 
524   ovl_overlap_checker_free(checker);
525   checker = NULL;
526   return num_trans;
527 
528  err:
529   ovl_overlap_checker_free(checker);
530   checker = NULL;
531   return -1;
532 }
533 
search_pure_translations(int atoms_found[],const Cell * cell,const double trans[3],const double symprec)534 static int search_pure_translations(int atoms_found[],
535                                     const Cell * cell,
536                                     const double trans[3],
537                                     const double symprec)
538 {
539   int i, j, num_trans, i_atom, initial_atom;
540   int *copy_atoms_found;
541   double vec[3];
542 
543   num_trans = 0;
544 
545   copy_atoms_found = (int*)malloc(sizeof(int) * cell->size);
546   for (i = 0; i < cell->size; i++) {
547     copy_atoms_found[i] = atoms_found[i];
548   }
549 
550   for (initial_atom = 0; initial_atom < cell->size; initial_atom++) {
551     if (!copy_atoms_found[initial_atom]) {
552       continue;
553     }
554 
555     i_atom = initial_atom;
556 
557     for (i = 0; i < cell->size; i++) {
558       for (j = 0; j < 3; j++) {
559         vec[j] = cell->position[i_atom][j] + trans[j];
560       }
561 
562       for (j = 0; j < cell->size; j++) {
563         if (cel_is_overlap_with_same_type(vec,
564                                           cell->position[j],
565                                           cell->types[i_atom],
566                                           cell->types[j],
567                                           cell->lattice,
568                                           symprec)) {
569           if (!atoms_found[j]) {
570             atoms_found[j] = 1;
571             num_trans++;
572           }
573           i_atom = j;
574 
575           break;
576         }
577       }
578 
579       if (i_atom == initial_atom) {
580         break;
581       }
582     }
583   }
584 
585   free(copy_atoms_found);
586 
587   return num_trans;
588 }
589 
590 /* Thoroughly confirms that a given symmetry operation is a symmetry. */
591 /* This is a convenient wrapper around ovl_check_total_overlap. */
592 /* -1: Error.  0: Not a symmetry.  1: Is a symmetry. */
is_overlap_all_atoms(const double trans[3],SPGCONST int rot[3][3],const Cell * cell,const double symprec,const int is_identity)593 static int is_overlap_all_atoms(const double trans[3],
594                                 SPGCONST int rot[3][3],
595                                 const Cell * cell,
596                                 const double symprec,
597                                 const int is_identity)
598 {
599   OverlapChecker * checker;
600   int result;
601 
602   checker = NULL;
603 
604   if ((checker = ovl_overlap_checker_init(cell)) == NULL) {
605     return -1;
606   }
607 
608   result = ovl_check_total_overlap(checker,
609                                    trans,
610                                    rot,
611                                    symprec,
612                                    is_identity);
613 
614   ovl_overlap_checker_free(checker);
615   checker = NULL;
616 
617   return result;
618 }
619 
get_index_with_least_atoms(const Cell * cell)620 static int get_index_with_least_atoms(const Cell *cell)
621 {
622   int i, j, min, min_index;
623   int *mapping;
624 
625   mapping = NULL;
626 
627   if ((mapping = (int *) malloc(sizeof(int) * cell->size)) == NULL) {
628     warning_print("spglib: Memory could not be allocated ");
629     return -1;
630   }
631 
632   for (i = 0; i < cell->size; i++) {
633     mapping[i] = 0;
634   }
635 
636   for (i = 0; i < cell->size; i++) {
637     for (j = 0; j < cell->size; j++) {
638       if (cell->types[i] == cell->types[j]) {
639         mapping[j]++;
640         break;
641       }
642     }
643   }
644 
645   min = mapping[0];
646   min_index = 0;
647   for (i = 0; i < cell->size; i++) {
648     if (min > mapping[i] && mapping[i] >0) {
649       min = mapping[i];
650       min_index = i;
651     }
652   }
653 
654   free(mapping);
655   mapping = NULL;
656 
657   return min_index;
658 }
659 
660 /* Return NULL if failed */
661 static Symmetry *
get_space_group_operations(SPGCONST PointSymmetry * lattice_sym,const Cell * primitive,const double symprec)662 get_space_group_operations(SPGCONST PointSymmetry *lattice_sym,
663                            const Cell *primitive,
664                            const double symprec)
665 {
666   int i, j, num_sym, total_num_sym;
667   VecDBL **trans;
668   Symmetry *symmetry;
669 
670   debug_print("get_space_group_operations (tolerance = %f):\n", symprec);
671 
672   trans = NULL;
673   symmetry = NULL;
674 
675   if ((trans = (VecDBL**) malloc(sizeof(VecDBL*) * lattice_sym->size))
676       == NULL) {
677     warning_print("spglib: Memory could not be allocated ");
678     return NULL;
679   }
680 
681   for (i = 0; i < lattice_sym->size; i++) {
682     trans[i] = NULL;
683   }
684 
685   total_num_sym = 0;
686   for (i = 0; i < lattice_sym->size; i++) {
687 
688     if ((trans[i] = get_translation(lattice_sym->rot[i], primitive, symprec, 0))
689         != NULL) {
690 
691       debug_print("  match translation %d/%d; tolerance = %f\n",
692                   i + 1, lattice_sym->size, symprec);
693 
694       total_num_sym += trans[i]->size;
695     }
696   }
697 
698   if ((symmetry = sym_alloc_symmetry(total_num_sym)) == NULL) {
699     goto ret;
700   }
701 
702   num_sym = 0;
703   for (i = 0; i < lattice_sym->size; i++) {
704     if (trans[i] == NULL) {
705       continue;
706     }
707     for (j = 0; j < trans[i]->size; j++) {
708       mat_copy_vector_d3(symmetry->trans[num_sym + j], trans[i]->vec[j]);
709       mat_copy_matrix_i3(symmetry->rot[num_sym + j], lattice_sym->rot[i]);
710     }
711     num_sym += trans[i]->size;
712   }
713 
714  ret:
715   for (i = 0; i < lattice_sym->size; i++) {
716     if (trans[i] != NULL) {
717       mat_free_VecDBL(trans[i]);
718       trans[i] = NULL;
719     }
720   }
721   free(trans);
722   trans = NULL;
723 
724   return symmetry;
725 }
726 
727 /* lattice_sym.size = 0 is returned if failed. */
get_lattice_symmetry(SPGCONST double cell_lattice[3][3],const double symprec,const double angle_symprec)728 static PointSymmetry get_lattice_symmetry(SPGCONST double cell_lattice[3][3],
729                                           const double symprec,
730                                           const double angle_symprec)
731 {
732   int i, j, k, attempt, num_sym;
733   double angle_tol;
734   int axes[3][3];
735   double lattice[3][3], min_lattice[3][3];
736   double metric[3][3], metric_orig[3][3];
737   PointSymmetry lattice_sym;
738 
739   debug_print("get_lattice_symmetry:\n");
740 
741   lattice_sym.size = 0;
742 
743   if (! del_delaunay_reduce(min_lattice, cell_lattice, symprec)) {
744     goto err;
745   }
746 
747   mat_get_metric(metric_orig, min_lattice);
748   angle_tol = angle_symprec;
749 
750   for (attempt = 0; attempt < NUM_ATTEMPT; attempt++) {
751     num_sym = 0;
752     for (i = 0; i < 26; i++) {
753       for (j = 0; j < 26; j++) {
754         for (k = 0; k < 26; k++) {
755           set_axes(axes, i, j, k);
756           if (! ((mat_get_determinant_i3(axes) == 1) ||
757                  (mat_get_determinant_i3(axes) == -1))) {
758             continue;
759           }
760           mat_multiply_matrix_di3(lattice, min_lattice, axes);
761           mat_get_metric(metric, lattice);
762 
763           if (is_identity_metric(metric, metric_orig, symprec, angle_tol)) {
764             if (num_sym > 47) {
765               warning_print("spglib: Too many lattice symmetries was found.\n");
766               if (angle_tol > 0) {
767                 angle_tol *= ANGLE_REDUCE_RATE;
768                 warning_print(
769                   "        Reduce angle tolerance to %f\n", angle_tol);
770               }
771               warning_print("        (line %d, %s).\n", __LINE__, __FILE__);
772               goto next_attempt;
773             }
774 
775             mat_copy_matrix_i3(lattice_sym.rot[num_sym], axes);
776             num_sym++;
777           }
778         }
779       }
780     }
781 
782     if (num_sym < 49 || angle_tol < 0) {
783       lattice_sym.size = num_sym;
784       return transform_pointsymmetry(&lattice_sym, cell_lattice, min_lattice);
785     }
786 
787   next_attempt:
788     ;
789   }
790 
791  err:
792   debug_print("get_lattice_symmetry failed.\n");
793   return lattice_sym;
794 }
795 
is_identity_metric(SPGCONST double metric_rotated[3][3],SPGCONST double metric_orig[3][3],const double symprec,const double angle_symprec)796 static int is_identity_metric(SPGCONST double metric_rotated[3][3],
797                               SPGCONST double metric_orig[3][3],
798                               const double symprec,
799                               const double angle_symprec)
800 {
801   int i, j, k;
802   int elem_sets[3][2] = {{0, 1},
803                          {0, 2},
804                          {1, 2}};
805   double cos1, cos2, x, length_ave2, sin_dtheta2;
806   double length_orig[3], length_rot[3];
807 
808   for (i = 0; i < 3; i++) {
809     length_orig[i] = sqrt(metric_orig[i][i]);
810     length_rot[i] = sqrt(metric_rotated[i][i]);
811     if (mat_Dabs(length_orig[i] - length_rot[i]) > symprec) {
812       goto fail;
813     }
814   }
815 
816   for (i = 0; i < 3; i++) {
817     j = elem_sets[i][0];
818     k = elem_sets[i][1];
819     if (angle_symprec > 0) {
820       if (mat_Dabs(get_angle(metric_orig, j, k) -
821                    get_angle(metric_rotated, j, k)) > angle_symprec) {
822         goto fail;
823       }
824     } else {
825       /* dtheta = arccos(cos(theta1) - arccos(cos(theta2))) */
826       /*        = arccos(c1) - arccos(c2) */
827       /*        = arccos(c1c2 + sqrt((1-c1^2)(1-c2^2))) */
828       /* sin(dtheta) = sin(arccos(x)) = sqrt(1 - x^2) */
829       cos1 = metric_orig[j][k] / length_orig[j] / length_orig[k];
830       cos2 = metric_rotated[j][k] / length_rot[j] / length_rot[k];
831       x = cos1 * cos2 + sqrt(1 - cos1 * cos1) * sqrt(1 - cos2 * cos2);
832       sin_dtheta2 = 1 - x * x;
833       length_ave2 = ((length_orig[j] + length_rot[j]) *
834                      (length_orig[k] + length_rot[k])) / 4;
835       if (sin_dtheta2 > 1e-12) {
836         if (sin_dtheta2 * length_ave2 > symprec * symprec) {
837           goto fail;
838         }
839       }
840     }
841   }
842 
843   return 1;
844 
845  fail:
846   return 0;
847 }
848 
get_angle(SPGCONST double metric[3][3],const int i,const int j)849 static double get_angle(SPGCONST double metric[3][3],
850                         const int i,
851                         const int j)
852 {
853   double length_i, length_j;
854 
855   length_i = sqrt(metric[i][i]);
856   length_j = sqrt(metric[j][j]);
857 
858   return acos(metric[i][j] / length_i / length_j) / PI * 180;
859 }
860 
861 static PointSymmetry
transform_pointsymmetry(SPGCONST PointSymmetry * lat_sym_orig,SPGCONST double new_lattice[3][3],SPGCONST double original_lattice[3][3])862 transform_pointsymmetry(SPGCONST PointSymmetry * lat_sym_orig,
863                         SPGCONST double new_lattice[3][3],
864                         SPGCONST double original_lattice[3][3])
865 {
866   int i, size;
867   double trans_mat[3][3], inv_mat[3][3], drot[3][3];
868   PointSymmetry lat_sym_new;
869 
870   lat_sym_new.size = 0;
871 
872   mat_inverse_matrix_d3(inv_mat, original_lattice, 0);
873   mat_multiply_matrix_d3(trans_mat, inv_mat, new_lattice);
874 
875   size = 0;
876   for (i = 0; i < lat_sym_orig->size; i++) {
877     mat_cast_matrix_3i_to_3d(drot, lat_sym_orig->rot[i]);
878     mat_get_similar_matrix_d3(drot, drot, trans_mat, 0);
879 
880     /* new_lattice may have lower point symmetry than original_lattice.*/
881     /* The operations that have non-integer elements are not counted. */
882     if (mat_is_int_matrix(drot, mat_Dabs(mat_get_determinant_d3(trans_mat)) / 10)) {
883       mat_cast_matrix_3d_to_3i(lat_sym_new.rot[size], drot);
884       if (abs(mat_get_determinant_i3(lat_sym_new.rot[size])) != 1) {
885         warning_print("spglib: A point symmetry operation is not unimodular.");
886         warning_print("(line %d, %s).\n", __LINE__, __FILE__);
887         goto err;
888       }
889       size++;
890     }
891   }
892 
893 #ifdef SPGWARNING
894   if (! (lat_sym_orig->size == size)) {
895     warning_print("spglib: Some of point symmetry operations were dropped.");
896     warning_print("(line %d, %s).\n", __LINE__, __FILE__);
897   }
898 #endif
899 
900   lat_sym_new.size = size;
901   return lat_sym_new;
902 
903  err:
904   return lat_sym_new;
905 }
906 
set_axes(int axes[3][3],const int a1,const int a2,const int a3)907 static void set_axes(int axes[3][3],
908                      const int a1, const int a2, const int a3)
909 {
910   int i;
911   for (i = 0; i < 3; i++) {axes[i][0] = relative_axes[a1][i]; }
912   for (i = 0; i < 3; i++) {axes[i][1] = relative_axes[a2][i]; }
913   for (i = 0; i < 3; i++) {axes[i][2] = relative_axes[a3][i]; }
914 }
915