00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include <stdio.h>
00036 #include <math.h>
00037 #include "utilities.h"
00038 #include "Measure.h"
00039 #include "MeasureSymmetry.h"
00040 #include "AtomSel.h"
00041 #include "Matrix4.h"
00042 #include "MoleculeList.h"
00043 #include "SpatialSearch.h"
00044 #include "MoleculeGraphics.h"
00045 #include "Inform.h"
00046 #include "WKFUtils.h"
00047
00048 #define POINTGROUP_UNKNOWN 0
00049 #define POINTGROUP_C1 1
00050 #define POINTGROUP_CN 2
00051 #define POINTGROUP_CNV 3
00052 #define POINTGROUP_CNH 4
00053 #define POINTGROUP_CINFV 5
00054 #define POINTGROUP_DN 6
00055 #define POINTGROUP_DND 7
00056 #define POINTGROUP_DNH 8
00057 #define POINTGROUP_DINFH 9
00058 #define POINTGROUP_CI 10
00059 #define POINTGROUP_CS 11
00060 #define POINTGROUP_S2N 12
00061 #define POINTGROUP_T 13
00062 #define POINTGROUP_TD 14
00063 #define POINTGROUP_TH 15
00064 #define POINTGROUP_O 16
00065 #define POINTGROUP_OH 17
00066 #define POINTGROUP_I 18
00067 #define POINTGROUP_IH 19
00068 #define POINTGROUP_KH 20
00069
00070 #define OVERLAPCUTOFF 0.4
00071
00072
00073
00074
00075
00076
00077 #define BONDSUMTOL 1.5
00078
00079
00080
00081 #define MINATOMDIST 0.6
00082
00083
00084 #define VERTICALPLANE 1
00085 #define DIHEDRALPLANE 3
00086 #define HORIZONTALPLANE 4
00087
00088
00089 #define INFINITE_ORDER -1
00090 #define PRELIMINARY_C2 -2
00091
00092
00093 #define TETRAHEDRON -4
00094 #define OCTAHEDRON -8
00095 #define DODECAHEDRON -12
00096
00097
00098
00099 #if defined(_MSC_VER)
00100
00101 static double myerfc(double x) {
00102 double p, a1, a2, a3, a4, a5;
00103 double t, erfcx;
00104
00105 p = 0.3275911;
00106 a1 = 0.254829592;
00107 a2 = -0.284496736;
00108 a3 = 1.421413741;
00109 a4 = -1.453152027;
00110 a5 = 1.061405429;
00111
00112 t = 1.0 / (1.0 + p*x);
00113 erfcx = ((a1 + (a2 + (a3 + (a4 + a5*t)*t)*t)*t)*t) * exp(-pow(x,2.0));
00114 return erfcx;
00115 }
00116 #endif
00117
00118
00119
00120 static inline bool coplanar(const float *normal1, const float *normal2, float tol);
00121 static inline bool collinear(const float *axis1, const float *axis2, float tol);
00122 static inline bool orthogonal(const float *axis1, const float *axis2, float tol);
00123 static inline bool behind_plane(const float *normal, const float *coor);
00124 static int isprime(int x);
00125 static int numprimefactors(int x);
00126 static void align_plane_with_axis(const float *normal, const float *axis, float *alignedplane);
00127 static void assign_atoms(AtomSel *sel, MoleculeList *mlist, float *(&mycoor), int *(&atomtype));
00128 static float trans_overlap(int *atomtype, float *(&coor), int numcoor,
00129 const Matrix4 *trans, float sigma,
00130 bool skipident, int maxnatoms, float &overlappermatch);
00131 static inline float trans_overlap(int *atomtype, float *(&coor), int numcoor,
00132 const Matrix4 *trans, float sigma,
00133 bool skipident, int maxnatoms);
00134
00135
00136
00137 typedef struct {
00138 int pointgroup;
00139 int pointgrouporder;
00140 float rmsd;
00141 float *idealcoor;
00142 Plane *planes;
00143 Axis *axes;
00144 Axis *rotreflections;
00145 bool linear;
00146 bool planar;
00147 bool inversion;
00148 bool sphericaltop;
00149 bool symmetrictop;
00150 } Best;
00151
00152
00153
00154 typedef struct {
00155 char name[6];
00156 ElementSummary summary;
00157 } PointGroupDefinition;
00158
00159 PointGroupDefinition pgdefinitions[] = {
00160
00161 { "C1", {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00162 { "Cs", {0, 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00163 { "Ci", {1, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00164 { "C2", {0, 0, 0, {0, 0, 1, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00165 { "C3", {0, 0, 0, {0, 0, 0, 1, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00166 { "C4", {0, 0, 0, {0, 0, 1, 0, 1, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00167 { "C5", {0, 0, 0, {0, 0, 0, 0, 0, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00168 { "C6", {0, 0, 0, {0, 0, 1, 1, 0, 0, 1, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00169 { "C7", {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00170 { "C8", {0, 0, 0, {0, 0, 1, 0, 1, 0, 0, 0, 1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00171 { "D2", {0, 0, 0, {0, 0, 3, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00172 { "D3", {0, 0, 0, {0, 0, 3, 1, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00173 { "D4", {0, 0, 0, {0, 0, 5, 0, 1, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00174 { "D5", {0, 0, 0, {0, 0, 5, 0, 0, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00175 { "D6", {0, 0, 0, {0, 0, 7, 1, 0, 0, 1, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00176 { "D7", {0, 0, 0, {0, 0, 7, 0, 0, 0, 0, 1, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00177 { "D8", {0, 0, 0, {0, 0, 9, 0, 1, 0, 0, 0, 1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00178 { "C2v", {0, 2, 0, {0, 0, 1, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00179 { "C3v", {0, 3, 0, {0, 0, 0, 1, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00180 { "C4v", {0, 4, 0, {0, 0, 1, 0, 1, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00181 { "C5v", {0, 5, 0, {0, 0, 0, 0, 0, 1, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00182 { "C6v", {0, 6, 0, {0, 0, 1, 1, 0, 0, 1, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00183 { "C7v", {0, 7, 0, {0, 0, 0, 0, 0, 0, 0, 1, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00184 { "C8v", {0, 8, 0, {0, 0, 1, 0, 1, 0, 0, 0, 1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00185 { "C2h", {1, 1, 0, {0, 0, 1, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00186 { "C3h", {0, 1, 0, {0, 0, 0, 1, 0, 0, 0, 0, 0}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00187 { "C4h", {1, 1, 0, {0, 0, 1, 0, 1, 0, 0, 0, 0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0}} },
00188 { "C5h", {0, 1, 0, {0, 0, 0, 0, 0, 1, 0, 0, 0}, {0,0,1,0,0,0,0,0,0,0,0,0,0,0}} },
00189 { "C6h", {1, 1, 0, {0, 0, 1, 1, 0, 0, 1, 0, 0}, {0,0,0,1,0,0,0,0,0,0,0,0,0,0}} },
00190 { "C7h", {0, 1, 0, {0, 0, 0, 0, 0, 0, 0, 1, 0}, {0,0,0,0,1,0,0,0,0,0,0,0,0,0}} },
00191 { "C8h", {1, 1, 0, {0, 0, 1, 0, 1, 0, 0, 0, 1}, {0,1,0,0,0,1,0,0,0,0,0,0,0,0}} },
00192 { "D2h", {1, 3, 0, {0, 0, 3, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00193 { "D3h", {0, 4, 0, {0, 0, 3, 1, 0, 0, 0, 0, 0}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00194 { "D4h", {1, 5, 0, {0, 0, 5, 0, 1, 0, 0, 0, 0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0}} },
00195 { "D5h", {0, 6, 0, {0, 0, 5, 0, 0, 1, 0, 0, 0}, {0,0,1,0,0,0,0,0,0,0,0,0,0,0}} },
00196 { "D6h", {1, 7, 0, {0, 0, 7, 1, 0, 0, 1, 0, 0}, {1,0,0,1,0,0,0,0,0,0,0,0,0,0}} },
00197 { "D7h", {0, 8, 0, {0, 0, 7, 0, 0, 0, 0, 1, 0}, {0,0,0,0,1,0,0,0,0,0,0,0,0,0}} },
00198 { "D8h", {1, 9, 0, {0, 0, 9, 0, 1, 0, 0, 0, 1}, {0,1,0,0,0,1,0,0,0,0,0,0,0,0}} },
00199 { "D2d", {0, 2, 0, {0, 0, 3, 0, 0, 0, 0, 0, 0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0}} },
00200 { "D3d", {1, 3, 0, {0, 0, 3, 1, 0, 0, 0, 0, 0}, {0,0,0,1,0,0,0,0,0,0,0,0,0,0}} },
00201 { "D4d", {0, 4, 0, {0, 0, 5, 0, 1, 0, 0, 0, 0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,0}} },
00202 { "D5d", {1, 5, 0, {0, 0, 5, 0, 0, 1, 0, 0, 0}, {0,0,0,0,0,0,0,1,0,0,0,0,0,0}} },
00203 { "D6d", {0, 6, 0, {0, 0, 7, 1, 0, 0, 1, 0, 0}, {0,1,0,0,0,0,0,0,0,1,0,0,0,0}} },
00204 { "D7d", {1, 7, 0, {0, 0, 7, 0, 0, 0, 0, 1, 0}, {0,0,0,0,0,0,0,0,0,0,0,1,0,0}} },
00205 { "D8d", {0, 8, 0, {0, 0, 9, 0, 1, 0, 0, 0, 1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,1}} },
00206 { "S4", {0, 0, 0, {0, 0, 1, 0, 0, 0, 0, 0, 0}, {0,1,0,0,0,0,0,0,0,0,0,0,0,0}} },
00207 { "S6", {1, 0, 0, {0, 0, 0, 1, 0, 0, 0, 0, 0}, {0,0,0,1,0,0,0,0,0,0,0,0,0,0}} },
00208 { "S8", {0, 0, 0, {0, 0, 1, 0, 1, 0, 0, 0, 0}, {0,0,0,0,0,1,0,0,0,0,0,0,0,0}} },
00209 { "T", {0, 0, 0, {0, 0, 3, 4, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00210 { "Th", {1, 3, 0, {0, 0, 3, 4, 0, 0, 0, 0, 0}, {0,0,0,4,0,0,0,0,0,0,0,0,0,0}} },
00211 { "Td", {0, 6, 0, {0, 0, 3, 4, 0, 0, 0, 0, 0}, {0,3,0,0,0,0,0,0,0,0,0,0,0,0}} },
00212 { "O", {0, 0, 0, {0, 0, 9, 4, 3, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00213 { "Oh", {1, 9, 0, {0, 0, 9, 4, 3, 0, 0, 0, 0}, {0,3,0,4,0,0,0,0,0,0,0,0,0,0}} },
00214 { "I", {0, 0, 0, {0, 0,15,10, 0, 6, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00215 { "Ih", {1,15, 0, {0, 0,15,10, 0, 6, 0, 0, 0}, {0,0,0,10,0,0,0,6,0,0,0,0,0,0}} },
00216 { "Cinfv", {0, 1, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00217 { "Dinfh", {1, 2, 1, {0, 0, 1, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00218 { "Kh", {1, 1, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0}} },
00219 };
00220
00221 #define NUMPOINTGROUPS (sizeof(pgdefinitions)/sizeof(PointGroupDefinition))
00222
00223
00224 Symmetry::Symmetry(AtomSel *mysel, MoleculeList *mymlist, int verbosity) :
00225 sel(mysel),
00226 mlist(mymlist),
00227 verbose(verbosity)
00228 {
00229 sigma = 0.1f;
00230 sphericaltop = 0;
00231 symmetrictop = 0;
00232 linear = 0;
00233 planar = 0;
00234 inversion = 0;
00235 maxaxisorder = 0;
00236 maxrotreflorder = 0;
00237 maxweight = 0;
00238 maxoverlap = 0.0;
00239 pointgroup = POINTGROUP_UNKNOWN;
00240 pointgrouporder = 0;
00241 numdihedralplanes = 0;
00242 numverticalplanes = 0;
00243 horizontalplane = -1;
00244 maxnatoms = 70;
00245 rmsd = 0.0;
00246 idealcoor = NULL;
00247 coor = NULL;
00248 checkbonds = 0;
00249 bondsum = NULL;
00250 bondsperatom = NULL;
00251 atomtype = NULL;
00252 atomindex = NULL;
00253 uniqueatoms = NULL;
00254 elementsummarystring = NULL;
00255 missingelementstring = new char[2 + 10*(3+MAXORDERCN+2*MAXORDERCN)];
00256 additionalelementstring = new char[2 + 10*(3+MAXORDERCN+2*MAXORDERCN)];
00257 missingelementstring[0] = '\0';
00258 additionalelementstring[0] = '\0';
00259
00260 memset(&elementsummary, 0, sizeof(ElementSummary));
00261
00262 if (sel->selected) {
00263 coor = new float[3*sel->selected];
00264 idealcoor = new float[3*sel->selected];
00265 bondsum = new float[3*sel->selected];
00266 bondsperatom = new Bondlist[sel->selected];
00267 atomtype = new int[sel->selected];
00268 atomindex = new int[sel->selected];
00269 uniqueatoms = new int[sel->selected];
00270
00271 memset(bondsperatom, 0, sel->selected*sizeof(Bondlist));
00272 }
00273
00274 memset(&(inertiaaxes[0][0]), 0, 9*sizeof(float));
00275 memset(&(inertiaeigenval[0]), 0, 3*sizeof(float));
00276 memset(&(uniqueprimary[0]), 0, 3*sizeof(int));
00277 memset(&(rcom[0]), 0, 3*sizeof(float));
00278
00279
00280
00281 assign_atoms(sel, mlist, coor, atomtype);
00282
00283
00284 assign_bonds();
00285
00286
00287
00288 collintol = cosf(float(DEGTORAD(10.0f)));
00289
00290
00291 orthogtol = cosf(float(DEGTORAD(80.0f)));
00292
00293 if (sel->selected <= 10) {
00294 collintol = cosf(float(DEGTORAD(15.0f)));
00295 orthogtol = cosf(float(DEGTORAD(75.0f)));
00296 }
00297 }
00298
00299
00300
00301 Symmetry::~Symmetry(void) {
00302 if (coor) delete [] coor;
00303 if (idealcoor) delete [] idealcoor;
00304 if (bondsum) delete [] bondsum;
00305 if (atomtype) delete [] atomtype;
00306 if (atomindex) delete [] atomindex;
00307 if (uniqueatoms) delete [] uniqueatoms;
00308 if (elementsummarystring) delete [] elementsummarystring;
00309 delete [] missingelementstring;
00310 delete [] additionalelementstring;
00311 if (bondsperatom) {
00312 int i;
00313 for (i=0; i<sel->selected; i++) {
00314 if (bondsperatom[i].bondto) delete [] bondsperatom[i].bondto;
00315 if (bondsperatom[i].length) delete [] bondsperatom[i].length;
00316 }
00317 delete [] bondsperatom;
00318 }
00319 }
00320
00323 void Symmetry::get_pointgroup(char pg[8], int *order) {
00324 char n[3];
00325 if (order==NULL) sprintf(n, "%i", pointgrouporder);
00326 else {
00327 strcpy(n, "n");
00328 *order = pointgrouporder;
00329 }
00330
00331 switch (pointgroup) {
00332 case POINTGROUP_UNKNOWN:
00333 strcpy(pg, "Unknown");
00334 break;
00335 case POINTGROUP_C1:
00336 strcpy(pg, "C1");
00337 break;
00338 case POINTGROUP_CN:
00339 sprintf(pg, "C%s", n);
00340 break;
00341 case POINTGROUP_CNV:
00342 sprintf(pg, "C%sv", n);
00343 break;
00344 case POINTGROUP_CNH:
00345 sprintf(pg, "C%sh", n);
00346 break;
00347 case POINTGROUP_CINFV:
00348 strcpy(pg, "Cinfv");
00349 break;
00350 case POINTGROUP_DN:
00351 sprintf(pg, "D%s", n);
00352 break;
00353 case POINTGROUP_DND:
00354 sprintf(pg, "D%sd", n);
00355 break;
00356 case POINTGROUP_DNH:
00357 sprintf(pg, "D%sh", n);
00358 break;
00359 case POINTGROUP_DINFH:
00360 strcpy(pg, "Dinfh");
00361 break;
00362 case POINTGROUP_CI:
00363 strcpy(pg, "Ci");
00364 break;
00365 case POINTGROUP_CS:
00366 strcpy(pg, "Cs");
00367 break;
00368 case POINTGROUP_S2N:
00369 if (!strcmp(n, "n"))
00370 strcpy(pg, "S2n");
00371 else
00372 sprintf(pg, "S%i", 2*pointgrouporder);
00373 break;
00374 case POINTGROUP_T:
00375 strcpy(pg, "T");
00376 break;
00377 case POINTGROUP_TD:
00378 strcpy(pg, "Td");
00379 break;
00380 case POINTGROUP_TH:
00381 strcpy(pg, "Th");
00382 break;
00383 case POINTGROUP_O:
00384 strcpy(pg, "O");
00385 break;
00386 case POINTGROUP_OH:
00387 strcpy(pg, "Oh");
00388 break;
00389 case POINTGROUP_I:
00390 strcpy(pg, "I");
00391 break;
00392 case POINTGROUP_IH:
00393 strcpy(pg, "Ih");
00394 break;
00395 case POINTGROUP_KH:
00396 strcpy(pg, "Kh");
00397 break;
00398 }
00399 }
00400
00401
00403 int Symmetry::get_axisorder(int n) {
00404 if (n<numaxes()) return axes[n].order;
00405 return 0;
00406 }
00407
00409 int Symmetry::get_rotreflectorder(int n) {
00410 if (n<numrotreflect()) return rotreflections[n].order;
00411 return 0;
00412 }
00413
00415 int Symmetry::numS2n() {
00416 int i=0, count=0;
00417 for (i=0; i<numrotreflect(); i++) {
00418 if (rotreflections[i].order % 2 == 0) count++;
00419 }
00420 return count;
00421 }
00422
00424 int Symmetry::numprimaryaxes() {
00425 int i;
00426 int numprimary = 0;
00427 for (i=0; i<numaxes(); i++) {
00428 if (axes[i].order==maxaxisorder) numprimary++;
00429 }
00430 return numprimary;
00431 }
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441 void Symmetry::impose(int have_inversion,
00442 int nplanes, const float *planev,
00443 int naxes, const float *axisv, const int *axisorder,
00444 int nrotrefl, const float* rotreflv,
00445 const int *rotreflorder) {
00446 int i;
00447 char buf[256];
00448 if (have_inversion) {
00449 inversion = 1;
00450 if (verbose>1) {
00451 sprintf(buf, "imposing inversion\n");
00452 msgInfo << buf << sendmsg;
00453 }
00454 }
00455 planes.clear();
00456 for (i=0; i<nplanes; i++) {
00457 Plane p;
00458 vec_copy(p.v, &planev[3*i]);
00459 vec_normalize(p.v);
00460 if (norm(p.v)==0.f) continue;
00461 p.overlap = 1.f;
00462 p.weight = 1;
00463 p.type = 0;
00464 planes.append(p);
00465 if (verbose>1) {
00466 sprintf(buf, "imposing plane {% .2f % .2f % .2f}\n", p.v[0], p.v[1], p.v[2]);
00467 msgInfo << buf << sendmsg;
00468 }
00469 }
00470 axes.clear();
00471 for (i=0; i<naxes; i++) {
00472 if (axisorder[i]<=1) continue;
00473 Axis a;
00474 vec_copy(a.v, &axisv[3*i]);
00475 vec_normalize(a.v);
00476 if (norm(a.v)==0.f) continue;
00477 a.order = axisorder[i];
00478 a.overlap = 1.f;
00479 a.weight = 1;
00480 a.type = 0;
00481 axes.append(a);
00482 if (verbose>1) {
00483 sprintf(buf, "imposing axis {% .2f % .2f % .2f}\n", a.v[0], a.v[1], a.v[2]);
00484 msgInfo << buf << sendmsg;
00485 }
00486 }
00487 rotreflections.clear();
00488 for (i=0; i<nrotrefl; i++) {
00489 if (rotreflorder[i]<=1) continue;
00490 Axis a;
00491 vec_copy(a.v, &rotreflv[3*i]);
00492 vec_normalize(a.v);
00493 if (norm(a.v)==0.f) continue;
00494 a.order = rotreflorder[i];
00495 a.overlap = 1.f;
00496 a.weight = 1;
00497 a.type = 0;
00498 rotreflections.append(a);
00499 if (verbose>1) {
00500 sprintf(buf, "imposing rraxis {% .2f % .2f % .2f}\n", a.v[0], a.v[1], a.v[2]);
00501 msgInfo << buf << sendmsg;
00502 }
00503 }
00504
00505
00506 float itensor[4][4];
00507 int ret = measure_inertia(sel, mlist, coor, rcom, inertiaaxes,
00508 itensor, inertiaeigenval);
00509 if (ret < 0) msgErr << "measure inertia failed with code " << ret << sendmsg;
00510
00511
00512
00513
00514 assign_bondvectors();
00515
00516
00517 for (i=0; i<2; i++) {
00518
00519
00520
00521 idealize_coordinates();
00522
00523
00524 memcpy(coor, idealcoor, 3*sel->selected*sizeof(float));
00525 }
00526 }
00527
00528
00531 int Symmetry::guess(float mysigma) {
00532 if (!sel) return MEASURE_ERR_NOSEL;
00533 if (sel->selected == 0) return MEASURE_ERR_NOATOMS;
00534
00535 if (sel->selected == 1) {
00536 pointgroup = POINTGROUP_KH;
00537 elementsummarystring = new char[1];
00538 elementsummarystring[0] = '\0';
00539 uniqueatoms[0] = 1;
00540 memcpy(idealcoor, coor, 3*sel->selected*sizeof(float));
00541
00542 return MEASURE_NOERR;
00543 }
00544
00545
00546 float maxsigma = mysigma;
00547
00548 wkf_timerhandle timer = wkf_timer_create();
00549 wkf_timer_start(timer);
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574 int beststep = -1;
00575 Best best;
00576 best.pointgroup = POINTGROUP_UNKNOWN;
00577 best.pointgrouporder = 0;
00578 best.idealcoor = NULL;
00579 int step, nstep;
00580 float stepsize = 0.05f;
00581 nstep = int(0.5f+(float)maxsigma/stepsize);
00582
00583 for (step=0; step<nstep; step++) {
00584 sigma = (1+step)*stepsize;
00585 if (verbose>1) {
00586 msgInfo << sendmsg;
00587 msgInfo << " STEP " << step << " sigma=" << sigma << sendmsg
00588 << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << sendmsg << sendmsg;
00589 }
00590
00591
00592
00593 if (!iterate_element_search()) continue;
00594
00595 wkf_timer_stop(timer);
00596 if (verbose>0) {
00597 char buf[256];
00598 sprintf(buf, "Guess %i completed in %f seconds, RMSD=%.3f", step, wkf_timer_time(timer), rmsd);
00599 msgInfo << buf << sendmsg;
00600 }
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610 if (!ideal_coordinate_sanity()) {
00611 if (verbose>1) {
00612 msgInfo << "INSANE IDEAL COORDINATES" << sendmsg;
00613 }
00614 break;
00615 }
00616
00617 if (verbose>1) {
00618 print_statistics();
00619 }
00620
00621
00622 determine_pointgroup();
00623
00624 char pgname[8];
00625 get_pointgroup(pgname, NULL);
00626 compare_element_summary(pgname);
00627
00628 if (verbose>1) {
00629 msgInfo << "Point group: " << pgname << sendmsg << sendmsg;
00630 print_element_summary(pgname);
00631
00632
00633 }
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643 if (pointgroup!=best.pointgroup &&
00644 !strlen(additionalelementstring) &&
00645 !strlen(missingelementstring) &&
00646 pointgroup_rank(pointgroup, pointgrouporder) >
00647 pointgroup_rank(best.pointgroup, best.pointgrouporder)) {
00648 beststep = step;
00649 best.pointgroup = pointgroup;
00650 best.pointgrouporder = pointgrouporder;
00651 if (!best.idealcoor) best.idealcoor = new float[3*sel->selected];
00652 memcpy(best.idealcoor, idealcoor, 3*sel->selected*sizeof(float));
00653 best.linear = linear;
00654 best.planar = planar;
00655 best.inversion = inversion;
00656 best.sphericaltop = sphericaltop;
00657 }
00658 }
00659
00660
00661
00662 sigma = (1+beststep)*stepsize;
00663 if (best.idealcoor) {
00664 memcpy(coor, best.idealcoor, 3*sel->selected*sizeof(float));
00665 delete [] best.idealcoor;
00666 }
00667 iterate_element_search();
00668
00669
00670 determine_pointgroup();
00671
00672 if (verbose>0) {
00673 msgInfo << sendmsg
00674 << "RESULT:" << sendmsg
00675 << "=======" << sendmsg << sendmsg;
00676
00677 msgInfo << "Needed tol = " << sigma << " to detect symmetry."
00678 << sendmsg;
00679
00680 print_statistics();
00681 char pgname[8];
00682 get_pointgroup(pgname, NULL);
00683 compare_element_summary(pgname);
00684 msgInfo << "Point group: " << pgname << sendmsg << sendmsg;
00685 print_element_summary(pgname);
00686 }
00687
00688
00689
00690 sort_planes();
00691
00692
00693 unique_coordinates();
00694
00695
00696
00697 orient_molecule();
00698
00699
00700 wkf_timer_destroy(timer);
00701
00702 return MEASURE_NOERR;
00703 }
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729 int Symmetry::iterate_element_search() {
00730 int iteration;
00731 char oldelementsummarystring[2 + 10*(3+MAXORDERCN+2*MAXORDERCN)];
00732 oldelementsummarystring[0] = '\0';
00733 float oldrmsd = 999999.9f;
00734 int converged = 0;
00735
00736 for (iteration=0; iteration<20; iteration++) {
00737
00738 pointgroup = POINTGROUP_UNKNOWN;
00739 inversion = 0;
00740 linear = 0;
00741 planar = 0;
00742 sphericaltop = 0;
00743 symmetrictop = 0;
00744 axes.clear();
00745 planes.clear();
00746 rotreflections.clear();
00747 bonds.clear();
00748 numdihedralplanes = 0;
00749 numverticalplanes = 0;
00750 horizontalplane = -1;
00751
00752 if (verbose>2) {
00753 msgInfo << sendmsg
00754 << "###################" << sendmsg;
00755 msgInfo << "Iteration " << iteration << sendmsg;
00756 msgInfo << "###################" << sendmsg << sendmsg;
00757 }
00758
00759
00760 assign_bondvectors();
00761
00762
00763 float itensor[4][4];
00764 int ret = measure_inertia(sel, mlist, coor, rcom, inertiaaxes,
00765 itensor, inertiaeigenval);
00766 if (ret < 0) msgErr << "measure inertia failed with code " << ret << sendmsg;
00767
00768
00769 if (verbose>2) {
00770 char buf[256];
00771 sprintf(buf, "eigenvalues: %.2f %.2f %.2f", inertiaeigenval[0], inertiaeigenval[1], inertiaeigenval[2]);
00772 msgInfo << buf << sendmsg;
00773 }
00774
00775
00776
00777 find_symmetry_elements();
00778
00779 if (verbose>2) {
00780 if (linear)
00781 msgInfo << "Linear molecule" << sendmsg;
00782 if (planar)
00783 msgInfo << "Planar molecule" << sendmsg;
00784 if (sphericaltop)
00785 msgInfo << "Molecule is a spherical top." << sendmsg;
00786 if (symmetrictop)
00787 msgInfo << "Molecule is a symmetric top." << sendmsg;
00788
00789 int i, numuniqueprimary = 0;
00790 for (i=0; i<3; i++) {
00791 if (uniqueprimary[i]) numuniqueprimary++;
00792 }
00793 msgInfo << "Number of unique primary axes = " << numuniqueprimary << sendmsg;
00794
00795 }
00796
00797
00798
00799
00800
00801
00802
00803
00804 idealize_elements();
00805
00806
00807
00808
00809 idealize_coordinates();
00810
00811
00812
00813
00814
00815
00816 rmsd = ideal_coordinate_rmsd();
00817
00818
00819 build_element_summary();
00820
00821
00822
00823 build_element_summary_string(elementsummary, elementsummarystring);
00824
00825
00826
00827
00828 converged = 0;
00829 if (fabs(rmsd-oldrmsd)<0.01 &&
00830 !strcmp(elementsummarystring, oldelementsummarystring)) {
00831 if (verbose>1) {
00832 msgInfo << "Symmetry search converged after " << iteration+1
00833 << " iterations" << sendmsg;
00834 }
00835 converged = 1;
00836 }
00837
00838
00839 if (verbose>3 && (converged || verbose>2)) {
00840 print_statistics();
00841 }
00842 if (converged) break;
00843
00844
00845 memcpy(coor, idealcoor, 3*sel->selected*sizeof(float));
00846
00847 oldrmsd = rmsd;
00848 strcpy(oldelementsummarystring, elementsummarystring);
00849 }
00850
00851 if (!converged) return 0;
00852 return 1;
00853 }
00854
00855
00856
00857 void Symmetry::find_symmetry_elements() {
00858
00859
00860
00861
00862 find_elements_from_inertia();
00863
00864
00865 find_planes();
00866
00867
00868 purge_planes(0.5);
00869
00870
00871 classify_planes();
00872
00873
00874 check_add_inversion();
00875
00876
00877
00878
00879
00880 find_axes_from_plane_intersections();
00881
00882
00883 assign_axis_orders();
00884
00885
00886
00887
00888 find_C2axes();
00889
00890 assign_prelimaxis_orders(2);
00891
00892 if (!axes.num() && !planes.num()) {
00893 int j;
00894 for (j=3; j<=8; j++) {
00895 if (isprime(j) && !axes.num()) {
00896 find_axes(j);
00897 assign_prelimaxis_orders(j);
00898 }
00899 }
00900 }
00901
00902
00903 sort_axes();
00904
00905
00906
00907
00908
00909
00910
00911 int i;
00912 for (i=0; i<numaxes(); i++) {
00913 check_add_rotary_reflection(axes[i].v, 2*axes[i].order);
00914 }
00915
00916
00917 maxrotreflorder = 0;
00918 for (i=0; i<numrotreflect(); i++) {
00919 vec_normalize(rotreflections[i].v);
00920
00921 if (rotreflections[i].order>maxrotreflorder)
00922 maxrotreflorder = rotreflections[i].order;
00923 }
00924
00925
00926
00927 purge_axes(0.7f);
00928 purge_rotreflections(0.75f);
00929
00930
00931
00932 classify_planes();
00933
00934
00935 classify_axes();
00936 }
00937
00938
00939
00940
00941
00942
00943
00944
00945 void Symmetry::find_elements_from_inertia() {
00946 int i;
00947 int numuniqueprimary = 0;
00948 memset(uniqueprimary, 0, 3*sizeof(int));
00949
00950
00951 float eigenval[3];
00952 float e0 = inertiaeigenval[0];
00953 for (i=0; i<3; i++) {
00954 eigenval[i] = inertiaeigenval[i]/e0;
00955 }
00956
00957
00958 if (fabs(eigenval[0]-eigenval[1])<0.05 && eigenval[2]<0.05) {
00959 linear = 1;
00960 }
00961
00962
00963 float overlap[3];
00964 int order[3], primaryCn[3];
00965 for (i=0; i<3; i++) {
00966 order[i] = axis_order(inertiaaxes[i], overlap+i);
00967
00968 if ((order[i]>1 && overlap[i]>1.5*OVERLAPCUTOFF) ||
00969 (linear && eigenval[i]<0.05)) {
00970 primaryCn[i] = 1;
00971 } else {
00972 primaryCn[i] = 0;
00973 }
00974 }
00975
00976 float tol = 0.25f*sigma + 1.0f/(powf(float(sel->selected+1), 1.5f));
00977
00978
00979
00980 if (fabs(eigenval[0]-eigenval[1])<tol &&
00981 fabs(eigenval[1]-eigenval[2])<tol) {
00982
00983 sphericaltop = 1;
00984
00985 } else {
00986
00987
00988
00989 if (fabs(eigenval[0]-eigenval[1])<tol ||
00990 fabs(eigenval[1]-eigenval[2])<tol ||
00991 fabs(eigenval[0]-eigenval[2])<tol) {
00992
00993
00994
00995
00996
00997 if (fabs(eigenval[1]-eigenval[2])<tol && primaryCn[0]) {
00998 uniqueprimary[0] = 1;
00999 numuniqueprimary++;
01000 }
01001 if (fabs(eigenval[0]-eigenval[2])<tol && primaryCn[1]) {
01002 uniqueprimary[1] = 1;
01003 numuniqueprimary++;
01004 }
01005 if (fabs(eigenval[0]-eigenval[1])<tol && primaryCn[2]) {
01006 uniqueprimary[2] = 1;
01007 numuniqueprimary++;
01008 }
01009
01010 if (numuniqueprimary==1) {
01011 symmetrictop = 1;
01012 }
01013 else {
01014 uniqueprimary[0] = 1;
01015 uniqueprimary[1] = 1;
01016 uniqueprimary[2] = 1;
01017 numuniqueprimary = 3;
01018 }
01019 }
01020 else {
01021 uniqueprimary[0] = 1;
01022 uniqueprimary[1] = 1;
01023 uniqueprimary[2] = 1;
01024 numuniqueprimary = 3;
01025 }
01026 }
01027
01028
01029 for (i=0; i<3; i++) {
01030
01031
01032
01033 int planarmol = is_planar(inertiaaxes[i]);
01034 if (planarmol && !linear) {
01035 if (verbose>3) {
01036 msgInfo << "Planar mol: primary axes " << i << " defines plane" << sendmsg;
01037 }
01038 check_add_plane(inertiaaxes[i]);
01039 planar = 1;
01040 }
01041
01042
01043 if (!uniqueprimary[i]) continue;
01044
01045 if (linear) {
01046
01047
01048
01049
01050 Axis a;
01051 vec_copy(a.v, inertiaaxes[i]);
01052 a.order = INFINITE_ORDER;
01053 float overlap1 = score_axis(a.v, 2);
01054 float overlap2 = score_axis(a.v, 4);
01055 a.overlap = 0.5f*(overlap1+overlap2);
01056 a.weight = sel->num_atoms/2;
01057 a.type = 0;
01058 axes.append(a);
01059
01060
01061 Plane p;
01062 vec_copy(p.v, inertiaaxes[0]);
01063 p.overlap = eigenval[0]*eigenval[1] - eigenval[2];
01064 p.weight = sel->num_atoms/2;
01065 p.type = 0;
01066 planes.append(p);
01067
01068 } else {
01069
01070
01071 if (order[i] > 1 && overlap[i]>OVERLAPCUTOFF) {
01072
01073 Axis a;
01074 vec_copy(a.v, inertiaaxes[i]);
01075 a.order = order[i];
01076 a.overlap = overlap[i];
01077 a.weight = 1;
01078 a.type = 0;
01079 axes.append(a);
01080 }
01081
01082
01083
01084
01085
01086
01087 int weight = 1;
01088 if (planarmol) weight = sel->num_atoms/2;
01089
01090 check_add_plane(inertiaaxes[i], weight);
01091 }
01092 }
01093 }
01094
01095
01096
01097
01098 void Symmetry::find_planes() {
01099 int i,j;
01100 float posA[3], posB[3];
01101
01102
01103 for (i=0; i<sel->selected; i++) {
01104
01105
01106
01107 if (sel->selected>maxnatoms && vmd_random()>maxnatoms/sel->selected)
01108 continue;
01109
01110 vec_sub(posA, coor+3*i, rcom);
01111
01112 float rA = sqrtf(posA[0]*posA[0] + posA[1]*posA[1] + posA[2]*posA[2]);
01113
01114
01115
01116
01117 if (rA < sigma || rA < sel->num_atoms/7.0*sigma) continue;
01118
01119 for (j=0; j<i; j++) {
01120 vec_sub(posB, coor+3*j, rcom);
01121
01122 float rB = sqrtf(posB[0]*posB[0] + posB[1]*posB[1] + posB[2]*posB[2]);
01123
01124 if (fabs(rA-rB) > sigma) continue;
01125
01126
01127 if (atomtype[i]==atomtype[j]) {
01128
01129
01130
01131 float dist = distance(posA, posB);
01132 if (dist<0.25) continue;
01133
01134
01135
01136
01137
01138
01139 float normal[3];
01140 vec_sub(normal, posA, posB);
01141 vec_normalize(normal);
01142
01143
01144 check_add_plane(normal);
01145 }
01146 }
01147 }
01148
01149
01150 for (i=0; i<numplanes(); i++) {
01151 vec_normalize(planes[i].v);
01152
01153 }
01154 }
01155
01156
01157
01158
01159 void Symmetry::find_C2axes() {
01160 int i,j;
01161 float posA[3], posB[3];
01162 float rA, rB;
01163
01164 for (i=0; i<sel->selected; i++) {
01165
01166
01167 if (sel->selected>maxnatoms && vmd_random()>maxnatoms/sel->selected)
01168 continue;
01169
01170 vec_sub(posA, coor+3*i, rcom);
01171
01172 rA = sqrtf(posA[0]*posA[0] + posA[1]*posA[1] + posA[2]*posA[2]);
01173
01174
01175
01176
01177 if (rA < sigma || rA < sel->num_atoms/7.0*sigma) continue;
01178
01179 for (j=i+1; j<sel->selected; j++) {
01180
01181
01182 if (sel->selected>maxnatoms && vmd_random()>maxnatoms/sel->selected) continue;
01183
01184 vec_sub(posB, coor+3*j, rcom);
01185
01186 rB = sqrtf(posB[0]*posB[0] + posB[1]*posB[1] + posB[2]*posB[2]);
01187
01188 if (fabs(rA-rB) > sigma) continue;
01189
01190
01191 if (atomtype[i]==atomtype[j]) {
01192
01193 float alpha = angle(posA, posB);
01194
01195
01196
01197
01198
01199 if (fabs(fabs(alpha)-180) > 10) {
01200 float testaxis[3];
01201 vec_add(testaxis, posA, posB);
01202 vec_normalize(testaxis);
01203
01204
01205 if (!planes.num() || numverticalplanes) {
01206
01207 check_add_axis(testaxis, 2);
01208 }
01209 }
01210 }
01211 }
01212 }
01213
01214
01215 for (i=0; i<numaxes(); i++) {
01216 vec_normalize(axes[i].v);
01217 }
01218 }
01219
01220
01221 static int fac(int n) {
01222 if (n==0) return 1;
01223 int i, x=1;
01224 for (i=1; i<=n; i++) x*=i;
01225 return x;
01226 }
01227
01228
01229
01230
01231 class Combinations {
01232 public:
01233 int *combolist;
01234 int *combo;
01235 int num;
01236 int n;
01237 int k;
01238
01239 Combinations(int N, int K);
01240 ~Combinations();
01241
01242
01243 void create() { recurse(0, -1); }
01244
01245 void recurse(int begin, int level);
01246 void print();
01247
01248
01249 int* get(int i) { return &combolist[i*k]; }
01250 };
01251
01252 Combinations::Combinations(int N, int K) : n(N), k(K) {
01253 if (n>10) n = 10;
01254 combo = new int[k];
01255 combolist = new int[k*fac(n)/(fac(k)*fac(n-k))];
01256 num = 0;
01257 }
01258
01259 Combinations::~Combinations() {
01260 delete [] combo;
01261 delete [] combolist;
01262 }
01263
01264
01265 void Combinations::recurse(int begin, int level) {
01266 int i;
01267 level++;
01268 if (level>=k) {
01269 for (i=0; i<k; i++) {
01270 combolist[num*k+i] = combo[i];
01271 }
01272 num++;
01273 return;
01274 }
01275
01276 for (i=begin; i<n; i++) {
01277 combo[level] = i;
01278 recurse(i+1, level);
01279 }
01280 }
01281
01282
01283 void Combinations::print() {
01284 int i, j;
01285 for (i=0; i<fac(n)/(fac(k)*fac(n-k)); i++) {
01286 printf("combo %d/%d {", i, num);
01287 for (j=0; j<k; j++) {
01288 printf(" %d", combolist[i*k+j]);
01289 }
01290 printf("}\n");
01291 }
01292
01293 }
01294
01295
01296
01297
01298
01299 void Symmetry::find_axes(int order) {
01300 int i,j;
01301 float posA[3], posB[3];
01302 float rA, rB;
01303 int *atomtuple = new int[sel->selected];
01304
01305
01306 for (i=0; i<sel->selected; i++) {
01307
01308
01309 if (sel->selected>maxnatoms && vmd_random()>maxnatoms/sel->selected)
01310 continue;
01311
01312 vec_sub(posA, coor+3*i, rcom);
01313
01314 rA = sqrtf(posA[0]*posA[0] + posA[1]*posA[1] + posA[2]*posA[2]);
01315
01316
01317
01318
01319 if (rA < sigma || rA < sel->num_atoms/7.0*sigma) continue;
01320
01321 atomtuple[0] = i;
01322 int ntup = 1;
01323
01324 for (j=i+1; j<sel->selected; j++) {
01325
01326
01327
01328 if (sel->selected>maxnatoms &&
01329 vmd_random()>maxnatoms/sel->selected) {
01330 continue;
01331 }
01332
01333
01334 if (atomtype[j]!=atomtype[i]) continue;
01335
01336 vec_sub(posB, coor+3*j, rcom);
01337
01338 rB = sqrtf(posB[0]*posB[0] + posB[1]*posB[1] + posB[2]*posB[2]);
01339
01340
01341 if (fabs(rA-rB) > sigma) continue;
01342
01343 atomtuple[ntup++] = j;
01344
01345
01346
01347 if (ntup>=10) break;
01348 }
01349 if (ntup<order) continue;
01350
01351
01352
01353
01354
01355
01356
01357 Combinations combo(ntup, order);
01358 combo.create();
01359
01360
01361 int m;
01362 for (j=0; j<combo.num; j++) {
01363 float testaxis[3], pos[3], pos2[3], cross[3], normal[3];
01364 vec_zero(testaxis);
01365 vec_zero(normal);
01366
01367
01368
01369
01370 int inplane = 1, anyinplane = 0;
01371 for (m=0; m<order; m++) {
01372 int atom = atomtuple[combo.get(j)[m]];
01373
01374 vec_sub(pos, coor+3*atom, rcom);
01375 vec_add(testaxis, testaxis, pos);
01376
01377
01378
01379
01380
01381 int n, found = 0;
01382 for (n=0; n<order; n++) {
01383 if (n==m) continue;
01384 int atom2 = atomtuple[combo.get(j)[m]];
01385 vec_sub(pos2, coor+3*atom2, rcom);
01386 if (angle(pos, pos2)>45.f) {
01387 found = 1;
01388 break;
01389 }
01390 }
01391 if (!found) continue;
01392
01393 cross_prod(cross, pos2, pos);
01394
01395
01396
01397
01398 if (collinear(normal, cross, collintol)) {
01399 vec_add(normal, normal, cross);
01400 anyinplane = 1;
01401 } else {
01402
01403 inplane = 0;
01404 break;
01405 }
01406 }
01407
01408
01409
01410
01411
01412
01413 if (!inplane || !anyinplane) continue;
01414
01415 vec_normalize(normal);
01416
01417
01418 printf("Checking C%d axis\n", order);
01419 check_add_axis(normal, order);
01420 }
01421
01422 }
01423
01424 delete [] atomtuple;
01425
01426
01427 for (i=0; i<numaxes(); i++) {
01428 vec_normalize(axes[i].v);
01429 }
01430 }
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440 void Symmetry::find_axes_from_plane_intersections() {
01441
01442 if (numplanes()<2) return;
01443
01444 int i,j;
01445
01446 for (i=0; i<numplanes(); i++) {
01447 for (j=i+1; j<numplanes(); j++) {
01448 float newaxis[3];
01449 cross_prod(newaxis, plane(i), plane(j));
01450
01451 vec_normalize(newaxis);
01452
01453
01454 if (norm(newaxis)<0.05) continue;
01455
01456
01457
01458
01459 int k;
01460 bool found = 0;
01461 for (k=0; k<numaxes(); k++) {
01462 float avgaxis[3];
01463 vec_copy(avgaxis, axis(k));
01464 vec_normalize(avgaxis);
01465
01466 float dot = dot_prod(avgaxis, newaxis);
01467
01468 if (fabs(dot) > collintol) {
01469
01470
01471 if (dot>0) {
01472 vec_incr(axis(k), newaxis);
01473 } else {
01474 vec_sub(axis(k), axis(k), newaxis);
01475 }
01476 axes[k].weight++;
01477 found = 1;
01478 break;
01479 }
01480 }
01481
01482 if (!found) {
01483
01484 Axis a;
01485 vec_copy(a.v, newaxis);
01486 a.type = 0;
01487 a.order = 0;
01488 a.overlap = 0.0;
01489 if (planes[i].weight<planes[j].weight)
01490 a.weight = planes[i].weight;
01491 else
01492 a.weight = planes[j].weight;
01493 axes.append(a);
01494 }
01495 }
01496 }
01497
01498
01499 for (i=0; i<numaxes(); i++) {
01500 vec_normalize(axis(i));
01501 }
01502 }
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513 void Symmetry::classify_planes() {
01514 if (!numplanes()) return;
01515 if (!numaxes()) return;
01516
01517 numdihedralplanes = 0;
01518 numverticalplanes = 0;
01519 horizontalplane = -1;
01520
01521 int i;
01522
01523
01524 for (i=0; i<numplanes(); i++) {
01525 if (collinear(axis(0), plane(i), collintol)) {
01526 horizontalplane = i;
01527 planes[i].type = HORIZONTALPLANE;
01528 break;
01529 }
01530 }
01531
01532
01533 for (i=0; i<numplanes(); i++) {
01534 if (!orthogonal(axis(0), plane(i), orthogtol)) continue;
01535
01536
01537 numverticalplanes++;
01538 planes[i].type = VERTICALPLANE;
01539
01540
01541 int j, k;
01542 for (j=1; j<numaxes(); j++) {
01543 if (axes[j].order != 2) continue;
01544 if (!orthogonal(axis(j), axis(0), orthogtol)) continue;
01545
01546 for (k=j+1; k<numaxes(); k++) {
01547 if (axes[k].order != 2) continue;
01548 if (!orthogonal(axis(k), axis(0), orthogtol)) continue;
01549
01550
01551 float bisect[3];
01552 vec_add(bisect, axis(j), axis(k));
01553 vec_normalize(bisect);
01554 if (orthogonal(bisect, plane(i), orthogtol)) {
01555 planes[i].type = DIHEDRALPLANE;
01556 numdihedralplanes++;
01557 j=numaxes();
01558 break;
01559 }
01560
01561 vec_sub(bisect, axis(j), axis(k));
01562 vec_normalize(bisect);
01563 if (orthogonal(bisect, plane(i), orthogtol)) {
01564 planes[i].type = DIHEDRALPLANE;
01565 numdihedralplanes++;
01566 j=numaxes();
01567 break;
01568 }
01569
01570 }
01571 }
01572 }
01573 }
01574
01575 void Symmetry::classify_axes() {
01576 if (!numaxes()) return;
01577
01578
01579
01580 int numprimary = 0;
01581 int i;
01582 for (i=0; i<numaxes(); i++) {
01583 if (axes[i].order==maxaxisorder) {
01584 axes[i].type = PRINCIPAL_AXIS;
01585 numprimary++;
01586 }
01587 }
01588 for (i=1; i<numaxes(); i++) {
01589 if (orthogonal(axis(0), axis(i), orthogtol))
01590 axes[i].type |= PERPENDICULAR_AXIS;
01591 }
01592 }
01593
01594
01595 float Symmetry::score_inversion() {
01596
01597 Matrix4 inv;
01598 inv.translate(rcom[0], rcom[1], rcom[2]);
01599 inv.scale(-1.0);
01600 inv.translate(-rcom[0], -rcom[1], -rcom[2]);
01601
01602
01603 return trans_overlap(atomtype, coor, sel->selected, &inv, 1.5f*sigma,
01604 NOSKIP_IDENTICAL, maxnatoms);
01605 }
01606
01607
01608
01609 void Symmetry::check_add_inversion() {
01610
01611 float overlap = score_inversion();
01612
01613
01614
01615
01616
01617
01618
01619 if (overlap>0.5-0.2/(1+(planes.num()*axes.num()))) {
01620 inversion = 1;
01621 }
01622 }
01623
01624
01625 float Symmetry::score_plane(const float *normal) {
01626
01627
01628
01629 Matrix4 mirror;
01630 mirror.translate(rcom[0], rcom[1], rcom[2]);
01631 mirror.scale(-1.0);
01632 mirror.rotate_axis(normal, float(VMD_PI));
01633 mirror.translate(-rcom[0], -rcom[1], -rcom[2]);
01634
01635
01636 return trans_overlap(atomtype, coor, sel->selected, &mirror, 1.5f*sigma, NOSKIP_IDENTICAL, maxnatoms);
01637 }
01638
01639
01640
01641
01642
01643
01644
01645 void Symmetry::check_add_plane(const float *normal, int weight) {
01646
01647
01648 float overlap = score_plane(normal);
01649
01650
01651 if (overlap<OVERLAPCUTOFF) return;
01652
01653
01654
01655
01656
01657
01658
01659
01660 int k;
01661 bool found=0;
01662 for (k=0; k<numplanes(); k++) {
01663 float avgnormal[3];
01664 vec_copy(avgnormal, plane(k));
01665 vec_normalize(avgnormal);
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675 float dot = dot_prod(avgnormal, normal);
01676 if (fabs(dot) > collintol) {
01677
01678
01679
01680 if (dot>0) {
01681 vec_incr(plane(k), normal);
01682 } else {
01683 vec_scaled_add(plane(k), -1, normal);
01684 }
01685 planes[k].overlap = (planes[k].weight*planes[k].overlap + overlap)/(planes[k].weight+1);
01686 (planes[k].weight)++;
01687 found = 1;
01688 break;
01689 }
01690 }
01691 if (!found) {
01692
01693 Plane p;
01694 vec_copy(p.v, normal);
01695 p.overlap = overlap;
01696 p.weight = weight;
01697 p.type = 0;
01698 planes.append(p);
01699 }
01700 }
01701
01702
01703
01704 float Symmetry::score_axis(const float *testaxis, int order) {
01705
01706 Matrix4 rot;
01707 rot.translate(rcom[0], rcom[1], rcom[2]);
01708 rot.rotate_axis(testaxis, float(VMD_TWOPI)/order);
01709 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
01710
01711
01712 return trans_overlap(atomtype, coor, sel->selected, &rot, 2*sigma,
01713 NOSKIP_IDENTICAL, maxnatoms);
01714 }
01715
01716
01717
01718
01719
01720
01721
01722 void Symmetry::check_add_axis(const float *testaxis, int order) {
01723 int k;
01724 bool found = 0;
01725 for (k=0; k<numaxes(); k++) {
01726 float avgaxis[3];
01727 vec_copy(avgaxis, axis(k));
01728 vec_normalize(avgaxis);
01729
01730
01731 float dot = dot_prod(avgaxis, testaxis);
01732 if (fabs(dot) > collintol) {
01733
01734
01735
01736
01737 if (axes[k].order==-order) {
01738 if (dot>0) {
01739 vec_incr(axis(k), testaxis);
01740 } else {
01741 vec_scaled_add(axis(k), -1, testaxis);
01742 }
01743 axes[k].weight++;
01744 }
01745 found = 1;
01746 break;
01747 }
01748 }
01749 if (!found) {
01750
01751 float overlap = score_axis(testaxis, order);
01752 if (overlap>OVERLAPCUTOFF) {
01753 Axis a;
01754 vec_copy(a.v, testaxis);
01755
01756
01757
01758 a.order = -order;
01759 a.overlap = overlap;
01760 a.weight = 1;
01761 a.type = 0;
01762 axes.append(a);
01763 }
01764 }
01765 }
01766
01767
01768 float Symmetry::score_rotary_reflection(const float *testaxis, int order) {
01769
01770
01771
01772 Matrix4 rot;
01773 rot.translate(rcom[0], rcom[1], rcom[2]);
01774 rot.rotate_axis(testaxis, float(VMD_TWOPI)/order);
01775 rot.scale(-1.0);
01776 rot.rotate_axis(testaxis, float(VMD_PI));
01777 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
01778
01779
01780 return trans_overlap(atomtype, coor, sel->selected, &rot, sigma,
01781 NOSKIP_IDENTICAL, maxnatoms);
01782 }
01783
01784
01785
01786 void Symmetry::check_add_rotary_reflection(const float *testaxis, int maxorder) {
01787 if (maxorder<4) return;
01788
01789
01790
01791 int n;
01792 for (n=3; n<=maxorder; n++) {
01793 if (n>=9 && n%2) continue;
01794 if (maxorder%n) continue;
01795
01796
01797 float overlap = score_rotary_reflection(testaxis, n);
01798
01799
01800
01801 if (overlap>OVERLAPCUTOFF) {
01802 int k;
01803 bool found = 0;
01804 for (k=0; k<numrotreflect(); k++) {
01805 float avgaxis[3];
01806 vec_copy(avgaxis, rotreflect(k));
01807 vec_normalize(avgaxis);
01808
01809 if (n!=rotreflections[k].order) continue;
01810
01811 float dot = dot_prod(avgaxis, testaxis);
01812 if (fabs(dot) > collintol) {
01813
01814
01815 if (dot>0) {
01816 vec_incr(rotreflect(k), testaxis);
01817 } else {
01818 vec_scaled_add(rotreflect(k), -1, testaxis);
01819 }
01820 rotreflections[k].weight++;
01821 found = 1;
01822 break;
01823 }
01824 }
01825 if (!found) {
01826
01827 Axis a;
01828 vec_copy(a.v, testaxis);
01829 a.order = n;
01830 a.overlap = overlap;
01831 a.weight = 1;
01832 a.type = 0;
01833 rotreflections.append(a);
01834 }
01835 }
01836 }
01837 }
01838
01839
01840
01841
01842 void Symmetry::purge_planes(float cutoff) {
01843 maxweight = 0;
01844 maxoverlap = 0.0;
01845 if (!numplanes()) return;
01846
01847 maxweight = planes[0].weight;
01848 maxoverlap = planes[0].overlap;
01849 int i;
01850 for (i=1; i<numplanes(); i++) {
01851 if (planes[i].overlap>maxoverlap) maxoverlap = planes[i].overlap;
01852 if (planes[i].weight >maxweight) maxweight = planes[i].weight;
01853 }
01854
01855 float tol = cutoff*maxoverlap;
01856 int *keep = new int[planes.num()];
01857 memset(keep, 0, planes.num()*sizeof(int));
01858
01859 for (i=0; i<numplanes(); i++) {
01860 if (planes[i].overlap>tol) {
01861
01862 keep[i] = 1;
01863 }
01864 }
01865
01866 prune_planes(keep);
01867
01868 delete [] keep;
01869 }
01870
01871
01872
01873
01874 void Symmetry::purge_axes(float cutoff) {
01875 if (!numaxes()) return;
01876
01877 int i;
01878 for (i=0; i<numaxes(); i++) {
01879 if (axes[i].overlap>maxoverlap) maxoverlap = axes[i].overlap;
01880 if (axes[i].weight >maxweight) maxweight = axes[i].weight;
01881 }
01882
01883 float tol = cutoff*maxoverlap;
01884 int *keep = new int[axes.num()];
01885 memset(keep, 0, axes.num()*sizeof(int));
01886
01887 for (i=0; i<numaxes(); i++) {
01888 if (axes[i].overlap>tol) {
01889
01890 keep[i] = 1;
01891 }
01892 }
01893
01894 prune_axes(keep);
01895
01896 delete [] keep;
01897 }
01898
01899
01900
01901
01902 void Symmetry::purge_rotreflections(float cutoff) {
01903 if (!numrotreflect()) return;
01904
01905 int i;
01906 for (i=0; i<numrotreflect(); i++) {
01907 if (rotreflections[i].overlap>maxoverlap) maxoverlap = rotreflections[i].overlap;
01908 if (rotreflections[i].weight >maxweight) maxweight = rotreflections[i].weight;
01909 }
01910
01911 float tol = cutoff*maxoverlap;
01912 int *keep = new int[rotreflections.num()];
01913 memset(keep, 0, rotreflections.num()*sizeof(int));
01914
01915 for (i=0; i<numrotreflect(); i++) {
01916 if (rotreflections[i].overlap>tol) {
01917
01918 keep[i] = 1;
01919 }
01920 else if (verbose>4) {
01921
01922 char buf[512];
01923 sprintf(buf, "purged S%i rotary reflection %i (weight=%i, overlap=%.2f tol=%.2f)",
01924 rotreflections[i].order, i, rotreflections[i].weight, rotreflections[i].overlap, tol);
01925 msgInfo << buf << sendmsg;
01926 }
01927 }
01928
01929 prune_rotreflections(keep);
01930
01931 delete [] keep;
01932 }
01933
01934
01935
01936 void Symmetry::prune_planes(int *keep) {
01937 if (!planes.num()) return;
01938
01939 numverticalplanes = 0;
01940 numdihedralplanes = 0;
01941
01942 int numkept = 0;
01943 Plane *keptplanes = new Plane[numplanes()];
01944 int i;
01945 for (i=0; i<planes.num(); i++) {
01946
01947 if (keep[i]) {
01948
01949 keptplanes[numkept] = planes[i];
01950 if (planes[i].type==HORIZONTALPLANE) horizontalplane=numkept;
01951 else if (planes[i].type==VERTICALPLANE) numverticalplanes++;
01952 else if (planes[i].type==DIHEDRALPLANE) {
01953 numdihedralplanes++;
01954 numverticalplanes++;
01955 }
01956 numkept++;
01957
01958 } else {
01959
01960 if (planes[i].type==HORIZONTALPLANE) horizontalplane=-1;
01961 if (verbose>3) {
01962 char buf[256];
01963 sprintf(buf, "removed %s%s%s plane %i (weight=%i, overlap=%.2f)\n",
01964 (planes[i].type==HORIZONTALPLANE?"horiz":""),
01965 (planes[i].type==VERTICALPLANE?"vert":""),
01966 (planes[i].type==DIHEDRALPLANE?"dihed":""),
01967 i, planes[i].weight, planes[i].overlap);
01968 msgInfo << buf << sendmsg;
01969 }
01970 }
01971 }
01972 memcpy(&(planes[0]), keptplanes, numkept*sizeof(Plane));
01973 planes.truncatelastn(numplanes()-numkept);
01974
01975 delete [] keptplanes;
01976 }
01977
01978
01979
01980 void Symmetry::prune_axes(int *keep) {
01981 if (!axes.num()) return;
01982
01983 int numkept = 0;
01984 Axis *keptaxes = new Axis[numaxes()];
01985
01986 maxaxisorder = 0;
01987 int i;
01988 for (i=0; i<axes.num(); i++) {
01989
01990 if (keep[i]) {
01991
01992 keptaxes[numkept++] = axes[i];
01993
01994 if (axes[i].order>maxaxisorder)
01995 maxaxisorder = axes[i].order;
01996 }
01997 }
01998 memcpy(&(axes[0]), keptaxes, numkept*sizeof(Axis));
01999 axes.truncatelastn(numaxes()-numkept);
02000
02001 delete [] keptaxes;
02002 }
02003
02004
02005
02006 void Symmetry::prune_rotreflections(int *keep) {
02007 if (!rotreflections.num()) return;
02008
02009 int numkept = 0;
02010 Axis *keptrotrefl = new Axis[numrotreflect()];
02011
02012 maxrotreflorder = 0;
02013 int i;
02014 for (i=0; i<numrotreflect(); i++) {
02015 if (keep[i]) {
02016
02017 keptrotrefl[numkept++] = rotreflections[i];
02018
02019 if (rotreflections[i].order>maxrotreflorder)
02020 maxrotreflorder = rotreflections[i].order;
02021 }
02022 }
02023
02024 memcpy(&(rotreflections[0]), keptrotrefl, numkept*sizeof(Axis));
02025 rotreflections.truncatelastn(numrotreflect()-numkept);
02026
02027 delete [] keptrotrefl;
02028 }
02029
02030
02031
02032 void Symmetry::assign_axis_orders() {
02033 if (!numaxes()) return;
02034
02035 maxaxisorder = axes[0].order;
02036
02037
02038 int i;
02039 for (i=0; i<numaxes(); i++) {
02040 if (!axes[i].order) axes[i].order = axis_order(axis(i), &(axes[i].overlap));
02041
02042
02043
02044 if (axes[i].order>maxaxisorder) {
02045 maxaxisorder = axes[i].order;
02046 }
02047 }
02048 }
02049
02050
02051
02052 void Symmetry::assign_prelimaxis_orders(int order) {
02053 int i;
02054 for (i=0; i<numaxes(); i++) {
02055 if (axes[i].order==-order) {
02056 axes[i].order=order;
02057 if (axes[i].weight>1) {
02058
02059
02060
02061 axes[i].overlap = score_axis(axis(i), order);
02062 }
02063 }
02064
02065 if (axes[i].order>maxaxisorder) maxaxisorder = axes[i].order;
02066 }
02067 }
02068
02069
02070
02071
02072 void Symmetry::sort_axes() {
02073 int i;
02074
02075
02076 int numsortedaxes = 0;
02077 for (i=0; i<numaxes(); i++) {
02078 if (axes[i].order>1 || axes[i].order==INFINITE_ORDER) numsortedaxes++;
02079 }
02080
02081 Axis *sortedaxes = new Axis[numsortedaxes*sizeof(Axis)];
02082 int j, k=0, have_inf=0;
02083 for (j=0; j<numaxes(); j++) {
02084 if (axes[j].order == INFINITE_ORDER) {
02085 sortedaxes[k] = axes[j];
02086 k++;
02087 have_inf = 1;
02088 }
02089 }
02090
02091 for (i=maxaxisorder; i>1; i--) {
02092 for (j=0; j<numaxes(); j++) {
02093 if (axes[j].order == i) {
02094 if (have_inf &&
02095 collinear(sortedaxes[0].v, axes[j].v, collintol)) {
02096 continue;
02097 }
02098 sortedaxes[k] = axes[j];
02099 k++;
02100 }
02101 }
02102 }
02103
02104 memcpy(&(axes[0]), sortedaxes, numsortedaxes*sizeof(Axis));
02105 axes.truncatelastn(numaxes()-numsortedaxes);
02106
02107 delete [] sortedaxes;
02108 }
02109
02110
02111
02112
02113 void Symmetry::sort_planes() {
02114 Plane *sortedplanes = new Plane[numplanes()*sizeof(Plane)];
02115
02116 int k = 0;
02117 if (horizontalplane>=0) {
02118 sortedplanes[k] = planes[horizontalplane];
02119 horizontalplane = k++;
02120 }
02121
02122 int j;
02123 for (j=0; j<numplanes(); j++) {
02124 if (planes[j].type == DIHEDRALPLANE) {
02125 sortedplanes[k++] = planes[j];
02126 }
02127 }
02128 for (j=0; j<numplanes(); j++) {
02129 if (planes[j].type == VERTICALPLANE) {
02130 sortedplanes[k++] = planes[j];
02131 }
02132 }
02133 for (j=0; j<numplanes(); j++) {
02134 if (planes[j].type == 0) {
02135 sortedplanes[k++] = planes[j];
02136 }
02137 }
02138
02139 memcpy(&(planes[0]), sortedplanes, numplanes()*sizeof(Plane));
02140
02141 delete [] sortedplanes;
02142 }
02143
02144
02145
02146
02147
02148 int Symmetry::axis_order(const float *axis, float *overlap) {
02149 int i;
02150
02151
02152 float overlaparray[MAXORDERCN+1];
02153 float overlappermarray[MAXORDERCN+1];
02154 overlappermarray[1] = 0.0;
02155 for (i=2; i<=MAXORDERCN; i++) {
02156 Matrix4 rot;
02157
02158 rot.translate(rcom[0], rcom[1], rcom[2]);
02159 rot.rotate_axis(axis, float(DEGTORAD(360.0f/i)));
02160 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
02161 overlaparray[i] = trans_overlap(atomtype, coor, sel->selected, &rot, 1.5f*sigma, SKIP_IDENTICAL, maxnatoms, overlappermarray[i]);
02162 }
02163
02164
02165 float maxover = overlaparray[2];
02166
02167 for (i=3; i<=MAXORDERCN; i++) {
02168
02169 if (overlaparray[i]>maxover) {
02170 maxover = overlaparray[i];
02171 }
02172 }
02173
02174
02175
02176 float maxfalseov = 0.0f;
02177 int maxorder = 1;
02178 short int orders[MAXORDERCN+1];
02179 for (i=2; i<=MAXORDERCN; i++) {
02180 if (overlaparray[i]>0.5f*maxover) {
02181 orders[i] = 1;
02182 maxorder = i;
02183 } else {
02184 orders[i] = 0;
02185 if (overlaparray[i]>maxfalseov) maxfalseov = overlaparray[i];
02186 }
02187 }
02188
02189 #if defined(_MSC_VER)
02190 float tol1 = 0.25f + 0.15f*float(myerfc(0.05f*float(sel->selected-50)));
02191 #elif defined(__irix)
02192 float tol1 = 0.25f + 0.15f*erfc(0.05f*float(sel->selected-50));
02193 #else
02194 float tol1 = 0.25f + 0.15f*erfcf(0.05f*float(sel->selected-50));
02195 #endif
02196
02197
02198
02199
02200 if (maxover<tol1+powf(0.09f*maxorder, 6)) {
02201 *overlap = maxover;
02202 return 1;
02203 }
02204
02205
02206
02207
02208 if (orders[2]) {
02209 if (orders[3] && orders[6]) {
02210 float avgov = (overlaparray[2]+overlaparray[3]+overlaparray[6])/3.0f;
02211 *overlap = 0.5f*(maxover + (avgov-maxfalseov)/avgov);
02212 return 6;
02213 } else if (orders[4]) {
02214 if (MAXORDERCN>=8 && orders[8]) {
02215 float avgov = (overlaparray[2]+overlaparray[4]+overlaparray[8])/3.0f;
02216 *overlap = 0.5f*(maxover + (avgov-maxfalseov)/avgov);
02217 return 8;
02218 }
02219 *overlap = 0.5f*(maxover + (overlaparray[4]-maxfalseov)/overlaparray[4]);
02220 return 4;
02221 }
02222
02223 *overlap = 0.5f*(maxover + (overlaparray[2]-maxfalseov)/overlaparray[2]);
02224 return 2;
02225 } else if (orders[3]) {
02226 *overlap = 0.5f*(maxover + (overlaparray[3]-maxfalseov)/overlaparray[3]);
02227 return 3;
02228 } else if (orders[5]) {
02229 *overlap = 0.5f*(maxover + (overlaparray[5]-maxfalseov)/overlaparray[5]);
02230 return 5;
02231 } else if (orders[7]) {
02232 *overlap = 0.5f*(maxover + (overlaparray[7]-maxfalseov)/overlaparray[7]);
02233 return 7;
02234 }
02235
02236 *overlap = maxover;
02237 return 1;
02238 }
02239
02240
02241
02242
02243
02244 void Symmetry::idealize_coordinates() {
02245 int i;
02246
02247
02248 memcpy(idealcoor, coor, 3*sel->selected*sizeof(float));
02249
02250
02251 int *keep = new int[planes.num()];
02252 memset(keep, 0, planes.num()*sizeof(int));
02253
02254 for (i=0; i<numplanes(); i++) {
02255 Matrix4 mirror;
02256 mirror.translate(rcom[0], rcom[1], rcom[2]);
02257 mirror.scale(-1.0);
02258 mirror.rotate_axis(planes[i].v, float(VMD_PI));
02259 mirror.translate(-rcom[0], -rcom[1], -rcom[2]);
02260
02261
02262 if (average_coordinates(&mirror)) keep[i] = 1;
02263 }
02264
02265 prune_planes(keep);
02266
02267
02268
02269 int *weight = new int[sel->selected];
02270 float *avgcoor = new float[3*sel->selected];
02271
02272 delete [] keep;
02273 keep = new int[axes.num()];
02274 memset(keep, 0, axes.num()*sizeof(int));
02275
02276 for (i=0; i<numaxes(); i++) {
02277 memset(weight, 0, sel->selected*sizeof(int));
02278 memcpy(avgcoor, idealcoor, 3*sel->selected*sizeof(int));
02279 int order = axes[i].order;
02280
02281
02282
02283
02284 if (order==INFINITE_ORDER) order = 4;
02285
02286
02287 int success = 1;
02288 int n, j;
02289 for (n=1; n<order; n++) {
02290 Matrix4 rot;
02291 rot.translate(rcom[0], rcom[1], rcom[2]);
02292 rot.rotate_axis(axis(i), n*float(VMD_TWOPI)/order);
02293 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
02294
02295
02296
02297
02298
02299 int nmatches;
02300 int *matchlist = NULL;
02301 identify_transformed_atoms(&rot, nmatches, matchlist);
02302
02303 for(j=0; j<sel->selected; j++) {
02304 int m = matchlist[j];
02305 if (m<0) continue;
02306
02307 if (checkbonds) {
02308
02309
02310 if (!check_bondsum(j, m, &rot)) {
02311 success = 0;
02312 break;
02313 }
02314 }
02315
02316 float tmpcoor[3];
02317 rot.multpoint3d(idealcoor+3*m, tmpcoor);
02318 vec_incr(avgcoor+3*j, tmpcoor);
02319 weight[j]++;
02320 }
02321 if (matchlist) delete [] matchlist;
02322
02323 if (!success) break;
02324
02325 }
02326
02327 if (success) {
02328
02329
02330 for(j=0; j<sel->selected; j++) {
02331 vec_scale(idealcoor+3*j, 1.0f/(1+weight[j]), avgcoor+3*j);
02332 }
02333
02334 keep[i] = 1;
02335 }
02336 }
02337
02338 prune_axes(keep);
02339
02340 delete [] weight;
02341 delete [] avgcoor;
02342
02343
02344
02345 delete [] keep;
02346 keep = new int[rotreflections.num()];
02347 memset(keep, 0, rotreflections.num()*sizeof(int));
02348
02349 for (i=0; i<numrotreflect(); i++) {
02350 Matrix4 rot;
02351 rot.translate(rcom[0], rcom[1], rcom[2]);
02352 rot.rotate_axis(rotreflect(i), float(VMD_TWOPI)/rotreflections[i].order);
02353 rot.scale(-1.0f);
02354 rot.rotate_axis(rotreflect(i), float(VMD_PI));
02355 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
02356
02357
02358 if (average_coordinates(&rot)) keep[i] = 1;
02359 }
02360
02361 prune_rotreflections(keep);
02362
02363 delete [] keep;
02364
02365
02366 if (inversion) {
02367
02368 Matrix4 inv;
02369 inv.translate(rcom[0], rcom[1], rcom[2]);
02370 inv.scale(-1.0f);
02371 inv.translate(-rcom[0], -rcom[1], -rcom[2]);
02372
02373 if (!average_coordinates(&inv)) inversion = 0;
02374 }
02375 }
02376
02377
02378
02379 int Symmetry::average_coordinates(Matrix4 *trans) {
02380 int j;
02381 int nmatches;
02382 int *matchlist = NULL;
02383 identify_transformed_atoms(trans, nmatches, matchlist);
02384
02385 if (checkbonds) {
02386 int success = 1;
02387 for(j=0; j<sel->selected; j++) {
02388 int m = matchlist[j];
02389
02390 if (m<0) continue;
02391
02392 if (!check_bondsum(j, m, trans)) {
02393 success = 0;
02394 break;
02395 }
02396 }
02397 if (!success) {
02398 if (verbose>3) {
02399 msgInfo << "Transformation messes up bonds!" << sendmsg;
02400 }
02401 if (matchlist) delete [] matchlist;
02402 return 0;
02403 }
02404 }
02405
02406 for(j=0; j<sel->selected; j++) {
02407 int m = matchlist[j];
02408
02409 if (m<0) continue;
02410
02411
02412 float avgcoor[3];
02413 trans->multpoint3d(idealcoor+3*m, avgcoor);
02414 vec_incr(avgcoor, idealcoor+3*j);
02415 vec_scale(idealcoor+3*j, 0.5, avgcoor);
02416 }
02417 if (matchlist) delete [] matchlist;
02418
02419 return 1;
02420 }
02421
02422
02423
02424 int Symmetry::check_bondsum(int j, int m, Matrix4 *trans) {
02425 float tmp[3];
02426 vec_add(tmp, bondsum+3*m, rcom);
02427 trans->multpoint3d(tmp, tmp);
02428 vec_sub(tmp, tmp, rcom);
02429
02430 if (distance(bondsum+3*j, tmp)>BONDSUMTOL) {
02431 if (verbose>4) {
02432 char buf[256];
02433 sprintf(buf, "Bond mismatch %i-->%i, bondsum distance = %.2f",
02434 atomindex[j], atomindex[m],
02435 distance(bondsum+3*j, tmp));
02436 msgInfo << buf << sendmsg;
02437 }
02438
02439
02440
02441 return 0;
02442 }
02443 return 1;
02444 }
02445
02446
02447
02448
02449
02450
02451
02452 void Symmetry::identify_transformed_atoms(Matrix4 *trans,
02453 int &nmatches,
02454 int *(&matchlist)) {
02455
02456 const float *posA = coor;
02457
02458 float *posB = new float[3*sel->selected];
02459
02460 if (matchlist) delete [] matchlist;
02461 matchlist = new int[sel->selected];
02462
02463
02464 int i;
02465 for(i=0; i<sel->selected; i++) {
02466 trans->multpoint3d(posA+3*i, posB+3*i);
02467 }
02468
02469 nmatches = 0;
02470 for(i=0; i<sel->selected; i++) {
02471 float minr2=999999.f;
02472 int k, kmatch = -1;
02473 for(k=0; k<sel->selected; k++) {
02474
02475 if (atomtype[i]==atomtype[k]) {
02476 #if 0
02477 if (checkbonds) {
02478 float imagebondsum[3];
02479 vec_add(imagebondsum, bondsum+3*k, rcom);
02480 trans->multpoint3d(imagebondsum, imagebondsum);
02481 vec_sub(imagebondsum, imagebondsum, rcom);
02482
02483 if (distance(bondsum+3*i, imagebondsum)>BONDSUMTOL) {
02484 continue;
02485 }
02486 }
02487 #endif
02488 float r2 = distance2(posA+3*i, posB+3*k);
02489
02490 if (r2<minr2) { minr2 = r2; kmatch = k; }
02491 }
02492 }
02493
02494 if (kmatch>=0) {
02495 matchlist[i] = kmatch;
02496 nmatches++;
02497
02498
02499 }
02500 else {
02501
02502 matchlist[i] = i;
02503
02504 if (verbose>3) {
02505 char buf[256];
02506 sprintf(buf, "No match for atom %i (atomtype %i)\n", atomindex[i], atomtype[i]);
02507 msgInfo << buf << sendmsg;
02508 }
02509 }
02510 }
02511
02512 delete [] posB;
02513 }
02514
02515
02516
02517 float Symmetry::ideal_coordinate_rmsd () {
02518
02519 const float *pos = sel->coordinates(mlist);
02520
02521 float rmsdsum = 0.0;
02522 int i, j=0;
02523 for (i=0; i<sel->num_atoms; i++) {
02524 if (sel->on[i]) {
02525 rmsdsum += distance2(pos+3*i, idealcoor+3*j);
02526 j++;
02527 }
02528 }
02529
02530 return sqrtf(rmsdsum / sel->selected);
02531 }
02532
02533
02534 int Symmetry::ideal_coordinate_sanity() {
02535 int i, j;
02536 float mindist2 = float(MINATOMDIST*MINATOMDIST);
02537 for (i=0; i<sel->selected; i++) {
02538 for (j=i+1; j<sel->selected; j++) {
02539 if (distance2(idealcoor+3*i, idealcoor+3*j)<mindist2)
02540 return 0;
02541 }
02542 }
02543
02544 return 1;
02545 }
02546
02547
02548
02549
02550
02551
02552
02553
02554
02555
02556
02557
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568 void Symmetry::idealize_elements() {
02569 int i;
02570
02571
02572 int *idealp = NULL;
02573 if (planes.num()) {
02574 idealp = new int[planes.num()];
02575 memset(idealp, 0, planes.num()*sizeof(int));
02576 }
02577
02578
02579
02580
02581
02582
02583 if (axes.num()) {
02584
02585
02586 if (horizontalplane>=0) {
02587
02588
02589 vec_copy(planes[horizontalplane].v, axes[0].v);
02590 idealp[horizontalplane] = 1;
02591 }
02592
02593 if (numverticalplanes) {
02594
02595
02596 int vref = -1;
02597 for (i=0; i<planes.num(); i++) {
02598 if (planes[i].type&VERTICALPLANE) {
02599
02600 vref = i;
02601 float normal[3];
02602 cross_prod(normal, axes[0].v, plane(vref));
02603 cross_prod(planes[vref].v, axes[0].v, normal);
02604 vec_normalize(planes[vref].v);
02605 idealp[vref] = 1;
02606 break;
02607 }
02608 }
02609
02610
02611
02612
02613 for (i=vref+1; i<planes.num(); i++) {
02614 if (planes[i].type&VERTICALPLANE) {
02615 align_plane_with_axis(plane(i), axes[0].v, plane(i));
02616
02617
02618 float idealplane[3];
02619 if (!idealize_angle(planes[vref].v, axes[0].v, plane(i), idealplane, axes[0].order)) {
02620 if (verbose>3)
02621 msgInfo << "Couldn't idealize vertical plane " << i << sendmsg;
02622 continue;
02623 }
02624
02625 int first = plane_exists(idealplane);
02626 if (first>=0) {
02627
02628
02629
02630 if (!idealp[first]) {
02631 vec_copy(planes[first].v, idealplane);
02632 idealp[first] = 1;
02633 }
02634 } else {
02635 vec_copy(planes[i].v, idealplane);
02636 idealp[i] = 1;
02637 }
02638 }
02639 }
02640 }
02641
02642
02643 } else {
02644
02645
02646 if (planes.num()<=1) {
02647 if (idealp) delete [] idealp;
02648 return;
02649 }
02650
02651
02652
02653
02654
02655
02656
02657 int bestplane = 0;
02658 float bestscore = 0.f;
02659 for (i=0; i<planes.num(); i++) {
02660 float score = planes[i].overlap*planes[i].weight;
02661 if (score>bestscore) {
02662 bestplane = i;
02663 bestscore = score;
02664 }
02665 idealp[i] = 0;
02666 }
02667 idealp[bestplane] = 1;
02668
02669 if (verbose>4) {
02670 msgErr << "Found planes without intersection axis!" << sendmsg;
02671 char buf[256];
02672 for (i=0; i<planes.num(); i++) {
02673 sprintf(buf, "plane[%i] {% .3f % .3f % .3f} overlap = %f, weight = %d", i,
02674 planes[i].v[0], planes[i].v[1], planes[i].v[2],
02675 planes[i].overlap, planes[i].weight);
02676 msgErr << buf << sendmsg;
02677 }
02678 }
02679 }
02680
02681 int numidealaxes = 0;
02682 int *ideala = NULL;
02683 if (axes.num()) {
02684 ideala = new int[axes.num()];
02685 memset(ideala, 0, axes.num()*sizeof(int));
02686 ideala[0] = 1;
02687 }
02688
02689 int geometry = 0;
02690 if (maxaxisorder==2) geometry = 4;
02691 else if (maxaxisorder==3) geometry = TETRAHEDRON;
02692 else if (maxaxisorder==4) geometry = OCTAHEDRON;
02693 else if (maxaxisorder==5) geometry = DODECAHEDRON;
02694
02695
02696 for (i=1; i<numaxes(); i++) {
02697 if (axes[i].order<maxaxisorder) continue;
02698 int j, vref=-1;
02699
02700
02701
02702
02703
02704 for (j=0; j<numplanes(); j++) {
02705 if (orthogonal(planes[j].v, axes[i].v, orthogtol) &&
02706 orthogonal(planes[j].v, axes[0].v, orthogtol)) {
02707 if (!idealp[j]) {
02708
02709
02710 continue;
02711 }
02712
02713 float idealaxis[3];
02714 if (!idealize_angle(axes[0].v, planes[j].v, axes[i].v,
02715 idealaxis, geometry)) {
02716 if (verbose>4) {
02717 msgInfo << "Couldn't idealize axis " << i
02718 << " wrt reference axis in plane " << j
02719 << "." << sendmsg;
02720 }
02721 continue;
02722 }
02723 vec_copy(axes[i].v, idealaxis);
02724 vref = j;
02725 ideala[i] = 1;
02726 break;
02727 }
02728 }
02729
02730 if (vref<0) continue;
02731
02732
02733
02734 for (j=0; j<planes.num(); j++) {
02735 if (idealp[j]) continue;
02736
02737
02738 if (orthogonal(planes[j].v, axes[i].v, orthogtol)) {
02739
02740 align_plane_with_axis(planes[j].v, axes[i].v, planes[j].v);
02741
02742
02743
02744 float idealplane[3];
02745 if (!idealize_angle(planes[vref].v, axes[i].v, plane(j), idealplane, axes[i].order)) {
02746 if (verbose>4) {
02747 msgInfo << "Vertical plane " <<j<< " couldn't be idealized!" << sendmsg;
02748 }
02749 continue;
02750 }
02751 int first = plane_exists(idealplane);
02752 if (first>=0) {
02753
02754
02755
02756 if (!idealp[first]) {
02757 vec_copy(planes[first].v, idealplane);
02758 idealp[first] = 1;
02759 }
02760 } else {
02761 vec_copy(planes[j].v, idealplane);
02762 idealp[j] = 1;
02763 }
02764 }
02765 }
02766 }
02767
02768
02769 prune_planes(idealp);
02770
02771
02772
02773
02774 for (i=0; i<numplanes(); i++) {
02775 int j;
02776 for (j=i+1; j<numplanes(); j++) {
02777
02778 float intersect[3];
02779 cross_prod(intersect, plane(i), plane(j));
02780 vec_normalize(intersect);
02781
02782 int k;
02783 for (k=0; k<axes.num(); k++) {
02784 if (ideala[k]) continue;
02785
02786 if (collinear(intersect, axes[k].v, collintol)) {
02787
02788 vec_copy(axes[k].v, intersect);
02789 ideala[k] = 1; numidealaxes++;
02790 break;
02791 }
02792 }
02793 }
02794 }
02795
02796 float halfcollintol = cosf(float(DEGTORAD(5.0f)));
02797
02798
02799
02800 for (i=0; i<numplanes(); i++) {
02801 int j;
02802 for (j=i+1; j<numplanes(); j++) {
02803
02804 float bisect1[3];
02805 vec_add(bisect1, planes[i].v, planes[j].v);
02806 vec_normalize(bisect1);
02807
02808
02809 float bisect2[3], tmp[3];
02810 vec_negate(tmp, planes[i].v);
02811 vec_add(bisect2, tmp, planes[j].v);
02812 vec_normalize(bisect2);
02813
02814 int k;
02815 int foundbi1=0, foundbi2=0;
02816 for (k=0; k<axes.num(); k++) {
02817 if (ideala[k]) continue;
02818
02819 if (!foundbi1 && collinear(bisect1, axes[k].v, halfcollintol)) {
02820
02821 vec_copy(axes[k].v, bisect1);
02822 ideala[k] = 1; numidealaxes++;
02823 foundbi1 = 1;
02824 if (foundbi2) break;
02825 }
02826 if (!foundbi2 && collinear(bisect2, axes[k].v, halfcollintol)) {
02827
02828 vec_copy(axes[k].v, bisect2);
02829 ideala[k] = 1; numidealaxes++;
02830 foundbi2 = 1;
02831 if (foundbi1) break;
02832 }
02833 }
02834 }
02835 }
02836
02837
02838
02839
02840
02841 if (numidealaxes<axes.num()) {
02842 int firstorth = -1;
02843 for (i=0; i<numaxes(); i++) {
02844 if (ideala[i]) continue;
02845
02846 if (!orthogonal(axes[i].v, axes[0].v, orthogtol)) continue;
02847
02848
02849 float tmp[3];
02850 cross_prod(tmp, axes[0].v, axes[i].v);
02851 vec_normalize(tmp);
02852 cross_prod(axes[i].v, tmp, axes[0].v);
02853
02854 if (firstorth<0) {
02855 firstorth = i;
02856 ideala[i] = 1;
02857 continue;
02858 }
02859
02860
02861 if (!idealize_angle(axes[firstorth].v, axes[0].v, axes[i].v, tmp, axes[0].order)) {
02862 if (verbose>4) {
02863 msgInfo << "Couldn't idealize axis "<<i<<" to first orthogonal axis "
02864 <<firstorth<< "!" << sendmsg;
02865 }
02866 continue;
02867 }
02868
02869 vec_copy(axes[i].v, tmp);
02870 ideala[i] = 1;
02871 }
02872 }
02873
02874
02875 prune_axes(ideala);
02876
02877 if (ideala) delete [] ideala;
02878 if (idealp) delete [] idealp;
02879
02880
02881
02882 if (numrotreflect()) {
02883 int *idealr = new int[numrotreflect()];
02884 memset(idealr, 0, numrotreflect()*sizeof(int));
02885
02886 for (i=0; i<numrotreflect(); i++) {
02887 int j, success=0;
02888 for (j=0; j<numaxes(); j++) {
02889 float dot = dot_prod(rotreflections[i].v, axes[j].v);
02890 if (fabs(dot) > collintol) {
02891 float tmp[3];
02892 if (dot>0) vec_negate(tmp, axes[j].v);
02893 else vec_copy(tmp, axes[j].v);
02894
02895 vec_copy(rotreflections[i].v, tmp);
02896 rotreflections[i].type = j;
02897 success = 1;
02898 break;
02899 }
02900 }
02901
02902 if (success) {
02903
02904 idealr[i] = 1;
02905 }
02906 }
02907
02908 prune_rotreflections(idealr);
02909 delete [] idealr;
02910 }
02911
02912 }
02913
02914
02915
02916
02917
02918
02919
02920
02921
02922
02923
02924
02925
02926
02927
02928 int Symmetry::idealize_angle(const float *refaxis, const float *hub,
02929 const float *myaxis, float *idealaxis, int reforder) {
02930 float alpha = angle(refaxis, myaxis);
02931
02932 const float tetrahedron = float(RADTODEG(2.0f*atanf(sqrtf(2.0f))));
02933 const float octahedron = 90.0f;
02934 const float dodecahedron = float(RADTODEG(acosf(-1/sqrtf(5.0f))));
02935 const float tol = 5.0f;
02936
02937 int success = 0;
02938 float idealangle=0.0f;
02939
02940 if (reforder==TETRAHEDRON) idealangle = tetrahedron;
02941 else if (reforder==OCTAHEDRON) idealangle = octahedron;
02942 else if (reforder==DODECAHEDRON) idealangle = dodecahedron;
02943
02944 if (reforder<0) {
02945 if (fabs(idealangle-alpha)<tol) {
02946 alpha = idealangle;
02947 success = 1;
02948
02949 } else if (fabs(180.0-idealangle-alpha)<tol) {
02950 alpha = 180-idealangle;
02951 success = 1;
02952 }
02953 }
02954
02955 int i;
02956 for (i=1; i<reforder; i++) {
02957 idealangle = i*180.0f/reforder;
02958 if (fabs(alpha-idealangle)<tol) {
02959 alpha = idealangle;
02960 success = 1;
02961 break;
02962 }
02963 }
02964
02965
02966 if (fabs(alpha)<tol || fabs(alpha-180)<tol) {
02967
02968 alpha = 0;
02969 success = 1;
02970 }
02971
02972 if (!success) {
02973
02974 return 0;
02975 }
02976
02977 float normal[3];
02978 cross_prod(normal, refaxis, myaxis);
02979 if (dot_prod(hub, normal) < 0) alpha = -alpha;
02980
02981
02982
02983 Matrix4 rot;
02984 rot.rotate_axis(hub, float(DEGTORAD(alpha)));
02985 rot.multpoint3d(refaxis, idealaxis);
02986
02987 return 1;
02988 }
02989
02990
02991
02992
02993
02994 void Symmetry::collapse_axis(const float *axis, int order,
02995 int refatom, const int *matchlist,
02996 int *(&connectedunique)) {
02997 int i;
02998 float refcoor[3];
02999 vec_sub(refcoor, coor+3*refatom, rcom);
03000
03001
03002 float r0[3];
03003 vec_scale(r0, dot_prod(refcoor, axis), axis);
03004 vec_sub(r0, refcoor, r0);
03005
03006 for (i=0; i<sel->selected; i++) {
03007 if (!uniqueatoms[i] || i==matchlist[i]) continue;
03008
03009
03010
03011
03012
03013
03014
03015
03016
03017 int image, found=0;
03018 int k = i;
03019 for (image=0; image<order; image++) {
03020 float tmp[3];
03021 vec_sub(tmp, idealcoor+3*k, rcom);
03022
03023
03024 float r[3];
03025 vec_scale(r, dot_prod(tmp, axis), axis);
03026 vec_sub(r, tmp, r);
03027
03028
03029
03030
03031 if (angle(r, r0) <= 180.0/order) {
03032 found = 1;
03033 break;
03034 }
03035 k = matchlist[k];
03036 }
03037 if (found && k!=i) {
03038
03039 uniqueatoms[i] = 0;
03040 uniqueatoms[k] = 1;
03041 }
03042 }
03043
03044
03045 wrap_unconnected_unique_atoms(refatom, matchlist, connectedunique);
03046 }
03047
03048
03049
03050
03051
03052
03053 void Symmetry::wrap_unconnected_unique_atoms(int root,
03054 const int *matchlist,
03055 int *(&connectedunique)) {
03056 int i, k=0;
03057 int numswapped = 0;
03058
03059
03060
03061
03062 if (!connectedunique) {
03063 connectedunique = new int[sel->selected];
03064 }
03065 find_connected_unique_atoms(connectedunique, root);
03066
03067
03068
03069
03070 do {
03071 numswapped = 0;
03072 for (i=0; i<sel->selected; i++) {
03073
03074 if (!uniqueatoms[i] || connectedunique[i]) continue;
03075
03076 int swap = 0;
03077 int image = 0;
03078 int j = matchlist[i];
03079
03080 while (j!=i && image<sel->selected) {
03081
03082 if (connectedunique[j]) {
03083
03084 swap = 1;
03085 } else {
03086
03087
03088 int k;
03089 for (k=0; k<bondsperatom[j].numbonds; k++) {
03090 int bondto = bondsperatom[j].bondto[k];
03091 if (connectedunique[bondto]) swap = 1;
03092 }
03093 }
03094 if (swap) break;
03095
03096 j = matchlist[j];
03097
03098 image++;
03099 }
03100
03101 if (swap) {
03102
03103 uniqueatoms[i] = 0;
03104 uniqueatoms[j] = 1;
03105 connectedunique[j] = 1;
03106 numswapped++;
03107 }
03108 }
03109
03110 if (k>=sel->selected) {
03111 msgErr << "Stop looping unconnected unique atoms" << sendmsg;
03112 break;
03113 }
03114 k++;
03115
03116 } while (numswapped);
03117
03118 }
03119
03120
03121
03122
03123
03124 void Symmetry::find_connected_unique_atoms(int *(&connectedunique),
03125 int root) {
03126 ResizeArray<int> leaves;
03127 ResizeArray<int> newleaves;
03128 leaves.append(root);
03129
03130 memset(connectedunique, 0, sel->selected*sizeof(int));
03131 connectedunique[root] = 1;
03132
03133 int numbonded = 1;
03134 int i;
03135
03136 do {
03137 newleaves.clear();
03138
03139
03140 for (i=0; i<leaves.num(); i++) {
03141 int j = leaves[i];
03142
03143 int k;
03144 for (k=0; k<bondsperatom[j].numbonds; k++) {
03145 int bondto = bondsperatom[j].bondto[k];
03146
03147 if (uniqueatoms[bondto]&& !connectedunique[bondto]) {
03148 connectedunique[bondto] = 1;
03149 newleaves.append(bondto);
03150 numbonded++;
03151 }
03152 }
03153
03154 }
03155
03156 leaves.clear();
03157 for (i=0; i<newleaves.num(); i++) {
03158 leaves.append(newleaves[i]);
03159 }
03160
03161 } while (newleaves.num());
03162
03163
03164
03165
03166
03167 }
03168
03169
03170
03171
03172
03173
03174
03175
03176 void Symmetry::unique_coordinates() {
03177 int i;
03178 for(i=0; i<sel->selected; i++) {
03179 uniqueatoms[i] = 1;
03180 }
03181
03182
03183
03184
03185
03186 float refcoor[3];
03187 int refatom = 0;
03188 vec_sub(refcoor, coor, rcom);
03189 if (norm(refcoor)<0.1) {
03190 refatom = 1;
03191 vec_sub(refcoor, coor+3*refatom, rcom);
03192 }
03193
03194 int *connectedunique = NULL;
03195
03196
03197 if (inversion) {
03198 Matrix4 inv;
03199 inv.translate(rcom[0], rcom[1], rcom[2]);
03200 inv.scale(-1.0);
03201 inv.translate(-rcom[0], -rcom[1], -rcom[2]);
03202
03203
03204 int *matchlist = unique_coordinates(&inv);
03205
03206 int j;
03207 for(j=0; j<sel->selected; j++) {
03208 if (!uniqueatoms[j] || j==matchlist[j]) continue;
03209 uniqueatoms[j] = 0;
03210 uniqueatoms[matchlist[j]] = 1;
03211 }
03212
03213 wrap_unconnected_unique_atoms(refatom, matchlist, connectedunique);
03214
03215 if (matchlist) delete [] matchlist;
03216 }
03217
03218
03219 for (i=0; i<numaxes(); i++) {
03220 Matrix4 rot;
03221 rot.translate(rcom[0], rcom[1], rcom[2]);
03222 rot.rotate_axis(axes[i].v, float(VMD_TWOPI)/axes[i].order);
03223 rot.translate(-rcom[0], -rcom[1], -rcom[2]);
03224
03225
03226 int *matchlist = unique_coordinates(&rot);
03227
03228
03229
03230
03231 collapse_axis(axes[i].v, axes[i].order, refatom, matchlist,
03232 connectedunique);
03233
03234 if (matchlist) delete [] matchlist;
03235 }
03236
03237
03238 for (i=0; i<numplanes(); i++) {
03239 Matrix4 mirror;
03240 mirror.translate(rcom[0], rcom[1], rcom[2]);
03241 mirror.scale(-1.0);
03242 mirror.rotate_axis(planes[i].v, float(VMD_PI));
03243 mirror.translate(-rcom[0], -rcom[1], -rcom[2]);
03244
03245
03246 int *matchlist = unique_coordinates(&mirror);
03247
03248 int j;
03249 for(j=0; j<sel->selected; j++) {
03250 if (!uniqueatoms[j] || j==matchlist[j]) continue;
03251
03252 float tmp[3];
03253 vec_sub(tmp, coor+3*j, rcom);
03254 if (behind_plane(planes[i].v, tmp)!=behind_plane(planes[i].v, refcoor)) {
03255 uniqueatoms[j] = 0;
03256 uniqueatoms[matchlist[j]] = 1;
03257 }
03258 }
03259 if (matchlist) delete [] matchlist;
03260 }
03261
03262
03263 for (i=0; i<numrotreflect(); i++) {
03264 Matrix4 rotref;
03265 rotref.translate(rcom[0], rcom[1], rcom[2]);
03266 rotref.rotate_axis(rotreflections[i].v, float(VMD_TWOPI)/rotreflections[i].order);
03267 rotref.scale(-1.0);
03268 rotref.rotate_axis(rotreflections[i].v, float(VMD_PI));
03269 rotref.translate(-rcom[0], -rcom[1], -rcom[2]);
03270
03271
03272 int *matchlist = unique_coordinates(&rotref);
03273
03274 collapse_axis(rotreflections[i].v, rotreflections[i].order, refatom, matchlist, connectedunique);
03275
03276 if (matchlist) delete [] matchlist;
03277 }
03278
03279 if (connectedunique) delete [] connectedunique;
03280 }
03281
03282
03283
03284
03285
03286
03287 int* Symmetry::unique_coordinates(Matrix4 *trans) {
03288 int nmatches;
03289 int *matchlist = NULL;
03290
03291
03292
03293
03294
03295 identify_transformed_atoms(trans, nmatches, matchlist);
03296
03297 int j;
03298 for(j=0; j<sel->selected; j++) {
03299 if (!uniqueatoms[j]) continue;
03300 int m = matchlist[j];
03301
03302 if (m<0) continue;
03303
03304 if (m>j) uniqueatoms[m] = 0;
03305 }
03306
03307 return matchlist;
03308 }
03309
03310
03312 void Symmetry::determine_pointgroup() {
03313 if (linear) {
03314 if (inversion) pointgroup = POINTGROUP_DINFH;
03315 else pointgroup = POINTGROUP_CINFV;
03316
03317 pointgrouporder = -1;
03318 }
03319
03320 else if (sphericaltop && maxaxisorder>=3 && axes[0].order>=2) {
03321 if (maxaxisorder==3) {
03322 if (numplanes()) {
03323 if (inversion) pointgroup = POINTGROUP_TH;
03324 else pointgroup = POINTGROUP_TD;
03325 }
03326 else pointgroup = POINTGROUP_T;
03327 }
03328 else if (maxaxisorder==4) {
03329 if (numplanes() || inversion) pointgroup = POINTGROUP_OH;
03330 else pointgroup = POINTGROUP_O;
03331 }
03332 else if (maxaxisorder==5) {
03333 if (numplanes() || inversion) pointgroup = POINTGROUP_IH;
03334 else pointgroup = POINTGROUP_I;
03335
03336 }
03337 else pointgroup = POINTGROUP_UNKNOWN;
03338
03339 }
03340
03341 else if (numaxes()) {
03342
03343
03344 int i;
03345 int perpC2 = 0;
03346 for (i=0; i<numaxes(); i++) {
03347 if (axes[i].order==2 && (axes[i].type & PERPENDICULAR_AXIS)) {
03348 perpC2++;
03349 }
03350 }
03351
03352 if (perpC2==maxaxisorder) {
03353 if (horizontalplane>=0) pointgroup = POINTGROUP_DNH;
03354 else {
03355
03356
03357 if (numdihedralplanes==maxaxisorder) {
03358 pointgroup = POINTGROUP_DND;
03359 }
03360 else {
03361 pointgroup = POINTGROUP_DN;
03362 }
03363 }
03364
03365 pointgrouporder = maxaxisorder;
03366 }
03367
03368 else {
03369 if (horizontalplane>=0) pointgroup = POINTGROUP_CNH;
03370 else {
03371
03372 if (numverticalplanes==maxaxisorder) {
03373 pointgroup = POINTGROUP_CNV;
03374 }
03375 else {
03376 if (numS2n()) {
03377 pointgroup = POINTGROUP_S2N;
03378 }
03379 else {
03380 pointgroup = POINTGROUP_CN;
03381 }
03382 }
03383 }
03384 pointgrouporder = maxaxisorder;
03385
03386 }
03387 }
03388
03389 else {
03390 if (numplanes()==1) pointgroup = POINTGROUP_CS;
03391 else {
03392 if (inversion) pointgroup = POINTGROUP_CI;
03393 else pointgroup = POINTGROUP_C1;
03394 }
03395 }
03396 }
03397
03398
03399
03400
03401
03402
03403 int Symmetry::pointgroup_rank(int pg, int order) {
03404 if (pg==POINTGROUP_C1) return 1;
03405 if (pg==POINTGROUP_CS || pg==POINTGROUP_CI) return 2;
03406 if (pg==POINTGROUP_CN) {
03407 return 1+numprimefactors(order);
03408 }
03409 if (pg==POINTGROUP_S2N) {
03410 return 1+numprimefactors(order*2);
03411 }
03412 if (pg==POINTGROUP_DN || pg==POINTGROUP_CNV ||
03413 pg==POINTGROUP_CNH) {
03414 return 2+numprimefactors(order);
03415 }
03416 if (pg==POINTGROUP_DND || pg==POINTGROUP_DNH) {
03417 return 3+numprimefactors(order);
03418 }
03419 if (pg==POINTGROUP_CINFV) return 3;
03420 if (pg==POINTGROUP_DINFH || pg==POINTGROUP_T) return 4;
03421 if (pg==POINTGROUP_TD || pg==POINTGROUP_TH ||
03422 pg==POINTGROUP_O) {
03423 return 5;
03424 }
03425 if (pg==POINTGROUP_OH || pg==POINTGROUP_I) return 6;
03426 if (pg==POINTGROUP_IH) return 7;
03427 return 0;
03428 }
03429
03430
03431
03432
03433
03434
03435
03436
03437
03438
03439
03440
03441
03442
03443
03444
03445
03446
03447
03448
03449
03450
03451
03452
03453
03454
03455
03456
03457
03458
03459
03460
03461
03462
03463
03464
03465
03466
03467
03468
03469
03470 void Symmetry::orient_molecule() {
03471 if (pointgroup==POINTGROUP_C1) return;
03472
03473
03474
03475
03476 if (linear) {
03477
03478 orient.transvec(0, 0, 1);
03479
03480 orient.transvecinv(axes[0].v[0], axes[0].v[1], axes[0].v[2]);
03481
03482 orient.translate(-rcom[0], -rcom[1], -rcom[2]);
03483 return;
03484 }
03485
03486 int i;
03487 Matrix4 rot;
03488
03489
03490
03491
03492
03493 if (!sphericaltop && numaxes()) {
03494
03495
03496
03497 rot.transvec(0, 0, 1);
03498
03499 rot.transvecinv(axes[0].v[0], axes[0].v[1], axes[0].v[2]);
03500
03501
03502 int j;
03503 for (j=0; j<=2; j++) {
03504 if (orthogonal(axes[0].v, inertiaaxes[j], orthogtol)) break;
03505 }
03506 float *ortho_inertiaaxis = inertiaaxes[j];
03507
03508
03509
03510 Matrix4 m;
03511 float tmp[3];
03512 rot.multpoint3d(ortho_inertiaaxis, tmp);
03513 m.transvecinv(tmp[0], tmp[1], tmp[2]);
03514
03515
03516 m.multmatrix(rot);
03517 rot.loadmatrix(m);
03518 }
03519
03520
03521
03522 int orthC2 = -1;
03523 for (i=1; i<numaxes(); i++) {
03524 if (axes[i].order==2 &&
03525 (orthogonal(axes[i].v, axes[0].v, orthogtol) ||
03526 (pointgroup>=POINTGROUP_T && pointgroup<=POINTGROUP_IH))) {
03527 orthC2 = i;
03528 break;
03529 }
03530 }
03531 if (orthC2>=0) {
03532
03533
03534
03535 float tmp[3];
03536 rot.multpoint3d(axes[orthC2].v, tmp);
03537 Matrix4 m;
03538 m.transvecinv(tmp[0], tmp[1], tmp[2]);
03539
03540
03541 m.multmatrix(rot);
03542 rot.loadmatrix(m);
03543 }
03544
03545
03546
03547 if (numverticalplanes && orthC2<0) {
03548 for (i=0; i<numplanes(); i++) {
03549 if (planes[i].type==VERTICALPLANE) break;
03550 }
03551
03552
03553 Matrix4 m;
03554
03555
03556 float Y[]={0, 1, 0};
03557 m.transvec(Y[0], Y[1], Y[2]);
03558
03559
03560 float tmp[3];
03561 rot.multpoint3d(planes[i].v, tmp);
03562 m.transvecinv(tmp[0], tmp[1], tmp[2]);
03563
03564
03565 m.multmatrix(rot);
03566 rot.loadmatrix(m);
03567 }
03568
03569
03570
03571
03572 if ((horizontalplane>=0 && !numverticalplanes && orthC2<0) ||
03573 pointgroup==POINTGROUP_CS) {
03574
03575 if (pointgroup==POINTGROUP_CS) i = 0;
03576 else i = horizontalplane;
03577
03578 Matrix4 m;
03579 float Z[]={0, 0, 1};
03580
03581
03582 m.transvec(Z[0], Z[1], Z[2]);
03583
03584
03585 float tmp[3];
03586 rot.multpoint3d(planes[i].v, tmp);
03587 m.transvecinv(tmp[0], tmp[1], tmp[2]);
03588
03589
03590 m.multmatrix(rot);
03591 rot.loadmatrix(m);
03592 }
03593
03594 if (pointgroup>=POINTGROUP_T && pointgroup<=POINTGROUP_IH) {
03595
03596
03597
03598 int found = 0;
03599 float uniqueaxis[3];
03600 for (i=1; i<numaxes(); i++) {
03601 if (axes[i].order<maxaxisorder) break;
03602 int j;
03603 for(j=0; j<sel->selected; j++) {
03604 if (!uniqueatoms[j]) continue;
03605
03606 float tmpcoor[3];
03607 vec_sub(tmpcoor, coor+3*j, rcom);
03608 if (norm(tmpcoor)<0.1) continue;
03609
03610 if (collinear(axes[i].v, tmpcoor, collintol)) {
03611 if (dot_prod(axes[i].v, tmpcoor)>0)
03612 vec_copy(uniqueaxis, axes[i].v);
03613 else
03614 vec_negate(uniqueaxis, axes[i].v);
03615 found = 1;
03616 }
03617 }
03618 }
03619
03620 if (!found)
03621 msgErr << "orient_molecule(): Couldn't find axis with unique atom!" << sendmsg;
03622
03623 Matrix4 m;
03624 float XYZ[]={1, 1, 1};
03625
03626
03627 m.transvec(XYZ[0], XYZ[1], XYZ[2]);
03628
03629
03630 float tmp[3];
03631 rot.multpoint3d(uniqueaxis, tmp);
03632 m.transvecinv(tmp[0], tmp[1], tmp[2]);
03633
03634
03635 m.multmatrix(rot);
03636 rot.loadmatrix(m);
03637 }
03638
03639 orient.multmatrix(rot);
03640 orient.translate(-rcom[0], -rcom[1], -rcom[2]);
03641 }
03642
03643
03646 void Symmetry::print_statistics() {
03647 int i;
03648
03649 #if 0
03650 for (i=0; i<numplanes(); i++) {
03651 printf("plane[%i]: weight=%3i, overlap=%.2f\n", i, planes[i].weight, planes[i].overlap);
03652 }
03653
03654 for (i=0; i<numaxes(); i++) {
03655 printf("axis[%i]: order=%i, weight=%3i, overlap=%.2f {%.2f %.2f %.2f}\n", i, axes[i].order, axes[i].weight, axes[i].overlap, axes[i].v[0], axes[i].v[1], axes[i].v[2]);
03656 }
03657
03658 for (i=0; i<numrotreflect(); i++) {
03659 printf("rotrefl[%i]: order=%i, weight=%3i, overlap=%.2f {%.2f %.2f %.2f}\n", i, rotreflections[i].order, rotreflections[i].weight, rotreflections[i].overlap, rotreflections[i].v[0], rotreflections[i].v[1], rotreflections[i].v[2]);
03660 }
03661 #endif
03662
03663 char buf [256];
03664 msgInfo << sendmsg;
03665 msgInfo << "Summary of symmetry elements:" << sendmsg;
03666 msgInfo << "+===============================+" << sendmsg;
03667 msgInfo << "| inversion center: " << (inversion ? "yes" : "no ")
03668 << " |" << sendmsg;
03669
03670 if (planes.num()) {
03671 int havehorizplane = 0;
03672 msgInfo << "| |" << sendmsg;
03673 if (horizontalplane>=0) {
03674 msgInfo << "| horizonal planes: 1 |" << sendmsg;
03675 havehorizplane = 1;
03676 }
03677 if (numverticalplanes) {
03678 sprintf(buf, "| vertical planes: %2d |", numverticalplanes);
03679 msgInfo << buf << sendmsg;
03680 }
03681 if (numdihedralplanes) {
03682 sprintf(buf, "| (%-2d of them dihedral) |", numdihedralplanes);
03683 msgInfo << buf << sendmsg;
03684 }
03685 int numregplanes = numplanes()-numverticalplanes-havehorizplane;
03686 if (numregplanes) {
03687 sprintf(buf, "| regular planes: %2d |", numregplanes);
03688 msgInfo << buf << sendmsg;
03689 }
03690 if (numplanes()>1) {
03691 msgInfo << "| ----------------------------- |" << sendmsg;
03692 sprintf(buf, "| total planes: %2d |", numplanes());
03693 msgInfo << buf << sendmsg;
03694 }
03695 }
03696
03697 if (axes.num()) {
03698 msgInfo << "| |" << sendmsg;
03699 if (elementsummary.Cinf) {
03700 sprintf(buf, "| Cinf rotation axes: %2d |", (int)elementsummary.Cinf);
03701 msgInfo << buf << sendmsg;
03702 }
03703 for (i=maxaxisorder; i>=1; i--) {
03704 if (elementsummary.C[i]) {
03705 sprintf(buf, "| C%-4i rotation axes: %2d |", i, elementsummary.C[i]);
03706 msgInfo << buf << sendmsg;
03707 }
03708 }
03709 if (numaxes()>1) {
03710 msgInfo << "| ----------------------------- |" << sendmsg;
03711 sprintf(buf, "| total rotation axes: %2d |", numaxes());
03712 msgInfo << buf << sendmsg;
03713 }
03714 }
03715
03716 if (rotreflections.num()) {
03717 msgInfo << "| |" << sendmsg;
03718 for (i=maxrotreflorder; i>=3; i--) {
03719 if (elementsummary.S[i-3]) {
03720 sprintf(buf, "| S%-4i rotary reflections: %2d |", i, elementsummary.S[i-3]);
03721 msgInfo << buf << sendmsg;
03722 }
03723 }
03724 if (rotreflections.num()>1) {
03725 msgInfo << "| ----------------------------- |" << sendmsg;
03726 sprintf(buf, "| total rotary reflections: %2d |", rotreflections.num());
03727 msgInfo << buf << sendmsg;
03728 }
03729 }
03730 msgInfo << "+===============================+" << sendmsg;
03731
03732 msgInfo << sendmsg;
03733 msgInfo << "Element summary: " << elementsummarystring << sendmsg;
03734 }
03735
03736
03737
03738 void Symmetry::build_element_summary() {
03739 memset(&elementsummary, 0, sizeof(ElementSummary));
03740
03741 elementsummary.inv = inversion;
03742 elementsummary.sigma = planes.num();
03743
03744 int i;
03745 for (i=0; i<numaxes(); i++) {
03746 if (axes[i].order==INFINITE_ORDER) {
03747 elementsummary.Cinf++;
03748 } else if (axes[i].order<=MAXORDERCN) {
03749 int j;
03750 for (j=2; j<=axes[i].order; j++) {
03751 if (axes[i].order % j == 0) {
03752 (elementsummary.C[j])++;
03753 }
03754 }
03755 }
03756 }
03757
03758 for (i=0; i<numrotreflect(); i++) {
03759 if (rotreflections[i].order<=2*MAXORDERCN) {
03760 (elementsummary.S[rotreflections[i].order-3])++;
03761 }
03762 }
03763 }
03764
03765
03766
03767
03768 void Symmetry::build_element_summary_string(ElementSummary summary, char *(&sestring)) {
03769 int i ;
03770 if (sestring) delete [] sestring;
03771 sestring = new char[2 + 10*(MAXORDERCN+2*MAXORDERCN+summary.Cinf
03772 +(summary.sigma?1:0)+summary.inv)];
03773 char buf[100] ;
03774 sestring[0] = '\0';
03775
03776 if (inversion) strcat(sestring, "(inv) ");
03777
03778 if (summary.sigma==1) strcat(sestring, "(sigma) ");
03779 if (summary.sigma>1) {
03780 sprintf(buf, "%d*(sigma) ", summary.sigma);
03781 strcat(sestring, buf);
03782 }
03783
03784 if (summary.Cinf==1)
03785 strcat(sestring, "(Cinf) ");
03786 else if (summary.Cinf>1) {
03787 sprintf(buf, "%d*(Cinf) ", summary.Cinf);
03788 strcat(sestring, buf);
03789 }
03790
03791 for (i=MAXORDERCN; i>=2; i--) {
03792 if (summary.C[i]==1) {
03793 sprintf(buf, "(C%d) ", i);
03794 strcat(sestring, buf);
03795 }
03796 else if (summary.C[i]>1) {
03797 sprintf(buf, "%d*(C%d) ", summary.C[i], i);
03798 strcat(sestring, buf);
03799 }
03800 }
03801
03802 for (i=2*MAXORDERCN; i>=3; i--) {
03803 if (summary.S[i-3]==1) {
03804 sprintf(buf, "(S%d) ", i);
03805 strcat(sestring, buf);
03806 }
03807 else if (summary.S[i-3]>1) {
03808 sprintf(buf, "%d*(S%d) ", summary.S[i-3], i);
03809 strcat(sestring, buf);
03810 }
03811 }
03812 }
03813
03814
03815
03816
03817
03818
03819 void Symmetry::compare_element_summary(const char *pointgroupname) {
03820 missingelementstring[0] = '\0';
03821 additionalelementstring[0] = '\0';
03822
03823 if (!strcmp(pointgroupname, "Unknown")) return;
03824
03825 unsigned int i;
03826 for (i=0; i<NUMPOINTGROUPS; i++) {
03827 if (!strcmp(pointgroupname, pgdefinitions[i].name)) {
03828
03829 if (elementsummary.inv<pgdefinitions[i].summary.inv)
03830 strcat(missingelementstring, "(inv) ");
03831 else if (elementsummary.inv>pgdefinitions[i].summary.inv)
03832 strcat(additionalelementstring, "(inv) ");
03833
03834 char buf[100];
03835 if (elementsummary.sigma<pgdefinitions[i].summary.sigma) {
03836 sprintf(buf, "%i*(sigma) ",
03837 pgdefinitions[i].summary.sigma-elementsummary.sigma);
03838 strcat(missingelementstring, buf);
03839 }
03840 else if (elementsummary.sigma>pgdefinitions[i].summary.sigma) {
03841 sprintf(buf, "%i*(sigma) ",
03842 elementsummary.sigma-pgdefinitions[i].summary.sigma);
03843 strcat(additionalelementstring, buf);
03844 }
03845
03846 int j;
03847 for (j=MAXORDERCN; j>=2; j--) {
03848 if (elementsummary.C[j]<pgdefinitions[i].summary.C[j]) {
03849 sprintf(buf, "%i*(C%i) ", pgdefinitions[i].summary.C[j]-elementsummary.C[j], j);
03850 strcat(missingelementstring, buf);
03851 }
03852 if (elementsummary.C[j]>pgdefinitions[i].summary.C[j]) {
03853 sprintf(buf, "%i*(C%i) ", elementsummary.C[j]-pgdefinitions[i].summary.C[j], j);
03854 strcat(additionalelementstring, buf);
03855 }
03856 }
03857
03858 for (j=2*MAXORDERCN; j>=3; j--) {
03859 if (elementsummary.S[j-3]<pgdefinitions[i].summary.S[j-3]) {
03860 sprintf(buf, "%i*(S%i) ",
03861 pgdefinitions[i].summary.S[j-3]-elementsummary.S[j-3], j);
03862 strcat(missingelementstring, buf);
03863 }
03864 if (elementsummary.S[j-3]>pgdefinitions[i].summary.S[j-3]) {
03865 sprintf(buf, "%i*(S%i) ",
03866 elementsummary.S[j-3]-pgdefinitions[i].summary.S[j-3], j);
03867 strcat(additionalelementstring, buf);
03868 }
03869 }
03870 break;
03871 }
03872 }
03873 }
03874
03875
03876 void Symmetry::print_element_summary(const char *pointgroupname) {
03877 int i, found = 0;
03878 for (i=0; i<(int)NUMPOINTGROUPS; i++) {
03879 if (!strcmp(pointgroupname, pgdefinitions[i].name)) {
03880 found = 1;
03881 break;
03882 }
03883 }
03884 if (!found) return;
03885
03886 char *idealstring=NULL;
03887 build_element_summary_string(pgdefinitions[i].summary, idealstring);
03888
03889
03890
03891 if (strcmp(idealstring, elementsummarystring)) {
03892 char buf[256];
03893 sprintf(buf, "Ideal elements (%5s): %s\n", pgdefinitions[i].name, idealstring);
03894 msgInfo << buf << sendmsg;
03895 sprintf(buf, "Found elements (%5s): %s\n", pointgroupname, elementsummarystring);
03896 msgInfo << buf << sendmsg;
03897 if (strlen(additionalelementstring))
03898 msgInfo << "Additional elements: " << additionalelementstring << sendmsg;
03899 if (strlen(missingelementstring))
03900 msgInfo << "Missing elements: " << missingelementstring << sendmsg;
03901 }
03902 delete [] idealstring;
03903 }
03904
03905
03906
03907
03908
03909 void Symmetry::draw_transformed_mol(Matrix4 rot) {
03910 int i;
03911 Molecule *mol = mlist->mol_from_id(sel->molid());
03912 MoleculeGraphics *gmol = mol->moleculeGraphics();
03913 const float *pos = sel->coordinates(mlist);
03914 for (i=0; i<sel->num_atoms; i++) {
03915 switch (mol->atom(i)->atomicnumber) {
03916 case 1:
03917 gmol->use_color(8);
03918 break;
03919 case 6:
03920 gmol->use_color(10);
03921 break;
03922 case 7:
03923 gmol->use_color(0);
03924 break;
03925 case 8:
03926 gmol->use_color(1);
03927 break;
03928 default:
03929 gmol->use_color(2);
03930 break;
03931 }
03932 float p[3];
03933 rot.multpoint3d(pos+3*i, p);
03934 gmol->add_sphere(p, 2*sigma, 16);
03935 char tmp[10];
03936 sprintf(tmp, " %i", i);
03937 gmol->add_text(p, tmp, 1, 1.0f);
03938 }
03939 }
03940
03941
03942
03943
03944
03945 static inline bool coplanar(const float *normal1, const float *normal2, float tol) {
03946 return collinear(normal1, normal2, tol);
03947 }
03948
03949 static inline bool collinear(const float *axis1, const float *axis2, float tol) {
03950 if (fabs(dot_prod(axis1, axis2)) > tol) return 1;
03951 return 0;
03952 }
03953
03954 static inline bool orthogonal(const float *axis1, const float *axis2, float tol) {
03955 if (fabs(dot_prod(axis1, axis2)) < tol) return 1;
03956 return 0;
03957 }
03958
03959 static inline bool behind_plane(const float *normal, const float *coor) {
03960 return (dot_prod(normal, coor)>0.01);
03961 }
03962
03963
03964 static void align_plane_with_axis(const float *normal, const float *axis, float *alignedplane) {
03965 float inplane[3];
03966 cross_prod(inplane, axis, normal);
03967 cross_prod(alignedplane, inplane, axis);
03968 vec_normalize(alignedplane);
03969 }
03970
03971
03972
03973 static int isprime(int x) {
03974 int i;
03975 for (i=2; i<x; i++) {
03976 if (!(x%i)) return 0;
03977 }
03978 return 1;
03979 }
03980
03981
03982
03983 static int numprimefactors(int x) {
03984 int i, numfac=0;
03985 for (i=2; i<=x; i++) {
03986 if (!isprime(i)) continue;
03987 if (!(x%i)) {
03988 x /= i;
03989 numfac++;
03990 i--;
03991 }
03992 }
03993 return numfac;
03994 }
03995
03996
03997 inline int Symmetry::find_collinear_axis(const float *myaxis) {
03998 int i;
03999 for (i=0; i<axes.num(); i++) {
04000 if (collinear(myaxis, axes[i].v, collintol)) {
04001 return i;
04002 }
04003 }
04004 return -1;
04005 }
04006
04008 inline int Symmetry::plane_exists(const float *myplane) {
04009 int i;
04010 for (i=0; i<planes.num(); i++) {
04011 if (collinear(myplane, planes[i].v, collintol)) {
04012 return i;
04013 }
04014 }
04015 return -1;
04016 }
04017
04018
04019
04020
04021
04022 int Symmetry::is_planar(const float *normal) {
04023 Matrix4 alignx;
04024 alignx.transvecinv(normal[0], normal[1], normal[2]);
04025 int j, k;
04026 float xmin=0.0, xmax=0.0;
04027 for (j=0; j<sel->selected; j++) {
04028 float tmpcoor[3];
04029 alignx.multpoint3d(coor+3*j, tmpcoor);
04030 xmin = tmpcoor[0];
04031 xmax = tmpcoor[0];
04032 break;
04033 }
04034
04035 for (k=j+1; k<sel->selected; k++) {
04036 float tmpcoor[3];
04037 alignx.multpoint3d(coor+3*k, tmpcoor);
04038 if (tmpcoor[0]<xmin) xmin = tmpcoor[0];
04039 else if (tmpcoor[0]>xmax) xmax = tmpcoor[0];
04040 }
04041
04042 if (xmax-xmin < 1.5*sigma) return 1;
04043
04044 return 0;
04045 }
04046
04047
04048
04049 void Symmetry::assign_bonds() {
04050 Molecule *mol = mlist->mol_from_id(sel->molid());
04051
04052
04053
04054 int i, j=0;
04055 for (i=0; i<sel->num_atoms; i++) {
04056 if (sel->on[i]) atomindex[j++] = i;
04057 }
04058
04059 for (i=0; i<sel->selected; i++) {
04060 int j = atomindex[i];
04061
04062 bondsperatom[i].numbonds = 0;
04063 if (bondsperatom[i].bondto) delete [] bondsperatom[i].bondto;
04064 if (bondsperatom[i].length) delete [] bondsperatom[i].length;
04065 bondsperatom[i].bondto = new int[mol->atom(j)->bonds];
04066 bondsperatom[i].length = new float[mol->atom(j)->bonds];
04067
04068 int k;
04069 for (k=0; k<mol->atom(j)->bonds; k++) {
04070 int bondto = mol->atom(j)->bondTo[k];
04071
04072
04073 if (!sel->on[bondto]) continue;
04074
04075 float order = mol->getbondorder(j, k);
04076 if (order<0.f) order = 1.f;
04077
04078 if (bondto > j) {
04079
04080 int m;
04081 int found = 0;
04082 for (m=i+1; m<sel->selected; m++) {
04083 if (atomindex[m]==bondto) { found = 1; break; }
04084 }
04085
04086
04087
04088 if (found) {
04089
04090 Bond b;
04091 b.atom0 = i;
04092 b.atom1 = m;
04093 b.order = order;
04094
04095 b.length = distance(coor+3*i, coor+3*m);
04096 bonds.append(b);
04097 }
04098 }
04099 }
04100 }
04101
04102 for (i=0; i<bonds.num(); i++) {
04103 if (verbose>3) {
04104 char buf[256];
04105 sprintf(buf, "Bond %d: %d--%d %.1f L=%.2f", i,
04106 atomindex[bonds[i].atom0],
04107 atomindex[bonds[i].atom1],
04108 bonds[i].order, bonds[i].length);
04109 msgInfo << buf << sendmsg;
04110 }
04111 int numbonds;
04112 numbonds = bondsperatom[bonds[i].atom0].numbonds;
04113 bondsperatom[bonds[i].atom0].bondto[numbonds] = bonds[i].atom1;
04114 bondsperatom[bonds[i].atom0].length[numbonds] = bonds[i].length;
04115 bondsperatom[bonds[i].atom0].numbonds++;
04116
04117 numbonds = bondsperatom[bonds[i].atom1].numbonds;
04118 bondsperatom[bonds[i].atom1].bondto[numbonds] = bonds[i].atom0;
04119 bondsperatom[bonds[i].atom1].length[numbonds] = bonds[i].length;
04120 bondsperatom[bonds[i].atom1].numbonds++;
04121 }
04122 }
04123
04124
04125
04126
04127
04128
04129
04130
04131
04132
04133 void Symmetry::assign_bondvectors() {
04134 Molecule *mol = mlist->mol_from_id(sel->molid());
04135 memset(bondsum, 0, 3*sel->selected*sizeof(float));
04136 int i;
04137 for (i=0; i<sel->selected; i++) {
04138 int k;
04139 for (k=0; k<bondsperatom[i].numbonds; k++) {
04140 int bondto = bondsperatom[i].bondto[k];
04141
04142
04143 if (!sel->on[bondto]) continue;
04144
04145 int j = atomindex[i];
04146 float order = mol->getbondorder(j, k);
04147 if (order<0.f) order = 1.f;
04148
04149 float bondvec[3];
04150 vec_sub(bondvec, coor+3*bondto, coor+3*i);
04151 vec_normalize(bondvec);
04152 vec_scaled_add(bondsum+3*i, order, bondvec);
04153 }
04154 }
04155 }
04156
04157
04158
04159
04160
04161 static void assign_atoms(AtomSel *sel, MoleculeList *mlist, float *(&mycoor), int *(&atomtype)) {
04162
04163 const float *allcoor = sel->coordinates(mlist);
04164
04165 Molecule *mol = mlist->mol_from_id(sel->molid());
04166
04167
04168 char **typestringptr = new char*[sel->selected];
04169 int numtypes = 0;
04170
04171 int i, j=0;
04172 for (i=0; i<sel->num_atoms; i++) {
04173 if (!sel->on[i]) continue;
04174
04175
04176 vec_copy(mycoor+3*j, allcoor+3*i);
04177
04178
04179
04180 int k;
04181 int minatomicnum = 999;
04182 int maxatomicnum = 0;
04183 for (k=0; k<mol->atom(i)->bonds; k++) {
04184 int bondto = mol->atom(i)->bondTo[k];
04185 int atomicnum = mol->atom(bondto)->atomicnumber;
04186 if (atomicnum<minatomicnum) minatomicnum = atomicnum;
04187 if (atomicnum>maxatomicnum) maxatomicnum = atomicnum;
04188 }
04189
04190
04191
04192
04193 char *typestring = new char[8+12*mol->atom(i)->bonds];
04194 typestring[0] = '\0';
04195 char buf[100];
04196
04197
04198 sprintf(buf, "%i %i ", mol->atom(i)->atomicnumber, mol->atom(i)->bonds);
04199 strcat(typestring, buf);
04200
04201
04202
04203
04204 int m;
04205 for (m=minatomicnum; m<=maxatomicnum; m++) {
04206 unsigned char bondorder[7];
04207 memset(bondorder, 0, 7*sizeof(unsigned char));
04208
04209 unsigned char bondedatomicnum = 0;
04210 for (k=0; k<mol->atom(i)->bonds; k++) {
04211 if (m == mol->atom(mol->atom(i)->bondTo[k])->atomicnumber) {
04212 bondedatomicnum++;
04213 float bo = mol->getbondorder(i, k);
04214 if (bo<0.f) bo = 1.f;
04215 (bondorder[(int)(2*bo)])++;
04216 }
04217 }
04218 for (k=0; k<7; k++) {
04219 if (bondorder[k]) {
04220 sprintf(buf, "%i*(%i)%i ", bondorder[k], k, m);
04221 strcat(typestring, buf);
04222 }
04223 }
04224 }
04225
04226
04227
04228 int found = 0;
04229 for (k=0; k<numtypes; k++) {
04230 if (!strcmp(typestringptr[k], typestring)) {
04231 atomtype[j] = k;
04232 found = 1;
04233 delete [] typestring;
04234 break;
04235 }
04236 }
04237 if (!found) {
04238 atomtype[j] = numtypes;
04239 typestringptr[numtypes++] = typestring;
04240 }
04241
04242
04243 j++;
04244 }
04245
04246 for (i=0; i<numtypes; i++) {
04247 delete [] typestringptr[i];
04248 }
04249 delete [] typestringptr;
04250 }
04251
04252
04253
04254
04255
04256
04257
04258
04259
04260
04261
04262 inline static float trans_overlap(int *atomtype, float *(&coor), int numcoor,
04263 const Matrix4 *trans, float sigma,
04264 bool skipident, int maxnatoms) {
04265 float overlappermatch;
04266 return trans_overlap(atomtype, coor, numcoor, trans, sigma, skipident, maxnatoms, overlappermatch);
04267 }
04268
04269 static float trans_overlap(int *atomtype, float *(&coor), int numcoor,
04270 const Matrix4 *trans, float sigma,
04271 bool skipident, int maxnatoms, float &overlappermatch) {
04272
04273 const float *posA;
04274 posA = coor;
04275
04276 int *flgs = new int[numcoor];
04277 float *posB = new float[3*numcoor];
04278 memset(flgs, 0, numcoor*sizeof(int));
04279
04280
04281 int i, ncompare=0;
04282 for(i=0; i<numcoor; i++) {
04283
04284
04285 if (numcoor>maxnatoms && vmd_random()>maxnatoms/numcoor) continue;
04286
04287 trans->multpoint3d(posA+3*i, posB+3*i);
04288
04289
04290
04291 if (!(skipident && distance(posA+3*i, posB+3*i) < sigma)) {
04292 flgs[i] = 1;
04293 ncompare++;
04294 }
04295 }
04296
04297 if (ncompare<0.5*numcoor) {
04298
04299 delete [] flgs;
04300 delete [] posB;
04301 return 0.0;
04302 }
04303
04304
04305
04306 float dist;
04307
04308 dist = 3*sigma;
04309 float wrongelementpenalty = 100.0f/ncompare;
04310
04311 float overlap = 0.0;
04312 float antioverlap = 0.0;
04313 int i1, nmatches = 0, noverlap = 0, nwrongelement = 0;
04314 float maxr2=powf(1.0f*dist, 2);
04315 float itwosig2 = 1.0f/(2.0f*sigma*sigma);
04316
04317
04318 for (i1=0; i1<numcoor; i1++) {
04319 if (!flgs[i1]) continue;
04320
04321 float minr2 = maxr2+1.0f;
04322 float r2 = 0.0f;
04323
04324
04325 int j, i2=-1;
04326 for (j=0; j<numcoor; j++) {
04327 if (!flgs[j]) continue;
04328
04329 r2 = distance2(posA+3*i1, posB+3*j);
04330
04331 if (r2<minr2) { minr2 = r2; i2 = j; }
04332 }
04333
04334
04335 if (minr2<maxr2) {
04336 noverlap++;
04337
04338
04339 if (atomtype[i1]==atomtype[i2]) {
04340
04341 overlap += expf(-itwosig2*minr2);
04342 nmatches++;
04343 }
04344 else {
04345
04346 antioverlap += wrongelementpenalty*expf(-itwosig2*r2);
04347 nwrongelement++;
04348
04349 }
04350 }
04351 }
04352
04353 float nomatchpenalty = 0.0;
04354 overlappermatch = 0.0;
04355 int numnomatch = ncompare-nmatches;
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366 if (nmatches) overlappermatch = overlap/nmatches;
04367
04368 overlap -= antioverlap;
04369
04370 if (!(numnomatch==0)) {
04371
04372 nomatchpenalty = powf(overlappermatch, 5);
04373
04374 overlap -= 8*numnomatch*nomatchpenalty;
04375 }
04376 if (overlap<0) overlap = 0.0f;
04377
04378 overlap /= ncompare;
04379
04380
04381
04382
04383 delete [] flgs;
04384 delete [] posB;
04385
04386 return overlap;
04387 }
04388
04389
04390
04391
04392
04393
04394
04395
04396
04397
04398
04399
04400
04401
04402
04403
04404 int measure_trans_overlap(AtomSel *sel, MoleculeList *mlist, const Matrix4 *trans,
04405 float sigma, bool skipident, int maxnatoms, float &overlap) {
04406 if (!sel) return MEASURE_ERR_NOSEL;
04407 if (sel->num_atoms == 0) return MEASURE_ERR_NOATOMS;
04408
04409 float *coor = new float[3*sel->selected];
04410
04411 int *atomtypes = new int[sel->selected];
04412 assign_atoms(sel, mlist, coor, atomtypes);
04413
04414 overlap = trans_overlap(atomtypes, coor, sel->selected, trans, sigma, skipident, maxnatoms);
04415
04416 return MEASURE_NOERR;
04417 }
04418
04419
04420
04421
04422
04423
04424
04425
04426 int measure_pointset_overlap(const float *posA, int natomsA, int *flagsA,
04427 const float *posB, int natomsB, int *flagsB,
04428 float sigma, float pairdist, float &overlap) {
04429
04430 int nsmall = natomsA<natomsB ? natomsA : natomsB;
04431
04432 int maxpairs = -1;
04433 GridSearchPair *pairlist, *p;
04434 pairlist = vmd_gridsearch3(posA, natomsA, flagsA, posB, natomsB, flagsB, pairdist,
04435 1, maxpairs);
04436
04437 overlap = 0.0;
04438 int i1, i2;
04439 float dx, dy, dz, r2, itwosig2 = 1.0f/(2.0f*sigma*sigma);
04440 for (p=pairlist; p; p=p->next) {
04441 i1 = p->ind1;
04442 i2 = p->ind2;
04443 dx = posA[3*i1 ]-posB[3*i2];
04444 dy = posA[3*i1+1]-posB[3*i2+1];
04445 dz = posA[3*i1+2]-posB[3*i2+2];
04446 r2 = dx*dx + dy*dy + dz*dz;
04447 overlap += expf(-itwosig2*r2);
04448 }
04449
04450 overlap /= nsmall;
04451
04452 return MEASURE_NOERR;
04453 }
04454