1 /*
2  *  (c) 2004 Iowa State University
3  *      see the LICENSE file in the top level directory
4  */
5 
6 /* MoleculeData.cpp
7 
8 	class MoleculeData organizes all information on the molecule for each window/file
9 
10 	Created from other files, BMB, 4/1998
11 */
12 
13 #include "Globals.h"
14 #include "GlobalExceptions.h"
15 #include "MoleculeData.h"
16 #include "Frame.h"
17 #include "Internals.h"
18 #include "BasisSet.h"
19 #include "Math3D.h"
20 #include "InputData.h"
21 #include "VirtualSphere.h"
22 #include "Progress.h"
23 #include "Prefs.h"
24 #include "myFiles.h"
25 #include <string.h>
26 #include <new>
27 #include <map>
28 #if defined(WIN32)
29 #undef AddAtom
30 #endif
31 
MoleculeData(MolDisplayWin * MolWin)32 MoleculeData::MoleculeData(MolDisplayWin *MolWin) {
33 	this->MolWin = MolWin;
34 	RotCoords = NULL;
35 	cFrame = Frames = new Frame(MolWin);
36 	CurrentFrame=1;
37 	NumFrames = 1;
38 	IntCoords = NULL;
39 	Basis = NULL;
40 	Description = NULL;
41 	MaxAtoms = 0;
42 	MaxSize=0.5;
43 	WindowSize=3.0;
44 	InitRotationMatrix(TotalRotation);
45 	InputOptions = NULL;
46 	DrawMode = 0;
47 	DrawLabels = 0;
48 	constrain_anno_id = -1;
49 }
~MoleculeData(void)50 MoleculeData::~MoleculeData(void) {
51 	DeleteAllAnnotations();
52 	while (Frames) {
53 		cFrame = Frames->GetNextFrame();
54 		delete Frames;
55 		Frames = cFrame;
56 	}
57 	if (RotCoords) delete [] RotCoords;
58 	if (Description) delete [] Description;
59 	if (IntCoords) delete IntCoords;
60 	if (Basis) delete Basis;
61 	if (InputOptions) delete InputOptions;
62 }
SetupFrameMemory(long NumAtoms,long NumBonds)63 bool MoleculeData::SetupFrameMemory(long NumAtoms, long NumBonds) {
64 	if (NumAtoms < cFrame->AtomAllocation) NumAtoms = cFrame->AtomAllocation;
65 	if (NumBonds < cFrame->BondAllocation) NumBonds = cFrame->BondAllocation;
66 	if ((NumAtoms == cFrame->AtomAllocation)&&
67 		(NumBonds == cFrame->BondAllocation)) return true;//no need to resize
68 	mpAtom * tempAtoms=NULL;
69 	Bond * tempBonds=NULL;
70 	try {
71 		tempAtoms = new mpAtom[NumAtoms];
72 		tempBonds = new Bond[NumBonds];
73 	}
74 	catch (std::bad_alloc) {
75 		if (tempAtoms) delete [] tempAtoms;
76 		if (tempBonds) delete [] tempBonds;
77 		return false;
78 	}
79 	memcpy(tempAtoms, cFrame->Atoms, cFrame->NumAtoms*sizeof(mpAtom));
80 	memcpy(tempBonds, cFrame->Bonds, cFrame->NumBonds*sizeof(Bond));
81 	if (cFrame->Atoms) delete [] cFrame->Atoms;	//delete old arrays, if any
82 	if (cFrame->Bonds) delete [] cFrame->Bonds;
83 	cFrame->Atoms = tempAtoms;
84 	cFrame->Bonds = tempBonds;
85 	cFrame->AtomAllocation = NumAtoms;
86 	cFrame->BondAllocation = NumBonds;
87 	if (NumAtoms > MaxAtoms) {	//Need to extend the size of the zbuffer arrays
88 		if (RotCoords) delete [] RotCoords;
89 		try {
90 			RotCoords = new CPoint3D[NumAtoms];
91 		}
92 		catch (std::bad_alloc) {
93 			if (RotCoords) delete [] RotCoords;
94 			RotCoords = new CPoint3D[MaxAtoms];
95 			return false;
96 		}
97 		MaxAtoms = NumAtoms;
98 	}
99 	return true;
100 }
101 /*Call to position a frame in the frame list according to the xcoord (time or IRC value)
102   routine returns a new empty frame ptr in the frame list*/
LocateNewFrame(float XPosition)103 Frame * MoleculeData::LocateNewFrame(float XPosition)
104 {	Frame * lNewFrame;
105 
106 	Frame * lFrame = cFrame;
107 	if (XPosition < lFrame->time) {
108 		while (XPosition < lFrame->time) {
109 			if (lFrame->PreviousFrame)
110 				lFrame = lFrame->PreviousFrame;
111 			else
112 				break;
113 		}
114 		if (lFrame->time < XPosition) lFrame = lFrame->NextFrame;
115 		if ((lFrame->time-XPosition) < 1.0E-6) return NULL; /*duplicate point*/
116 		lNewFrame = new Frame(MolWin);
117 		if (lNewFrame == NULL) throw MemoryError();
118 		lNewFrame->PreviousFrame = lFrame->PreviousFrame;
119 		lFrame->PreviousFrame = lNewFrame;
120 		lNewFrame->NextFrame = lFrame;
121 		if (lNewFrame->PreviousFrame) lNewFrame->PreviousFrame->NextFrame = lNewFrame;
122 	} else {
123 		while (XPosition > lFrame->time) {
124 			if (lFrame->NextFrame)
125 				lFrame = lFrame->NextFrame;
126 			else
127 				break;
128 		}
129 		if (lFrame->time > XPosition) lFrame = lFrame->PreviousFrame;
130 		if ((XPosition-lFrame->time) < 1.0E-6) return NULL; /*duplicate point*/
131 		lNewFrame = new Frame(MolWin);
132 		if (lNewFrame == NULL) throw MemoryError();
133 		lNewFrame->NextFrame = lFrame->NextFrame;
134 		lFrame->NextFrame = lNewFrame;
135 		lNewFrame->PreviousFrame = lFrame;
136 		if (lNewFrame->NextFrame) lNewFrame->NextFrame->PreviousFrame = lNewFrame;
137 	}
138 	if (lNewFrame->PreviousFrame == NULL) Frames = lNewFrame;
139 	cFrame = lNewFrame;
140 	lNewFrame->time = XPosition;
141 	lFrame = Frames;
142 	for (CurrentFrame=1; lFrame!=lNewFrame; CurrentFrame++)
143 		lFrame = lFrame->NextFrame;
144 	NumFrames ++;
145 	return lNewFrame;
146 } /*LocateNewFrame*/
147 
AddFrame(long NumAtoms,long NumBonds)148 Frame * MoleculeData::AddFrame(long NumAtoms, long NumBonds) {
149 	Frame * temp = new Frame(MolWin);
150 	if (!temp) throw MemoryError();
151 	if (cFrame->NextFrame) {
152 		cFrame->NextFrame->PreviousFrame = temp;
153 		temp->NextFrame = cFrame->NextFrame;
154 	}
155 	temp->PreviousFrame = cFrame;
156 	cFrame->NextFrame = temp;
157 	cFrame = temp;
158 
159 	if (!SetupFrameMemory(NumAtoms, NumBonds)) throw MemoryError();
160 	NumFrames++;
161 	CurrentFrame++;
162 	return temp;
163 }
164 
DeleteFrame(void)165 void MoleculeData::DeleteFrame(void) {
166 	if (!cFrame->NextFrame && !cFrame->PreviousFrame) return;
167 	Frame * temp = cFrame;
168 	if (cFrame->NextFrame) {
169 		cFrame->NextFrame->PreviousFrame = cFrame->PreviousFrame;
170 		if (cFrame->PreviousFrame)
171 			cFrame->PreviousFrame->NextFrame = cFrame->NextFrame;
172 		cFrame = cFrame->NextFrame;
173 	} else {
174 		cFrame->PreviousFrame->NextFrame=NULL;
175 		cFrame = cFrame->PreviousFrame;
176 		CurrentFrame--;
177 	}
178 	if (temp == Frames) Frames = temp->NextFrame;
179 	delete temp;
180 	NumFrames--;
181 }
GetRotationMatrix(Matrix4D copy)182 void MoleculeData::GetRotationMatrix(Matrix4D copy) {
183 	CopyMatrix(TotalRotation, copy);
184 }
185 
GetRotationMatrix() const186 const Matrix4D& MoleculeData::GetRotationMatrix() const {
187 	return TotalRotation;
188 }
189 
190 #include <iostream>
191 //routine calculates the cartesian center of the molecule and offsets the
192 //coordinate system of the window to match the moleculer center.
CenterModelWindow(void)193 void MoleculeData::CenterModelWindow(void) {
194 	float	XMin, XMax, YMin, YMax, ZMin, ZMax;
195 //first set the Min and Max to big and small values
196 	XMin=YMin=ZMin=1.0e10;
197 	XMax=YMax=ZMax=-1.0e10;
198 
199 //Start at the beginning of the frame list so that the center for the entire
200 //frame list is generated.
201 	Frame *	lFrame = Frames;
202 //Now find the min and max in each direction
203 	while (lFrame) {
204 		for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
205 			XMin = MIN(XMin, lFrame->Atoms[iatom].Position.x);
206 			XMax = MAX(XMax, lFrame->Atoms[iatom].Position.x);
207 			YMin = MIN(YMin, lFrame->Atoms[iatom].Position.y);
208 			YMax = MAX(YMax, lFrame->Atoms[iatom].Position.y);
209 			ZMin = MIN(ZMin, lFrame->Atoms[iatom].Position.z);
210 			ZMax = MAX(ZMax, lFrame->Atoms[iatom].Position.z);
211 		}
212 		lFrame = lFrame->GetNextFrame();
213 	}
214 //The center is now just half of the min plus the max
215 	Centroid.x = (XMax+XMin)*0.5f;
216 	Centroid.y = (YMax+YMin)*0.5f;
217 	Centroid.z = (ZMax+ZMin)*0.5f;
218 //Recompute the maximum width of the molecule
219 	MaxSize = MAX((XMax-XMin), (YMax-YMin));
220 	MaxSize = MAX(MaxSize, (ZMax-ZMin));
221 	MolCentroid = Centroid;
222 
223 }	/*CenterModelWindow*/
224 
ResetRotation(void)225 void MoleculeData::ResetRotation(void) {
226 	if ((cFrame->NumAtoms > MaxAtoms)||(RotCoords==NULL)) {
227 		if (RotCoords != NULL) {
228 			delete [] RotCoords;
229 			RotCoords = NULL;
230 		}
231 		RotCoords = new CPoint3D[cFrame->NumAtoms];
232 		MaxAtoms = cFrame->NumAtoms;
233 	}
234 	for (long i=0; i<cFrame->NumAtoms; i++) {
235 		Rotate3DPt(TotalRotation, (cFrame->Atoms[i].Position), &(RotCoords[i]));
236 	}
237 		//Now sort the Z Buffer
238 //	SortzBuffer(RotCoords, zBuffer, cFrame->NumAtoms);
239 }
InitializeInternals(void)240 void MoleculeData::InitializeInternals(void) {
241 	if (!IntCoords) IntCoords = new Internals;
242 }
243 // Stick the coordinates by moving the rotated coordinates to the file coordinates
244 // being careful to invalidate any orbital info
StickCoordinates(void)245 void MoleculeData::StickCoordinates(void) {
246 	//Clean up the rotation matrix before making permenant changes
247 	OrthogonalizeRotationMatrix(TotalRotation);
248 	if (cFrame->Orbs.size() > 0) {	// rotate any MO vectors and update the surface list
249 		std::vector<OrbitalRec *>::const_iterator OrbSet = cFrame->Orbs.begin();
250 		while (OrbSet != cFrame->Orbs.end()) {
251 			(*OrbSet)->RotateVectors(TotalRotation, Basis, cFrame->NumAtoms);
252 			OrbSet++;
253 		}
254 			Surface * lSurface = cFrame->SurfaceList;
255 		while (lSurface) {
256 			lSurface->RotateSurface(TotalRotation);
257 			lSurface = lSurface->GetNextSurface();
258 		}
259 	}
260 		// rotate any frequencies
261 	if (cFrame->Vibs) {
262 		std::vector<CPoint3D> & Modes = cFrame->Vibs->NormMode;
263 		CPoint3D NMode;
264 		for (long imode=0; imode < cFrame->Vibs->NumModes; imode++) {
265 			long cmode = imode*cFrame->NumAtoms;
266 			for (long iatom=0; iatom<cFrame->NumAtoms; iatom++) {
267 				Rotate3DOffset(TotalRotation, Modes[iatom+cmode], &NMode);
268 				Modes[iatom+cmode] = NMode;
269 			}
270 		}
271 	}
272 		long i;
273 	for (i=0; i<cFrame->NumAtoms; i++) {
274 		Rotate3DPt(TotalRotation, cFrame->Atoms[i].Position - Centroid,
275 				   &(RotCoords[i]));
276 	}
277 	for (i=0; i<cFrame->NumAtoms; i++) {
278 		cFrame->Atoms[i].Position = RotCoords[i];
279 	}
280 	Centroid = CPoint3D(0.0f, 0.0f, 0.0f);
281 	InitRotationMatrix(TotalRotation);
282 }
283 
NewAtom(const mpAtom & atom,bool updateGlobal,long index,const CPoint3D * pos)284 void MoleculeData::NewAtom(const mpAtom& atom, bool updateGlobal, long index, const CPoint3D *pos) {
285 	cFrame->AddAtom(atom, index, pos);
286 
287 	// Adjust annotations that connect higher-numbered atoms.
288 	if (index >= 0 && index < cFrame->NumAtoms - 1) {
289 		std::vector<Annotation *>::iterator anno;
290 		for (anno = Annotations.begin(); anno != Annotations.end(); ++anno) {
291 			(*anno)->adjustIds(index - 1, 1);
292 		}
293 	}
294 
295 	if (IntCoords) {
296 		MOPacInternals * mInts = IntCoords->GetMOPacStyle();
297 		if (mInts) mInts->AppendAtom(this);
298 	}
299 	if (updateGlobal) AtomAdded();
300 }
301 
NewAtom(long AtomType,const CPoint3D & AtomPosition,bool updateGlobal,long index)302 void MoleculeData::NewAtom(long AtomType, const CPoint3D & AtomPosition, bool updateGlobal, long index) {
303 	cFrame->AddAtom(AtomType, AtomPosition, index);
304 
305 	// Adjust annotations that connect higher-numbered atoms.
306 	if (index >= 0 && index < cFrame->NumAtoms - 1) {
307 		std::vector<Annotation *>::iterator anno;
308 		for (anno = Annotations.begin(); anno != Annotations.end(); ++anno) {
309 			(*anno)->adjustIds(index - 1, 1);
310 		}
311 	}
312 
313 	if (IntCoords) {
314 		MOPacInternals * mInts = IntCoords->GetMOPacStyle();
315 		if (mInts) mInts->AppendAtom(this);
316 	}
317 	if (updateGlobal) AtomAdded();
318 }
319 
AtomAdded(void)320 void MoleculeData::AtomAdded(void) {
321 	if (cFrame->AtomAllocation>MaxAtoms) {
322 		if (RotCoords) delete [] RotCoords;
323 		RotCoords = new CPoint3D[cFrame->AtomAllocation];
324 		if (!RotCoords) throw MemoryError();
325 		MaxAtoms = cFrame->AtomAllocation;
326 	}
327 	float	XMin, XMax, YMin, YMax, ZMin, ZMax;
328 	//first set the Min and Max to big and small values
329 	XMin=YMin=ZMin=1.0E10;
330 	XMax=YMax=ZMax=-1.0E10;
331 
332 	for (long iatom=0; iatom<cFrame->NumAtoms; iatom++) {
333 		XMin = MIN(XMin, cFrame->Atoms[iatom].Position.x);
334 		XMax = MAX(XMax, cFrame->Atoms[iatom].Position.x);
335 		YMin = MIN(YMin, cFrame->Atoms[iatom].Position.y);
336 		YMax = MAX(YMax, cFrame->Atoms[iatom].Position.y);
337 		ZMin = MIN(ZMin, cFrame->Atoms[iatom].Position.z);
338 		ZMax = MAX(ZMax, cFrame->Atoms[iatom].Position.z);
339 	}
340 	//Recompute the maximum width of the molecule
341 	MaxSize = MAX(MaxSize, (XMax-XMin));
342 	MaxSize = MAX(MaxSize, (YMax-YMin));
343 	MaxSize = MAX(MaxSize, (ZMax-ZMin));
344 
345 	while (FMOFragmentIds.size() < cFrame->GetNumAtoms())
346 		FMOFragmentIds.push_back(1);	//initialize the fragment id for the new atoms
347 
348 	ResetRotation();
349 }
SetDescription(char * NewLabel)350 void MoleculeData::SetDescription(char * NewLabel) {
351 	if (Description) delete [] Description;
352 	long LineLength = strlen(NewLabel);
353 	Description = new char[LineLength+1];
354 	if (Description) strcpy(Description, NewLabel);
355 }
InvertMode(void)356 void MoleculeData::InvertMode(void) {
357 	if (!cFrame->Vibs) return;
358 
359 	CPoint3D * Freq = &(cFrame->Vibs->NormMode[cFrame->NumAtoms*cFrame->Vibs->CurrentMode]);
360 	for (long iatm=0; iatm<cFrame->NumAtoms; iatm++) {
361 		Freq[iatm] *= -1.0;
362 	}
363 }	/*InvertMode*/
UnitConversion(bool AtoB)364 void MoleculeData::UnitConversion(bool AtoB) {
365 	float		factor;
366 //Choose the correct factor
367 	if (AtoB) factor = kBohr2AngConversion;
368 	else factor = kAng2BohrConversion;
369 
370 	Frame * lFrame = Frames;
371 //Now scale the permenant set of coordinates (for all frames)
372 	while (lFrame) {
373 		for (long iatm=0; iatm < lFrame->NumAtoms; iatm++) {
374 			lFrame->Atoms[iatm].Position *= factor;
375 		}
376 		lFrame = lFrame->NextFrame;
377 	}
378 	TotalRotation[3][0] *= factor;	//scale the origin offset so things stay centered
379 	TotalRotation[3][1] *= factor;
380 	TotalRotation[3][2] *= factor;
381 //Finally update the window scaling sizes so things are still drawn the same
382 	MaxSize *= factor;
383 	WindowSize *= factor;
384 } /*UnitConversion*/
385 //Rotate the molecule by 180 degrees about either the horizontal or vertical axis
386 //Pass in 0 to flip about the horizontal axis, 1 for vertical
FlipRotation(short theItem)387 void MoleculeData::FlipRotation(short theItem) {
388 	wxPoint			p(0,0), q(10,10), sphereCenter(10,10);
389 	long			sphereRadius=10;
390 	Matrix4D		rotationMatrix, tempcopyMatrix;
391 
392 //180 degree rotation is desired, but if the two vectors are colinear then the
393 //rotation will not have a well defined axis so use two 90 degree rotations instead
394 	if (theItem == 0) {	//flipHitem
395 		p.y = 10;
396 	} else {	//theItem == 1 (flipVitem)
397 		p.x = 10;
398 	}
399 	VirtualSphereQD3D (p, q, sphereCenter, sphereRadius, rotationMatrix, TotalRotation);
400 	float *m = (float *) rotationMatrix;
401 	std::cout << "m[#](# in 0,16): " << std::endl
402 		<< m[0] << ", " << m[1] << ", " << m[2] << ", " << m[3] << ", " <<
403 										   std::endl
404 		<< m[4] << ", " << m[5] << ", " << m[6] << ", " << m[7] << ", " <<
405 										   std::endl
406 		<< m[8] << ", " << m[9] << ", " << m[10] << ", " << m[11] << ", " <<
407 										   std::endl
408 		<< m[12] << ", " << m[13] << ", " << m[14] << ", " << m[15] << std::endl;
409 
410 /* Concatenate the new rotation with the current rotation */
411 //Mulitply twice since the rotate generated is 90 degrees and we want 180
412 	MultiplyMatrix (rotationMatrix, TotalRotation, tempcopyMatrix);
413 	CopyMatrix (tempcopyMatrix, TotalRotation);
414 	MultiplyMatrix (rotationMatrix, TotalRotation, tempcopyMatrix);
415 	CopyMatrix (tempcopyMatrix, TotalRotation);
416 }	/*FlipRotation*/
417 // Sets the plane of the screen to that defined by the three points provided
SetScreenPlane(CPoint3D * Points)418 bool MoleculeData::SetScreenPlane(CPoint3D *Points) {
419 	CPoint3D	Vector1, Vector2, Vector3;
420 
421 	Vector1.x = Points[1].x - Points[0].x;
422 	Vector1.y = Points[1].y - Points[0].y;
423 	Vector1.z = Points[1].z - Points[0].z;
424 	Vector2.x = Points[2].x - Points[0].x;
425 	Vector2.y = Points[2].y - Points[0].y;
426 	Vector2.z = Points[2].z - Points[0].z;
427 
428 	float length = Vector1.Magnitude();
429 	if (length <= 0.0) return false;	//2 points with the same coordinates
430 	Vector1.x /= length;
431 	Vector1.y /= length;
432 	Vector1.z /= length;
433 	length = Vector2.Magnitude();
434 	if (length <= 0.0) return false;
435 	Vector2.x /= length;
436 	Vector2.y /= length;
437 	Vector2.z /= length;
438 	float V1DotV2 = DotProduct3D(&Vector2, &Vector1);
439 		//Make sure the vectors are not parallel or anti-parallel
440 	if (fabs(V1DotV2) >= 1.0) return false;
441 	Vector2.x -= V1DotV2*Vector1.x;
442 	Vector2.y -= V1DotV2*Vector1.y;
443 	Vector2.z -= V1DotV2*Vector1.z;
444 	Normalize3D(&Vector2);
445 	CrossProduct3D (&Vector1, &Vector2, &Vector3);
446 	Normalize3D(&Vector3);
447 
448 	TotalRotation[0][0] = Vector1.x;
449 	TotalRotation[1][0] = Vector1.y;
450 	TotalRotation[2][0] = Vector1.z;
451 	TotalRotation[0][3] = 0.0;
452 	TotalRotation[0][1] = Vector2.x;
453 	TotalRotation[1][1] = Vector2.y;
454 	TotalRotation[2][1] = Vector2.z;
455 	TotalRotation[1][3] = 0.0;
456 	TotalRotation[0][2] = Vector3.x;
457 	TotalRotation[1][2] = Vector3.y;
458 	TotalRotation[2][2] = Vector3.z;
459 	TotalRotation[2][3] = TotalRotation[3][0] = TotalRotation[3][1] = TotalRotation[3][2] = 0.0;
460 	TotalRotation[3][3] = 1.0;
461 		//Rotation the first Point and use for the translation
462 	/* Rotate3DPt(TotalRotation, Points[0], &Vector1); */
463 	/* Centroid = Vector1 * -1.0f; */
464 
465 	ResetRotation();
466 	return true;
467 }
468 //Linear Least Squares fit:  minimize the sum of squares of the differences between each
469 //frame. This will "fix" a series of frames to give a nice, smooth animation.
LinearLeastSquaresFit(Progress * lProgress)470 void MoleculeData::LinearLeastSquaresFit(Progress * lProgress) {
471 	long		MinFrameNum=CurrentFrame;
472 
473 	Frame * lFrame = Frames;
474 	Frame * l2Frame = lFrame->NextFrame;
475 	lProgress->ChangeText("Minimizing frame motions...");
476 
477 //First run through the frame list and make sure that each frame has the same number
478 //and order for the atoms. If not this routine will not work correctly!
479 	while (l2Frame) {
480 		if (lFrame->NumAtoms != l2Frame->NumAtoms) goto FrameIncorrect;
481 		for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
482 			if (lFrame->Atoms[iatom].Type != l2Frame->Atoms[iatom].Type) goto FrameIncorrect;
483 		}
484 		l2Frame = l2Frame->NextFrame;
485 	}
486 //Ok set of frames so get started fitting
487 	l2Frame = lFrame->NextFrame;
488 	Matrix4D	FitMatrix, TempRotMat;
489 	float		SquaresValue, NewSquareValue, RotAngle, TransAmount;
490 	long		iOptAtoms;
491 		//Save the current frame and rotation
492 	Frame * SavedFrame;
493 	SavedFrame = cFrame;
494 	Matrix4D SavedRotation;
495 	::CopyMatrix(TotalRotation, SavedRotation);
496 	while (l2Frame) {
497 		InitRotationMatrix(FitMatrix);		//zero out the fit matrix to start with...
498 //Now move the first atom of the second frame to the same position as the first frame.
499 //This is done by first moving it to the origin, then putting a translation in the rotation
500 //matrix to the desired location.
501 		FitMatrix[3][0] = -l2Frame->Atoms[0].Position.x;
502 		FitMatrix[3][1] = -l2Frame->Atoms[0].Position.y;
503 		FitMatrix[3][2] = -l2Frame->Atoms[0].Position.z;
504 		for (long iatom=0; iatom<lFrame->NumAtoms; iatom++) {
505 			l2Frame->Atoms[iatom].Position.x += FitMatrix[3][0];
506 			l2Frame->Atoms[iatom].Position.y += FitMatrix[3][1];
507 			l2Frame->Atoms[iatom].Position.z += FitMatrix[3][2];
508 		}
509 		FitMatrix[3][0] = lFrame->Atoms[0].Position.x;
510 		FitMatrix[3][1] = lFrame->Atoms[0].Position.y;
511 		FitMatrix[3][2] = lFrame->Atoms[0].Position.z;
512 		bool	Done;
513 		for (long ipass=0; ipass<4; ipass++) {
514 			if (ipass<3) {
515 				RotAngle = 10.0f;	TransAmount=0.1f;
516 				if (ipass == 0) iOptAtoms = MIN(2, lFrame->NumAtoms);
517 				else if (ipass == 1) iOptAtoms = MIN(3, lFrame->NumAtoms);
518 				else iOptAtoms = lFrame->NumAtoms;
519 				for (int ii=0; ii<iOptAtoms; ii++)
520 					Rotate3DPt(FitMatrix, l2Frame->Atoms[ii].Position, &(RotCoords[ii]));
521 				SquaresValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
522 			}
523 			Done = false;
524 			while (!Done) {
525 				Done = true;
526 				bool RotDone = false;
527 				for (long jpass=0; jpass<2; jpass++) {
528 					while (!RotDone) {
529 						RotDone = true;
530 						::CopyMatrix (FitMatrix, TempRotMat);
531 						for (long ii=0; ii<3; ii++) {
532 							ApplyRotation(TempRotMat, ii, RotAngle);
533 							for (int i=0; i<iOptAtoms; i++)
534 								Rotate3DPt(TempRotMat, l2Frame->Atoms[i].Position, &(RotCoords[i]));
535 							NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
536 							if (NewSquareValue<SquaresValue) {
537 								RotDone = Done = false;
538 								SquaresValue = NewSquareValue;
539 								CopyMatrix (TempRotMat, FitMatrix);
540 							} else {
541 								ApplyRotation(TempRotMat, ii, -2.0f*RotAngle);
542 								for (int i=0; i<iOptAtoms; i++)
543 									Rotate3DPt(TempRotMat, l2Frame->Atoms[i].Position, &(RotCoords[i]));
544 								NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
545 								if (NewSquareValue<SquaresValue) {
546 									RotDone = Done = false;
547 									SquaresValue = NewSquareValue;
548 									CopyMatrix (TempRotMat, FitMatrix);
549 								} else {
550 									CopyMatrix (FitMatrix, TempRotMat);
551 								}
552 							}
553 						}
554 		//"clean up" the rotation matrix make the rotation part orthogonal and magnitude 1
555 						OrthogonalizeRotationMatrix (FitMatrix);
556 					}
557 					RotAngle *= 0.1;
558 				}
559 				if (ipass > 2) {	//Only rotate for the first two passes
560 					bool TransDone = false;
561 					while (!TransDone) {
562 						TransDone = true;
563 						for (long ii=0; ii<3; ii++) {
564 							FitMatrix[3][ii] += TransAmount;
565 							for (int i=0; i<iOptAtoms; i++)
566 								Rotate3DPt(FitMatrix, l2Frame->Atoms[i].Position, &(RotCoords[i]));
567 							NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
568 							if (NewSquareValue<SquaresValue) {
569 								TransDone = Done = false;
570 								SquaresValue = NewSquareValue;
571 							} else {
572 								FitMatrix[3][ii] -= 2.0*TransAmount;
573 								for (int i=0; i<iOptAtoms; i++)
574 									Rotate3DPt(FitMatrix, l2Frame->Atoms[i].Position, &(RotCoords[i]));
575 								NewSquareValue = CalculateSquaresValue(iOptAtoms, lFrame->Atoms, RotCoords);
576 								if (NewSquareValue<SquaresValue) {
577 									TransDone = Done = false;
578 									SquaresValue = NewSquareValue;
579 								} else {
580 									FitMatrix[3][ii] += TransAmount;
581 								}
582 							}
583 						}
584 						if (!lProgress->UpdateProgress(MinFrameNum/(NumFrames-CurrentFrame)))
585 							goto CleanUpAndExit;
586 					}
587 				}
588 			}
589 			TransAmount *= 0.1;
590 		}
591 //Done with this set of frames. Call StickCoordinates to copy over the coords, and
592 //update any MO vectors, surfaces, and normal mode information.
593 		cFrame = l2Frame;
594 		::CopyMatrix(FitMatrix, TotalRotation);
595 		StickCoordinates();
596 
597 		lFrame = l2Frame;
598 		l2Frame = l2Frame->NextFrame;
599 		MinFrameNum++;
600 	}
601 CleanUpAndExit:		//Clean up the RotCoords Array...
602 	cFrame = SavedFrame;
603 	::CopyMatrix(SavedRotation, TotalRotation);
604 	ResetRotation();
605 	return;	//Correct return
606 FrameIncorrect:		//Incorrect frame list for this routine: throw up an error and exit
607 	MessageAlert("Sorry each frame must have the same number and order of atoms in order to fit an animation path.");
608 }	/*LinearLeastSquaresFit*/
OrbSurfacePossible(void) const609 bool MoleculeData::OrbSurfacePossible(void) const {
610 	bool result = false;
611 	if (Basis) result = true;
612 	return result;
613 }
TotalDensityPossible(void) const614 bool MoleculeData::TotalDensityPossible(void) const {
615 	bool result = false;
616 	if (Basis) {
617 		if (cFrame->Orbs.size() > 0) {//Currently TE density is based on eigenvectors only
618 			std::vector<OrbitalRec *>::const_iterator OrbSet = cFrame->Orbs.begin();
619 			while (OrbSet != cFrame->Orbs.end()) {
620 				if ((*OrbSet)->TotalDensityPossible()) {	//Is this a good enough test?
621 					result = true;
622 					break;
623 				}
624 				OrbSet++;
625 			}
626 		}
627 	}
628 	return result;
629 }
GradientVectorAvailable(void) const630 bool MoleculeData::GradientVectorAvailable(void) const {return cFrame->GradientVectorAvailable();};
MEPCalculationPossible(void) const631 bool MoleculeData::MEPCalculationPossible(void) const {
632 	bool result = false;
633 	if (Basis) {
634 		result = Basis->AreNuclearChargesValid();
635 		if (result) result = TotalDensityPossible();
636 	}
637 	return result;
638 }
639 //Read in a general basis set from a GAMESS log file
ParseGAMESSBasisSet(BufferFile * Buffer)640 void MoleculeData::ParseGAMESSBasisSet(BufferFile * Buffer) {
641 
642 	Basis = BasisSet::ParseGAMESSBasisSet(Buffer, cFrame->NumAtoms, cFrame->Atoms);
643 	if (Basis) {	//Setup the default nuclear charge array. This will be changed if
644 					//ECP's are used.
645 			long iatom;
646 		for (iatom=0; iatom<cFrame->NumAtoms; iatom++) {
647 			Basis->NuclearCharge[iatom] = cFrame->Atoms[iatom].GetNuclearCharge();
648 			if (Basis->NuclearCharge[iatom] == 115)	//dummy atom/no charge
649 				Basis->NuclearCharge[iatom] = 0;
650 		}
651 		Basis->NuclearChargesAreValid(true);
652 	}
653 }	/*ParseGAMESSBasisSet*/
ParseFMOIds(BufferFile * Buffer,const long & AtomCount,const wxFileOffset & EndOfGroup)654 bool MoleculeData::ParseFMOIds(BufferFile * Buffer, const long & AtomCount, const wxFileOffset & EndOfGroup) {
655 	if (Buffer->LocateKeyWord("INDAT", 5, EndOfGroup)) {
656 		Buffer->LocateKeyWord("=", 1, EndOfGroup);
657 		Buffer->BufferSkip(1);
658 
659 		//Prep the fragment mapping array so we can detect
660 		if (FMOFragmentIds.size() > 0) {
661 			//hmmm this array is already setup? must be an error?? Consider this as non-fatal
662 			wxLogMessage(_("Starting to parse INDAT array, but already have existing data!"));
663 		}
664 		FMOFragmentIds.reserve(AtomCount);
665 		for (long i=0; i<AtomCount; i++) FMOFragmentIds.push_back(0);
666 
667 		// read in the ids here
668 		//This array comes in two forms, but either way it specifes how atoms are divided into fragments
669 		long tempL;
670 		if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
671 			wxLogMessage(_("Error: Unable to locate the first integer for the INDAT array. Skipping Fragment Ids."));
672 			return false;
673 		}
674 
675 		if (tempL == 0) { //Style b, atoms grouped by fragment
676 			InputOptions->FMO.UseFragmentINDAT(true);
677 			for (long ifragment=0; ifragment<InputOptions->FMO.GetNumberFragments(); ifragment++) {
678 				long lastatom=0;
679 				do {
680 					if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
681 						wxString msg;
682 						msg.Printf(_("Error: Unable to parse atom Id for fragment %d. Skipping the remainder of the array."),
683 								   ifragment+1);
684 						wxLogMessage(msg);
685 						return false;
686 					}
687 					if ((tempL > 0)&&(tempL<=AtomCount)) {
688 						FMOFragmentIds[tempL-1] = ifragment+1;
689 						lastatom = tempL;
690 					} else if (tempL<0) {
691 						if (lastatom==0) {
692 							wxString msg;
693 							msg.Printf(_("Error: Encountered %d not preceeded by a valid atom id. Skipping rest of INDAT."),
694 									   tempL);
695 							wxLogMessage(msg);
696 							return false;
697 						}
698 						tempL *= -1;
699 						if ((tempL <= (lastatom))||(tempL > AtomCount)) {
700 							wxString msg;
701 							msg.Printf(_("Error: Encountered %d preceeded by %d. Skipping invalid data."),
702 									   -1*tempL, lastatom);
703 							wxLogMessage(msg);
704 						} else {
705 							for (long jatom=lastatom; jatom < tempL; jatom++) {
706 								FMOFragmentIds[jatom] = ifragment+1;
707 							}
708 						}
709 					}
710 				} while (tempL > 0);
711 			}
712 		} else {	//Style a, array of fragment ids, one per atom
713 			//read in each id, test it, and store it
714 			InputOptions->FMO.UseFragmentINDAT(false);
715 
716 			for (long iatm=0; iatm<AtomCount; iatm++) {
717 				if (iatm>0) {	//skip the 1st pass since we already have it from above
718 					if (!Buffer->ReadLongValue(tempL, EndOfGroup)) {
719 						wxString msg;
720 						msg.Printf(_("Error: Unable to parse fragment Id for atom %d. Skipping the remainder of the array."),
721 								   iatm+1);
722 						wxLogMessage(msg);
723 						return false;
724 					}
725 				}
726 				if ((tempL < 1)||(tempL > InputOptions->FMO.GetNumberFragments())) {
727 					wxString msg;
728 					msg.Printf(_("Warning: Invalid fragment id assigned to atom %d, value %d."), iatm+1, tempL);
729 					wxLogMessage(msg);
730 				} else
731 					FMOFragmentIds[iatm] = tempL;
732 			}
733 		}
734 
735 		// Now check the array to see if all atoms were assigned to a fragment
736 		for (int i=0; i<FMOFragmentIds.size(); i++) {
737 			if (FMOFragmentIds[i] < 1) {
738 				wxString msg;
739 				msg.Printf(_("Warning: Atom %d is not assigned to a fragment."), i);
740 				wxLogMessage(msg);
741 			}
742 		}
743 	}
744 	return true;
745 }
746 //Parse a gaussian style zmatrix as would be formated in GAMESS input
ParseZMatrix(BufferFile * Buffer,const long & nAtoms,WinPrefs * Prefs)747 void MoleculeData::ParseZMatrix(BufferFile * Buffer, const long & nAtoms, WinPrefs * Prefs) {
748 	if (!IntCoords) {
749 		IntCoords = new Internals;
750 	}
751 	MOPacInternals * mInts = IntCoords->GetMOPacStyle();
752 	if (!mInts) {
753 		IntCoords->CreateMOPacInternals(3*nAtoms);
754 		mInts = IntCoords->GetMOPacStyle();
755 	}
756 	float unitConversion = 1.0;
757 	if (InputOptions && InputOptions->Data->GetUnits()) unitConversion = kBohr2AngConversion;
758 	int iline=0;
759 	long StartPos = Buffer->GetFilePos();
760 	Buffer->LocateKeyWord("$END", 4);
761 	long EndPos = Buffer->GetFilePos();
762 	Buffer->SetFilePos(StartPos);
763 	//First jump past the atoms and build a map of any string=value pairs (if any)
764 	std::map<std::string, double> valueMap;
765 	long bPos = Buffer->FindBlankLine();
766 	if ((bPos > 0)&&(bPos < EndPos)) {
767 		Buffer->SetFilePos(bPos);
768 		Buffer->SkipnLines(1);
769 		while (Buffer->GetFilePos() < EndPos) {
770 			char		token[kMaxLineLength], Line[kMaxLineLength];
771 			double		value;
772 			Buffer->GetLine(Line);
773 			char * last=NULL;
774 			int foo=0;	//Since VS lacks strtok_r we'll make do with this
775 			while ((Line[foo] != '\0')&&(foo < kMaxLineLength)) {
776 				if (Line[foo] == '=') {
777 					Line[foo]='\0';
778 					last = &(Line[foo+1]);
779 					break;
780 				}
781 				foo++;
782 			}
783 		//	if (strtok_r(Line, "=", &last) == NULL) break;
784 			if (last == NULL) break;
785 			int readCount = sscanf(Line, "%s", token);
786 			readCount += sscanf(last, "%lf", &value);
787 			if (readCount!=2) break;
788 			std::pair<std::string, double> myVal(token, value);
789 			std::pair<std::map<std::string, double>::iterator, bool> p = valueMap.insert(myVal);
790 			if (! p.second) {	//We have hit a duplicate value
791 				MessageAlert("Duplicate keys detected in the value list");
792 			}
793 		}
794 		Buffer->SetFilePos(StartPos);
795 	}
796 
797 	while ((Buffer->GetFilePos() < EndPos)&&(iline<nAtoms)) {
798 		CPoint3D	pos = CPoint3D(0.0f, 0.0f, 0.0f);	//This is just a placeholder
799 		char		token[kMaxLineLength], Line[kMaxLineLength], bondText[kMaxLineLength],
800 					angleText[kMaxLineLength], dihedralText[kMaxLineLength];
801 		float		bondLength, bondAngle, bondDihedral;
802 		long		AtomType;
803 		int			con1, con2, con3;
804 		Buffer->GetLine(Line);
805 		int readCount = sscanf(Line, "%255s %d %255s %d %255s %d %255s", token, &con1, bondText, &con2, angleText, &con3,
806 							   dihedralText);
807 		if (readCount < 1) break;	//failed to parse anything??
808 		AtomType = SetAtomType((unsigned char *) token);
809 		if (AtomType < 0) break;
810 		cFrame->AddAtom(AtomType, pos);
811 		if (iline > 0) {
812 			if (readCount < 2) break;
813 			if (sscanf(bondText, "%f", &bondLength) != 1) {
814 				//attempt to lookup the value in the map
815 				char * s = bondText;
816 				double flip = 1.0;
817 				if (bondText[0] == '-') {
818 					s = &(bondText[1]);
819 					flip = -1.0;
820 				}
821 				std::map<std::string, double>::iterator p = valueMap.find(s);
822 				if (p != valueMap.end()) {
823 					bondLength = flip * (*p).second;
824 				} else break;	//missing key value
825 			}
826 			if (iline >= 2) {
827 				if (readCount < 4) break;
828 				if (sscanf(angleText, "%f", &bondAngle) != 1) {
829 					//attempt to lookup the value in the map
830 					char * s = angleText;
831 					double flip = 1.0;
832 					if (angleText[0] == '-') {
833 						s = &(angleText[1]);
834 						flip = -1.0;
835 					}
836 					std::map<std::string, double>::iterator p = valueMap.find(s);
837 					if (p != valueMap.end()) {
838 						bondAngle = flip * (*p).second;
839 					} else break;	//missing key value
840 				}
841 				if (iline >= 3) {
842 					if (readCount < 6) break;
843 					if (sscanf(dihedralText, "%f", &bondDihedral) != 1) {
844 						//attempt to lookup the value in the map
845 						char * s = dihedralText;
846 						double flip = 1.0;
847 						if (dihedralText[0] == '-') {
848 							s = &(dihedralText[1]);
849 							flip = -1.0;
850 						}
851 						std::map<std::string, double>::iterator p = valueMap.find(s);
852 						if (p != valueMap.end()) {
853 							bondDihedral = flip * (*p).second;
854 						} else break;	//missing key value
855 					}
856 				}
857 			}
858 			if (bondLength < 0.0) break;
859 			con1--;
860 			con2--;
861 			con3--;
862 			if (con1 >= iline) break;
863 			mInts->AddInternalCoordinate(iline, con1, 0, bondLength*unitConversion);
864 			if (iline > 1) {
865 				if (con2 >= iline) break;
866 				mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
867 				if (iline > 2) {
868 					if (con3 >= iline) break;
869 					mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
870 				}
871 			}
872 		}
873 		iline++;
874 	}
875 	//if we punted after the AddAtom call delete off the atom without internal coordinate information
876 	if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
877 	//Now convert the set of internals into cartesians
878 	mInts->InternalsToCartesians(this, Prefs, 0);
879 }
ParseGAMESSUKZMatrix(BufferFile * Buffer,WinPrefs * Prefs)880 void MoleculeData::ParseGAMESSUKZMatrix(BufferFile * Buffer, WinPrefs * Prefs) {
881 	//In order to support concatinated sets of coords we will use a local set of internals if a preexisting
882 	//set is present
883 	//First determine the potential number of atoms
884 	char	Line[kMaxLineLength];
885 	Buffer->GetLine(Line);
886 	if (FindKeyWord(Line, "ZMAT", 4) < 0) return;	//Not positioned at the start of the zmatrix block
887 	float	unitConversion = kBohr2AngConversion;	//The default units are AU
888 	if (FindKeyWord(Line, "ANGS", 4) > 0) unitConversion = 1.0;
889 	long BlockStart = Buffer->GetFilePos();
890 	if (! Buffer->LocateKeyWord("END", 3, -1)) return;	//end of block marker not found
891 	long EndPos = Buffer->GetFilePos();
892 	Buffer->SetFilePos(BlockStart);
893 	long LastAtomPos = EndPos;
894 	if (Buffer->LocateKeyWord("VARIABLES", 9, EndPos)) {
895 		LastAtomPos = Buffer->GetFilePos();
896 		Buffer->SetFilePos(BlockStart);
897 	}
898 	if (Buffer->LocateKeyWord("CONSTANTS", 9, EndPos)) {
899 		if (Buffer->GetFilePos() < LastAtomPos)
900 			LastAtomPos = Buffer->GetFilePos();
901 		Buffer->SetFilePos(BlockStart);
902 	}
903 	long atmCount = Buffer->GetNumLines(LastAtomPos);
904 
905 	bool internalsAreLocal=false;
906 	if (!IntCoords) {
907 		IntCoords = new Internals;
908 	}
909 	MOPacInternals * mInts = IntCoords->GetMOPacStyle();
910 	if (!mInts) {
911 		IntCoords->CreateMOPacInternals(3*atmCount);
912 		mInts = IntCoords->GetMOPacStyle();
913 	} else {
914 		internalsAreLocal = true;
915 		mInts = new MOPacInternals(3*atmCount);
916 	}
917 	//First jump past the atoms and build a map of any string=value pairs (if any)
918 	std::map<std::string, double> valueMap;
919 	if (Buffer->LocateKeyWord("VARIABLES", 9, EndPos)) {
920 		Buffer->SkipnLines(1);
921 		while (Buffer->GetFilePos() < EndPos) {
922 			char		token[kMaxLineLength];
923 			double		value;
924 			Buffer->GetLine(Line);
925 			int readCount = sscanf(Line, "%s %lf", token, &value);
926 			if (readCount!=2) break;
927 			std::pair<std::string, double> myVal(token, value);
928 			/*std::pair<std::map<std::string, double>::iterator, bool> p = */ valueMap.insert(myVal);
929 			if (! myVal.second) {	//We have hit a duplicate value
930 				MessageAlert("Duplicate keys detected in the value list");
931 			}
932 		}
933 		Buffer->SetFilePos(BlockStart);
934 	}
935 	if (Buffer->LocateKeyWord("CONSTANTS", 9, EndPos)) {
936 		Buffer->SkipnLines(1);
937 		while (Buffer->GetFilePos() < EndPos) {
938 			char		token[kMaxLineLength];
939 			double		value;
940 			Buffer->GetLine(Line);
941 			int readCount = sscanf(Line, "%s %lf", token, &value);
942 			if (readCount!=2) break;
943 			std::pair<std::string, double> myVal(token, value);
944 			/*std::pair<std::map<std::string, double>::iterator, bool> p = */ valueMap.insert(myVal);
945 			if (! myVal.second) {	//We have hit a duplicate value
946 				MessageAlert("Duplicate keys detected in the value list");
947 			}
948 		}
949 		Buffer->SetFilePos(BlockStart);
950 	}
951 
952 	int iline=0;
953 	while ((Buffer->GetFilePos() < LastAtomPos)&&(iline<atmCount)) {
954 		CPoint3D	pos = CPoint3D(0.0f, 0.0f, 0.0f);	//This is just a placeholder
955 		char		token[kMaxLineLength], bondText[kMaxLineLength],
956 			angleText[kMaxLineLength], dihedralText[kMaxLineLength];
957 		float		bondLength, bondAngle, bondDihedral;
958 		long		AtomType;
959 		int			con1, con2, con3;
960 		Buffer->GetLine(Line);
961 		int readCount = sscanf(Line, "%255s %d %255s %d %255s %d %255s", token, &con1, bondText, &con2, angleText, &con3,
962 							   dihedralText);
963 		if (readCount < 1) break;	//failed to parse anything??
964 		AtomType = SetAtomType((unsigned char *) token);
965 		if (AtomType < 0) break;
966 		cFrame->AddAtom(AtomType, pos);
967 		if (iline > 0) {
968 			if (readCount < 2) break;
969 			if (sscanf(bondText, "%f", &bondLength) != 1) {
970 				//attempt to lookup the value in the map
971 				char * s = bondText;
972 				double flip = 1.0;
973 				if (bondText[0] == '-') {
974 					s = &(bondText[1]);
975 					flip = -1.0;
976 				}
977 				std::map<std::string, double>::iterator p = valueMap.find(s);
978 				if (p != valueMap.end()) {
979 					bondLength = flip * (*p).second;
980 				} else break;	//missing key value
981 			}
982 			if (iline >= 2) {
983 				if (readCount < 4) break;
984 				if (sscanf(angleText, "%f", &bondAngle) != 1) {
985 					//attempt to lookup the value in the map
986 					char * s = angleText;
987 					double flip = 1.0;
988 					if (angleText[0] == '-') {
989 						s = &(angleText[1]);
990 						flip = -1.0;
991 					}
992 					std::map<std::string, double>::iterator p = valueMap.find(s);
993 					if (p != valueMap.end()) {
994 						bondAngle = flip * (*p).second;
995 					} else break;	//missing key value
996 				}
997 				if (iline >= 3) {
998 					if (readCount < 6) break;
999 					if (sscanf(dihedralText, "%f", &bondDihedral) != 1) {
1000 						//attempt to lookup the value in the map
1001 						char * s = dihedralText;
1002 						double flip = 1.0;
1003 						if (dihedralText[0] == '-') {
1004 							s = &(dihedralText[1]);
1005 							flip = -1.0;
1006 						}
1007 						std::map<std::string, double>::iterator p = valueMap.find(s);
1008 						if (p != valueMap.end()) {
1009 							bondDihedral = flip * (*p).second;
1010 						} else break;	//missing key value
1011 					}
1012 				}
1013 			}
1014 			if (bondLength < 0.0) break;
1015 			con1--;
1016 			con2--;
1017 			con3--;
1018 			if (con1 >= iline) break;
1019 			mInts->AddInternalCoordinate(iline, con1, 0, bondLength);
1020 			if (iline > 1) {
1021 				if (con2 >= iline) break;
1022 				mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
1023 				if (iline > 2) {
1024 					if (con3 >= iline) break;
1025 					mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
1026 				}
1027 			}
1028 		}
1029 		iline++;
1030 	}
1031 	//if we punted after the AddAtom call delete off the atom without internal coordinate information
1032 	if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
1033 	//Now convert the set of internals into cartesians
1034 	mInts->InternalsToCartesians(this, Prefs, 0);
1035 	if (internalsAreLocal) delete mInts;
1036 	Buffer->SetFilePos(EndPos);
1037 	Buffer->SkipnLines(1);
1038 }
ParseMOPACZMatrix(BufferFile * Buffer,const long & nAtoms,WinPrefs * Prefs)1039 void MoleculeData::ParseMOPACZMatrix(BufferFile * Buffer, const long & nAtoms, WinPrefs * Prefs) {
1040 	if (!IntCoords) {
1041 		IntCoords = new Internals;
1042 	}
1043 	MOPacInternals * mInts = IntCoords->GetMOPacStyle();
1044 	if (!mInts) {
1045 		IntCoords->CreateMOPacInternals(3*nAtoms);
1046 		mInts = IntCoords->GetMOPacStyle();
1047 	}
1048 	float unitConversion = 1.0;
1049 	if (InputOptions && InputOptions->Data->GetUnits()) unitConversion = kBohr2AngConversion;
1050 	int iline=0;
1051 	long EndPos = Buffer->GetFileSize();
1052 	while ((Buffer->GetFilePos() < EndPos)&&(iline<nAtoms)) {
1053 		CPoint3D	pos = CPoint3D(0.0f, 0.0f, 0.0f);	//This is just a placeholder
1054 		char		token[kMaxLineLength], Line[kMaxLineLength];
1055 		float		bondLength, bondAngle, bondDihedral;
1056 		long		AtomType;
1057 		int			j1, j2, j3, con1, con2, con3;
1058 		Buffer->GetLine(Line);
1059 		int readCount = sscanf(Line, "%s %f %d %f %d %f %d %d %d %d", token, &bondLength, &j1, &bondAngle, &j2,
1060 			   &bondDihedral, &j3, &con1, &con2, &con3);
1061 		if (readCount < 1) break;	//failed to parse anything??
1062 		AtomType = SetAtomType((unsigned char *) token);
1063 		cFrame->AddAtom(AtomType, pos);
1064 		if (iline > 0) {
1065 			if (readCount < 2) break;
1066 			if (iline == 1) {	//the second atom will specify only the bond length
1067 				con1 = 1;
1068 			} else {
1069 				if (iline == 2) {	//For the third atom the connectivity is optional
1070 					if ((readCount >= 5)&&(readCount <=7)) {
1071 						con1 = 2;
1072 						con2 = 1;	//The default allows the connections to be assumed
1073 						if (readCount >= 6) {
1074 							con1 = (int) bondDihedral;
1075 							con2 = j3;
1076 						}
1077 					} else break;	//invalid line
1078 				}
1079 			}
1080 			if (bondLength < 0.0) break;
1081 			con1--;
1082 			con2--;
1083 			con3--;
1084 			if (con1 >= iline) break;
1085 			mInts->AddInternalCoordinate(iline, con1, 0, bondLength*unitConversion);
1086 			if (iline > 1) {
1087 				mInts->AddInternalCoordinate(iline, con2, 1, bondAngle);
1088 				if (iline > 2)
1089 					mInts->AddInternalCoordinate(iline, con3, 2, bondDihedral);
1090 			}
1091 		}
1092 		iline++;
1093 	}
1094 		//if we punted after the AddAtom call delete off the atom without internal coordinate information
1095 	if (iline > cFrame->NumAtoms) cFrame->DeleteAtom(iline-1);
1096 	//Now convert the set of internals into cartesians
1097 	mInts->InternalsToCartesians(this, Prefs, 0);
1098 }
GetModelCenter(CPoint3D * center)1099 void MoleculeData::GetModelCenter(CPoint3D * center) {
1100 	*center = Centroid;
1101 }
SetModelCenter(CPoint3D * center)1102 void MoleculeData::SetModelCenter(CPoint3D * center) {
1103 	Centroid = *center;
1104 }
GetModelRotation(float * Psi,float * Phi,float * Theta)1105 void MoleculeData::GetModelRotation(float * Psi, float * Phi, float * Theta) {
1106 	MatrixToEulerAngles(TotalRotation, Psi, Phi, Theta);
1107 }
SetModelRotation(float Psi,float Phi,float Theta)1108 void MoleculeData::SetModelRotation(float Psi, float Phi, float Theta) {
1109 	CPoint3D	Center;
1110 	GetModelCenter(&Center);	//Retrieve off the old center
1111 	EulerAnglesToMatrix(TotalRotation, Psi, Phi, Theta);
1112 	SetModelCenter(&Center);
1113 }
RotateToPrincipleOrientation(WinPrefs * Prefs,double precision)1114 void MoleculeData::RotateToPrincipleOrientation(WinPrefs * Prefs, double precision) {
1115 	if (!DeterminePrincipleOrientation(TotalRotation, Centroid, Prefs, precision)) {
1116 		MessageAlert("Unable to determine the proper symmetry adapted rotation. This"
1117 					 " may mean your selected point group is incorrect.");
1118 	}
1119 }
DeterminePointGroup(bool * pgFlags,WinPrefs * Prefs,double precision)1120 void MoleculeData::DeterminePointGroup(bool * pgFlags, WinPrefs * Prefs, double precision) {
1121 	pgFlags[0] = true;
1122 	for (int i=0; i<kNumSymmetryPointGroups; i++) pgFlags[i] = false;
1123 	//order of the flags
1124 	//c1, cs, ci, c2h-c8h (3-9), c2v-c8v (10-16), c2-c8 (17-23), s2-s8 (24-26),
1125 	//D2d-d8d (27-33), d2h-d8h (34-40), d2-d8 (41-47), Td, Th, T, Oh, O
1126 	int PrincAxisOrder = 1;
1127 	Matrix4D temp;
1128 	CPoint3D ptmp;
1129 
1130 	GAMESSPointGroup savedpg = GAMESS_C1;
1131 	long savedpgOrder = 1;
1132 	if (InputOptions) {
1133 		savedpg = InputOptions->Data->GetPointGroup();
1134 		savedpgOrder = InputOptions->Data->GetPointGroupOrder();
1135 	} else {
1136 		InputOptions = new InputData;
1137 	}
1138 	//First determine the rotation axis orders and store in Cn
1139 	for (int i=2; i<=8; i++) {
1140 		InputOptions->Data->SetPointGroup(GAMESS_CN);
1141 		InputOptions->Data->SetPointGroupOrder(i);
1142 		if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1143 			pgFlags[17 - 2 + i] = true;
1144 			PrincAxisOrder = i;
1145 			InputOptions->Data->SetPointGroup(GAMESS_CNH);
1146 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1147 				pgFlags[3 - 2 + i] = true;
1148 			}
1149 			InputOptions->Data->SetPointGroup(GAMESS_CNV);
1150 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1151 				pgFlags[10 - 2 + i] = true;
1152 			}
1153 			InputOptions->Data->SetPointGroup(GAMESS_DN);
1154 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1155 				pgFlags[41 - 2 + i] = true;
1156 				InputOptions->Data->SetPointGroup(GAMESS_DND);
1157 				if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1158 					pgFlags[27 - 2 + i] = true;
1159 				}
1160 				InputOptions->Data->SetPointGroup(GAMESS_DNH);
1161 				if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1162 					pgFlags[34 - 2 + i] = true;
1163 				}
1164 			}
1165 			if (i < 5) {
1166 				InputOptions->Data->SetPointGroup(GAMESS_S2N);
1167 				InputOptions->Data->SetPointGroupOrder(i-1);
1168 				if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1169 					pgFlags[24 - 2 + i] = true;
1170 				}
1171 			}
1172 		}
1173 	}
1174 	if (PrincAxisOrder > 1) {
1175 		InputOptions->Data->SetPointGroup(GAMESS_T);
1176 		if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1177 			pgFlags[50] = true;
1178 			InputOptions->Data->SetPointGroup(GAMESS_O);
1179 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1180 				pgFlags[52] = true;
1181 				InputOptions->Data->SetPointGroup(GAMESS_OH);
1182 				if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1183 					pgFlags[51] = true;
1184 				}
1185 			}
1186 			InputOptions->Data->SetPointGroup(GAMESS_TD);
1187 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1188 				pgFlags[48] = true;
1189 			}
1190 			InputOptions->Data->SetPointGroup(GAMESS_TH);
1191 			if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1192 				pgFlags[49] = true;
1193 			}
1194 		}
1195 	}
1196 	InputOptions->Data->SetPointGroup(GAMESS_CS);
1197 	if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1198 		pgFlags[1] = true;
1199 	}
1200 	InputOptions->Data->SetPointGroup(GAMESS_CI);
1201 	if (DeterminePrincipleOrientation(temp, ptmp, Prefs, precision)) {
1202 		pgFlags[2] = true;
1203 	}
1204 	//restore the entry point group
1205 	InputOptions->Data->SetPointGroup(savedpg);
1206 	InputOptions->Data->SetPointGroupOrder(savedpgOrder);
1207 }
DeterminePrincipleOrientation(Matrix4D result,CPoint3D & translation,WinPrefs * Prefs,double precision) const1208 bool MoleculeData::DeterminePrincipleOrientation(Matrix4D result,
1209 												 CPoint3D& translation,
1210 												 WinPrefs * Prefs,
1211 												 double precision) const {
1212 	//Setup the rotation matrix to present the molecule in the priciple orientation
1213 
1214 	InitRotationMatrix(result);
1215 	//First compute and offset to the center of mass
1216 	CPoint3D centerOfMass;
1217 	CalculateCenterOfMass(cFrame->Atoms, cFrame->GetNumAtoms(),
1218 						  Prefs->GetAtomMassLoc(), &centerOfMass);
1219 	translation = centerOfMass;
1220 	centerOfMass *= -1.0;
1221 	CPoint3D rotatedCenterOfMass = centerOfMass;
1222 
1223 	//Compute the moment of interia tensor
1224 	double xx=0.0, xy=0.0, yy=0.0, xz=0.0, yz=0.0, zz=0.0;
1225 	for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1226 		double atmMass = Prefs->GetAtomMass(cFrame->Atoms[i].GetType()-1);
1227 		double xc = cFrame->Atoms[i].Position.x + centerOfMass.x;
1228 		double yc = cFrame->Atoms[i].Position.y + centerOfMass.y;
1229 		double zc = cFrame->Atoms[i].Position.z + centerOfMass.z;
1230 		xx += atmMass*yc*yc + atmMass*zc*zc;
1231 		yy += atmMass*xc*xc + atmMass*zc*zc;
1232 		zz += atmMass*xc*xc + atmMass*yc*yc;
1233 		xy -= atmMass*xc*yc;
1234 		xz -= atmMass*xc*zc;
1235 		yz -= atmMass*yc*zc;
1236 	}
1237 	if ((fabs(xy)>1.0e-8)||(fabs(xz)>1.0e-8)||(fabs(yz)>1.0e-8)) {
1238 		//Diagonalize the moment of interia tensor to yield the
1239 		//rotation into the principle axis.
1240 		double tri[6];
1241 		tri[0] = xx;
1242 		tri[1] = xy;
1243 		tri[2] = yy;
1244 		tri[3] = xz;
1245 		tri[4] = yz;
1246 		tri[5] = zz;
1247 		double rot[9], eig[3];
1248 		SymmetricJacobiDiagonalization(tri, rot, eig, 3, 3);
1249 		for (int ii=0; ii<3; ii++) {
1250 			for (int j=0; j<3; j++) {
1251 				result[ii][j] = rot[3*ii + j];
1252 			}
1253 		}
1254 		float det = DeterminantMatrix(result);
1255 		//The determinate should be positive, multiple by -1 if not
1256 		//I think this is to ensure the rotation is right-handed
1257 		if (det < 0.0) {
1258 			for (int ii=0; ii<3; ii++) {
1259 				result[ii][0] *= -1.0;
1260 			}
1261 		}
1262 		Rotate3DOffset(result,centerOfMass,&rotatedCenterOfMass);
1263 	}
1264 
1265 	for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1266 		CPoint3D temp = (cFrame->Atoms[i].Position) + centerOfMass;
1267 		Rotate3DPt(result, temp, &(RotCoords[i]));
1268 	}
1269 	//generate the moments of inertia
1270 	xx = yy = zz = xy = xz = yz = 0.0;
1271 	for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1272 		double atmMass = Prefs->GetAtomMass(cFrame->Atoms[i].GetType()-1);
1273 		double xc = RotCoords[i].x;
1274 		double yc = RotCoords[i].y;
1275 		double zc = RotCoords[i].z;
1276 		xx += atmMass*xc*xc;
1277 		yy += atmMass*yc*yc;
1278 		zz += atmMass*zc*zc;
1279 		xy += atmMass*xc*yc;
1280 		xz += atmMass*xc*zc;
1281 		yz += atmMass*yc*zc;
1282 	}
1283 	if ((xx>0.1)&&(fabs(xx - yy)<1.0e-4)&&(fabs(zz-xx)>1.0e-4)) {
1284 		//The x and y axis do not appear to be unique, Try to rotate an atom onto the x axis
1285 		//loop through and look for atoms not on the z-axis. If there is already an atom on the
1286 		//x or y axis we can skip as well. If the z axis is not unique we skip as well
1287 		bool needRot = true;
1288 		for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1289 			if (fabs(RotCoords[i].x)>1.0e-4) {
1290 				if (fabs(RotCoords[i].y)<1.0e-4) {
1291 					needRot = false;	//Atom already on y-axis
1292 					break;
1293 				}
1294 			} else if (fabs(RotCoords[i].y)>1.0e-4) {
1295 				needRot	= false;	//Atom already on x-axis
1296 				break;
1297 			}
1298 		}
1299 		if (needRot) {
1300 			//ok pick the first atom off of the z-axis
1301 			for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1302 				if ((fabs(RotCoords[i].x)>1.0e-4)||(fabs(RotCoords[i].y)>1.0e-4)) {
1303 					double theta = atan(RotCoords[i].y/RotCoords[i].x);
1304 					double costh = cos(-theta);
1305 					double sinth = sin(-theta);
1306 					Matrix4D rotation;
1307 					InitRotationMatrix(rotation);
1308 					rotation[0][0] = costh;
1309 					rotation[0][1] = sinth;
1310 					rotation[1][0] = -sinth;
1311 					rotation[1][1] = costh;
1312 					Matrix4D temp;
1313 					MultiplyMatrix(result,rotation,temp);
1314 					CopyMatrix(temp,result);
1315 					break;
1316 				}
1317 			}
1318 		}
1319 	}
1320 
1321 	//To complete the determination of the principle axis we must apply all of the symmetry
1322 	//operators to each of the permutations of the axis. When we find a set that results in an
1323 	//unchanged set of coordinates we are done.
1324 	GAMESSPointGroup pg = GAMESS_C1;
1325 	long pgOrder = 1;
1326 	if (InputOptions) {
1327 		pg = InputOptions->Data->GetPointGroup();
1328 		pgOrder = InputOptions->Data->GetPointGroupOrder();
1329 	}
1330 	bool success = true;
1331 	//If we are in C1 symmetry we are done.
1332 	if (pg != GAMESS_C1) {
1333 
1334 		for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1335 			CPoint3D temp = (cFrame->Atoms[i].Position) + centerOfMass;
1336 			Rotate3DPt(result, temp, &(RotCoords[i]));
1337 		}
1338 
1339 		SymmetryOps symOps(pg, pgOrder);
1340 		Matrix4D permuteMatrix;
1341 		for (int pass=0; pass<6; pass++) {
1342 			switch (pass) {
1343 				case 0:	//default axis
1344 					InitRotationMatrix(permuteMatrix);
1345 					break;
1346 				case 1: //x, z, y
1347 					permuteMatrix[1][1] = permuteMatrix[2][2] = 0.0;
1348 					permuteMatrix[1][2] = -1.0;
1349 					permuteMatrix[2][1] = 1.0;
1350 					break;
1351 				case 2: //y, x, z
1352 					permuteMatrix[0][0] = permuteMatrix[1][2] = permuteMatrix[2][1] = 0.0;
1353 					permuteMatrix[0][1] = -1.0;
1354 					permuteMatrix[1][0] = permuteMatrix[2][2] = 1.0;
1355 					break;
1356 				case 3: //y, z, x
1357 					permuteMatrix[0][1] = permuteMatrix[2][2] = 0.0;
1358 					permuteMatrix[0][2] = permuteMatrix[2][1] = 1.0;
1359 					break;
1360 				case 4: //z, x, y
1361 					permuteMatrix[0][2] = permuteMatrix[1][0] = permuteMatrix[2][1] = 0.0;
1362 					permuteMatrix[0][2] = permuteMatrix[1][0] = permuteMatrix[2][1] = 1.0;
1363 					break;
1364 				case 5: //z, y, x
1365 					permuteMatrix[1][0] = permuteMatrix[2][1] = 0.0;
1366 					permuteMatrix[2][0] = -1.0;
1367 					permuteMatrix[1][1] = 1.0;
1368 					break;
1369 			}
1370 			success = true;
1371 			for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1372 				bool SymMatch = true;
1373 				for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1374 					CPoint3D test;
1375 					Rotate3DPt(permuteMatrix, RotCoords[atm], &test);
1376 					CPoint3D result;
1377 					symOps.ApplyOperator(test, result, iOp);
1378 					bool match = false;
1379 					for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1380 						if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1381 							continue;
1382 						CPoint3D testpt;
1383 						Rotate3DPt(permuteMatrix, RotCoords[testatom], &testpt);
1384 						CPoint3D offset = result - testpt;
1385 							//test the difference in position. They should be quite close!
1386 						if (offset.Magnitude() < precision) {
1387 							match = true;
1388 							break;
1389 						}
1390 					}
1391 					if (!match) { //No matching atom so this permutation fails
1392 						SymMatch = false;
1393 						break;
1394 					}
1395 				}
1396 				if (!SymMatch) {
1397 					success = false;
1398 					break;
1399 				}
1400 			}
1401 			if (success) { //we are done
1402 				//combine the permutation with the rotation matrix
1403 				Matrix4D temp;
1404 				MultiplyMatrix(result,permuteMatrix,temp);
1405 				CopyMatrix(temp,result);
1406 				Rotate3DOffset(result,centerOfMass,&rotatedCenterOfMass);
1407 				break;
1408 			}
1409 		}
1410 	}
1411 	return success;
1412 }
GenerateSymmetryDependentAtoms(bool do_warnings)1413 void MoleculeData::GenerateSymmetryDependentAtoms(bool do_warnings) {
1414 	// The input coordinates (in the current frame) must contain the symmetry
1415 	// unique atoms in the proper symmetry adapted reference frame. This
1416 	// routine will generate the symmetry dependent atoms.
1417 	GAMESSPointGroup pg = GAMESS_C1;
1418 	long pgOrder = 1;
1419 	if (InputOptions) {
1420 		pg = InputOptions->Data->GetPointGroup();
1421 		pgOrder = InputOptions->Data->GetPointGroupOrder();
1422 	}
1423 	bool conflicts = false;
1424 	bool closeAtoms = false;
1425 	//If we are in C1 symmetry we are done.
1426 	if ((pg != GAMESS_C1)&&(cFrame->GetNumAtoms()>0)) {
1427 		SymmetryOps symOps(pg, pgOrder);
1428 		//loop over the symmetry operations
1429 		for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1430 			for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1431 				if (cFrame->Atoms[atm].IsSymmetryUnique()) {
1432 					//Apply the operator to each atom
1433 					CPoint3D result;
1434 					symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1435 					bool match = false;
1436 					for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1437 						//Now loop over the atoms to see if this generates a new atom
1438 						//or if there are any conflicts
1439 						CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1440 						float dist = offset.Magnitude();
1441 						if (dist < 1.0e-3) {	//this cutoff might need tuning
1442 							//If we have a match the atoms had better be the same type
1443 							if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1444 								conflicts = true;
1445 							match = true;
1446 						} else if (dist < 0.2) {
1447 							//GAMESS would consider this an error
1448 							closeAtoms = true;
1449 							match = true;
1450 						}
1451 					}
1452 					if (!match) {
1453 						//In order to match the order of atoms that GAMESS generates we need
1454 						//to insert the generated atom before the input one.
1455 						/* NewAtom(cFrame->Atoms[atm].GetType(), result, atm); */
1456 						NewAtom(cFrame->Atoms[atm], true, atm, &result);
1457 						cFrame->Atoms[atm].IsSymmetryUnique(false);
1458 						cFrame->SetAtomSelection(atm, false);
1459 						atm++;
1460 					}
1461 				}
1462 			}
1463 		}
1464 	}
1465 
1466 	if (do_warnings) {
1467 		if (conflicts) {
1468 			MessageAlert("Found conflicts during generation of symmetry dependent coordinates. Your starting coordinates are probably incorrect for the chosen symmetry point group.");
1469 		}
1470 		if (closeAtoms) {
1471 			MessageAlert("Atoms closer than 0.2 Angstroms have been removed. Your coordinates may be incorrect for the chosen symmetry point group.");
1472 		}
1473 	}
1474 }
1475 
GenerateSymmetryUniqueAtoms(double tolerance)1476 bool MoleculeData::GenerateSymmetryUniqueAtoms(double tolerance) {
1477 	//On input the coordinates should contain the full set of atoms in the
1478 	//correct orientation for the selected symmetry point group. The
1479 	//coordinates will not be changed, just "marked" as symmetry unique or not.
1480 	//If a symmetry operation generates a new atom an error is generated and
1481 	//the operation fails.  GAMESS likes the symmetry unique atom last. So if
1482 	//an atom can be generated by an atom later in the list we consider it
1483 	//symmetry dependent.
1484 	GAMESSPointGroup pg = GAMESS_C1;
1485 	long pgOrder = 1;
1486 	if (InputOptions) {
1487 		pg = InputOptions->Data->GetPointGroup();
1488 		pgOrder = InputOptions->Data->GetPointGroupOrder();
1489 	}
1490 	//Clear the unique flags
1491 	for (int i=0; i<cFrame->GetNumAtoms(); i++) {
1492 		cFrame->Atoms[i].IsSymmetryUnique(false);
1493 	}
1494 	bool conflicts = false;
1495 	bool closeAtoms = false;
1496 	SymmetryOps symOps(pg, pgOrder);
1497 	//loop over the symmetry operations
1498 	for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1499 		bool match = false;
1500 		for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1501 			//Apply the operator to each atom
1502 			CPoint3D result;
1503 			symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1504 			for (int testatom=atm+1; testatom<cFrame->GetNumAtoms(); testatom++) {
1505 				//Now loop over the atoms to see if this generates a new atom
1506 				//or if there are any conflicts
1507 				CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1508 				float dist = offset.Magnitude();
1509 				if (dist < tolerance) {	//this cutoff might need tuning
1510 										//If we have a match the atoms had better be the same type
1511 					if (cFrame->Atoms[atm].GetType() != cFrame->Atoms[testatom].GetType())
1512 						conflicts = true;
1513 					match = true;
1514 					break;
1515 				} else if (dist < 0.2) {
1516 					//GAMESS would consider this an error
1517 					closeAtoms = true;
1518 					match = true;
1519 					break;
1520 				}
1521 			}
1522 			if (match) {
1523 				break;
1524 			}
1525 		}
1526 		if (match == false) cFrame->Atoms[atm].IsSymmetryUnique(true);
1527 	}
1528 //	if (conflicts) {
1529 //		MessageAlert("Found conflicts during generation of symmetry dependent coordinates. Your starting coordinates are probably incorrect for the chosen symmetry point group.");
1530 //	}
1531 //	if (closeAtoms) {
1532 //		MessageAlert("Atoms closer than 0.2 Angstroms have been removed. Your coordinates may be incorrect for the chosen symmetry point group.");
1533 //	}
1534 	return (!conflicts);
1535 }
SymmetrizeCoordinates(bool selected_only)1536 void MoleculeData::SymmetrizeCoordinates(bool selected_only) {
1537 	//The purpose of this routine is to remove the "slop" in the coordinates such that they
1538 	//more tightly meet the selected point group.
1539 	GAMESSPointGroup pg = GAMESS_C1;
1540 	long pgOrder = 1;
1541 	if (InputOptions) {
1542 		pg = InputOptions->Data->GetPointGroup();
1543 		pgOrder = InputOptions->Data->GetPointGroupOrder();
1544 	}
1545 	if (pg == GAMESS_C1) return; //there is nothing to be done for C1
1546 	SymmetryOps symOps(pg, pgOrder);
1547 	//My strategy is to loop over the symmetry unique atoms, apply each operator.
1548 	//If the resulting operation generates an atom "close" to the original atom add it
1549 	//to a vector sum. After all of the operators are applied divid the sum by the number of
1550 	//images. Next pass through the symmetry dependent atoms and adjust their position to
1551 	//match the newly generated coordinates
1552 	for (int atm=0; atm<cFrame->GetNumAtoms(); atm++) {
1553 		if (cFrame->Atoms[atm].IsSymmetryUnique() &&
1554 			(!selected_only || cFrame->GetAtomSelection(atm))) {
1555 			CPoint3D sum;
1556 			int matchcount = 0;
1557 			for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1558 				CPoint3D result;
1559 				//Apply the operator to each atom
1560 				symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1561 				CPoint3D offset = result - cFrame->Atoms[atm].Position;
1562 				float dist = offset.Magnitude();
1563 				if (dist < 0.3) {	//this cutoff might need tuning
1564 					sum += result;
1565 					matchcount++;
1566 				}
1567 			}
1568 			if (matchcount > 1) {
1569 				//update the symmetry unique coordinate
1570 				cFrame->Atoms[atm].Position = sum / (float) matchcount;
1571 				//and now update symmetry dependent atoms
1572 				for (int iOp=0; iOp<symOps.getOperationCount(); iOp++) {
1573 					CPoint3D result;
1574 					//Apply the operator to each atom
1575 					symOps.ApplyOperator(cFrame->Atoms[atm].Position, result, iOp);
1576 					for (int testatom=0; testatom<cFrame->GetNumAtoms(); testatom++) {
1577 						if (testatom == atm || cFrame->GetAtomType(testatom) != cFrame->GetAtomType(atm)) continue;
1578 						//Now loop over the atoms to see if this generates a new atom
1579 						//or if there are any conflicts
1580 						CPoint3D offset = result - cFrame->Atoms[testatom].Position;
1581 						float dist = offset.Magnitude();
1582 						if (dist < 0.3) {	//this cutoff might need tuning
1583 							cFrame->Atoms[testatom].Position = result;
1584 						}
1585 					}
1586 				}
1587 			}
1588 		}
1589 	}
1590 }
CreateLLM(long NumPts,WinPrefs * Prefs)1591 void MoleculeData::CreateLLM(long NumPts, WinPrefs * Prefs) {
1592 	Frame *	NewFrame, * NewFrame2;
1593 
1594 	Frame * lFrame = cFrame;
1595 	Frame * lEndFrame = lFrame->NextFrame;
1596 	//saniety check
1597 	if ((lFrame == NULL)||(lEndFrame == NULL)||(NumPts<1)) return;
1598 	long NumAtoms = lFrame->NumAtoms;
1599 
1600 	CPoint3D * offset = new CPoint3D[NumAtoms];
1601 	if (!offset) throw MemoryError();
1602 	NumPts++;
1603 	for (long i=0; i<NumAtoms; i++) {
1604 		offset[i].x = (lEndFrame->Atoms[i].Position.x-lFrame->Atoms[i].Position.x)/NumPts;
1605 		offset[i].y = (lEndFrame->Atoms[i].Position.y-lFrame->Atoms[i].Position.y)/NumPts;
1606 		offset[i].z = (lEndFrame->Atoms[i].Position.z-lFrame->Atoms[i].Position.z)/NumPts;
1607 	}
1608 	NumPts--;
1609 	NewFrame2 = lFrame;
1610 	for (long counter=0; counter<NumPts; counter++) {
1611 		NewFrame = AddFrame(NumAtoms, lFrame->NumBonds);
1612 		for (long iatm=0; iatm<NumAtoms; iatm++) {
1613 			NewFrame->Atoms[iatm].Type = lFrame->Atoms[iatm].Type;
1614 			NewFrame->Atoms[iatm].Position.x = NewFrame2->Atoms[iatm].Position.x + offset[iatm].x;
1615 			NewFrame->Atoms[iatm].Position.y = NewFrame2->Atoms[iatm].Position.y + offset[iatm].y;
1616 			NewFrame->Atoms[iatm].Position.z = NewFrame2->Atoms[iatm].Position.z + offset[iatm].z;
1617 		}
1618 		NewFrame->NumAtoms = NumAtoms;
1619 		if (Prefs->GetAutoBond()) {
1620 			Progress lProg;
1621 			NewFrame->SetBonds(Prefs, false, &lProg);
1622 		}
1623 		NewFrame2 = NewFrame;
1624 	}
1625 	delete [] offset;
1626 } /*CreateLLM*/
1627 
1628 //CreateInternalLLM creates a linear least motion path based on the internal
1629 //coordinates of the two frames. In general this will result is a lower energy
1630 //path than a cartesian based LLM path, since it prevents bonds from being
1631 //stretched or squashed.
CreateInternalLLM(long NumPts,WinPrefs * Prefs)1632 void MoleculeData::CreateInternalLLM(long NumPts, WinPrefs * Prefs) {
1633 	Frame *	NewFrame, * NewFrame2;
1634 
1635 	Frame * lFrame = cFrame;
1636 	Frame * lEndFrame = lFrame->NextFrame;
1637 	//saniety check
1638 	if ((lFrame == NULL)||(lEndFrame == NULL)||(NumPts<1)) return;
1639 
1640 	long NumAtoms = lFrame->NumAtoms;
1641 	long SavedFrameNum = CurrentFrame;
1642 
1643 	if (NumAtoms <= 2) {	//No point to using internals when there are less than 3 atoms!
1644 		CreateLLM(NumPts, Prefs);
1645 		return;
1646 	}
1647 	MOPacInternals * lInternals = IntCoords->GetMOPacStyle();
1648 	if (!lInternals) return;
1649 	float * OffsetValues = new float[3*NumAtoms];
1650 	float * StartValues = new float[3*NumAtoms];
1651 	if (!OffsetValues || !StartValues) throw MemoryError();
1652 	Progress * lProg = new Progress;
1653 		//Make sure the internals are up to date for the first frame
1654 	lInternals->CartesiansToInternals(this);
1655 		long i, part;
1656 	for (i=1; i<NumAtoms; i++)
1657 		for (part=0; part<3; part++)
1658 			StartValues[3*i+part] = lInternals->GetValue(i, part);
1659 	cFrame = lEndFrame;
1660 	lInternals->CartesiansToInternals(this);
1661 	for (i=0; i<9; i++) OffsetValues[i] = 0.0;
1662 	NumPts++;
1663 	for (i=1; i<NumAtoms; i++) {
1664 		OffsetValues[3*i] = (lInternals->GetValue(i, 0) - StartValues[3*i])/NumPts;
1665 		if (i>=2) {
1666 			OffsetValues[3*i+1] = (lInternals->GetValue(i, 1) - StartValues[3*i+1])/NumPts;
1667 			if (i>=3) {	//dihedrals are tricky. We must take into account that they run from
1668 					//180 to -180 and the shorest rotation might be through 180.
1669 				OffsetValues[3*i+2] = lInternals->GetValue(i, 2) - StartValues[3*i+2];
1670 				if (OffsetValues[3*i+2] > 180.0) OffsetValues[3*i+2] = OffsetValues[3*i+2] - 360.0;
1671 				else if (OffsetValues[3*i+2] < -180.0) OffsetValues[3*i+2] = 360 + OffsetValues[3*i+2];
1672 				OffsetValues[3*i+2] /= NumPts;
1673 			}
1674 		}
1675 	}
1676 	NumPts--;
1677 	cFrame = lFrame;
1678 	NewFrame2 = lFrame;
1679 	for (long counter=0; counter<NumPts; counter++) {
1680 			//add a new frame and copy the coordinates from the previous frame
1681 		NewFrame = AddFrame(NumAtoms, lFrame->NumBonds);
1682 		for (long iatm=0; iatm<NumAtoms; iatm++) {
1683 			NewFrame->Atoms[iatm].Type = lFrame->Atoms[iatm].Type;
1684 			NewFrame->Atoms[iatm].Position.x = NewFrame2->Atoms[iatm].Position.x;
1685 			NewFrame->Atoms[iatm].Position.y = NewFrame2->Atoms[iatm].Position.y;
1686 			NewFrame->Atoms[iatm].Position.z = NewFrame2->Atoms[iatm].Position.z;
1687 		}
1688 		NewFrame->NumAtoms = NumAtoms;
1689 			//Now set the internals to their proper values
1690 		cFrame = NewFrame;
1691 		for (i=1; i<NumAtoms; i++) {
1692 			StartValues[3*i] += OffsetValues[3*i];
1693 			lInternals->SetValue(i, 0, StartValues[3*i]);
1694 			if (i>=2) {
1695 				StartValues[3*i+1] += OffsetValues[3*i+1];
1696 				if (StartValues[3*i+1] < 0) StartValues[3*i+1] = 0.0;
1697 				if (StartValues[3*i+1] > 180.0) StartValues[3*i+1] = 180.0;
1698 				lInternals->SetValue(i, 1, StartValues[3*i+1]);
1699 				if (i>=3) {
1700 					StartValues[3*i+2] += OffsetValues[3*i+2];
1701 					if (StartValues[3*i+2] < -180.0) StartValues[3*i+2] += 360.0;;
1702 					if (StartValues[3*i+2] > 180.0) StartValues[3*i+2] -= 360.0;
1703 					lInternals->SetValue(i, 2, StartValues[3*i+2]);
1704 				}
1705 			}
1706 		}
1707 			//Now convert the internals to cartesians so they are saved.
1708 		lInternals->InternalsToCartesians(this, Prefs, 0);
1709 
1710 		if (Prefs->GetAutoBond())
1711 			NewFrame->SetBonds(Prefs, false, lProg);
1712 		NewFrame2 = NewFrame;
1713 	}
1714 	cFrame = lFrame;	//reset the current frame pointer
1715 	CurrentFrame = SavedFrameNum;
1716 	delete [] OffsetValues;
1717 	delete [] StartValues;
1718 	if (lProg) {
1719 		LinearLeastSquaresFit(lProg);
1720 		delete lProg;
1721 	}
1722 }
1723 
AdvanceFrame(void)1724 void MoleculeData::AdvanceFrame(void) {
1725 	if (cFrame->NextFrame) cFrame = cFrame->NextFrame;
1726 }
SetCurrentFrame(long FrameNum)1727 void MoleculeData::SetCurrentFrame(long FrameNum) {
1728 	if ((FrameNum>NumFrames)||(FrameNum<1)) return;
1729 	if (FrameNum < CurrentFrame) {
1730 		cFrame = Frames;
1731 		CurrentFrame = 1;
1732 	}
1733 	while (CurrentFrame < FrameNum) {
1734 		cFrame = cFrame->NextFrame;
1735 		CurrentFrame++;
1736 	}
1737 }
SurfaceExportPossible(void)1738 bool MoleculeData::SurfaceExportPossible(void) {
1739 	return cFrame->SurfaceExportPossible();
1740 }
ModeVisible(void) const1741 bool MoleculeData::ModeVisible(void) const {
1742 	return ((cFrame->Vibs)&&GetDrawMode());
1743 }
GetNumBonds(void) const1744 long MoleculeData::GetNumBonds(void) const {
1745 	return cFrame->GetNumBonds();
1746 }
DeleteAtom(long AtomNum,bool allFrames)1747 long MoleculeData::DeleteAtom(long AtomNum, bool allFrames) {
1748 	long offset = AtomNum;
1749 	int fragId = -1;
1750 	if (cFrame->Atoms[AtomNum].IsEffectiveFragment()) fragId = cFrame->Atoms[AtomNum].GetFragmentNumber();
1751 	if (allFrames) {
1752 		Frame * lFrame = Frames;
1753 		while (lFrame) {
1754 			lFrame->DeleteAtom(AtomNum);
1755 			lFrame = lFrame->NextFrame;
1756 		}
1757 	} else
1758 		cFrame->DeleteAtom(AtomNum);
1759 	if (IntCoords) {
1760 		MOPacInternals * mInts = IntCoords->GetMOPacStyle();
1761 		if (mInts) mInts->DeleteAtom(this, AtomNum);
1762 	}
1763 	std::vector<Annotation *>::iterator anno;
1764 	anno = Annotations.begin();
1765 	for (anno = Annotations.begin(); anno != Annotations.end(); ) {
1766 		if ((*anno)->containsAtom(AtomNum)) {
1767 			delete (*anno);
1768 			anno = Annotations.erase(anno);
1769 		} else {
1770 			(*anno)->adjustIds(AtomNum, -1);
1771 			anno++;
1772 		}
1773 	}
1774 
1775 	if (fragId > 0) { //recurse to delete the other atoms in the same fragment
1776 		for (long i=0; i<cFrame->NumAtoms; i++) {
1777 			if (cFrame->Atoms[i].IsEffectiveFragment() &&
1778 				cFrame->Atoms[i].GetFragmentNumber() == fragId) {
1779 				//turn off fragment bit to prevent multilevel recursion
1780 				cFrame->Atoms[i].IsEffectiveFragment(false);
1781 				DeleteAtom(i, allFrames);
1782 				--i;
1783 			}
1784 		}
1785 		//remove the fragment name from the name list
1786 		std::vector<std::string>::iterator iter = FragmentNames.begin();
1787 		for (int i=1; i<fragId; i++) ++iter;
1788 		FragmentNames.erase(iter);
1789 		offset = 0;	//Have the caller rescan the whole list to be safe.
1790 	}
1791 	if (FMOFragmentIds.size() > AtomNum) FMOFragmentIds.erase(FMOFragmentIds.begin() + AtomNum);
1792 
1793 	ResetRotation();
1794 	return offset;
1795 }
ReorderAtomList(long index1,long targetindex)1796 void MoleculeData::ReorderAtomList(long index1, long targetindex) {
1797 	//Sanity check the move
1798 	if ((index1 < 0)||(targetindex < 0)||(index1 >= cFrame->NumAtoms)||
1799 		(targetindex >= cFrame->NumAtoms)||(index1 == targetindex)) return;
1800 	//This should really remove basis set, orbitals, etc unless they are also updated
1801 	//Move the atom
1802 	mpAtom temp;
1803 	if (targetindex > index1) {
1804 		temp = cFrame->Atoms[index1];
1805 		for (long i=index1; i<targetindex; i++) {
1806 			cFrame->Atoms[i] = cFrame->Atoms[i+1];
1807 		}
1808 		cFrame->Atoms[targetindex] = temp;
1809 		//update the bonds list
1810 		for (long i=0; i<cFrame->NumBonds; i++) {
1811 			if (cFrame->Bonds[i].Atom1 == index1)
1812 				cFrame->Bonds[i].Atom1 = targetindex;
1813 			else if ((cFrame->Bonds[i].Atom1 > index1)&&(cFrame->Bonds[i].Atom1 <= targetindex))
1814 				cFrame->Bonds[i].Atom1 --;
1815 			if (cFrame->Bonds[i].Atom2 == index1)
1816 				cFrame->Bonds[i].Atom2 = targetindex;
1817 			else if ((cFrame->Bonds[i].Atom2 > index1)&&(cFrame->Bonds[i].Atom2 <= targetindex))
1818 				cFrame->Bonds[i].Atom2 --;
1819 		}
1820 	} else {
1821 		temp = cFrame->Atoms[index1];
1822 		for (long i=index1; i>targetindex; i--) {
1823 			cFrame->Atoms[i] = cFrame->Atoms[i-1];
1824 		}
1825 		cFrame->Atoms[targetindex] = temp;
1826 		//update the bonds list
1827 		for (long i=0; i<cFrame->NumBonds; i++) {
1828 			if (cFrame->Bonds[i].Atom1 == index1)
1829 				cFrame->Bonds[i].Atom1 = targetindex;
1830 			else if ((cFrame->Bonds[i].Atom1 < index1)&&(cFrame->Bonds[i].Atom1 >= targetindex))
1831 				cFrame->Bonds[i].Atom1 ++;
1832 			if (cFrame->Bonds[i].Atom2 == index1)
1833 				cFrame->Bonds[i].Atom2 = targetindex;
1834 			else if ((cFrame->Bonds[i].Atom2 < index1)&&(cFrame->Bonds[i].Atom2 >= targetindex))
1835 				cFrame->Bonds[i].Atom2 ++;
1836 		}
1837 	}
1838 	//update the internals list
1839 	if (IntCoords) {
1840 		IntCoords->ChangeAtomIndex(this, index1, targetindex);
1841 	}
1842 }
ValidAtom(long AtomNum)1843 bool MoleculeData::ValidAtom(long AtomNum) {
1844 	return ((AtomNum>=0)&&(AtomNum<cFrame->NumAtoms));
1845 }
GetAtomTypes(void)1846 AtomTypeList * MoleculeData::GetAtomTypes(void) {return cFrame->GetAtomTypes();}
GetNumBasisFunctions(void) const1847 long MoleculeData::GetNumBasisFunctions(void) const {
1848 	return ((Basis!=NULL)?Basis->NumFuncs : 0);
1849 }
DeleteAllAnnotations(void)1850 void MoleculeData::DeleteAllAnnotations(void) {
1851 	std::vector<Annotation *>::const_iterator anno;
1852 	anno = Annotations.begin();
1853 	for (anno = Annotations.begin(); anno != Annotations.end(); ) {
1854 		delete (*anno);
1855 		anno++;
1856 	}
1857 	Annotations.clear();
1858 }
1859 
GetFragmentName(unsigned long index) const1860 const char * MoleculeData::GetFragmentName(unsigned long index) const {
1861 	if (index < FragmentNames.size())
1862 		return FragmentNames[index].c_str();
1863 	else
1864 		return "H2ORHF";
1865 }
1866 
PruneUnusedFragments()1867 void MoleculeData::PruneUnusedFragments() {
1868 
1869 	bool found_instance;
1870 	std::map<std::string, EFrag>::iterator frag;
1871 	std::vector<std::string>::const_iterator fragname;
1872 
1873 	for (frag = efrags.begin(); frag != efrags.end(); /* BLANK */) {
1874 		found_instance = false;
1875 		for (fragname = FragmentNames.begin();
1876 			 !found_instance && fragname != FragmentNames.end();
1877 			 ++fragname) {
1878 			if (frag->first.compare(*fragname) == 0) {
1879 				found_instance = true;
1880 			}
1881 		}
1882 		if (!found_instance) {
1883 			efrags.erase(frag++);
1884 		} else {
1885 			++frag;
1886 		}
1887 	}
1888 
1889 }
1890 
CreateFMOFragmentation(const int & NumMolecules,std::vector<long> & newFragmentation)1891 long MoleculeData::CreateFMOFragmentation(const int & NumMolecules, std::vector<long> & newFragmentation) {
1892 	//Create an FMO fragment list by breaking up the system into non-bonded pieces (H-bonds are ignored)
1893 
1894 	long result = 0;
1895 	//Start by initializing the vector with invalid fragment numbers
1896 	newFragmentation.clear();
1897 	newFragmentation.reserve(cFrame->GetNumAtoms());
1898 	for (long i=0; i<cFrame->GetNumAtoms(); i++) {
1899 		newFragmentation.push_back(-1);
1900 	}
1901 
1902 	long NextId = 1;
1903 	long atomNum = 0;
1904 	while (atomNum < cFrame->GetNumAtoms()) {
1905 		if (newFragmentation[atomNum] < 1) {
1906 			newFragmentation[atomNum] = NextId;
1907 			//starting a new fragment
1908 			//a recursive function would be simpler to write and should normally be fine, but if somebody runs this
1909 			//on a system with a large molecule that could be rather abusive on the stack
1910 			//Scan through the bonds list and check both ends of the bond for the active fragment Id. If one
1911 			//atom is tagged then the other needs to be. The process is done when a further pass through the list
1912 			//does not produce any more tags. This seems a bit brute force and is horrible on memory accesses, but
1913 			//it is probably fast enough for the very limited number of times it be executed.
1914 			bool done=false;
1915 			while (!done) {
1916 				done = true;
1917 				for (long ib=0; ib<cFrame->GetNumBonds(); ++ib) {
1918 					if (newFragmentation[cFrame->Bonds[ib].Atom1] == NextId) {
1919 						if (newFragmentation[cFrame->Bonds[ib].Atom2] != NextId) {
1920 							newFragmentation[cFrame->Bonds[ib].Atom2] = NextId;
1921 							done = false;
1922 						}
1923 					} else if (newFragmentation[cFrame->Bonds[ib].Atom2] == NextId) {
1924 						if (newFragmentation[cFrame->Bonds[ib].Atom1] != NextId) {
1925 							newFragmentation[cFrame->Bonds[ib].Atom1] = NextId;
1926 							done = false;
1927 						}
1928 					}
1929 				}
1930 			}
1931 			++NextId;
1932 		}
1933 		++atomNum;
1934 	}
1935 	result = NextId - 1;
1936 	//Ok we should now have the non-bonded pieces separated. If the user wants more than one non-bonded piece in
1937 	//each fragment coalesce them now.
1938 	if (NumMolecules > 1) {
1939 		--NextId;	//this leaves NextId containing the starting number of fragments
1940 		//I think it is probably best to combine fragments that are closest to each other.
1941 		//It's probably best to consider the center of each fragment for the distance computation.
1942 		std::vector<CPoint3D> fragCenters;
1943 		fragCenters.reserve(NextId);
1944 		for (long ifrag=0; ifrag<NextId; ++ifrag) {
1945 			fragCenters.push_back(CPoint3D(0,0,0));
1946 			long atmcount=0;
1947 			for (long iatom=0; iatom<cFrame->GetNumAtoms(); ++iatom) {
1948 				if (newFragmentation[iatom] == (ifrag+1)) {
1949 					fragCenters[ifrag].x += cFrame->Atoms[iatom].Position.x;
1950 					fragCenters[ifrag].y += cFrame->Atoms[iatom].Position.y;
1951 					fragCenters[ifrag].z += cFrame->Atoms[iatom].Position.z;
1952 					atmcount++;
1953 				}
1954 			}
1955 			fragCenters[ifrag].x /= (float) atmcount;
1956 			fragCenters[ifrag].y /= (float) atmcount;
1957 			fragCenters[ifrag].z /= (float) atmcount;
1958 		}
1959 
1960 		for (long ifrag=0; ifrag<NextId; ifrag++) {
1961 			for (int i=1; i<NumMolecules; i++) {
1962 				if ((ifrag+1) == NextId) break;	//we've ran out of fragments, the last one will have to be short
1963 				CPoint3D offset;
1964 				offset.x = fragCenters[ifrag].x-fragCenters[ifrag+1].x;
1965 				offset.y = fragCenters[ifrag].y-fragCenters[ifrag+1].y;
1966 				offset.z = fragCenters[ifrag].z-fragCenters[ifrag+1].z;
1967 				float distance = offset.x * offset.x + offset.y * offset.y + offset.z * offset.z;
1968 
1969 				long targetId = ifrag+1;
1970 				for (long jfrag=ifrag+2; jfrag<NextId; jfrag++) {
1971 					offset.x = fragCenters[ifrag].x-fragCenters[jfrag].x;
1972 					offset.y = fragCenters[ifrag].y-fragCenters[jfrag].y;
1973 					offset.z = fragCenters[ifrag].z-fragCenters[jfrag].z;
1974 					float testDistance = offset.x * offset.x + offset.y * offset.y + offset.z * offset.z;
1975 					if (testDistance < distance) {
1976 						distance = testDistance;
1977 						targetId = jfrag;
1978 					}
1979 				}
1980 				//Since we are removing a fragment we need to pull down the Ids higher up
1981 				//one thing I'm not doing is recomputing the fragment center so the next addition will
1982 				//reference the original fragment only
1983 				for (long atomi=0; atomi<cFrame->GetNumAtoms(); ++atomi) {
1984 					if (newFragmentation[atomi] == (targetId+1)) newFragmentation[atomi] = (ifrag+1);
1985 					if (newFragmentation[atomi] > (targetId+1)) newFragmentation[atomi]--;
1986 				}
1987 				fragCenters.erase(fragCenters.begin() + targetId);
1988 				NextId--;
1989 			}
1990 		}
1991 		result = NextId;
1992 	}
1993 	return result;
1994 }
GetFMOFragmentId(const long & AtomId) const1995 long MoleculeData::GetFMOFragmentId(const long & AtomId) const {
1996 	if ((AtomId >= 0) && (AtomId < FMOFragmentIds.size())) {
1997 		return FMOFragmentIds[AtomId];
1998 	}
1999 	return -1;
2000 }
SetFMOFragmentId(const long & AtomId,const long & FragId)2001 void MoleculeData::SetFMOFragmentId(const long & AtomId, const long & FragId) {
2002 	if ((AtomId<0)||(AtomId>=cFrame->GetNumAtoms())) return;	//invalid atom
2003 	if ((FragId<1)||(FragId > InputOptions->FMO.GetNumberFragments())) return; //invalid fragment id
2004 	//Is the fragment id array setup?
2005 	while (FMOFragmentIds.size() < cFrame->GetNumAtoms()) {
2006 		FMOFragmentIds.push_back(1);	//simply initialize atoms to fragment 1
2007 	}
2008 	FMOFragmentIds[AtomId] = FragId;
2009 }
2010 
ExportPOV(BufferFile * Buffer,WinPrefs * Prefs)2011 void MoleculeData::ExportPOV(BufferFile *Buffer, WinPrefs *Prefs) {
2012 
2013 	Frame *lFrame = GetCurrentFramePtr();
2014 	mpAtom *lAtoms = lFrame->Atoms;
2015 
2016 	long NumAtoms = lFrame->NumAtoms;
2017 	float AtomScale = Prefs->GetAtomScale();
2018 	long curAtomType;
2019 	RGBColor * AtomColor;
2020 	float red, green, blue;
2021 	char tmpStr[500];
2022 
2023 	Buffer->PutText("#include \"transforms.inc\"\n\n");
2024 
2025 	sprintf(tmpStr,
2026 			"camera {\n"
2027 			"\tlocation <0, 0, 0>\n"
2028 			"\tsky <0, 1, 0>\n"
2029 			"\tlook_at <0, 0, -1>\n"
2030 			"}\n\n");
2031 	Buffer->PutText(tmpStr);
2032 
2033 	sprintf(tmpStr,
2034 			"light_source {\n"
2035 			"\t<6, 6, 12>, rgb <1, 1, 1>\n"
2036 			"}\n\n");
2037 	Buffer->PutText(tmpStr);
2038 
2039 	sprintf(tmpStr,
2040 			"light_source {\n"
2041 			"\t<-6, 6, 12>, rgb <1, 1, 1>\n"
2042 			"}\n\n");
2043 	Buffer->PutText(tmpStr);
2044 
2045 	sprintf(tmpStr,
2046 			"background {\n"
2047 			"\trgb <1, 1, 1>\n"
2048 			"}\n\n");
2049 	Buffer->PutText(tmpStr);
2050 
2051 	Buffer->PutText("#declare AtomBondFinish = finish {specular 0.95 roughness 0.005}\n");
2052 	Buffer->PutText("#declare SurfaceFinish = finish {specular 0.95 roughness 0.001}\n\n");
2053 
2054 	Buffer->PutText("union {\n");
2055 	for (long iatom = 0; iatom < NumAtoms; iatom++) {
2056 		if (lAtoms[iatom].GetInvisibility()) continue;
2057 
2058 		curAtomType = lAtoms[iatom].GetType() - 1;
2059 		AtomColor = Prefs->GetAtomColorLoc(curAtomType);
2060 		red = AtomColor->red / 65536.0;
2061 		green = AtomColor->green / 65536.0;
2062 		blue = AtomColor->blue / 65536.0;
2063 
2064 		float radius = AtomScale*Prefs->GetAtomSize(curAtomType);
2065 		Buffer->PutText("sphere {\n");
2066 		sprintf(tmpStr, "\t<%f, %f, %f>, %f\n",
2067 				lAtoms[iatom].Position.x,
2068 				lAtoms[iatom].Position.y,
2069 				lAtoms[iatom].Position.z, radius);
2070 		Buffer->PutText(tmpStr);
2071 		sprintf(tmpStr, "\ttexture {\n"
2072 				"\t\tpigment {color rgb <%f, %f, %f>}\n"
2073 				"\t\tfinish {AtomBondFinish}\n"
2074 				"\t}\n", red, green, blue);
2075 		Buffer->PutText(tmpStr);
2076 		Buffer->PutText("}\n\n");
2077 	}
2078 
2079 	Bond *lBonds = lFrame->Bonds;
2080 	long NumBonds = lFrame->NumBonds;
2081 	double BondSize = Prefs->GetQD3DBondWidth();
2082 	for (long ibond = 0; ibond < NumBonds; ibond++) {
2083 		const mpAtom& atom1 = lAtoms[lBonds[ibond].Atom1];
2084 		const mpAtom& atom2 = lAtoms[lBonds[ibond].Atom2];
2085 
2086 		CPoint3D offset = atom2.Position - atom1.Position;
2087 		double length = offset.Magnitude();
2088 
2089 		double radius1 = AtomScale * Prefs->GetAtomSize(atom1.GetType() - 1);
2090 		double radius2 = AtomScale * Prefs->GetAtomSize(atom2.GetType() - 1);
2091 		double percent1 = radius1 / length;
2092 		double percent2 = radius2 / length;
2093 		double centerPercent = 0.5 + 0.5*(percent1-percent2);
2094 		CPoint3D halfway = atom1.Position + offset * centerPercent;
2095 
2096 		AtomColor = Prefs->GetAtomColorLoc(atom1.GetType() - 1);
2097 		red = AtomColor->red / 65536.0;
2098 		green = AtomColor->green / 65536.0;
2099 		blue = AtomColor->blue / 65536.0;
2100 
2101 		Buffer->PutText("cylinder {\n");
2102 		sprintf(tmpStr, "\t<%f, %f, %f>, <%f, %f, %f>, %f\n",
2103 				atom1.Position.x, atom1.Position.y, atom1.Position.z,
2104 				halfway.x, halfway.y, halfway.z, BondSize);
2105 		Buffer->PutText(tmpStr);
2106 		sprintf(tmpStr,
2107 				"\ttexture {\n"
2108 				"\t\tpigment {color rgb <%f, %f, %f>}\n"
2109 				"\t\tfinish {AtomBondFinish}\n"
2110 				"\t}\n",
2111 				red, green, blue);
2112 		Buffer->PutText(tmpStr);
2113 		Buffer->PutText("}\n\n");
2114 
2115 		AtomColor = Prefs->GetAtomColorLoc(atom2.GetType() - 1);
2116 		red = AtomColor->red / 65536.0;
2117 		green = AtomColor->green / 65536.0;
2118 		blue = AtomColor->blue / 65536.0;
2119 
2120 		Buffer->PutText("cylinder {\n");
2121 		sprintf(tmpStr, "\t<%f, %f, %f>, <%f, %f, %f> %f\n",
2122 				atom2.Position.x, atom2.Position.y, atom2.Position.z,
2123 				halfway.x, halfway.y, halfway.z, BondSize);
2124 		Buffer->PutText(tmpStr);
2125 		sprintf(tmpStr, "\ttexture {\n"
2126 				"\t\tpigment {color rgb <%f, %f, %f>}\n"
2127 				"\t\tfinish {AtomBondFinish}\n"
2128 				"\t}\n", red, green, blue);
2129 		Buffer->PutText(tmpStr);
2130 		Buffer->PutText("}\n\n");
2131 	}
2132 
2133 	// Export any surfaces.
2134 	Surface *lSurface = lFrame->SurfaceList;
2135 	while (lSurface) {
2136 		Buffer->PutText("// ");
2137 		Buffer->PutText(lSurface->GetLabel());
2138 		Buffer->PutText("\n");
2139 		lSurface->ExportPOV(this, Prefs, Buffer);
2140 		lSurface = lSurface->GetNextSurface();
2141 	}
2142 
2143 	// Now, transform scene to mimic current rotation and translation.
2144 	float *m = (float *) TotalRotation;
2145 	sprintf(tmpStr,
2146 			"\n\tmatrix <%f, %f, %f,"
2147 			"\n\t        %f, %f, %f,"
2148 			"\n\t        %f, %f, %f,"
2149 			"\n\t        %f, %f, %f>\n",
2150 			m[0], m[1], m[2], m[4], m[5], m[6],
2151 			m[8], m[9], m[10], m[12], m[13], m[14]);
2152 	Buffer->PutText(tmpStr);
2153 	Buffer->PutText("\n\tscale <-1, 1, 1>\n");
2154 	sprintf(tmpStr, "\n\ttranslate <0, 0, %f>\n", -WindowSize);
2155 	Buffer->PutText(tmpStr);
2156 	Buffer->PutText("}\n\n");
2157 
2158 	if (Prefs->ShowAtomicSymbolLabels()) {
2159 		CPoint3D text_pos;
2160 		wxString atomic_symbol;
2161 		unsigned int i;
2162 
2163 		for (i = 0; i < kMaxAtomTypes; i++) {
2164 			Prefs->GetAtomLabel(i, atomic_symbol);
2165 			sprintf(tmpStr,
2166 					"#declare Atom_%03d = "
2167 					"   text {"
2168 					"      ttf \"timrom.ttf\", \"%s\", 0.01, 0"
2169 					"   }\n\n",
2170 					i, (const char *) atomic_symbol.mb_str(wxConvUTF8));
2171 			Buffer->PutText(tmpStr);
2172 		}
2173 
2174 		Buffer->PutText("union {\n");
2175 		float radius;
2176 		for (long iatom = 0; iatom < NumAtoms; iatom++) {
2177 			if (lAtoms[iatom].GetInvisibility()) continue;
2178 
2179 			Rotate3DPt(TotalRotation, lAtoms[iatom].Position, &text_pos);
2180 			curAtomType = lAtoms[iatom].GetType() - 1;
2181 			radius = AtomScale * Prefs->GetAtomSize(curAtomType);
2182 
2183 			AtomColor = Prefs->GetAtomColorLoc(curAtomType);
2184 			red = AtomColor->red / 65536.0;
2185 			green = AtomColor->green / 65536.0;
2186 			blue = AtomColor->blue / 65536.0;
2187 
2188 			sprintf(tmpStr,
2189 					"object {\n"
2190 					"   Atom_%03ld\n"
2191 					"   Center_Trans(Atom_%03ld, x + y)\n"
2192 					"   scale <0.25, 0.25, 1.0>\n"
2193 					"   translate <%f, %f, %f>\n"
2194 					"   no_shadow\n"
2195 					"   pigment { color rgb <%f, %f, %f> }\n"
2196 					"}\n\n", curAtomType, curAtomType,
2197 					text_pos.x, text_pos.y, text_pos.z + radius,
2198 					1.0f - red, 1.0f - green, 1.0f - blue);
2199 			Buffer->PutText(tmpStr);
2200 		}
2201 		Buffer->PutText("\n\tscale <-1, 1, 1>\n");
2202 		sprintf(tmpStr, "\n\ttranslate <0, 0, %f>\n", -WindowSize);
2203 		Buffer->PutText(tmpStr);
2204 		Buffer->PutText("}\n");
2205 	}
2206 
2207 }
2208