Changeset 4cd46b for src


Ignore:
Timestamp:
Jul 20, 2017, 9:38:37 AM (8 years ago)
Author:
Frederik Heber <frederik.heber@…>
Branches:
ForceAnnealing_with_BondGraph_continued
Children:
6d360a
Parents:
cc3964
git-author:
Frederik Heber <frederik.heber@…> (06/13/17 22:46:53)
git-committer:
Frederik Heber <frederik.heber@…> (07/20/17 09:38:37)
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.
  • FIX: ForceAnnealing's NextStep was confusingly named.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    rcc3964 r4cd46b  
    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"
     
    9294   * \todo This is not yet checked if it is correctly working with DoConstrainedMD set >0.
    9395   */
    94   void operator()(const int NextStep, const size_t offset)
     96  void operator()(const int CurrentTimeStep, const size_t offset)
    9597  {
    9698    // make sum of forces equal zero
    97     AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(offset,NextStep);
     99    AtomicForceManipulator<T>::correctForceMatrixForFixedCenterOfMass(offset,CurrentTimeStep);
    98100
    99101    // are we in initial step? Then set static entities
     
    108110
    109111    // get nodes on either side of selected bond via BFS discovery
    110 //    std::vector<atomId_t> atomids;
    111 //    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    112 //        iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
    113 //      atomids.push_back((*iter)->getId());
    114 //    }
    115 //    ASSERT( atomids.size() == AtomicForceManipulator<T>::atoms.size(),
    116 //        "ForceAnnealing() - could not gather all atomic ids?");
    117112    BoostGraphCreator BGcreator;
    118113    BGcreator.createFromRange(
     
    130125    /// PositionUpdate. This is almost what we are going to do.
    131126
    132     /// One more issue is: If we need to shorten bond, then we use the PositionUpdate
     127    /// One issue is: If we need to shorten bond, then we use the PositionUpdate
    133128    /// also on the the other bond partner already. This is because it is in the
    134129    /// direction of the bond. Therefore, the update is actually performed twice on
     
    140135    /// check whether each gradient points inwards out or outwards with respect
    141136    /// to the bond and then shift accordingly.
     137
    142138    /// One more issue is that the projection onto the bond directions does not
    143139    /// recover the gradient but may be larger as the bond directions are a
     
    146142    /// overestimation and obtain a weighting for each bond.
    147143
    148     // gather weights
     144    // initialize helper class for bond vectors using bonds from range of atoms
     145    BondVectors bv;
     146    bv.setFromAtomRange< T >(
     147        AtomicForceManipulator<T>::atoms.begin(),
     148        AtomicForceManipulator<T>::atoms.end(),
     149        currentStep);
     150    const BondVectors::container_t &sorted_bonds = bv.getSorted();
     151
     152    // knowing the number of bonds in total, we can setup the storage for the
     153    // projected forces
     154    enum whichatom_t {
     155      leftside=0,
     156      rightside=1,
     157      MAX_sides
     158    };
     159    std::vector< // time step
     160      std::vector< // which bond side
     161        std::vector<double> > // over all bonds
     162          > projected_forces(2); // one for leftatoms, one for rightatoms (and for both time steps)
     163    for (size_t i=0;i<2;++i) {
     164      projected_forces[i].resize(MAX_sides);
     165      for (size_t j=0;j<MAX_sides;++j)
     166        projected_forces[i][j].resize(sorted_bonds.size(), 0.);
     167    }
     168
     169    // for each atom we need to gather weights and then project the gradient
    149170    typedef std::deque<double> weights_t;
    150171    typedef std::map<atomId_t, weights_t > weights_per_atom_t;
    151172    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;
     173    for (size_t timestep = 0; timestep <= 1; ++timestep) {
     174      // TODO: We have this extra step in between because of DoOutput copying
     175      // change this making the code easier to understand
     176      const size_t CurrentStep = CurrentTimeStep-2*timestep >= 0 ? CurrentTimeStep - 2*timestep : 0;
     177
     178      // get all bond vectors for this time step (from the perspective of the
     179      // bonds taken from the currentStep)
     180      const BondVectors::mapped_t bondvectors = bv.getBondVectorsAtStep(CurrentStep);
     181
    154182      for(typename AtomSetMixin<T>::const_iterator iter = AtomicForceManipulator<T>::atoms.begin();
    155183          iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
    156184        const atom &walker = *(*iter);
    157185        const Vector &walkerGradient = walker.getAtomicForceAtStep(CurrentStep);
    158 
     186        LOG(3, "DEBUG: Gradient of atom #" << walker.getId() << ", namely "
     187            << walker << " is " << walkerGradient);
     188
     189        const BondList& ListOfBonds = walker.getListOfBonds();
    159190        if (walkerGradient.Norm() > MYEPSILON) {
    160191
    161           // gather BondVector and projected gradient over all bonds
    162           const BondList& ListOfBonds = walker.getListOfBondsAtStep(CurrentStep);
    163           std::vector<double> projected_forces;
     192          // gather subset of BondVectors for the current atom
    164193          std::vector<Vector> BondVectors;
    165           projected_forces.reserve(ListOfBonds.size());
    166194          for(BondList::const_iterator bonditer = ListOfBonds.begin();
    167195              bonditer != ListOfBonds.end(); ++bonditer) {
    168196            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) );
     197            const BondVectors::mapped_t::const_iterator bviter =
     198                bondvectors.find(current_bond);
     199            ASSERT( bviter != bondvectors.end(),
     200                "ForceAnnealing() - cannot find current_bond ?");
     201            ASSERT( bviter != bondvectors.end(),
     202                "ForceAnnealing - cannot find current bond "+toString(*current_bond)
     203                +" in bonds.");
     204            BondVectors.push_back(bviter->second);
    175205          }
    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
     206          LOG(4, "DEBUG: BondVectors for atom #" << walker.getId() << ": " << BondVectors);
     207
     208          // go through all its bonds and calculate what magnitude is represented
     209          // by the others i.e. sum of scalar products against other bonds
    179210          std::pair<weights_per_atom_t::iterator, bool> inserter =
    180               weights_per_atom[timestep-1].insert(
     211              weights_per_atom[timestep].insert(
    181212                  std::make_pair(walker.getId(), weights_t()) );
    182213          ASSERT( inserter.second,
    183214              "ForceAnnealing::operator() - weight map for atom "+toString(walker)
    184               +" and time step "+toString(timestep-1)+" already filled?");
     215              +" and time step "+toString(timestep)+" already filled?");
    185216          weights_t &weights = inserter.first->second;
    186217          for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    187218              iter != BondVectors.end(); ++iter) {
    188219            std::vector<double> scps;
     220            scps.reserve(BondVectors.size());
    189221            std::transform(
    190222                BondVectors.begin(), BondVectors.end(),
    191223                std::back_inserter(scps),
    192                 boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1)
     224                boost::bind(static_cast< double (*)(double) >(&fabs),
     225                    boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1))
    193226                );
    194227            const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
     228            ASSERT( (scp_sum-1.) > -MYEPSILON,
     229                "ForceAnnealing() - sum of weights must be equal or larger one but is "
     230                +toString(scp_sum));
    195231            weights.push_back( 1./scp_sum );
    196232          }
     233          LOG(4, "DEBUG: Weights for atom #" << walker.getId() << ": " << weights);
     234
    197235          // for testing we check whether all weighted scalar products now come out as 1.
    198236#ifndef NDEBUG
    199237          for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    200238              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             }
     239            std::vector<double> scps;
     240            scps.reserve(BondVectors.size());
     241            std::transform(
     242                BondVectors.begin(), BondVectors.end(),
     243                weights.begin(),
     244                std::back_inserter(scps),
     245                boost::bind(static_cast< double (*)(double) >(&fabs),
     246                    boost::bind(std::multiplies<double>(),
     247                        boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1),
     248                        _2))
     249                );
     250            const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
    207251            ASSERT( fabs(scp_sum - 1.) < MYEPSILON,
    208252                "ForceAnnealing::operator() - for BondVector "+toString(*iter)
     
    210254          }
    211255#endif
     256
     257          // projected gradient over all bonds and place in one of projected_forces
     258          // using the obtained weights
     259          {
     260            weights_t::const_iterator weightiter = weights.begin();
     261            std::vector<Vector>::const_iterator vectoriter = BondVectors.begin();
     262            Vector forcesum_check;
     263            for(BondList::const_iterator bonditer = ListOfBonds.begin();
     264                bonditer != ListOfBonds.end(); ++bonditer, ++weightiter, ++vectoriter) {
     265              const bond::ptr &current_bond = *bonditer;
     266              const Vector &BondVector = *vectoriter;
     267
     268              std::vector<double> &forcelist = (&walker == current_bond->leftatom) ?
     269                  projected_forces[timestep][leftside] : projected_forces[timestep][rightside];
     270              const size_t index = bv.getIndexForBond(current_bond);
     271              ASSERT( index != (size_t)-1,
     272                  "ForceAnnealing() - could not find bond "+toString(*current_bond)
     273                  +" in bondvectors");
     274              forcelist[index] = (*weightiter)*walkerGradient.ScalarProduct(BondVector);
     275              LOG(4, "DEBUG: BondVector " << BondVector << " receives projected force of "
     276                  << forcelist[index]);
     277              forcesum_check += forcelist[index] * BondVector;
     278            }
     279            ASSERT( weightiter == weights.end(),
     280                "ForceAnnealing - weightiter is not at end when it should be.");
     281            ASSERT( vectoriter == BondVectors.end(),
     282                "ForceAnnealing - vectoriter is not at end when it should be.");
     283            LOG(3, "DEBUG: sum of projected forces is " << forcesum_check);
     284          }
     285
    212286        } else {
    213287          LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than "
    214288              << MYEPSILON << " for atom " << walker);
     289          // note that projected_forces is initialized to full length and filled
     290          // with zeros. Hence, nothing to do here
    215291        }
    216292      }
     
    219295    // step through each bond and shift the atoms
    220296    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;
     297
     298    LOG(3, "DEBUG: current step is " << currentStep << ", given time step is " << CurrentTimeStep);
     299    const BondVectors::mapped_t bondvectors = bv.getBondVectorsAtStep(currentStep);
     300
     301    for (BondVectors::container_t::const_iterator bondsiter = sorted_bonds.begin();
     302        bondsiter != sorted_bonds.end(); ++bondsiter) {
     303      const bond::ptr &current_bond = *bondsiter;
     304      const size_t index = bv.getIndexForBond(current_bond);
     305      const atom* bondatom[MAX_sides] = {
     306          current_bond->leftatom,
     307          current_bond->rightatom
     308      };
     309
     310      // remove the edge
     311#ifndef NDEBUG
     312      const bool status =
     313#endif
     314          BGcreator.removeEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
     315      ASSERT( status, "ForceAnnealing() - edge to found bond is not present?");
     316
     317      // gather nodes for either atom
     318      BoostGraphHelpers::Nodeset_t bondside_set[MAX_sides];
     319      BreadthFirstSearchGatherer::distance_map_t distance_map[MAX_sides];
     320      for (size_t side=leftside;side<MAX_sides;++side) {
     321        bondside_set[side] = NodeGatherer(bondatom[side]->getId(), max_distance);
     322        distance_map[side] = NodeGatherer.getDistances();
     323        std::sort(bondside_set[side].begin(), bondside_set[side].end());
     324      }
     325
     326      // re-add edge
     327      BGcreator.addEdge(bondatom[leftside]->getId(), bondatom[rightside]->getId());
     328
     329      // do for both leftatom and rightatom of bond
     330      for (size_t side = leftside; side < MAX_sides; ++side) {
     331        const double &bondforce = projected_forces[0][side][index];
     332        const double &oldbondforce = projected_forces[1][side][index];
     333        const double bondforcedifference = (bondforce - oldbondforce);
     334        // if difference or bondforce itself is zero, do nothing
     335        if ((fabs(bondforce) < MYEPSILON) || (fabs(bondforcedifference) < MYEPSILON))
     336          continue;
     337        const BondVectors::mapped_t::const_iterator bviter =
     338            bondvectors.find(current_bond);
     339        ASSERT( bviter != bondvectors.end(),
     340            "ForceAnnealing() - cannot find current_bond ?");
     341        const Vector &BondVector = bviter->second;
     342        const Vector &oldPosition = bondatom[side]->getPositionAtStep(CurrentTimeStep-1 >= 0 ? CurrentTimeStep - 1 : 0);
     343        const Vector &currentPosition = bondatom[side]->getPosition();
     344        const double stepwidth =
     345            fabs((currentPosition - oldPosition).ScalarProduct(BondVector))/bondforcedifference;
     346        Vector PositionUpdate = stepwidth * BondVector;
     347        if (fabs(stepwidth) < 1e-10) {
     348          // dont' warn in first step, deltat usage normal
     349          if (currentStep != 1)
     350            ELOG(1, "INFO: Barzilai-Borwein stepwidth is zero, using deltat " << currentDeltat << " instead.");
     351          PositionUpdate = currentDeltat * BondVector;
     352        }
     353        LOG(3, "DEBUG: Update would be " << PositionUpdate);
     354
     355        // add PositionUpdate for all nodes in the bondside_set
     356        for (BoostGraphHelpers::Nodeset_t::const_iterator setiter = bondside_set[side].begin();
     357            setiter != bondside_set[side].end(); ++setiter) {
     358          const BreadthFirstSearchGatherer::distance_map_t::const_iterator diter
     359            = distance_map[side].find(*setiter);
     360          ASSERT( diter != distance_map[side].end(),
     361              "ForceAnnealing() - could not find distance to an atom.");
     362          const double factor = pow(damping_factor, diter->second);
     363          LOG(3, "DEBUG: Update for atom #" << *setiter << " will be "
     364              << factor << "*" << PositionUpdate);
     365          if (GatheredUpdates.count((*setiter))) {
     366            GatheredUpdates[(*setiter)] += factor*PositionUpdate;
     367          } else {
     368            GatheredUpdates.insert(
     369                std::make_pair(
     370                    (*setiter),
     371                    factor*PositionUpdate) );
     372          }
     373        }
     374      }
     375    }
    291376
    292377    Vector maxComponents(zeroVec);
Note: See TracChangeset for help on using the changeset viewer.