~ubuntu-branches/debian/stretch/openbabel/stretch

« back to all changes in this revision

Viewing changes to src/pointgroup.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michael Banck
  • Date: 2008-07-22 23:54:58 UTC
  • mfrom: (3.1.10 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080722235458-3o606czluviz4akx
Tags: 2.2.0-2
* Upload to unstable.
* debian/control: Updated descriptions.
* debian/patches/gauss_cube_format.patch: New patch, makes the 
  gaussian cube format available again.
* debian/rules (DEB_DH_MAKESHLIBS_ARGS_libopenbabel3): Removed.
* debian/rules (DEB_CONFIGURE_EXTRA_FLAGS): Likewise.
* debian/libopenbabel3.install: Adjust formats directory to single 
  version hierarchy.

Show diffs side-by-side

added added

removed removed

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