Changeset 355915 for src/Dynamics
- Timestamp:
- Apr 10, 2018, 6:43:30 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:
- d51e62
- Parents:
- e77580
- git-author:
- Frederik Heber <frederik.heber@…> (05/31/17 08:20:55)
- git-committer:
- Frederik Heber <frederik.heber@…> (04/10/18 06:43:30)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Dynamics/ForceAnnealing.hpp
re77580 r355915 13 13 #include <config.h> 14 14 #endif 15 16 #include <algorithm> 17 #include <iterator> 18 19 #include <boost/bind.hpp> 15 20 16 21 #include "Atom/atom.hpp" … … 249 254 /// need to be made longer (then the force vector is facing the other 250 255 /// 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; 256 /// As a remedy we need to average the force on either end of the bond and 257 /// check whether each gradient points inwards out or outwards with respect 258 /// to the bond and then shift accordingly. 259 /// One more issue is that the projection onto the bond directions does not 260 /// recover the gradient but may be larger as the bond directions are a 261 /// generating system and not a basis (e.g. 3 bonds on a plane where 2 would 262 /// suffice to span the plane). To this end, we need to account for the 263 /// overestimation and obtain a weighting for each bond. 264 265 // gather weights 266 typedef std::deque<double> weights_t; 267 typedef std::map<atomId_t, weights_t > weights_per_atom_t; 268 std::vector<weights_per_atom_t> weights_per_atom(2); 269 for (size_t timestep = 0; timestep <= 1; ++timestep) { 270 const size_t CurrentStep = CurrentTimeStep-2*timestep >= 0 ? CurrentTimeStep - 2*timestep : 0; 271 LOG(2, "DEBUG: CurrentTimeStep is " << CurrentTimeStep 272 << ", timestep is " << timestep 273 << ", and CurrentStep is " << CurrentStep); 274 275 for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin(); 276 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 277 const atom &walker = *(*iter); 278 const Vector &walkerGradient = walker.getAtomicForceAtStep(CurrentStep); 279 280 if (walkerGradient.Norm() > MYEPSILON) { 281 282 // gather BondVector and projected gradient over all bonds 283 const BondList& ListOfBonds = walker.getListOfBondsAtStep(CurrentStep); 284 std::vector<double> projected_forces; 285 std::vector<Vector> BondVectors; 286 projected_forces.reserve(ListOfBonds.size()); 287 for(BondList::const_iterator bonditer = ListOfBonds.begin(); 288 bonditer != ListOfBonds.end(); ++bonditer) { 289 const bond::ptr ¤t_bond = *bonditer; 290 BondVectors.push_back( 291 current_bond->leftatom->getPositionAtStep(CurrentStep) 292 - current_bond->rightatom->getPositionAtStep(CurrentStep)); 293 Vector &BondVector = BondVectors.back(); 294 BondVector.Normalize(); 295 projected_forces.push_back( walkerGradient.ScalarProduct(BondVector) ); 296 } 297 298 // go through all bonds and check what magnitude is represented by the others 299 // i.e. sum of scalar products against other bonds 300 std::pair<weights_per_atom_t::iterator, bool> inserter = 301 weights_per_atom[timestep-1].insert( 302 std::make_pair(walker.getId(), weights_t()) ); 303 ASSERT( inserter.second, 304 "ForceAnnealing::operator() - weight map for atom "+toString(walker) 305 +" and time step "+toString(timestep-1)+" already filled?"); 306 weights_t &weights = inserter.first->second; 307 for (std::vector<Vector>::const_iterator iter = BondVectors.begin(); 308 iter != BondVectors.end(); ++iter) { 309 std::vector<double> scps; 310 std::transform( 311 BondVectors.begin(), BondVectors.end(), 312 std::back_inserter(scps), 313 boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1) 314 ); 315 const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.); 316 weights.push_back( 1./scp_sum ); 317 } 318 // for testing we check whether all weighted scalar products now come out as 1. 319 #ifndef NDEBUG 320 for (std::vector<Vector>::const_iterator iter = BondVectors.begin(); 321 iter != BondVectors.end(); ++iter) { 322 double scp_sum = 0.; 323 weights_t::const_iterator weightiter = weights.begin(); 324 for (std::vector<Vector>::const_iterator otheriter = BondVectors.begin(); 325 otheriter != BondVectors.end(); ++otheriter, ++weightiter) { 326 scp_sum += (*weightiter)*(*iter).ScalarProduct(*otheriter); 327 } 328 ASSERT( fabs(scp_sum - 1.) < MYEPSILON, 329 "ForceAnnealing::operator() - for BondVector "+toString(*iter) 330 +" we have weighted scalar product of "+toString(scp_sum)+" != 1."); 331 } 332 #endif 333 } else { 334 LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than " 335 << MYEPSILON << " for atom " << walker); 336 } 337 } 338 } 339 340 // step through each bond and shift the atoms 341 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 342 // for (size_t i = 0;i<bondvector.size();++i) { 343 // const atom* bondatom[2] = { 344 // bondvector[i]->leftatom, 345 // bondvector[i]->rightatom}; 346 // const double &bondforce = bondforces[i]; 347 // const double &oldbondforce = oldbondforces[i]; 348 // const double bondforcedifference = (bondforce - oldbondforce); 349 // Vector BondVector = (bondatom[0]->getPosition() - bondatom[1]->getPosition()); 350 // BondVector.Normalize(); 351 // double stepwidth = 0.; 352 // for (size_t n=0;n<2;++n) { 353 // const Vector oldPosition = bondatom[n]->getPositionAtStep(CurrentTimeStep-2 >= 0 ? CurrentTimeStep - 2 : 0); 354 // const Vector currentPosition = bondatom[n]->getPosition(); 355 // stepwidth += fabs((currentPosition - oldPosition).ScalarProduct(BondVector))/bondforcedifference; 356 // } 357 // stepwidth = stepwidth/2; 358 // Vector PositionUpdate = stepwidth * BondVector; 359 // if (fabs(stepwidth) < 1e-10) { 360 // // dont' warn in first step, deltat usage normal 361 // if (currentStep != 1) 362 // ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead."); 363 // PositionUpdate = currentDeltat * BondVector; 364 // } 365 // LOG(3, "DEBUG: Update would be " << PositionUpdate); 366 // 367 // // remove the edge 368 //#ifndef NDEBUG 369 // const bool status = 370 //#endif 371 // BGcreator.removeEdge(bondatom[0]->getId(), bondatom[1]->getId()); 372 // ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 373 // 374 // // gather nodes for either atom 375 // BoostGraphHelpers::Nodeset_t bondside_set[2]; 376 // BreadthFirstSearchGatherer::distance_map_t distance_map[2]; 377 // for (size_t n=0;n<2;++n) { 378 // bondside_set[n] = NodeGatherer(bondatom[n]->getId(), max_distance); 379 // distance_map[n] = NodeGatherer.getDistances(); 380 // std::sort(bondside_set[n].begin(), bondside_set[n].end()); 381 // } 382 // 383 // // re-add edge 384 // BGcreator.addEdge(bondatom[0]->getId(), bondatom[1]->getId()); 385 // 386 // // add PositionUpdate for all nodes in the bondside_set 387 // for (size_t n=0;n<2;++n) { 388 // for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[n].begin(); 389 // setiter != bondside_set[n].end(); ++setiter) { 390 // const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter 391 // = distance_map[n].find(*setiter); 392 // ASSERT( diter != distance_map[n].end(), 393 // "ForceAnnealing() - could not find distance to an atom."); 394 // const double factor = pow(damping_factor, diter->second); 395 // LOG(3, "DEBUG: Update for atom #" << *setiter << " will be " 396 // << factor << "*" << PositionUpdate); 397 // if (GatheredUpdates.count((*setiter))) { 398 // GatheredUpdates[(*setiter)] += factor*PositionUpdate; 399 // } else { 400 // GatheredUpdates.insert( 401 // std::make_pair( 402 // (*setiter), 403 // factor*PositionUpdate) ); 404 // } 405 // } 406 // // invert for other atom 407 // PositionUpdate *= -1; 408 // } 409 // } 410 // delete[] bondforces; 411 // delete[] oldbondforces; 412 262 413 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 263 414 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 264 const atom &walker = *(*iter); 265 allatomids.push_back(walker.getId()); 266 const BondList& ListOfBonds = walker.getListOfBonds(); 267 for(BondList::const_iterator bonditer = ListOfBonds.begin(); 268 bonditer != ListOfBonds.end(); ++bonditer) { 269 const bond::ptr ¤t_bond = *bonditer; 270 allbonds.insert(current_bond); 271 } 272 415 atom &walker = *(*iter); 273 416 // extract largest components for showing progress of annealing 274 const Vector currentGradient = (*iter)->getAtomicForce();417 const Vector currentGradient = walker.getAtomicForce(); 275 418 for(size_t i=0;i<NDIM;++i) 276 419 maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i])); … … 278 421 // reset force vector for next step except on final one 279 422 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 } 423 walker.setAtomicForce(zeroVec); 406 424 } 407 425
Note:
See TracChangeset
for help on using the changeset viewer.