Changeset 77d0cd 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:
6458e7
Parents:
d51e62
git-author:
Frederik Heber <frederik.heber@…> (06/13/17 22:46:53)
git-committer:
Frederik Heber <frederik.heber@…> (04/10/18 06:43:30)
Message:

ForceAnnealing now uses BondVectors to optimize the structure from the perspective of the bond.

  • we calculate the atomic force projected onto each bond (including weights because the bond vectors form a generating system not a basis).
  • then we go through each bond and shift its leftatom and rightatom including its BFS neighbors by a PositionUpdate.
  • Gradient is printed with magnitude.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    rd51e62 r77d0cd  
    1515
    1616#include <algorithm>
     17#include <functional>
    1718#include <iterator>
    1819
     
    2728#include "Descriptors/AtomIdDescriptor.hpp"
    2829#include "Dynamics/AtomicForceManipulator.hpp"
     30#include "Dynamics/BondVectors.hpp"
    2931#include "Fragmentation/ForceMatrix.hpp"
    3032#include "Graph/BoostGraphCreator.hpp"
     
    100102  {
    101103    // make sum of forces equal zero
    102     AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(_offset, _CurrentTimeStep);
     104    AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(
     105        _offset,
     106        _CurrentTimeStep-1>=0 ? _CurrentTimeStep - 1 : 0);
    103107
    104108    // are we in initial step? Then set static entities
     
    225229  {
    226230    // get nodes on either side of selected bond via BFS discovery
    227 //    std::vector<atomId_t> atomids;
    228 //    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    229 //        iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
    230 //      atomids.push_back((*iter)->getId());
    231 //    }
    232 //    ASSERT( atomids.size() == AtomicForceManipulator<T>::atoms.size(),
    233 //        "ForceAnnealing() - could not gather all atomic ids?");
    234231    BoostGraphCreator BGcreator;
    235232    BGcreator.createFromRange(
     
    247244    /// PositionUpdate. This is almost what we are going to do.
    248245
    249     /// One more issue is: If we need to shorten bond, then we use the PositionUpdate
     246    /// One issue is: If we need to shorten bond, then we use the PositionUpdate
    250247    /// also on the the other bond partner already. This is because it is in the
    251248    /// direction of the bond. Therefore, the update is actually performed twice on
     
    257254    /// check whether each gradient points inwards out or outwards with respect
    258255    /// to the bond and then shift accordingly.
     256
    259257    /// One more issue is that the projection onto the bond directions does not
    260258    /// recover the gradient but may be larger as the bond directions are a
     
    263261    /// overestimation and obtain a weighting for each bond.
    264262
    265     // gather weights
     263    // initialize helper class for bond vectors using bonds from range of atoms
     264    BondVectors bv;
     265    bv.setFromAtomRange< T >(
     266        AtomicForceManipulator<T>::atoms.begin(),
     267        AtomicForceManipulator<T>::atoms.end(),
     268        CurrentTimeStep);
     269    const BondVectors::container_t &sorted_bonds = bv.getSorted();
     270
     271    // knowing the number of bonds in total, we can setup the storage for the
     272    // projected forces
     273    enum whichatom_t {
     274      leftside=0,
     275      rightside=1,
     276      MAX_sides
     277    };
     278    std::vector< // time step
     279      std::vector< // which bond side
     280        std::vector<double> > // over all bonds
     281          > projected_forces(2); // one for leftatoms, one for rightatoms (and for both time steps)
     282    for (size_t i=0;i<2;++i) {
     283      projected_forces[i].resize(MAX_sides);
     284      for (size_t j=0;j<MAX_sides;++j)
     285        projected_forces[i][j].resize(sorted_bonds.size(), 0.);
     286    }
     287
     288    // for each atom we need to gather weights and then project the gradient
    266289    typedef std::deque<double> weights_t;
    267290    typedef std::map<atomId_t, weights_t > weights_per_atom_t;
    268291    std::vector<weights_per_atom_t> weights_per_atom(2);
    269292    for (size_t timestep = 0; timestep <= 1; ++timestep) {
    270       const size_t CurrentStep = CurrentTimeStep-2*timestep >= 0 ? CurrentTimeStep - 2*timestep : 0;
     293      const size_t CurrentStep = CurrentTimeStep-timestep-1 >= 0 ? CurrentTimeStep-timestep-1 : 0;
    271294      LOG(2, "DEBUG: CurrentTimeStep is " << CurrentTimeStep
    272295          << ", timestep is " << timestep
    273296          << ", and CurrentStep is " << CurrentStep);
     297
     298      // get all bond vectors for this time step (from the perspective of the
     299      // bonds taken from the currentStep)
     300      const BondVectors::mapped_t bondvectors = bv.getBondVectorsAtStep(CurrentStep);
    274301
    275302      for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin();
     
    277304        const atom &walker = *(*iter);
    278305        const Vector &walkerGradient = walker.getAtomicForceAtStep(CurrentStep);
    279 
     306        LOG(3, "DEBUG: Gradient of atom #" << walker.getId() << ", namely "
     307            << walker << " is " << walkerGradient << " with magnitude of "
     308            << walkerGradient.Norm());
     309
     310        const BondList& ListOfBonds = walker.getListOfBonds();
    280311        if (walkerGradient.Norm() > MYEPSILON) {
    281312
    282           // gather BondVector and projected gradient over all bonds
    283           const BondList& ListOfBonds = walker.getListOfBondsAtStep(CurrentStep);
    284           std::vector<double> projected_forces;
     313          // gather subset of BondVectors for the current atom
    285314          std::vector<Vector> BondVectors;
    286           projected_forces.reserve(ListOfBonds.size());
    287315          for(BondList::const_iterator bonditer = ListOfBonds.begin();
    288316              bonditer != ListOfBonds.end(); ++bonditer) {
    289317            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) );
     318            const BondVectors::mapped_t::const_iterator bviter =
     319                bondvectors.find(current_bond);
     320            ASSERT( bviter != bondvectors.end(),
     321                "ForceAnnealing() - cannot find current_bond ?");
     322            ASSERT( bviter != bondvectors.end(),
     323                "ForceAnnealing - cannot find current bond "+toString(*current_bond)
     324                +" in bonds.");
     325            BondVectors.push_back(bviter->second);
    296326          }
    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
     327          LOG(4, "DEBUG: BondVectors for atom #" << walker.getId() << ": " << BondVectors);
     328
     329          // go through all its bonds and calculate what magnitude is represented
     330          // by the others i.e. sum of scalar products against other bonds
    300331          std::pair<weights_per_atom_t::iterator, bool> inserter =
    301               weights_per_atom[timestep-1].insert(
     332              weights_per_atom[timestep].insert(
    302333                  std::make_pair(walker.getId(), weights_t()) );
    303334          ASSERT( inserter.second,
    304335              "ForceAnnealing::operator() - weight map for atom "+toString(walker)
    305               +" and time step "+toString(timestep-1)+" already filled?");
     336              +" and time step "+toString(timestep)+" already filled?");
    306337          weights_t &weights = inserter.first->second;
    307338          for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    308339              iter != BondVectors.end(); ++iter) {
    309340            std::vector<double> scps;
     341            scps.reserve(BondVectors.size());
    310342            std::transform(
    311343                BondVectors.begin(), BondVectors.end(),
    312344                std::back_inserter(scps),
    313                 boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1)
     345                boost::bind(static_cast< double (*)(double) >(&fabs),
     346                    boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1))
    314347                );
    315348            const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
     349            ASSERT( (scp_sum-1.) > -MYEPSILON,
     350                "ForceAnnealing() - sum of weights must be equal or larger one but is "
     351                +toString(scp_sum));
    316352            weights.push_back( 1./scp_sum );
    317353          }
     354          LOG(4, "DEBUG: Weights for atom #" << walker.getId() << ": " << weights);
     355
    318356          // for testing we check whether all weighted scalar products now come out as 1.
    319357#ifndef NDEBUG
    320358          for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    321359              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             }
     360            std::vector<double> scps;
     361            scps.reserve(BondVectors.size());
     362            std::transform(
     363                BondVectors.begin(), BondVectors.end(),
     364                weights.begin(),
     365                std::back_inserter(scps),
     366                boost::bind(static_cast< double (*)(double) >(&fabs),
     367                    boost::bind(std::multiplies<double>(),
     368                        boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1),
     369                        _2))
     370                );
     371            const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
    328372            ASSERT( fabs(scp_sum - 1.) < MYEPSILON,
    329373                "ForceAnnealing::operator() - for BondVector "+toString(*iter)
     
    331375          }
    332376#endif
     377
     378          // projected gradient over all bonds and place in one of projected_forces
     379          // using the obtained weights
     380          {
     381            weights_t::const_iterator weightiter = weights.begin();
     382            std::vector<Vector>::const_iterator vectoriter = BondVectors.begin();
     383            Vector forcesum_check;
     384            for(BondList::const_iterator bonditer = ListOfBonds.begin();
     385                bonditer != ListOfBonds.end(); ++bonditer, ++weightiter, ++vectoriter) {
     386              const bond::ptr &current_bond = *bonditer;
     387              const Vector &BondVector = *vectoriter;
     388
     389              std::vector<double> &forcelist = (&walker == current_bond->leftatom) ?
     390                  projected_forces[timestep][leftside] : projected_forces[timestep][rightside];
     391              const size_t index = bv.getIndexForBond(current_bond);
     392              ASSERT( index != (size_t)-1,
     393                  "ForceAnnealing() - could not find bond "+toString(*current_bond)
     394                  +" in bondvectors");
     395              forcelist[index] = (*weightiter)*walkerGradient.ScalarProduct(BondVector);
     396              LOG(4, "DEBUG: BondVector " << BondVector << " receives projected force of "
     397                  << forcelist[index]);
     398              forcesum_check += forcelist[index] * BondVector;
     399            }
     400            ASSERT( weightiter == weights.end(),
     401                "ForceAnnealing - weightiter is not at end when it should be.");
     402            ASSERT( vectoriter == BondVectors.end(),
     403                "ForceAnnealing - vectoriter is not at end when it should be.");
     404            LOG(3, "DEBUG: sum of projected forces is " << forcesum_check);
     405          }
     406
    333407        } else {
    334408          LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than "
    335409              << MYEPSILON << " for atom " << walker);
     410          // note that projected_forces is initialized to full length and filled
     411          // with zeros. Hence, nothing to do here
    336412        }
    337413      }
     
    340416    // step through each bond and shift the atoms
    341417    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;
     418
     419    LOG(3, "DEBUG: current step is " << currentStep << ", given time step is " << CurrentTimeStep);
     420    const BondVectors::mapped_t bondvectors = bv.getBondVectorsAtStep(CurrentTimeStep);
     421
     422    for (BondVectors::container_t::const_iterator bondsiter = sorted_bonds.begin();
     423        bondsiter != sorted_bonds.end(); ++bondsiter) {
     424      const bond::ptr &current_bond = *bondsiter;
     425      const size_t index = bv.getIndexForBond(current_bond);
     426      const atom* bondatom[MAX_sides] = {
     427          current_bond->leftatom,
     428          current_bond->rightatom
     429      };
     430
     431      // remove the edge
     432#ifndef NDEBUG
     433      const bool status =
     434#endif
     435          BGcreator.removeEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
     436      ASSERT( status, "ForceAnnealing() - edge to found bond is not present?");
     437
     438      // gather nodes for either atom
     439      BoostGraphHelpers::Nodeset_t bondside_set[MAX_sides];
     440      BreadthFirstSearchGatherer::distance_map_t distance_map[MAX_sides];
     441      for (size_t side=leftside;side<MAX_sides;++side) {
     442        bondside_set[side] = NodeGatherer(bondatom[side]->getId(), max_distance);
     443        distance_map[side] = NodeGatherer.getDistances();
     444        std::sort(bondside_set[side].begin(), bondside_set[side].end());
     445      }
     446
     447      // re-add edge
     448      BGcreator.addEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
     449
     450      // do for both leftatom and rightatom of bond
     451      for (size_t side = leftside; side < MAX_sides; ++side) {
     452        const double &bondforce = projected_forces[0][side][index];
     453        const double &oldbondforce = projected_forces[1][side][index];
     454        const double bondforcedifference = (bondforce - oldbondforce);
     455        // if difference or bondforce itself is zero, do nothing
     456        if ((fabs(bondforce) < MYEPSILON) || (fabs(bondforcedifference) < MYEPSILON))
     457          continue;
     458        const BondVectors::mapped_t::const_iterator bviter =
     459            bondvectors.find(current_bond);
     460        ASSERT( bviter != bondvectors.end(),
     461            "ForceAnnealing() - cannot find current_bond ?");
     462        const Vector &BondVector = bviter->second;
     463
     464        // calculate gradient and position differences for stepwidth
     465        const Vector currentGradient = bondforce * BondVector;
     466        LOG(4, "DEBUG: current projected gradient for "
     467            << (side == leftside ? "left" : "right") << " side of bond is " << currentGradient);
     468        const Vector &oldPosition = bondatom[side]->getPositionAtStep(CurrentTimeStep-2 >= 0 ? CurrentTimeStep - 2 : 0);
     469        const Vector &currentPosition = bondatom[side]->getPositionAtStep(CurrentTimeStep-1>=0 ? CurrentTimeStep - 1 : 0);
     470        const Vector PositionDifference = currentPosition - oldPosition;
     471        LOG(4, "DEBUG: old position is " << oldPosition);
     472        LOG(4, "DEBUG: current position is " << currentPosition);
     473        LOG(4, "DEBUG: difference in position is " << PositionDifference);
     474        LOG(4, "DEBUG: bondvector is " << BondVector);
     475        const double projected_PositionDifference = PositionDifference.ScalarProduct(BondVector);
     476        LOG(4, "DEBUG: difference in position projected onto bondvector is "
     477            << projected_PositionDifference);
     478        LOG(4, "DEBUG: abs. difference in forces is " << bondforcedifference);
     479
     480        // calculate step width
     481        Vector PositionUpdate;
     482        double stepwidth =
     483            fabs(projected_PositionDifference)/bondforcedifference;
     484        if (fabs(stepwidth) < 1e-10) {
     485          // dont' warn in first step, deltat usage normal
     486          if (currentStep != 1)
     487            ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
     488          PositionUpdate = currentDeltat * BondVector;
     489        }
     490        LOG(3, "DEBUG: Update would be " << PositionUpdate);
     491
     492        // add PositionUpdate for all nodes in the bondside_set
     493        for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[side].begin();
     494            setiter != bondside_set[side].end(); ++setiter) {
     495          const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter
     496            = distance_map[side].find(*setiter);
     497          ASSERT( diter != distance_map[side].end(),
     498              "ForceAnnealing() - could not find distance to an atom.");
     499          const double factor = pow(damping_factor, diter->second);
     500          LOG(3, "DEBUG: Update for atom #" << *setiter << " will be "
     501              << factor << "*" << PositionUpdate);
     502          if (GatheredUpdates.count((*setiter))) {
     503            GatheredUpdates[(*setiter)] += factor*PositionUpdate;
     504          } else {
     505            GatheredUpdates.insert(
     506                std::make_pair(
     507                    (*setiter),
     508                    factor*PositionUpdate) );
     509          }
     510        }
     511      }
     512    }
    412513
    413514    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
     
    415516      atom &walker = *(*iter);
    416517      // extract largest components for showing progress of annealing
    417       const Vector currentGradient = walker.getAtomicForce();
     518      const Vector currentGradient = walker.getAtomicForceAtStep(CurrentTimeStep-1>=0 ? CurrentTimeStep-1 : 0);
    418519      for(size_t i=0;i<NDIM;++i)
    419520        maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
     
    434535      LOG(3, "DEBUG: Applying update " << update << " to atom #" << atomid
    435536          << ", namely " << *walker);
    436       walker->setPosition( walker->getPosition() + update );
     537      walker->setPosition(
     538          walker->getPositionAtStep(CurrentTimeStep-1>=0 ? CurrentTimeStep - 1 : 0)
     539          + update);
     540//      walker->setAtomicForce( RemnantGradient_per_atom[walker->getId()] );
    437541    }
    438542  }
Note: See TracChangeset for help on using the changeset viewer.