1 | //
|
---|
2 | // molsymm.cc
|
---|
3 | //
|
---|
4 | // Copyright (C) 1997 Limit Point Systems, Inc.
|
---|
5 | //
|
---|
6 | // Author: Curtis Janssen <cljanss@limitpt.com>
|
---|
7 | // Maintainer: LPS
|
---|
8 | //
|
---|
9 | // This file is part of the SC Toolkit.
|
---|
10 | //
|
---|
11 | // The SC Toolkit is free software; you can redistribute it and/or modify
|
---|
12 | // it under the terms of the GNU Library General Public License as published by
|
---|
13 | // the Free Software Foundation; either version 2, or (at your option)
|
---|
14 | // any later version.
|
---|
15 | //
|
---|
16 | // The SC Toolkit is distributed in the hope that it will be useful,
|
---|
17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
19 | // GNU Library General Public License for more details.
|
---|
20 | //
|
---|
21 | // You should have received a copy of the GNU Library General Public License
|
---|
22 | // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
|
---|
23 | // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
24 | //
|
---|
25 | // The U.S. Government is granted a limited license as per AL 91-7.
|
---|
26 | //
|
---|
27 |
|
---|
28 | #include <util/misc/math.h>
|
---|
29 |
|
---|
30 | #include <util/misc/formio.h>
|
---|
31 | #include <chemistry/molecule/molecule.h>
|
---|
32 |
|
---|
33 | using namespace std;
|
---|
34 | using namespace sc;
|
---|
35 |
|
---|
36 | #undef DEBUG
|
---|
37 |
|
---|
38 | void
|
---|
39 | Molecule::clear_symmetry_info()
|
---|
40 | {
|
---|
41 | for (int i=0; i<nuniq_; i++) {
|
---|
42 | delete[] equiv_[i];
|
---|
43 | }
|
---|
44 | delete[] equiv_;
|
---|
45 | delete[] nequiv_;
|
---|
46 | delete[] atom_to_uniq_;
|
---|
47 | nuniq_ = 0;
|
---|
48 | equiv_ = 0;
|
---|
49 | nequiv_ = 0;
|
---|
50 | atom_to_uniq_ = 0;
|
---|
51 | }
|
---|
52 |
|
---|
53 | void
|
---|
54 | Molecule::init_symmetry_info(double tol)
|
---|
55 | {
|
---|
56 | if (equiv_)
|
---|
57 | clear_symmetry_info();
|
---|
58 |
|
---|
59 | if (natom() == 0) {
|
---|
60 | nuniq_ = 0;
|
---|
61 | equiv_ = 0;
|
---|
62 | nequiv_ = 0;
|
---|
63 | atom_to_uniq_ = 0;
|
---|
64 | return;
|
---|
65 | }
|
---|
66 |
|
---|
67 | nequiv_ = new int[natom()];
|
---|
68 | atom_to_uniq_ = new int[natom()];
|
---|
69 | equiv_ = new int*[natom()];
|
---|
70 |
|
---|
71 | if (!strcmp(point_group()->symbol(),"c1")) {
|
---|
72 | nuniq_ = natom();
|
---|
73 | for (int i=0; i < natom(); i++) {
|
---|
74 | nequiv_[i]=1;
|
---|
75 | equiv_[i]=new int[1];
|
---|
76 | equiv_[i][0]=i;
|
---|
77 | atom_to_uniq_[i]=i;
|
---|
78 | }
|
---|
79 | return;
|
---|
80 | }
|
---|
81 |
|
---|
82 | // the first atom is always unique
|
---|
83 | nuniq_ = 1;
|
---|
84 | nequiv_[0]=1;
|
---|
85 | equiv_[0] = new int[1];
|
---|
86 | equiv_[0][0]=0;
|
---|
87 | atom_to_uniq_[0]=0;
|
---|
88 |
|
---|
89 | CharacterTable ct = point_group()->char_table();
|
---|
90 |
|
---|
91 | SCVector3 ac;
|
---|
92 | SymmetryOperation so;
|
---|
93 | SCVector3 np;
|
---|
94 |
|
---|
95 | // find the equivalent atoms
|
---|
96 | int i;
|
---|
97 | for (i=1; i < natom(); i++) {
|
---|
98 | ac = r(i);
|
---|
99 | int i_is_unique=1;
|
---|
100 | int i_equiv=0;
|
---|
101 |
|
---|
102 | // apply all symmetry ops in the group to the atom
|
---|
103 | for (int g=0; g < ct.order(); g++) {
|
---|
104 | so = ct.symm_operation(g);
|
---|
105 | for (int ii=0; ii < 3; ii++) {
|
---|
106 | np[ii]=0;
|
---|
107 | for (int jj=0; jj < 3; jj++) np[ii] += so(ii,jj) * ac[jj];
|
---|
108 | }
|
---|
109 |
|
---|
110 | // see if the transformed atom is equivalent to a unique atom
|
---|
111 | for (int j=0; j<nuniq_; j++) {
|
---|
112 | int uniq = equiv_[j][0];
|
---|
113 | SCVector3 aj(r(uniq));
|
---|
114 | if (np.dist(aj) < tol
|
---|
115 | && Z(uniq) == Z(i)
|
---|
116 | && fabs(charge(uniq)-charge(i)) < tol
|
---|
117 | && fabs(mass(uniq)-mass(i)) < tol) {
|
---|
118 | i_is_unique = 0;
|
---|
119 | i_equiv = j;
|
---|
120 | break;
|
---|
121 | }
|
---|
122 | }
|
---|
123 | }
|
---|
124 | if (i_is_unique) {
|
---|
125 | nequiv_[nuniq_]=1;
|
---|
126 | equiv_[nuniq_]=new int[1];
|
---|
127 | equiv_[nuniq_][0]=i;
|
---|
128 | atom_to_uniq_[i] = nuniq_;
|
---|
129 | nuniq_++;
|
---|
130 | }
|
---|
131 | else {
|
---|
132 | int *tmp = new int[nequiv_[i_equiv]+1];
|
---|
133 | memcpy(tmp,equiv_[i_equiv],nequiv_[i_equiv]*sizeof(int));
|
---|
134 | delete[] equiv_[i_equiv];
|
---|
135 | equiv_[i_equiv] = tmp;
|
---|
136 | equiv_[i_equiv][nequiv_[i_equiv]] = i;
|
---|
137 | nequiv_[i_equiv]++;
|
---|
138 | atom_to_uniq_[i] = i_equiv;
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | // The first atom in the equiv list is considered the primary unique
|
---|
143 | // atom. Just to make things look pretty, make the atom with the most
|
---|
144 | // zeros in its x, y, z coordinate the unique atom. Nothing else should
|
---|
145 | // rely on this being done.
|
---|
146 | double ztol=1.0e-5;
|
---|
147 | for (i=0; i < nuniq_; i++) {
|
---|
148 | int maxzero = 0;
|
---|
149 | int jmaxzero = 0;
|
---|
150 | for (int j=0; j<nequiv_[i]; j++) {
|
---|
151 | int nzero = 0;
|
---|
152 | for (int k=0; k<3; k++) if (fabs(r(equiv_[i][j],k)) < ztol) nzero++;
|
---|
153 | if (nzero > maxzero) {
|
---|
154 | maxzero = nzero;
|
---|
155 | jmaxzero = j;
|
---|
156 | }
|
---|
157 | }
|
---|
158 | int tmp = equiv_[i][jmaxzero];
|
---|
159 | equiv_[i][jmaxzero] = equiv_[i][0];
|
---|
160 | equiv_[i][0] = tmp;
|
---|
161 | }
|
---|
162 | }
|
---|
163 |
|
---|
164 | int
|
---|
165 | Molecule::has_inversion(SCVector3 &origin, double tol) const
|
---|
166 | {
|
---|
167 | for (int i=0; i<natom(); i++) {
|
---|
168 | SCVector3 inverted = origin-(SCVector3(r(i))-origin);
|
---|
169 | int atom = atom_at_position(inverted.data(), tol);
|
---|
170 | if (atom < 0
|
---|
171 | || Z(atom) != Z(i)
|
---|
172 | || fabs(charge(atom)-charge(i)) > tol
|
---|
173 | || fabs(mass(atom)-mass(i)) > tol) {
|
---|
174 | return 0;
|
---|
175 | }
|
---|
176 | }
|
---|
177 | return 1;
|
---|
178 | }
|
---|
179 |
|
---|
180 | int
|
---|
181 | Molecule::is_plane(SCVector3 &origin, SCVector3 &uperp, double tol) const
|
---|
182 | {
|
---|
183 | for (int i=0; i<natom(); i++) {
|
---|
184 | SCVector3 A = SCVector3(r(i))-origin;
|
---|
185 | SCVector3 Apar = uperp.dot(A) * uperp;
|
---|
186 | SCVector3 Aperp = A - Apar;
|
---|
187 | A = (Aperp - Apar) + origin;
|
---|
188 | int atom = atom_at_position(A.data(), tol);
|
---|
189 | if (atom < 0
|
---|
190 | || Z(atom) != Z(i)
|
---|
191 | || fabs(charge(atom)-charge(i)) > tol
|
---|
192 | || fabs(mass(atom)-mass(i)) > tol) {
|
---|
193 | //ExEnv::outn() << " is_plane: rejected (atom " << i << ")" << endl;
|
---|
194 | return 0;
|
---|
195 | }
|
---|
196 | }
|
---|
197 | return 1;
|
---|
198 | }
|
---|
199 |
|
---|
200 | int
|
---|
201 | Molecule::is_axis(SCVector3 &origin, SCVector3 &axis,
|
---|
202 | int order, double tol) const
|
---|
203 | {
|
---|
204 | // loop through atoms to see if axis is a c2 axis
|
---|
205 | for (int i=0; i<natom(); i++) {
|
---|
206 | SCVector3 A = SCVector3(r(i))-origin;
|
---|
207 | for (int j=1; j<order; j++) {
|
---|
208 | SCVector3 R = A;
|
---|
209 | R.rotate(j*2.0*M_PI/order, axis);
|
---|
210 | R += origin;
|
---|
211 | int atom = atom_at_position(R.data(), tol);
|
---|
212 | if (atom < 0
|
---|
213 | || Z(atom) != Z(i)
|
---|
214 | || fabs(charge(atom)-charge(i)) > tol
|
---|
215 | || fabs(mass(atom)-mass(i)) > tol) {
|
---|
216 | //ExEnv::outn() << " is_axis: rejected (atom " << i << ")" << endl;
|
---|
217 | return 0;
|
---|
218 | }
|
---|
219 | }
|
---|
220 | }
|
---|
221 | return 1;
|
---|
222 | }
|
---|
223 |
|
---|
224 | enum AxisName { XAxis, YAxis, ZAxis };
|
---|
225 |
|
---|
226 | static AxisName
|
---|
227 | like_world_axis(SCVector3 &axis,
|
---|
228 | const SCVector3 &worldxaxis,
|
---|
229 | const SCVector3 &worldyaxis,
|
---|
230 | const SCVector3 &worldzaxis
|
---|
231 | )
|
---|
232 | {
|
---|
233 | AxisName like;
|
---|
234 | double xlikeness = fabs(axis.dot(worldxaxis));
|
---|
235 | double ylikeness = fabs(axis.dot(worldyaxis));
|
---|
236 | double zlikeness = fabs(axis.dot(worldzaxis));
|
---|
237 | if (xlikeness > ylikeness && xlikeness > zlikeness) {
|
---|
238 | like = XAxis;
|
---|
239 | if (axis.dot(worldxaxis) < 0) axis = - axis;
|
---|
240 | }
|
---|
241 | else if (ylikeness > zlikeness) {
|
---|
242 | like = YAxis;
|
---|
243 | if (axis.dot(worldyaxis) < 0) axis = - axis;
|
---|
244 | }
|
---|
245 | else {
|
---|
246 | like = ZAxis;
|
---|
247 | if (axis.dot(worldzaxis) < 0) axis = - axis;
|
---|
248 | }
|
---|
249 | return like;
|
---|
250 | }
|
---|
251 |
|
---|
252 | Ref<PointGroup>
|
---|
253 | Molecule::highest_point_group(double tol) const
|
---|
254 | {
|
---|
255 | int i,j;
|
---|
256 |
|
---|
257 | SCVector3 com = center_of_mass();
|
---|
258 |
|
---|
259 | SCVector3 worldzaxis(0.,0.,1.);
|
---|
260 | SCVector3 worldxaxis(1.,0.,0.);
|
---|
261 | SCVector3 worldyaxis(0.,1.,0.);
|
---|
262 |
|
---|
263 | int linear,planar;
|
---|
264 | is_linear_planar(linear,planar,tol);
|
---|
265 |
|
---|
266 | int have_inversion = has_inversion(com,tol);
|
---|
267 |
|
---|
268 | // check for C2 axis
|
---|
269 | SCVector3 c2axis;
|
---|
270 | int have_c2axis = 0;
|
---|
271 | if (natom() < 2) {
|
---|
272 | have_c2axis = 1;
|
---|
273 | c2axis = SCVector3(0.0,0.0,1.0);
|
---|
274 | }
|
---|
275 | else if (linear) {
|
---|
276 | have_c2axis = 1;
|
---|
277 | c2axis = SCVector3(r(1)) - SCVector3(r(0));
|
---|
278 | c2axis.normalize();
|
---|
279 | }
|
---|
280 | else if (planar && have_inversion) {
|
---|
281 | // there is a c2 axis that won't be found using the usual algorithm.
|
---|
282 | // find two noncolinear atom-atom vectors (we know that linear==0)
|
---|
283 | SCVector3 BA = SCVector3(r(1))-SCVector3(r(0));
|
---|
284 | BA.normalize();
|
---|
285 | for (i=2; i<natom(); i++) {
|
---|
286 | SCVector3 CA = SCVector3(r(i))-SCVector3(r(0));
|
---|
287 | CA.normalize();
|
---|
288 | SCVector3 BAxCA = BA.cross(CA);
|
---|
289 | if (BAxCA.norm() > tol) {
|
---|
290 | have_c2axis = 1;
|
---|
291 | BAxCA.normalize();
|
---|
292 | c2axis = BAxCA;
|
---|
293 | break;
|
---|
294 | }
|
---|
295 | }
|
---|
296 | }
|
---|
297 | else {
|
---|
298 | // loop through pairs of atoms to find C2 axis candidates
|
---|
299 | for (i=0; i<natom(); i++) {
|
---|
300 | SCVector3 A = SCVector3(r(i))-com;
|
---|
301 | double AdotA = A.dot(A);
|
---|
302 | for (j=0; j<=i; j++) {
|
---|
303 | // the atoms must be identical
|
---|
304 | if (Z(i) != Z(j) || fabs(mass(i)-mass(j)) > tol) continue;
|
---|
305 | SCVector3 B = SCVector3(r(j))-com;
|
---|
306 | // the atoms must be the same distance from the com
|
---|
307 | if (fabs(AdotA - B.dot(B)) > tol) continue;
|
---|
308 | SCVector3 axis = A+B;
|
---|
309 | // atoms colinear with the com don't work
|
---|
310 | if (axis.norm() < tol) continue;
|
---|
311 | axis.normalize();
|
---|
312 | if (is_axis(com,axis,2,tol)) {
|
---|
313 | have_c2axis = 1;
|
---|
314 | c2axis = axis;
|
---|
315 | goto found_c2axis;
|
---|
316 | }
|
---|
317 | }
|
---|
318 | }
|
---|
319 | }
|
---|
320 | found_c2axis:
|
---|
321 |
|
---|
322 | AxisName c2like = ZAxis;
|
---|
323 | if (have_c2axis) {
|
---|
324 | // try to make the sign of the axis correspond to one of the world axes
|
---|
325 | c2like = like_world_axis(c2axis,worldxaxis,worldyaxis,worldzaxis);
|
---|
326 | }
|
---|
327 |
|
---|
328 | // check for C2 axis perp to first C2 axis
|
---|
329 | SCVector3 c2axisperp;
|
---|
330 | int have_c2axisperp = 0;
|
---|
331 | if (have_c2axis) {
|
---|
332 | if (natom() < 2) {
|
---|
333 | have_c2axisperp = 1;
|
---|
334 | c2axisperp = SCVector3(1.0,0.0,0.0);
|
---|
335 | }
|
---|
336 | else if (linear) {
|
---|
337 | if (have_inversion) {
|
---|
338 | have_c2axisperp = 1;
|
---|
339 | c2axisperp = c2axis.perp_unit(SCVector3(0.0,0.0,1.0));
|
---|
340 | }
|
---|
341 | }
|
---|
342 | else {
|
---|
343 | // loop through pairs of atoms to find C2 axis candidates
|
---|
344 | for (i=0; i<natom(); i++) {
|
---|
345 | SCVector3 A = SCVector3(r(i))-com;
|
---|
346 | double AdotA = A.dot(A);
|
---|
347 | for (j=0; j<i; j++) {
|
---|
348 | // the atoms must be identical
|
---|
349 | if (Z(i) != Z(j) || fabs(mass(i)-mass(j)) > tol) continue;
|
---|
350 | SCVector3 B = SCVector3(r(j))-com;
|
---|
351 | // the atoms must be the same distance from the com
|
---|
352 | if (fabs(AdotA - B.dot(B)) > tol) continue;
|
---|
353 | SCVector3 axis = A+B;
|
---|
354 | // atoms colinear with the com don't work
|
---|
355 | if (axis.norm() < tol) continue;
|
---|
356 | axis.normalize();
|
---|
357 | // if axis is not perp continue
|
---|
358 | if (fabs(axis.dot(c2axis)) > tol) continue;
|
---|
359 | if (is_axis(com,axis,2,tol)) {
|
---|
360 | have_c2axisperp = 1;
|
---|
361 | c2axisperp = axis;
|
---|
362 | goto found_c2axisperp;
|
---|
363 | }
|
---|
364 | }
|
---|
365 | }
|
---|
366 | }
|
---|
367 | }
|
---|
368 | found_c2axisperp:
|
---|
369 |
|
---|
370 | AxisName c2perplike;
|
---|
371 | if (have_c2axisperp) {
|
---|
372 | // try to make the sign of the axis correspond to one of the world axes
|
---|
373 | c2perplike = like_world_axis(c2axisperp,worldxaxis,worldyaxis,worldzaxis);
|
---|
374 |
|
---|
375 | // try to make c2axis the z axis
|
---|
376 | if (c2perplike == ZAxis) {
|
---|
377 | SCVector3 tmpv = c2axisperp;
|
---|
378 | tmpv = c2axisperp; c2axisperp = c2axis; c2axis = tmpv;
|
---|
379 | c2perplike = c2like;
|
---|
380 | c2like = ZAxis;
|
---|
381 | }
|
---|
382 | if (c2like != ZAxis) {
|
---|
383 | if (c2like == XAxis) c2axis = c2axis.cross(c2axisperp);
|
---|
384 | else c2axis = c2axisperp.cross(c2axis);
|
---|
385 | c2like = like_world_axis(c2axis,worldxaxis,worldyaxis,worldzaxis);
|
---|
386 | }
|
---|
387 |
|
---|
388 | // try to make c2axisperp like the x axis
|
---|
389 | if (c2perplike == YAxis) {
|
---|
390 | c2axisperp = c2axisperp.cross(c2axis);
|
---|
391 | c2perplike = like_world_axis(c2axisperp,
|
---|
392 | worldxaxis,worldyaxis,worldzaxis);
|
---|
393 | }
|
---|
394 | }
|
---|
395 |
|
---|
396 | // check for vertical plane
|
---|
397 | int have_sigmav = 0;
|
---|
398 | SCVector3 sigmav;
|
---|
399 | if (have_c2axis) {
|
---|
400 | if (natom() < 2) {
|
---|
401 | have_sigmav = 1;
|
---|
402 | sigmav = c2axisperp;
|
---|
403 | }
|
---|
404 | else if (linear) {
|
---|
405 | have_sigmav = 1;
|
---|
406 | if (have_c2axisperp) {
|
---|
407 | sigmav = c2axisperp;
|
---|
408 | }
|
---|
409 | else {
|
---|
410 | sigmav = c2axis.perp_unit(SCVector3(0.0,0.0,1.0));
|
---|
411 | }
|
---|
412 | }
|
---|
413 | else {
|
---|
414 | // loop through pairs of atoms to find sigma v plane candidates
|
---|
415 | for (i=0; i<natom(); i++) {
|
---|
416 | SCVector3 A = SCVector3(r(i))-com;
|
---|
417 | double AdotA = A.dot(A);
|
---|
418 | // the second atom can equal i because i might be in the plane.
|
---|
419 | for (j=0; j<=i; j++) {
|
---|
420 | // the atoms must be identical
|
---|
421 | if (Z(i) != Z(j) || fabs(mass(i)-mass(j)) > tol) continue;
|
---|
422 | SCVector3 B = SCVector3(r(j))-com;
|
---|
423 | // the atoms must be the same distance from the com
|
---|
424 | if (fabs(AdotA - B.dot(B)) > tol) continue;
|
---|
425 | SCVector3 inplane = B+A;
|
---|
426 | double norm_inplane = inplane.norm();
|
---|
427 | if (norm_inplane < tol) continue;
|
---|
428 | inplane *= 1.0/norm_inplane;
|
---|
429 | SCVector3 perp = c2axis.cross(inplane);
|
---|
430 | double norm_perp = perp.norm();
|
---|
431 | if (norm_perp < tol) continue;
|
---|
432 | perp *= 1.0/norm_perp;
|
---|
433 | if (is_plane(com,perp,tol)) {
|
---|
434 | have_sigmav = 1;
|
---|
435 | sigmav = perp;
|
---|
436 | goto found_sigmav;
|
---|
437 | }
|
---|
438 | }
|
---|
439 | }
|
---|
440 | }
|
---|
441 | }
|
---|
442 | found_sigmav:
|
---|
443 |
|
---|
444 | if (have_sigmav) {
|
---|
445 | // try to make the sign of the oop vec correspond to one of the world axes
|
---|
446 | int sigmavlike = like_world_axis(sigmav,worldxaxis,worldyaxis,worldzaxis);
|
---|
447 |
|
---|
448 | // choose sigmav to be the world x axis, if possible
|
---|
449 | if (c2like == ZAxis && sigmavlike == YAxis) {
|
---|
450 | sigmav = sigmav.cross(c2axis);
|
---|
451 | }
|
---|
452 | else if (c2like == YAxis && sigmavlike == ZAxis) {
|
---|
453 | sigmav = c2axis.cross(sigmav);
|
---|
454 | }
|
---|
455 | }
|
---|
456 |
|
---|
457 | // under certain conditions i need to know if there is any sigma plane
|
---|
458 | int have_sigma = 0;
|
---|
459 | SCVector3 sigma;
|
---|
460 | if (!have_inversion && !have_c2axis) {
|
---|
461 | if (planar) {
|
---|
462 | // find two noncolinear atom-atom vectors
|
---|
463 | // we know that linear==0 since !have_c2axis
|
---|
464 | SCVector3 BA = SCVector3(r(1))-SCVector3(r(0));
|
---|
465 | BA.normalize();
|
---|
466 | for (i=2; i<natom(); i++) {
|
---|
467 | SCVector3 CA = SCVector3(r(i))-SCVector3(r(0));
|
---|
468 | CA.normalize();
|
---|
469 | SCVector3 BAxCA = BA.cross(CA);
|
---|
470 | if (BAxCA.norm() > tol) {
|
---|
471 | have_sigma = 1;
|
---|
472 | BAxCA.normalize();
|
---|
473 | sigma = BAxCA;
|
---|
474 | break;
|
---|
475 | }
|
---|
476 | }
|
---|
477 | }
|
---|
478 | else {
|
---|
479 | // loop through pairs of atoms to construct trial planes
|
---|
480 | for (i=0; i<natom(); i++) {
|
---|
481 | SCVector3 A = SCVector3(r(i))-com;
|
---|
482 | double AdotA = A.dot(A);
|
---|
483 | for (j=0; j<i; j++) {
|
---|
484 | //ExEnv::outn() << "sigma atoms = " << i << ", " << j << endl;
|
---|
485 | // the atoms must be identical
|
---|
486 | if (Z(i) != Z(j) || fabs(mass(i)-mass(j)) > tol) continue;
|
---|
487 | SCVector3 B = SCVector3(r(j))-com;
|
---|
488 | double BdotB = B.dot(B);
|
---|
489 | // the atoms must be the same distance from the com
|
---|
490 | if (fabs(AdotA - BdotB) > tol) continue;
|
---|
491 | SCVector3 perp = B-A;
|
---|
492 | double norm_perp = perp.norm();
|
---|
493 | if (norm_perp < tol) {
|
---|
494 | //ExEnv::outn() << " rejected (atoms at same point?)" << endl;
|
---|
495 | continue;
|
---|
496 | }
|
---|
497 | perp *= 1.0/norm_perp;
|
---|
498 | if (is_plane(com,perp,tol)) {
|
---|
499 | have_sigma = 1;
|
---|
500 | sigma = perp;
|
---|
501 | goto found_sigma;
|
---|
502 | }
|
---|
503 | }
|
---|
504 | }
|
---|
505 | }
|
---|
506 | }
|
---|
507 | found_sigma:
|
---|
508 |
|
---|
509 | if (have_sigma) {
|
---|
510 | // try to make the sign of the oop vec correspond to one of the world axes
|
---|
511 | double xlikeness = fabs(sigma.dot(worldxaxis));
|
---|
512 | double ylikeness = fabs(sigma.dot(worldyaxis));
|
---|
513 | double zlikeness = fabs(sigma.dot(worldzaxis));
|
---|
514 | if (xlikeness > ylikeness && xlikeness > zlikeness) {
|
---|
515 | if (sigma.dot(worldxaxis) < 0) sigma = - sigma;
|
---|
516 | }
|
---|
517 | else if (ylikeness > zlikeness) {
|
---|
518 | if (sigma.dot(worldyaxis) < 0) sigma = - sigma;
|
---|
519 | }
|
---|
520 | else {
|
---|
521 | if (sigma.dot(worldzaxis) < 0) sigma = - sigma;
|
---|
522 | }
|
---|
523 | }
|
---|
524 |
|
---|
525 | #ifdef DEBUG
|
---|
526 | ExEnv::out0()
|
---|
527 | << indent << "highest point group:" << endl
|
---|
528 | << indent << " linear = " << linear << endl
|
---|
529 | << indent << " planar = " << planar << endl
|
---|
530 | << indent << " have_inversion = " << have_inversion << endl
|
---|
531 | << indent << " have_c2axis = " << have_c2axis << endl
|
---|
532 | << indent << " have_c2axisperp = " << have_c2axisperp << endl
|
---|
533 | << indent << " have_sigmav = " << have_sigmav << endl
|
---|
534 | << indent << " have_sigma = " << have_sigma << endl;
|
---|
535 |
|
---|
536 | if (have_c2axis)
|
---|
537 | ExEnv::out0() << indent << " c2axis = " << c2axis << endl;
|
---|
538 | if (have_c2axisperp)
|
---|
539 | ExEnv::out0() << indent << " c2axisperp = " << c2axisperp << endl;
|
---|
540 | if (have_sigmav)
|
---|
541 | ExEnv::out0() << indent << " sigmav = " << sigmav << endl;
|
---|
542 | if (have_sigma)
|
---|
543 | ExEnv::out0() << indent << " sigma = " << sigma << endl;
|
---|
544 | #endif
|
---|
545 |
|
---|
546 | // Find the three axes for the symmetry frame
|
---|
547 | SCVector3 xaxis = worldxaxis;
|
---|
548 | SCVector3 yaxis;
|
---|
549 | SCVector3 zaxis = worldzaxis;;
|
---|
550 | if (have_c2axis) {
|
---|
551 | zaxis = c2axis;
|
---|
552 | if (have_sigmav) {
|
---|
553 | xaxis = sigmav;
|
---|
554 | }
|
---|
555 | else if (have_c2axisperp) {
|
---|
556 | xaxis = c2axisperp;
|
---|
557 | }
|
---|
558 | else {
|
---|
559 | // any axis orthogonal to the zaxis will do
|
---|
560 | xaxis = zaxis.perp_unit(zaxis);
|
---|
561 | }
|
---|
562 | }
|
---|
563 | else if (have_sigma) {
|
---|
564 | zaxis = sigma;
|
---|
565 | xaxis = zaxis.perp_unit(zaxis);
|
---|
566 | }
|
---|
567 | // the y axis is then -x cross z
|
---|
568 | yaxis = - xaxis.cross(zaxis);
|
---|
569 |
|
---|
570 | #ifdef DEBUG
|
---|
571 | ExEnv::outn() << "X: " << xaxis << endl;
|
---|
572 | ExEnv::outn() << "Y: " << yaxis << endl;
|
---|
573 | ExEnv::outn() << "Z: " << zaxis << endl;
|
---|
574 | #endif
|
---|
575 |
|
---|
576 | SymmetryOperation frame;
|
---|
577 | SCVector3 origin;
|
---|
578 | for (i=0; i<3; i++) {
|
---|
579 | frame(i,0) = xaxis[i];
|
---|
580 | frame(i,1) = yaxis[i];
|
---|
581 | frame(i,2) = zaxis[i];
|
---|
582 | origin[i] = com[i];
|
---|
583 | }
|
---|
584 |
|
---|
585 | #ifdef DEBUG
|
---|
586 | ExEnv::out0() << "frame:" << endl;
|
---|
587 | frame.print(ExEnv::out0());
|
---|
588 |
|
---|
589 | ExEnv::out0() << "origin:" << endl;
|
---|
590 | origin.print(ExEnv::out0());
|
---|
591 | #endif
|
---|
592 |
|
---|
593 | Ref<PointGroup> pg;
|
---|
594 | if (have_inversion) {
|
---|
595 | if (have_c2axis) {
|
---|
596 | if (have_sigmav) {
|
---|
597 | pg = new PointGroup("d2h",frame,origin);
|
---|
598 | }
|
---|
599 | else {
|
---|
600 | pg = new PointGroup("c2h",frame,origin);
|
---|
601 | }
|
---|
602 | }
|
---|
603 | else {
|
---|
604 | pg = new PointGroup("ci",frame,origin);
|
---|
605 | }
|
---|
606 | }
|
---|
607 | else {
|
---|
608 | if (have_c2axis) {
|
---|
609 | if (have_sigmav) {
|
---|
610 | pg = new PointGroup("c2v",frame,origin);
|
---|
611 | }
|
---|
612 | else {
|
---|
613 | if (have_c2axisperp) {
|
---|
614 | pg = new PointGroup("d2",frame,origin);
|
---|
615 | }
|
---|
616 | else {
|
---|
617 | pg = new PointGroup("c2",frame,origin);
|
---|
618 | }
|
---|
619 | }
|
---|
620 | }
|
---|
621 | else {
|
---|
622 | if (have_sigma) {
|
---|
623 | pg = new PointGroup("cs",frame,origin);
|
---|
624 | }
|
---|
625 | else {
|
---|
626 | pg = new PointGroup("c1",frame,origin);
|
---|
627 | }
|
---|
628 | }
|
---|
629 | }
|
---|
630 |
|
---|
631 | return pg;
|
---|
632 | }
|
---|
633 |
|
---|
634 | int
|
---|
635 | Molecule::is_linear(double tol) const
|
---|
636 | {
|
---|
637 | int linear, planar;
|
---|
638 |
|
---|
639 | is_linear_planar(linear,planar,tol);
|
---|
640 |
|
---|
641 | return linear;
|
---|
642 | }
|
---|
643 |
|
---|
644 | int
|
---|
645 | Molecule::is_planar(double tol) const
|
---|
646 | {
|
---|
647 | int linear, planar;
|
---|
648 |
|
---|
649 | is_linear_planar(linear,planar,tol);
|
---|
650 |
|
---|
651 | return planar;
|
---|
652 | }
|
---|
653 |
|
---|
654 | void
|
---|
655 | Molecule::is_linear_planar(int &linear, int &planar, double tol) const
|
---|
656 | {
|
---|
657 | if (natom() < 3) {
|
---|
658 | linear = 1;
|
---|
659 | planar = 1;
|
---|
660 | return;
|
---|
661 | }
|
---|
662 |
|
---|
663 | // find three atoms not on the same line
|
---|
664 | SCVector3 A = r(0);
|
---|
665 | SCVector3 B = r(1);
|
---|
666 | SCVector3 BA = B-A;
|
---|
667 | BA.normalize();
|
---|
668 | SCVector3 CA;
|
---|
669 |
|
---|
670 | int i;
|
---|
671 | double min_BAdotCA = 1.0;
|
---|
672 | for (i=2; i<natom(); i++) {
|
---|
673 | SCVector3 tmp = SCVector3(r(i))-A;
|
---|
674 | tmp.normalize();
|
---|
675 | if (fabs(BA.dot(tmp)) < min_BAdotCA) {
|
---|
676 | CA = tmp;
|
---|
677 | min_BAdotCA = fabs(BA.dot(tmp));
|
---|
678 | }
|
---|
679 | }
|
---|
680 | if (min_BAdotCA >= 1.0 - tol) {
|
---|
681 | linear = 1;
|
---|
682 | planar = 1;
|
---|
683 | return;
|
---|
684 | }
|
---|
685 |
|
---|
686 | linear = 0;
|
---|
687 | if (natom() < 4) {
|
---|
688 | planar = 1;
|
---|
689 | return;
|
---|
690 | }
|
---|
691 |
|
---|
692 | // check for nontrivial planar molecules
|
---|
693 | SCVector3 BAxCA = BA.cross(CA);
|
---|
694 | BAxCA.normalize();
|
---|
695 | for (i=2; i<natom(); i++) {
|
---|
696 | SCVector3 tmp = SCVector3(r(i))-A;
|
---|
697 | if (fabs(tmp.dot(BAxCA)) > tol) {
|
---|
698 | planar = 0;
|
---|
699 | return;
|
---|
700 | }
|
---|
701 | }
|
---|
702 | planar = 1;
|
---|
703 | return;
|
---|
704 | }
|
---|
705 |
|
---|
706 | /////////////////////////////////////////////////////////////////////////////
|
---|
707 |
|
---|
708 | // Local Variables:
|
---|
709 | // mode: c++
|
---|
710 | // c-file-style: "CLJ-CONDENSED"
|
---|
711 | // End:
|
---|