source: src/Dynamics/ForceAnnealing.hpp@ c5ac2a

ForceAnnealing_with_BondGraph_oldbranch
Last change on this file since c5ac2a was c5ac2a, checked in by Frederik Heber <frederik.heber@…>, 8 years ago

FIX: Removed deltat reduction from anneal_BarzilaiBorwein() as it stops convergence at some point.

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