source: src/molecule_dynamics.cpp@ c42e60

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 c42e60 was e4afb4, checked in by Frederik Heber <heber@…>, 14 years ago

Huge refactoring: Introduction of Traits to Actions.

This change is really big but the introduction of the Trait concept (at least
in its current light form) is so fundamental that lots of pieces had to be
changed in order to get everything working.

The main point why it was necessary to add these traits in the first place was
to comfortably allow for adding extension of Actions information-wise, i.e.
with stuff that is only important for the QtUI, such as icons, or tooltips, ...
This extra information should not be stored with Action itself, as it has
nothing to do with the workings of the Action. And neither should it get
stored with some blown-out-of-proportions MapOfActions class ...

The gist of the change is as follows:

  • OptionTrait contains the token, description, shortform and type of an option, such as ("position", "position in space, none, typeid(Vector)).
  • ActionTrait is the derived form for actions where additionally MenuPosition and MenuName are stored (and probably more to come for the GUI), also we have a set of OptionTrait instances, one for each option of the Action.
  • Action then contains this ActionTrait, specialized for each Action.
  • the preprocessor macros have been enhanced to gather all this information from the .def files.
  • MapOfActions is gone. Completely. Most of its use was to store this extra information and the ValueStorage part now is just in class ValueStorage.
  • ValueStorage is no more an interface to MapOfActions but as the name says a (type-safe) ValueStorage.

Listing the (remaining) changes in alphabetical order of the class:

  • Action
    • member value ::name dropped, ::getName() uses ActionTraits::getName()
    • new define NODEFAULT which is used in paramdefaults in .def files
    • all derived actions classes such as Process, Calculations, MakroAction,... have been adapated to use the ActionTrait concept as well.
  • ActionHistory
    • extraced RedoAction and UndoAction, shifted implementation into their own object files and they use .def files as well (i.e. streamlined with method used for other actions)
  • MenuDescription
    • contain information on Menus such as name, ...
    • new unit test checks for consistency
  • molecule
    • const member functions: Copy(), Output() and OutputBonds()
  • OptionRegistry
    • new registry class for options only
    • we want the same type throughout the code for each token, e.g. "position"
    • the registry containts checks for consistency
  • OptionTrait
    • default values are specified in paramdefaults, none are given by NODEFAULT
    • introduced default for translate-atoms, point-correlation, pair-correlation
  • Registry pattern
    • new unit test, but only sceleton code so far
  • ...Query, also ...Pipe
    • atoms, molecule and elements are now all const
    • also ValueStorage's signatures all have const therein
  • ValueStorage
    • set/queryCurrentValue from MapOfActions
    • at times VectorValue has been in .def files where Vector was in the signature. This is cleared. Such stuff is only present for e.g. BoxVector being queried as a Vector. But this is a feature and intended.
  • World
    • most of the (un)selection functions now work on const atoms and molecules
    • in one case we need a const_cast to remove this, but this is intentional, as the vector of selected atoms stores non-const pointers and this is ok.

There is only one test which had to be changed slightly because a specific
option token as "position" must now have the same type everywhere, e.g. always
Vector.

  • TESTFIX: Simple_configuration/2: --position -> --domain-position (and associated to BoxVector)
  • Property mode set to 100644
File size: 31.6 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010 University of Bonn. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 */
7
8/*
9 * molecule_dynamics.cpp
10 *
11 * Created on: Oct 5, 2009
12 * Author: heber
13 */
14
15// include config.h
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
20#include "Helpers/MemDebug.hpp"
21
22#include "World.hpp"
23#include "atom.hpp"
24#include "config.hpp"
25#include "element.hpp"
26#include "Helpers/Info.hpp"
27#include "Helpers/Verbose.hpp"
28#include "Helpers/Log.hpp"
29#include "molecule.hpp"
30#include "parser.hpp"
31#include "LinearAlgebra/Plane.hpp"
32#include "ThermoStatContainer.hpp"
33#include "Thermostats/Berendsen.hpp"
34
35#include <gsl/gsl_matrix.h>
36#include <gsl/gsl_vector.h>
37#include <gsl/gsl_linalg.h>
38
39/************************************* Functions for class molecule *********************************/
40
41/** Penalizes long trajectories.
42 * \param *Walker atom to check against others
43 * \param *mol molecule with other atoms
44 * \param &Params constraint potential parameters
45 * \return penalty times each distance
46 */
47double SumDistanceOfTrajectories(atom *Walker, molecule *mol, struct EvaluatePotential &Params)
48{
49 gsl_matrix *A = gsl_matrix_alloc(NDIM,NDIM);
50 gsl_vector *x = gsl_vector_alloc(NDIM);
51 atom *Sprinter = NULL;
52 Vector trajectory1, trajectory2, normal, TestVector;
53 double Norm1, Norm2, tmp, result = 0.;
54
55 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
56 if ((*iter) == Walker) // hence, we only go up to the Walker, not beyond (similar to i=0; i<j; i++)
57 break;
58 // determine normalized trajectories direction vector (n1, n2)
59 Sprinter = Params.PermutationMap[Walker->nr]; // find first target point
60 trajectory1 = Sprinter->Trajectory.R.at(Params.endstep) - Walker->Trajectory.R.at(Params.startstep);
61 trajectory1.Normalize();
62 Norm1 = trajectory1.Norm();
63 Sprinter = Params.PermutationMap[(*iter)->nr]; // find second target point
64 trajectory2 = Sprinter->Trajectory.R.at(Params.endstep) - (*iter)->Trajectory.R.at(Params.startstep);
65 trajectory2.Normalize();
66 Norm2 = trajectory1.Norm();
67 // check whether either is zero()
68 if ((Norm1 < MYEPSILON) && (Norm2 < MYEPSILON)) {
69 tmp = Walker->Trajectory.R.at(Params.startstep).distance((*iter)->Trajectory.R.at(Params.startstep));
70 } else if (Norm1 < MYEPSILON) {
71 Sprinter = Params.PermutationMap[Walker->nr]; // find first target point
72 trajectory1 = Sprinter->Trajectory.R.at(Params.endstep) - (*iter)->Trajectory.R.at(Params.startstep);
73 trajectory2 *= trajectory1.ScalarProduct(trajectory2); // trajectory2 is scaled to unity, hence we don't need to divide by anything
74 trajectory1 -= trajectory2; // project the part in norm direction away
75 tmp = trajectory1.Norm(); // remaining norm is distance
76 } else if (Norm2 < MYEPSILON) {
77 Sprinter = Params.PermutationMap[(*iter)->nr]; // find second target point
78 trajectory2 = Sprinter->Trajectory.R.at(Params.endstep) - Walker->Trajectory.R.at(Params.startstep); // copy second offset
79 trajectory1 *= trajectory2.ScalarProduct(trajectory1); // trajectory1 is scaled to unity, hence we don't need to divide by anything
80 trajectory2 -= trajectory1; // project the part in norm direction away
81 tmp = trajectory2.Norm(); // remaining norm is distance
82 } else if ((fabs(trajectory1.ScalarProduct(trajectory2)/Norm1/Norm2) - 1.) < MYEPSILON) { // check whether they're linear dependent
83 // Log() << Verbose(3) << "Both trajectories of " << *Walker << " and " << *Runner << " are linear dependent: ";
84 // Log() << Verbose(0) << trajectory1;
85 // Log() << Verbose(0) << " and ";
86 // Log() << Verbose(0) << trajectory2;
87 tmp = Walker->Trajectory.R.at(Params.startstep).distance((*iter)->Trajectory.R.at(Params.startstep));
88 // Log() << Verbose(0) << " with distance " << tmp << "." << endl;
89 } else { // determine distance by finding minimum distance
90 // Log() << Verbose(3) << "Both trajectories of " << *Walker << " and " << *(*iter) << " are linear independent ";
91 // Log() << Verbose(0) << endl;
92 // Log() << Verbose(0) << "First Trajectory: ";
93 // Log() << Verbose(0) << trajectory1 << endl;
94 // Log() << Verbose(0) << "Second Trajectory: ";
95 // Log() << Verbose(0) << trajectory2 << endl;
96 // determine normal vector for both
97 normal = Plane(trajectory1, trajectory2,0).getNormal();
98 // print all vectors for debugging
99 // Log() << Verbose(0) << "Normal vector in between: ";
100 // Log() << Verbose(0) << normal << endl;
101 // setup matrix
102 for (int i=NDIM;i--;) {
103 gsl_matrix_set(A, 0, i, trajectory1[i]);
104 gsl_matrix_set(A, 1, i, trajectory2[i]);
105 gsl_matrix_set(A, 2, i, normal[i]);
106 gsl_vector_set(x,i, (Walker->Trajectory.R.at(Params.startstep)[i] - (*iter)->Trajectory.R.at(Params.startstep)[i]));
107 }
108 // solve the linear system by Householder transformations
109 gsl_linalg_HH_svx(A, x);
110 // distance from last component
111 tmp = gsl_vector_get(x,2);
112 // Log() << Verbose(0) << " with distance " << tmp << "." << endl;
113 // test whether we really have the intersection (by checking on c_1 and c_2)
114 trajectory1.Scale(gsl_vector_get(x,0));
115 trajectory2.Scale(gsl_vector_get(x,1));
116 normal.Scale(gsl_vector_get(x,2));
117 TestVector = (*iter)->Trajectory.R.at(Params.startstep) + trajectory2 + normal
118 - (Walker->Trajectory.R.at(Params.startstep) + trajectory1);
119 if (TestVector.Norm() < MYEPSILON) {
120 // Log() << Verbose(2) << "Test: ok.\tDistance of " << tmp << " is correct." << endl;
121 } else {
122 // Log() << Verbose(2) << "Test: failed.\tIntersection is off by ";
123 // Log() << Verbose(0) << TestVector;
124 // Log() << Verbose(0) << "." << endl;
125 }
126 }
127 // add up
128 tmp *= Params.IsAngstroem ? 1. : 1./AtomicLengthToAngstroem;
129 if (fabs(tmp) > MYEPSILON) {
130 result += Params.PenaltyConstants[1] * 1./tmp;
131 //Log() << Verbose(4) << "Adding " << 1./tmp*constants[1] << "." << endl;
132 }
133 }
134 return result;
135};
136
137/** Penalizes atoms heading to same target.
138 * \param *Walker atom to check against others
139 * \param *mol molecule with other atoms
140 * \param &Params constrained potential parameters
141 * \return \a penalty times the number of equal targets
142 */
143double PenalizeEqualTargets(atom *Walker, molecule *mol, struct EvaluatePotential &Params)
144{
145 double result = 0.;
146 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
147 if ((Params.PermutationMap[Walker->nr] == Params.PermutationMap[(*iter)->nr]) && (Walker->nr < (*iter)->nr)) {
148 // atom *Sprinter = PermutationMap[Walker->nr];
149 // Log() << Verbose(0) << *Walker << " and " << *(*iter) << " are heading to the same target at ";
150 // Log() << Verbose(0) << Sprinter->Trajectory.R.at(endstep);
151 // Log() << Verbose(0) << ", penalting." << endl;
152 result += Params.PenaltyConstants[2];
153 //Log() << Verbose(4) << "Adding " << constants[2] << "." << endl;
154 }
155 }
156 return result;
157};
158
159/** Evaluates the potential energy used for constrained molecular dynamics.
160 * \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$
161 * 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
162 * trajectories i and j) and the third term is a penalty for two atoms trying to each the same target point.
163 * Note that for the second term we have to solve the following linear system:
164 * \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,
165 * offset vector \f$p_1\f$ in direction \f$n_1\f$, offset vector \f$p_2\f$ in direction \f$n_2\f$,
166 * \f$n_3\f$ is the normal vector to both directions. \f$C\f$ would be the minimum distance between the two lines.
167 * \sa molecule::MinimiseConstrainedPotential(), molecule::VerletForceIntegration()
168 * \param *out output stream for debugging
169 * \param &Params constrained potential parameters
170 * \return potential energy
171 * \note This routine is scaling quadratically which is not optimal.
172 * \todo There's a bit double counting going on for the first time, bu nothing to worry really about.
173 */
174double molecule::ConstrainedPotential(struct EvaluatePotential &Params)
175{
176 double tmp = 0.;
177 double result = 0.;
178 // go through every atom
179 atom *Runner = NULL;
180 for (molecule::const_iterator iter = begin(); iter != end(); ++iter) {
181 // first term: distance to target
182 Runner = Params.PermutationMap[(*iter)->nr]; // find target point
183 tmp = ((*iter)->Trajectory.R.at(Params.startstep).distance(Runner->Trajectory.R.at(Params.endstep)));
184 tmp *= Params.IsAngstroem ? 1. : 1./AtomicLengthToAngstroem;
185 result += Params.PenaltyConstants[0] * tmp;
186 //Log() << Verbose(4) << "Adding " << tmp*constants[0] << "." << endl;
187
188 // second term: sum of distances to other trajectories
189 result += SumDistanceOfTrajectories((*iter), this, Params);
190
191 // third term: penalty for equal targets
192 result += PenalizeEqualTargets((*iter), this, Params);
193 }
194
195 return result;
196};
197
198/** print the current permutation map.
199 * \param *out output stream for debugging
200 * \param &Params constrained potential parameters
201 * \param AtomCount number of atoms
202 */
203void PrintPermutationMap(int AtomCount, struct EvaluatePotential &Params)
204{
205 stringstream zeile1, zeile2;
206 int *DoubleList = new int[AtomCount];
207 for(int i=0;i<AtomCount;i++)
208 DoubleList[i] = 0;
209 int doubles = 0;
210 zeile1 << "PermutationMap: ";
211 zeile2 << " ";
212 for (int i=0;i<AtomCount;i++) {
213 Params.DoubleList[Params.PermutationMap[i]->nr]++;
214 zeile1 << i << " ";
215 zeile2 << Params.PermutationMap[i]->nr << " ";
216 }
217 for (int i=0;i<AtomCount;i++)
218 if (Params.DoubleList[i] > 1)
219 doubles++;
220 if (doubles >0)
221 DoLog(2) && (Log() << Verbose(2) << "Found " << doubles << " Doubles." << endl);
222 delete[](DoubleList);
223// Log() << Verbose(2) << zeile1.str() << endl << zeile2.str() << endl;
224};
225
226/** \f$O(N^2)\f$ operation of calculation distance between each atom pair and putting into DistanceList.
227 * \param *mol molecule to scan distances in
228 * \param &Params constrained potential parameters
229 */
230void FillDistanceList(molecule *mol, struct EvaluatePotential &Params)
231{
232 for (int i=mol->getAtomCount(); i--;) {
233 Params.DistanceList[i] = new DistanceMap; // is the distance sorted target list per atom
234 Params.DistanceList[i]->clear();
235 }
236
237 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
238 for (molecule::const_iterator runner = mol->begin(); runner != mol->end(); ++runner) {
239 Params.DistanceList[(*iter)->nr]->insert( DistancePair((*iter)->Trajectory.R.at(Params.startstep).distance((*runner)->Trajectory.R.at(Params.endstep)), (*runner)) );
240 }
241 }
242};
243
244/** initialize lists.
245 * \param *out output stream for debugging
246 * \param *mol molecule to scan distances in
247 * \param &Params constrained potential parameters
248 */
249void CreateInitialLists(molecule *mol, struct EvaluatePotential &Params)
250{
251 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
252 Params.StepList[(*iter)->nr] = Params.DistanceList[(*iter)->nr]->begin(); // stores the step to the next iterator that could be a possible next target
253 Params.PermutationMap[(*iter)->nr] = Params.DistanceList[(*iter)->nr]->begin()->second; // always pick target with the smallest distance
254 Params.DoubleList[Params.DistanceList[(*iter)->nr]->begin()->second->nr]++; // increase this target's source count (>1? not injective)
255 Params.DistanceIterators[(*iter)->nr] = Params.DistanceList[(*iter)->nr]->begin(); // and remember which one we picked
256 DoLog(2) && (Log() << Verbose(2) << **iter << " starts with distance " << Params.DistanceList[(*iter)->nr]->begin()->first << "." << endl);
257 }
258};
259
260/** Try the next nearest neighbour in order to make the permutation map injective.
261 * \param *out output stream for debugging
262 * \param *mol molecule
263 * \param *Walker atom to change its target
264 * \param &OldPotential old value of constraint potential to see if we do better with new target
265 * \param &Params constrained potential parameters
266 */
267double TryNextNearestNeighbourForInjectivePermutation(molecule *mol, atom *Walker, double &OldPotential, struct EvaluatePotential &Params)
268{
269 double Potential = 0;
270 DistanceMap::iterator NewBase = Params.DistanceIterators[Walker->nr]; // store old base
271 do {
272 NewBase++; // take next further distance in distance to targets list that's a target of no one
273 } while ((Params.DoubleList[NewBase->second->nr] != 0) && (NewBase != Params.DistanceList[Walker->nr]->end()));
274 if (NewBase != Params.DistanceList[Walker->nr]->end()) {
275 Params.PermutationMap[Walker->nr] = NewBase->second;
276 Potential = fabs(mol->ConstrainedPotential(Params));
277 if (Potential > OldPotential) { // undo
278 Params.PermutationMap[Walker->nr] = Params.DistanceIterators[Walker->nr]->second;
279 } else { // do
280 Params.DoubleList[Params.DistanceIterators[Walker->nr]->second->nr]--; // decrease the old entry in the doubles list
281 Params.DoubleList[NewBase->second->nr]++; // increase the old entry in the doubles list
282 Params.DistanceIterators[Walker->nr] = NewBase;
283 OldPotential = Potential;
284 DoLog(3) && (Log() << Verbose(3) << "Found a new permutation, new potential is " << OldPotential << "." << endl);
285 }
286 }
287 return Potential;
288};
289
290/** Permutes \a **&PermutationMap until the penalty is below constants[2].
291 * \param *out output stream for debugging
292 * \param *mol molecule to scan distances in
293 * \param &Params constrained potential parameters
294 */
295void MakeInjectivePermutation(molecule *mol, struct EvaluatePotential &Params)
296{
297 molecule::const_iterator iter = mol->begin();
298 DistanceMap::iterator NewBase;
299 double Potential = fabs(mol->ConstrainedPotential(Params));
300
301 if (mol->empty()) {
302 eLog() << Verbose(1) << "Molecule is empty." << endl;
303 return;
304 }
305 while ((Potential) > Params.PenaltyConstants[2]) {
306 PrintPermutationMap(mol->getAtomCount(), Params);
307 iter++;
308 if (iter == mol->end()) // round-robin at the end
309 iter = mol->begin();
310 if (Params.DoubleList[Params.DistanceIterators[(*iter)->nr]->second->nr] <= 1) // no need to make those injective that aren't
311 continue;
312 // now, try finding a new one
313 Potential = TryNextNearestNeighbourForInjectivePermutation(mol, (*iter), Potential, Params);
314 }
315 for (int i=mol->getAtomCount(); i--;) // now each single entry in the DoubleList should be <=1
316 if (Params.DoubleList[i] > 1) {
317 DoeLog(0) && (eLog()<< Verbose(0) << "Failed to create an injective PermutationMap!" << endl);
318 performCriticalExit();
319 }
320 DoLog(1) && (Log() << Verbose(1) << "done." << endl);
321};
322
323/** Minimises the extra potential for constrained molecular dynamics and gives forces and the constrained potential energy.
324 * We do the following:
325 * -# Generate a distance list from all source to all target points
326 * -# Sort this per source point
327 * -# Take for each source point the target point with minimum distance, use this as initial permutation
328 * -# check whether molecule::ConstrainedPotential() is greater than injective penalty
329 * -# If so, we go through each source point, stepping down in the sorted target point distance list and re-checking potential.
330 * -# Next, we only apply transformations that keep the injectivity of the permutations list.
331 * -# Hence, for one source point we step down the ladder and seek the corresponding owner of this new target
332 * point and try to change it for one with lesser distance, or for the next one with greater distance, but only
333 * if this decreases the conditional potential.
334 * -# finished.
335 * -# Then, we calculate the forces by taking the spatial derivative, where we scale the potential to such a degree,
336 * that the total force is always pointing in direction of the constraint force (ensuring that we move in the
337 * right direction).
338 * -# Finally, we calculate the potential energy and return.
339 * \param *out output stream for debugging
340 * \param **PermutationMap on return: mapping between the atom label of the initial and the final configuration
341 * \param startstep current MD step giving initial position between which and \a endstep we perform the constrained MD (as further steps are always concatenated)
342 * \param endstep step giving final position in constrained MD
343 * \param IsAngstroem whether coordinates are in angstroem (true) or bohrradius (false)
344 * \sa molecule::VerletForceIntegration()
345 * \return potential energy (and allocated **PermutationMap (array of molecule::AtomCount ^2)
346 * \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
347 * to ensure they're properties (e.g. constants[2] always greater than the energy of the system).
348 * \bug this all is not O(N log N) but O(N^2)
349 */
350double molecule::MinimiseConstrainedPotential(atom **&PermutationMap, int startstep, int endstep, bool IsAngstroem)
351{
352 double Potential, OldPotential, OlderPotential;
353 struct EvaluatePotential Params;
354 Params.PermutationMap = new atom *[getAtomCount()];
355 Params.DistanceList = new DistanceMap *[getAtomCount()];
356 Params.DistanceIterators = new DistanceMap::iterator[getAtomCount()];
357 Params.DoubleList = new int[getAtomCount()];
358 Params.StepList = new DistanceMap::iterator[getAtomCount()];
359 int round;
360 atom *Sprinter = NULL;
361 DistanceMap::iterator Rider, Strider;
362
363 // set to zero
364 for (int i=0;i<getAtomCount();i++) {
365 Params.PermutationMap[i] = NULL;
366 Params.DoubleList[i] = 0;
367 }
368
369 /// Minimise the potential
370 // set Lagrange multiplier constants
371 Params.PenaltyConstants[0] = 10.;
372 Params.PenaltyConstants[1] = 1.;
373 Params.PenaltyConstants[2] = 1e+7; // just a huge penalty
374 // generate the distance list
375 DoLog(1) && (Log() << Verbose(1) << "Allocating, initializting and filling the distance list ... " << endl);
376 FillDistanceList(this, Params);
377
378 // create the initial PermutationMap (source -> target)
379 CreateInitialLists(this, Params);
380
381 // make the PermutationMap injective by checking whether we have a non-zero constants[2] term in it
382 DoLog(1) && (Log() << Verbose(1) << "Making the PermutationMap injective ... " << endl);
383 MakeInjectivePermutation(this, Params);
384 delete[](Params.DoubleList);
385
386 // argument minimise the constrained potential in this injective PermutationMap
387 DoLog(1) && (Log() << Verbose(1) << "Argument minimising the PermutationMap." << endl);
388 OldPotential = 1e+10;
389 round = 0;
390 do {
391 DoLog(2) && (Log() << Verbose(2) << "Starting round " << ++round << ", at current potential " << OldPotential << " ... " << endl);
392 OlderPotential = OldPotential;
393 molecule::const_iterator iter;
394 do {
395 iter = begin();
396 for (; iter != end(); ++iter) {
397 PrintPermutationMap(getAtomCount(), Params);
398 Sprinter = Params.DistanceIterators[(*iter)->nr]->second; // store initial partner
399 Strider = Params.DistanceIterators[(*iter)->nr]; //remember old iterator
400 Params.DistanceIterators[(*iter)->nr] = Params.StepList[(*iter)->nr];
401 if (Params.DistanceIterators[(*iter)->nr] == Params.DistanceList[(*iter)->nr]->end()) {// stop, before we run through the list and still on
402 Params.DistanceIterators[(*iter)->nr] == Params.DistanceList[(*iter)->nr]->begin();
403 break;
404 }
405 //Log() << Verbose(2) << "Current Walker: " << *(*iter) << " with old/next candidate " << *Sprinter << "/" << *DistanceIterators[(*iter)->nr]->second << "." << endl;
406 // find source of the new target
407 molecule::const_iterator runner = begin();
408 for (; runner != end(); ++runner) { // find the source whose toes we might be stepping on (Walker's new target should be in use by another already)
409 if (Params.PermutationMap[(*runner)->nr] == Params.DistanceIterators[(*iter)->nr]->second) {
410 //Log() << Verbose(2) << "Found the corresponding owner " << *(*runner) << " to " << *PermutationMap[(*runner)->nr] << "." << endl;
411 break;
412 }
413 }
414 if (runner != end()) { // we found the other source
415 // then look in its distance list for Sprinter
416 Rider = Params.DistanceList[(*runner)->nr]->begin();
417 for (; Rider != Params.DistanceList[(*runner)->nr]->end(); Rider++)
418 if (Rider->second == Sprinter)
419 break;
420 if (Rider != Params.DistanceList[(*runner)->nr]->end()) { // if we have found one
421 //Log() << Verbose(2) << "Current Other: " << *(*runner) << " with old/next candidate " << *PermutationMap[(*runner)->nr] << "/" << *Rider->second << "." << endl;
422 // exchange both
423 Params.PermutationMap[(*iter)->nr] = Params.DistanceIterators[(*iter)->nr]->second; // put next farther distance into PermutationMap
424 Params.PermutationMap[(*runner)->nr] = Sprinter; // and hand the old target to its respective owner
425 PrintPermutationMap(getAtomCount(), Params);
426 // calculate the new potential
427 //Log() << Verbose(2) << "Checking new potential ..." << endl;
428 Potential = ConstrainedPotential(Params);
429 if (Potential > OldPotential) { // we made everything worse! Undo ...
430 //Log() << Verbose(3) << "Nay, made the potential worse: " << Potential << " vs. " << OldPotential << "!" << endl;
431 //Log() << Verbose(3) << "Setting " << *(*runner) << "'s source to " << *Params.DistanceIterators[(*runner)->nr]->second << "." << endl;
432 // Undo for Runner (note, we haven't moved the iteration yet, we may use this)
433 Params.PermutationMap[(*runner)->nr] = Params.DistanceIterators[(*runner)->nr]->second;
434 // Undo for Walker
435 Params.DistanceIterators[(*iter)->nr] = Strider; // take next farther distance target
436 //Log() << Verbose(3) << "Setting " << *(*iter) << "'s source to " << *Params.DistanceIterators[(*iter)->nr]->second << "." << endl;
437 Params.PermutationMap[(*iter)->nr] = Params.DistanceIterators[(*iter)->nr]->second;
438 } else {
439 Params.DistanceIterators[(*runner)->nr] = Rider; // if successful also move the pointer in the iterator list
440 DoLog(3) && (Log() << Verbose(3) << "Found a better permutation, new potential is " << Potential << " vs." << OldPotential << "." << endl);
441 OldPotential = Potential;
442 }
443 if (Potential > Params.PenaltyConstants[2]) {
444 DoeLog(1) && (eLog()<< Verbose(1) << "The two-step permutation procedure did not maintain injectivity!" << endl);
445 exit(255);
446 }
447 //Log() << Verbose(0) << endl;
448 } else {
449 DoeLog(1) && (eLog()<< Verbose(1) << **runner << " was not the owner of " << *Sprinter << "!" << endl);
450 exit(255);
451 }
452 } else {
453 Params.PermutationMap[(*iter)->nr] = Params.DistanceIterators[(*iter)->nr]->second; // new target has no source!
454 }
455 Params.StepList[(*iter)->nr]++; // take next farther distance target
456 }
457 } while (++iter != end());
458 } while ((OlderPotential - OldPotential) > 1e-3);
459 DoLog(1) && (Log() << Verbose(1) << "done." << endl);
460
461
462 /// free memory and return with evaluated potential
463 for (int i=getAtomCount(); i--;)
464 Params.DistanceList[i]->clear();
465 delete[](Params.DistanceList);
466 delete[](Params.DistanceIterators);
467 return ConstrainedPotential(Params);
468};
469
470
471/** Evaluates the (distance-related part) of the constrained potential for the constrained forces.
472 * \param *out output stream for debugging
473 * \param startstep current MD step giving initial position between which and \a endstep we perform the constrained MD (as further steps are always concatenated)
474 * \param endstep step giving final position in constrained MD
475 * \param **PermutationMap mapping between the atom label of the initial and the final configuration
476 * \param *Force ForceMatrix containing force vectors from the external energy functional minimisation.
477 * \todo the constant for the constrained potential distance part is hard-coded independently of the hard-coded value in MinimiseConstrainedPotential()
478 */
479void molecule::EvaluateConstrainedForces(int startstep, int endstep, atom **PermutationMap, ForceMatrix *Force)
480{
481 /// evaluate forces (only the distance to target dependent part) with the final PermutationMap
482 DoLog(1) && (Log() << Verbose(1) << "Calculating forces and adding onto ForceMatrix ... " << endl);
483 for_each(atoms.begin(),
484 atoms.end(),
485 boost::bind(&atom::EvaluateConstrainedForce,_1,startstep,endstep,PermutationMap,Force));
486 //ActOnAllAtoms( &atom::EvaluateConstrainedForce, startstep, endstep, PermutationMap, Force );
487 DoLog(1) && (Log() << Verbose(1) << "done." << endl);
488};
489
490/** Performs a linear interpolation between two desired atomic configurations with a given number of steps.
491 * Note, step number is config::MaxOuterStep
492 * \param *out output stream for debugging
493 * \param startstep stating initial configuration in molecule::Trajectories
494 * \param endstep stating final configuration in molecule::Trajectories
495 * \param &prefix path and prefix
496 * \param &config configuration structure
497 * \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()
498 * \return true - success in writing step files, false - error writing files or only one step in molecule::Trajectories
499 */
500bool molecule::LinearInterpolationBetweenConfiguration(int startstep, int endstep, std::string prefix, config &configuration, bool MapByIdentity)
501{
502 // TODO: rewrite permutationMaps using enumeration objects
503 molecule *mol = NULL;
504 bool status = true;
505 int MaxSteps = configuration.MaxOuterStep;
506 MoleculeListClass *MoleculePerStep = new MoleculeListClass(World::getPointer());
507 // Get the Permutation Map by MinimiseConstrainedPotential
508 atom **PermutationMap = NULL;
509 atom *Sprinter = NULL;
510 if (!MapByIdentity)
511 MinimiseConstrainedPotential(PermutationMap, startstep, endstep, configuration.GetIsAngstroem());
512 else {
513 // TODO: construction of enumeration goes here
514 PermutationMap = new atom *[getAtomCount()];
515 for(internal_iterator iter = atoms.begin(); iter != atoms.end();++iter){
516 PermutationMap[(*iter)->nr] = (*iter);
517 }
518 }
519
520 // check whether we have sufficient space in Trajectories for each atom
521 for_each(atoms.begin(),atoms.end(),bind2nd(mem_fun(&atom::ResizeTrajectory),MaxSteps));
522 // push endstep to last one
523 for_each(atoms.begin(),atoms.end(),boost::bind(&atom::CopyStepOnStep,_1,MaxSteps,endstep));
524 endstep = MaxSteps;
525
526 // go through all steps and add the molecular configuration to the list and to the Trajectories of \a this molecule
527 DoLog(1) && (Log() << Verbose(1) << "Filling intermediate " << MaxSteps << " steps with MDSteps of " << MDSteps << "." << endl);
528 for (int step = 0; step <= MaxSteps; step++) {
529 mol = World::getInstance().createMolecule();
530 MoleculePerStep->insert(mol);
531 for (molecule::const_iterator iter = begin(); iter != end(); ++iter) {
532 // add to molecule list
533 Sprinter = mol->AddCopyAtom((*iter));
534 for (int n=NDIM;n--;) {
535 Sprinter->set(n, (*iter)->Trajectory.R.at(startstep)[n] + (PermutationMap[(*iter)->nr]->Trajectory.R.at(endstep)[n] - (*iter)->Trajectory.R.at(startstep)[n])*((double)step/(double)MaxSteps));
536 // add to Trajectories
537 //Log() << Verbose(3) << step << ">=" << MDSteps-1 << endl;
538 if (step < MaxSteps) {
539 (*iter)->Trajectory.R.at(step)[n] = (*iter)->Trajectory.R.at(startstep)[n] + (PermutationMap[(*iter)->nr]->Trajectory.R.at(endstep)[n] - (*iter)->Trajectory.R.at(startstep)[n])*((double)step/(double)MaxSteps);
540 (*iter)->Trajectory.U.at(step)[n] = 0.;
541 (*iter)->Trajectory.F.at(step)[n] = 0.;
542 }
543 }
544 }
545 }
546 MDSteps = MaxSteps+1; // otherwise new Trajectories' points aren't stored on save&exit
547
548 // store the list to single step files
549 int *SortIndex = new int[getAtomCount()];
550 for (int i=getAtomCount(); i--; )
551 SortIndex[i] = i;
552
553 status = MoleculePerStep->OutputConfigForListOfFragments(prefix, SortIndex);
554 delete[](SortIndex);
555
556 // free and return
557 delete[](PermutationMap);
558 delete(MoleculePerStep);
559 return status;
560};
561
562/** Parses nuclear forces from file and performs Verlet integration.
563 * Note that we assume the parsed forces to be in atomic units (hence, if coordinates are in angstroem, we
564 * have to transform them).
565 * This adds a new MD step to the config file.
566 * \param *file filename
567 * \param config structure with config::Deltat, config::IsAngstroem, config::DoConstrained
568 * \param offset offset in matrix file to the first force component
569 * \return true - file found and parsed, false - file not found or imparsable
570 * \todo This is not yet checked if it is correctly working with DoConstrained set to true.
571 */
572bool molecule::VerletForceIntegration(char *file, config &configuration, const size_t offset)
573{
574 Info FunctionInfo(__func__);
575 ifstream input(file);
576 string token;
577 stringstream item;
578 double IonMass, ConstrainedPotentialEnergy, ActualTemp;
579 Vector Velocity;
580 ForceMatrix Force;
581
582 const int AtomCount = getAtomCount();
583 // check file
584 if (input == NULL) {
585 return false;
586 } else {
587 // parse file into ForceMatrix
588 if (!Force.ParseMatrix(file, 0,0,0)) {
589 DoeLog(0) && (eLog()<< Verbose(0) << "Could not parse Force Matrix file " << file << "." << endl);
590 performCriticalExit();
591 return false;
592 }
593 if (Force.RowCounter[0] != AtomCount) {
594 DoeLog(0) && (eLog()<< Verbose(0) << "Mismatch between number of atoms in file " << Force.RowCounter[0] << " and in molecule " << getAtomCount() << "." << endl);
595 performCriticalExit();
596 return false;
597 }
598 // correct Forces
599 Velocity.Zero();
600 for(int i=0;i<AtomCount;i++)
601 for(int d=0;d<NDIM;d++) {
602 Velocity[d] += Force.Matrix[0][i][d+offset];
603 }
604 for(int i=0;i<AtomCount;i++)
605 for(int d=0;d<NDIM;d++) {
606 Force.Matrix[0][i][d+offset] -= Velocity[d]/static_cast<double>(AtomCount);
607 }
608 // solve a constrained potential if we are meant to
609 if (configuration.DoConstrainedMD) {
610 // calculate forces and potential
611 atom **PermutationMap = NULL;
612 ConstrainedPotentialEnergy = MinimiseConstrainedPotential(PermutationMap,configuration.DoConstrainedMD, 0, configuration.GetIsAngstroem());
613 EvaluateConstrainedForces(configuration.DoConstrainedMD, 0, PermutationMap, &Force);
614 delete[](PermutationMap);
615 }
616
617 // and perform Verlet integration for each atom with position, velocity and force vector
618 // check size of vectors
619 //ActOnAllAtoms( &atom::ResizeTrajectory, MDSteps+10 );
620 for_each(atoms.begin(),
621 atoms.end(),
622 boost::bind(&atom::VelocityVerletUpdate,_1,MDSteps+1, &configuration, &Force, (const size_t) 0));
623 }
624 // correct velocities (rather momenta) so that center of mass remains motionless
625 Velocity = atoms.totalMomentumAtStep(MDSteps+1);
626 IonMass = atoms.totalMass();
627
628 // correct velocities (rather momenta) so that center of mass remains motionless
629 Velocity *= 1./IonMass;
630
631 atoms.addVelocityAtStep(-1*Velocity,MDSteps+1);
632 ActualTemp = atoms.totalTemperatureAtStep(MDSteps+1);
633 Berendsen berendsen = Berendsen();
634 berendsen.addToContainer(configuration.Thermostats);
635 double ekin = berendsen.scaleAtoms(MDSteps,ActualTemp,atoms);
636 DoLog(1) && (Log() << Verbose(1) << "Kinetic energy is " << ekin << "." << endl);
637 MDSteps++;
638
639 // exit
640 return true;
641};
Note: See TracBrowser for help on using the repository browser.