source: src/Dynamics/ForceAnnealing.hpp@ 4fc0ea

Candidate_v1.6.1 ChemicalSpaceEvaluator TremoloParser_IncreasedPrecision
Last change on this file since 4fc0ea was 8450da, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

ForceAnnealing functions now have better readable time step variables.

  • _TimeStep is the parameter, Old.. and CurrentTimeStep designate the two reference time steps for calculating step width.
  • Split functions into simple step width and using BB step width.
  • TESTFIX: Removed XFAIL from all Python ForceAnnealing tests.
  • TESTFIX: 5-body annealing without bond graph has slightly changed but quality of results is the same.
  • Property mode set to 100644
File size: 28.7 KB
Line 
1/*
2 * ForceAnnealing.hpp
3 *
4 * Created on: Aug 02, 2014
5 * Author: heber
6 */
7
8#ifndef FORCEANNEALING_HPP_
9#define FORCEANNEALING_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include <algorithm>
17#include <functional>
18#include <iterator>
19
20#include <boost/bind.hpp>
21
22#include "Atom/atom.hpp"
23#include "Atom/AtomSet.hpp"
24#include "CodePatterns/Assert.hpp"
25#include "CodePatterns/Info.hpp"
26#include "CodePatterns/Log.hpp"
27#include "CodePatterns/Verbose.hpp"
28#include "Descriptors/AtomIdDescriptor.hpp"
29#include "Dynamics/AtomicForceManipulator.hpp"
30#include "Dynamics/BondVectors.hpp"
31#include "Fragmentation/ForceMatrix.hpp"
32#include "Graph/BoostGraphCreator.hpp"
33#include "Graph/BoostGraphHelpers.hpp"
34#include "Graph/BreadthFirstSearchGatherer.hpp"
35#include "Helpers/helpers.hpp"
36#include "Helpers/defs.hpp"
37#include "LinearAlgebra/LinearSystemOfEquations.hpp"
38#include "LinearAlgebra/MatrixContent.hpp"
39#include "LinearAlgebra/Vector.hpp"
40#include "LinearAlgebra/VectorContent.hpp"
41#include "Thermostats/ThermoStatContainer.hpp"
42#include "Thermostats/Thermostat.hpp"
43#include "World.hpp"
44
45/** This class is the essential build block for performing structural optimization.
46 *
47 * Sadly, we have to use some static instances as so far values cannot be passed
48 * between actions. Hence, we need to store the current step and the adaptive-
49 * step width (we cannot perform a line search, as we have no control over the
50 * calculation of the forces).
51 *
52 * However, we do use the bond graph, i.e. if a single atom needs to be shifted
53 * to the left, then the whole molecule left of it is shifted, too. This is
54 * controlled by the \a max_distance parameter.
55 */
56template <class T>
57class ForceAnnealing : public AtomicForceManipulator<T>
58{
59public:
60 /** Constructor of class ForceAnnealing.
61 *
62 * \note We use a fixed delta t of 1.
63 *
64 * \param _atoms set of atoms to integrate
65 * \param _Deltat time step width in atomic units
66 * \param _IsAngstroem whether length units are in angstroem or bohr radii
67 * \param _maxSteps number of optimization steps to perform
68 * \param _max_distance up to this bond order is bond graph taken into account.
69 */
70 ForceAnnealing(
71 AtomSetMixin<T> &_atoms,
72 const double _Deltat,
73 bool _IsAngstroem,
74 const size_t _maxSteps,
75 const int _max_distance,
76 const double _damping_factor) :
77 AtomicForceManipulator<T>(_atoms, _Deltat, _IsAngstroem),
78 maxSteps(_maxSteps),
79 max_distance(_max_distance),
80 damping_factor(_damping_factor)
81 {}
82
83 /** Destructor of class ForceAnnealing.
84 *
85 */
86 ~ForceAnnealing()
87 {}
88
89 /** Performs Gradient optimization.
90 *
91 * We assume that forces have just been calculated.
92 *
93 *
94 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
95 * \param offset offset in matrix file to the first force component
96 * \todo This is not yet checked if it is correctly working with DoConstrainedMD set >0.
97 */
98 void operator()(
99 const int _TimeStep,
100 const size_t _offset,
101 const bool _UseBondgraph)
102 {
103 const int CurrentTimeStep = _TimeStep-1;
104 ASSERT( CurrentTimeStep >= 0,
105 "ForceAnnealing::operator() - a new time step (upon which we work) must already have been copied.");
106
107 // make sum of forces equal zero
108 AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(
109 _offset,
110 CurrentTimeStep);
111
112 // are we in initial step? Then set static entities
113 Vector maxComponents(zeroVec);
114 if (currentStep == 0) {
115 currentDeltat = AtomicForceManipulator<T>::Deltat;
116 currentStep = 1;
117 LOG(2, "DEBUG: Initial step, setting values, current step is #" << currentStep);
118
119 // always use atomic annealing on first step
120 maxComponents = anneal(_TimeStep);
121 } else {
122 ++currentStep;
123 LOG(2, "DEBUG: current step is #" << currentStep);
124
125 // bond graph annealing is always followed by a normal annealing
126 if (_UseBondgraph)
127 maxComponents = annealWithBondGraph_BarzilaiBorwein(_TimeStep);
128 // cannot store RemnantGradient in Atom's Force as it ruins BB stepwidth calculation
129 else
130 maxComponents = anneal_BarzilaiBorwein(_TimeStep);
131 }
132
133
134 LOG(1, "STATUS: Largest remaining force components at step #"
135 << currentStep << " are " << maxComponents);
136
137 // are we in final step? Remember to reset static entities
138 if (currentStep == maxSteps) {
139 LOG(2, "DEBUG: Final step, resetting values");
140 reset();
141 }
142 }
143
144 /** Helper function to calculate the Barzilai-Borwein stepwidth.
145 *
146 * \param _PositionDifference difference in position between current and last step
147 * \param _GradientDifference difference in gradient between current and last step
148 * \return step width according to Barzilai-Borwein
149 */
150 double getBarzilaiBorweinStepwidth(const Vector &_PositionDifference, const Vector &_GradientDifference)
151 {
152 double stepwidth = 0.;
153 if (_GradientDifference.NormSquared() > MYEPSILON)
154 stepwidth = fabs(_PositionDifference.ScalarProduct(_GradientDifference))/
155 _GradientDifference.NormSquared();
156 if (fabs(stepwidth) < 1e-10) {
157 // dont' warn in first step, deltat usage normal
158 if (currentStep != 1)
159 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
160 stepwidth = currentDeltat;
161 }
162 return stepwidth;
163 }
164
165 /** Performs Gradient optimization on the atoms.
166 *
167 * We assume that forces have just been calculated.
168 *
169 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
170 * \return to be filled with maximum force component over all atoms
171 */
172 Vector anneal(
173 const int _TimeStep)
174 {
175 const int CurrentTimeStep = _TimeStep-1;
176 ASSERT( CurrentTimeStep >= 0,
177 "ForceAnnealing::anneal() - a new time step (upon which we work) must already have been copied.");
178
179 LOG(1, "STATUS: performing simple anneal with default stepwidth " << currentDeltat << " at step #" << currentStep);
180
181 Vector maxComponents;
182 bool deltat_decreased = false;
183 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
184 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
185 // atom's force vector gives steepest descent direction
186 const Vector currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
187 const Vector currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
188 LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
189 LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
190// LOG(4, "DEBUG: Force for atom " << **iter << " is " << currentGradient);
191
192 // we use Barzilai-Borwein update with position reversed to get descent
193 double stepwidth = currentDeltat;
194 Vector PositionUpdate = stepwidth * currentGradient;
195 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
196
197 // extract largest components for showing progress of annealing
198 for(size_t i=0;i<NDIM;++i)
199 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
200
201 // steps may go back and forth again (updates are of same magnitude but
202 // have different sign: Check whether this is the case and one step with
203 // deltat to interrupt this sequence
204 if (currentStep > 1) {
205 const int OldTimeStep = CurrentTimeStep-1;
206 ASSERT( OldTimeStep >= 0,
207 "ForceAnnealing::anneal() - if currentStep is "+toString(currentStep)
208 +", then there should be at least three time steps.");
209 const Vector oldPosition = (*iter)->getPositionAtStep(OldTimeStep);
210 const Vector PositionDifference = currentPosition - oldPosition;
211 LOG(4, "DEBUG: oldPosition for atom #" << (*iter)->getId() << " is " << oldPosition);
212 LOG(4, "DEBUG: PositionDifference for atom #" << (*iter)->getId() << " is " << PositionDifference);
213 if ((PositionUpdate.ScalarProduct(PositionDifference) < 0)
214 && (fabs(PositionUpdate.NormSquared()-PositionDifference.NormSquared()) < 1e-3)) {
215 // for convergence we want a null sequence here, too
216 if (!deltat_decreased) {
217 deltat_decreased = true;
218 currentDeltat = .5*currentDeltat;
219 }
220 LOG(2, "DEBUG: Upgrade in other direction: " << PositionUpdate
221 << " > " << PositionDifference
222 << ", using deltat: " << currentDeltat);
223 PositionUpdate = currentDeltat * currentGradient;
224 }
225 }
226
227 // finally set new values
228 (*iter)->setPositionAtStep(_TimeStep, currentPosition + PositionUpdate);
229 }
230
231 return maxComponents;
232 }
233
234 /** Performs Gradient optimization on the atoms using BarzilaiBorwein step width.
235 *
236 * \note this can only be called when there are at least two optimization
237 * time steps present, i.e. this must be preceeded by a simple anneal().
238 *
239 * We assume that forces have just been calculated.
240 *
241 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
242 * \return to be filled with maximum force component over all atoms
243 */
244 Vector anneal_BarzilaiBorwein(
245 const int _TimeStep)
246 {
247 const int OldTimeStep = _TimeStep-2;
248 const int CurrentTimeStep = _TimeStep-1;
249 ASSERT( OldTimeStep >= 0,
250 "ForceAnnealing::anneal_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
251 ASSERT(currentStep > 1,
252 "ForceAnnealing::anneal_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
253
254 LOG(1, "STATUS: performing BarzilaiBorwein anneal at step #" << currentStep);
255
256 Vector maxComponents;
257 bool deltat_decreased = false;
258 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
259 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
260 // atom's force vector gives steepest descent direction
261 const Vector oldPosition = (*iter)->getPositionAtStep(OldTimeStep);
262 const Vector currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
263 const Vector oldGradient = (*iter)->getAtomicForceAtStep(OldTimeStep);
264 const Vector currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
265 LOG(4, "DEBUG: oldPosition for atom #" << (*iter)->getId() << " is " << oldPosition);
266 LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
267 LOG(4, "DEBUG: oldGradient for atom #" << (*iter)->getId() << " is " << oldGradient);
268 LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
269// LOG(4, "DEBUG: Force for atom #" << (*iter)->getId() << " is " << currentGradient);
270
271 // we use Barzilai-Borwein update with position reversed to get descent
272 const Vector PositionDifference = currentPosition - oldPosition;
273 const Vector GradientDifference = (currentGradient - oldGradient);
274 double stepwidth = getBarzilaiBorweinStepwidth(PositionDifference, GradientDifference);
275 Vector PositionUpdate = stepwidth * currentGradient;
276 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
277
278 // extract largest components for showing progress of annealing
279 for(size_t i=0;i<NDIM;++i)
280 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
281
282// // steps may go back and forth again (updates are of same magnitude but
283// // have different sign: Check whether this is the case and one step with
284// // deltat to interrupt this sequence
285// if (!PositionDifference.IsZero())
286// if ((PositionUpdate.ScalarProduct(PositionDifference) < 0)
287// && (fabs(PositionUpdate.NormSquared()-PositionDifference.NormSquared()) < 1e-3)) {
288// // for convergence we want a null sequence here, too
289// if (!deltat_decreased) {
290// deltat_decreased = true;
291// currentDeltat = .5*currentDeltat;
292// }
293// LOG(2, "DEBUG: Upgrade in other direction: " << PositionUpdate
294// << " > " << PositionDifference
295// << ", using deltat: " << currentDeltat);
296// PositionUpdate = currentDeltat * currentGradient;
297// }
298
299 // finally set new values
300 (*iter)->setPositionAtStep(_TimeStep, currentPosition + PositionUpdate);
301 }
302
303 return maxComponents;
304 }
305
306 // knowing the number of bonds in total, we can setup the storage for the
307 // projected forces
308 enum whichatom_t {
309 leftside=0,
310 rightside=1,
311 MAX_sides
312 };
313
314 /** Helper function to put bond force into a container.
315 *
316 * \param _walker atom
317 * \param _current_bond current bond of \a _walker
318 * \param _timestep time step
319 * \param _force calculated bond force
320 * \param _bv bondvectors for obtaining the correct index
321 * \param _projected_forces container
322 */
323 static void ForceStore(
324 const atom &_walker,
325 const bond::ptr &_current_bond,
326 const size_t &_timestep,
327 const double _force,
328 const BondVectors &_bv,
329 std::vector< // time step
330 std::vector< // which bond side
331 std::vector<double> > // over all bonds
332 > &_projected_forces)
333 {
334 std::vector<double> &forcelist = (&_walker == _current_bond->leftatom) ?
335 _projected_forces[_timestep][leftside] : _projected_forces[_timestep][rightside];
336 const size_t index = _bv.getIndexForBond(_current_bond);
337 ASSERT( index != (size_t)-1,
338 "ForceAnnealing() - could not find bond "+toString(*_current_bond)
339 +" in bondvectors");
340 forcelist[index] = _force;
341 }
342
343 /** Performs Gradient optimization on the bonds with BarzilaiBorwein stepwdith.
344 *
345 * \note this can only be called when there are at least two optimization
346 * time steps present, i.e. this must be preceeded by a simple anneal().
347 *
348 * We assume that forces have just been calculated. These forces are projected
349 * onto the bonds and these are annealed subsequently by moving atoms in the
350 * bond neighborhood on either side conjunctively.
351 *
352 *
353 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
354 * \param maxComponents to be filled with maximum force component over all atoms
355 */
356 Vector annealWithBondGraph_BarzilaiBorwein(
357 const int _TimeStep)
358 {
359 const int OldTimeStep = _TimeStep-2;
360 const int CurrentTimeStep = _TimeStep-1;
361 ASSERT(OldTimeStep >= 0,
362 "annealWithBondGraph_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth, and the new one to update on already present.");
363 ASSERT(currentStep > 1,
364 "annealWithBondGraph_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
365
366 LOG(1, "STATUS: performing BarzilaiBorwein anneal on bonds at step #" << currentStep);
367
368 Vector maxComponents;
369
370 // get nodes on either side of selected bond via BFS discovery
371 BoostGraphCreator BGcreator;
372 BGcreator.createFromRange(
373 AtomicForceManipulator<T>::atoms.begin(),
374 AtomicForceManipulator<T>::atoms.end(),
375 AtomicForceManipulator<T>::atoms.size(),
376 BreadthFirstSearchGatherer::AlwaysTruePredicate);
377 BreadthFirstSearchGatherer NodeGatherer(BGcreator);
378
379 /// We assume that a force is local, i.e. a bond is too short yet and hence
380 /// the atom needs to be moved. However, all the adjacent (bound) atoms might
381 /// already be at the perfect distance. If we just move the atom alone, we ruin
382 /// all the other bonds. Hence, it would be sensible to move every atom found
383 /// through the bond graph in the direction of the force as well by the same
384 /// PositionUpdate. This is almost what we are going to do.
385
386 /// One issue is: If we need to shorten bond, then we use the PositionUpdate
387 /// also on the the other bond partner already. This is because it is in the
388 /// direction of the bond. Therefore, the update is actually performed twice on
389 /// each bond partner, i.e. the step size is twice as large as it should be.
390 /// This problem only occurs when bonds need to be shortened, not when they
391 /// need to be made longer (then the force vector is facing the other
392 /// direction than the bond vector).
393 /// As a remedy we need to average the force on either end of the bond and
394 /// check whether each gradient points inwards out or outwards with respect
395 /// to the bond and then shift accordingly.
396
397 /// One more issue is that the projection onto the bond directions does not
398 /// recover the gradient but may be larger as the bond directions are a
399 /// generating system and not a basis (e.g. 3 bonds on a plane where 2 would
400 /// suffice to span the plane). To this end, we need to account for the
401 /// overestimation and obtain a weighting for each bond.
402
403 // initialize helper class for bond vectors using bonds from range of atoms
404 BondVectors bv;
405 bv.setFromAtomRange< T >(
406 AtomicForceManipulator<T>::atoms.begin(),
407 AtomicForceManipulator<T>::atoms.end(),
408 _TimeStep); // use time step to update here as this is the current set of bonds
409 const BondVectors::container_t &sorted_bonds = bv.getSorted();
410
411 std::vector< // time step
412 std::vector< // which bond side
413 std::vector<double> > // over all bonds
414 > projected_forces(2); // one for leftatoms, one for rightatoms (and for both time steps)
415 for (size_t i=0;i<2;++i) {
416 projected_forces[i].resize(MAX_sides);
417 for (size_t j=0;j<MAX_sides;++j)
418 projected_forces[i][j].resize(sorted_bonds.size(), 0.);
419 }
420
421 // for each atom we need to gather weights and then project the gradient
422 typedef std::map<atomId_t, BondVectors::weights_t > weights_per_atom_t;
423 std::vector<weights_per_atom_t> weights_per_atom(2);
424 typedef std::map<atomId_t, Vector> RemnantGradient_per_atom_t;
425 RemnantGradient_per_atom_t RemnantGradient_per_atom;
426 for (size_t timestep = 0; timestep <= 1; ++timestep) {
427 const size_t ReferenceTimeStep = CurrentTimeStep-timestep;
428 LOG(2, "DEBUG: given time step is " << _TimeStep
429 << ", timestep is " << timestep
430 << ", and ReferenceTimeStep is " << ReferenceTimeStep);
431
432 for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin();
433 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
434 const atom &walker = *(*iter);
435 const Vector &walkerGradient = walker.getAtomicForceAtStep(ReferenceTimeStep);
436 LOG(3, "DEBUG: Gradient of atom #" << walker.getId() << ", namely "
437 << walker << " is " << walkerGradient << " with magnitude of "
438 << walkerGradient.Norm());
439
440 const BondList& ListOfBonds = walker.getListOfBonds();
441 if (walkerGradient.Norm() > MYEPSILON) {
442
443 // gather subset of BondVectors for the current atom
444 const std::vector<Vector> BondVectors =
445 bv.getAtomsBondVectorsAtStep(walker, ReferenceTimeStep);
446
447 // go through all its bonds and calculate what magnitude is represented
448 // by the others i.e. sum of scalar products against other bonds
449 const std::pair<weights_per_atom_t::iterator, bool> inserter =
450 weights_per_atom[timestep].insert(
451 std::make_pair(walker.getId(),
452 bv.getWeightsForAtomAtStep(walker, BondVectors, ReferenceTimeStep)) );
453 ASSERT( inserter.second,
454 "ForceAnnealing::operator() - weight map for atom "+toString(walker)
455 +" and time step "+toString(timestep)+" already filled?");
456 BondVectors::weights_t &weights = inserter.first->second;
457 ASSERT( weights.size() == ListOfBonds.size(),
458 "ForceAnnealing::operator() - number of weights "
459 +toString(weights.size())+" does not match number of bonds "
460 +toString(ListOfBonds.size())+", error in calculation?");
461
462 // projected gradient over all bonds and place in one of projected_forces
463 // using the obtained weights
464 BondVectors::forcestore_t forcestoring =
465 boost::bind(&ForceAnnealing::ForceStore, _1, _2, _3, _4,
466 boost::cref(bv), boost::ref(projected_forces));
467 const Vector RemnantGradient = bv.getRemnantGradientForAtomAtStep(
468 walker, walkerGradient, BondVectors, weights, timestep, forcestoring
469 );
470 RemnantGradient_per_atom.insert( std::make_pair(walker.getId(), RemnantGradient) );
471 } else {
472 LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than "
473 << MYEPSILON << " for atom " << walker);
474 // note that projected_forces is initialized to full length and filled
475 // with zeros. Hence, nothing to do here
476 }
477 }
478 }
479
480 // step through each bond and shift the atoms
481 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end
482
483 LOG(3, "DEBUG: current step is " << currentStep << ", given time step is " << CurrentTimeStep);
484 const BondVectors::mapped_t bondvectors = bv.getBondVectorsAtStep(CurrentTimeStep);
485
486 for (BondVectors::container_t::const_iterator bondsiter = sorted_bonds.begin();
487 bondsiter != sorted_bonds.end(); ++bondsiter) {
488 const bond::ptr &current_bond = *bondsiter;
489 const size_t index = bv.getIndexForBond(current_bond);
490 const atom* bondatom[MAX_sides] = {
491 current_bond->leftatom,
492 current_bond->rightatom
493 };
494
495 // remove the edge
496#ifndef NDEBUG
497 const bool status =
498#endif
499 BGcreator.removeEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
500 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?");
501
502 // gather nodes for either atom
503 BoostGraphHelpers::Nodeset_t bondside_set[MAX_sides];
504 BreadthFirstSearchGatherer::distance_map_t distance_map[MAX_sides];
505 for (size_t side=leftside;side<MAX_sides;++side) {
506 bondside_set[side] = NodeGatherer(bondatom[side]->getId(), max_distance);
507 distance_map[side] = NodeGatherer.getDistances();
508 std::sort(bondside_set[side].begin(), bondside_set[side].end());
509 }
510
511 // re-add edge
512 BGcreator.addEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
513
514 // do for both leftatom and rightatom of bond
515 for (size_t side = leftside; side < MAX_sides; ++side) {
516 const double &bondforce = projected_forces[0][side][index];
517 const double &oldbondforce = projected_forces[1][side][index];
518 const double bondforcedifference = fabs(bondforce - oldbondforce);
519 LOG(4, "DEBUG: bondforce for " << (side == leftside ? "left" : "right")
520 << " side of bond is " << bondforce);
521 LOG(4, "DEBUG: oldbondforce for " << (side == leftside ? "left" : "right")
522 << " side of bond is " << oldbondforce);
523 // if difference or bondforce itself is zero, do nothing
524 if ((fabs(bondforce) < MYEPSILON) || (fabs(bondforcedifference) < MYEPSILON))
525 continue;
526
527 // get BondVector to bond
528 const BondVectors::mapped_t::const_iterator bviter =
529 bondvectors.find(current_bond);
530 ASSERT( bviter != bondvectors.end(),
531 "ForceAnnealing() - cannot find current_bond ?");
532 ASSERT( fabs(bviter->second.Norm() -1.) < MYEPSILON,
533 "ForceAnnealing() - norm of BondVector is not one");
534 const Vector &BondVector = bviter->second;
535
536 // calculate gradient and position differences for stepwidth
537 const Vector currentGradient = bondforce * BondVector;
538 LOG(4, "DEBUG: current projected gradient for "
539 << (side == leftside ? "left" : "right") << " side of bond is " << currentGradient);
540 const Vector &oldPosition = bondatom[side]->getPositionAtStep(OldTimeStep);
541 const Vector &currentPosition = bondatom[side]->getPositionAtStep(CurrentTimeStep);
542 const Vector PositionDifference = currentPosition - oldPosition;
543 LOG(4, "DEBUG: old position is " << oldPosition);
544 LOG(4, "DEBUG: current position is " << currentPosition);
545 LOG(4, "DEBUG: difference in position is " << PositionDifference);
546 LOG(4, "DEBUG: bondvector is " << BondVector);
547 const double projected_PositionDifference = PositionDifference.ScalarProduct(BondVector);
548 LOG(4, "DEBUG: difference in position projected onto bondvector is "
549 << projected_PositionDifference);
550 LOG(4, "DEBUG: abs. difference in forces is " << bondforcedifference);
551
552 // calculate step width
553 double stepwidth =
554 fabs(projected_PositionDifference)/bondforcedifference;
555 if (fabs(stepwidth) < 1e-10) {
556 // dont' warn in first step, deltat usage normal
557 if (currentStep != 1)
558 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
559 stepwidth = currentDeltat;
560 }
561 Vector PositionUpdate = stepwidth * currentGradient;
562 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
563
564 // add PositionUpdate for all nodes in the bondside_set
565 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[side].begin();
566 setiter != bondside_set[side].end(); ++setiter) {
567 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter
568 = distance_map[side].find(*setiter);
569 ASSERT( diter != distance_map[side].end(),
570 "ForceAnnealing() - could not find distance to an atom.");
571 const double factor = pow(damping_factor, diter->second+1);
572 LOG(3, "DEBUG: Update for atom #" << *setiter << " will be "
573 << factor << "*" << PositionUpdate);
574 if (GatheredUpdates.count((*setiter))) {
575 GatheredUpdates[(*setiter)] += factor*PositionUpdate;
576 } else {
577 GatheredUpdates.insert(
578 std::make_pair(
579 (*setiter),
580 factor*PositionUpdate) );
581 }
582 }
583 }
584 }
585
586 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
587 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
588 atom &walker = *(*iter);
589 // extract largest components for showing progress of annealing
590 const Vector currentGradient = walker.getAtomicForceAtStep(CurrentTimeStep);
591 for(size_t i=0;i<NDIM;++i)
592 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
593 }
594
595 // remove center of weight translation from gathered updates
596 Vector CommonTranslation;
597 for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
598 iter != GatheredUpdates.end(); ++iter) {
599 const Vector &update = iter->second;
600 CommonTranslation += update;
601 }
602 CommonTranslation *= 1./(double)GatheredUpdates.size();
603 LOG(3, "DEBUG: Subtracting common translation " << CommonTranslation
604 << " from all updates.");
605
606 // apply the gathered updates and set remnant gradients for atomic annealing
607 for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
608 iter != GatheredUpdates.end(); ++iter) {
609 const atomId_t &atomid = iter->first;
610 const Vector &update = iter->second;
611 atom* const walker = World::getInstance().getAtom(AtomById(atomid));
612 ASSERT( walker != NULL,
613 "ForceAnnealing() - walker with id "+toString(atomid)+" has suddenly disappeared.");
614 LOG(3, "DEBUG: Applying update " << update << " to atom #" << atomid
615 << ", namely " << *walker);
616 walker->setPositionAtStep(_TimeStep,
617 walker->getPositionAtStep(CurrentTimeStep)
618 + update - CommonTranslation);
619// walker->setAtomicForce( RemnantGradient_per_atom[walker->getId()] );
620 }
621
622 return maxComponents;
623 }
624
625 /** Reset function to unset static entities and artificial velocities.
626 *
627 */
628 void reset()
629 {
630 currentDeltat = 0.;
631 currentStep = 0;
632 }
633
634private:
635 //!> contains the current step in relation to maxsteps
636 static size_t currentStep;
637 //!> contains the maximum number of steps, determines initial and final step with currentStep
638 size_t maxSteps;
639 static double currentDeltat;
640 //!> minimum deltat for internal while loop (adaptive step width)
641 static double MinimumDeltat;
642 //!> contains the maximum bond graph distance up to which shifts of a single atom are spread
643 const int max_distance;
644 //!> the shifted is dampened by this factor with the power of the bond graph distance to the shift causing atom
645 const double damping_factor;
646};
647
648template <class T>
649double ForceAnnealing<T>::currentDeltat = 0.;
650template <class T>
651size_t ForceAnnealing<T>::currentStep = 0;
652template <class T>
653double ForceAnnealing<T>::MinimumDeltat = 1e-8;
654
655#endif /* FORCEANNEALING_HPP_ */
Note: See TracBrowser for help on using the repository browser.