Changeset ab7bfa6 for src/Dynamics
- Timestamp:
- Jul 20, 2017, 9:38:37 AM (8 years ago)
- Branches:
- ForceAnnealing_with_BondGraph_continued
- Children:
- 57092a
- Parents:
- 539845
- git-author:
- Frederik Heber <frederik.heber@…> (05/30/17 20:31:38)
- git-committer:
- Frederik Heber <frederik.heber@…> (07/20/17 09:38:37)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Dynamics/ForceAnnealing.hpp
r539845 rab7bfa6 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" … … 115 118 BreadthFirstSearchGatherer NodeGatherer(BGcreator); 116 119 117 std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end 120 /// We assume that a force is local, i.e. a bond is too short yet and hence 121 /// the atom needs to be moved. However, all the adjacent (bound) atoms might 122 /// already be at the perfect distance. If we just move the atom alone, we ruin 123 /// all the other bonds. Hence, it would be sensible to move every atom found 124 /// through the bond graph in the direction of the force as well by the same 125 /// PositionUpdate. This is almost what we are going to do. 126 127 /// One more issue is: If we need to shorten bond, then we use the PositionUpdate 128 /// also on the the other bond partner already. This is because it is in the 129 /// direction of the bond. Therefore, the update is actually performed twice on 130 /// each bond partner, i.e. the step size is twice as large as it should be. 131 /// This problem only occurs when bonds need to be shortened, not when they 132 /// need to be made longer (then the force vector is facing the other 133 /// 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; 118 145 Vector maxComponents(zeroVec); 119 146 for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin(); 120 147 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); 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 157 // extract largest components for showing progress of annealing 125 158 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; 159 for(size_t i=0;i<NDIM;++i) 160 if (currentGradient[i] > maxComponents[i]) 161 maxComponents[i] = currentGradient[i]; 162 163 // reset force vector for next step except on final one 164 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; 134 241 if (fabs(stepwidth) < 1e-10) { 135 242 // dont' warn in first step, deltat usage normal 136 243 if (currentStep != 1) 137 244 ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead."); 138 PositionUpdate = currentDeltat * currentGradient;245 PositionUpdate = currentDeltat * BondVector; 139 246 } 140 247 LOG(3, "DEBUG: Update would be " << PositionUpdate); 141 248 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 ¤t_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); 249 // remove the edge 171 250 #ifndef NDEBUG 172 251 const bool status = 173 252 #endif 174 BGcreator.removeEdge(current_bond.leftatom->getId(), current_bond.rightatom->getId()); 175 ASSERT( status, "ForceAnnealing() - edge to found bond is not present?"); 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 } 176 287 } 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 } 288 // invert for other atom 289 PositionUpdate *= -1; 290 } 291 } 292 215 293 // apply the gathered updates 216 294 for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
Note:
See TracChangeset
for help on using the changeset viewer.