Changeset e77580 for src/Dynamics
- Timestamp:
- Apr 10, 2018, 6:43:29 AM (7 years ago)
- Branches:
- AutomationFragmentation_failures, Candidate_v1.6.1, ChemicalSpaceEvaluator, Exclude_Hydrogens_annealWithBondGraph, ForceAnnealing_with_BondGraph, ForceAnnealing_with_BondGraph_contraction-expansion, Gui_displays_atomic_force_velocity, PythonUI_with_named_parameters, StoppableMakroAction, TremoloParser_IncreasedPrecision
- Children:
- 355915
- Parents:
- d3e1d5
- git-author:
- Frederik Heber <frederik.heber@…> (05/30/17 20:31:38)
- git-committer:
- Frederik Heber <frederik.heber@…> (04/10/18 06:43:29)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Dynamics/ForceAnnealing.hpp
rd3e1d5 re77580 28 28 #include "Helpers/helpers.hpp" 29 29 #include "Helpers/defs.hpp" 30 #include "LinearAlgebra/LinearSystemOfEquations.hpp" 31 #include "LinearAlgebra/MatrixContent.hpp" 30 32 #include "LinearAlgebra/Vector.hpp" 33 #include "LinearAlgebra/VectorContent.hpp" 31 34 #include "Thermostats/ThermoStatContainer.hpp" 32 35 #include "Thermostats/Thermostat.hpp" … … 232 235 BreadthFirstSearchGatherer NodeGatherer(BGcreator); 233 236 234 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 237 /// We assume that a force is local, i.e. a bond is too short yet and hence 238 /// the atom needs to be moved. However, all the adjacent (bound) atoms might 239 /// already be at the perfect distance. If we just move the atom alone, we ruin 240 /// all the other bonds. Hence, it would be sensible to move every atom found 241 /// through the bond graph in the direction of the force as well by the same 242 /// PositionUpdate. This is almost what we are going to do. 243 244 /// One more issue is: If we need to shorten bond, then we use the PositionUpdate 245 /// also on the the other bond partner already. This is because it is in the 246 /// direction of the bond. Therefore, the update is actually performed twice on 247 /// each bond partner, i.e. the step size is twice as large as it should be. 248 /// This problem only occurs when bonds need to be shortened, not when they 249 /// need to be made longer (then the force vector is facing the other 250 /// direction than the bond vector). 251 /// As a remedy we need to know the forces "per bond" and not per atom. 252 /// In effect, the gradient is the error per atom. However, we need an 253 /// error per bond. To this end, we set up a matrix A that translate the 254 /// vector of bond energies into a vector of atomic force component and 255 /// then we simply solve the linear system (inverse problem) via an SVD 256 /// and use the bond gradients to get the PositionUpdate for both bond 257 /// partners at the same time when we go through all bonds. 258 259 // gather/enumerate all bonds 260 std::set<bond::ptr> allbonds; 261 std::vector<atomId_t> allatomids; 235 262 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 236 263 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 237 // atom's force vector gives steepest descent direction 238 const Vector oldPosition = (*iter)->getPositionAtStep(CurrentTimeStep-1 >= 0 ? CurrentTimeStep - 1 : 0); 239 const Vector currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep); 240 const Vector oldGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep-1 >= 0 ? CurrentTimeStep - 1 : 0); 241 const Vector currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep); 242 LOG(4, "DEBUG: Force for atom " << **iter << " is " << currentGradient); 243 244 // we use Barzilai-Borwein update with position reversed to get descent 245 const double stepwidth = getBarzilaiBorweinStepwidth( 246 currentPosition - oldPosition, currentGradient - oldGradient); 247 const Vector PositionUpdate = stepwidth * currentGradient; 248 LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate); 249 250 // // add update to central atom 251 // const atomId_t atomid = (*iter)->getId(); 252 // if (GatheredUpdates.count(atomid)) { 253 // GatheredUpdates[atomid] += PositionUpdate; 254 // } else 255 // GatheredUpdates.insert( std::make_pair(atomid, PositionUpdate) ); 256 257 // We assume that a force is local, i.e. a bond is too short yet and hence 258 // the atom needs to be moved. However, all the adjacent (bound) atoms might 259 // already be at the perfect distance. If we just move the atom alone, we ruin 260 // all the other bonds. Hence, it would be sensible to move every atom found 261 // through the bond graph in the direction of the force as well by the same 262 // PositionUpdate. This is just what we are going to do. 263 264 /// get all nodes from bonds in the direction of the current force 265 266 // remove edges facing in the wrong direction 267 std::vector<bond::ptr> removed_bonds; 268 const BondList& ListOfBonds = (*iter)->getListOfBonds(); 264 const atom &walker = *(*iter); 265 allatomids.push_back(walker.getId()); 266 const BondList& ListOfBonds = walker.getListOfBonds(); 269 267 for(BondList::const_iterator bonditer = ListOfBonds.begin(); 270 268 bonditer != ListOfBonds.end(); ++bonditer) { 271 const bond ¤t_bond = *(*bonditer); 272 LOG(2, "DEBUG: Looking at bond " << current_bond); 273 Vector BondVector = (*iter)->getPositionAtStep(CurrentTimeStep); 274 BondVector -= ((*iter)->getId() == current_bond.rightatom->getId()) 275 ? current_bond.rightatom->getPositionAtStep(CurrentTimeStep) : current_bond.leftatom->getPositionAtStep(CurrentTimeStep); 276 BondVector.Normalize(); 277 if (BondVector.ScalarProduct(currentGradient) < 0) { 278 removed_bonds.push_back(*bonditer); 279 #ifndef NDEBUG 280 const bool status = 281 #endif 282 BGcreator.removeEdge(current_bond.leftatom->getId(), current_bond.rightatom->getId()); 283 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 284 } 285 } 286 BoostGraphHelpers::Nodeset_t bondside_set = NodeGatherer((*iter)->getId(), max_distance); 287 const BreadthFirstSearchGatherer::distance_map_t &distance_map = NodeGatherer.getDistances(); 288 std::sort(bondside_set.begin(), bondside_set.end()); 289 // re-add those edges 290 for (std::vector<bond::ptr>::const_iterator bonditer = removed_bonds.begin(); 291 bonditer != removed_bonds.end(); ++bonditer) 292 BGcreator.addEdge((*bonditer)->leftatom->getId(), (*bonditer)->rightatom->getId()); 293 294 // apply PositionUpdate to all nodes in the bondside_set 295 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set.begin(); 296 setiter != bondside_set.end(); ++setiter) { 297 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter 298 = distance_map.find(*setiter); 299 ASSERT( diter != distance_map.end(), 300 "ForceAnnealing() - could not find distance to an atom."); 301 const double factor = pow(damping_factor, diter->second); 302 LOG(3, "DEBUG: Update for atom #" << *setiter << " will be " 303 << factor << "*" << PositionUpdate); 304 if (GatheredUpdates.count((*setiter))) { 305 GatheredUpdates[(*setiter)] += factor*PositionUpdate; 306 } else { 307 GatheredUpdates.insert( 308 std::make_pair( 309 (*setiter), 310 factor*PositionUpdate) ); 311 } 269 const bond::ptr ¤t_bond = *bonditer; 270 allbonds.insert(current_bond); 312 271 } 313 272 314 273 // extract largest components for showing progress of annealing 274 const Vector currentGradient = (*iter)->getAtomicForce(); 315 275 for(size_t i=0;i<NDIM;++i) 316 276 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i])); 317 } 277 278 // reset force vector for next step except on final one 279 if (currentStep != maxSteps) 280 (*iter)->setAtomicForce(zeroVec); 281 } 282 std::sort(allatomids.begin(), allatomids.end()); 283 // converting set back to vector is fastest when requiring sorted and unique, 284 // see https://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector 285 typedef std::vector<bond::ptr> bondvector_t; 286 bondvector_t bondvector( allbonds.begin(), allbonds.end() ); 287 288 // setup matrix A given the enumerated bonds 289 LinearSystemOfEquations lseq(AtomicForceManipulator<T>::atoms.size(), bondvector.size()); 290 for (size_t i = 0;i<bondvector.size();++i) { 291 const atom* bondatom[2] = { 292 bondvector[i]->leftatom, 293 bondvector[i]->rightatom 294 }; 295 size_t index[2]; 296 for (size_t n=0;n<2;++n) { 297 const std::pair<std::vector<atomId_t>::iterator, std::vector<atomId_t>::iterator> atomiditer = 298 std::equal_range(allatomids.begin(), allatomids.end(), bondatom[n]->getId()); 299 index[n] = std::distance(allatomids.begin(), atomiditer.first); 300 (*lseq.A).at(index[0],index[1]) = 1.; 301 (*lseq.A).at(index[1],index[0]) = 1.; 302 } 303 } 304 lseq.SetSymmetric(true); 305 306 // for each component and for current and last time step 307 // solve Ax=y with given A and y being the vectorized atomic force 308 double *tmpforces = new double[bondvector.size()]; 309 double *bondforces = new double[bondvector.size()]; 310 double *oldbondforces = new double[bondvector.size()]; 311 double *bondforceref[2] = { 312 bondforces, 313 oldbondforces 314 }; 315 for (size_t n=0;n<bondvector.size();++n) { 316 bondforces[n] = 0.; 317 oldbondforces[n] = 0.; 318 } 319 for (size_t timestep = 0; timestep <= 1; ++timestep) { 320 for (size_t component = 0; component < NDIM; ++component) { 321 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 322 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 323 const atom &walker = *(*iter); 324 const std::pair<std::vector<atomId_t>::iterator, std::vector<atomId_t>::iterator> atomiditer = 325 std::equal_range(allatomids.begin(), allatomids.end(), walker.getId()); 326 const size_t i = std::distance(allatomids.begin(), atomiditer.first); 327 (*lseq.b).at(i) = timestep == 0 ? 328 walker.getAtomicForce()[component] : 329 walker.getAtomicForceAtStep(CurrentTimeStep-2 >= 0 ? CurrentTimeStep - 2 : 0)[component]; 330 } 331 lseq.GetSolutionAsArray(tmpforces); 332 for (size_t i = 0;i<bondvector.size();++i) 333 bondforceref[timestep][i] += tmpforces[i]; 334 } 335 } 336 337 // step through each bond and shift the atoms 338 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 339 for (size_t i = 0;i<bondvector.size();++i) { 340 const atom* bondatom[2] = { 341 bondvector[i]->leftatom, 342 bondvector[i]->rightatom}; 343 const double &bondforce = bondforces[i]; 344 const double &oldbondforce = oldbondforces[i]; 345 const double bondforcedifference = (bondforce - oldbondforce); 346 Vector BondVector = (bondatom[0]->getPosition() - bondatom[1]->getPosition()); 347 BondVector.Normalize(); 348 double stepwidth = 0.; 349 for (size_t n=0;n<2;++n) { 350 const Vector oldPosition = bondatom[n]->getPositionAtStep(CurrentTimeStep-2 >= 0 ? CurrentTimeStep - 2 : 0); 351 const Vector currentPosition = bondatom[n]->getPosition(); 352 stepwidth += fabs((currentPosition - oldPosition).ScalarProduct(BondVector))/bondforcedifference; 353 } 354 stepwidth = stepwidth/2; 355 Vector PositionUpdate = stepwidth * BondVector; 356 if (fabs(stepwidth) < 1e-10) { 357 // dont' warn in first step, deltat usage normal 358 if (currentStep != 1) 359 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead."); 360 PositionUpdate = currentDeltat * BondVector; 361 } 362 LOG(3, "DEBUG: Update would be " << PositionUpdate); 363 364 // remove the edge 365 #ifndef NDEBUG 366 const bool status = 367 #endif 368 BGcreator.removeEdge(bondatom[0]->getId(), bondatom[1]->getId()); 369 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 370 371 // gather nodes for either atom 372 BoostGraphHelpers::Nodeset_t bondside_set[2]; 373 BreadthFirstSearchGatherer::distance_map_t distance_map[2]; 374 for (size_t n=0;n<2;++n) { 375 bondside_set[n] = NodeGatherer(bondatom[n]->getId(), max_distance); 376 distance_map[n] = NodeGatherer.getDistances(); 377 std::sort(bondside_set[n].begin(), bondside_set[n].end()); 378 } 379 380 // re-add edge 381 BGcreator.addEdge(bondatom[0]->getId(), bondatom[1]->getId()); 382 383 // add PositionUpdate for all nodes in the bondside_set 384 for (size_t n=0;n<2;++n) { 385 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[n].begin(); 386 setiter != bondside_set[n].end(); ++setiter) { 387 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter 388 = distance_map[n].find(*setiter); 389 ASSERT( diter != distance_map[n].end(), 390 "ForceAnnealing() - could not find distance to an atom."); 391 const double factor = pow(damping_factor, diter->second); 392 LOG(3, "DEBUG: Update for atom #" << *setiter << " will be " 393 << factor << "*" << PositionUpdate); 394 if (GatheredUpdates.count((*setiter))) { 395 GatheredUpdates[(*setiter)] += factor*PositionUpdate; 396 } else { 397 GatheredUpdates.insert( 398 std::make_pair( 399 (*setiter), 400 factor*PositionUpdate) ); 401 } 402 } 403 // invert for other atom 404 PositionUpdate *= -1; 405 } 406 } 407 318 408 // apply the gathered updates 319 409 for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
Note:
See TracChangeset
for help on using the changeset viewer.