Changeset 57092a for src/Dynamics
- Timestamp:
- Jul 20, 2017, 9:38:37 AM (8 years ago)
- Branches:
- ForceAnnealing_with_BondGraph_continued
- Children:
- cc3964
- Parents:
- ab7bfa6
- git-author:
- Frederik Heber <frederik.heber@…> (05/31/17 08:20:55)
- git-committer:
- Frederik Heber <frederik.heber@…> (07/20/17 09:38:37)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Dynamics/ForceAnnealing.hpp
rab7bfa6 r57092a 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" … … 132 137 /// need to be made longer (then the force vector is facing the other 133 138 /// direction than the bond vector). 134 /// As a remedy we need to know the forces "per bond" and not per atom. 135 /// In effect, the gradient is the error per atom. However, we need an 136 /// error per bond. To this end, we set up a matrix A that translate the 137 /// vector of bond energies into a vector of atomic force component and 138 /// then we simply solve the linear system (inverse problem) via an SVD 139 /// and use the bond gradients to get the PositionUpdate for both bond 140 /// partners at the same time when we go through all bonds. 141 142 // gather/enumerate all bonds 143 std::set<bond::ptr> allbonds; 144 std::vector<atomId_t> allatomids; 139 /// As a remedy we need to average the force on either end of the bond and 140 /// check whether each gradient points inwards out or outwards with respect 141 /// to the bond and then shift accordingly. 142 /// One more issue is that the projection onto the bond directions does not 143 /// recover the gradient but may be larger as the bond directions are a 144 /// generating system and not a basis (e.g. 3 bonds on a plane where 2 would 145 /// suffice to span the plane). To this end, we need to account for the 146 /// overestimation and obtain a weighting for each bond. 147 148 // gather weights 149 typedef std::deque<double> weights_t; 150 typedef std::map<atomId_t, weights_t > weights_per_atom_t; 151 std::vector<weights_per_atom_t> weights_per_atom(2); 152 for (size_t timestep = 1; timestep <= 2; ++timestep) { 153 const size_t CurrentStep = NextStep-timestep >= 0 ? NextStep - timestep : 0; 154 for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin(); 155 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 156 const atom &walker = *(*iter); 157 const Vector &walkerGradient = walker.getAtomicForceAtStep(CurrentStep); 158 159 if (walkerGradient.Norm() > MYEPSILON) { 160 161 // gather BondVector and projected gradient over all bonds 162 const BondList& ListOfBonds = walker.getListOfBondsAtStep(CurrentStep); 163 std::vector<double> projected_forces; 164 std::vector<Vector> BondVectors; 165 projected_forces.reserve(ListOfBonds.size()); 166 for(BondList::const_iterator bonditer = ListOfBonds.begin(); 167 bonditer != ListOfBonds.end(); ++bonditer) { 168 const bond::ptr ¤t_bond = *bonditer; 169 BondVectors.push_back( 170 current_bond->leftatom->getPositionAtStep(CurrentStep) 171 - current_bond->rightatom->getPositionAtStep(CurrentStep)); 172 Vector &BondVector = BondVectors.back(); 173 BondVector.Normalize(); 174 projected_forces.push_back( walkerGradient.ScalarProduct(BondVector) ); 175 } 176 177 // go through all bonds and check what magnitude is represented by the others 178 // i.e. sum of scalar products against other bonds 179 std::pair<weights_per_atom_t::iterator, bool> inserter = 180 weights_per_atom[timestep-1].insert( 181 std::make_pair(walker.getId(), weights_t()) ); 182 ASSERT( inserter.second, 183 "ForceAnnealing::operator() - weight map for atom "+toString(walker) 184 +" and time step "+toString(timestep-1)+" already filled?"); 185 weights_t &weights = inserter.first->second; 186 for (std::vector<Vector>::const_iterator iter = BondVectors.begin(); 187 iter != BondVectors.end(); ++iter) { 188 std::vector<double> scps; 189 std::transform( 190 BondVectors.begin(), BondVectors.end(), 191 std::back_inserter(scps), 192 boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1) 193 ); 194 const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.); 195 weights.push_back( 1./scp_sum ); 196 } 197 // for testing we check whether all weighted scalar products now come out as 1. 198 #ifndef NDEBUG 199 for (std::vector<Vector>::const_iterator iter = BondVectors.begin(); 200 iter != BondVectors.end(); ++iter) { 201 double scp_sum = 0.; 202 weights_t::const_iterator weightiter = weights.begin(); 203 for (std::vector<Vector>::const_iterator otheriter = BondVectors.begin(); 204 otheriter != BondVectors.end(); ++otheriter, ++weightiter) { 205 scp_sum += (*weightiter)*(*iter).ScalarProduct(*otheriter); 206 } 207 ASSERT( fabs(scp_sum - 1.) < MYEPSILON, 208 "ForceAnnealing::operator() - for BondVector "+toString(*iter) 209 +" we have weighted scalar product of "+toString(scp_sum)+" != 1."); 210 } 211 #endif 212 } else { 213 LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than " 214 << MYEPSILON << " for atom " << walker); 215 } 216 } 217 } 218 219 // step through each bond and shift the atoms 220 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 221 // for (size_t i = 0;i<bondvector.size();++i) { 222 // const atom* bondatom[2] = { 223 // bondvector[i]->leftatom, 224 // bondvector[i]->rightatom}; 225 // const double &bondforce = bondforces[i]; 226 // const double &oldbondforce = oldbondforces[i]; 227 // const double bondforcedifference = (bondforce - oldbondforce); 228 // Vector BondVector = (bondatom[0]->getPosition() - bondatom[1]->getPosition()); 229 // BondVector.Normalize(); 230 // double stepwidth = 0.; 231 // for (size_t n=0;n<2;++n) { 232 // const Vector oldPosition = bondatom[n]->getPositionAtStep(NextStep-2 >= 0 ? NextStep - 2 : 0); 233 // const Vector currentPosition = bondatom[n]->getPosition(); 234 // stepwidth += fabs((currentPosition - oldPosition).ScalarProduct(BondVector))/bondforcedifference; 235 // } 236 // stepwidth = stepwidth/2; 237 // Vector PositionUpdate = stepwidth * BondVector; 238 // if (fabs(stepwidth) < 1e-10) { 239 // // dont' warn in first step, deltat usage normal 240 // if (currentStep != 1) 241 // ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead."); 242 // PositionUpdate = currentDeltat * BondVector; 243 // } 244 // LOG(3, "DEBUG: Update would be " << PositionUpdate); 245 // 246 // // remove the edge 247 //#ifndef NDEBUG 248 // const bool status = 249 //#endif 250 // BGcreator.removeEdge(bondatom[0]->getId(), bondatom[1]->getId()); 251 // ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 252 // 253 // // gather nodes for either atom 254 // BoostGraphHelpers::Nodeset_t bondside_set[2]; 255 // BreadthFirstSearchGatherer::distance_map_t distance_map[2]; 256 // for (size_t n=0;n<2;++n) { 257 // bondside_set[n] = NodeGatherer(bondatom[n]->getId(), max_distance); 258 // distance_map[n] = NodeGatherer.getDistances(); 259 // std::sort(bondside_set[n].begin(), bondside_set[n].end()); 260 // } 261 // 262 // // re-add edge 263 // BGcreator.addEdge(bondatom[0]->getId(), bondatom[1]->getId()); 264 // 265 // // add PositionUpdate for all nodes in the bondside_set 266 // for (size_t n=0;n<2;++n) { 267 // for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[n].begin(); 268 // setiter != bondside_set[n].end(); ++setiter) { 269 // const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter 270 // = distance_map[n].find(*setiter); 271 // ASSERT( diter != distance_map[n].end(), 272 // "ForceAnnealing() - could not find distance to an atom."); 273 // const double factor = pow(damping_factor, diter->second); 274 // LOG(3, "DEBUG: Update for atom #" << *setiter << " will be " 275 // << factor << "*" << PositionUpdate); 276 // if (GatheredUpdates.count((*setiter))) { 277 // GatheredUpdates[(*setiter)] += factor*PositionUpdate; 278 // } else { 279 // GatheredUpdates.insert( 280 // std::make_pair( 281 // (*setiter), 282 // factor*PositionUpdate) ); 283 // } 284 // } 285 // // invert for other atom 286 // PositionUpdate *= -1; 287 // } 288 // } 289 // delete[] bondforces; 290 // delete[] oldbondforces; 291 145 292 Vector maxComponents(zeroVec); 146 293 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 147 294 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 148 const atom &walker = *(*iter); 149 allatomids.push_back(walker.getId()); 150 const BondList& ListOfBonds = walker.getListOfBonds(); 151 for(BondList::const_iterator bonditer = ListOfBonds.begin(); 152 bonditer != ListOfBonds.end(); ++bonditer) { 153 const bond::ptr ¤t_bond = *bonditer; 154 allbonds.insert(current_bond); 155 } 156 295 atom &walker = *(*iter); 157 296 // extract largest components for showing progress of annealing 158 const Vector currentGradient = (*iter)->getAtomicForce();297 const Vector currentGradient = walker.getAtomicForce(); 159 298 for(size_t i=0;i<NDIM;++i) 160 299 if (currentGradient[i] > maxComponents[i]) … … 163 302 // reset force vector for next step except on final one 164 303 if (currentStep != maxSteps) 165 (*iter)->setAtomicForce(zeroVec); 166 } 167 std::sort(allatomids.begin(), allatomids.end()); 168 // converting set back to vector is fastest when requiring sorted and unique, 169 // see https://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector 170 typedef std::vector<bond::ptr> bondvector_t; 171 bondvector_t bondvector( allbonds.begin(), allbonds.end() ); 172 173 // setup matrix A given the enumerated bonds 174 LinearSystemOfEquations lseq(AtomicForceManipulator<T>::atoms.size(), bondvector.size()); 175 for (size_t i = 0;i<bondvector.size();++i) { 176 const atom* bondatom[2] = { 177 bondvector[i]->leftatom, 178 bondvector[i]->rightatom 179 }; 180 size_t index[2]; 181 for (size_t n=0;n<2;++n) { 182 const std::pair<std::vector<atomId_t>::iterator, std::vector<atomId_t>::iterator> atomiditer = 183 std::equal_range(allatomids.begin(), allatomids.end(), bondatom[n]->getId()); 184 index[n] = std::distance(allatomids.begin(), atomiditer.first); 185 (*lseq.A).at(index[0],index[1]) = 1.; 186 (*lseq.A).at(index[1],index[0]) = 1.; 187 } 188 } 189 lseq.SetSymmetric(true); 190 191 // for each component and for current and last time step 192 // solve Ax=y with given A and y being the vectorized atomic force 193 double *tmpforces = new double[bondvector.size()]; 194 double *bondforces = new double[bondvector.size()]; 195 double *oldbondforces = new double[bondvector.size()]; 196 double *bondforceref[2] = { 197 bondforces, 198 oldbondforces 199 }; 200 for (size_t n=0;n<bondvector.size();++n) { 201 bondforces[n] = 0.; 202 oldbondforces[n] = 0.; 203 } 204 for (size_t timestep = 0; timestep <= 1; ++timestep) { 205 for (size_t component = 0; component < NDIM; ++component) { 206 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 207 iter != AtomicForceManipulator<T>::atoms.end(); ++iter) { 208 const atom &walker = *(*iter); 209 const std::pair<std::vector<atomId_t>::iterator, std::vector<atomId_t>::iterator> atomiditer = 210 std::equal_range(allatomids.begin(), allatomids.end(), walker.getId()); 211 const size_t i = std::distance(allatomids.begin(), atomiditer.first); 212 (*lseq.b).at(i) = timestep == 0 ? 213 walker.getAtomicForce()[component] : 214 walker.getAtomicForceAtStep(NextStep-2 >= 0 ? NextStep - 2 : 0)[component]; 215 } 216 lseq.GetSolutionAsArray(tmpforces); 217 for (size_t i = 0;i<bondvector.size();++i) 218 bondforceref[timestep][i] += tmpforces[i]; 219 } 220 } 221 222 // step through each bond and shift the atoms 223 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 224 for (size_t i = 0;i<bondvector.size();++i) { 225 const atom* bondatom[2] = { 226 bondvector[i]->leftatom, 227 bondvector[i]->rightatom}; 228 const double &bondforce = bondforces[i]; 229 const double &oldbondforce = oldbondforces[i]; 230 const double bondforcedifference = (bondforce - oldbondforce); 231 Vector BondVector = (bondatom[0]->getPosition() - bondatom[1]->getPosition()); 232 BondVector.Normalize(); 233 double stepwidth = 0.; 234 for (size_t n=0;n<2;++n) { 235 const Vector oldPosition = bondatom[n]->getPositionAtStep(NextStep-2 >= 0 ? NextStep - 2 : 0); 236 const Vector currentPosition = bondatom[n]->getPosition(); 237 stepwidth += fabs((currentPosition - oldPosition).ScalarProduct(BondVector))/bondforcedifference; 238 } 239 stepwidth = stepwidth/2; 240 Vector PositionUpdate = stepwidth * BondVector; 241 if (fabs(stepwidth) < 1e-10) { 242 // dont' warn in first step, deltat usage normal 243 if (currentStep != 1) 244 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead."); 245 PositionUpdate = currentDeltat * BondVector; 246 } 247 LOG(3, "DEBUG: Update would be " << PositionUpdate); 248 249 // remove the edge 250 #ifndef NDEBUG 251 const bool status = 252 #endif 253 BGcreator.removeEdge(bondatom[0]->getId(), bondatom[1]->getId()); 254 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 255 256 // gather nodes for either atom 257 BoostGraphHelpers::Nodeset_t bondside_set[2]; 258 BreadthFirstSearchGatherer::distance_map_t distance_map[2]; 259 for (size_t n=0;n<2;++n) { 260 bondside_set[n] = NodeGatherer(bondatom[n]->getId(), max_distance); 261 distance_map[n] = NodeGatherer.getDistances(); 262 std::sort(bondside_set[n].begin(), bondside_set[n].end()); 263 } 264 265 // re-add edge 266 BGcreator.addEdge(bondatom[0]->getId(), bondatom[1]->getId()); 267 268 // add PositionUpdate for all nodes in the bondside_set 269 for (size_t n=0;n<2;++n) { 270 for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[n].begin(); 271 setiter != bondside_set[n].end(); ++setiter) { 272 const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter 273 = distance_map[n].find(*setiter); 274 ASSERT( diter != distance_map[n].end(), 275 "ForceAnnealing() - could not find distance to an atom."); 276 const double factor = pow(damping_factor, diter->second); 277 LOG(3, "DEBUG: Update for atom #" << *setiter << " will be " 278 << factor << "*" << PositionUpdate); 279 if (GatheredUpdates.count((*setiter))) { 280 GatheredUpdates[(*setiter)] += factor*PositionUpdate; 281 } else { 282 GatheredUpdates.insert( 283 std::make_pair( 284 (*setiter), 285 factor*PositionUpdate) ); 286 } 287 } 288 // invert for other atom 289 PositionUpdate *= -1; 290 } 304 walker.setAtomicForce(zeroVec); 291 305 } 292 306
Note:
See TracChangeset
for help on using the changeset viewer.