Changeset ab7bfa6 for src/Dynamics


Ignore:
Timestamp:
Jul 20, 2017, 9:38:37 AM (8 years ago)
Author:
Frederik Heber <frederik.heber@…>
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)
Message:

ForceAnnealing converts atomic forces to bond forces and optimize these.

  • the initial idea was using the bond graph (i.e. pushing also the adjacent atoms in order not to ruin the distance to them). However, this falls short when bonds need to be shortened - we update each bond partner twice.
  • Therefore, we convert the atomic force to a force per bond via an SVD and shift then both bond partners (and adjacent neighbors) at the same time.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    r539845 rab7bfa6  
    2828#include "Helpers/helpers.hpp"
    2929#include "Helpers/defs.hpp"
     30#include "LinearAlgebra/LinearSystemOfEquations.hpp"
     31#include "LinearAlgebra/MatrixContent.hpp"
    3032#include "LinearAlgebra/Vector.hpp"
     33#include "LinearAlgebra/VectorContent.hpp"
    3134#include "Thermostats/ThermoStatContainer.hpp"
    3235#include "Thermostats/Thermostat.hpp"
     
    115118    BreadthFirstSearchGatherer NodeGatherer(BGcreator);
    116119
    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;
    118145    Vector maxComponents(zeroVec);
    119146    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    120147        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 &current_bond = *bonditer;
     154        allbonds.insert(current_bond);
     155      }
     156
     157      // extract largest components for showing progress of annealing
    125158      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;
    134241      if (fabs(stepwidth) < 1e-10) {
    135242        // dont' warn in first step, deltat usage normal
    136243        if (currentStep != 1)
    137244          ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
    138         PositionUpdate = currentDeltat * currentGradient;
     245        PositionUpdate = currentDeltat * BondVector;
    139246      }
    140247      LOG(3, "DEBUG: Update would be " << PositionUpdate);
    141248
    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);
     249      // remove the edge
    171250#ifndef NDEBUG
    172           const bool status =
     251      const bool status =
    173252#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          }
    176287        }
    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
    215293    // apply the gathered updates
    216294    for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
Note: See TracChangeset for help on using the changeset viewer.