source: src/molecule.cpp@ 681a8a

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 681a8a was 681a8a, checked in by Frederik Heber <heber@…>, 15 years ago

in molecule::OutputTrajectoriesXYZ() ActOnAllAtoms used by new function atom::OutputTrajectoryXYZ().

  • Property mode set to 100755
File size: 47.7 KB
Line 
1/** \file molecules.cpp
2 *
3 * Functions for the class molecule.
4 *
5 */
6
7#include "config.hpp"
8#include "helpers.hpp"
9#include "memoryallocator.hpp"
10#include "molecule.hpp"
11
12/************************************* Functions for class molecule *********************************/
13
14/** Constructor of class molecule.
15 * Initialises molecule list with correctly referenced start and end, and sets molecule::last_atom to zero.
16 */
17molecule::molecule(periodentafel *teil)
18{
19 // init atom chain list
20 start = new atom;
21 end = new atom;
22 start->father = NULL;
23 end->father = NULL;
24 link(start,end);
25 InternalPointer = start;
26 // init bond chain list
27 first = new bond(start, end, 1, -1);
28 last = new bond(start, end, 1, -1);
29 link(first,last);
30 // other stuff
31 MDSteps = 0;
32 last_atom = 0;
33 elemente = teil;
34 AtomCount = 0;
35 BondCount = 0;
36 NoNonBonds = 0;
37 NoNonHydrogen = 0;
38 NoCyclicBonds = 0;
39 ListOfBondsPerAtom = NULL;
40 NumberOfBondsPerAtom = NULL;
41 ElementCount = 0;
42 for(int i=MAX_ELEMENTS;i--;)
43 ElementsInMolecule[i] = 0;
44 cell_size[0] = cell_size[2] = cell_size[5]= 20.;
45 cell_size[1] = cell_size[3] = cell_size[4]= 0.;
46 strcpy(name,"none");
47 IndexNr = -1;
48 ActiveFlag = false;
49 TesselStruct = NULL;
50};
51
52/** Destructor of class molecule.
53 * Initialises molecule list with correctly referenced start and end, and sets molecule::last_atom to zero.
54 */
55molecule::~molecule()
56{
57 if (ListOfBondsPerAtom != NULL)
58 for(int i=AtomCount;i--;)
59 Free(&ListOfBondsPerAtom[i]);
60 Free(&ListOfBondsPerAtom);
61 Free(&NumberOfBondsPerAtom);
62 if (TesselStruct != NULL)
63 delete(TesselStruct);
64 CleanupMolecule();
65 delete(first);
66 delete(last);
67 delete(end);
68 delete(start);
69};
70
71
72/** Adds given atom \a *pointer from molecule list.
73 * Increases molecule::last_atom and gives last number to added atom and names it according to its element::abbrev and molecule::AtomCount
74 * \param *pointer allocated and set atom
75 * \return true - succeeded, false - atom not found in list
76 */
77bool molecule::AddAtom(atom *pointer)
78{
79 if (pointer != NULL) {
80 pointer->sort = &pointer->nr;
81 pointer->nr = last_atom++; // increase number within molecule
82 AtomCount++;
83 if (pointer->type != NULL) {
84 if (ElementsInMolecule[pointer->type->Z] == 0)
85 ElementCount++;
86 ElementsInMolecule[pointer->type->Z]++; // increase number of elements
87 if (pointer->type->Z != 1)
88 NoNonHydrogen++;
89 if (pointer->Name == NULL) {
90 Free(&pointer->Name);
91 pointer->Name = Malloc<char>(6, "molecule::AddAtom: *pointer->Name");
92 sprintf(pointer->Name, "%2s%02d", pointer->type->symbol, pointer->nr+1);
93 }
94 }
95 return add(pointer, end);
96 } else
97 return false;
98};
99
100/** Adds a copy of the given atom \a *pointer from molecule list.
101 * Increases molecule::last_atom and gives last number to added atom.
102 * \param *pointer allocated and set atom
103 * \return pointer to the newly added atom
104 */
105atom * molecule::AddCopyAtom(atom *pointer)
106{
107 if (pointer != NULL) {
108 atom *walker = new atom(pointer);
109 walker->Name = Malloc<char>(strlen(pointer->Name) + 1, "atom::atom: *Name");
110 strcpy (walker->Name, pointer->Name);
111 walker->nr = last_atom++; // increase number within molecule
112 add(walker, end);
113 if ((pointer->type != NULL) && (pointer->type->Z != 1))
114 NoNonHydrogen++;
115 AtomCount++;
116 return walker;
117 } else
118 return NULL;
119};
120
121/** Adds a Hydrogen atom in replacement for the given atom \a *partner in bond with a *origin.
122 * Here, we have to distinguish between single, double or triple bonds as stated by \a BondDegree, that each demand
123 * a different scheme when adding \a *replacement atom for the given one.
124 * -# Single Bond: Simply add new atom with bond distance rescaled to typical hydrogen one
125 * -# Double Bond: Here, we need the **BondList of the \a *origin atom, by scanning for the other bonds instead of
126 * *Bond, we use the through these connected atoms to determine the plane they lie in, vector::MakeNormalvector().
127 * The orthonormal vector to this plane along with the vector in *Bond direction determines the plane the two
128 * replacing hydrogens shall lie in. Now, all remains to do is take the usual hydrogen double bond angle for the
129 * element of *origin and form the sin/cos admixture of both plane vectors for the new coordinates of the two
130 * hydrogens forming this angle with *origin.
131 * -# Triple Bond: The idea is to set up a tetraoid (C1-H1-H2-H3) (however the lengths \f$b\f$ of the sides of the base
132 * triangle formed by the to be added hydrogens are not equal to the typical bond distance \f$l\f$ but have to be
133 * determined from the typical angle \f$\alpha\f$ for a hydrogen triple connected to the element of *origin):
134 * We have the height \f$d\f$ as the vector in *Bond direction (from triangle C1-H1-H2).
135 * \f[ h = l \cdot \cos{\left (\frac{\alpha}{2} \right )} \qquad b = 2l \cdot \sin{\left (\frac{\alpha}{2} \right)} \quad \rightarrow \quad d = l \cdot \sqrt{\cos^2{\left (\frac{\alpha}{2} \right)}-\frac{1}{3}\cdot\sin^2{\left (\frac{\alpha}{2}\right )}}
136 * \f]
137 * vector::GetNormalvector() creates one orthonormal vector from this *Bond vector and vector::MakeNormalvector creates
138 * the third one from the former two vectors. The latter ones form the plane of the base triangle mentioned above.
139 * The lengths for these are \f$f\f$ and \f$g\f$ (from triangle H1-H2-(center of H1-H2-H3)) with knowledge that
140 * the median lines in an isosceles triangle meet in the center point with a ratio 2:1.
141 * \f[ f = \frac{b}{\sqrt{3}} \qquad g = \frac{b}{2}
142 * \f]
143 * as the coordination of all three atoms in the coordinate system of these three vectors:
144 * \f$\pmatrix{d & f & 0}\f$, \f$\pmatrix{d & -0.5 \cdot f & g}\f$ and \f$\pmatrix{d & -0.5 \cdot f & -g}\f$.
145 *
146 * \param *out output stream for debugging
147 * \param *Bond pointer to bond between \a *origin and \a *replacement
148 * \param *TopOrigin son of \a *origin of upper level molecule (the atom added to this molecule as a copy of \a *origin)
149 * \param *origin pointer to atom which acts as the origin for scaling the added hydrogen to correct bond length
150 * \param *replacement pointer to the atom which shall be copied as a hydrogen atom in this molecule
151 * \param **BondList list of bonds \a *replacement has (necessary to determine plane for double and triple bonds)
152 * \param NumBond number of bonds in \a **BondList
153 * \param isAngstroem whether the coordination of the given atoms is in AtomicLength (false) or Angstrom(true)
154 * \return number of atoms added, if < bond::BondDegree then something went wrong
155 * \todo double and triple bonds splitting (always use the tetraeder angle!)
156 */
157bool molecule::AddHydrogenReplacementAtom(ofstream *out, bond *TopBond, atom *BottomOrigin, atom *TopOrigin, atom *TopReplacement, bond **BondList, int NumBond, bool IsAngstroem)
158{
159 double bondlength; // bond length of the bond to be replaced/cut
160 double bondangle; // bond angle of the bond to be replaced/cut
161 double BondRescale; // rescale value for the hydrogen bond length
162 bool AllWentWell = true; // flag gathering the boolean return value of molecule::AddAtom and other functions, as return value on exit
163 bond *FirstBond = NULL, *SecondBond = NULL; // Other bonds in double bond case to determine "other" plane
164 atom *FirstOtherAtom = NULL, *SecondOtherAtom = NULL, *ThirdOtherAtom = NULL; // pointer to hydrogen atoms to be added
165 double b,l,d,f,g, alpha, factors[NDIM]; // hold temporary values in triple bond case for coordination determination
166 Vector Orthovector1, Orthovector2; // temporary vectors in coordination construction
167 Vector InBondvector; // vector in direction of *Bond
168 bond *Binder = NULL;
169 double *matrix;
170
171// *out << Verbose(3) << "Begin of AddHydrogenReplacementAtom." << endl;
172 // create vector in direction of bond
173 InBondvector.CopyVector(&TopReplacement->x);
174 InBondvector.SubtractVector(&TopOrigin->x);
175 bondlength = InBondvector.Norm();
176
177 // is greater than typical bond distance? Then we have to correct periodically
178 // the problem is not the H being out of the box, but InBondvector have the wrong direction
179 // due to TopReplacement or Origin being on the wrong side!
180 if (bondlength > BondDistance) {
181// *out << Verbose(4) << "InBondvector is: ";
182// InBondvector.Output(out);
183// *out << endl;
184 Orthovector1.Zero();
185 for (int i=NDIM;i--;) {
186 l = TopReplacement->x.x[i] - TopOrigin->x.x[i];
187 if (fabs(l) > BondDistance) { // is component greater than bond distance
188 Orthovector1.x[i] = (l < 0) ? -1. : +1.;
189 } // (signs are correct, was tested!)
190 }
191 matrix = ReturnFullMatrixforSymmetric(cell_size);
192 Orthovector1.MatrixMultiplication(matrix);
193 InBondvector.SubtractVector(&Orthovector1); // subtract just the additional translation
194 Free(&matrix);
195 bondlength = InBondvector.Norm();
196// *out << Verbose(4) << "Corrected InBondvector is now: ";
197// InBondvector.Output(out);
198// *out << endl;
199 } // periodic correction finished
200
201 InBondvector.Normalize();
202 // get typical bond length and store as scale factor for later
203 BondRescale = TopOrigin->type->HBondDistance[TopBond->BondDegree-1];
204 if (BondRescale == -1) {
205 cerr << Verbose(3) << "ERROR: There is no typical hydrogen bond distance in replacing bond (" << TopOrigin->Name << "<->" << TopReplacement->Name << ") of degree " << TopBond->BondDegree << "!" << endl;
206 return false;
207 BondRescale = bondlength;
208 } else {
209 if (!IsAngstroem)
210 BondRescale /= (1.*AtomicLengthToAngstroem);
211 }
212
213 // discern single, double and triple bonds
214 switch(TopBond->BondDegree) {
215 case 1:
216 FirstOtherAtom = new atom(); // new atom
217 FirstOtherAtom->type = elemente->FindElement(1); // element is Hydrogen
218 FirstOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
219 FirstOtherAtom->FixedIon = TopReplacement->FixedIon;
220 if (TopReplacement->type->Z == 1) { // neither rescale nor replace if it's already hydrogen
221 FirstOtherAtom->father = TopReplacement;
222 BondRescale = bondlength;
223 } else {
224 FirstOtherAtom->father = NULL; // if we replace hydrogen, we mark it as our father, otherwise we are just an added hydrogen with no father
225 }
226 InBondvector.Scale(&BondRescale); // rescale the distance vector to Hydrogen bond length
227 FirstOtherAtom->x.CopyVector(&TopOrigin->x); // set coordination to origin ...
228 FirstOtherAtom->x.AddVector(&InBondvector); // ... and add distance vector to replacement atom
229 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
230// *out << Verbose(4) << "Added " << *FirstOtherAtom << " at: ";
231// FirstOtherAtom->x.Output(out);
232// *out << endl;
233 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
234 Binder->Cyclic = false;
235 Binder->Type = TreeEdge;
236 break;
237 case 2:
238 // determine two other bonds (warning if there are more than two other) plus valence sanity check
239 for (int i=0;i<NumBond;i++) {
240 if (BondList[i] != TopBond) {
241 if (FirstBond == NULL) {
242 FirstBond = BondList[i];
243 FirstOtherAtom = BondList[i]->GetOtherAtom(TopOrigin);
244 } else if (SecondBond == NULL) {
245 SecondBond = BondList[i];
246 SecondOtherAtom = BondList[i]->GetOtherAtom(TopOrigin);
247 } else {
248 *out << Verbose(3) << "WARNING: Detected more than four bonds for atom " << TopOrigin->Name;
249 }
250 }
251 }
252 if (SecondOtherAtom == NULL) { // then we have an atom with valence four, but only 3 bonds: one to replace and one which is TopBond (third is FirstBond)
253 SecondBond = TopBond;
254 SecondOtherAtom = TopReplacement;
255 }
256 if (FirstOtherAtom != NULL) { // then we just have this double bond and the plane does not matter at all
257// *out << Verbose(3) << "Regarding the double bond (" << TopOrigin->Name << "<->" << TopReplacement->Name << ") to be constructed: Taking " << FirstOtherAtom->Name << " and " << SecondOtherAtom->Name << " along with " << TopOrigin->Name << " to determine orthogonal plane." << endl;
258
259 // determine the plane of these two with the *origin
260 AllWentWell = AllWentWell && Orthovector1.MakeNormalVector(&TopOrigin->x, &FirstOtherAtom->x, &SecondOtherAtom->x);
261 } else {
262 Orthovector1.GetOneNormalVector(&InBondvector);
263 }
264 //*out << Verbose(3)<< "Orthovector1: ";
265 //Orthovector1.Output(out);
266 //*out << endl;
267 // orthogonal vector and bond vector between origin and replacement form the new plane
268 Orthovector1.MakeNormalVector(&InBondvector);
269 Orthovector1.Normalize();
270 //*out << Verbose(3) << "ReScaleCheck: " << Orthovector1.Norm() << " and " << InBondvector.Norm() << "." << endl;
271
272 // create the two Hydrogens ...
273 FirstOtherAtom = new atom();
274 SecondOtherAtom = new atom();
275 FirstOtherAtom->type = elemente->FindElement(1);
276 SecondOtherAtom->type = elemente->FindElement(1);
277 FirstOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
278 FirstOtherAtom->FixedIon = TopReplacement->FixedIon;
279 SecondOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
280 SecondOtherAtom->FixedIon = TopReplacement->FixedIon;
281 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
282 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
283 bondangle = TopOrigin->type->HBondAngle[1];
284 if (bondangle == -1) {
285 *out << Verbose(3) << "ERROR: There is no typical hydrogen bond angle in replacing bond (" << TopOrigin->Name << "<->" << TopReplacement->Name << ") of degree " << TopBond->BondDegree << "!" << endl;
286 return false;
287 bondangle = 0;
288 }
289 bondangle *= M_PI/180./2.;
290// *out << Verbose(3) << "ReScaleCheck: InBondvector ";
291// InBondvector.Output(out);
292// *out << endl;
293// *out << Verbose(3) << "ReScaleCheck: Orthovector ";
294// Orthovector1.Output(out);
295// *out << endl;
296// *out << Verbose(3) << "Half the bond angle is " << bondangle << ", sin and cos of it: " << sin(bondangle) << ", " << cos(bondangle) << endl;
297 FirstOtherAtom->x.Zero();
298 SecondOtherAtom->x.Zero();
299 for(int i=NDIM;i--;) { // rotate by half the bond angle in both directions (InBondvector is bondangle = 0 direction)
300 FirstOtherAtom->x.x[i] = InBondvector.x[i] * cos(bondangle) + Orthovector1.x[i] * (sin(bondangle));
301 SecondOtherAtom->x.x[i] = InBondvector.x[i] * cos(bondangle) + Orthovector1.x[i] * (-sin(bondangle));
302 }
303 FirstOtherAtom->x.Scale(&BondRescale); // rescale by correct BondDistance
304 SecondOtherAtom->x.Scale(&BondRescale);
305 //*out << Verbose(3) << "ReScaleCheck: " << FirstOtherAtom->x.Norm() << " and " << SecondOtherAtom->x.Norm() << "." << endl;
306 for(int i=NDIM;i--;) { // and make relative to origin atom
307 FirstOtherAtom->x.x[i] += TopOrigin->x.x[i];
308 SecondOtherAtom->x.x[i] += TopOrigin->x.x[i];
309 }
310 // ... and add to molecule
311 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
312 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
313// *out << Verbose(4) << "Added " << *FirstOtherAtom << " at: ";
314// FirstOtherAtom->x.Output(out);
315// *out << endl;
316// *out << Verbose(4) << "Added " << *SecondOtherAtom << " at: ";
317// SecondOtherAtom->x.Output(out);
318// *out << endl;
319 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
320 Binder->Cyclic = false;
321 Binder->Type = TreeEdge;
322 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
323 Binder->Cyclic = false;
324 Binder->Type = TreeEdge;
325 break;
326 case 3:
327 // take the "usual" tetraoidal angle and add the three Hydrogen in direction of the bond (height of the tetraoid)
328 FirstOtherAtom = new atom();
329 SecondOtherAtom = new atom();
330 ThirdOtherAtom = new atom();
331 FirstOtherAtom->type = elemente->FindElement(1);
332 SecondOtherAtom->type = elemente->FindElement(1);
333 ThirdOtherAtom->type = elemente->FindElement(1);
334 FirstOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
335 FirstOtherAtom->FixedIon = TopReplacement->FixedIon;
336 SecondOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
337 SecondOtherAtom->FixedIon = TopReplacement->FixedIon;
338 ThirdOtherAtom->v.CopyVector(&TopReplacement->v); // copy velocity
339 ThirdOtherAtom->FixedIon = TopReplacement->FixedIon;
340 FirstOtherAtom->father = NULL; // we are just an added hydrogen with no father
341 SecondOtherAtom->father = NULL; // we are just an added hydrogen with no father
342 ThirdOtherAtom->father = NULL; // we are just an added hydrogen with no father
343
344 // we need to vectors orthonormal the InBondvector
345 AllWentWell = AllWentWell && Orthovector1.GetOneNormalVector(&InBondvector);
346// *out << Verbose(3) << "Orthovector1: ";
347// Orthovector1.Output(out);
348// *out << endl;
349 AllWentWell = AllWentWell && Orthovector2.MakeNormalVector(&InBondvector, &Orthovector1);
350// *out << Verbose(3) << "Orthovector2: ";
351// Orthovector2.Output(out);
352// *out << endl;
353
354 // create correct coordination for the three atoms
355 alpha = (TopOrigin->type->HBondAngle[2])/180.*M_PI/2.; // retrieve triple bond angle from database
356 l = BondRescale; // desired bond length
357 b = 2.*l*sin(alpha); // base length of isosceles triangle
358 d = l*sqrt(cos(alpha)*cos(alpha) - sin(alpha)*sin(alpha)/3.); // length for InBondvector
359 f = b/sqrt(3.); // length for Orthvector1
360 g = b/2.; // length for Orthvector2
361// *out << Verbose(3) << "Bond length and half-angle: " << l << ", " << alpha << "\t (b,d,f,g) = " << b << ", " << d << ", " << f << ", " << g << ", " << endl;
362// *out << Verbose(3) << "The three Bond lengths: " << sqrt(d*d+f*f) << ", " << sqrt(d*d+(-0.5*f)*(-0.5*f)+g*g) << ", " << sqrt(d*d+(-0.5*f)*(-0.5*f)+g*g) << endl;
363 factors[0] = d;
364 factors[1] = f;
365 factors[2] = 0.;
366 FirstOtherAtom->x.LinearCombinationOfVectors(&InBondvector, &Orthovector1, &Orthovector2, factors);
367 factors[1] = -0.5*f;
368 factors[2] = g;
369 SecondOtherAtom->x.LinearCombinationOfVectors(&InBondvector, &Orthovector1, &Orthovector2, factors);
370 factors[2] = -g;
371 ThirdOtherAtom->x.LinearCombinationOfVectors(&InBondvector, &Orthovector1, &Orthovector2, factors);
372
373 // rescale each to correct BondDistance
374// FirstOtherAtom->x.Scale(&BondRescale);
375// SecondOtherAtom->x.Scale(&BondRescale);
376// ThirdOtherAtom->x.Scale(&BondRescale);
377
378 // and relative to *origin atom
379 FirstOtherAtom->x.AddVector(&TopOrigin->x);
380 SecondOtherAtom->x.AddVector(&TopOrigin->x);
381 ThirdOtherAtom->x.AddVector(&TopOrigin->x);
382
383 // ... and add to molecule
384 AllWentWell = AllWentWell && AddAtom(FirstOtherAtom);
385 AllWentWell = AllWentWell && AddAtom(SecondOtherAtom);
386 AllWentWell = AllWentWell && AddAtom(ThirdOtherAtom);
387// *out << Verbose(4) << "Added " << *FirstOtherAtom << " at: ";
388// FirstOtherAtom->x.Output(out);
389// *out << endl;
390// *out << Verbose(4) << "Added " << *SecondOtherAtom << " at: ";
391// SecondOtherAtom->x.Output(out);
392// *out << endl;
393// *out << Verbose(4) << "Added " << *ThirdOtherAtom << " at: ";
394// ThirdOtherAtom->x.Output(out);
395// *out << endl;
396 Binder = AddBond(BottomOrigin, FirstOtherAtom, 1);
397 Binder->Cyclic = false;
398 Binder->Type = TreeEdge;
399 Binder = AddBond(BottomOrigin, SecondOtherAtom, 1);
400 Binder->Cyclic = false;
401 Binder->Type = TreeEdge;
402 Binder = AddBond(BottomOrigin, ThirdOtherAtom, 1);
403 Binder->Cyclic = false;
404 Binder->Type = TreeEdge;
405 break;
406 default:
407 cerr << "ERROR: BondDegree does not state single, double or triple bond!" << endl;
408 AllWentWell = false;
409 break;
410 }
411
412// *out << Verbose(3) << "End of AddHydrogenReplacementAtom." << endl;
413 return AllWentWell;
414};
415
416/** Adds given atom \a *pointer from molecule list.
417 * Increases molecule::last_atom and gives last number to added atom.
418 * \param filename name and path of xyz file
419 * \return true - succeeded, false - file not found
420 */
421bool molecule::AddXYZFile(string filename)
422{
423 istringstream *input = NULL;
424 int NumberOfAtoms = 0; // atom number in xyz read
425 int i, j; // loop variables
426 atom *Walker = NULL; // pointer to added atom
427 char shorthand[3]; // shorthand for atom name
428 ifstream xyzfile; // xyz file
429 string line; // currently parsed line
430 double x[3]; // atom coordinates
431
432 xyzfile.open(filename.c_str());
433 if (!xyzfile)
434 return false;
435
436 getline(xyzfile,line,'\n'); // Read numer of atoms in file
437 input = new istringstream(line);
438 *input >> NumberOfAtoms;
439 cout << Verbose(0) << "Parsing " << NumberOfAtoms << " atoms in file." << endl;
440 getline(xyzfile,line,'\n'); // Read comment
441 cout << Verbose(1) << "Comment: " << line << endl;
442
443 if (MDSteps == 0) // no atoms yet present
444 MDSteps++;
445 for(i=0;i<NumberOfAtoms;i++){
446 Walker = new atom;
447 getline(xyzfile,line,'\n');
448 istringstream *item = new istringstream(line);
449 //istringstream input(line);
450 //cout << Verbose(1) << "Reading: " << line << endl;
451 *item >> shorthand;
452 *item >> x[0];
453 *item >> x[1];
454 *item >> x[2];
455 Walker->type = elemente->FindElement(shorthand);
456 if (Walker->type == NULL) {
457 cerr << "Could not parse the element at line: '" << line << "', setting to H.";
458 Walker->type = elemente->FindElement(1);
459 }
460 if (Walker->Trajectory.R.size() <= (unsigned int)MDSteps) {
461 Walker->Trajectory.R.resize(MDSteps+10);
462 Walker->Trajectory.U.resize(MDSteps+10);
463 Walker->Trajectory.F.resize(MDSteps+10);
464 }
465 for(j=NDIM;j--;) {
466 Walker->x.x[j] = x[j];
467 Walker->Trajectory.R.at(MDSteps-1).x[j] = x[j];
468 Walker->Trajectory.U.at(MDSteps-1).x[j] = 0;
469 Walker->Trajectory.F.at(MDSteps-1).x[j] = 0;
470 }
471 AddAtom(Walker); // add to molecule
472 delete(item);
473 }
474 xyzfile.close();
475 delete(input);
476 return true;
477};
478
479/** Creates a copy of this molecule.
480 * \return copy of molecule
481 */
482molecule *molecule::CopyMolecule()
483{
484 molecule *copy = new molecule(elemente);
485 atom *LeftAtom = NULL, *RightAtom = NULL;
486
487 // copy all atoms
488 ActOnCopyWithEachAtom ( &molecule::AddCopyAtom, copy );
489
490 // copy all bonds
491 bond *Binder = first;
492 bond *NewBond = NULL;
493 while(Binder->next != last) {
494 Binder = Binder->next;
495
496 // get the pendant atoms of current bond in the copy molecule
497 copy->ActOnAllAtoms( &atom::EqualsFather, Binder->leftatom, &LeftAtom );
498 copy->ActOnAllAtoms( &atom::EqualsFather, Binder->rightatom, &RightAtom );
499
500 NewBond = copy->AddBond(LeftAtom, RightAtom, Binder->BondDegree);
501 NewBond->Cyclic = Binder->Cyclic;
502 if (Binder->Cyclic)
503 copy->NoCyclicBonds++;
504 NewBond->Type = Binder->Type;
505 }
506 // correct fathers
507 ActOnAllAtoms( &atom::CorrectFather );
508
509 // copy values
510 copy->CountAtoms((ofstream *)&cout);
511 copy->CountElements();
512 if (first->next != last) { // if adjaceny list is present
513 copy->BondDistance = BondDistance;
514 copy->CreateListOfBondsPerAtom((ofstream *)&cout);
515 }
516
517 return copy;
518};
519
520
521/**
522 * Copies all atoms of a molecule which are within the defined parallelepiped.
523 *
524 * @param offest for the origin of the parallelepiped
525 * @param three vectors forming the matrix that defines the shape of the parallelpiped
526 */
527molecule* molecule::CopyMoleculeFromSubRegion(Vector offset, double *parallelepiped) {
528 molecule *copy = new molecule(elemente);
529
530 ActOnCopyWithEachAtomIfTrue ( &molecule::AddCopyAtom, copy, &atom::IsInParallelepiped, offset, parallelepiped );
531
532 //TODO: copy->BuildInducedSubgraph((ofstream *)&cout, this);
533
534 return copy;
535}
536
537/** Adds a bond to a the molecule specified by two atoms, \a *first and \a *second.
538 * Also updates molecule::BondCount and molecule::NoNonBonds.
539 * \param *first first atom in bond
540 * \param *second atom in bond
541 * \return pointer to bond or NULL on failure
542 */
543bond * molecule::AddBond(atom *atom1, atom *atom2, int degree)
544{
545 bond *Binder = NULL;
546 if ((atom1 != NULL) && (FindAtom(atom1->nr) != NULL) && (atom2 != NULL) && (FindAtom(atom2->nr) != NULL)) {
547 Binder = new bond(atom1, atom2, degree, BondCount++);
548 if ((atom1->type != NULL) && (atom1->type->Z != 1) && (atom2->type != NULL) && (atom2->type->Z != 1))
549 NoNonBonds++;
550 add(Binder, last);
551 } else {
552 cerr << Verbose(1) << "ERROR: Could not add bond between " << atom1->Name << " and " << atom2->Name << " as one or both are not present in the molecule." << endl;
553 }
554 return Binder;
555};
556
557/** Remove bond from bond chain list.
558 * \todo Function not implemented yet
559 * \param *pointer bond pointer
560 * \return true - bound found and removed, false - bond not found/removed
561 */
562bool molecule::RemoveBond(bond *pointer)
563{
564 //cerr << Verbose(1) << "molecule::RemoveBond: Function not implemented yet." << endl;
565 removewithoutcheck(pointer);
566 return true;
567};
568
569/** Remove every bond from bond chain list that atom \a *BondPartner is a constituent of.
570 * \todo Function not implemented yet
571 * \param *BondPartner atom to be removed
572 * \return true - bounds found and removed, false - bonds not found/removed
573 */
574bool molecule::RemoveBonds(atom *BondPartner)
575{
576 cerr << Verbose(1) << "molecule::RemoveBond: Function not implemented yet." << endl;
577 return false;
578};
579
580/** Set molecule::name from the basename without suffix in the given \a *filename.
581 * \param *filename filename
582 */
583void molecule::SetNameFromFilename(const char *filename)
584{
585 int length = 0;
586 const char *molname = strrchr(filename, '/');
587 if (molname != NULL)
588 molname += sizeof(char); // search for filename without dirs
589 else
590 molname = filename; // contains no slashes
591 char *endname = strchr(molname, '.');
592 if ((endname == NULL) || (endname < molname))
593 length = strlen(molname);
594 else
595 length = strlen(molname) - strlen(endname);
596 strncpy(name, molname, length);
597 name[length]='\0';
598};
599
600/** Sets the molecule::cell_size to the components of \a *dim (rectangular box)
601 * \param *dim vector class
602 */
603void molecule::SetBoxDimension(Vector *dim)
604{
605 cell_size[0] = dim->x[0];
606 cell_size[1] = 0.;
607 cell_size[2] = dim->x[1];
608 cell_size[3] = 0.;
609 cell_size[4] = 0.;
610 cell_size[5] = dim->x[2];
611};
612
613/** Removes atom from molecule list and deletes it.
614 * \param *pointer atom to be removed
615 * \return true - succeeded, false - atom not found in list
616 */
617bool molecule::RemoveAtom(atom *pointer)
618{
619 if (ElementsInMolecule[pointer->type->Z] != 0) { // this would indicate an error
620 ElementsInMolecule[pointer->type->Z]--; // decrease number of atom of this element
621 AtomCount--;
622 } else
623 cerr << "ERROR: Atom " << pointer->Name << " is of element " << pointer->type->Z << " but the entry in the table of the molecule is 0!" << endl;
624 if (ElementsInMolecule[pointer->type->Z] == 0) // was last atom of this element?
625 ElementCount--;
626 return remove(pointer, start, end);
627};
628
629/** Removes atom from molecule list, but does not delete it.
630 * \param *pointer atom to be removed
631 * \return true - succeeded, false - atom not found in list
632 */
633bool molecule::UnlinkAtom(atom *pointer)
634{
635 if (pointer == NULL)
636 return false;
637 if (ElementsInMolecule[pointer->type->Z] != 0) // this would indicate an error
638 ElementsInMolecule[pointer->type->Z]--; // decrease number of atom of this element
639 else
640 cerr << "ERROR: Atom " << pointer->Name << " is of element " << pointer->type->Z << " but the entry in the table of the molecule is 0!" << endl;
641 if (ElementsInMolecule[pointer->type->Z] == 0) // was last atom of this element?
642 ElementCount--;
643 unlink(pointer);
644 return true;
645};
646
647/** Removes every atom from molecule list.
648 * \return true - succeeded, false - atom not found in list
649 */
650bool molecule::CleanupMolecule()
651{
652 return (cleanup(start,end) && cleanup(first,last));
653};
654
655/** Finds an atom specified by its continuous number.
656 * \param Nr number of atom withim molecule
657 * \return pointer to atom or NULL
658 */
659atom * molecule::FindAtom(int Nr) const{
660 atom * walker = find(&Nr, start,end);
661 if (walker != NULL) {
662 //cout << Verbose(0) << "Found Atom Nr. " << walker->nr << endl;
663 return walker;
664 } else {
665 cout << Verbose(0) << "Atom not found in list." << endl;
666 return NULL;
667 }
668};
669
670/** Asks for atom number, and checks whether in list.
671 * \param *text question before entering
672 */
673atom * molecule::AskAtom(string text)
674{
675 int No;
676 atom *ion = NULL;
677 do {
678 //cout << Verbose(0) << "============Atom list==========================" << endl;
679 //mol->Output((ofstream *)&cout);
680 //cout << Verbose(0) << "===============================================" << endl;
681 cout << Verbose(0) << text;
682 cin >> No;
683 ion = this->FindAtom(No);
684 } while (ion == NULL);
685 return ion;
686};
687
688/** Checks if given coordinates are within cell volume.
689 * \param *x array of coordinates
690 * \return true - is within, false - out of cell
691 */
692bool molecule::CheckBounds(const Vector *x) const
693{
694 bool result = true;
695 int j =-1;
696 for (int i=0;i<NDIM;i++) {
697 j += i+1;
698 result = result && ((x->x[i] >= 0) && (x->x[i] < cell_size[j]));
699 }
700 //return result;
701 return true; /// probably not gonna use the check no more
702};
703
704/** Prints molecule to *out.
705 * \param *out output stream
706 */
707bool molecule::Output(ofstream *out)
708{
709 int ElementNo[MAX_ELEMENTS], AtomNo[MAX_ELEMENTS];
710 CountElements();
711
712 for (int i=0;i<MAX_ELEMENTS;++i) {
713 AtomNo[i] = 0;
714 ElementNo[i] = 0;
715 }
716 if (out == NULL) {
717 return false;
718 } else {
719 *out << "#Ion_TypeNr._Nr.R[0] R[1] R[2] MoveType (0 MoveIon, 1 FixedIon)" << endl;
720 SetIndexedArrayForEachAtomTo ( ElementNo, &element::Z, &AbsoluteValue, 1);
721 int current=1;
722 for (int i=0;i<MAX_ELEMENTS;++i) {
723 if (ElementNo[i] == 1)
724 ElementNo[i] = current++;
725 }
726 ActOnAllAtoms( &atom::Output, out, ElementNo, AtomNo, (const char *) NULL ); // (bool (atom::*)(int *, int *, ofstream *, const char *))
727 return true;
728 }
729};
730
731/** Prints molecule with all atomic trajectory positions to *out.
732 * \param *out output stream
733 */
734bool molecule::OutputTrajectories(ofstream *out)
735{
736 atom *walker = NULL;
737 int ElementNo[MAX_ELEMENTS], AtomNo[MAX_ELEMENTS];
738 CountElements();
739
740 if (out == NULL) {
741 return false;
742 } else {
743 for (int step = 0; step < MDSteps; step++) {
744 if (step == 0) {
745 *out << "#Ion_TypeNr._Nr.R[0] R[1] R[2] MoveType (0 MoveIon, 1 FixedIon)" << endl;
746 } else {
747 *out << "# ====== MD step " << step << " =========" << endl;
748 }
749 for (int i=0;i<MAX_ELEMENTS;++i) {
750 AtomNo[i] = 0;
751 ElementNo[i] = 0;
752 }
753 SetIndexedArrayForEachAtomTo ( ElementNo, &element::Z, &AbsoluteValue, 1);
754 int current=1;
755 for (int i=0;i<MAX_ELEMENTS;++i) {
756 if (ElementNo[i] == 1)
757 ElementNo[i] = current++;
758 }
759 ActOnAllAtoms( &atom::OutputTrajectory, out, ElementNo, AtomNo, step );
760 }
761 return true;
762 }
763};
764
765/** Outputs contents of molecule::ListOfBondsPerAtom.
766 * \param *out output stream
767 */
768void molecule::OutputListOfBonds(ofstream *out) const
769{
770 *out << Verbose(2) << endl << "From Contents of ListOfBondsPerAtom, all non-hydrogen atoms:" << endl;
771 atom *Walker = start;
772 while (Walker->next != end) {
773 Walker = Walker->next;
774#ifdef ADDHYDROGEN
775 if (Walker->type->Z != 1) { // regard only non-hydrogen
776#endif
777 *out << Verbose(2) << "Atom " << Walker->Name << " has Bonds: "<<endl;
778 for(int j=0;j<NumberOfBondsPerAtom[Walker->nr];j++) {
779 *out << Verbose(3) << *(ListOfBondsPerAtom)[Walker->nr][j] << endl;
780 }
781#ifdef ADDHYDROGEN
782 }
783#endif
784 }
785 *out << endl;
786};
787
788/** Output of element before the actual coordination list.
789 * \param *out stream pointer
790 */
791bool molecule::Checkout(ofstream *out) const
792{
793 return elemente->Checkout(out, ElementsInMolecule);
794};
795
796/** Prints molecule with all its trajectories to *out as xyz file.
797 * \param *out output stream
798 */
799bool molecule::OutputTrajectoriesXYZ(ofstream *out)
800{
801 int No = 0;
802 time_t now;
803
804 if (out != NULL) {
805 now = time((time_t *)NULL); // Get the system time and put it into 'now' as 'calender time'
806 for (int step=0;step<MDSteps;step++) {
807 *out << AtomCount << "\n\tCreated by molecuilder, step " << step << ", on " << ctime(&now);
808 ActOnAllAtoms( &atom::OutputTrajectoryXYZ, out, step );
809 }
810 return true;
811 } else
812 return false;
813};
814
815/** Prints molecule to *out as xyz file.
816* \param *out output stream
817 */
818bool molecule::OutputXYZ(ofstream *out) const
819{
820 atom *walker = NULL;
821 int AtomNo = 0;
822 time_t now;
823
824 now = time((time_t *)NULL); // Get the system time and put it into 'now' as 'calender time'
825 walker = start;
826 while (walker->next != end) { // go through every atom and count
827 walker = walker->next;
828 AtomNo++;
829 }
830 if (out != NULL) {
831 *out << AtomNo << "\n\tCreated by molecuilder on " << ctime(&now);
832 ActOnAllAtoms( &atom::OutputXYZLine, out );
833 return true;
834 } else
835 return false;
836};
837
838/** Brings molecule::AtomCount and atom::*Name up-to-date.
839 * \param *out output stream for debugging
840 */
841void molecule::CountAtoms(ofstream *out)
842{
843 int i = 0;
844 atom *Walker = start;
845 while (Walker->next != end) {
846 Walker = Walker->next;
847 i++;
848 }
849 if ((AtomCount == 0) || (i != AtomCount)) {
850 *out << Verbose(3) << "Mismatch in AtomCount " << AtomCount << " and recounted number " << i << ", renaming all." << endl;
851 AtomCount = i;
852
853 // count NonHydrogen atoms and give each atom a unique name
854 if (AtomCount != 0) {
855 i=0;
856 NoNonHydrogen = 0;
857 Walker = start;
858 while (Walker->next != end) {
859 Walker = Walker->next;
860 Walker->nr = i; // update number in molecule (for easier referencing in FragmentMolecule lateron)
861 if (Walker->type->Z != 1) // count non-hydrogen atoms whilst at it
862 NoNonHydrogen++;
863 Free(&Walker->Name);
864 Walker->Name = Malloc<char>(6, "molecule::CountAtoms: *walker->Name");
865 sprintf(Walker->Name, "%2s%02d", Walker->type->symbol, Walker->nr+1);
866 *out << "Naming atom nr. " << Walker->nr << " " << Walker->Name << "." << endl;
867 i++;
868 }
869 } else
870 *out << Verbose(3) << "AtomCount is still " << AtomCount << ", thus counting nothing." << endl;
871 }
872};
873
874/** Brings molecule::ElementCount and molecule::ElementsInMolecule up-to-date.
875 */
876void molecule::CountElements()
877{
878 int i = 0;
879 for(i=MAX_ELEMENTS;i--;)
880 ElementsInMolecule[i] = 0;
881 ElementCount = 0;
882
883 atom *walker = start;
884 while (walker->next != end) {
885 walker = walker->next;
886 ElementsInMolecule[walker->type->Z]++;
887 i++;
888 }
889 for(i=MAX_ELEMENTS;i--;)
890 ElementCount += (ElementsInMolecule[i] != 0 ? 1 : 0);
891};
892
893
894
895/** Counts necessary number of valence electrons and returns number and SpinType.
896 * \param configuration containing everything
897 */
898void molecule::CalculateOrbitals(class config &configuration)
899{
900 configuration.MaxPsiDouble = configuration.PsiMaxNoDown = configuration.PsiMaxNoUp = configuration.PsiType = 0;
901 for(int i=MAX_ELEMENTS;i--;) {
902 if (ElementsInMolecule[i] != 0) {
903 //cout << "CalculateOrbitals: " << elemente->FindElement(i)->name << " has a valence of " << (int)elemente->FindElement(i)->Valence << " and there are " << ElementsInMolecule[i] << " of it." << endl;
904 configuration.MaxPsiDouble += ElementsInMolecule[i]*((int)elemente->FindElement(i)->Valence);
905 }
906 }
907 configuration.PsiMaxNoDown = configuration.MaxPsiDouble/2 + (configuration.MaxPsiDouble % 2);
908 configuration.PsiMaxNoUp = configuration.MaxPsiDouble/2;
909 configuration.MaxPsiDouble /= 2;
910 configuration.PsiType = (configuration.PsiMaxNoDown == configuration.PsiMaxNoUp) ? 0 : 1;
911 if ((configuration.PsiType == 1) && (configuration.ProcPEPsi < 2)) {
912 configuration.ProcPEGamma /= 2;
913 configuration.ProcPEPsi *= 2;
914 } else {
915 configuration.ProcPEGamma *= configuration.ProcPEPsi;
916 configuration.ProcPEPsi = 1;
917 }
918 configuration.InitMaxMinStopStep = configuration.MaxMinStopStep = configuration.MaxPsiDouble;
919};
920
921
922/** Creates an 2d array of pointer with an entry for each atom and each bond it has.
923 * Updates molecule::ListOfBondsPerAtom, molecule::NumberOfBondsPerAtom by parsing through
924 * bond chain list, using molecule::AtomCount and molecule::BondCount.
925 * Allocates memory, fills the array and exits
926 * \param *out output stream for debugging
927 */
928void molecule::CreateListOfBondsPerAtom(ofstream *out)
929{
930 bond *Binder = NULL;
931 atom *Walker = NULL;
932 int TotalDegree;
933 *out << Verbose(1) << "Begin of Creating ListOfBondsPerAtom: AtomCount = " << AtomCount << "\tBondCount = " << BondCount << "\tNoNonBonds = " << NoNonBonds << "." << endl;
934
935 // re-allocate memory
936 *out << Verbose(2) << "(Re-)Allocating memory." << endl;
937 if (ListOfBondsPerAtom != NULL) {
938 for(int i=AtomCount;i--;)
939 Free(&ListOfBondsPerAtom[i]);
940 Free(&ListOfBondsPerAtom);
941 }
942 if (NumberOfBondsPerAtom != NULL)
943 Free(&NumberOfBondsPerAtom);
944 ListOfBondsPerAtom = Malloc<bond**>(AtomCount, "molecule::CreateListOfBondsPerAtom: ***ListOfBondsPerAtom");
945 NumberOfBondsPerAtom = Malloc<int>(AtomCount, "molecule::CreateListOfBondsPerAtom: *NumberOfBondsPerAtom");
946
947 // reset bond counts per atom
948 for(int i=AtomCount;i--;)
949 NumberOfBondsPerAtom[i] = 0;
950 // count bonds per atom
951 Binder = first;
952 while (Binder->next != last) {
953 Binder = Binder->next;
954 NumberOfBondsPerAtom[Binder->leftatom->nr]++;
955 NumberOfBondsPerAtom[Binder->rightatom->nr]++;
956 }
957 for(int i=AtomCount;i--;) {
958 // allocate list of bonds per atom
959 ListOfBondsPerAtom[i] = Malloc<bond*>(NumberOfBondsPerAtom[i], "molecule::CreateListOfBondsPerAtom: **ListOfBondsPerAtom[]");
960 // clear the list again, now each NumberOfBondsPerAtom marks current free field
961 NumberOfBondsPerAtom[i] = 0;
962 }
963 // fill the list
964 Binder = first;
965 while (Binder->next != last) {
966 Binder = Binder->next;
967 ListOfBondsPerAtom[Binder->leftatom->nr][NumberOfBondsPerAtom[Binder->leftatom->nr]++] = Binder;
968 ListOfBondsPerAtom[Binder->rightatom->nr][NumberOfBondsPerAtom[Binder->rightatom->nr]++] = Binder;
969 }
970
971 // output list for debugging
972 *out << Verbose(3) << "ListOfBondsPerAtom for each atom:" << endl;
973 Walker = start;
974 while (Walker->next != end) {
975 Walker = Walker->next;
976 *out << Verbose(4) << "Atom " << Walker->Name << "/" << Walker->nr << " with " << NumberOfBondsPerAtom[Walker->nr] << " bonds: ";
977 TotalDegree = 0;
978 for (int j=0;j<NumberOfBondsPerAtom[Walker->nr];j++) {
979 *out << *ListOfBondsPerAtom[Walker->nr][j] << "\t";
980 TotalDegree += ListOfBondsPerAtom[Walker->nr][j]->BondDegree;
981 }
982 *out << " -- TotalDegree: " << TotalDegree << endl;
983 }
984 *out << Verbose(1) << "End of Creating ListOfBondsPerAtom." << endl << endl;
985};
986
987
988/** Blows the 6-dimensional \a cell_size array up to a full NDIM by NDIM matrix.
989 * \param *symm 6-dim array of unique symmetric matrix components
990 * \return allocated NDIM*NDIM array with the symmetric matrix
991 */
992double * molecule::ReturnFullMatrixforSymmetric(double *symm)
993{
994 double *matrix = Malloc<double>(NDIM * NDIM, "molecule::ReturnFullMatrixforSymmetric: *matrix");
995 matrix[0] = symm[0];
996 matrix[1] = symm[1];
997 matrix[2] = symm[3];
998 matrix[3] = symm[1];
999 matrix[4] = symm[2];
1000 matrix[5] = symm[4];
1001 matrix[6] = symm[3];
1002 matrix[7] = symm[4];
1003 matrix[8] = symm[5];
1004 return matrix;
1005};
1006
1007
1008/** Comparison function for GSL heapsort on distances in two molecules.
1009 * \param *a
1010 * \param *b
1011 * \return <0, \a *a less than \a *b, ==0 if equal, >0 \a *a greater than \a *b
1012 */
1013inline int CompareDoubles (const void * a, const void * b)
1014{
1015 if (*(double *)a > *(double *)b)
1016 return -1;
1017 else if (*(double *)a < *(double *)b)
1018 return 1;
1019 else
1020 return 0;
1021};
1022
1023/** Determines whether two molecules actually contain the same atoms and coordination.
1024 * \param *out output stream for debugging
1025 * \param *OtherMolecule the molecule to compare this one to
1026 * \param threshold upper limit of difference when comparing the coordination.
1027 * \return NULL - not equal, otherwise an allocated (molecule::AtomCount) permutation map of the atom numbers (which corresponds to which)
1028 */
1029int * molecule::IsEqualToWithinThreshold(ofstream *out, molecule *OtherMolecule, double threshold)
1030{
1031 int flag;
1032 double *Distances = NULL, *OtherDistances = NULL;
1033 Vector CenterOfGravity, OtherCenterOfGravity;
1034 size_t *PermMap = NULL, *OtherPermMap = NULL;
1035 int *PermutationMap = NULL;
1036 atom *Walker = NULL;
1037 bool result = true; // status of comparison
1038
1039 *out << Verbose(3) << "Begin of IsEqualToWithinThreshold." << endl;
1040 /// first count both their atoms and elements and update lists thereby ...
1041 //*out << Verbose(0) << "Counting atoms, updating list" << endl;
1042 CountAtoms(out);
1043 OtherMolecule->CountAtoms(out);
1044 CountElements();
1045 OtherMolecule->CountElements();
1046
1047 /// ... and compare:
1048 /// -# AtomCount
1049 if (result) {
1050 if (AtomCount != OtherMolecule->AtomCount) {
1051 *out << Verbose(4) << "AtomCounts don't match: " << AtomCount << " == " << OtherMolecule->AtomCount << endl;
1052 result = false;
1053 } else *out << Verbose(4) << "AtomCounts match: " << AtomCount << " == " << OtherMolecule->AtomCount << endl;
1054 }
1055 /// -# ElementCount
1056 if (result) {
1057 if (ElementCount != OtherMolecule->ElementCount) {
1058 *out << Verbose(4) << "ElementCount don't match: " << ElementCount << " == " << OtherMolecule->ElementCount << endl;
1059 result = false;
1060 } else *out << Verbose(4) << "ElementCount match: " << ElementCount << " == " << OtherMolecule->ElementCount << endl;
1061 }
1062 /// -# ElementsInMolecule
1063 if (result) {
1064 for (flag=MAX_ELEMENTS;flag--;) {
1065 //*out << Verbose(5) << "Element " << flag << ": " << ElementsInMolecule[flag] << " <-> " << OtherMolecule->ElementsInMolecule[flag] << "." << endl;
1066 if (ElementsInMolecule[flag] != OtherMolecule->ElementsInMolecule[flag])
1067 break;
1068 }
1069 if (flag < MAX_ELEMENTS) {
1070 *out << Verbose(4) << "ElementsInMolecule don't match." << endl;
1071 result = false;
1072 } else *out << Verbose(4) << "ElementsInMolecule match." << endl;
1073 }
1074 /// then determine and compare center of gravity for each molecule ...
1075 if (result) {
1076 *out << Verbose(5) << "Calculating Centers of Gravity" << endl;
1077 DeterminePeriodicCenter(CenterOfGravity);
1078 OtherMolecule->DeterminePeriodicCenter(OtherCenterOfGravity);
1079 *out << Verbose(5) << "Center of Gravity: ";
1080 CenterOfGravity.Output(out);
1081 *out << endl << Verbose(5) << "Other Center of Gravity: ";
1082 OtherCenterOfGravity.Output(out);
1083 *out << endl;
1084 if (CenterOfGravity.DistanceSquared(&OtherCenterOfGravity) > threshold*threshold) {
1085 *out << Verbose(4) << "Centers of gravity don't match." << endl;
1086 result = false;
1087 }
1088 }
1089
1090 /// ... then make a list with the euclidian distance to this center for each atom of both molecules
1091 if (result) {
1092 *out << Verbose(5) << "Calculating distances" << endl;
1093 Distances = Malloc<double>(AtomCount, "molecule::IsEqualToWithinThreshold: Distances");
1094 OtherDistances = Malloc<double>(AtomCount, "molecule::IsEqualToWithinThreshold: OtherDistances");
1095 Walker = start;
1096 while (Walker->next != end) {
1097 Walker = Walker->next;
1098 Distances[Walker->nr] = CenterOfGravity.DistanceSquared(&Walker->x);
1099 }
1100 Walker = OtherMolecule->start;
1101 while (Walker->next != OtherMolecule->end) {
1102 Walker = Walker->next;
1103 OtherDistances[Walker->nr] = OtherCenterOfGravity.DistanceSquared(&Walker->x);
1104 }
1105
1106 /// ... sort each list (using heapsort (o(N log N)) from GSL)
1107 *out << Verbose(5) << "Sorting distances" << endl;
1108 PermMap = Malloc<size_t>(AtomCount, "molecule::IsEqualToWithinThreshold: *PermMap");
1109 OtherPermMap = Malloc<size_t>(AtomCount, "molecule::IsEqualToWithinThreshold: *OtherPermMap");
1110 gsl_heapsort_index (PermMap, Distances, AtomCount, sizeof(double), CompareDoubles);
1111 gsl_heapsort_index (OtherPermMap, OtherDistances, AtomCount, sizeof(double), CompareDoubles);
1112 PermutationMap = Malloc<int>(AtomCount, "molecule::IsEqualToWithinThreshold: *PermutationMap");
1113 *out << Verbose(5) << "Combining Permutation Maps" << endl;
1114 for(int i=AtomCount;i--;)
1115 PermutationMap[PermMap[i]] = (int) OtherPermMap[i];
1116
1117 /// ... and compare them step by step, whether the difference is individually(!) below \a threshold for all
1118 *out << Verbose(4) << "Comparing distances" << endl;
1119 flag = 0;
1120 for (int i=0;i<AtomCount;i++) {
1121 *out << Verbose(5) << "Distances squared: |" << Distances[PermMap[i]] << " - " << OtherDistances[OtherPermMap[i]] << "| = " << fabs(Distances[PermMap[i]] - OtherDistances[OtherPermMap[i]]) << " ?<? " << threshold << endl;
1122 if (fabs(Distances[PermMap[i]] - OtherDistances[OtherPermMap[i]]) > threshold*threshold)
1123 flag = 1;
1124 }
1125
1126 // free memory
1127 Free(&PermMap);
1128 Free(&OtherPermMap);
1129 Free(&Distances);
1130 Free(&OtherDistances);
1131 if (flag) { // if not equal
1132 Free(&PermutationMap);
1133 result = false;
1134 }
1135 }
1136 /// return pointer to map if all distances were below \a threshold
1137 *out << Verbose(3) << "End of IsEqualToWithinThreshold." << endl;
1138 if (result) {
1139 *out << Verbose(3) << "Result: Equal." << endl;
1140 return PermutationMap;
1141 } else {
1142 *out << Verbose(3) << "Result: Not equal." << endl;
1143 return NULL;
1144 }
1145};
1146
1147/** Returns an index map for two father-son-molecules.
1148 * The map tells which atom in this molecule corresponds to which one in the other molecul with their fathers.
1149 * \param *out output stream for debugging
1150 * \param *OtherMolecule corresponding molecule with fathers
1151 * \return allocated map of size molecule::AtomCount with map
1152 * \todo make this with a good sort O(n), not O(n^2)
1153 */
1154int * molecule::GetFatherSonAtomicMap(ofstream *out, molecule *OtherMolecule)
1155{
1156 atom *Walker = NULL, *OtherWalker = NULL;
1157 *out << Verbose(3) << "Begin of GetFatherAtomicMap." << endl;
1158 int *AtomicMap = Malloc<int>(AtomCount, "molecule::GetAtomicMap: *AtomicMap");
1159 for (int i=AtomCount;i--;)
1160 AtomicMap[i] = -1;
1161 if (OtherMolecule == this) { // same molecule
1162 for (int i=AtomCount;i--;) // no need as -1 means already that there is trivial correspondence
1163 AtomicMap[i] = i;
1164 *out << Verbose(4) << "Map is trivial." << endl;
1165 } else {
1166 *out << Verbose(4) << "Map is ";
1167 Walker = start;
1168 while (Walker->next != end) {
1169 Walker = Walker->next;
1170 if (Walker->father == NULL) {
1171 AtomicMap[Walker->nr] = -2;
1172 } else {
1173 OtherWalker = OtherMolecule->start;
1174 while (OtherWalker->next != OtherMolecule->end) {
1175 OtherWalker = OtherWalker->next;
1176 //for (int i=0;i<AtomCount;i++) { // search atom
1177 //for (int j=0;j<OtherMolecule->AtomCount;j++) {
1178 //*out << Verbose(4) << "Comparing father " << Walker->father << " with the other one " << OtherWalker->father << "." << endl;
1179 if (Walker->father == OtherWalker)
1180 AtomicMap[Walker->nr] = OtherWalker->nr;
1181 }
1182 }
1183 *out << AtomicMap[Walker->nr] << "\t";
1184 }
1185 *out << endl;
1186 }
1187 *out << Verbose(3) << "End of GetFatherAtomicMap." << endl;
1188 return AtomicMap;
1189};
1190
1191/** Stores the temperature evaluated from velocities in molecule::Trajectories.
1192 * We simply use the formula equivaleting temperature and kinetic energy:
1193 * \f$k_B T = \sum_i m_i v_i^2\f$
1194 * \param *out output stream for debugging
1195 * \param startstep first MD step in molecule::Trajectories
1196 * \param endstep last plus one MD step in molecule::Trajectories
1197 * \param *output output stream of temperature file
1198 * \return file written (true), failure on writing file (false)
1199 */
1200bool molecule::OutputTemperatureFromTrajectories(ofstream *out, int startstep, int endstep, ofstream *output)
1201{
1202 double temperature;
1203 atom *Walker = NULL;
1204 // test stream
1205 if (output == NULL)
1206 return false;
1207 else
1208 *output << "# Step Temperature [K] Temperature [a.u.]" << endl;
1209 for (int step=startstep;step < endstep; step++) { // loop over all time steps
1210 temperature = 0.;
1211 Walker = start;
1212 while (Walker->next != end) {
1213 Walker = Walker->next;
1214 for (int i=NDIM;i--;)
1215 temperature += Walker->type->mass * Walker->Trajectory.U.at(step).x[i]* Walker->Trajectory.U.at(step).x[i];
1216 }
1217 *output << step << "\t" << temperature*AtomicEnergyToKelvin << "\t" << temperature << endl;
1218 }
1219 return true;
1220};
Note: See TracBrowser for help on using the repository browser.