source: src/Dynamics/ForceAnnealing.hpp@ 0477b0

AutomationFragmentation_failures Candidate_v1.6.1 ChemicalSpaceEvaluator Enhanced_StructuralOptimization_continued Exclude_Hydrogens_annealWithBondGraph ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_contraction-expansion Gui_displays_atomic_force_velocity PythonUI_with_named_parameters StoppableMakroAction TremoloParser_IncreasedPrecision
Last change on this file since 0477b0 was 56b4c6, checked in by Frederik Heber <frederik.heber@…>, 7 years ago

Added damping-factor as another option to ForceAnnealing for proper testing of bond graph change effectiveness.

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