| [0b990d] | 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:
 | 
|---|