Octopus
symmetries_finite.c
Go to the documentation of this file.
1/*
2 * Brute force symmetry analyzer.
3 * This is actually C++ program, masquerading as a C one!
4 *
5 * (C) 2011 X. Andrade Converted to a Fortran library.
6 * (C) 1996, 2003 S. Patchkovskii, Serguei.Patchkovskii@sympatico.ca
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * Revision 1.16 2003/04/04 13:05:03 patchkov
23 * Revision 1.15 2000/01/25 16:47:17 patchkov
24 * Revision 1.14 2000/01/25 16:39:08 patchkov
25 * Revision 1.13 1996/05/24 12:32:08 ps
26 * Revision 1.12 1996/05/23 16:10:47 ps
27 * First reasonably stable version.
28 *
29 */
30
31#include <config.h>
32
33#include <math.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include <string_f.h>
39
40#define DIMENSION 3
41#define MAXPARAM 7
42
43typedef struct {
44 int type;
45 double x[DIMENSION];
46} ATOM;
47
48/*
49 * All specific structures should have corresponding elements in the
50 * same position generic structure does.
51 *
52 * Planes are characterized by the surface normal direction
53 * (taken in the direction *from* the coordinate origin)
54 * and distance from the coordinate origin to the plane
55 * in the direction of the surface normal.
56 *
57 * Inversion is characterized by location of the inversion center.
58 *
59 * Rotation is characterized by a vector (distance+direction) from the origin
60 * to the rotation axis, axis direction and rotation order. Rotations
61 * are in the clockwise direction looking opposite to the direction
62 * of the axis. Note that this definition of the rotation axis
63 * is *not* unique, since an arbitrary multiple of the axis direction
64 * can be added to the position vector without changing actual operation.
65 *
66 * Mirror rotation is defined by the same parameters as normal rotation,
67 * but the origin is now unambiguous since it defines the position of the
68 * plane associated with the axis.
69 *
70 */
72typedef struct _SYMMETRY_ELEMENT_ {
73 void (*transform_atom)(struct _SYMMETRY_ELEMENT_ *el, ATOM *from, ATOM *to);
74 int *transform; /* Correspondence table for the transformation */
75 int order; /* Applying transformation this many times is identity */
76 int nparam; /* 4 for inversion and planes, 7 for axes */
77 double maxdev; /* Larges error associated with the element */
78 double distance;
79 double normal[DIMENSION];
80 double direction[DIMENSION];
82
83typedef struct {
84 char *group_name; /* Canonical group name */
85 char *symmetry_code; /* Group symmetry code */
86 int (*check)(void); /* Additional verification routine, not used */
89static double ToleranceSame = 1e-3;
90static double TolerancePrimary = 5e-2;
91static double ToleranceFinal = 1e-4;
92static double MaxOptStep = 5e-1;
93static double MinOptStep = 1e-7;
94static double GradientStep = 1e-7;
95static double OptChangeThreshold = 1e-10;
96static double CenterOfSomething[DIMENSION];
97static double *DistanceFromCenter = NULL;
98static int verbose = 0;
99static int MaxOptCycles = 200;
100static int OptChangeHits = 5;
101static int MaxAxisOrder = 20;
102static int AtomsCount = 0;
103static ATOM *Atoms = NULL;
104static int PlanesCount = 0;
105static SYMMETRY_ELEMENT **Planes = NULL;
108static SYMMETRY_ELEMENT **InversionCenters = NULL;
109static int NormalAxesCount = 0;
110static SYMMETRY_ELEMENT **NormalAxes = NULL;
111static int ImproperAxesCount = 0;
112static SYMMETRY_ELEMENT **ImproperAxes = NULL;
113static int *NormalAxesCounts = NULL;
114static int *ImproperAxesCounts = NULL;
115static int BadOptimization = 0;
116static char *SymmetryCode = NULL; /*"" ;*/
118 * Statistics
119 */
120static long StatTotal = 0;
121static long StatEarly = 0;
122static long StatPairs = 0;
123static long StatDups = 0;
124static long StatOrder = 0;
125static long StatOpt = 0;
126static long StatAccept = 0;
129 * Point groups I know about
130 */
131int true(void) { return 1; }
133 {"C1", "", true},
134 {"Cs", "(sigma) ", true},
135 {"Ci", "(i) ", true},
136 {"C2", "(C2) ", true},
137 {"C3", "(C3) ", true},
138 {"C4", "(C4) (C2) ", true},
139 {"C5", "(C5) ", true},
140 {"C6", "(C6) (C3) (C2) ", true},
141 {"C7", "(C7) ", true},
142 {"C8", "(C8) (C4) (C2) ", true},
143 {"D2", "3*(C2) ", true},
144 {"D3", "(C3) 3*(C2) ", true},
145 {"D4", "(C4) 5*(C2) ", true},
146 {"D5", "(C5) 5*(C2) ", true},
147 {"D6", "(C6) (C3) 7*(C2) ", true},
148 {"D7", "(C7) 7*(C2) ", true},
149 {"D8", "(C8) (C4) 9*(C2) ", true},
150 {"C2v", "(C2) 2*(sigma) ", true},
151 {"C3v", "(C3) 3*(sigma) ", true},
152 {"C4v", "(C4) (C2) 4*(sigma) ", true},
153 {"C5v", "(C5) 5*(sigma) ", true},
154 {"C6v", "(C6) (C3) (C2) 6*(sigma) ", true},
155 {"C7v", "(C7) 7*(sigma) ", true},
156 {"C8v", "(C8) (C4) (C2) 8*(sigma) ", true},
157 {"C2h", "(i) (C2) (sigma) ", true},
158 {"C3h", "(C3) (S3) (sigma) ", true},
159 {"C4h", "(i) (C4) (C2) (S4) (sigma) ", true},
160 {"C5h", "(C5) (S5) (sigma) ", true},
161 {"C6h", "(i) (C6) (C3) (C2) (S6) (S3) (sigma) ", true},
162 {"C7h", "(C7) (S7) (sigma) ", true},
163 {"C8h", "(i) (C8) (C4) (C2) (S8) (S4) (sigma) ", true},
164 {"D2h", "(i) 3*(C2) 3*(sigma) ", true},
165 {"D3h", "(C3) 3*(C2) (S3) 4*(sigma) ", true},
166 {"D4h", "(i) (C4) 5*(C2) (S4) 5*(sigma) ", true},
167 {"D5h", "(C5) 5*(C2) (S5) 6*(sigma) ", true},
168 {"D6h", "(i) (C6) (C3) 7*(C2) (S6) (S3) 7*(sigma) ", true},
169 {"D7h", "(C7) 7*(C2) (S7) 8*(sigma) ", true},
170 {"D8h", "(i) (C8) (C4) 9*(C2) (S8) (S4) 9*(sigma) ", true},
171 {"D2d", "3*(C2) (S4) 2*(sigma) ", true},
172 {"D3d", "(i) (C3) 3*(C2) (S6) 3*(sigma) ", true},
173 {"D4d", "(C4) 5*(C2) (S8) 4*(sigma) ", true},
174 {"D5d", "(i) (C5) 5*(C2) (S10) 5*(sigma) ", true},
175 {"D6d", "(C6) (C3) 7*(C2) (S12) (S4) 6*(sigma) ", true},
176 {"D7d", "(i) (C7) 7*(C2) (S14) 7*(sigma) ", true},
177 {"D8d", "(C8) (C4) 9*(C2) (S16) 8*(sigma) ", true},
178 {"S4", "(C2) (S4) ", true},
179 {"S6", "(i) (C3) (S6) ", true},
180 {"S8", "(C4) (C2) (S8) ", true},
181 {"T", "4*(C3) 3*(C2) ", true},
182 {"Th", "(i) 4*(C3) 3*(C2) 4*(S6) 3*(sigma) ", true},
183 {"Td", "4*(C3) 3*(C2) 3*(S4) 6*(sigma) ", true},
184 {"O", "3*(C4) 4*(C3) 9*(C2) ", true},
185 {"Oh", "(i) 3*(C4) 4*(C3) 9*(C2) 4*(S6) 3*(S4) 9*(sigma) ", true},
186 {"Cinfv", "(Cinf) (sigma) ", true},
187 {"Dinfh", "(i) (Cinf) (C2) 2*(sigma) ", true},
188 {"I", "6*(C5) 10*(C3) 15*(C2) ", true},
189 {"Ih", "(i) 6*(C5) 10*(C3) 15*(C2) 6*(S10) 10*(S6) 15*(sigma) ", true},
190 {"Kh", "(i) (Cinf) (sigma) ", true},
191};
192#define PointGroupsCount (sizeof(PointGroups) / sizeof(POINT_GROUP))
193char *PointGroupRejectionReason = NULL;
194
195/*
196 * Generic functions
197 */
198
199double pow2(double x) { return x * x; }
200
202 int i, j, k, best_j;
203 char *atom_used = calloc(AtomsCount, 1);
204 double distance, best_distance;
205 ATOM symmetric;
206
207 if (atom_used == NULL) {
208 fprintf(stderr, "Out of memory for tagging array in establish_pairs()\n");
209 exit(EXIT_FAILURE);
210 }
211 for (i = 0; i < AtomsCount; i++) {
212 if (elem->transform[i] >= AtomsCount) { /* No symmetric atom yet */
213 if (verbose > 2)
214 printf(" looking for a pair for %d\n", i);
215 elem->transform_atom(elem, Atoms + i, &symmetric);
216 if (verbose > 2)
217 printf(" new coordinates are: (%g,%g,%g)\n", symmetric.x[0],
218 symmetric.x[1], symmetric.x[2]);
219 best_j = i;
220 best_distance = 2 * TolerancePrimary; /* Performance value we'll reject */
221 for (j = 0; j < AtomsCount; j++) {
222 if (Atoms[j].type != symmetric.type || atom_used[j])
223 continue;
224 for (k = 0, distance = 0; k < DIMENSION; k++) {
225 distance += pow2(symmetric.x[k] - Atoms[j].x[k]);
226 }
228 if (verbose > 2)
229 printf(" distance to %d is %g\n", j, distance);
230 if (distance < best_distance) {
231 best_j = j;
232 best_distance = distance;
233 }
234 }
235 if (best_distance >
236 TolerancePrimary) { /* Too bad, there is no symmetric atom */
237 if (verbose > 0)
238 printf(" no pair for atom %d - best was %d with err = %g\n", i,
239 best_j, best_distance);
240 free(atom_used);
241 return -1;
242 }
243 elem->transform[i] = best_j;
244 atom_used[best_j] = 1;
245 if (verbose > 1)
246 printf(" atom %d transforms to the atom %d, err = %g\n", i,
247 best_j, best_distance);
249 }
250 free(atom_used);
251 return 0;
253
255 int i, j, k;
258 for (i = 0; i < AtomsCount; i++) {
259 if (elem->transform[i] == i) /* Identity transform is Ok for any order */
260 continue;
262 j = elem->transform[i];
263 if (elem->transform[j] == i)
264 continue; /* Second-order transform is Ok for improper axis */
265 }
266 for (j = elem->order - 1, k = elem->transform[i]; j > 0;
267 j--, k = elem->transform[k]) {
268 if (k == i) {
269 if (verbose > 0)
270 printf(" transform looped %d steps too early from atom %d\n",
271 j, i);
272 return -1;
273 }
275 if (k != i && elem->transform_atom == rotate_reflect_atom) {
276 /* For improper axes, the complete loop may also take twice the order */
277 for (j = elem->order; j > 0; j--, k = elem->transform[k]) {
278 if (k == i) {
279 if (verbose > 0)
280 printf(" (improper) transform looped %d steps too early "
281 "from atom %d\n",
282 j, i);
283 return -1;
284 }
286 }
287 if (k != i) {
288 if (verbose > 0)
289 printf(" transform failed to loop after %d steps from atom %d\n",
290 elem->order, i);
291 return -1;
292 }
293 }
294 return 0;
295}
296
298 int i, j;
299 int code;
300
301 if ((a->order != b->order) || (a->nparam != b->nparam) ||
303 return 0;
304 for (i = 0, code = 1; i < AtomsCount; i++) {
305 if (a->transform[i] != b->transform[i]) {
306 code = 0;
307 break;
308 }
309 }
310 if (code == 0 &&
311 a->order > 2) { /* b can also be a reverse transformation for a */
312 for (i = 0; i < AtomsCount; i++) {
313 j = a->transform[i];
314 if (b->transform[j] != i)
315 return 0;
316 }
317 return 1;
318 }
319 return code;
321
323 SYMMETRY_ELEMENT *elem = calloc(1, sizeof(SYMMETRY_ELEMENT));
324 int i;
325
326 if (elem == NULL) {
327 fprintf(stderr, "Out of memory allocating symmetry element\n");
328 exit(EXIT_FAILURE);
329 }
330 elem->transform = calloc(AtomsCount, sizeof(int));
331 if (elem->transform == NULL) {
332 fprintf(stderr,
333 "Out of memory allocating transform table for symmetry element\n");
334 exit(EXIT_FAILURE);
335 }
336 for (i = 0; i < AtomsCount; i++) {
337 elem->transform[i] = AtomsCount + 1; /* An impossible value */
338 }
339 return elem;
340}
341
343 if (elem != NULL) {
344 if (elem->transform != NULL)
345 free(elem->transform);
346 free(elem);
347 }
349
351 int i, j, k;
352 ATOM symmetric;
353 double r, max_r;
354
355 for (i = 0, max_r = 0; i < AtomsCount; i++) {
356 j = elem->transform[i];
357 elem->transform_atom(elem, Atoms + i, &symmetric);
358 for (k = 0, r = 0; k < DIMENSION; k++) {
359 r += pow2(symmetric.x[k] - Atoms[j].x[k]);
361 r = sqrt(r);
362 if (r > ToleranceFinal) {
363 if (verbose > 0)
364 printf(" distance to symmetric atom (%g) is too big for %d\n", r,
365 i);
366 return -1;
367 }
368 if (r > max_r)
369 max_r = r;
370 }
371 elem->maxdev = max_r;
372 return 0;
373}
374
375double eval_optimization_target_function(SYMMETRY_ELEMENT *elem, int *finish) {
376 int i, j, k;
377 ATOM symmetric;
378 double target, r, maxr;
379
380 if (elem->nparam >= 4) {
381 for (k = 0, r = 0; k < DIMENSION; k++) {
382 r += elem->normal[k] * elem->normal[k];
383 }
384 r = sqrt(r);
385 if (r < ToleranceSame) {
386 fprintf(stderr, "Normal collapsed!\n");
387 exit(EXIT_FAILURE);
388 }
389 for (k = 0; k < DIMENSION; k++) {
390 elem->normal[k] /= r;
391 }
392 if (elem->distance < 0) {
393 elem->distance = -elem->distance;
394 for (k = 0; k < DIMENSION; k++) {
395 elem->normal[k] = -elem->normal[k];
396 }
397 }
398 }
399 if (elem->nparam >= 7) {
400 for (k = 0, r = 0; k < DIMENSION; k++) {
401 r += elem->direction[k] * elem->direction[k];
402 }
403 r = sqrt(r);
404 if (r < ToleranceSame) {
405 fprintf(stderr, "Direction collapsed!\n");
406 exit(EXIT_FAILURE);
407 }
408 for (k = 0; k < DIMENSION; k++) {
409 elem->direction[k] /= r;
410 }
411 }
412 for (i = 0, target = maxr = 0; i < AtomsCount; i++) {
413 elem->transform_atom(elem, Atoms + i, &symmetric);
414 j = elem->transform[i];
415 for (k = 0, r = 0; k < DIMENSION; k++) {
416 r += pow2(Atoms[j].x[k] - symmetric.x[k]);
417 }
418 if (r > maxr)
419 maxr = r;
420 target += r;
421 }
422 if (finish != NULL) {
423 *finish = 0;
424 if (sqrt(maxr) < ToleranceFinal)
425 *finish = 1;
426 }
427 return target;
428}
429
430void get_params(SYMMETRY_ELEMENT *elem, double values[]) {
431 memcpy(values, &elem->distance, elem->nparam * sizeof(double));
432}
433
434void set_params(SYMMETRY_ELEMENT *elem, double values[]) {
435 memcpy(&elem->distance, values, elem->nparam * sizeof(double));
436}
437
439 double values[MAXPARAM];
440 double grad[MAXPARAM];
441 double force[MAXPARAM];
442 double step[MAXPARAM];
443 double f, fold, fnew, fnew2, fdn, fup, snorm;
444 double a, b, x;
445 int vars = elem->nparam;
446 int cycle = 0;
447 int i, finish;
448 int hits = 0;
449
450 if (vars > MAXPARAM) {
451 fprintf(stderr, "Catastrophe in optimize_transformation_params()!\n");
452 exit(EXIT_FAILURE);
453 }
454 f = 0;
455 do {
456 fold = f;
457 f = eval_optimization_target_function(elem, &finish);
458 /* Evaluate function, gradient and diagonal force constants */
459 if (verbose > 1)
460 printf(" function value = %g\n", f);
461 if (finish) {
462 if (verbose > 1)
463 printf(" function value is small enough\n");
464 break;
465 }
466 if (cycle > 0) {
467 if (fabs(f - fold) > OptChangeThreshold)
468 hits = 0;
469 else
470 hits++;
471 if (hits >= OptChangeHits) {
472 if (verbose > 1)
473 printf(" no progress is made, stop optimization\n");
474 break;
475 }
476 }
477 get_params(elem, values);
478 for (i = 0; i < vars; i++) {
480 set_params(elem, values);
481 fdn = eval_optimization_target_function(elem, NULL);
482 values[i] += 2 * GradientStep;
483 set_params(elem, values);
484 fup = eval_optimization_target_function(elem, NULL);
486 grad[i] = (fup - fdn) / (2 * GradientStep);
487 force[i] = (fup + fdn - 2 * f) / (GradientStep * GradientStep);
488 if (verbose > 1)
489 printf(" i = %d, grad = %12.6e, force = %12.6e\n", i, grad[i],
490 force[i]);
491 }
492 /* Do a quasy-Newton step */
493 for (i = 0, snorm = 0; i < vars; i++) {
494 if (force[i] < 0)
495 force[i] = -force[i];
496 if (force[i] < 1e-3)
497 force[i] = 1e-3;
498 if (force[i] > 1e3)
499 force[i] = 1e3;
500 step[i] = -grad[i] / force[i];
501 snorm += step[i] * step[i];
502 }
503 snorm = sqrt(snorm);
504 if (snorm > MaxOptStep) { /* Renormalize step */
505 for (i = 0; i < vars; i++)
506 step[i] *= MaxOptStep / snorm;
507 snorm = MaxOptStep;
508 }
509 do {
510 for (i = 0; i < vars; i++) {
511 values[i] += step[i];
512 }
513 set_params(elem, values);
514 fnew = eval_optimization_target_function(elem, NULL);
515 if (fnew < f)
516 break;
517 for (i = 0; i < vars; i++) {
518 values[i] -= step[i];
519 step[i] /= 2;
520 }
521 set_params(elem, values);
522 snorm /= 2;
523 } while (snorm > MinOptStep);
524 if ((snorm > MinOptStep) &&
525 (snorm < MaxOptStep / 2)) { /* try to do quadratic interpolation */
526 for (i = 0; i < vars; i++)
527 values[i] += step[i];
528 set_params(elem, values);
529 fnew2 = eval_optimization_target_function(elem, NULL);
530 if (verbose > 1)
531 printf(" interpolation base points: %g, %g, %g\n", f, fnew,
532 fnew2);
533 for (i = 0; i < vars; i++)
534 values[i] -= 2 * step[i];
535 a = (4 * f - fnew2 - 3 * fnew) / 2;
536 b = (f + fnew2 - 2 * fnew) / 2;
537 if (verbose > 1)
538 printf(" linear interpolation coefficients %g, %g\n", a, b);
539 if (b > 0) {
540 x = -a / (2 * b);
541 if (x > 0.2 && x < 1.8) {
542 if (verbose > 1)
543 printf(" interpolated: %g\n", x);
544 for (i = 0; i < vars; i++)
545 values[i] += x * step[i];
546 } else
547 b = 0;
548 }
549 if (b <= 0) {
550 if (fnew2 < fnew) {
551 for (i = 0; i < vars; i++)
552 values[i] += 2 * step[i];
553 } else {
554 for (i = 0; i < vars; i++)
555 values[i] += step[i];
556 }
557 }
558 set_params(elem, values);
559 }
560 } while (snorm > MinOptStep && ++cycle < MaxOptCycles);
561 f = eval_optimization_target_function(elem, NULL);
562 if (cycle >= MaxOptCycles)
563 BadOptimization = 1;
564 if (verbose > 0) {
565 if (cycle >= MaxOptCycles)
566 printf(" maximum number of optimization cycles made\n");
567 printf(" optimization completed after %d cycles with f = %g\n",
568 cycle, f);
569 }
570}
571
572int refine_symmetry_element(SYMMETRY_ELEMENT *elem, int build_table) {
573 int i;
574
575 if (build_table && (establish_pairs(elem) < 0)) {
576 StatPairs++;
577 if (verbose > 0)
578 printf(" no transformation correspondence table can be "
579 "constructed\n");
580 return -1;
581 }
582 for (i = 0; i < PlanesCount; i++) {
583 if (same_transform(Planes[i], elem)) {
584 StatDups++;
585 if (verbose > 0)
586 printf(" transformation is identical to plane %d\n", i);
587 return -1;
588 }
589 }
590 for (i = 0; i < InversionCentersCount; i++) {
591 if (same_transform(InversionCenters[i], elem)) {
592 StatDups++;
593 if (verbose > 0)
594 printf(" transformation is identical to inversion center %d\n",
595 i);
596 return -1;
597 }
598 }
599 for (i = 0; i < NormalAxesCount; i++) {
600 if (same_transform(NormalAxes[i], elem)) {
601 StatDups++;
602 if (verbose > 0)
603 printf(" transformation is identical to normal axis %d\n", i);
604 return -1;
605 }
606 }
607 for (i = 0; i < ImproperAxesCount; i++) {
608 if (same_transform(ImproperAxes[i], elem)) {
609 StatDups++;
610 if (verbose > 0)
611 printf(" transformation is identical to improper axis %d\n", i);
612 return -1;
613 }
614 }
615 if (check_transform_order(elem) < 0) {
616 StatOrder++;
617 if (verbose > 0)
618 printf(" incorrect transformation order\n");
619 return -1;
620 }
622 if (check_transform_quality(elem) < 0) {
623 StatOpt++;
624 if (verbose > 0)
625 printf(" refined transformation does not pass the numeric "
626 "threshold\n");
627 return -1;
628 }
629 StatAccept++;
630 return 0;
631}
632
633/*
634 * Plane-specific functions
635 */
636
637void mirror_atom(SYMMETRY_ELEMENT *plane, ATOM *from, ATOM *to) {
638 int i;
639 double r;
640
641 for (i = 0, r = plane->distance; i < DIMENSION; i++) {
642 r -= from->x[i] * plane->normal[i];
643 }
644 to->type = from->type;
645 for (i = 0; i < DIMENSION; i++) {
646 to->x[i] = from->x[i] + 2 * r * plane->normal[i];
647 }
648}
649
652 double dx[DIMENSION], midpoint[DIMENSION], rab, r;
653 int k;
654
655 if (verbose > 0)
656 printf("Trying mirror plane for atoms %d,%d\n", i, j);
657 StatTotal++;
659 plane->order = 2;
660 plane->nparam = 4;
661 for (k = 0, rab = 0; k < DIMENSION; k++) {
662 dx[k] = Atoms[i].x[k] - Atoms[j].x[k];
663 midpoint[k] = (Atoms[i].x[k] + Atoms[j].x[k]) / 2.0;
664 rab += dx[k] * dx[k];
665 }
666 rab = sqrt(rab);
667 if (rab < ToleranceSame) {
668 fprintf(stderr, "Atoms %d and %d coincide (r = %g)\n", i, j, rab);
669 exit(EXIT_FAILURE);
670 }
671 for (k = 0, r = 0; k < DIMENSION; k++) {
672 plane->normal[k] = dx[k] / rab;
673 r += midpoint[k] * plane->normal[k];
674 }
675 if (r < 0) { /* Reverce normal direction, distance is always positive! */
676 r = -r;
677 for (k = 0; k < DIMENSION; k++) {
678 plane->normal[k] = -plane->normal[k];
679 }
680 }
681 plane->distance = r;
682 if (verbose > 0)
683 printf(" initial plane is at %g from the origin\n", r);
684 if (refine_symmetry_element(plane, 1) < 0) {
685 if (verbose > 0)
686 printf(" refinement failed for the plane\n");
688 return NULL;
689 }
690 return plane;
691}
692
695 double d0[DIMENSION], d1[DIMENSION], d2[DIMENSION];
696 double p[DIMENSION];
697 double r, s0, s1, s2;
698 double *d;
699 int i, j, k;
700
701 if (verbose > 0)
702 printf("Trying whole-molecule mirror plane\n");
703 StatTotal++;
705 plane->order = 1;
706 plane->nparam = 4;
707 for (k = 0; k < DIMENSION; k++)
708 d0[k] = d1[k] = d2[k] = 0;
709 d0[0] = 1;
710 d1[1] = 1;
711 d2[2] = 1;
712 for (i = 1; i < AtomsCount; i++) {
713 for (j = 0; j < i; j++) {
714 for (k = 0, r = 0; k < DIMENSION; k++) {
715 p[k] = Atoms[i].x[k] - Atoms[j].x[k];
716 r += p[k] * p[k];
717 }
718 r = sqrt(r);
719 for (k = 0, s0 = s1 = s2 = 0; k < DIMENSION; k++) {
720 p[k] /= r;
721 s0 += p[k] * d0[k];
722 s1 += p[k] * d1[k];
723 s2 += p[k] * d2[k];
724 }
725 for (k = 0; k < DIMENSION; k++) {
726 d0[k] -= s0 * p[k];
727 d1[k] -= s1 * p[k];
728 d2[k] -= s2 * p[k];
729 }
730 }
731 }
732 for (k = 0, s0 = s1 = s2 = 0; k < DIMENSION; k++) {
733 s0 += d0[k];
734 s1 += d1[k];
735 s2 += d2[k];
736 }
737 d = NULL;
738 if (s0 >= s1 && s0 >= s2)
739 d = d0;
740 if (s1 >= s0 && s1 >= s2)
741 d = d1;
742 if (s2 >= s0 && s2 >= s1)
743 d = d2;
744 if (d == NULL) {
745 fprintf(stderr,
746 "Catastrophe in init_ultimate_plane(): %g, %g and %g have no "
747 "ordering!\n",
748 s0, s1, s2);
749 exit(EXIT_FAILURE);
750 }
751 for (k = 0, r = 0; k < DIMENSION; k++)
752 r += d[k] * d[k];
753 r = sqrt(r);
754 if (r > 0) {
755 for (k = 0; k < DIMENSION; k++)
756 plane->normal[k] = d[k] / r;
757 } else {
758 for (k = 1; k < DIMENSION; k++)
759 plane->normal[k] = 0;
760 plane->normal[0] = 1;
761 }
762 for (k = 0, r = 0; k < DIMENSION; k++)
763 r += CenterOfSomething[k] * plane->normal[k];
764 plane->distance = r;
765 for (k = 0; k < AtomsCount; k++)
766 plane->transform[k] = k;
767 if (refine_symmetry_element(plane, 0) < 0) {
768 if (verbose > 0)
769 printf(" refinement failed for the plane\n");
771 return NULL;
772 }
773 return plane;
774}
775/*
776 * Inversion-center specific functions
777 */
778void invert_atom(SYMMETRY_ELEMENT *center, ATOM *from, ATOM *to) {
779 int i;
780
781 to->type = from->type;
782 for (i = 0; i < DIMENSION; i++) {
783 to->x[i] = 2 * center->distance * center->normal[i] - from->x[i];
784 }
785}
786
789 int k;
790 double r;
791
792 if (verbose > 0)
793 printf("Trying inversion center at the center of something\n");
794 StatTotal++;
795 center->transform_atom = invert_atom;
796 center->order = 2;
797 center->nparam = 4;
798 for (k = 0, r = 0; k < DIMENSION; k++)
800 r = sqrt(r);
801 if (r > 0) {
802 for (k = 0; k < DIMENSION; k++)
803 center->normal[k] = CenterOfSomething[k] / r;
804 } else {
805 center->normal[0] = 1;
806 for (k = 1; k < DIMENSION; k++)
807 center->normal[k] = 0;
808 }
809 center->distance = r;
810 if (verbose > 0)
811 printf(" initial inversion center is at %g from the origin\n", r);
812 if (refine_symmetry_element(center, 1) < 0) {
813 if (verbose > 0)
814 printf(" refinement failed for the inversion center\n");
816 return NULL;
817 }
818 return center;
819}
820
821/*
822 * Normal rotation axis-specific routines.
823 */
824void rotate_atom(SYMMETRY_ELEMENT *axis, ATOM *from, ATOM *to) {
825 double x[3], y[3], a[3], b[3], c[3];
826 double angle = axis->order ? 2 * M_PI / axis->order : 1.0;
827 double a_sin = sin(angle);
828 double a_cos = cos(angle);
829 double dot;
830 int i;
831
832 if (DIMENSION != 3) {
833 fprintf(stderr, "Catastrophe in rotate_atom!\n");
834 exit(EXIT_FAILURE);
835 }
836 for (i = 0; i < 3; i++)
837 x[i] = from->x[i] - axis->distance * axis->normal[i];
838 for (i = 0, dot = 0; i < 3; i++)
839 dot += x[i] * axis->direction[i];
840 for (i = 0; i < 3; i++)
841 a[i] = axis->direction[i] * dot;
842 for (i = 0; i < 3; i++)
843 b[i] = x[i] - a[i];
844 c[0] = b[1] * axis->direction[2] - b[2] * axis->direction[1];
845 c[1] = b[2] * axis->direction[0] - b[0] * axis->direction[2];
846 c[2] = b[0] * axis->direction[1] - b[1] * axis->direction[0];
847 for (i = 0; i < 3; i++)
848 y[i] = a[i] + b[i] * a_cos + c[i] * a_sin;
849 for (i = 0; i < 3; i++)
850 to->x[i] = y[i] + axis->distance * axis->normal[i];
851 to->type = from->type;
852}
853
856 double dir[DIMENSION], rel[DIMENSION];
857 double s;
858 int i, k;
859
860 if (verbose > 0)
861 printf("Trying infinity axis\n");
862 StatTotal++;
864 axis->order = 0;
865 axis->nparam = 7;
866 for (k = 0; k < DIMENSION; k++)
867 dir[k] = 0;
868 for (i = 0; i < AtomsCount; i++) {
869 for (k = 0, s = 0; k < DIMENSION; k++) {
870 rel[k] = Atoms[i].x[k] - CenterOfSomething[k];
871 s += rel[k] * dir[k];
872 }
873 if (s >= 0)
874 for (k = 0; k < DIMENSION; k++)
875 dir[k] += rel[k];
876 else
877 for (k = 0; k < DIMENSION; k++)
878 dir[k] -= rel[k];
879 }
880 for (k = 0, s = 0; k < DIMENSION; k++)
881 s += pow2(dir[k]);
882 s = sqrt(s);
883 if (s > 0)
884 for (k = 0; k < DIMENSION; k++)
885 dir[k] /= s;
886 else
887 dir[0] = 1;
888 for (k = 0; k < DIMENSION; k++)
889 axis->direction[k] = dir[k];
890 for (k = 0, s = 0; k < DIMENSION; k++)
891 s += pow2(CenterOfSomething[k]);
892 s = sqrt(s);
893 if (s > 0)
894 for (k = 0; k < DIMENSION; k++)
895 axis->normal[k] = CenterOfSomething[k] / s;
896 else {
897 for (k = 1; k < DIMENSION; k++)
898 axis->normal[k] = 0;
899 axis->normal[0] = 1;
900 }
901 axis->distance = s;
902 for (k = 0; k < AtomsCount; k++)
903 axis->transform[k] = k;
904 if (refine_symmetry_element(axis, 0) < 0) {
905 if (verbose > 0)
906 printf(" refinement failed for the infinity axis\n");
908 return NULL;
909 }
910 return axis;
911}
912
913SYMMETRY_ELEMENT *init_c2_axis(int i, int j, double support[DIMENSION]) {
914 SYMMETRY_ELEMENT *axis;
915 int k;
916 double ris, rjs;
917 double r, center[DIMENSION];
918
919 if (verbose > 0)
920 printf("Trying c2 axis for the pair (%d,%d) with the support (%g,%g,%g)\n",
921 i, j, support[0], support[1], support[2]);
922 StatTotal++;
923 /* First, do a quick sanity check */
924 for (k = 0, ris = rjs = 0; k < DIMENSION; k++) {
925 ris += pow2(Atoms[i].x[k] - support[k]);
926 rjs += pow2(Atoms[j].x[k] - support[k]);
927 }
928 ris = sqrt(ris);
929 rjs = sqrt(rjs);
930 if (fabs(ris - rjs) > TolerancePrimary) {
931 StatEarly++;
932 if (verbose > 0)
933 printf(" Support can't actually define a rotation axis\n");
934 return NULL;
935 }
936 axis = alloc_symmetry_element();
938 axis->order = 2;
939 axis->nparam = 7;
940 for (k = 0, r = 0; k < DIMENSION; k++)
942 r = sqrt(r);
943 if (r > 0) {
944 for (k = 0; k < DIMENSION; k++)
945 axis->normal[k] = CenterOfSomething[k] / r;
946 } else {
947 axis->normal[0] = 1;
948 for (k = 1; k < DIMENSION; k++)
949 axis->normal[k] = 0;
950 }
951 axis->distance = r;
952 for (k = 0, r = 0; k < DIMENSION; k++) {
953 center[k] = (Atoms[i].x[k] + Atoms[j].x[k]) / 2 - support[k];
954 r += center[k] * center[k];
955 }
956 r = sqrt(r);
957 if (r <=
958 TolerancePrimary) { /* c2 is underdefined, let's do something special */
959 if (MolecularPlane != NULL) {
960 if (verbose > 0)
961 printf(" c2 is underdefined, but there is a molecular plane\n");
962 for (k = 0; k < DIMENSION; k++)
963 axis->direction[k] = MolecularPlane->normal[k];
964 } else {
965 if (verbose > 0)
966 printf(" c2 is underdefined, trying random direction\n");
967 for (k = 0; k < DIMENSION; k++)
968 center[k] = Atoms[i].x[k] - Atoms[j].x[k];
969 if (fabs(center[2]) + fabs(center[1]) > ToleranceSame) {
970 axis->direction[0] = 0;
971 axis->direction[1] = center[2];
972 axis->direction[2] = -center[1];
973 } else {
974 axis->direction[0] = -center[2];
975 axis->direction[1] = 0;
976 axis->direction[2] = center[0];
977 }
978 for (k = 0, r = 0; k < DIMENSION; k++)
979 r += axis->direction[k] * axis->direction[k];
980 r = sqrt(r);
981 for (k = 0; k < DIMENSION; k++)
982 axis->direction[k] /= r;
983 }
984 } else { /* direction is Ok, renormalize it */
985 for (k = 0; k < DIMENSION; k++)
986 axis->direction[k] = center[k] / r;
987 }
988 if (refine_symmetry_element(axis, 1) < 0) {
989 if (verbose > 0)
990 printf(" refinement failed for the c2 axis\n");
992 return NULL;
993 }
994 return axis;
995}
996
997SYMMETRY_ELEMENT *init_axis_parameters(double a[3], double b[3], double c[3]) {
998 SYMMETRY_ELEMENT *axis;
999 int i, order, sign;
1000 double ra, rb, rc, rab, rbc, rac, r;
1001 double angle;
1002
1003 ra = rb = rc = rab = rbc = rac = 0;
1004 for (i = 0; i < DIMENSION; i++) {
1005 ra += a[i] * a[i];
1006 rb += b[i] * b[i];
1007 rc += c[i] * c[i];
1008 }
1009 ra = sqrt(ra);
1010 rb = sqrt(rb);
1011 rc = sqrt(rc);
1012 if (fabs(ra - rb) > TolerancePrimary || fabs(ra - rc) > TolerancePrimary ||
1013 fabs(rb - rc) > TolerancePrimary) {
1014 StatEarly++;
1015 if (verbose > 0)
1016 printf(" points are not on a sphere\n");
1017 return NULL;
1018 }
1019 for (i = 0; i < DIMENSION; i++) {
1020 rab += (a[i] - b[i]) * (a[i] - b[i]);
1021 rac += (a[i] - c[i]) * (a[i] - c[i]);
1022 rbc += (c[i] - b[i]) * (c[i] - b[i]);
1023 }
1024 rab = sqrt(rab);
1025 rac = sqrt(rac);
1026 rbc = sqrt(rbc);
1027 if (fabs(rab - rbc) > TolerancePrimary) {
1028 StatEarly++;
1029 if (verbose > 0)
1030 printf(" points can't be rotation-equivalent\n");
1031 return NULL;
1032 }
1033 if (rab <= ToleranceSame || rbc <= ToleranceSame || rac <= ToleranceSame) {
1034 StatEarly++;
1035 if (verbose > 0)
1036 printf(" rotation is underdefined by these points\n");
1037 return NULL;
1038 }
1039 rab = (rab + rbc) / 2;
1040 angle = M_PI - 2 * asin(rac / (2 * rab));
1041 if (verbose > 1)
1042 printf(" rotation angle is %f\n", angle);
1043 if (fabs(angle) <= M_PI / (MaxAxisOrder + 1)) {
1044 StatEarly++;
1045 if (verbose > 0)
1046 printf(" atoms are too close to a straight line\n");
1047 return NULL;
1048 }
1049 order = floor((2 * M_PI) / angle + 0.5);
1050 if (order <= 2 || order > MaxAxisOrder) {
1051 StatEarly++;
1052 if (verbose > 0)
1053 printf(" rotation axis order (%d) is not from 3 to %d\n", order,
1054 MaxAxisOrder);
1055 return NULL;
1056 }
1057 axis = alloc_symmetry_element();
1058 axis->order = order;
1059 axis->nparam = 7;
1060 for (i = 0, r = 0; i < DIMENSION; i++)
1062 r = sqrt(r);
1063 if (r > 0) {
1064 for (i = 0; i < DIMENSION; i++)
1065 axis->normal[i] = CenterOfSomething[i] / r;
1066 } else {
1067 axis->normal[0] = 1;
1068 for (i = 1; i < DIMENSION; i++)
1069 axis->normal[i] = 0;
1070 }
1071 axis->distance = r;
1072 axis->direction[0] =
1073 (b[1] - a[1]) * (c[2] - b[2]) - (b[2] - a[2]) * (c[1] - b[1]);
1074 axis->direction[1] =
1075 (b[2] - a[2]) * (c[0] - b[0]) - (b[0] - a[0]) * (c[2] - b[2]);
1076 axis->direction[2] =
1077 (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]);
1078 /*
1079 * Arbitrarily select axis direction so that first non-zero component
1080 * of the direction is positive.
1081 */
1082 sign = 0;
1083 if (axis->direction[0] <= 0) {
1084 if (axis->direction[0] < 0) {
1085 sign = 1;
1086 } else if (axis->direction[1] <= 0) {
1087 if (axis->direction[1] < 0) {
1088 sign = 1;
1089 } else if (axis->direction[2] < 0) {
1090 sign = 1;
1091 }
1092 }
1093 }
1094 if (sign)
1095 for (i = 0; i < DIMENSION; i++)
1096 axis->direction[i] = -axis->direction[i];
1097 for (i = 0, r = 0; i < DIMENSION; i++)
1098 r += axis->direction[i] * axis->direction[i];
1099 r = sqrt(r);
1100 for (i = 0; i < DIMENSION; i++)
1101 axis->direction[i] /= r;
1102 if (verbose > 1) {
1103 printf(" axis origin is at (%g,%g,%g)\n",
1104 axis->normal[0] * axis->distance, axis->normal[1] * axis->distance,
1105 axis->normal[2] * axis->distance);
1106 printf(" axis is in the direction (%g,%g,%g)\n", axis->direction[0],
1107 axis->direction[1], axis->direction[2]);
1108 }
1109 return axis;
1110}
1111
1112SYMMETRY_ELEMENT *init_higher_axis(int ia, int ib, int ic) {
1113 SYMMETRY_ELEMENT *axis;
1114 double a[DIMENSION], b[DIMENSION], c[DIMENSION];
1115 int i;
1116
1117 if (verbose > 0)
1118 printf("Trying cn axis for the triplet (%d,%d,%d)\n", ia, ib, ic);
1119 StatTotal++;
1120 /* Do a quick check of geometry validity */
1121 for (i = 0; i < DIMENSION; i++) {
1122 a[i] = Atoms[ia].x[i] - CenterOfSomething[i];
1123 b[i] = Atoms[ib].x[i] - CenterOfSomething[i];
1124 c[i] = Atoms[ic].x[i] - CenterOfSomething[i];
1125 }
1126 if ((axis = init_axis_parameters(a, b, c)) == NULL) {
1127 if (verbose > 0)
1128 printf(" no coherent axis is defined by the points\n");
1129 return NULL;
1130 }
1132 if (refine_symmetry_element(axis, 1) < 0) {
1133 if (verbose > 0)
1134 printf(" refinement failed for the c%d axis\n", axis->order);
1136 return NULL;
1137 }
1138 return axis;
1139}
1140
1141/*
1142 * Improper axes-specific routines.
1143 * These are obtained by slight modifications of normal rotation
1144 * routines.
1145 */
1146void rotate_reflect_atom(SYMMETRY_ELEMENT *axis, ATOM *from, ATOM *to) {
1147 double x[3], y[3], a[3], b[3], c[3];
1148 double angle = 2 * M_PI / axis->order;
1149 double a_sin = sin(angle);
1150 double a_cos = cos(angle);
1151 double dot;
1152 int i;
1153
1154 if (DIMENSION != 3) {
1155 fprintf(stderr, "Catastrophe in rotate_reflect_atom!\n");
1156 exit(EXIT_FAILURE);
1157 }
1158 for (i = 0; i < 3; i++)
1159 x[i] = from->x[i] - axis->distance * axis->normal[i];
1160 for (i = 0, dot = 0; i < 3; i++)
1161 dot += x[i] * axis->direction[i];
1162 for (i = 0; i < 3; i++)
1163 a[i] = axis->direction[i] * dot;
1164 for (i = 0; i < 3; i++)
1165 b[i] = x[i] - a[i];
1166 c[0] = b[1] * axis->direction[2] - b[2] * axis->direction[1];
1167 c[1] = b[2] * axis->direction[0] - b[0] * axis->direction[2];
1168 c[2] = b[0] * axis->direction[1] - b[1] * axis->direction[0];
1169 for (i = 0; i < 3; i++)
1170 y[i] = -a[i] + b[i] * a_cos + c[i] * a_sin;
1171 for (i = 0; i < 3; i++)
1172 to->x[i] = y[i] + axis->distance * axis->normal[i];
1173 to->type = from->type;
1174}
1175
1176SYMMETRY_ELEMENT *init_improper_axis(int ia, int ib, int ic) {
1177 SYMMETRY_ELEMENT *axis;
1178 double a[DIMENSION], b[DIMENSION], c[DIMENSION];
1179 double centerpoint[DIMENSION];
1180 double r;
1181 int i;
1182
1183 if (verbose > 0)
1184 printf("Trying sn axis for the triplet (%d,%d,%d)\n", ia, ib, ic);
1185 StatTotal++;
1186 /* First, reduce the problem to Cn case */
1187 for (i = 0; i < DIMENSION; i++) {
1188 a[i] = Atoms[ia].x[i] - CenterOfSomething[i];
1189 b[i] = Atoms[ib].x[i] - CenterOfSomething[i];
1190 c[i] = Atoms[ic].x[i] - CenterOfSomething[i];
1191 }
1192 for (i = 0, r = 0; i < DIMENSION; i++) {
1193 centerpoint[i] = a[i] + c[i] + 2 * b[i];
1194 r += centerpoint[i] * centerpoint[i];
1195 }
1196 r = sqrt(r);
1197 if (r <= ToleranceSame) {
1198 StatEarly++;
1199 if (verbose > 0)
1200 printf(
1201 " atoms can not define improper axis of the order more than 2\n");
1202 return NULL;
1203 }
1204 for (i = 0; i < DIMENSION; i++)
1205 centerpoint[i] /= r;
1206 for (i = 0, r = 0; i < DIMENSION; i++)
1207 r += centerpoint[i] * b[i];
1208 for (i = 0; i < DIMENSION; i++)
1209 b[i] = 2 * r * centerpoint[i] - b[i];
1210 /* Do a quick check of geometry validity */
1211 if ((axis = init_axis_parameters(a, b, c)) == NULL) {
1212 if (verbose > 0)
1213 printf(" no coherent improper axis is defined by the points\n");
1214 return NULL;
1215 }
1217 if (refine_symmetry_element(axis, 1) < 0) {
1218 if (verbose > 0)
1219 printf(" refinement failed for the s%d axis\n", axis->order);
1221 return NULL;
1222 }
1223 return axis;
1224}
1225
1226/*
1227 * Control routines
1228 */
1229
1230void find_center_of_something(void) {
1231 int i, j;
1232 double coord_sum[DIMENSION];
1233 double r;
1234
1235 for (j = 0; j < DIMENSION; j++)
1236 coord_sum[j] = 0;
1237 for (i = 0; i < AtomsCount; i++) {
1238 for (j = 0; j < DIMENSION; j++)
1239 coord_sum[j] += Atoms[i].x[j];
1240 }
1241 for (j = 0; j < DIMENSION; j++)
1242 CenterOfSomething[j] = coord_sum[j] / AtomsCount;
1243 if (verbose > 0)
1244 printf("Center of something is at %15.10f, %15.10f, %15.10f\n",
1246 DistanceFromCenter = (double *)calloc(AtomsCount, sizeof(double));
1247 if (DistanceFromCenter == NULL) {
1248 fprintf(stderr, "Unable to allocate array for the distances\n");
1249 exit(EXIT_FAILURE);
1251 for (i = 0; i < AtomsCount; i++) {
1252 for (j = 0, r = 0; j < DIMENSION; j++)
1253 r += pow2(Atoms[i].x[j] - CenterOfSomething[j]);
1254 DistanceFromCenter[i] = r;
1255 }
1256}
1257
1258void find_planes(void) {
1259 int i, j;
1260 SYMMETRY_ELEMENT *plane = NULL;
1261
1262 plane = init_ultimate_plane();
1263 if (plane != NULL) {
1265 PlanesCount++;
1267 PlanesCount);
1268 if (Planes == NULL) {
1269 perror("Out of memory in find_planes");
1270 exit(EXIT_FAILURE);
1271 }
1272 Planes[PlanesCount - 1] = plane;
1274 for (i = 1; i < AtomsCount; i++) {
1275 for (j = 0; j < i; j++) {
1276 if (Atoms[i].type != Atoms[j].type)
1277 continue;
1278 if ((plane = init_mirror_plane(i, j)) != NULL) {
1279 PlanesCount++;
1280 Planes = (SYMMETRY_ELEMENT **)realloc(
1281 Planes, sizeof(SYMMETRY_ELEMENT *) * PlanesCount);
1282 if (Planes == NULL) {
1283 perror("Out of memory in find_planes");
1284 exit(EXIT_FAILURE);
1285 }
1286 Planes[PlanesCount - 1] = plane;
1287 }
1288 }
1289 }
1290}
1291
1292void destroy_planes(void) {
1293 int i;
1294 for (i = 0; i < PlanesCount; i++)
1296 PlanesCount = 0;
1297 free(Planes);
1298 MolecularPlane = NULL;
1299 Planes = NULL;
1300}
1301
1302void find_inversion_centers(void) {
1304
1305 if ((center = init_inversion_center()) != NULL) {
1307 (SYMMETRY_ELEMENT **)calloc(1, sizeof(SYMMETRY_ELEMENT *));
1310 }
1311}
1312
1313void destroy_inversion_centers(void) {
1314 int i;
1315 for (i = 0; i < InversionCentersCount; i++)
1318 free(InversionCenters);
1319 InversionCenters = NULL;
1320}
1321
1322void find_infinity_axis(void) {
1323 SYMMETRY_ELEMENT *axis;
1324
1325 if ((axis = init_ultimate_axis()) != NULL) {
1327 NormalAxes = (SYMMETRY_ELEMENT **)realloc(
1329 if (NormalAxes == NULL) {
1330 perror("Out of memory in find_infinity_axes()");
1331 exit(EXIT_FAILURE);
1332 }
1333 NormalAxes[NormalAxesCount - 1] = axis;
1334 }
1335}
1336
1337void find_c2_axes(void) {
1338 int i, j, k, l, m;
1339 double center[DIMENSION];
1340 double *distances = calloc(AtomsCount, sizeof(double));
1341 double r;
1342 SYMMETRY_ELEMENT *axis;
1343
1344 if (distances == NULL) {
1345 fprintf(stderr, "Out of memory in find_c2_axes()\n");
1346 exit(EXIT_FAILURE);
1347 }
1348 for (i = 1; i < AtomsCount; i++) {
1349 for (j = 0; j < i; j++) {
1350 if (Atoms[i].type != Atoms[j].type)
1351 continue;
1354 continue; /* A very cheap, but quite effective check */
1355 /*
1356 * First, let's try to get it cheap and use CenterOfSomething
1357 */
1358 for (k = 0, r = 0; k < DIMENSION; k++) {
1359 center[k] = (Atoms[i].x[k] + Atoms[j].x[k]) / 2;
1360 r += pow2(center[k] - CenterOfSomething[k]);
1361 }
1362 r = sqrt(r);
1363 if (r > 5 * TolerancePrimary) { /* It's Ok to use CenterOfSomething */
1364 if ((axis = init_c2_axis(i, j, CenterOfSomething)) != NULL) {
1366 NormalAxes = (SYMMETRY_ELEMENT **)realloc(
1368 if (NormalAxes == NULL) {
1369 perror("Out of memory in find_c2_axes");
1370 exit(EXIT_FAILURE);
1371 }
1372 NormalAxes[NormalAxesCount - 1] = axis;
1373 }
1374 continue;
1375 }
1376 /*
1377 * Now, C2 axis can either pass through an atom, or through the
1378 * middle of the other pair.
1379 */
1380 for (k = 0; k < AtomsCount; k++) {
1381 if ((axis = init_c2_axis(i, j, Atoms[k].x)) != NULL) {
1383 NormalAxes = (SYMMETRY_ELEMENT **)realloc(
1385 if (NormalAxes == NULL) {
1386 perror("Out of memory in find_c2_axes");
1387 exit(EXIT_FAILURE);
1388 }
1389 NormalAxes[NormalAxesCount - 1] = axis;
1390 }
1391 }
1392 /*
1393 * Prepare data for an additional pre-screening check
1394 */
1395 for (k = 0; k < AtomsCount; k++) {
1396 for (l = 0, r = 0; l < DIMENSION; l++)
1397 r += pow2(Atoms[k].x[l] - center[l]);
1398 distances[k] = sqrt(r);
1399 }
1400 for (k = 0; k < AtomsCount; k++) {
1401 for (l = 0; l < AtomsCount; l++) {
1402 if (Atoms[k].type != Atoms[l].type)
1403 continue;
1406 fabs(distances[k] - distances[l]) > TolerancePrimary)
1407 continue; /* We really need this one to run reasonably fast! */
1408 for (m = 0; m < DIMENSION; m++)
1409 center[m] = (Atoms[k].x[m] + Atoms[l].x[m]) / 2;
1410 if ((axis = init_c2_axis(i, j, center)) != NULL) {
1412 NormalAxes = (SYMMETRY_ELEMENT **)realloc(
1414 if (NormalAxes == NULL) {
1415 perror("Out of memory in find_c2_axes");
1416 exit(EXIT_FAILURE);
1417 }
1418 NormalAxes[NormalAxesCount - 1] = axis;
1419 }
1420 }
1421 }
1422 }
1423 }
1424 free(distances);
1425}
1426
1427void destroy_normal_axes() {
1428 int i = 0;
1429 for (i = 0; i < NormalAxesCount; i++)
1431 NormalAxesCount = 0;
1432 free(NormalAxes);
1433 NormalAxes = NULL;
1434}
1435
1436void destroy_improper_axes() {
1437 int i = 0;
1438 for (i = 0; i < ImproperAxesCount; i++)
1441 free(ImproperAxes);
1442 ImproperAxes = NULL;
1443}
1444
1445void find_higher_axes(void) {
1446 int i, j, k;
1447 SYMMETRY_ELEMENT *axis;
1448
1449 for (i = 0; i < AtomsCount; i++) {
1450 for (j = i + 1; j < AtomsCount; j++) {
1451 if (Atoms[i].type != Atoms[j].type)
1452 continue;
1455 continue; /* A very cheap, but quite effective check */
1456 for (k = 0; k < AtomsCount; k++) {
1457 if (Atoms[i].type != Atoms[k].type)
1458 continue;
1463 continue;
1464 if ((axis = init_higher_axis(i, j, k)) != NULL) {
1466 NormalAxes = (SYMMETRY_ELEMENT **)realloc(
1468 if (NormalAxes == NULL) {
1469 perror("Out of memory in find_higher_axes");
1470 exit(EXIT_FAILURE);
1471 }
1472 NormalAxes[NormalAxesCount - 1] = axis;
1473 }
1474 }
1475 }
1476 }
1477}
1478
1479void find_improper_axes(void) {
1480 int i, j, k;
1481 SYMMETRY_ELEMENT *axis;
1482
1483 for (i = 0; i < AtomsCount; i++) {
1484 for (j = i + 1; j < AtomsCount; j++) {
1485 for (k = 0; k < AtomsCount; k++) {
1486 if ((axis = init_improper_axis(i, j, k)) != NULL) {
1488 ImproperAxes = (SYMMETRY_ELEMENT **)realloc(
1490 if (ImproperAxes == NULL) {
1491 perror("Out of memory in find_higher_axes");
1492 exit(EXIT_FAILURE);
1493 }
1494 ImproperAxes[ImproperAxesCount - 1] = axis;
1495 }
1496 }
1497 }
1498 }
1499}
1500
1501void report_planes(void) {
1502 int i;
1503
1504 if (PlanesCount == 0)
1505 printf("There are no planes of symmetry in the molecule\n");
1506 else {
1507 if (PlanesCount == 1)
1508 printf("There is a plane of symmetry in the molecule\n");
1509 else
1510 printf("There are %d planes of symmetry in the molecule\n", PlanesCount);
1511 printf(
1512 " Residual Direction of the normal Distance\n");
1513 for (i = 0; i < PlanesCount; i++) {
1514 printf("%3d %8.4e ", i, Planes[i]->maxdev);
1515 printf("(%11.8f,%11.8f,%11.8f) ", Planes[i]->normal[0],
1516 Planes[i]->normal[1], Planes[i]->normal[2]);
1517 printf("%14.8f\n", Planes[i]->distance);
1518 }
1519 }
1520}
1521
1522void report_inversion_centers(void) {
1523 if (InversionCentersCount == 0)
1524 printf("There is no inversion center in the molecule\n");
1525 else {
1526 printf("There in an inversion center in the molecule\n");
1527 printf(" Residual Position\n");
1528 printf(" %8.4e ", InversionCenters[0]->maxdev);
1529 printf("(%14.8f,%14.8f,%14.8f)\n",
1530 InversionCenters[0]->distance * InversionCenters[0]->normal[0],
1531 InversionCenters[0]->distance * InversionCenters[0]->normal[1],
1532 InversionCenters[0]->distance * InversionCenters[0]->normal[2]);
1533 }
1534}
1535
1536void report_axes(void) {
1537 int i;
1538
1539 if (NormalAxesCount == 0)
1540 printf("There are no normal axes in the molecule\n");
1541 else {
1542 if (NormalAxesCount == 1)
1543 printf("There is a normal axis in the molecule\n");
1544 else
1545 printf("There are %d normal axes in the molecule\n", NormalAxesCount);
1546 printf(" Residual Order Direction of the axis "
1547 " Supporting point\n");
1548 for (i = 0; i < NormalAxesCount; i++) {
1549 printf("%3d %8.4e ", i, NormalAxes[i]->maxdev);
1550 if (NormalAxes[i]->order == 0)
1551 printf("Inf ");
1552 else
1553 printf("%3d ", NormalAxes[i]->order);
1554 printf("(%11.8f,%11.8f,%11.8f) ", NormalAxes[i]->direction[0],
1555 NormalAxes[i]->direction[1], NormalAxes[i]->direction[2]);
1556 printf("(%14.8f,%14.8f,%14.8f)\n",
1557 NormalAxes[0]->distance * NormalAxes[0]->normal[0],
1558 NormalAxes[0]->distance * NormalAxes[0]->normal[1],
1559 NormalAxes[0]->distance * NormalAxes[0]->normal[2]);
1560 }
1561 }
1562}
1563
1564void report_improper_axes(void) {
1565 int i;
1566
1567 if (ImproperAxesCount == 0)
1568 printf("There are no improper axes in the molecule\n");
1569 else {
1570 if (ImproperAxesCount == 1)
1571 printf("There is an improper axis in the molecule\n");
1572 else
1573 printf("There are %d improper axes in the molecule\n", ImproperAxesCount);
1574 printf(" Residual Order Direction of the axis "
1575 " Supporting point\n");
1576 for (i = 0; i < ImproperAxesCount; i++) {
1577 printf("%3d %8.4e ", i, ImproperAxes[i]->maxdev);
1578 if (ImproperAxes[i]->order == 0)
1579 printf("Inf ");
1580 else
1581 printf("%3d ", ImproperAxes[i]->order);
1582 printf("(%11.8f,%11.8f,%11.8f) ", ImproperAxes[i]->direction[0],
1583 ImproperAxes[i]->direction[1], ImproperAxes[i]->direction[2]);
1584 printf("(%14.8f,%14.8f,%14.8f)\n",
1585 ImproperAxes[0]->distance * ImproperAxes[0]->normal[0],
1586 ImproperAxes[0]->distance * ImproperAxes[0]->normal[1],
1587 ImproperAxes[0]->distance * ImproperAxes[0]->normal[2]);
1588 }
1589 }
1590}
1591
1592/*
1593 * General symmetry handling
1594 */
1595void report_and_reset_counters(void) {
1596 printf(" %10ld candidates examined\n"
1597 " %10ld removed early\n"
1598 " %10ld removed during initial mating stage\n"
1599 " %10ld removed as duplicates\n"
1600 " %10ld removed because of the wrong transformation order\n"
1601 " %10ld removed after unsuccessful optimization\n"
1602 " %10ld accepted\n",
1604 StatAccept);
1606 StatAccept = 0;
1607}
1608
1609void find_symmetry_elements(void) {
1611 if (verbose > -1) {
1612 printf("Looking for the inversion center\n");
1613 }
1615 if (verbose > -1) {
1617 printf("Looking for the planes of symmetry\n");
1618 }
1619 find_planes();
1620 if (verbose > -1) {
1622 printf("Looking for infinity axis\n");
1623 }
1625 if (verbose > -1) {
1627 printf("Looking for C2 axes\n");
1628 }
1629 find_c2_axes();
1630 if (verbose > -1) {
1632 printf("Looking for higher axes\n");
1633 }
1635 if (verbose > -1) {
1637 printf("Looking for the improper axes\n");
1638 }
1640 if (verbose > -1) {
1642 }
1643}
1644
1645int compare_axes(const void *a, const void *b) {
1646 SYMMETRY_ELEMENT *axis_a = *(SYMMETRY_ELEMENT **)a;
1647 SYMMETRY_ELEMENT *axis_b = *(SYMMETRY_ELEMENT **)b;
1648 int i, order_a, order_b;
1649
1650 order_a = axis_a->order;
1651 if (order_a == 0)
1652 order_a = 10000;
1653 order_b = axis_b->order;
1654 if (order_b == 0)
1655 order_b = 10000;
1656 if ((i = order_b - order_a) != 0)
1657 return i;
1658 if (axis_a->maxdev > axis_b->maxdev)
1659 return -1;
1660 if (axis_a->maxdev < axis_b->maxdev)
1661 return 1;
1662 return 0;
1663}
1664
1665void sort_symmetry_elements(void) {
1666 if (PlanesCount > 1) {
1667 qsort(Planes, PlanesCount, sizeof(SYMMETRY_ELEMENT *), compare_axes);
1668 }
1669 if (NormalAxesCount > 1) {
1671 compare_axes);
1672 }
1673 if (ImproperAxesCount > 1) {
1675 compare_axes);
1676 }
1677}
1678
1683 report_planes();
1684}
1685
1686void summarize_symmetry_elements(void) {
1687 int i;
1688
1689 NormalAxesCounts = (int *)calloc(MaxAxisOrder + 1, sizeof(int));
1690 ImproperAxesCounts = (int *)calloc(MaxAxisOrder + 1, sizeof(int));
1691 for (i = 0; i < NormalAxesCount; i++)
1692 NormalAxesCounts[NormalAxes[i]->order]++;
1693 for (i = 0; i < ImproperAxesCount; i++)
1695}
1696
1698 int i;
1699 char *symmetry_code =
1700 calloc(1, 10 * (PlanesCount + NormalAxesCount + ImproperAxesCount +
1702 char buf[100];
1703
1704 if (symmetry_code == NULL) {
1705 fprintf(stderr, "Unable to allocate memory for symmetry ID code in "
1706 "report_symmetry_elements_brief()\n");
1707 exit(EXIT_FAILURE);
1708 }
1711 0) {
1712 if (verbose > -1)
1713 printf("Molecule has no symmetry elements\n");
1714 } else {
1715 if (verbose > -1)
1716 printf("Molecule has the following symmetry elements: ");
1717 if (InversionCentersCount > 0)
1718 strcat(symmetry_code, "(i) ");
1719 if (NormalAxesCounts[0] == 1)
1720 strcat(symmetry_code, "(Cinf) ");
1721 if (NormalAxesCounts[0] > 1) {
1722 sprintf(buf, "%d*(Cinf) ", NormalAxesCounts[0]);
1723 strcat(symmetry_code, buf);
1724 }
1725 for (i = MaxAxisOrder; i >= 2; i--) {
1726 if (NormalAxesCounts[i] == 1) {
1727 sprintf(buf, "(C%d) ", i);
1728 strcat(symmetry_code, buf);
1729 }
1730 if (NormalAxesCounts[i] > 1) {
1731 sprintf(buf, "%d*(C%d) ", NormalAxesCounts[i], i);
1732 strcat(symmetry_code, buf);
1733 }
1734 }
1735 for (i = MaxAxisOrder; i >= 2; i--) {
1736 if (ImproperAxesCounts[i] == 1) {
1737 sprintf(buf, "(S%d) ", i);
1738 strcat(symmetry_code, buf);
1739 }
1740 if (ImproperAxesCounts[i] > 1) {
1741 sprintf(buf, "%d*(S%d) ", ImproperAxesCounts[i], i);
1742 strcat(symmetry_code, buf);
1743 }
1744 }
1745 if (PlanesCount == 1)
1746 strcat(symmetry_code, "(sigma) ");
1747 if (PlanesCount > 1) {
1748 sprintf(buf, "%d*(sigma) ", PlanesCount);
1749 strcat(symmetry_code, buf);
1750 }
1751 if (verbose > -1)
1752 printf("%s\n", symmetry_code);
1753 }
1754 SymmetryCode = symmetry_code;
1755}
1756
1757void identify_point_group(int *point_group) {
1758 int i;
1759 int last_matching = -1;
1760 int matching_count = 0;
1761
1762 for (i = 0; i < PointGroupsCount; i++) {
1763 if (strcmp(SymmetryCode, PointGroups[i].symmetry_code) == 0) {
1764 if (PointGroups[i].check() == 1) {
1765 last_matching = i;
1766 matching_count++;
1767 } else {
1768 if (verbose > -2) {
1769 printf("It looks very much like %s, but it is not since %s\n",
1771 }
1772 }
1773 }
1774 }
1775 if (matching_count == 0) {
1776 printf("These symmetry elements match no point group I know of. Sorry.\n");
1777 }
1778 if (matching_count > 1) {
1779 printf("These symmetry elements match more than one group I know of.\n"
1780 "SOMETHING IS VERY WRONG\n");
1781 printf("Matching groups are:\n");
1782 for (i = 0; i < PointGroupsCount; i++) {
1783 if ((strcmp(SymmetryCode, PointGroups[i].symmetry_code) == 0) &&
1784 (PointGroups[i].check() == 1)) {
1785 printf(" %s\n", PointGroups[i].group_name);
1786 }
1787 }
1788 }
1789 if (verbose > 0 && matching_count == 1) {
1790 printf("It seems to be the %s point group\n",
1791 PointGroups[last_matching].group_name);
1792 }
1793
1794 *point_group = last_matching;
1795}
1796
1797/*
1798 * Input/Output
1799 */
1800
1801void FC_FUNC_(symmetries_finite_init,
1802 SYMMETRIES_FINITE_INIT)(const int *natoms, const int *types,
1803 const double *positions,
1804 const int *verbosity, int *point_group) {
1805 int i;
1806
1807 verbose = *verbosity;
1808 AtomsCount = *natoms;
1809 Atoms = calloc(AtomsCount, sizeof(ATOM));
1810 for (i = 0; i < AtomsCount; i++) {
1811 Atoms[i].type = types[i];
1812 Atoms[i].x[0] = positions[3 * i + 0];
1813 Atoms[i].x[1] = positions[3 * i + 1];
1814 Atoms[i].x[2] = positions[3 * i + 2];
1815 if (verbose > 5)
1816 printf("%d %f %f %f\n", Atoms[i].type, Atoms[i].x[0], Atoms[i].x[1],
1817 Atoms[i].x[2]);
1818 }
1819
1820 if (AtomsCount > 0)
1823
1825 if (BadOptimization)
1826 printf("Refinement of some symmetry elements was terminated before "
1827 "convergence was reached.\n"
1828 "Some symmetry elements may remain unidentified.\n");
1829 if (verbose >= 0)
1832 identify_point_group(point_group);
1833}
1834
1835void FC_FUNC_(symmetries_finite_get_group_name,
1836 SYMMETRIES_FINITE_GET_GROUP_NAME)(const int *point_group,
1837 STR_F_TYPE name STR_ARG1) {
1838
1839 TO_F_STR1(PointGroups[*point_group].group_name, name);
1840}
1841
1842void FC_FUNC_(symmetries_finite_get_group_elements,
1843 SYMMETRIES_FINITE_GET_GROUP_ELEMENTS)(
1844 const int *point_group, STR_F_TYPE elements STR_ARG1) {
1845
1846 TO_F_STR1(PointGroups[*point_group].symmetry_code, elements);
1847}
1848
1849void FC_FUNC_(symmetries_finite_end, SYMMETRIES_FINITE_END)() {
1854
1855 free(SymmetryCode);
1856 SymmetryCode = NULL;
1857
1858 free(DistanceFromCenter);
1859 DistanceFromCenter = NULL;
1860
1861 free(NormalAxesCounts);
1862 NormalAxesCounts = NULL;
1863
1864 free(ImproperAxesCounts);
1865 ImproperAxesCounts = NULL;
1866
1867 free(Atoms);
1868 Atoms = NULL;
1869}
void * memcpy(void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__((__nothrow__
!in the code
Definition: global.h:59
pure real(real64) function center(this)
Center of the filter interval.
real(real64) function s()
ptrdiff_t l
Definition: operate_inc.c:12
ptrdiff_t i
Definition: operate_inc.c:12
ptrdiff_t j
Definition: operate_inc.c:12
for(l=0;l< nri;l++)
Definition: operate_inc.c:15
void(* transform_atom)(struct _SYMMETRY_ELEMENT_ *el, ATOM *from, ATOM *to)
SYMMETRY_ELEMENT * init_inversion_center(void)
char * PointGroupRejectionReason
void set_params(SYMMETRY_ELEMENT *elem, double values[])
SYMMETRY_ELEMENT * init_axis_parameters(double a[3], double b[3], double c[3])
void find_center_of_something(void)
static double OptChangeThreshold
SYMMETRY_ELEMENT * init_c2_axis(int i, int j, double support[3])
struct _SYMMETRY_ELEMENT_ SYMMETRY_ELEMENT
static int verbose
void report_improper_axes(void)
SYMMETRY_ELEMENT * init_mirror_plane(int i, int j)
int establish_pairs(SYMMETRY_ELEMENT *elem)
static SYMMETRY_ELEMENT ** NormalAxes
static long StatPairs
int compare_axes(const void *a, const void *b)
static int PlanesCount
void destroy_inversion_centers(void)
void sort_symmetry_elements(void)
void find_improper_axes(void)
void find_inversion_centers(void)
double pow2(double x)
static double * DistanceFromCenter
double sin(double __x) __attribute__((__nothrow__
int check_transform_order(SYMMETRY_ELEMENT *elem)
static double TolerancePrimary
static int MaxAxisOrder
static int NormalAxesCount
void find_higher_axes(void)
void destroy_symmetry_element(SYMMETRY_ELEMENT *elem)
static long StatOrder
static double ToleranceSame
static long StatAccept
static double MaxOptStep
void destroy_normal_axes()
static int ImproperAxesCount
double asin(double __x) __attribute__((__nothrow__
static char * SymmetryCode
void find_symmetry_elements(void)
double fabs(double __x) __attribute__((__nothrow__
void find_c2_axes(void)
static double GradientStep
void find_infinity_axis(void)
SYMMETRY_ELEMENT * alloc_symmetry_element(void)
static SYMMETRY_ELEMENT * MolecularPlane
void optimize_transformation_params(SYMMETRY_ELEMENT *elem)
static long StatOpt
POINT_GROUP PointGroups[]
static SYMMETRY_ELEMENT ** InversionCenters
void invert_atom(SYMMETRY_ELEMENT *center, ATOM *from, ATOM *to)
static ATOM * Atoms
static double ToleranceFinal
SYMMETRY_ELEMENT * init_improper_axis(int ia, int ib, int ic)
static double CenterOfSomething[3]
double sqrt(double __x) __attribute__((__nothrow__
static double MinOptStep
void report_symmetry_elements_brief()
double eval_optimization_target_function(SYMMETRY_ELEMENT *elem, int *finish)
void summarize_symmetry_elements(void)
void report_planes(void)
SYMMETRY_ELEMENT * init_ultimate_plane(void)
void destroy_improper_axes()
void report_symmetry_elements_verbose(void)
static int AtomsCount
static int BadOptimization
int check_transform_quality(SYMMETRY_ELEMENT *elem)
static int * NormalAxesCounts
static SYMMETRY_ELEMENT ** Planes
void rotate_atom(SYMMETRY_ELEMENT *axis, ATOM *from, ATOM *to)
double cos(double __x) __attribute__((__nothrow__
void report_and_reset_counters(void)
void destroy_planes(void)
FILE * stderr
void identify_point_group(int *point_group)
void report_inversion_centers(void)
static int OptChangeHits
void rotate_reflect_atom(SYMMETRY_ELEMENT *axis, ATOM *from, ATOM *to)
static int MaxOptCycles
void get_params(SYMMETRY_ELEMENT *elem, double values[])
double floor(double __x) __attribute__((__nothrow__
static long StatEarly
int same_transform(SYMMETRY_ELEMENT *a, SYMMETRY_ELEMENT *b)
void find_planes(void)
static long StatTotal
void mirror_atom(SYMMETRY_ELEMENT *plane, ATOM *from, ATOM *to)
static int InversionCentersCount
void report_axes(void)
SYMMETRY_ELEMENT * init_ultimate_axis(void)
static long StatDups
static int * ImproperAxesCounts
SYMMETRY_ELEMENT * init_higher_axis(int ia, int ib, int ic)
static SYMMETRY_ELEMENT ** ImproperAxes
int refine_symmetry_element(SYMMETRY_ELEMENT *elem, int build_table)
real(real64) function values(xx)
Definition: test.F90:1867
static var_type * vars
Definition: varinfo_low.c:2763
void distance(const int iatom, const int jatom, const double coordinates[], double *rr, double *rr2, double *rr6, double *rr7)
Definition: vdw_ts_low.c:2059