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

AutomationFragmentation_failures Candidate_v1.6.1 ChemicalSpaceEvaluator Exclude_Hydrogens_annealWithBondGraph ForceAnnealing_with_BondGraph_contraction-expansion StoppableMakroAction
Last change on this file since 4b2adf was 4b2adf, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

ForceAnnealiing now returns bool to indicate stop condition for MakroAction.

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