Changeset 57092a 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:
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)
Message:

ForceAnnealing now calculates force on either side of bond in direction of bond.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    rab7bfa6 r57092a  
    1313#include <config.h>
    1414#endif
     15
     16#include <algorithm>
     17#include <iterator>
     18
     19#include <boost/bind.hpp>
    1520
    1621#include "Atom/atom.hpp"
     
    132137    /// need to be made longer (then the force vector is facing the other
    133138    /// 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 &current_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
    145292    Vector maxComponents(zeroVec);
    146293    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    147294        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 &current_bond = *bonditer;
    154         allbonds.insert(current_bond);
    155       }
    156 
     295      atom &walker = *(*iter);
    157296      // extract largest components for showing progress of annealing
    158       const Vector currentGradient = (*iter)->getAtomicForce();
     297      const Vector currentGradient = walker.getAtomicForce();
    159298      for(size_t i=0;i<NDIM;++i)
    160299        if (currentGradient[i] > maxComponents[i])
     
    163302      // reset force vector for next step except on final one
    164303      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);
    291305    }
    292306
Note: See TracChangeset for help on using the changeset viewer.