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