source: src/molecule_dynamics.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 fcd7b6, checked in by Frederik Heber <heber@…>, 16 years ago

In molecule::OutputTrajectories() ActOnAllAtoms() with new function atom::OutputTrajectory() is used.

For this to work, I had to change the Trajectory struct that was so far included in molecule.hpp to be incorporated directly into the class atom.
NOTE: This incorporation is incomplete and a ticket (#34) has been filed to remind of this issue.
However, the trajectory is better suited to reside in atom anyway and was probably just put in molecule due to memory prejudices against STL vector<>.
Functions in molecule.cpp, config.cpp, molecule_geometry.cpp and molecule_dynamics.cpp were adapted (changed from Trajectories[atom *] to atom *->Trajectory).
And the atom pointer in the Trajectory structure was removed.

  • Property mode set to 100644
File size: 38.8 KB
Line 
1/*
2 * molecule_dynamics.cpp
3 *
4 * Created on: Oct 5, 2009
5 * Author: heber
6 */
7
8#include "config.hpp"
9#include "memoryallocator.hpp"
10#include "molecule.hpp"
11
12/************************************* Functions for class molecule *********************************/
13
14
15/** Evaluates the potential energy used for constrained molecular dynamics.
16 * \f$V_i^{con} = c^{bond} \cdot | r_{P(i)} - R_i | + sum_{i \neq j} C^{min} \cdot \frac{1}{C_{ij}} + C^{inj} \Bigl (1 - \theta \bigl (\prod_{i \neq j} (P(i) - P(j)) \bigr ) \Bigr )\f$
17 * where the first term points to the target in minimum distance, the second is a penalty for trajectories lying too close to each other (\f$C_{ij}\f$ is minimum distance between
18 * trajectories i and j) and the third term is a penalty for two atoms trying to each the same target point.
19 * Note that for the second term we have to solve the following linear system:
20 * \f$-c_1 \cdot n_1 + c_2 \cdot n_2 + C \cdot n_3 = - p_2 + p_1\f$, where \f$c_1\f$, \f$c_2\f$ and \f$C\f$ are constants,
21 * offset vector \f$p_1\f$ in direction \f$n_1\f$, offset vector \f$p_2\f$ in direction \f$n_2\f$,
22 * \f$n_3\f$ is the normal vector to both directions. \f$C\f$ would be the minimum distance between the two lines.
23 * \sa molecule::MinimiseConstrainedPotential(), molecule::VerletForceIntegration()
24 * \param *out output stream for debugging
25 * \param *PermutationMap gives target ptr for each atom, array of size molecule::AtomCount (this is "x" in \f$ V^{con}(x) \f$ )
26 * \param startstep start configuration (MDStep in molecule::trajectories)
27 * \param endstep end configuration (MDStep in molecule::trajectories)
28 * \param *constants constant in front of each term
29 * \param IsAngstroem whether coordinates are in angstroem (true) or bohrradius (false)
30 * \return potential energy
31 * \note This routine is scaling quadratically which is not optimal.
32 * \todo There's a bit double counting going on for the first time, bu nothing to worry really about.
33 */
34double molecule::ConstrainedPotential(ofstream *out, atom **PermutationMap, int startstep, int endstep, double *constants, bool IsAngstroem)
35{
36 double result = 0., tmp, Norm1, Norm2;
37 atom *Walker = NULL, *Runner = NULL, *Sprinter = NULL;
38 Vector trajectory1, trajectory2, normal, TestVector;
39 gsl_matrix *A = gsl_matrix_alloc(NDIM,NDIM);
40 gsl_vector *x = gsl_vector_alloc(NDIM);
41
42 // go through every atom
43 Walker = start;
44 while (Walker->next != end) {
45 Walker = Walker->next;
46 // first term: distance to target
47 Runner = PermutationMap[Walker->nr]; // find target point
48 tmp = (Walker->Trajectory.R.at(startstep).Distance(&Runner->Trajectory.R.at(endstep)));
49 tmp *= IsAngstroem ? 1. : 1./AtomicLengthToAngstroem;
50 result += constants[0] * tmp;
51 //*out << Verbose(4) << "Adding " << tmp*constants[0] << "." << endl;
52
53 // second term: sum of distances to other trajectories
54 Runner = start;
55 while (Runner->next != end) {
56 Runner = Runner->next;
57 if (Runner == Walker) // hence, we only go up to the Walker, not beyond (similar to i=0; i<j; i++)
58 break;
59 // determine normalized trajectories direction vector (n1, n2)
60 Sprinter = PermutationMap[Walker->nr]; // find first target point
61 trajectory1.CopyVector(&Sprinter->Trajectory.R.at(endstep));
62 trajectory1.SubtractVector(&Walker->Trajectory.R.at(startstep));
63 trajectory1.Normalize();
64 Norm1 = trajectory1.Norm();
65 Sprinter = PermutationMap[Runner->nr]; // find second target point
66 trajectory2.CopyVector(&Sprinter->Trajectory.R.at(endstep));
67 trajectory2.SubtractVector(&Runner->Trajectory.R.at(startstep));
68 trajectory2.Normalize();
69 Norm2 = trajectory1.Norm();
70 // check whether either is zero()
71 if ((Norm1 < MYEPSILON) && (Norm2 < MYEPSILON)) {
72 tmp = Walker->Trajectory.R.at(startstep).Distance(&Runner->Trajectory.R.at(startstep));
73 } else if (Norm1 < MYEPSILON) {
74 Sprinter = PermutationMap[Walker->nr]; // find first target point
75 trajectory1.CopyVector(&Sprinter->Trajectory.R.at(endstep)); // copy first offset
76 trajectory1.SubtractVector(&Runner->Trajectory.R.at(startstep)); // subtract second offset
77 trajectory2.Scale( trajectory1.ScalarProduct(&trajectory2) ); // trajectory2 is scaled to unity, hence we don't need to divide by anything
78 trajectory1.SubtractVector(&trajectory2); // project the part in norm direction away
79 tmp = trajectory1.Norm(); // remaining norm is distance
80 } else if (Norm2 < MYEPSILON) {
81 Sprinter = PermutationMap[Runner->nr]; // find second target point
82 trajectory2.CopyVector(&Sprinter->Trajectory.R.at(endstep)); // copy second offset
83 trajectory2.SubtractVector(&Walker->Trajectory.R.at(startstep)); // subtract first offset
84 trajectory1.Scale( trajectory2.ScalarProduct(&trajectory1) ); // trajectory1 is scaled to unity, hence we don't need to divide by anything
85 trajectory2.SubtractVector(&trajectory1); // project the part in norm direction away
86 tmp = trajectory2.Norm(); // remaining norm is distance
87 } else if ((fabs(trajectory1.ScalarProduct(&trajectory2)/Norm1/Norm2) - 1.) < MYEPSILON) { // check whether they're linear dependent
88// *out << Verbose(3) << "Both trajectories of " << *Walker << " and " << *Runner << " are linear dependent: ";
89// *out << trajectory1;
90// *out << " and ";
91// *out << trajectory2;
92 tmp = Walker->Trajectory.R.at(startstep).Distance(&Runner->Trajectory.R.at(startstep));
93// *out << " with distance " << tmp << "." << endl;
94 } else { // determine distance by finding minimum distance
95// *out << Verbose(3) << "Both trajectories of " << *Walker << " and " << *Runner << " are linear independent ";
96// *out << endl;
97// *out << "First Trajectory: ";
98// *out << trajectory1 << endl;
99// *out << "Second Trajectory: ";
100// *out << trajectory2 << endl;
101 // determine normal vector for both
102 normal.MakeNormalVector(&trajectory1, &trajectory2);
103 // print all vectors for debugging
104// *out << "Normal vector in between: ";
105// *out << normal << endl;
106 // setup matrix
107 for (int i=NDIM;i--;) {
108 gsl_matrix_set(A, 0, i, trajectory1.x[i]);
109 gsl_matrix_set(A, 1, i, trajectory2.x[i]);
110 gsl_matrix_set(A, 2, i, normal.x[i]);
111 gsl_vector_set(x,i, (Walker->Trajectory.R.at(startstep).x[i] - Runner->Trajectory.R.at(startstep).x[i]));
112 }
113 // solve the linear system by Householder transformations
114 gsl_linalg_HH_svx(A, x);
115 // distance from last component
116 tmp = gsl_vector_get(x,2);
117// *out << " with distance " << tmp << "." << endl;
118 // test whether we really have the intersection (by checking on c_1 and c_2)
119 TestVector.CopyVector(&Runner->Trajectory.R.at(startstep));
120 trajectory2.Scale(gsl_vector_get(x,1));
121 TestVector.AddVector(&trajectory2);
122 normal.Scale(gsl_vector_get(x,2));
123 TestVector.AddVector(&normal);
124 TestVector.SubtractVector(&Walker->Trajectory.R.at(startstep));
125 trajectory1.Scale(gsl_vector_get(x,0));
126 TestVector.SubtractVector(&trajectory1);
127 if (TestVector.Norm() < MYEPSILON) {
128// *out << Verbose(2) << "Test: ok.\tDistance of " << tmp << " is correct." << endl;
129 } else {
130// *out << Verbose(2) << "Test: failed.\tIntersection is off by ";
131// *out << TestVector;
132// *out << "." << endl;
133 }
134 }
135 // add up
136 tmp *= IsAngstroem ? 1. : 1./AtomicLengthToAngstroem;
137 if (fabs(tmp) > MYEPSILON) {
138 result += constants[1] * 1./tmp;
139 //*out << Verbose(4) << "Adding " << 1./tmp*constants[1] << "." << endl;
140 }
141 }
142
143 // third term: penalty for equal targets
144 Runner = start;
145 while (Runner->next != end) {
146 Runner = Runner->next;
147 if ((PermutationMap[Walker->nr] == PermutationMap[Runner->nr]) && (Walker->nr < Runner->nr)) {
148 Sprinter = PermutationMap[Walker->nr];
149// *out << *Walker << " and " << *Runner << " are heading to the same target at ";
150// *out << Sprinter->Trajectory.R.at(endstep);
151// *out << ", penalting." << endl;
152 result += constants[2];
153 //*out << Verbose(4) << "Adding " << constants[2] << "." << endl;
154 }
155 }
156 }
157
158 return result;
159};
160
161void PrintPermutationMap(ofstream *out, atom **PermutationMap, int Nr)
162{
163 stringstream zeile1, zeile2;
164 int *DoubleList = Malloc<int>(Nr, "PrintPermutationMap: *DoubleList");
165 int doubles = 0;
166 for (int i=0;i<Nr;i++)
167 DoubleList[i] = 0;
168 zeile1 << "PermutationMap: ";
169 zeile2 << " ";
170 for (int i=0;i<Nr;i++) {
171 DoubleList[PermutationMap[i]->nr]++;
172 zeile1 << i << " ";
173 zeile2 << PermutationMap[i]->nr << " ";
174 }
175 for (int i=0;i<Nr;i++)
176 if (DoubleList[i] > 1)
177 doubles++;
178 // *out << "Found " << doubles << " Doubles." << endl;
179 Free(&DoubleList);
180// *out << zeile1.str() << endl << zeile2.str() << endl;
181};
182
183/** Minimises the extra potential for constrained molecular dynamics and gives forces and the constrained potential energy.
184 * We do the following:
185 * -# Generate a distance list from all source to all target points
186 * -# Sort this per source point
187 * -# Take for each source point the target point with minimum distance, use this as initial permutation
188 * -# check whether molecule::ConstrainedPotential() is greater than injective penalty
189 * -# If so, we go through each source point, stepping down in the sorted target point distance list and re-checking potential.
190 * -# Next, we only apply transformations that keep the injectivity of the permutations list.
191 * -# Hence, for one source point we step down the ladder and seek the corresponding owner of this new target
192 * point and try to change it for one with lesser distance, or for the next one with greater distance, but only
193 * if this decreases the conditional potential.
194 * -# finished.
195 * -# Then, we calculate the forces by taking the spatial derivative, where we scale the potential to such a degree,
196 * that the total force is always pointing in direction of the constraint force (ensuring that we move in the
197 * right direction).
198 * -# Finally, we calculate the potential energy and return.
199 * \param *out output stream for debugging
200 * \param **PermutationMap on return: mapping between the atom label of the initial and the final configuration
201 * \param startstep current MD step giving initial position between which and \a endstep we perform the constrained MD (as further steps are always concatenated)
202 * \param endstep step giving final position in constrained MD
203 * \param IsAngstroem whether coordinates are in angstroem (true) or bohrradius (false)
204 * \sa molecule::VerletForceIntegration()
205 * \return potential energy (and allocated **PermutationMap (array of molecule::AtomCount ^2)
206 * \todo The constrained potential's constants are set to fixed values right now, but they should scale based on checks of the system in order
207 * to ensure they're properties (e.g. constants[2] always greater than the energy of the system).
208 * \bug this all is not O(N log N) but O(N^2)
209 */
210double molecule::MinimiseConstrainedPotential(ofstream *out, atom **&PermutationMap, int startstep, int endstep, bool IsAngstroem)
211{
212 double Potential, OldPotential, OlderPotential;
213 PermutationMap = Malloc<atom*>(AtomCount, "molecule::MinimiseConstrainedPotential: **PermutationMap");
214 DistanceMap **DistanceList = Malloc<DistanceMap*>(AtomCount, "molecule::MinimiseConstrainedPotential: **DistanceList");
215 DistanceMap::iterator *DistanceIterators = Malloc<DistanceMap::iterator>(AtomCount, "molecule::MinimiseConstrainedPotential: *DistanceIterators");
216 int *DoubleList = Malloc<int>(AtomCount, "molecule::MinimiseConstrainedPotential: *DoubleList");
217 DistanceMap::iterator *StepList = Malloc<DistanceMap::iterator>(AtomCount, "molecule::MinimiseConstrainedPotential: *StepList");
218 double constants[3];
219 int round;
220 atom *Walker = NULL, *Runner = NULL, *Sprinter = NULL;
221 DistanceMap::iterator Rider, Strider;
222
223 /// Minimise the potential
224 // set Lagrange multiplier constants
225 constants[0] = 10.;
226 constants[1] = 1.;
227 constants[2] = 1e+7; // just a huge penalty
228 // generate the distance list
229 *out << Verbose(1) << "Creating the distance list ... " << endl;
230 for (int i=AtomCount; i--;) {
231 DoubleList[i] = 0; // stores for how many atoms in startstep this atom is a target in endstep
232 DistanceList[i] = new DistanceMap; // is the distance sorted target list per atom
233 DistanceList[i]->clear();
234 }
235 *out << Verbose(1) << "Filling the distance list ... " << endl;
236 Walker = start;
237 while (Walker->next != end) {
238 Walker = Walker->next;
239 Runner = start;
240 while(Runner->next != end) {
241 Runner = Runner->next;
242 DistanceList[Walker->nr]->insert( DistancePair(Walker->Trajectory.R.at(startstep).Distance(&Runner->Trajectory.R.at(endstep)), Runner) );
243 }
244 }
245 // create the initial PermutationMap (source -> target)
246 Walker = start;
247 while (Walker->next != end) {
248 Walker = Walker->next;
249 StepList[Walker->nr] = DistanceList[Walker->nr]->begin(); // stores the step to the next iterator that could be a possible next target
250 PermutationMap[Walker->nr] = DistanceList[Walker->nr]->begin()->second; // always pick target with the smallest distance
251 DoubleList[DistanceList[Walker->nr]->begin()->second->nr]++; // increase this target's source count (>1? not injective)
252 DistanceIterators[Walker->nr] = DistanceList[Walker->nr]->begin(); // and remember which one we picked
253 *out << *Walker << " starts with distance " << DistanceList[Walker->nr]->begin()->first << "." << endl;
254 }
255 *out << Verbose(1) << "done." << endl;
256 // make the PermutationMap injective by checking whether we have a non-zero constants[2] term in it
257 *out << Verbose(1) << "Making the PermutationMap injective ... " << endl;
258 Walker = start;
259 DistanceMap::iterator NewBase;
260 OldPotential = fabs(ConstrainedPotential(out, PermutationMap, startstep, endstep, constants, IsAngstroem));
261 while ((OldPotential) > constants[2]) {
262 PrintPermutationMap(out, PermutationMap, AtomCount);
263 Walker = Walker->next;
264 if (Walker == end) // round-robin at the end
265 Walker = start->next;
266 if (DoubleList[DistanceIterators[Walker->nr]->second->nr] <= 1) // no need to make those injective that aren't
267 continue;
268 // now, try finding a new one
269 NewBase = DistanceIterators[Walker->nr]; // store old base
270 do {
271 NewBase++; // take next further distance in distance to targets list that's a target of no one
272 } while ((DoubleList[NewBase->second->nr] != 0) && (NewBase != DistanceList[Walker->nr]->end()));
273 if (NewBase != DistanceList[Walker->nr]->end()) {
274 PermutationMap[Walker->nr] = NewBase->second;
275 Potential = fabs(ConstrainedPotential(out, PermutationMap, startstep, endstep, constants, IsAngstroem));
276 if (Potential > OldPotential) { // undo
277 PermutationMap[Walker->nr] = DistanceIterators[Walker->nr]->second;
278 } else { // do
279 DoubleList[DistanceIterators[Walker->nr]->second->nr]--; // decrease the old entry in the doubles list
280 DoubleList[NewBase->second->nr]++; // increase the old entry in the doubles list
281 DistanceIterators[Walker->nr] = NewBase;
282 OldPotential = Potential;
283 *out << Verbose(3) << "Found a new permutation, new potential is " << OldPotential << "." << endl;
284 }
285 }
286 }
287 for (int i=AtomCount; i--;) // now each single entry in the DoubleList should be <=1
288 if (DoubleList[i] > 1) {
289 cerr << "Failed to create an injective PermutationMap!" << endl;
290 exit(1);
291 }
292 *out << Verbose(1) << "done." << endl;
293 Free(&DoubleList);
294 // argument minimise the constrained potential in this injective PermutationMap
295 *out << Verbose(1) << "Argument minimising the PermutationMap, at current potential " << OldPotential << " ... " << endl;
296 OldPotential = 1e+10;
297 round = 0;
298 do {
299 *out << "Starting round " << ++round << " ... " << endl;
300 OlderPotential = OldPotential;
301 do {
302 Walker = start;
303 while (Walker->next != end) { // pick one
304 Walker = Walker->next;
305 PrintPermutationMap(out, PermutationMap, AtomCount);
306 Sprinter = DistanceIterators[Walker->nr]->second; // store initial partner
307 Strider = DistanceIterators[Walker->nr]; //remember old iterator
308 DistanceIterators[Walker->nr] = StepList[Walker->nr];
309 if (DistanceIterators[Walker->nr] == DistanceList[Walker->nr]->end()) {// stop, before we run through the list and still on
310 DistanceIterators[Walker->nr] == DistanceList[Walker->nr]->begin();
311 break;
312 }
313 //*out << Verbose(2) << "Current Walker: " << *Walker << " with old/next candidate " << *Sprinter << "/" << *DistanceIterators[Walker->nr]->second << "." << endl;
314 // find source of the new target
315 Runner = start->next;
316 while(Runner != end) { // find the source whose toes we might be stepping on (Walker's new target should be in use by another already)
317 if (PermutationMap[Runner->nr] == DistanceIterators[Walker->nr]->second) {
318 //*out << Verbose(2) << "Found the corresponding owner " << *Runner << " to " << *PermutationMap[Runner->nr] << "." << endl;
319 break;
320 }
321 Runner = Runner->next;
322 }
323 if (Runner != end) { // we found the other source
324 // then look in its distance list for Sprinter
325 Rider = DistanceList[Runner->nr]->begin();
326 for (; Rider != DistanceList[Runner->nr]->end(); Rider++)
327 if (Rider->second == Sprinter)
328 break;
329 if (Rider != DistanceList[Runner->nr]->end()) { // if we have found one
330 //*out << Verbose(2) << "Current Other: " << *Runner << " with old/next candidate " << *PermutationMap[Runner->nr] << "/" << *Rider->second << "." << endl;
331 // exchange both
332 PermutationMap[Walker->nr] = DistanceIterators[Walker->nr]->second; // put next farther distance into PermutationMap
333 PermutationMap[Runner->nr] = Sprinter; // and hand the old target to its respective owner
334 PrintPermutationMap(out, PermutationMap, AtomCount);
335 // calculate the new potential
336 //*out << Verbose(2) << "Checking new potential ..." << endl;
337 Potential = ConstrainedPotential(out, PermutationMap, startstep, endstep, constants, IsAngstroem);
338 if (Potential > OldPotential) { // we made everything worse! Undo ...
339 //*out << Verbose(3) << "Nay, made the potential worse: " << Potential << " vs. " << OldPotential << "!" << endl;
340 //*out << Verbose(3) << "Setting " << *Runner << "'s source to " << *DistanceIterators[Runner->nr]->second << "." << endl;
341 // Undo for Runner (note, we haven't moved the iteration yet, we may use this)
342 PermutationMap[Runner->nr] = DistanceIterators[Runner->nr]->second;
343 // Undo for Walker
344 DistanceIterators[Walker->nr] = Strider; // take next farther distance target
345 //*out << Verbose(3) << "Setting " << *Walker << "'s source to " << *DistanceIterators[Walker->nr]->second << "." << endl;
346 PermutationMap[Walker->nr] = DistanceIterators[Walker->nr]->second;
347 } else {
348 DistanceIterators[Runner->nr] = Rider; // if successful also move the pointer in the iterator list
349 *out << Verbose(3) << "Found a better permutation, new potential is " << Potential << " vs." << OldPotential << "." << endl;
350 OldPotential = Potential;
351 }
352 if (Potential > constants[2]) {
353 cerr << "ERROR: The two-step permutation procedure did not maintain injectivity!" << endl;
354 exit(255);
355 }
356 //*out << endl;
357 } else {
358 cerr << "ERROR: " << *Runner << " was not the owner of " << *Sprinter << "!" << endl;
359 exit(255);
360 }
361 } else {
362 PermutationMap[Walker->nr] = DistanceIterators[Walker->nr]->second; // new target has no source!
363 }
364 StepList[Walker->nr]++; // take next farther distance target
365 }
366 } while (Walker->next != end);
367 } while ((OlderPotential - OldPotential) > 1e-3);
368 *out << Verbose(1) << "done." << endl;
369
370
371 /// free memory and return with evaluated potential
372 for (int i=AtomCount; i--;)
373 DistanceList[i]->clear();
374 Free(&DistanceList);
375 Free(&DistanceIterators);
376 return ConstrainedPotential(out, PermutationMap, startstep, endstep, constants, IsAngstroem);
377};
378
379/** Evaluates the (distance-related part) of the constrained potential for the constrained forces.
380 * \param *out output stream for debugging
381 * \param startstep current MD step giving initial position between which and \a endstep we perform the constrained MD (as further steps are always concatenated)
382 * \param endstep step giving final position in constrained MD
383 * \param **PermutationMap mapping between the atom label of the initial and the final configuration
384 * \param *Force ForceMatrix containing force vectors from the external energy functional minimisation.
385 * \todo the constant for the constrained potential distance part is hard-coded independently of the hard-coded value in MinimiseConstrainedPotential()
386 */
387void molecule::EvaluateConstrainedForces(ofstream *out, int startstep, int endstep, atom **PermutationMap, ForceMatrix *Force)
388{
389 double constant = 10.;
390 atom *Walker = NULL, *Sprinter = NULL;
391
392 /// evaluate forces (only the distance to target dependent part) with the final PermutationMap
393 *out << Verbose(1) << "Calculating forces and adding onto ForceMatrix ... " << endl;
394 Walker = start;
395 while (Walker->next != NULL) {
396 Walker = Walker->next;
397 Sprinter = PermutationMap[Walker->nr];
398 // set forces
399 for (int i=NDIM;i++;)
400 Force->Matrix[0][Walker->nr][5+i] += 2.*constant*sqrt(Walker->Trajectory.R.at(startstep).Distance(&Sprinter->Trajectory.R.at(endstep)));
401 }
402 *out << Verbose(1) << "done." << endl;
403};
404
405/** Performs a linear interpolation between two desired atomic configurations with a given number of steps.
406 * Note, step number is config::MaxOuterStep
407 * \param *out output stream for debugging
408 * \param startstep stating initial configuration in molecule::Trajectories
409 * \param endstep stating final configuration in molecule::Trajectories
410 * \param &config configuration structure
411 * \param MapByIdentity if true we just use the identity to map atoms in start config to end config, if not we find mapping by \sa MinimiseConstrainedPotential()
412 * \return true - success in writing step files, false - error writing files or only one step in molecule::Trajectories
413 */
414bool molecule::LinearInterpolationBetweenConfiguration(ofstream *out, int startstep, int endstep, const char *prefix, config &configuration, bool MapByIdentity)
415{
416 molecule *mol = NULL;
417 bool status = true;
418 int MaxSteps = configuration.MaxOuterStep;
419 MoleculeListClass *MoleculePerStep = new MoleculeListClass();
420 // Get the Permutation Map by MinimiseConstrainedPotential
421 atom **PermutationMap = NULL;
422 atom *Walker = NULL, *Sprinter = NULL;
423 if (!MapByIdentity)
424 MinimiseConstrainedPotential(out, PermutationMap, startstep, endstep, configuration.GetIsAngstroem());
425 else {
426 PermutationMap = Malloc<atom *>(AtomCount, "molecule::LinearInterpolationBetweenConfiguration: **PermutationMap");
427 Walker = start;
428 while (Walker->next != end) {
429 Walker = Walker->next;
430 PermutationMap[Walker->nr] = Walker; // always pick target with the smallest distance
431 }
432 }
433
434 // check whether we have sufficient space in Trajectories for each atom
435 Walker = start;
436 while (Walker->next != end) {
437 Walker = Walker->next;
438 if (Walker->Trajectory.R.size() <= (unsigned int)(MaxSteps)) {
439 //cout << "Increasing size for trajectory array of " << keyword << " to " << (MaxSteps+1) << "." << endl;
440 Walker->Trajectory.R.resize(MaxSteps+1);
441 Walker->Trajectory.U.resize(MaxSteps+1);
442 Walker->Trajectory.F.resize(MaxSteps+1);
443 }
444 }
445 // push endstep to last one
446 Walker = start;
447 while (Walker->next != end) { // remove the endstep (is now the very last one)
448 Walker = Walker->next;
449 for (int n=NDIM;n--;) {
450 Walker->Trajectory.R.at(MaxSteps).x[n] = Walker->Trajectory.R.at(endstep).x[n];
451 Walker->Trajectory.U.at(MaxSteps).x[n] = Walker->Trajectory.U.at(endstep).x[n];
452 Walker->Trajectory.F.at(MaxSteps).x[n] = Walker->Trajectory.F.at(endstep).x[n];
453 }
454 }
455 endstep = MaxSteps;
456
457 // go through all steps and add the molecular configuration to the list and to the Trajectories of \a this molecule
458 *out << Verbose(1) << "Filling intermediate " << MaxSteps << " steps with MDSteps of " << MDSteps << "." << endl;
459 for (int step = 0; step <= MaxSteps; step++) {
460 mol = new molecule(elemente);
461 MoleculePerStep->insert(mol);
462 Walker = start;
463 while (Walker->next != end) {
464 Walker = Walker->next;
465 // add to molecule list
466 Sprinter = mol->AddCopyAtom(Walker);
467 for (int n=NDIM;n--;) {
468 Sprinter->x.x[n] = Walker->Trajectory.R.at(startstep).x[n] + (PermutationMap[Walker->nr]->Trajectory.R.at(endstep).x[n] - Walker->Trajectory.R.at(startstep).x[n])*((double)step/(double)MaxSteps);
469 // add to Trajectories
470 //*out << Verbose(3) << step << ">=" << MDSteps-1 << endl;
471 if (step < MaxSteps) {
472 Walker->Trajectory.R.at(step).x[n] = Walker->Trajectory.R.at(startstep).x[n] + (PermutationMap[Walker->nr]->Trajectory.R.at(endstep).x[n] - Walker->Trajectory.R.at(startstep).x[n])*((double)step/(double)MaxSteps);
473 Walker->Trajectory.U.at(step).x[n] = 0.;
474 Walker->Trajectory.F.at(step).x[n] = 0.;
475 }
476 }
477 }
478 }
479 MDSteps = MaxSteps+1; // otherwise new Trajectories' points aren't stored on save&exit
480
481 // store the list to single step files
482 int *SortIndex = Malloc<int>(AtomCount, "molecule::LinearInterpolationBetweenConfiguration: *SortIndex");
483 for (int i=AtomCount; i--; )
484 SortIndex[i] = i;
485 status = MoleculePerStep->OutputConfigForListOfFragments(out, &configuration, SortIndex);
486
487 // free and return
488 Free(&PermutationMap);
489 delete(MoleculePerStep);
490 return status;
491};
492
493/** Parses nuclear forces from file and performs Verlet integration.
494 * Note that we assume the parsed forces to be in atomic units (hence, if coordinates are in angstroem, we
495 * have to transform them).
496 * This adds a new MD step to the config file.
497 * \param *out output stream for debugging
498 * \param *file filename
499 * \param config structure with config::Deltat, config::IsAngstroem, config::DoConstrained
500 * \param delta_t time step width in atomic units
501 * \param IsAngstroem whether coordinates are in angstroem (true) or bohrradius (false)
502 * \param DoConstrained whether we perform a constrained (>0, target step in molecule::trajectories) or unconstrained (0) molecular dynamics, \sa molecule::MinimiseConstrainedPotential()
503 * \return true - file found and parsed, false - file not found or imparsable
504 * \todo This is not yet checked if it is correctly working with DoConstrained set to true.
505 */
506bool molecule::VerletForceIntegration(ofstream *out, char *file, config &configuration)
507{
508 atom *walker = NULL;
509 ifstream input(file);
510 string token;
511 stringstream item;
512 double IonMass, Vector[NDIM], ConstrainedPotentialEnergy, ActualTemp;
513 ForceMatrix Force;
514
515 CountElements(); // make sure ElementsInMolecule is up to date
516
517 // check file
518 if (input == NULL) {
519 return false;
520 } else {
521 // parse file into ForceMatrix
522 if (!Force.ParseMatrix(file, 0,0,0)) {
523 cerr << "Could not parse Force Matrix file " << file << "." << endl;
524 return false;
525 }
526 if (Force.RowCounter[0] != AtomCount) {
527 cerr << "Mismatch between number of atoms in file " << Force.RowCounter[0] << " and in molecule " << AtomCount << "." << endl;
528 return false;
529 }
530 // correct Forces
531 for(int d=0;d<NDIM;d++)
532 Vector[d] = 0.;
533 for(int i=0;i<AtomCount;i++)
534 for(int d=0;d<NDIM;d++) {
535 Vector[d] += Force.Matrix[0][i][d+5];
536 }
537 for(int i=0;i<AtomCount;i++)
538 for(int d=0;d<NDIM;d++) {
539 Force.Matrix[0][i][d+5] -= Vector[d]/(double)AtomCount;
540 }
541 // solve a constrained potential if we are meant to
542 if (configuration.DoConstrainedMD) {
543 // calculate forces and potential
544 atom **PermutationMap = NULL;
545 ConstrainedPotentialEnergy = MinimiseConstrainedPotential(out, PermutationMap,configuration.DoConstrainedMD, 0, configuration.GetIsAngstroem());
546 EvaluateConstrainedForces(out, configuration.DoConstrainedMD, 0, PermutationMap, &Force);
547 Free(&PermutationMap);
548 }
549
550 // and perform Verlet integration for each atom with position, velocity and force vector
551 walker = start;
552 while (walker->next != end) { // go through every atom of this element
553 walker = walker->next;
554 //a = configuration.Deltat*0.5/walker->type->mass; // (F+F_old)/2m = a and thus: v = (F+F_old)/2m * t = (F + F_old) * a
555 // check size of vectors
556 if (walker->Trajectory.R.size() <= (unsigned int)(MDSteps)) {
557 //out << "Increasing size for trajectory array of " << *walker << " to " << (size+10) << "." << endl;
558 walker->Trajectory.R.resize(MDSteps+10);
559 walker->Trajectory.U.resize(MDSteps+10);
560 walker->Trajectory.F.resize(MDSteps+10);
561 }
562
563 // Update R (and F)
564 for (int d=0; d<NDIM; d++) {
565 walker->Trajectory.F.at(MDSteps).x[d] = -Force.Matrix[0][walker->nr][d+5]*(configuration.GetIsAngstroem() ? AtomicLengthToAngstroem : 1.);
566 walker->Trajectory.R.at(MDSteps).x[d] = walker->Trajectory.R.at(MDSteps-1).x[d];
567 walker->Trajectory.R.at(MDSteps).x[d] += configuration.Deltat*(walker->Trajectory.U.at(MDSteps-1).x[d]); // s(t) = s(0) + v * deltat + 1/2 a * deltat^2
568 walker->Trajectory.R.at(MDSteps).x[d] += 0.5*configuration.Deltat*configuration.Deltat*(walker->Trajectory.F.at(MDSteps).x[d]/walker->type->mass); // F = m * a and s = 0.5 * F/m * t^2 = F * a * t
569 }
570 // Update U
571 for (int d=0; d<NDIM; d++) {
572 walker->Trajectory.U.at(MDSteps).x[d] = walker->Trajectory.U.at(MDSteps-1).x[d];
573 walker->Trajectory.U.at(MDSteps).x[d] += configuration.Deltat * (walker->Trajectory.F.at(MDSteps).x[d]+walker->Trajectory.F.at(MDSteps-1).x[d]/walker->type->mass); // v = F/m * t
574 }
575// out << "Integrated position&velocity of step " << (MDSteps) << ": (";
576// for (int d=0;d<NDIM;d++)
577// out << walker->Trajectory.R.at(MDSteps).x[d] << " "; // next step
578// out << ")\t(";
579// for (int d=0;d<NDIM;d++)
580// cout << walker->Trajectory.U.at(MDSteps).x[d] << " "; // next step
581// out << ")" << endl;
582 // next atom
583 }
584 }
585 // correct velocities (rather momenta) so that center of mass remains motionless
586 for(int d=0;d<NDIM;d++)
587 Vector[d] = 0.;
588 IonMass = 0.;
589 walker = start;
590 while (walker->next != end) { // go through every atom
591 walker = walker->next;
592 IonMass += walker->type->mass; // sum up total mass
593 for(int d=0;d<NDIM;d++) {
594 Vector[d] += walker->Trajectory.U.at(MDSteps).x[d]*walker->type->mass;
595 }
596 }
597 // correct velocities (rather momenta) so that center of mass remains motionless
598 for(int d=0;d<NDIM;d++)
599 Vector[d] /= IonMass;
600 ActualTemp = 0.;
601 walker = start;
602 while (walker->next != end) { // go through every atom of this element
603 walker = walker->next;
604 for(int d=0;d<NDIM;d++) {
605 walker->Trajectory.U.at(MDSteps).x[d] -= Vector[d];
606 ActualTemp += 0.5 * walker->type->mass * walker->Trajectory.U.at(MDSteps).x[d] * walker->Trajectory.U.at(MDSteps).x[d];
607 }
608 }
609 Thermostats(configuration, ActualTemp, Berendsen);
610 MDSteps++;
611
612
613 // exit
614 return true;
615};
616
617/** Implementation of various thermostats.
618 * All these thermostats apply an additional force which has the following forms:
619 * -# Woodcock
620 * \f$p_i \rightarrow \sqrt{\frac{T_0}{T}} \cdot p_i\f$
621 * -# Gaussian
622 * \f$ \frac{ \sum_i \frac{p_i}{m_i} \frac{\partial V}{\partial q_i}} {\sum_i \frac{p^2_i}{m_i}} \cdot p_i\f$
623 * -# Langevin
624 * \f$p_{i,n} \rightarrow \sqrt{1-\alpha^2} p_{i,0} + \alpha p_r\f$
625 * -# Berendsen
626 * \f$p_i \rightarrow \left [ 1+ \frac{\delta t}{\tau_T} \left ( \frac{T_0}{T} \right ) \right ]^{\frac{1}{2}} \cdot p_i\f$
627 * -# Nose-Hoover
628 * \f$\zeta p_i \f$ with \f$\frac{\partial \zeta}{\partial t} = \frac{1}{M_s} \left ( \sum^N_{i=1} \frac{p_i^2}{m_i} - g k_B T \right )\f$
629 * These Thermostats either simply rescale the velocities, thus this function should be called after ion velocities have been updated, and/or
630 * have a constraint force acting additionally on the ions. In the latter case, the ion speeds have to be modified
631 * belatedly and the constraint force set.
632 * \param *P Problem at hand
633 * \param i which of the thermostats to take: 0 - none, 1 - Woodcock, 2 - Gaussian, 3 - Langevin, 4 - Berendsen, 5 - Nose-Hoover
634 * \sa InitThermostat()
635 */
636void molecule::Thermostats(config &configuration, double ActualTemp, int Thermostat)
637{
638 double ekin = 0.;
639 double E = 0., G = 0.;
640 double delta_alpha = 0.;
641 double ScaleTempFactor;
642 double sigma;
643 double IonMass;
644 int d;
645 gsl_rng * r;
646 const gsl_rng_type * T;
647 double *U = NULL, *F = NULL, FConstraint[NDIM];
648 atom *walker = NULL;
649
650 // calculate scale configuration
651 ScaleTempFactor = configuration.TargetTemp/ActualTemp;
652
653 // differentating between the various thermostats
654 switch(Thermostat) {
655 case None:
656 cout << Verbose(2) << "Applying no thermostat..." << endl;
657 break;
658 case Woodcock:
659 if ((configuration.ScaleTempStep > 0) && ((MDSteps-1) % configuration.ScaleTempStep == 0)) {
660 cout << Verbose(2) << "Applying Woodcock thermostat..." << endl;
661 walker = start;
662 while (walker->next != end) { // go through every atom of this element
663 walker = walker->next;
664 IonMass = walker->type->mass;
665 U = walker->Trajectory.U.at(MDSteps).x;
666 if (walker->FixedIon == 0) // even FixedIon moves, only not by other's forces
667 for (d=0; d<NDIM; d++) {
668 U[d] *= sqrt(ScaleTempFactor);
669 ekin += 0.5*IonMass * U[d]*U[d];
670 }
671 }
672 }
673 break;
674 case Gaussian:
675 cout << Verbose(2) << "Applying Gaussian thermostat..." << endl;
676 walker = start;
677 while (walker->next != end) { // go through every atom of this element
678 walker = walker->next;
679 IonMass = walker->type->mass;
680 U = walker->Trajectory.U.at(MDSteps).x;
681 F = walker->Trajectory.F.at(MDSteps).x;
682 if (walker->FixedIon == 0) // even FixedIon moves, only not by other's forces
683 for (d=0; d<NDIM; d++) {
684 G += U[d] * F[d];
685 E += U[d]*U[d]*IonMass;
686 }
687 }
688 cout << Verbose(1) << "Gaussian Least Constraint constant is " << G/E << "." << endl;
689 walker = start;
690 while (walker->next != end) { // go through every atom of this element
691 walker = walker->next;
692 IonMass = walker->type->mass;
693 U = walker->Trajectory.U.at(MDSteps).x;
694 F = walker->Trajectory.F.at(MDSteps).x;
695 if (walker->FixedIon == 0) // even FixedIon moves, only not by other's forces
696 for (d=0; d<NDIM; d++) {
697 FConstraint[d] = (G/E) * (U[d]*IonMass);
698 U[d] += configuration.Deltat/IonMass * (FConstraint[d]);
699 ekin += IonMass * U[d]*U[d];
700 }
701 }
702 break;
703 case Langevin:
704 cout << Verbose(2) << "Applying Langevin thermostat..." << endl;
705 // init random number generator
706 gsl_rng_env_setup();
707 T = gsl_rng_default;
708 r = gsl_rng_alloc (T);
709 // Go through each ion
710 walker = start;
711 while (walker->next != end) { // go through every atom of this element
712 walker = walker->next;
713 IonMass = walker->type->mass;
714 sigma = sqrt(configuration.TargetTemp/IonMass); // sigma = (k_b T)/m (Hartree/atomicmass = atomiclength/atomictime)
715 U = walker->Trajectory.U.at(MDSteps).x;
716 F = walker->Trajectory.F.at(MDSteps).x;
717 if (walker->FixedIon == 0) { // even FixedIon moves, only not by other's forces
718 // throw a dice to determine whether it gets hit by a heat bath particle
719 if (((((rand()/(double)RAND_MAX))*configuration.TempFrequency) < 1.)) {
720 cout << Verbose(3) << "Particle " << *walker << " was hit (sigma " << sigma << "): " << sqrt(U[0]*U[0]+U[1]*U[1]+U[2]*U[2]) << " -> ";
721 // pick three random numbers from a Boltzmann distribution around the desired temperature T for each momenta axis
722 for (d=0; d<NDIM; d++) {
723 U[d] = gsl_ran_gaussian (r, sigma);
724 }
725 cout << sqrt(U[0]*U[0]+U[1]*U[1]+U[2]*U[2]) << endl;
726 }
727 for (d=0; d<NDIM; d++)
728 ekin += 0.5*IonMass * U[d]*U[d];
729 }
730 }
731 break;
732 case Berendsen:
733 cout << Verbose(2) << "Applying Berendsen-VanGunsteren thermostat..." << endl;
734 walker = start;
735 while (walker->next != end) { // go through every atom of this element
736 walker = walker->next;
737 IonMass = walker->type->mass;
738 U = walker->Trajectory.U.at(MDSteps).x;
739 F = walker->Trajectory.F.at(MDSteps).x;
740 if (walker->FixedIon == 0) { // even FixedIon moves, only not by other's forces
741 for (d=0; d<NDIM; d++) {
742 U[d] *= sqrt(1+(configuration.Deltat/configuration.TempFrequency)*(ScaleTempFactor-1));
743 ekin += 0.5*IonMass * U[d]*U[d];
744 }
745 }
746 }
747 break;
748 case NoseHoover:
749 cout << Verbose(2) << "Applying Nose-Hoover thermostat..." << endl;
750 // dynamically evolve alpha (the additional degree of freedom)
751 delta_alpha = 0.;
752 walker = start;
753 while (walker->next != end) { // go through every atom of this element
754 walker = walker->next;
755 IonMass = walker->type->mass;
756 U = walker->Trajectory.U.at(MDSteps).x;
757 if (walker->FixedIon == 0) { // even FixedIon moves, only not by other's forces
758 for (d=0; d<NDIM; d++) {
759 delta_alpha += U[d]*U[d]*IonMass;
760 }
761 }
762 }
763 delta_alpha = (delta_alpha - (3.*AtomCount+1.) * configuration.TargetTemp)/(configuration.HooverMass*Units2Electronmass);
764 configuration.alpha += delta_alpha*configuration.Deltat;
765 cout << Verbose(3) << "alpha = " << delta_alpha << " * " << configuration.Deltat << " = " << configuration.alpha << "." << endl;
766 // apply updated alpha as additional force
767 walker = start;
768 while (walker->next != end) { // go through every atom of this element
769 walker = walker->next;
770 IonMass = walker->type->mass;
771 U = walker->Trajectory.U.at(MDSteps).x;
772 if (walker->FixedIon == 0) { // even FixedIon moves, only not by other's forces
773 for (d=0; d<NDIM; d++) {
774 FConstraint[d] = - configuration.alpha * (U[d] * IonMass);
775 U[d] += configuration.Deltat/IonMass * (FConstraint[d]);
776 ekin += (0.5*IonMass) * U[d]*U[d];
777 }
778 }
779 }
780 break;
781 }
782 cout << Verbose(1) << "Kinetic energy is " << ekin << "." << endl;
783};
Note: See TracBrowser for help on using the repository browser.