// // molsymm.cc // // Copyright (C) 1997 Limit Point Systems, Inc. // // Author: Curtis Janssen // Maintainer: LPS // // This file is part of the SC Toolkit. // // The SC Toolkit is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // The SC Toolkit is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with the SC Toolkit; see the file COPYING.LIB. If not, write to // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // // The U.S. Government is granted a limited license as per AL 91-7. // #include #include #include using namespace std; using namespace sc; #undef DEBUG void Molecule::clear_symmetry_info() { for (int i=0; isymbol(),"c1")) { nuniq_ = natom(); for (int i=0; i < natom(); i++) { nequiv_[i]=1; equiv_[i]=new int[1]; equiv_[i][0]=i; atom_to_uniq_[i]=i; } return; } // the first atom is always unique nuniq_ = 1; nequiv_[0]=1; equiv_[0] = new int[1]; equiv_[0][0]=0; atom_to_uniq_[0]=0; CharacterTable ct = point_group()->char_table(); SCVector3 ac; SymmetryOperation so; SCVector3 np; // find the equivalent atoms int i; for (i=1; i < natom(); i++) { ac = r(i); int i_is_unique=1; int i_equiv=0; // apply all symmetry ops in the group to the atom for (int g=0; g < ct.order(); g++) { so = ct.symm_operation(g); for (int ii=0; ii < 3; ii++) { np[ii]=0; for (int jj=0; jj < 3; jj++) np[ii] += so(ii,jj) * ac[jj]; } // see if the transformed atom is equivalent to a unique atom for (int j=0; j maxzero) { maxzero = nzero; jmaxzero = j; } } int tmp = equiv_[i][jmaxzero]; equiv_[i][jmaxzero] = equiv_[i][0]; equiv_[i][0] = tmp; } } int Molecule::has_inversion(SCVector3 &origin, double tol) const { for (int i=0; i tol || fabs(mass(atom)-mass(i)) > tol) { return 0; } } return 1; } int Molecule::is_plane(SCVector3 &origin, SCVector3 &uperp, double tol) const { for (int i=0; i tol || fabs(mass(atom)-mass(i)) > tol) { //ExEnv::outn() << " is_plane: rejected (atom " << i << ")" << endl; return 0; } } return 1; } int Molecule::is_axis(SCVector3 &origin, SCVector3 &axis, int order, double tol) const { // loop through atoms to see if axis is a c2 axis for (int i=0; i tol || fabs(mass(atom)-mass(i)) > tol) { //ExEnv::outn() << " is_axis: rejected (atom " << i << ")" << endl; return 0; } } } return 1; } enum AxisName { XAxis, YAxis, ZAxis }; static AxisName like_world_axis(SCVector3 &axis, const SCVector3 &worldxaxis, const SCVector3 &worldyaxis, const SCVector3 &worldzaxis ) { AxisName like; double xlikeness = fabs(axis.dot(worldxaxis)); double ylikeness = fabs(axis.dot(worldyaxis)); double zlikeness = fabs(axis.dot(worldzaxis)); if (xlikeness > ylikeness && xlikeness > zlikeness) { like = XAxis; if (axis.dot(worldxaxis) < 0) axis = - axis; } else if (ylikeness > zlikeness) { like = YAxis; if (axis.dot(worldyaxis) < 0) axis = - axis; } else { like = ZAxis; if (axis.dot(worldzaxis) < 0) axis = - axis; } return like; } Ref Molecule::highest_point_group(double tol) const { int i,j; SCVector3 com = center_of_mass(); SCVector3 worldzaxis(0.,0.,1.); SCVector3 worldxaxis(1.,0.,0.); SCVector3 worldyaxis(0.,1.,0.); int linear,planar; is_linear_planar(linear,planar,tol); int have_inversion = has_inversion(com,tol); // check for C2 axis SCVector3 c2axis; int have_c2axis = 0; if (natom() < 2) { have_c2axis = 1; c2axis = SCVector3(0.0,0.0,1.0); } else if (linear) { have_c2axis = 1; c2axis = SCVector3(r(1)) - SCVector3(r(0)); c2axis.normalize(); } else if (planar && have_inversion) { // there is a c2 axis that won't be found using the usual algorithm. // find two noncolinear atom-atom vectors (we know that linear==0) SCVector3 BA = SCVector3(r(1))-SCVector3(r(0)); BA.normalize(); for (i=2; i tol) { have_c2axis = 1; BAxCA.normalize(); c2axis = BAxCA; break; } } } else { // loop through pairs of atoms to find C2 axis candidates for (i=0; i tol) continue; SCVector3 B = SCVector3(r(j))-com; // the atoms must be the same distance from the com if (fabs(AdotA - B.dot(B)) > tol) continue; SCVector3 axis = A+B; // atoms colinear with the com don't work if (axis.norm() < tol) continue; axis.normalize(); if (is_axis(com,axis,2,tol)) { have_c2axis = 1; c2axis = axis; goto found_c2axis; } } } } found_c2axis: AxisName c2like = ZAxis; if (have_c2axis) { // try to make the sign of the axis correspond to one of the world axes c2like = like_world_axis(c2axis,worldxaxis,worldyaxis,worldzaxis); } // check for C2 axis perp to first C2 axis SCVector3 c2axisperp; int have_c2axisperp = 0; if (have_c2axis) { if (natom() < 2) { have_c2axisperp = 1; c2axisperp = SCVector3(1.0,0.0,0.0); } else if (linear) { if (have_inversion) { have_c2axisperp = 1; c2axisperp = c2axis.perp_unit(SCVector3(0.0,0.0,1.0)); } } else { // loop through pairs of atoms to find C2 axis candidates for (i=0; i tol) continue; SCVector3 B = SCVector3(r(j))-com; // the atoms must be the same distance from the com if (fabs(AdotA - B.dot(B)) > tol) continue; SCVector3 axis = A+B; // atoms colinear with the com don't work if (axis.norm() < tol) continue; axis.normalize(); // if axis is not perp continue if (fabs(axis.dot(c2axis)) > tol) continue; if (is_axis(com,axis,2,tol)) { have_c2axisperp = 1; c2axisperp = axis; goto found_c2axisperp; } } } } } found_c2axisperp: AxisName c2perplike; if (have_c2axisperp) { // try to make the sign of the axis correspond to one of the world axes c2perplike = like_world_axis(c2axisperp,worldxaxis,worldyaxis,worldzaxis); // try to make c2axis the z axis if (c2perplike == ZAxis) { SCVector3 tmpv = c2axisperp; tmpv = c2axisperp; c2axisperp = c2axis; c2axis = tmpv; c2perplike = c2like; c2like = ZAxis; } if (c2like != ZAxis) { if (c2like == XAxis) c2axis = c2axis.cross(c2axisperp); else c2axis = c2axisperp.cross(c2axis); c2like = like_world_axis(c2axis,worldxaxis,worldyaxis,worldzaxis); } // try to make c2axisperp like the x axis if (c2perplike == YAxis) { c2axisperp = c2axisperp.cross(c2axis); c2perplike = like_world_axis(c2axisperp, worldxaxis,worldyaxis,worldzaxis); } } // check for vertical plane int have_sigmav = 0; SCVector3 sigmav; if (have_c2axis) { if (natom() < 2) { have_sigmav = 1; sigmav = c2axisperp; } else if (linear) { have_sigmav = 1; if (have_c2axisperp) { sigmav = c2axisperp; } else { sigmav = c2axis.perp_unit(SCVector3(0.0,0.0,1.0)); } } else { // loop through pairs of atoms to find sigma v plane candidates for (i=0; i tol) continue; SCVector3 B = SCVector3(r(j))-com; // the atoms must be the same distance from the com if (fabs(AdotA - B.dot(B)) > tol) continue; SCVector3 inplane = B+A; double norm_inplane = inplane.norm(); if (norm_inplane < tol) continue; inplane *= 1.0/norm_inplane; SCVector3 perp = c2axis.cross(inplane); double norm_perp = perp.norm(); if (norm_perp < tol) continue; perp *= 1.0/norm_perp; if (is_plane(com,perp,tol)) { have_sigmav = 1; sigmav = perp; goto found_sigmav; } } } } } found_sigmav: if (have_sigmav) { // try to make the sign of the oop vec correspond to one of the world axes int sigmavlike = like_world_axis(sigmav,worldxaxis,worldyaxis,worldzaxis); // choose sigmav to be the world x axis, if possible if (c2like == ZAxis && sigmavlike == YAxis) { sigmav = sigmav.cross(c2axis); } else if (c2like == YAxis && sigmavlike == ZAxis) { sigmav = c2axis.cross(sigmav); } } // under certain conditions i need to know if there is any sigma plane int have_sigma = 0; SCVector3 sigma; if (!have_inversion && !have_c2axis) { if (planar) { // find two noncolinear atom-atom vectors // we know that linear==0 since !have_c2axis SCVector3 BA = SCVector3(r(1))-SCVector3(r(0)); BA.normalize(); for (i=2; i tol) { have_sigma = 1; BAxCA.normalize(); sigma = BAxCA; break; } } } else { // loop through pairs of atoms to construct trial planes for (i=0; i tol) continue; SCVector3 B = SCVector3(r(j))-com; double BdotB = B.dot(B); // the atoms must be the same distance from the com if (fabs(AdotA - BdotB) > tol) continue; SCVector3 perp = B-A; double norm_perp = perp.norm(); if (norm_perp < tol) { //ExEnv::outn() << " rejected (atoms at same point?)" << endl; continue; } perp *= 1.0/norm_perp; if (is_plane(com,perp,tol)) { have_sigma = 1; sigma = perp; goto found_sigma; } } } } } found_sigma: if (have_sigma) { // try to make the sign of the oop vec correspond to one of the world axes double xlikeness = fabs(sigma.dot(worldxaxis)); double ylikeness = fabs(sigma.dot(worldyaxis)); double zlikeness = fabs(sigma.dot(worldzaxis)); if (xlikeness > ylikeness && xlikeness > zlikeness) { if (sigma.dot(worldxaxis) < 0) sigma = - sigma; } else if (ylikeness > zlikeness) { if (sigma.dot(worldyaxis) < 0) sigma = - sigma; } else { if (sigma.dot(worldzaxis) < 0) sigma = - sigma; } } #ifdef DEBUG ExEnv::out0() << indent << "highest point group:" << endl << indent << " linear = " << linear << endl << indent << " planar = " << planar << endl << indent << " have_inversion = " << have_inversion << endl << indent << " have_c2axis = " << have_c2axis << endl << indent << " have_c2axisperp = " << have_c2axisperp << endl << indent << " have_sigmav = " << have_sigmav << endl << indent << " have_sigma = " << have_sigma << endl; if (have_c2axis) ExEnv::out0() << indent << " c2axis = " << c2axis << endl; if (have_c2axisperp) ExEnv::out0() << indent << " c2axisperp = " << c2axisperp << endl; if (have_sigmav) ExEnv::out0() << indent << " sigmav = " << sigmav << endl; if (have_sigma) ExEnv::out0() << indent << " sigma = " << sigma << endl; #endif // Find the three axes for the symmetry frame SCVector3 xaxis = worldxaxis; SCVector3 yaxis; SCVector3 zaxis = worldzaxis;; if (have_c2axis) { zaxis = c2axis; if (have_sigmav) { xaxis = sigmav; } else if (have_c2axisperp) { xaxis = c2axisperp; } else { // any axis orthogonal to the zaxis will do xaxis = zaxis.perp_unit(zaxis); } } else if (have_sigma) { zaxis = sigma; xaxis = zaxis.perp_unit(zaxis); } // the y axis is then -x cross z yaxis = - xaxis.cross(zaxis); #ifdef DEBUG ExEnv::outn() << "X: " << xaxis << endl; ExEnv::outn() << "Y: " << yaxis << endl; ExEnv::outn() << "Z: " << zaxis << endl; #endif SymmetryOperation frame; SCVector3 origin; for (i=0; i<3; i++) { frame(i,0) = xaxis[i]; frame(i,1) = yaxis[i]; frame(i,2) = zaxis[i]; origin[i] = com[i]; } #ifdef DEBUG ExEnv::out0() << "frame:" << endl; frame.print(ExEnv::out0()); ExEnv::out0() << "origin:" << endl; origin.print(ExEnv::out0()); #endif Ref pg; if (have_inversion) { if (have_c2axis) { if (have_sigmav) { pg = new PointGroup("d2h",frame,origin); } else { pg = new PointGroup("c2h",frame,origin); } } else { pg = new PointGroup("ci",frame,origin); } } else { if (have_c2axis) { if (have_sigmav) { pg = new PointGroup("c2v",frame,origin); } else { if (have_c2axisperp) { pg = new PointGroup("d2",frame,origin); } else { pg = new PointGroup("c2",frame,origin); } } } else { if (have_sigma) { pg = new PointGroup("cs",frame,origin); } else { pg = new PointGroup("c1",frame,origin); } } } return pg; } int Molecule::is_linear(double tol) const { int linear, planar; is_linear_planar(linear,planar,tol); return linear; } int Molecule::is_planar(double tol) const { int linear, planar; is_linear_planar(linear,planar,tol); return planar; } void Molecule::is_linear_planar(int &linear, int &planar, double tol) const { if (natom() < 3) { linear = 1; planar = 1; return; } // find three atoms not on the same line SCVector3 A = r(0); SCVector3 B = r(1); SCVector3 BA = B-A; BA.normalize(); SCVector3 CA; int i; double min_BAdotCA = 1.0; for (i=2; i= 1.0 - tol) { linear = 1; planar = 1; return; } linear = 0; if (natom() < 4) { planar = 1; return; } // check for nontrivial planar molecules SCVector3 BAxCA = BA.cross(CA); BAxCA.normalize(); for (i=2; i tol) { planar = 0; return; } } planar = 1; return; } ///////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: c++ // c-file-style: "CLJ-CONDENSED" // End: