source: src/Dynamics/ForceAnnealing.hpp@ 9190e6

ForceAnnealing_goodresults ForceAnnealing_tocheck
Last change on this file since 9190e6 was 9190e6, checked in by Frederik Heber <frederik.heber@…>, 8 years ago

ForceAnnealing now shifts atoms in the bond graph to anneal forces, too.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * ForceAnnealing.hpp
3 *
4 * Created on: Aug 02, 2014
5 * Author: heber
6 */
7
8#ifndef FORCEANNEALING_HPP_
9#define FORCEANNEALING_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include "Atom/atom.hpp"
17#include "Atom/AtomSet.hpp"
18#include "CodePatterns/Assert.hpp"
19#include "CodePatterns/Info.hpp"
20#include "CodePatterns/Log.hpp"
21#include "CodePatterns/Verbose.hpp"
22#include "Dynamics/AtomicForceManipulator.hpp"
23#include "Fragmentation/ForceMatrix.hpp"
24#include "Graph/BoostGraphCreator.hpp"
25#include "Graph/BoostGraphHelpers.hpp"
26#include "Graph/BreadthFirstSearchGatherer.hpp"
27#include "Helpers/helpers.hpp"
28#include "Helpers/defs.hpp"
29#include "LinearAlgebra/Vector.hpp"
30#include "Thermostats/ThermoStatContainer.hpp"
31#include "Thermostats/Thermostat.hpp"
32#include "World.hpp"
33
34/** This class is the essential build block for performing structural optimization.
35 *
36 * Sadly, we have to use some static instances as so far values cannot be passed
37 * between actions. Hence, we need to store the current step and the adaptive-
38 * step width (we cannot perform a line search, as we have no control over the
39 * calculation of the forces).
40 */
41template <class T>
42class ForceAnnealing : public AtomicForceManipulator<T>
43{
44public:
45 /** Constructor of class ForceAnnealing.
46 *
47 * \note We use a fixed delta t of 1.
48 *
49 * \param _atoms set of atoms to integrate
50 * \param _Deltat time step width in atomic units
51 * \param _IsAngstroem whether length units are in angstroem or bohr radii
52 * \param _maxSteps number of optimization steps to perform
53 */
54 ForceAnnealing(
55 AtomSetMixin<T> &_atoms,
56 bool _IsAngstroem,
57 const size_t _maxSteps) :
58 AtomicForceManipulator<T>(_atoms, 1., _IsAngstroem),
59 maxSteps(_maxSteps)
60 {}
61 /** Destructor of class ForceAnnealing.
62 *
63 */
64 ~ForceAnnealing()
65 {}
66
67 /** Performs Gradient optimization.
68 *
69 * We assume that forces have just been calculated.
70 *
71 *
72 * \param NextStep current time step (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
73 * \param offset offset in matrix file to the first force component
74 * \todo This is not yet checked if it is correctly working with DoConstrainedMD set >0.
75 */
76 void operator()(const int NextStep, const size_t offset)
77 {
78 // make sum of forces equal zero
79 AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(offset,NextStep);
80
81 // are we in initial step? Then set static entities
82 if (currentStep == 0) {
83 currentDeltat = AtomicForceManipulator<T>::Deltat;
84 currentStep = 1;
85 LOG(2, "DEBUG: Initial step, setting values, current step is #" << currentStep);
86 } else {
87 ++currentStep;
88 LOG(2, "DEBUG: current step is #" << currentStep);
89 }
90
91 // get nodes on either side of selected bond via BFS discovery
92// std::vector<atomId_t> atomids;
93// for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
94// iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
95// atomids.push_back((*iter)->getId());
96// }
97// ASSERT( atomids.size() == AtomicForceManipulator<T>::atoms.size(),
98// "ForceAnnealing() - could not gather all atomic ids?");
99 BoostGraphCreator BGcreator;
100 BGcreator.createFromRange(
101 AtomicForceManipulator<T>::atoms.begin(),
102 AtomicForceManipulator<T>::atoms.end(),
103 AtomicForceManipulator<T>::atoms.size(),
104 BreadthFirstSearchGatherer::AlwaysTruePredicate);
105 BreadthFirstSearchGatherer NodeGatherer(BGcreator);
106
107 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end
108 Vector maxComponents(zeroVec);
109 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
110 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
111 // atom's force vector gives steepest descent direction
112 const Vector oldPosition = (*iter)->getPositionAtStep(NextStep-2 >= 0 ? NextStep - 2 : 0);
113 const Vector currentPosition = (*iter)->getPosition();
114 const Vector oldGradient = (*iter)->getAtomicForceAtStep(NextStep-2 >= 0 ? NextStep - 2 : 0);
115 const Vector currentGradient = (*iter)->getAtomicForce();
116 LOG(4, "DEBUG: Force for atom " << **iter << " is " << currentGradient);
117
118 // we use Barzilai-Borwein update with position reversed to get descent
119 const Vector GradientDifference = (currentGradient - oldGradient);
120 const double stepwidth =
121 fabs((currentPosition - oldPosition).ScalarProduct(GradientDifference))/
122 GradientDifference.NormSquared();
123 Vector PositionUpdate = stepwidth * currentGradient;
124 if (fabs(stepwidth) < 1e-10) {
125 // dont' warn in first step, deltat usage normal
126 if (currentStep != 1)
127 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
128 PositionUpdate = currentDeltat * currentGradient;
129 }
130 LOG(3, "DEBUG: Update would be " << PositionUpdate);
131
132 // add update to central atom
133 const atomId_t atomid = (*iter)->getId();
134 if (GatheredUpdates.count(atomid)) {
135 GatheredUpdates[atomid] += PositionUpdate;
136 } else
137 GatheredUpdates.insert( std::make_pair(atomid, PositionUpdate) );
138
139 // We assume that a force is local, i.e. a bond is too short yet and hence
140 // the atom needs to be moved. However, all the adjacent (bound) atoms might
141 // already be at the perfect distance. If we just move the atom alone, we ruin
142 // all the other bonds. Hence, it would be sensible to move every atom found
143 // through the bond graph in the direction of the force as well by the same
144 // PositionUpdate. This is just what we are going to do.
145
146 /// get all nodes from bonds in the direction of the current force
147 const size_t max_distance = 4;
148 const double damping_factor = 0.9;
149
150 // remove edges facing in the wrong direction
151 std::vector<bond::ptr> removed_bonds;
152 const BondList& ListOfBonds = (*iter)->getListOfBonds();
153 for(BondList::const_iterator bonditer = ListOfBonds.begin();
154 bonditer != ListOfBonds.end(); ++bonditer) {
155 const bond &current_bond = *(*bonditer);
156 LOG(2, "DEBUG: Looking at bond " << current_bond);
157 Vector BondVector = (*iter)->getPosition();
158 BondVector -= ((*iter)->getId() == current_bond.rightatom->getId())
159 ? current_bond.rightatom->getPosition() : current_bond.leftatom->getPosition();
160 BondVector.Normalize();
161 if (BondVector.ScalarProduct(currentGradient) < 0) {
162 removed_bonds.push_back(*bonditer);
163#ifndef NDEBUG
164 const bool status =
165#endif
166 BGcreator.removeEdge(current_bond.leftatom->getId(), current_bond.rightatom->getId());
167 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?");
168 }
169 }
170 BoostGraphHelpers::Nodeset_t bondside_set = NodeGatherer((*iter)->getId(), max_distance);
171 const BreadthFirstSearchGatherer::distance_map_t &distance_map = NodeGatherer.getDistances();
172 std::sort(bondside_set.begin(), bondside_set.end());
173 // re-add those edges
174 for (std::vector<bond::ptr>::const_iterator bonditer = removed_bonds.begin();
175 bonditer != removed_bonds.end(); ++bonditer)
176 BGcreator.addEdge((*bonditer)->leftatom->getId(), (*bonditer)->rightatom->getId());
177
178 // apply PositionUpdate to all nodes in the bondside_set
179 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set.begin();
180 setiter != bondside_set.end(); ++setiter) {
181 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter
182 = distance_map.find(*setiter);
183 ASSERT( diter != distance_map.end(),
184 "ForceAnnealing() - could not find distance to an atom.");
185 const double factor = pow(damping_factor, diter->second);
186 if (GatheredUpdates.count((*setiter))) {
187 GatheredUpdates[(*setiter)] += factor*PositionUpdate;
188 } else {
189 GatheredUpdates.insert(
190 std::make_pair(
191 (*setiter),
192 factor*PositionUpdate) );
193 }
194 }
195
196 // extract largest components for showing progress of annealing
197 for(size_t i=0;i<NDIM;++i)
198 if (currentGradient[i] > maxComponents[i])
199 maxComponents[i] = currentGradient[i];
200
201 // are we in initial step? Then don't check against velocity
202 if ((currentStep > 1) && (!(*iter)->getAtomicVelocity().IsZero()))
203 // update with currentDelta tells us how the current gradient relates to
204 // the last one: If it has become larger, reduce currentDelta
205 if ((PositionUpdate.ScalarProduct((*iter)->getAtomicVelocity()) < 0)
206 && (currentDeltat > MinimumDeltat)) {
207 currentDeltat = .5*currentDeltat;
208 LOG(2, "DEBUG: Upgrade in other direction: " << PositionUpdate.NormSquared()
209 << " > " << (*iter)->getAtomicVelocity().NormSquared()
210 << ", decreasing deltat: " << currentDeltat);
211 PositionUpdate = currentDeltat * currentGradient;
212 }
213
214 // finally set new values
215 (*iter)->setPosition(currentPosition + PositionUpdate);
216 (*iter)->setAtomicVelocity(PositionUpdate);
217 //std::cout << "Id of atom is " << (*iter)->getId() << std::endl;
218// (*iter)->VelocityVerletUpdateU((*iter)->getId(), NextStep-1, Deltat, IsAngstroem);
219
220 // reset force vector for next step except on final one
221 if (currentStep != maxSteps)
222 (*iter)->setAtomicForce(zeroVec);
223 }
224
225 LOG(1, "STATUS: Largest remaining force components at step #"
226 << currentStep << " are " << maxComponents);
227
228 // are we in final step? Remember to reset static entities
229 if (currentStep == maxSteps) {
230 LOG(2, "DEBUG: Final step, resetting values");
231 reset();
232 }
233 }
234
235 /** Reset function to unset static entities and artificial velocities.
236 *
237 */
238 void reset()
239 {
240 currentDeltat = 0.;
241 currentStep = 0;
242
243 // reset (artifical) velocities
244 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
245 iter != AtomicForceManipulator<T>::atoms.end(); ++iter)
246 (*iter)->setAtomicVelocity(zeroVec);
247 }
248
249private:
250 //!> contains the current step in relation to maxsteps
251 static size_t currentStep;
252 //!> contains the maximum number of steps, determines initial and final step with currentStep
253 size_t maxSteps;
254 static double currentDeltat;
255 //!> minimum deltat for internal while loop (adaptive step width)
256 static double MinimumDeltat;
257};
258
259template <class T>
260double ForceAnnealing<T>::currentDeltat = 0.;
261template <class T>
262size_t ForceAnnealing<T>::currentStep = 0;
263template <class T>
264double ForceAnnealing<T>::MinimumDeltat = 1e-8;
265
266#endif /* FORCEANNEALING_HPP_ */
Note: See TracBrowser for help on using the repository browser.