Changeset 355915 for src/Dynamics


Ignore:
Timestamp:
Apr 10, 2018, 6:43:30 AM (7 years ago)
Author:
Frederik Heber <frederik.heber@…>
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)
Message:

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

  • With DoOutput() removed, i.e. always true, we need to look behind two steps.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    re77580 r355915  
    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"
     
    249254    /// need to be made longer (then the force vector is facing the other
    250255    /// 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 &current_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
    262413    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    263414        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 &current_bond = *bonditer;
    270         allbonds.insert(current_bond);
    271       }
    272 
     415      atom &walker = *(*iter);
    273416      // extract largest components for showing progress of annealing
    274       const Vector currentGradient = (*iter)->getAtomicForce();
     417      const Vector currentGradient = walker.getAtomicForce();
    275418      for(size_t i=0;i<NDIM;++i)
    276419        maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
     
    278421      // reset force vector for next step except on final one
    279422      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);
    406424    }
    407425
Note: See TracChangeset for help on using the changeset viewer.