source: src/Dynamics/ForceAnnealing.hpp@ 91c409

Candidate_v1.7.0 stable
Last change on this file since 91c409 was 0682c2, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

Excluded hydrogens due to their small mass from spreading updates.

  • hydrogens will rather adapt to position changes of other atoms than the other way round.
  • Property mode set to 100644
File size: 34.3 KB
RevLine 
[1a48d2]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
[355915]16#include <algorithm>
[77d0cd]17#include <functional>
[355915]18#include <iterator>
[b3aaf4]19#include <math.h>
[355915]20
21#include <boost/bind.hpp>
22
[1a48d2]23#include "Atom/atom.hpp"
24#include "Atom/AtomSet.hpp"
25#include "CodePatterns/Assert.hpp"
26#include "CodePatterns/Info.hpp"
27#include "CodePatterns/Log.hpp"
28#include "CodePatterns/Verbose.hpp"
[cdfb6f]29#include "Descriptors/AtomIdDescriptor.hpp"
[1a48d2]30#include "Dynamics/AtomicForceManipulator.hpp"
[77d0cd]31#include "Dynamics/BondVectors.hpp"
[1a48d2]32#include "Fragmentation/ForceMatrix.hpp"
[cdfb6f]33#include "Graph/BoostGraphCreator.hpp"
34#include "Graph/BoostGraphHelpers.hpp"
35#include "Graph/BreadthFirstSearchGatherer.hpp"
[1a48d2]36#include "Helpers/helpers.hpp"
37#include "Helpers/defs.hpp"
[e77580]38#include "LinearAlgebra/LinearSystemOfEquations.hpp"
39#include "LinearAlgebra/MatrixContent.hpp"
[1a48d2]40#include "LinearAlgebra/Vector.hpp"
[e77580]41#include "LinearAlgebra/VectorContent.hpp"
[1a48d2]42#include "Thermostats/ThermoStatContainer.hpp"
43#include "Thermostats/Thermostat.hpp"
44#include "World.hpp"
45
[cdfb6f]46/** This class is the essential build block for performing structural optimization.
[1a48d2]47 *
48 * Sadly, we have to use some static instances as so far values cannot be passed
[322d58]49 * between actions. Hence, we need to store the current step and the adaptive-
[cdfb6f]50 * step width (we cannot perform a line search, as we have no control over the
[1a48d2]51 * calculation of the forces).
[cdfb6f]52 *
53 * However, we do use the bond graph, i.e. if a single atom needs to be shifted
54 * to the left, then the whole molecule left of it is shifted, too. This is
55 * controlled by the \a max_distance parameter.
[1a48d2]56 */
57template <class T>
58class ForceAnnealing : public AtomicForceManipulator<T>
59{
60public:
61 /** Constructor of class ForceAnnealing.
[322d58]62 *
63 * \note We use a fixed delta t of 1.
[1a48d2]64 *
65 * \param _atoms set of atoms to integrate
66 * \param _Deltat time step width in atomic units
67 * \param _IsAngstroem whether length units are in angstroem or bohr radii
68 * \param _maxSteps number of optimization steps to perform
[cdfb6f]69 * \param _max_distance up to this bond order is bond graph taken into account.
[1a48d2]70 */
71 ForceAnnealing(
72 AtomSetMixin<T> &_atoms,
[216840]73 const double _Deltat,
[1a48d2]74 bool _IsAngstroem,
[cdfb6f]75 const size_t _maxSteps,
[56b4c6]76 const int _max_distance,
77 const double _damping_factor) :
[216840]78 AtomicForceManipulator<T>(_atoms, _Deltat, _IsAngstroem),
[cdfb6f]79 maxSteps(_maxSteps),
80 max_distance(_max_distance),
[4b2adf]81 damping_factor(_damping_factor),
82 FORCE_THRESHOLD(1e-8)
[1a48d2]83 {}
[216840]84
[1a48d2]85 /** Destructor of class ForceAnnealing.
86 *
87 */
88 ~ForceAnnealing()
89 {}
90
91 /** Performs Gradient optimization.
92 *
93 * We assume that forces have just been calculated.
94 *
95 *
[8450da]96 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
[1a48d2]97 * \param offset offset in matrix file to the first force component
[4b2adf]98 * \return false - need to continue annealing, true - may stop because forces very small
[1a48d2]99 * \todo This is not yet checked if it is correctly working with DoConstrainedMD set >0.
100 */
[4b2adf]101 bool operator()(
[8450da]102 const int _TimeStep,
[b2acca]103 const size_t _offset,
104 const bool _UseBondgraph)
[1a48d2]105 {
[8450da]106 const int CurrentTimeStep = _TimeStep-1;
107 ASSERT( CurrentTimeStep >= 0,
108 "ForceAnnealing::operator() - a new time step (upon which we work) must already have been copied.");
109
[1a48d2]110 // make sum of forces equal zero
[77d0cd]111 AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(
112 _offset,
[8450da]113 CurrentTimeStep);
[1a48d2]114
115 // are we in initial step? Then set static entities
[b2acca]116 Vector maxComponents(zeroVec);
[1a48d2]117 if (currentStep == 0) {
118 currentDeltat = AtomicForceManipulator<T>::Deltat;
119 currentStep = 1;
120 LOG(2, "DEBUG: Initial step, setting values, current step is #" << currentStep);
[b2acca]121
122 // always use atomic annealing on first step
[8450da]123 maxComponents = anneal(_TimeStep);
[1a48d2]124 } else {
125 ++currentStep;
126 LOG(2, "DEBUG: current step is #" << currentStep);
[b2acca]127
[f433ec]128 // bond graph annealing is always followed by a normal annealing
[b2acca]129 if (_UseBondgraph)
[8450da]130 maxComponents = annealWithBondGraph_BarzilaiBorwein(_TimeStep);
[07d4b1]131 // cannot store RemnantGradient in Atom's Force as it ruins BB stepwidth calculation
132 else
[8450da]133 maxComponents = anneal_BarzilaiBorwein(_TimeStep);
[1a48d2]134 }
135
[6458e7]136
[b2acca]137 LOG(1, "STATUS: Largest remaining force components at step #"
138 << currentStep << " are " << maxComponents);
139
[4b2adf]140 // check whether are smaller than threshold
141 bool AnnealingFinished = false;
142 double maxcomp = 0.;
143 for (size_t i=0;i<NDIM;++i)
144 maxcomp = std::max(maxcomp, fabs(maxComponents[i]));
145 if (maxcomp < FORCE_THRESHOLD) {
146 LOG(1, "STATUS: Force components are all less than " << FORCE_THRESHOLD
147 << ", stopping.");
148 currentStep = maxSteps;
149 AnnealingFinished = true;
150 }
151
[b2acca]152 // are we in final step? Remember to reset static entities
153 if (currentStep == maxSteps) {
154 LOG(2, "DEBUG: Final step, resetting values");
155 reset();
156 }
[4b2adf]157
158 return AnnealingFinished;
[b2acca]159 }
160
[e21d55]161 /** Helper function to calculate the Barzilai-Borwein stepwidth.
162 *
163 * \param _PositionDifference difference in position between current and last step
164 * \param _GradientDifference difference in gradient between current and last step
165 * \return step width according to Barzilai-Borwein
166 */
167 double getBarzilaiBorweinStepwidth(const Vector &_PositionDifference, const Vector &_GradientDifference)
168 {
169 double stepwidth = 0.;
[b3aaf4]170 if (_GradientDifference.Norm() > MYEPSILON)
[e21d55]171 stepwidth = fabs(_PositionDifference.ScalarProduct(_GradientDifference))/
172 _GradientDifference.NormSquared();
173 if (fabs(stepwidth) < 1e-10) {
174 // dont' warn in first step, deltat usage normal
175 if (currentStep != 1)
176 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
177 stepwidth = currentDeltat;
178 }
[bd19c1]179 return std::min(1., stepwidth);
[e21d55]180 }
181
[b2acca]182 /** Performs Gradient optimization on the atoms.
183 *
184 * We assume that forces have just been calculated.
185 *
[8450da]186 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
[0c4f24]187 * \return to be filled with maximum force component over all atoms
[b2acca]188 */
[0c4f24]189 Vector anneal(
[8450da]190 const int _TimeStep)
[b2acca]191 {
[8450da]192 const int CurrentTimeStep = _TimeStep-1;
193 ASSERT( CurrentTimeStep >= 0,
194 "ForceAnnealing::anneal() - a new time step (upon which we work) must already have been copied.");
195
196 LOG(1, "STATUS: performing simple anneal with default stepwidth " << currentDeltat << " at step #" << currentStep);
197
[0c4f24]198 Vector maxComponents;
[be729b]199 bool deltat_decreased = false;
[b2acca]200 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
201 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
202 // atom's force vector gives steepest descent direction
[b3aaf4]203 const Vector &currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
204 const Vector &currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
[8450da]205 LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
206 LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
[b2acca]207// LOG(4, "DEBUG: Force for atom " << **iter << " is " << currentGradient);
208
209 // we use Barzilai-Borwein update with position reversed to get descent
[8450da]210 double stepwidth = currentDeltat;
[b2acca]211 Vector PositionUpdate = stepwidth * currentGradient;
212 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
213
214 // extract largest components for showing progress of annealing
215 for(size_t i=0;i<NDIM;++i)
[9bb8c8]216 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
[b2acca]217
[be729b]218 // steps may go back and forth again (updates are of same magnitude but
219 // have different sign: Check whether this is the case and one step with
220 // deltat to interrupt this sequence
[8450da]221 if (currentStep > 1) {
222 const int OldTimeStep = CurrentTimeStep-1;
223 ASSERT( OldTimeStep >= 0,
224 "ForceAnnealing::anneal() - if currentStep is "+toString(currentStep)
225 +", then there should be at least three time steps.");
[83956e]226 const Vector &oldPosition = (*iter)->getPositionAtStep(OldTimeStep);
[8450da]227 const Vector PositionDifference = currentPosition - oldPosition;
228 LOG(4, "DEBUG: oldPosition for atom #" << (*iter)->getId() << " is " << oldPosition);
229 LOG(4, "DEBUG: PositionDifference for atom #" << (*iter)->getId() << " is " << PositionDifference);
[be729b]230 if ((PositionUpdate.ScalarProduct(PositionDifference) < 0)
231 && (fabs(PositionUpdate.NormSquared()-PositionDifference.NormSquared()) < 1e-3)) {
232 // for convergence we want a null sequence here, too
233 if (!deltat_decreased) {
234 deltat_decreased = true;
235 currentDeltat = .5*currentDeltat;
236 }
237 LOG(2, "DEBUG: Upgrade in other direction: " << PositionUpdate
238 << " > " << PositionDifference
239 << ", using deltat: " << currentDeltat);
[b2acca]240 PositionUpdate = currentDeltat * currentGradient;
[8450da]241 }
[b2acca]242 }
[be729b]243
[b2acca]244 // finally set new values
[8450da]245 (*iter)->setPositionAtStep(_TimeStep, currentPosition + PositionUpdate);
246 }
247
248 return maxComponents;
249 }
250
[b3aaf4]251 /** Performs Gradient optimization on a single atom using BarzilaiBorwein step width.
252 *
253 * \param _atom atom to anneal
254 * \param OldTimeStep old time step
255 * \param CurrentTimeStep current time step whose gradient we've just calculated
256 * \param TimeStepToSet time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
257 */
258 void annealAtom_BarzilaiBorwein(
259 atom * const _atom,
260 const int &OldTimeStep,
261 const int &CurrentTimeStep,
262 const int &TimeStepToSet
263 )
264 {
265 // atom's force vector gives steepest descent direction
266 const Vector &oldPosition = _atom->getPositionAtStep(OldTimeStep);
267 const Vector &currentPosition = _atom->getPositionAtStep(CurrentTimeStep);
268 const Vector &oldGradient = _atom->getAtomicForceAtStep(OldTimeStep);
269 const Vector &currentGradient = _atom->getAtomicForceAtStep(CurrentTimeStep);
270 LOG(4, "DEBUG: oldPosition for atom #" << _atom->getId() << " is " << oldPosition);
271 LOG(4, "DEBUG: currentPosition for atom #" << _atom->getId() << " is " << currentPosition);
272 LOG(4, "DEBUG: oldGradient for atom #" << _atom->getId() << " is " << oldGradient);
273 LOG(4, "DEBUG: currentGradient for atom #" << _atom->getId() << " is " << currentGradient);
274// LOG(4, "DEBUG: Force for atom #" << _atom->getId() << " is " << currentGradient);
275
276 // we use Barzilai-Borwein update with position reversed to get descent
277 const Vector PositionDifference = currentPosition - oldPosition;
278 const Vector GradientDifference = (currentGradient - oldGradient);
279 double stepwidth = getBarzilaiBorweinStepwidth(PositionDifference, GradientDifference);
280 Vector PositionUpdate = stepwidth * currentGradient;
281 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
282
283 // finally set new values
284 _atom->setPositionAtStep(TimeStepToSet, currentPosition + PositionUpdate);
285 }
286
[8450da]287 /** Performs Gradient optimization on the atoms using BarzilaiBorwein step width.
288 *
289 * \note this can only be called when there are at least two optimization
[b3aaf4]290 * time steps present, i.e. this must be preceded by a simple anneal().
[8450da]291 *
292 * We assume that forces have just been calculated.
293 *
294 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
295 * \return to be filled with maximum force component over all atoms
296 */
297 Vector anneal_BarzilaiBorwein(
298 const int _TimeStep)
299 {
300 const int OldTimeStep = _TimeStep-2;
301 const int CurrentTimeStep = _TimeStep-1;
302 ASSERT( OldTimeStep >= 0,
303 "ForceAnnealing::anneal_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
304 ASSERT(currentStep > 1,
305 "ForceAnnealing::anneal_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
306
307 LOG(1, "STATUS: performing BarzilaiBorwein anneal at step #" << currentStep);
308
309 Vector maxComponents;
310 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
311 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
312
[b3aaf4]313 annealAtom_BarzilaiBorwein(*iter, OldTimeStep, CurrentTimeStep, _TimeStep);
[8450da]314
315 // extract largest components for showing progress of annealing
[b3aaf4]316 const Vector &currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
[8450da]317 for(size_t i=0;i<NDIM;++i)
318 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
[b2acca]319 }
[0c4f24]320
321 return maxComponents;
[b2acca]322 }
323
[0682c2]324 /** Helper function to insert \a PositionUpdate into a map for every atom.
325 *
326 * \param _GatheredUpdates map of updates per atom
327 * \param _LargestUpdate_per_Atom map with the largest update per atom for checking
328 * \param _atomno key for map
329 * \param _PositionUpdate update to add
330 * \param _factor optional dampening factor
331 */
332 void updateInserter(
333 std::map<atomId_t, Vector> &_GatheredUpdates,
334 std::map<atomId_t, double> &_LargestUpdate_per_Atom,
335 const atomId_t _atomno,
336 const Vector &_PositionUpdate,
337 const double _factor = 1.
338 )
339 {
340 if (_GatheredUpdates.count(_atomno)) {
341 _GatheredUpdates[_atomno] += _factor*_PositionUpdate;
342 _LargestUpdate_per_Atom[_atomno] =
343 std::max(_LargestUpdate_per_Atom[_atomno], _factor*_PositionUpdate.Norm());
344 } else {
345 _GatheredUpdates.insert(
346 std::make_pair(
347 _atomno,
348 _factor*_PositionUpdate) );
349 _LargestUpdate_per_Atom.insert(
350 std::make_pair(
351 _atomno,
352 _PositionUpdate.Norm()) );
353 }
354 }
355
[8450da]356 /** Performs Gradient optimization on the bonds with BarzilaiBorwein stepwdith.
357 *
358 * \note this can only be called when there are at least two optimization
359 * time steps present, i.e. this must be preceeded by a simple anneal().
[b2acca]360 *
361 * We assume that forces have just been calculated. These forces are projected
362 * onto the bonds and these are annealed subsequently by moving atoms in the
363 * bond neighborhood on either side conjunctively.
364 *
365 *
[8450da]366 * \param _TimeStep time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
[b2acca]367 * \param maxComponents to be filled with maximum force component over all atoms
368 */
[8450da]369 Vector annealWithBondGraph_BarzilaiBorwein(
370 const int _TimeStep)
[b2acca]371 {
[8450da]372 const int OldTimeStep = _TimeStep-2;
373 const int CurrentTimeStep = _TimeStep-1;
374 ASSERT(OldTimeStep >= 0,
375 "annealWithBondGraph_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth, and the new one to update on already present.");
376 ASSERT(currentStep > 1,
377 "annealWithBondGraph_BarzilaiBorwein() - we need two present optimization steps to compute stepwidth.");
378
379 LOG(1, "STATUS: performing BarzilaiBorwein anneal on bonds at step #" << currentStep);
380
[0c4f24]381 Vector maxComponents;
382
[cdfb6f]383 // get nodes on either side of selected bond via BFS discovery
384 BoostGraphCreator BGcreator;
385 BGcreator.createFromRange(
386 AtomicForceManipulator<T>::atoms.begin(),
387 AtomicForceManipulator<T>::atoms.end(),
388 AtomicForceManipulator<T>::atoms.size(),
389 BreadthFirstSearchGatherer::AlwaysTruePredicate);
390 BreadthFirstSearchGatherer NodeGatherer(BGcreator);
391
[83956e]392 /** We assume that a force is local, i.e. a bond is too short yet and hence
393 * the atom needs to be moved. However, all the adjacent (bound) atoms might
394 * already be at the perfect distance. If we just move the atom alone, we ruin
395 * all the other bonds. Hence, it would be sensible to move every atom found
396 * through the bond graph in the direction of the force as well by the same
397 * PositionUpdate. This is almost what we are going to do, see below.
398 *
399 * This is to make the force a little more global in the sense of a multigrid
400 * solver that uses various coarser grids to transport errors more effectively
401 * over finely resolved grids.
402 *
403 */
404
405 /** The idea is that we project the gradients onto the bond vectors and determine
406 * from the sum of projected gradients from either side whether the bond is
407 * to contract or to expand. As the gradient acting as the normal vector of
408 * a plane supported at the position of the atom separates all bonds into two
409 * sets, we check whether all on one side are contracting and all on the other
410 * side are expanding. In this case we may move not only the atom itself but
411 * may propagate its update along a limited-horizon BFS to neighboring atoms.
412 *
413 */
[e77580]414
[77d0cd]415 // initialize helper class for bond vectors using bonds from range of atoms
416 BondVectors bv;
417 bv.setFromAtomRange< T >(
418 AtomicForceManipulator<T>::atoms.begin(),
419 AtomicForceManipulator<T>::atoms.end(),
[8450da]420 _TimeStep); // use time step to update here as this is the current set of bonds
[77d0cd]421
[83956e]422 std::vector< // which bond side
423 std::vector<double> > // over all bonds
424 projected_forces; // one for leftatoms, one for rightatoms
425 projected_forces.resize(BondVectors::MAX_sides);
426 for (size_t j=0;j<BondVectors::MAX_sides;++j)
427 projected_forces[j].resize(bv.size(), 0.);
[1a48d2]428
[83956e]429 // for each atom we need to project the gradient
430 for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin();
431 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
432 const atom &walker = *(*iter);
433 const Vector &walkerGradient = walker.getAtomicForceAtStep(CurrentTimeStep);
434 const double GradientNorm = walkerGradient.Norm();
435 LOG(3, "DEBUG: Gradient of atom #" << walker.getId() << ", namely "
436 << walker << " is " << walkerGradient << " with magnitude of "
437 << GradientNorm);
438
439 if (GradientNorm > MYEPSILON) {
440 bv.getProjectedGradientsForAtomAtStep(
441 walker, walkerGradient, CurrentTimeStep, projected_forces
442 );
443 } else {
444 LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than "
445 << MYEPSILON << " for atom " << walker);
446 // note that projected_forces is initialized to full length and filled
447 // with zeros. Hence, nothing to do here
[e77580]448 }
449 }
[cdfb6f]450
[e77580]451 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end
[b3aaf4]452 std::map<atomId_t, double> LargestUpdate_per_Atom; //!< check whether updates cancelled each other
[83956e]453 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
454 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
455 atom &walker = *(*iter);
[77d0cd]456
[83956e]457 /// calculate step width
458 const Vector &oldPosition = (*iter)->getPositionAtStep(OldTimeStep);
459 const Vector &currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
460 const Vector &oldGradient = (*iter)->getAtomicForceAtStep(OldTimeStep);
461 const Vector &currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
462 LOG(4, "DEBUG: oldPosition for atom #" << (*iter)->getId() << " is " << oldPosition);
463 LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
464 LOG(4, "DEBUG: oldGradient for atom #" << (*iter)->getId() << " is " << oldGradient);
465 LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
466// LOG(4, "DEBUG: Force for atom #" << (*iter)->getId() << " is " << currentGradient);
[77d0cd]467
[83956e]468 // we use Barzilai-Borwein update with position reversed to get descent
469 const Vector PositionDifference = currentPosition - oldPosition;
470 const Vector GradientDifference = (currentGradient - oldGradient);
[06536b]471 double stepwidth = getBarzilaiBorweinStepwidth(PositionDifference, GradientDifference);
[83956e]472 Vector PositionUpdate = stepwidth * currentGradient;
[b3aaf4]473 // cap updates (if non-zero) at 0.2 angstroem. BB tends to overshoot.
474 for (size_t i=0;i<NDIM;++i)
475 if (fabs(PositionUpdate[i]) > MYEPSILON)
[bd19c1]476 PositionUpdate[i] = std::min(0.2, fabs(PositionUpdate[i]))*PositionUpdate[i]/fabs(PositionUpdate[i]);
[83956e]477 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
478
[0682c2]479 if (walker.getElementNo() != 1) {
480 /** for each atom, we imagine a plane at the position of the atom with
481 * its atomic gradient as the normal vector. We go through all its bonds
482 * and check on which side of the plane the bond is. This defines whether
483 * the bond is contracting (+) or expanding (-) with respect to this atom.
484 *
485 * A bond has two atoms, however. Hence, we do this for either atom and
486 * look at the combination: Is it in sum contracting or expanding given
487 * both projected_forces?
488 */
489
490 /** go through all bonds and check projected_forces and side of plane
491 * the idea is that if all bonds on one side are contracting ones or expanding,
492 * respectively, then we may shift not only the atom with respect to its
493 * gradient but also its neighbors (towards contraction or towards
494 * expansion depending on direction of gradient).
495 * if they are mixed on both sides of the plane, then we simply shift
496 * only the atom itself.
497 * if they are not mixed on either side, then we also only shift the
498 * atom, namely away from expanding and towards contracting bonds.
499 *
500 * We may get this information right away by looking at the projected_forces.
501 * They give the atomic gradient of either atom projected onto the BondVector
502 * with an additional weight in [0,1].
503 */
504
505 // sign encodes side of plane and also encodes contracting(-) or expanding(+)
506 typedef std::vector<int> sides_t;
507 typedef std::vector<int> types_t;
508 sides_t sides;
509 types_t types;
[83956e]510 const BondList& ListOfBonds = walker.getListOfBonds();
[0682c2]511 for(BondList::const_iterator bonditer = ListOfBonds.begin();
512 bonditer != ListOfBonds.end(); ++bonditer) {
513 const bond::ptr &current_bond = *bonditer;
514
515 // BondVector goes from bond::rightatom to bond::leftatom
516 const size_t index = bv.getIndexForBond(current_bond);
517 std::vector<double> &forcelist = (&walker == current_bond->leftatom) ?
518 projected_forces[BondVectors::leftside] : projected_forces[BondVectors::rightside];
519 // note that projected_forces has sign such as to indicate whether
520 // atomic gradient wants bond to contract (-) or expand (+).
521 // This goes into sides: Minus side points away from gradient, plus side point
522 // towards gradient.
523 //
524 // the sum of both bond sides goes into types, depending on which is
525 // stronger if either wants a different thing
526 const double &temp = forcelist[index];
527 if (fabs(temp) < MYEPSILON)
528 sides.push_back(1);
529 else
530 sides.push_back( -1.*temp/fabs(temp) ); // BondVectors has exactly opposite sign for sides decision
531 ASSERT( (sides.back() == 1) || (sides.back() == -1),
532 "ForceAnnealing() - sides is not in {-1,1}.");
533 const double sum =
534 projected_forces[BondVectors::leftside][index]+projected_forces[BondVectors::rightside][index];
535 types.push_back( sum/fabs(sum) );
536 LOG(4, "DEBUG: Bond " << *current_bond << " is on side " << sides.back()
537 << " and has type " << types.back());
538 }
539 // /// check whether both conditions are compatible:
540 // // i.e. either we have ++/-- for all entries in sides and types
541 // // or we have +-/-+ for all entries
542 // // hence, multiplying and taking the sum and its absolute value
543 // // should be equal to the maximum number of entries
544 // sides_t results;
545 // std::transform(
546 // sides.begin(), sides.end(),
547 // types.begin(),
548 // std::back_inserter(results),
549 // std::multiplies<int>);
550 // int result = abs(std::accumulate(results.begin(), results.end(), 0, std::plus<int>));
551
552 std::vector<size_t> first_per_side(2, (size_t)-1); //!< mark down one representative from either side
553 std::vector< std::vector<int> > types_per_side(2); //!< gather all types on each side
554 types_t::const_iterator typesiter = types.begin();
[83956e]555 for (sides_t::const_iterator sidesiter = sides.begin();
[0682c2]556 sidesiter != sides.end(); ++sidesiter, ++typesiter) {
557 const size_t index = (*sidesiter+1)/2;
558 types_per_side[index].push_back(*typesiter);
559 if (first_per_side[index] == (size_t)-1)
560 first_per_side[index] = std::distance(const_cast<const sides_t &>(sides).begin(), sidesiter);
561 }
562 LOG(4, "DEBUG: First on side minus is " << first_per_side[0] << ", and first on side plus is "
563 << first_per_side[1]);
564 //!> enumerate types per side with a little witching with the numbers to allow easy setting from types
565 enum whichtypes_t {
566 contracting=0,
567 unset=1,
568 expanding=2,
569 mixed
570 };
571 std::vector<int> typeside(2, unset);
572 for(size_t i=0;i<2;++i) {
573 for (std::vector<int>::const_iterator tpsiter = types_per_side[i].begin();
574 tpsiter != types_per_side[i].end(); ++tpsiter) {
575 if (typeside[i] == unset) {
576 typeside[i] = *tpsiter+1; //contracting(0) or expanding(2)
577 } else {
578 if (typeside[i] != (*tpsiter+1)) // no longer he same type
579 typeside[i] = mixed;
580 }
[83956e]581 }
[77d0cd]582 }
[0682c2]583 LOG(4, "DEBUG: Minus side is " << typeside[0] << " and plus side is " << typeside[1]);
584
585 typedef std::vector< std::pair<atomId_t, atomId_t> > RemovedEdges_t;
586 if ((typeside[0] != mixed) || (typeside[1] != mixed)) {
587 const size_t sideno = ((typeside[0] != mixed) && (typeside[0] != unset)) ? 0 : 1;
588 LOG(4, "DEBUG: Chosen side is " << sideno << " with type " << typeside[sideno]);
589 ASSERT( (typeside[sideno] == contracting) || (typeside[sideno] == expanding),
590 "annealWithBondGraph_BB() - chosen side is neither expanding nor contracting.");
591 // one side is not mixed, all bonds on one side are of same type
592 // hence, find out which bonds to exclude
593 const BondList& ListOfBonds = walker.getListOfBonds();
594
595 // sideno is away (0) or in direction (1) of gradient
596 // tpyes[first_per_side[sideno]] is either contracting (-1) or expanding (+1)
597 // : side (i), where (i) means which bonds we keep for the BFS, bonds
598 // on side (-i) are removed
599 // If all bonds on side away (0) want expansion (+1), move towards side with atom: side 1
600 // if all bonds side towards (1) want contraction (-1), move away side with atom : side -1
601
602 // unsure whether this or do nothing in the remaining cases:
603 // If all bonds on side toward (1) want expansion (+1), move away side with atom : side -1
604 // (the reasoning is that the bond's other atom must have a stronger
605 // gradient in the same direction and they push along atoms in
606 // gradient direction: we don't want to interface with those.
607 // Hence, move atoms along on away side
608 // if all bonds side away (0) want contraction (-1), move towards side with atom: side 1
609 // (the reasoning is the same, don't interfere with update from
610 // stronger gradient)
611 // hence, the decision is only based on sides once we have picked a side
612 // depending on all bonds associated with have same good type.
613
614 // away from gradient (minus) and contracting
615 // or towards gradient (plus) and expanding
616 // gather all on same side and remove
617 const double sign =
618 (sides[first_per_side[sideno]] == types[first_per_side[sideno]])
619 ? sides[first_per_side[sideno]] : -1.*sides[first_per_side[sideno]];
620
621 LOG(4, "DEBUG: Removing edges from side with sign " << sign);
622 BondList::const_iterator bonditer = ListOfBonds.begin();
623 RemovedEdges_t RemovedEdges;
624 for (sides_t::const_iterator sidesiter = sides.begin();
625 sidesiter != sides.end(); ++sidesiter, ++bonditer) {
626 if (*sidesiter == sign) {
627 // remove the edge
628 const bond::ptr &current_bond = *bonditer;
629 LOG(5, "DEBUG: Removing edge " << *current_bond);
630 RemovedEdges.push_back( std::make_pair(
631 current_bond->leftatom->getId(),
632 current_bond->rightatom->getId())
633 );
634 #ifndef NDEBUG
635 const bool status =
636 #endif
637 BGcreator.removeEdge(RemovedEdges.back());
638 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?");
639 }
640 }
641 // perform limited-horizon BFS
642 BoostGraphHelpers::Nodeset_t bondside_set;
643 BreadthFirstSearchGatherer::distance_map_t distance_map;
644 bondside_set = NodeGatherer(walker.getId(), max_distance);
645 distance_map = NodeGatherer.getDistances();
646 std::sort(bondside_set.begin(), bondside_set.end());
647
648 // re-add edge
649 for (RemovedEdges_t::const_iterator edgeiter = RemovedEdges.begin();
650 edgeiter != RemovedEdges.end(); ++edgeiter)
651 BGcreator.addEdge(edgeiter->first, edgeiter->second);
652
653 // update position with dampening factor on the discovered bonds
654 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set.begin();
655 setiter != bondside_set.end(); ++setiter) {
656 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter
657 = distance_map.find(*setiter);
658 ASSERT( diter != distance_map.end(),
659 "ForceAnnealing() - could not find distance to an atom.");
660 const double factor = pow(damping_factor, diter->second+1);
661 LOG(3, "DEBUG: Update for atom #" << *setiter << " will be "
662 << factor << "*" << PositionUpdate);
663 updateInserter(GatheredUpdates, LargestUpdate_per_Atom, *setiter, PositionUpdate, factor);
[77d0cd]664 }
[0682c2]665 } else {
666 // simple atomic annealing, i.e. damping factor of 1
667 updateInserter(GatheredUpdates, LargestUpdate_per_Atom, walker.getId(), PositionUpdate);
[77d0cd]668 }
[83956e]669 } else {
[0682c2]670 // hydrogens (are light-weighted and therefore) are always updated normally
671 LOG(3, "DEBUG: Update for hydrogen #" << walker.getId() << " will be " << PositionUpdate);
672 updateInserter(GatheredUpdates, LargestUpdate_per_Atom, walker.getId(), PositionUpdate);
[77d0cd]673 }
674 }
[cdfb6f]675
[355915]676 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
677 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
678 atom &walker = *(*iter);
679 // extract largest components for showing progress of annealing
[b3aaf4]680 const Vector &currentGradient = walker.getAtomicForceAtStep(CurrentTimeStep);
[355915]681 for(size_t i=0;i<NDIM;++i)
682 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
[1a48d2]683 }
[e77580]684
[83956e]685// // remove center of weight translation from gathered updates
686// Vector CommonTranslation;
687// for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
688// iter != GatheredUpdates.end(); ++iter) {
689// const Vector &update = iter->second;
690// CommonTranslation += update;
691// }
692// CommonTranslation *= 1./(double)GatheredUpdates.size();
693// LOG(3, "DEBUG: Subtracting common translation " << CommonTranslation
694// << " from all updates.");
[90050b]695
[f433ec]696 // apply the gathered updates and set remnant gradients for atomic annealing
[646f73]697 Vector LargestUpdate;
[cdfb6f]698 for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
699 iter != GatheredUpdates.end(); ++iter) {
700 const atomId_t &atomid = iter->first;
701 const Vector &update = iter->second;
702 atom* const walker = World::getInstance().getAtom(AtomById(atomid));
703 ASSERT( walker != NULL,
704 "ForceAnnealing() - walker with id "+toString(atomid)+" has suddenly disappeared.");
[866dec]705 LOG(3, "DEBUG: Applying update " << update << " to atom #" << atomid
706 << ", namely " << *walker);
[646f73]707 for (size_t i=0;i<NDIM;++i)
708 LargestUpdate[i] = std::max(LargestUpdate[i], fabs(update[i]));
[b3aaf4]709
710 std::map<atomId_t, double>::const_iterator largestiter = LargestUpdate_per_Atom.find(atomid);
711 ASSERT( largestiter != LargestUpdate_per_Atom.end(),
712 "ForceAnnealing() - walker with id "+toString(atomid)+" not in LargestUpdates.");
713 // if we had large updates but their sum is very small
714 if (update.Norm()/largestiter->second > MYEPSILON) {
715 walker->setPositionAtStep(_TimeStep,
716 walker->getPositionAtStep(CurrentTimeStep) + update); // - CommonTranslation);
717 } else {
718 // then recalc update with simple anneal
719 LOG(2, "WARNING: Updates on atom " << *iter << " cancel themselves, performing simple anneal step.");
720 annealAtom_BarzilaiBorwein(walker, OldTimeStep, CurrentTimeStep, _TimeStep);
721 }
[cdfb6f]722 }
[646f73]723 LOG(1, "STATUS: Largest absolute update components are " << LargestUpdate);
[0c4f24]724
725 return maxComponents;
[1a48d2]726 }
727
[1e49e66]728 /** Reset function to unset static entities and artificial velocities.
729 *
730 */
731 void reset()
732 {
733 currentDeltat = 0.;
734 currentStep = 0;
735 }
736
[1a48d2]737private:
738 //!> contains the current step in relation to maxsteps
739 static size_t currentStep;
740 //!> contains the maximum number of steps, determines initial and final step with currentStep
741 size_t maxSteps;
742 static double currentDeltat;
743 //!> minimum deltat for internal while loop (adaptive step width)
744 static double MinimumDeltat;
[cdfb6f]745 //!> contains the maximum bond graph distance up to which shifts of a single atom are spread
746 const int max_distance;
747 //!> the shifted is dampened by this factor with the power of the bond graph distance to the shift causing atom
748 const double damping_factor;
[4b2adf]749 //!> threshold for force components to stop annealing
750 const double FORCE_THRESHOLD;
[1a48d2]751};
752
753template <class T>
754double ForceAnnealing<T>::currentDeltat = 0.;
755template <class T>
756size_t ForceAnnealing<T>::currentStep = 0;
757template <class T>
758double ForceAnnealing<T>::MinimumDeltat = 1e-8;
759
760#endif /* FORCEANNEALING_HPP_ */
Note: See TracBrowser for help on using the repository browser.