Beispiel #1
0
   /* 
   * Build the PairList.
   */ 
   void MdPairPotential::buildPairList() 
   {

      // Recalculate the grid for the internal CellList
      pairList_.makeGrid(boundary());

      // Clear all atoms from the internal CellList
      pairList_.clear();

      // Add every atom in this System to the CellList
      System::MoleculeIterator molIter;
      Molecule::AtomIterator   atomIter;
      for (int iSpec=0; iSpec < simulation().nSpecies(); ++iSpec) {
         for (begin(iSpec, molIter); molIter.notEnd(); ++molIter) {
            molIter->begin(atomIter); 
            for ( ; atomIter.notEnd(); ++atomIter) {
               #ifdef MCMD_SHIFT
               boundary().shift(atomIter->position(), atomIter->shift());
               #else
               boundary().shift(atomIter->position());
               #endif
               pairList_.addAtom(*atomIter);
            }
         }
      }

      // Use the completed CellList to build the PairList 
      pairList_.build(boundary());
   }
   /*
   * Evaluate external energy, and add to ensemble. 
   */
   void McExternalEnergyAverage::sample(long iStep) 
   {
      if (!isAtInterval(iStep)) return;

      double energy = 0.0;
      System::MoleculeIterator molIter;
      Molecule::AtomIterator atomIter;
      for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
         for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
             for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
                 energy += system().externalPotential().energy(atomIter->position(), atomIter->typeId());
             }
         }
      }
      accumulator_.sample(energy, outputFile_);
   }
 void HoomdMove::addBonds()
 {
    // Loop over bonds and initialize them
    int iSpec;
    System::MoleculeIterator molIter;
    Molecule::BondIterator bondIter;
    for (iSpec=0; iSpec < simulation().nSpecies(); ++iSpec) {
       for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter) {
          for (molIter->begin(bondIter); bondIter.notEnd(); ++bondIter) {
             ::Bond bond(bondIter->typeId(),
                         bondIter->atom(0).id(),
                         bondIter->atom(1).id());
             bondDataSPtr_->addBond(bond);
           }
       }
    } 
 }
Beispiel #4
0
   /// Add particle pairs to RDF histogram.
   void BondLengthDist::sample(long iStep) 
   {
      if (isAtInterval(iStep))  {

         System::MoleculeIterator molIter;
         Molecule::BondIterator   bondIter;
         double    lsq, l;

         for (system().begin(speciesId_, molIter); molIter.notEnd(); ++molIter) {
            for (molIter->begin(bondIter); bondIter.notEnd(); ++bondIter) {
               lsq = system().boundary().distanceSq( bondIter->atom(0).position(), 
                                                     bondIter->atom(1).position());
               l = sqrt(lsq);
               accumulator_.sample(l);
            }
         }
      }
   }  
   /* 
   * Build the CellList.
   */ 
   void McPairPotential::buildCellList() 
   {
      // Set up a grid of empty cells.
      cellList_.setup(boundary(), maxPairCutoff());

      // Add all atoms to cellList_ 
      System::MoleculeIterator molIter;
      Molecule::AtomIterator atomIter;
      for (int iSpec=0; iSpec < simulation().nSpecies(); ++iSpec) {
         for (begin(iSpec, molIter); molIter.notEnd(); ++molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               boundary().shift(atomIter->position());
               cellList_.addAtom(*atomIter);
            }
         }
      }

   }
Beispiel #6
0
   /*
   * Identify all clusters in the system.
   */
   void ClusterIdentifier::identifyClusters()
   {

      // Initialize all data structures:
      // Setup a grid of empty cells
      cellList_.setup(system().boundary(), cutoff_);
      // Clear clusters array and all links
      clusters_.clear();
      for (int i = 0; i < links_.capacity(); ++i) {
         links_[i].clear();
      }

      // Build the cellList, associate Molecule with ClusterLink.
      // Iterate over molecules of species speciesId_
      System::MoleculeIterator molIter;
      Molecule::AtomIterator atomIter;
      system().begin(speciesId_, molIter);
      for ( ; molIter.notEnd(); ++molIter) {

         // Associate this Molecule with a ClusterLink
         links_[molIter->id()].setMolecule(*molIter.get());

         // Add atoms of type = atomTypeId_ to the CellList
         for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
            if (atomIter->typeId() == atomTypeId_) {
               system().boundary().shift(atomIter->position());
               cellList_.addAtom(*atomIter);
            }

         }
      }

      // Identify all clusters
      Cluster* clusterPtr;
      ClusterLink* linkPtr;
      int clusterId = 0;
      system().begin(speciesId_, molIter);
      for ( ; molIter.notEnd(); ++molIter) {

         // Find the link with same index as this molecule
         linkPtr = &(links_[molIter->id()]);
         assert (&(linkPtr->molecule()) == molIter.get());

         // If this link is not in a cluster, begin a new cluster
         if (linkPtr->clusterId() == -1) {

            // Add a new empty cluster to clusters_ array
            clusters_.resize(clusterId+1);
            clusterPtr = &clusters_[clusterId];
            clusterPtr->clear();
            clusterPtr->setId(clusterId);

            // Identify molecules in this cluster
            clusterPtr->addLink(*linkPtr);
            workStack_.push(*linkPtr);
            while (workStack_.size() > 0) {
               processNextMolecule(*clusterPtr);
            }

            clusterId++;
         }

      }

      // Validity check - throws exception on failure.
      isValid();
   }
   /*
   * Verlet MD NVE integrator step
   *
   * This method implements the algorithm:
   *
   *        vm(n)  = v(n) + 0.5*a(n)*dt
   *        x(n+1) = x(n) + vm(n)*dt
   *
   *        calculate acceleration a(n+1)
   *
   *        v(n+1) = vm(n) + 0.5*a(n+1)*dt
   *
   * where x is position, v is velocity, and a is acceleration.
   */
   void NveVvIntegrator::step() 
   {
      Vector dv;
      Vector dr;
      System::MoleculeIterator molIter;
      double prefactor;
      int iSpecies, nSpecies;

      nSpecies = simulation().nSpecies();

      // 1st half velocity Verlet, loop over atoms 
      #if USE_ITERATOR
      Molecule::AtomIterator atomIter;
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {

               prefactor = prefactors_[atomIter->typeId()];

               dv.multiply(atomIter->force(), prefactor);
               atomIter->velocity() += dv;

               dr.multiply(atomIter->velocity(), dt_);
               atomIter->position() += dr;

            }
         }
      }
      #else 
      Atom* atomPtr;
      int   ia;
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            for (ia=0; ia < molIter->nAtom(); ++ia) {
               atomPtr = &molIter->atom(ia);
               prefactor = prefactors_[atomPtr->typeId()];

               dv.multiply(atomPtr->force(), prefactor);
               atomPtr->velocity() += dv;
               dr.multiply(atomPtr->velocity(), dt_);
               
               atomPtr->position() += dr;

            }
         }
      }
      #endif

      system().calculateForces();

      // 2nd half velocity Verlet, loop over atoms
      #if USE_ITERATOR
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               prefactor = prefactors_[atomIter->typeId()];
               dv.multiply(atomIter->force(), prefactor);
               atomIter->velocity() += dv;
            }
         }
      }
      #else
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter)
         {
            for (ia=0; ia < molIter->nAtom(); ++ia) {
               atomPtr = &molIter->atom(ia);
               prefactor = prefactors_[atomPtr->typeId()];
               dv.multiply(atomPtr->force(), prefactor);
               atomPtr->velocity() += dv;
            }
         }
      }
      #endif

      #ifndef INTER_NOPAIR
      if (!system().pairPotential().isPairListCurrent()) {
         system().pairPotential().buildPairList();
      }
      #endif

   }
Beispiel #8
0
   /*
   * Generate, attempt and accept or reject a Hybrid MD/MC move.
   */
   bool HybridNphMdMove::move()
   {
      System::MoleculeIterator molIter;
      Molecule::AtomIterator   atomIter;
      double oldEnergy, newEnergy;
      int    iSpec;
      int    nSpec = simulation().nSpecies();

      bool   accept;

      if (nphIntegratorPtr_ == NULL) {
         UTIL_THROW("null integrator pointer");
      }
      // Increment counter for attempted moves
      incrementNAttempt();
      
      // Store old boundary lengths.
      Vector oldLengths = system().boundary().lengths();

      // Store old atom positions in oldPositions_ array.
      for (iSpec = 0; iSpec < nSpec; ++iSpec) {
         mdSystemPtr_->begin(iSpec, molIter);
         for ( ; molIter.notEnd(); ++molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               oldPositions_[atomIter->id()] = atomIter->position();
            }
         }
      }

      // Initialize MdSystem
      #ifndef INTER_NOPAIR
      mdSystemPtr_->pairPotential().buildPairList();
      #endif
      mdSystemPtr_->calculateForces();
      mdSystemPtr_->setBoltzmannVelocities(energyEnsemble().temperature());
      nphIntegratorPtr_->setup();
      
      // generate integrator variables from a Gaussian distribution
      Random& random = simulation().random();
      
      double temp = system().energyEnsemble().temperature();
       
      double volume = system().boundary().volume();
      
      if (mode_ == Cubic) {
         // one degree of freedom
	 // barostat_energy = 1/2 (1/W) eta_x^2
         double sigma = sqrt(temp/barostatMass_);
         nphIntegratorPtr_->setEta(0, sigma*random.gaussian());
      } else if (mode_ == Tetragonal) {
         // two degrees of freedom
         // barostat_energy = 1/2 (1/W) eta_x^2 + 1/2 (1/(2W)) eta_y^2
         double sigma1 = sqrt(temp/barostatMass_);
         nphIntegratorPtr_->setEta(0, sigma1*random.gaussian());
         double sigma2 = sqrt(temp/barostatMass_/2.0);
         nphIntegratorPtr_->setEta(1, sigma2*random.gaussian());
      } else if (mode_ == Orthorhombic) { 
         // three degrees of freedom 
         // barostat_energy = 1/2 (1/W) (eta_x^2 + eta_y^2 + eta_z^2)
         double sigma = sqrt(temp/barostatMass_);
         nphIntegratorPtr_->setEta(0, sigma*random.gaussian());
         nphIntegratorPtr_->setEta(1, sigma*random.gaussian());
         nphIntegratorPtr_->setEta(2, sigma*random.gaussian());
      }

      // Store old energy
      oldEnergy  = mdSystemPtr_->potentialEnergy();
      oldEnergy += mdSystemPtr_->kineticEnergy();
      oldEnergy += system().boundaryEnsemble().pressure()*volume;
      oldEnergy += nphIntegratorPtr_->barostatEnergy();

      // Run a short MD simulation
      for (int iStep = 0; iStep < nStep_; ++iStep) {
         nphIntegratorPtr_->step();
      }
      
      volume = system().boundary().volume();

      // Calculate new energy
      newEnergy  = mdSystemPtr_->potentialEnergy();
      newEnergy += mdSystemPtr_->kineticEnergy();
      newEnergy += system().boundaryEnsemble().pressure()*volume;
      newEnergy += nphIntegratorPtr_->barostatEnergy();

      // Decide whether to accept or reject
      accept = random.metropolis( boltzmann(newEnergy-oldEnergy) );

      // Accept move
      if (accept) {
         
         #ifndef INTER_NOPAIR
         // Rebuild the McSystem cellList using the new positions.
         system().pairPotential().buildCellList();
         #endif

         // Increment counter for the number of accepted moves.
         incrementNAccept();

      } else {
         
         // Restore old boundary lengths
         system().boundary().setOrthorhombic(oldLengths);

         // Restore old atom positions
         for (iSpec = 0; iSpec < nSpec; ++iSpec) {
            mdSystemPtr_->begin(iSpec, molIter);
            for ( ; molIter.notEnd(); ++molIter) {
               molIter->begin(atomIter);
               for ( ; atomIter.notEnd(); ++atomIter) {
                  atomIter->position() = oldPositions_[atomIter->id()];
               }
            }
         }

      }

      return accept;

   }
   /* 
   * Evaluate end-to-end vectors of all chains, add to ensemble.
   */
   void ClustersDynamics::sample(long iStep) 
   { if (isAtInterval(iStep)) {
         newClustersPtr_->sample(iStep);

         if (iStep == 0) {
            oldClustersPtr_->sample(iStep);
         }         
         int clustersCorrelations_[newClustersPtr_->clusterLengths_.size()][oldClustersPtr_->clusterLengths_.size()];
          
         for (int i = 0; i < newClustersPtr_->clusterLengths_.size(); i++) {
             for (int j = 0; j < oldClustersPtr_->clusterLengths_.size(); j++) {
                 clustersCorrelations_[i][j] = 0;
             }
         }

         System::MoleculeIterator molIter;
         for (system().begin(speciesId_, molIter); molIter.notEnd(); ++molIter) {
             clustersCorrelations_[newClustersPtr_->clusters_[molIter->id()].clusterId_][oldClustersPtr_->clusters_[molIter->id()].clusterId_] += 1 ;
         }

         bool usedIds_[newClustersPtr_->clusterLengths_.size()];
         int idConversions_[newClustersPtr_->clusterLengths_.size()];

         for (int i = 0; i < newClustersPtr_->clusterLengths_.size(); i++) {
             usedIds_[i] = false;
             idConversions_[i] = 0;
         }
         int id_ = 0;
         for (int i = 0; i < newClustersPtr_->clusterLengths_.size(); i++) {
           for (int j = 0; j < oldClustersPtr_->clusterLengths_.size(); j++) {
             //this only works if the criterion > 50%
                 if (clustersCorrelations_[i][j] > criterion_ * oldClustersPtr_->clusterLengths_[i]) id_ = i;
             }
             usedIds_[id_] = true;
             idConversions_[i] = id_;
         }
         int usedIdCounter_ = 0;
         for (int i = 0; i < newClustersPtr_->clusterLengths_.size(); i++) {
             if (idConversions_[i] == 0) {
                while (usedIds_[usedIdCounter_] == true) usedIdCounter_++;
                idConversions_[i] = usedIdCounter_;
                usedIds_[usedIdCounter_] = true;
             }  
         }

/*         for (system().begin(speciesId_, molIter); molIter.notEnd(); ++molIter) {
             newClustersPtr_->clusters_[molIter->id()].clusterId_ = idConversions_[newClustersPtr_->clusters_[molIter->id()].clusterId_];
         } */

         std::string fileName;
         fileName  = outputFileName();
         fileName += toString(iStep);
         fileName += ".dynamics";
         fileMaster().openOutputFile(fileName, outputFile_);
         for (int i = 0; i < newClustersPtr_->clusterLengths_.size(); i++) {
             outputFile_<<"<"<< i <<">"<<"("<<newClustersPtr_->clusterLengths_[i]<<") = ";
             for (int j=0; j < oldClustersPtr_->clusterLengths_.size(); j++) {
                  if (clustersCorrelations_[i][j] != 0){
                    outputFile_<<"<"<< j <<">["<<oldClustersPtr_->clusterLengths_[j]<<"]("<<clustersCorrelations_[i][j]<<") ";
                 }
             }
             outputFile_<<"\n";
         }
         outputFile_.close();

         ClustersFinder *tmp = oldClustersPtr_;
         oldClustersPtr_ = &(*newClustersPtr_);
         newClustersPtr_ = &(*tmp);
      }
   }
Beispiel #10
0
   // Create links and print.
   void Crosslinker::sample(long iStep) 
   {
      if (isAtInterval(iStep)) {

         // Clear the cellList
         cellList_.clear();
         // Add every atom in this System to the CellList
         System::MoleculeIterator molIter;
         Atom*                     atomPtr;
         for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec) {
            for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter) {
               for (int ia=0; ia < molIter->nAtom(); ++ia) {
                  atomPtr = &molIter->atom(ia);
                  system().boundary().shift(atomPtr->position());
                  cellList_.addAtom(*atomPtr);
               }
            }
         }

         //Use the cell list to find neighbours and create links
         CellList::NeighborArray cellNeighbor;
         Vector  iPos, jPos;
         Atom   *iAtomPtr, *jAtomPtr;
         double  dRSq, cutoffSq=cutoff_*cutoff_;
         int     nCellNeighbor, nCellAtom, totCells;
         int     ic, ip, iAtomId, jp, jAtomId;
         // Loop over cells containing primary atom. ic = cell index
         totCells = cellList_.totCells();
         for (ic = 0; ic < totCells; ++ic) {
           // Get Array cellNeighbor of Ids of neighbor atoms for cell ic.
           // Elements 0,..., nCellAtom - 1 contain Ids for atoms in cell ic.
           // Elements nCellAtom,..., nCellNeighbor-1 are from neighboring cells.
           cellList_.getCellNeighbors(ic, cellNeighbor, nCellAtom);
           nCellNeighbor = cellNeighbor.size();
           // Loop over atoms in cell ic
           for (ip = 0; ip < nCellAtom; ++ip) {
              iAtomPtr = cellNeighbor[ip]; 
              iPos     = iAtomPtr->position();
              iAtomId  = iAtomPtr->id();
              // Loop over atoms in all neighboring cells, including cell ic.
              for (jp = 0; jp < nCellNeighbor; ++jp) {
                 jAtomPtr = cellNeighbor[jp]; 
                 jPos     = jAtomPtr->position();
                 jAtomId  = jAtomPtr->id();
                 // Avoid double counting: only count pairs with jAtomId > iAtomId
                 if ( jAtomId > iAtomId ) {
                   // Exclude bonded pairs
                   if (!iAtomPtr->mask().isMasked(*jAtomPtr))  {
                      // Calculate distance between atoms i and j
                      dRSq = system().boundary().distanceSq(iPos, jPos);
                      if (dRSq < cutoffSq) {
                         if(system().simulation().random().uniform(0.0, 1.0) < probability_){ 
                          //create a link between i and j atoms
                           system().linkMaster().addLink(*iAtomPtr, *jAtomPtr, 0);
                         }
                      }
                   }
                 } // end if jAtomId > iAtomId
              } // end for jp (j atom)
           } // end for ip (i atom)
         } // end for ic (i cell)

         // Construct a string representation of integer nSample
         std::stringstream ss;
         std::string       nSampleString;
         ss << nSample_;
         nSampleString = ss.str();

         // Construct new fileName: outputFileName + char(nSample)
         std::string filename;
         filename  = outputFileName();
         filename += nSampleString; 

         // Open output file, write data, and close file
         fileMaster().openOutputFile(filename, outputFile_);
         system().writeConfig(outputFile_);
         outputFile_.close();
         nSample_++;

         // Clear the LinkMaster
         system().linkMaster().clear();

      } // end isAtInterval
   }
Beispiel #11
0
   void NphIntegrator::step() 
   {
      System::MoleculeIterator molIter;
      double prefactor;
      int    iSpecies, nSpecies;

      nSpecies = simulation().nSpecies();

      /* perform the first half step of the explicitly reversible NPH integration scheme.
  
         This follows from operator factorization
  
         - orthorhombic case:
  
           1) eta_alpha(t+dt/2) = eta_alpha(t) + dt/2 * V/L_alpha * ( P_{alpha,alpha}(t) - P_ext)
           2) v' = v(t) + (1/2)a(t)*dt
           3) L(t+dt/2) = L(t) + dt*eta(t+dt/2)/(2*W)
           4) r'_alpha = r(t) + v'*dt* (L_{alpha}^2(t)/L_{alpha}^2(t+dt/2))
           5a) L(t+dt) = L(t+dt/2) + dt*eta(t+dt/2)/2/W
           5b) r_alpha(t+dt) = L_alpha(t+dt)/L_alpha(t)*r'_alpha
           5c) v''_alpha = L_alpha(t)/L_alpha(t+dt) * v'_alpha
  
         alpha denotes a cartesian index.
  
         - isotropic case:
  
           only eta_x := eta is used and instead of step 1) we have
  
           1') eta(t+dt/2) = eta(t) + dt/2 * (1/3*Tr P(t) - P_ext)
  
           furthermore, in step 3) and 5a) L is replaced with V=L^3
  
         - tetragonal case:
  
           Lx := L_perp, Ly = Lz := L_par
           eta_x := eta_perp, etay := eta_par, etaz unused
  
           instead of step 1) we have
  
           1'a) eta_perp(t+dt/2) = eta_perp + dt/2 * V/L_perp * ( P_xx(t) - P_ext)
           1'b) eta_par(t+dt/2) = eta_par + dt/2 * V/L_par * ( P_yy(t) + P_zz(t) - 2*P_ext)
  
           steps 3) and 5a) are split into two sub-steps
  
           L_perp(i+1) = L_perp(i) + dt/(2*W)*eta_perp
           L_par(i+1) = L_par(i) + dt/(4*W)*eta_par
      */

      // obtain current stress tensor (diagonal components)
      system().computeStress(currP_);

      // obtain current box lengths
      Vector lengths  = system().boundary().lengths();
      double volume = lengths[0]*lengths[1]*lengths[2];

      double extP = system().boundaryEnsemble().pressure();

      // advance eta(t)->eta(t+dt/2) (step one)
      if (mode_ == Orthorhombic) {
         double Vdthalf = 1.0/2.0 * volume * dt_;
         eta_[0] += Vdthalf/lengths[0] * (currP_[0] - extP);
         eta_[1] += Vdthalf/lengths[1] * (currP_[1] - extP);
         eta_[2] += Vdthalf/lengths[2] * (currP_[2] - extP);
      } else if (mode_ == Tetragonal) { 
         double Vdthalf = 1.0/2.0* volume * dt_; 
         eta_[0] += Vdthalf/lengths[0]*(currP_[0] - extP);
         eta_[1] += Vdthalf/lengths[1]*(currP_[1] + currP_[2] - 2.0*extP);
      } else if (mode_ == Cubic) {
         eta_[0] += 1.0/2.0*dt_*(1.0/3.0*(currP_[0]+currP_[1]+currP_[2]) - extP);
      }

      // update the box length L(t) -> L(t+dt/2) (step three)
      // (since we still keep the accelerations a(t) computed for box length L_alpha(t) in memory,
      // needed in step two, we can exchange the order of the two steps)
      // also pre-calculate L(t+dt) (step 5a, only depends on eta(t) of step one)

      Vector lengthsOld = lengths;
      Vector lengthsFinal;
      double volumeFinal = 0.0;

      double dthalfoverW = 1.0/2.0*dt_/W_;

      if (mode_ == Orthorhombic) {
         lengths[0] += dthalfoverW*eta_[0];
         lengths[1] += dthalfoverW*eta_[1];
         lengths[2] += dthalfoverW*eta_[2];
         lengthsFinal[0] = lengths[0] + dthalfoverW*eta_[0];
         lengthsFinal[1] = lengths[1] + dthalfoverW*eta_[1];
         lengthsFinal[2] = lengths[2] + dthalfoverW*eta_[2];
         volumeFinal = lengthsFinal[0]*lengthsFinal[1]*lengthsFinal[2];
      } else if (mode_ == Tetragonal) {
         lengths[0] += dthalfoverW*eta_[0];
         lengths[1] += (1.0/2.0)*dthalfoverW*eta_[1];
         lengths[2] = lengths[1];
         lengthsFinal[0] = lengths[0] + dthalfoverW*eta_[0];
         lengthsFinal[1] = lengths[1] + (1.0/2.0)*dthalfoverW*eta_[1];
         lengthsFinal[2] = lengthsFinal[1];
         volumeFinal = lengthsFinal[0]*lengthsFinal[1]*lengthsFinal[2];
      } else if (mode_ == Cubic) {
         volume += dthalfoverW*eta_[0];
         lengths[0] = pow(volume,1.0/3.0); // Lx = Ly = Lz = V^(1/3)
         lengths[1] = lengths[0];
         lengths[2] = lengths[0];
         volumeFinal = volume + dthalfoverW*eta_[0];
         lengthsFinal[0] = pow(volumeFinal,1./3.);
         lengthsFinal[1] = lengthsFinal[0];
         lengthsFinal[2] = lengthsFinal[0];
      }
         
      // update simulation box 
      system().boundary().setOrthorhombic(lengthsFinal); 

      
      // update particles
      Atom* atomPtr;
      int   ia;
      Vector vtmp, dr, rtmp, dv;
      Vector lengthsFac, dtLengthsFac2;

      for (int i=0; i<3; i++) {
         lengthsFac[i] = lengthsFinal[i]/lengthsOld[i];
         dtLengthsFac2[i] = dt_ * lengthsOld[i]*lengthsOld[i]/lengths[i]/lengths[i];
      }

      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            for (ia=0; ia < molIter->nAtom(); ++ia) {
               atomPtr = &molIter->atom(ia);
               prefactor = prefactors_[atomPtr->typeId()];

               dv.multiply(atomPtr->force(), prefactor);
               vtmp.add(atomPtr->velocity(),dv);

               dr[0] = vtmp[0] * dtLengthsFac2[0];
               dr[1] = vtmp[1] * dtLengthsFac2[1];
               dr[2] = vtmp[2] * dtLengthsFac2[2];

               rtmp.add(atomPtr->position(), dr);

               rtmp[0] *= lengthsFac[0];
               rtmp[1] *= lengthsFac[1];
               rtmp[2] *= lengthsFac[2];

               atomPtr->position() = rtmp;

               vtmp[0] /= lengthsFac[0];
               vtmp[1] /= lengthsFac[1];
               vtmp[2] /= lengthsFac[2];

               atomPtr->velocity() = vtmp;
            }
         }
      }

      #ifndef INTER_NOPAIR
      if (!system().pairPotential().isPairListCurrent()) {
         system().pairPotential().buildPairList();
      } 
      #endif

      system().calculateForces();

      /* the second step of the explicitly reversible integrator consists of the following to sub-steps

         6) v(t+dt) = v'' + 1/2 * a(t+dt)*dt
         7) eta(t+dt/2) -> eta(t+dt)
       */

      // v(t+dt) = v'' + 1/2 * a(t+dt)*dt
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter)
         {
            for (ia=0; ia < molIter->nAtom(); ++ia) {
               atomPtr = &molIter->atom(ia);
               prefactor = prefactors_[atomPtr->typeId()];
               dv.multiply(atomPtr->force(), prefactor);
               atomPtr->velocity() += dv;
            }
         }
      }

      // now compute pressure tensor with updated virial and velocities
      system().computeStress(currP_);

      //  advance eta(t+dt/2) -> eta(t+dt)
      if (mode_ == Orthorhombic) {
         double Vdthalf = 1.0/2.0 * volumeFinal * dt_;
         eta_[0] += Vdthalf/lengthsFinal[0] * (currP_[0] - extP);
         eta_[1] += Vdthalf/lengthsFinal[1] * (currP_[1] - extP);
         eta_[2] += Vdthalf/lengthsFinal[2] * (currP_[2] - extP);
      } else if (mode_ == Tetragonal) {
         double Vdthalf = 1.0/2.0 * volumeFinal * dt_;
         eta_[0] += Vdthalf/lengthsFinal[0]*(currP_[0] - extP);
         eta_[1] += Vdthalf/lengthsFinal[1]*(currP_[1] + currP_[2] - 2.0*extP);
      } else if (mode_ == Cubic) {
         eta_[0] += 1.0/2.0*dt_*(1.0/3.0*(currP_[0]+currP_[1]+currP_[2]) - extP);
      }
 
      #ifndef INTER_NOPAIR
      if (!system().pairPotential().isPairListCurrent()) {
         system().pairPotential().buildPairList();
      }
      #endif
     
      /*
      // debug output
      double P;
      system().computeStress(P);
      std::cout << system().boundary() << std::endl;
      std::cout << "P = " << P << " ";
      std::cout << "Ekin = " << system().kineticEnergy() << " ";
      std::cout << "Epot = " << system().potentialEnergy() << " ";
      std::cout << "PV = " << extP * system().boundary().volume() << " ";
      std::cout << "Ebaro = " << barostatEnergy() << " "; 
      std::cout << "H = " << system().potentialEnergy() + system().kineticEnergy() +
                    extP * system().boundary().volume() + barostatEnergy() << std::endl;
      */
   }
   /*
   * Nose-Hoover integrator step.
   *
   * This implements a reversible Velocity-Verlet MD NVT integrator step.
   *
   * Reference: Winkler, Kraus, and Reineker, J. Chem. Phys. 102, 9018 (1995).
   */
   void NvtNhIntegrator::step() 
   {
      Vector  dv;
      Vector  dr;
      System::MoleculeIterator molIter;
      double  dtHalf = 0.5*dt_;
      double  prefactor;
      double  factor;
      Molecule::AtomIterator atomIter;
      int  iSpecies, nSpecies;
      int  nAtom;

      T_target_ = energyEnsemblePtr_->temperature();
      nSpecies  = simulation().nSpecies();
      nAtom     = system().nAtom();

      factor = exp(-dtHalf*(xi_ + xiDot_*dtHalf));

      // 1st half velocity Verlet, loop over atoms 
      for (iSpecies = 0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {

            molIter->begin(atomIter); 
            for ( ; atomIter.notEnd(); ++atomIter) {

               atomIter->velocity() *= factor;

               prefactor = prefactors_[atomIter->typeId()];
               dv.multiply(atomIter->force(), prefactor);
               //dv.multiply(atomIter->force(), dtHalf);

               atomIter->velocity() += dv;
               dr.multiply(atomIter->velocity(), dt_);

               atomIter->position() += dr;

            }

         }
      }

      // First half of update of xi_
      xi_ += xiDot_*dtHalf;

      #ifndef INTER_NOPAIR
      // Rebuild the pair list if necessary
      if (!system().pairPotential().isPairListCurrent()) {
         system().pairPotential().buildPairList();
      }
      #endif

      system().calculateForces();

      // 2nd half velocity Verlet, loop over atoms
      for (iSpecies=0; iSpecies < nSpecies; ++iSpecies) {
         system().begin(iSpecies, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               prefactor = prefactors_[atomIter->typeId()];
               dv.multiply(atomIter->force(), prefactor);
               atomIter->velocity() += dv;
               atomIter->velocity() *=factor;
            }
         }
      }

      // Update xiDot and complete update of xi_
      T_kinetic_ = system().kineticEnergy()*2.0/double(3*nAtom);
      xiDot_ = (T_kinetic_/T_target_ -1.0)*nuT_*nuT_;
      xi_ += xiDot_*dtHalf;

   }
Beispiel #13
0
   /*
   * Perform replica exchange move.
   */
   bool ReplicaMove::move()
   {
      MPI::Request request[4];
      MPI::Status  status;
      System::MoleculeIterator molIter;
      Molecule::AtomIterator   atomIter;
      int iA;
      int recvPt, sendPt;
  
      DArray<int> permutation;
      permutation.allocate(nProcs_);

      // Gather all derivatives of the perturbation Hamiltonians and parameters on processor with rank 0
      DArray<double> myDerivatives;
      myDerivatives.allocate(nParameters_);
      DArray<double> myParameters;
      myParameters.allocate(nParameters_);

      for (int i=0; i< nParameters_; i++) {
         myDerivatives[i] = system().perturbation().derivative(i);
         myParameters[i] = system().perturbation().parameter(i);
      }

      int size = 0;
      size += memorySize(myDerivatives);
      size += memorySize(myParameters);

      if (myId_ != 0) {

         MemoryOArchive sendCurrent;
         sendCurrent.allocate(size);

         sendCurrent << myDerivatives;
         sendCurrent << myParameters;

         sendCurrent.send(*communicatorPtr_, 0);
      } else {
         DArray< DArray<double> > allDerivatives;
         DArray< DArray<double> > allParameters;
         allDerivatives.allocate(nProcs_);
         allParameters.allocate(nProcs_);
   
         allDerivatives[0].allocate(nParameters_);
         allDerivatives[0] = myDerivatives;
         allParameters[0].allocate(nParameters_);
         allParameters[0] = myParameters;

         for (int i = 1; i<nProcs_; i++) {
            MemoryIArchive recvPartner;
            recvPartner.allocate(size);
            recvPartner.recv(*communicatorPtr_, i);
            allDerivatives[i].allocate(nParameters_);
            allParameters[i].allocate(nParameters_);
            recvPartner >> allDerivatives[i];
            recvPartner >> allParameters[i];
         }

         // Now we have the complete matrix U_ij = u_i(x_j), permutate nsampling steps according
         // to acceptance criterium
  
         // start with identity permutation
         for (int i = 0; i < nProcs_; i++)
            permutation[i] = i;

         for (int n =0; n < nSampling_; n++) {
            swapAttempt_++;
            // choose a pair i,j, i!= j at random
            int i = system().simulation().random().uniformInt(0,nProcs_);
            int j = system().simulation().random().uniformInt(0,nProcs_-1);
            if (i<=j) j++;

            // apply acceptance criterium
            double weight = 0;
            for (int k = 0; k < nParameters_; k++) {
               double deltaDerivative = allDerivatives[i][k] - allDerivatives[j][k];
               // the permutations operate on the states (the perturbation parameters)
               weight += (allParameters[permutation[j]][k] - allParameters[permutation[i]][k])*deltaDerivative;
             }
            double exponential = exp(-weight);
            int accept = system().simulation().random(). metropolis(exponential) ? 1 : 0;
   
            if (accept) {
               swapAccept_++;
               // swap states of pair i,j
               int tmp = permutation[i];
               permutation[i] = permutation[j];
               permutation[j] = tmp;
               }
         }
    
         // send exchange partner information to all other processors
         for (int i = 0; i < nProcs_; i++) {
            if (i != 0)
                communicatorPtr_->Send(&permutation[i], 1, MPI::INT, i, 0);
            else
                sendPt = permutation[i];

            if (permutation[i] != 0)
               communicatorPtr_->Send(&i, 1, MPI::INT, permutation[i], 1);
            else
               recvPt = i;
         }
      }

      
      if (myId_ != 0) {
         // partner id to receive from
         communicatorPtr_->Recv(&sendPt, 1, MPI::INT, 0, 0);
         // partner id to send to
         communicatorPtr_->Recv(&recvPt, 1, MPI::INT, 0, 1);
      }

      if (recvPt == myId_ || sendPt == myId_) {
         // no exchange necessary
         outputFile_ << sendPt << std::endl;
         return true;
      }

      assert(recvPt != myId_ && sendPt != myId_);

      Vector myBoundary;
      myBoundary = system().boundary().lengths();
            
      Vector ptBoundary;
            
      // Accomodate new boundary dimensions.
      request[0] = communicatorPtr_->Irecv(&ptBoundary, 1,
                                           MpiTraits<Vector>::type, recvPt, 1);

      // Send old boundary dimensions.
      request[1] = communicatorPtr_->Isend(&myBoundary, 1,
                                            MpiTraits<Vector>::type, sendPt, 1);

      request[0].Wait();
      request[1].Wait();
             
      system().boundary().setOrthorhombic(ptBoundary);

      // Pack atomic positions and types.
      iA = 0;
      for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
         for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               myPositionPtr_[iA] = atomIter->position();
               iA++;
            }
         }
      }
      
      // Accomodate new configuration.
      request[2] = communicatorPtr_->Irecv(ptPositionPtr_, iA,
                       MpiTraits<Vector>::type, recvPt, 2); 

      // Send old configuration.
      request[3] = communicatorPtr_->Isend(myPositionPtr_, iA,
                       MpiTraits<Vector>::type, sendPt, 2);

      request[2].Wait();
      request[3].Wait();
      
      // Adopt the new atomic positions.
      iA = 0;
      for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
         for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter){
               atomIter->position() = ptPositionPtr_[iA];
               ++iA;
            }
         }
      }
      

      // Notify component observers.
      sendRecvPair partners;
      partners[0] = sendPt;
      partners[1] = recvPt;
      Notifier<sendRecvPair>::notifyObservers(partners);

      // Log information about exchange partner to file
      outputFile_ << sendPt << std::endl;

      return true;

   }
   /*
   * Generate, attempt and accept or reject a Hybrid MD/MC move.
   */
   bool HoomdMove::move()
   {
      if ((!HoomdIsInitialized_) || moleculeSetHasChanged_) {
         initSimulation();
         moleculeSetHasChanged_ = false;
      }

      // We need to create the Integrator every time since we are starting
      // with new coordinates, velocities etc.
      // this does not seem to incur a significant performance decrease
      createIntegrator();

      System::MoleculeIterator molIter;
      Molecule::AtomIterator   atomIter;
      int nSpec = simulation().nSpecies();

      // Increment counter for attempted moves
      incrementNAttempt();

      double oldEnergy, newEnergy;

      {
      // Copy atom coordinates into HOOMD
      ArrayHandle<Scalar4> h_pos(particleDataSPtr_->getPositions(), access_location::host, access_mode::readwrite);
      ArrayHandle<Scalar4> h_vel(particleDataSPtr_->getVelocities(), access_location::host, access_mode::readwrite);
      ArrayHandle<unsigned int> h_tag(particleDataSPtr_->getTags(), access_location::host, access_mode::readwrite);
      ArrayHandle<unsigned int> h_rtag(particleDataSPtr_->getRTags(), access_location::host, access_mode::readwrite);

      for (int iSpec =0; iSpec < nSpec; ++iSpec) {
         system().begin(iSpec, molIter);
         for ( ; molIter.notEnd(); ++ molIter) {
            for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
               unsigned int idx = (unsigned int) atomIter->id();
               Vector& pos = atomIter->position();
               h_pos.data[idx].x = pos[0] - lengths_[0]/2.;
               h_pos.data[idx].y = pos[1] - lengths_[1]/2.;
               h_pos.data[idx].z = pos[2] - lengths_[2]/2.;
 
               int type = atomIter->typeId();
               h_vel.data[idx].w = simulation().atomType(type).mass();
               h_pos.data[idx].w = __int_as_scalar(type);
               h_tag.data[idx] = idx;
               h_rtag.data[idx] = idx; 
            }
         }
      }
 
      // Generate random velocities
      generateRandomVelocities(h_vel);
      }


      // Notify that the particle order has changed
      particleDataSPtr_->notifyParticleSort();

      // Initialize integrator (calculate forces and potential energy for step 0)
      integratorSPtr_->prepRun(0);

      // Calculate oldEnergy
      thermoSPtr_->compute(0);
      oldEnergy = thermoSPtr_->getLogValue("kinetic_energy",0);
      oldEnergy += thermoSPtr_->getLogValue("potential_energy",0);

      // Integrate nStep_ steps forward
      for (int iStep = 0; iStep < nStep_; ++iStep) {
         integratorSPtr_->update(iStep);

         // do we need to sort the particles?
         // do not sort at time step 0 to speed up short runs
         if (! (iStep % sorterPeriod_) && iStep)
           sorterSPtr_->update(iStep);
      }


      // Calculate new energy
      thermoSPtr_->compute(nStep_);
      newEnergy = thermoSPtr_->getLogValue("kinetic_energy",nStep_);
      newEnergy += thermoSPtr_->getLogValue("potential_energy",nStep_);

      // Decide whether to accept or reject
      bool accept = random().metropolis( boltzmann(newEnergy-oldEnergy) );

      if (accept) {
         // read back integrated positions 
         ArrayHandle<Scalar4> h_pos(particleDataSPtr_->getPositions(), access_location::host, access_mode::read);
         ArrayHandle<Scalar4> h_vel(particleDataSPtr_->getVelocities(), access_location::host, access_mode::read);
         ArrayHandle<unsigned int> h_tag(particleDataSPtr_->getTags(), access_location::host, access_mode::read);
         ArrayHandle<unsigned int> h_rtag(particleDataSPtr_->getRTags(), access_location::host, access_mode::read);
         for (int iSpec = 0; iSpec < nSpec; ++iSpec) {
            system().begin(iSpec, molIter);
            for ( ; molIter.notEnd(); ++molIter) {
               for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
                  unsigned int idx = h_rtag.data[atomIter->id()]; 
                  atomIter->position() = Vector(h_pos.data[idx].x+lengths_[0]/2.,
                                                h_pos.data[idx].y+lengths_[1]/2.,
                                                h_pos.data[idx].z+lengths_[2]/2.);
               }
            }
         }

         system().pairPotential().buildCellList();
         incrementNAccept();
      } else {
         // not accepted, do nothing
      }

      return accept;
   }
   /*
   * Perform replica exchange move.
   */
   bool ReplicaMove::move()
   {
      MPI::Request request[8];
      MPI::Status  status;
      double myWeight, ptWeight, exponential;
      int    isLeft, iAccept, myPort, ptPort;
      System::MoleculeIterator molIter;
      Molecule::AtomIterator   atomIter;
      int iA;
      
      // Default value for no replica exchange
      isLeft  = -1;
      iAccept = 0;

      if ((myId_ < nProcs_ - 1) && (stepCount_ >= myId_) &&
         ((stepCount_ - myId_) % (nProcs_ - 1)  ==0)) {
         // we are exchanging with processor myId_ + 1
         isLeft = 1;
         ptId_ = myId_ + 1;

      } else if ((myId_ > 0) && (stepCount_ >= myId_ - 1) &&
                ((stepCount_  - (myId_ - 1)) % (nProcs_ -1 ) == 0)) {
         // we are exchanging with processor myId_ - 1
         isLeft = 0;
         ptId_ = myId_ - 1; 
      } 
      if (isLeft == 0 || isLeft == 1) {
         // Set the port value for message tag
         myPort = myId_%2;
         ptPort = ptId_%2;
 
         // Update accumulator
         repxAttempt_[isLeft] += 1;

         // Exchange coupling parameters with partner
         for (int i = 0; i < nParameters_; ++i) {
            request[0] = communicatorPtr_->Irecv(&ptParam_[i], 1, MPI::DOUBLE, ptId_,
                                              TagParam[ptPort]);
            request[1] = communicatorPtr_->Isend(&myParam_[i], 1, MPI::DOUBLE, ptId_,
                                              TagParam[myPort]);
         
            // Synchronizing
            request[0].Wait();
            request[1].Wait();
         }   
         myWeight = system().perturbation().difference(ptParam_);
         
         // Collect tempering weights and make decision
         if (isLeft == 1) {

            // Receive energy difference from the right box
            request[2] = communicatorPtr_->Irecv(&ptWeight, 1, MPI::DOUBLE,
                                                ptId_, TagDecision[ptPort]);
            request[2].Wait();

            exponential = exp(-myWeight - ptWeight);

         } else {

            // Send energy difference to the left box
            request[2] = communicatorPtr_->Isend(&myWeight, 1, MPI::DOUBLE,
                                                 ptId_, TagDecision[myPort]);
            request[2].Wait();
         }

         // Collect tempering weights and make decision
         if (isLeft == 1) {
   
            // Metropolis test
            iAccept = system().simulation().random().
                      metropolis(exponential) ? 1 : 0;

            // Send decision to the right box
            request[3] = communicatorPtr_->Isend(&iAccept, 1, MPI::INT,
                                                 ptId_, TagDecision[myPort]);
            request[3].Wait();

         } else {

            // Receive decision from the left box
            request[3] = communicatorPtr_->Irecv(&iAccept, 1, MPI::INT,
                                                 ptId_, TagDecision[ptPort]);
            request[3].Wait();

         }
         // Exchange particle configurations if the move is accepted
         if (iAccept == 1) {
         
            // Update accumulator
            repxAccept_[isLeft] += 1;

            Vector myBoundary;
            myBoundary = system().boundary().lengths();
                  
            Vector ptBoundary;
                  
            // Accomodate new boundary dimensions.
            request[4] = communicatorPtr_->Irecv(&ptBoundary, 1,
                                                 MpiTraits<Vector>::type, ptId_, TagConfig[ptPort]);

            // Send old boundary dimensions.
            request[5] = communicatorPtr_->Isend(&myBoundary, 1,
                                                  MpiTraits<Vector>::type, ptId_, TagConfig[myPort]);

            request[4].Wait();
            request[5].Wait();
                   
            system().boundary().setOrthorhombic(ptBoundary);

            // Pack atomic positions and types.
            iA = 0;
            for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
               for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
                  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
                     myPositionPtr_[iA] = atomIter->position();
                     iA++;
                  }
               }
            }
            
            // Accomodate new configuration.
            request[6] = communicatorPtr_->Irecv(ptPositionPtr_, iA,
                             MpiTraits<Vector>::type, ptId_, TagConfig[ptPort]);

            // Send old configuration.
            request[7] = communicatorPtr_->Isend(myPositionPtr_, iA,
                             MpiTraits<Vector>::type, ptId_, TagConfig[myPort]);

            request[6].Wait();
            request[7].Wait();
            
            // Adopt the new atomic positions.
            iA = 0;
            for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
               for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
                  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter){
                     atomIter->position() = ptPositionPtr_[iA];
                     ++iA;
                  }
               }
            }
            

            // Notify component observers.
            //notifyObservers(ptId_);
            Notifier<int>::notifyObservers(ptId_);

         }  else {
            // the move was not accepted, do nothing
         }

      }


      // Output results needed to build exchange profile.
      if (isLeft == -1) {
        outputFile_ << "n" << std::endl;
      } else if ( isLeft != -1 && iAccept == 0) {
        outputFile_ << myId_ << std::endl;
      } else if ( isLeft != -1 && iAccept == 1) {
        outputFile_ << ptId_ << std::endl;
      }

      stepCount_++;

      return (iAccept == 1 ? true : false);

   }
   /*
   * Evaluate change in energy, add Boltzmann factor to accumulator.
   */
   void McMuExchange::sample(long iStep)
   {
      if (isAtInterval(iStep))  {

         // Preconditions
         if (!system().energyEnsemble().isIsothermal()) {
            UTIL_THROW("EnergyEnsemble is not isothermal");
         }
         if (nMolecule_ != system().nMolecule(speciesId_)) {
            UTIL_THROW("nMolecule has changed since setup");
         }

         McPairPotential& potential = system().pairPotential();
         const CellList& cellList = potential.cellList();
         System::MoleculeIterator molIter;
         Atom* ptr0 = 0;    // Pointer to first atom in molecule
         Atom* ptr1 = 0;    // Pointer to flipped atom
         Atom* ptr2 = 0;    // Pointer to neighboring atom
         Mask* maskPtr = 0; // Mask of flipped atom
         double rsq, dE, beta, boltzmann;
         int j, k, nNeighbor, nFlip;
         int i1, i2, id1, id2, t1, t2, t1New, iMol;
         beta = system().energyEnsemble().beta();
         nFlip = flipAtomIds_.size();

         // Loop over molecules in species
         iMol = 0;
         system().begin(speciesId_, molIter); 
         for ( ; molIter.notEnd(); ++molIter) {
            dE = 0.0;
            ptr0 = &molIter->atom(0);

            // Loop over flipped atoms
            for (j = 0; j < nFlip; ++j) {
               i1 = flipAtomIds_[j];
               t1New = newTypeIds_[i1];
               ptr1 = &molIter->atom(i1);
               id1 = ptr1->id();
               t1 = ptr1->typeId();
               maskPtr = &(ptr1->mask());

               // Loop over neighboring atoms
               cellList.getNeighbors(ptr1->position(), neighbors_);
               nNeighbor = neighbors_.size();
               for (k = 0; k < nNeighbor; ++k) {
                  ptr2 = neighbors_[k];
                  id2 = ptr2->id();

                  // Check if atoms are identical
                  if (id2 != id1) {
          
                     // Check if pair is masked
                     if (!maskPtr->isMasked(*ptr2)) {

                        rsq = boundary().distanceSq(ptr1->position(), 
                                                    ptr2->position());
                        t2 = ptr2->typeId();
                        if (&(ptr1->molecule()) != &(ptr2->molecule())) {

                           // Intermolecular atom pair 
                           dE -= potential.energy(rsq, t1, t2);
                           dE += potential.energy(rsq, t1New, t2);

                        } else {

                           // Intramolecular atom pair 
                           if (id2 > id1) {
                              dE -= potential.energy(rsq, t1, t2);
                              i2 = (int)(ptr2 - ptr0);
                              t2 = newTypeIds_[i2];
                              dE += potential.energy(rsq, t1New, t2);
                           } else {
                              i2 = (int)(ptr2 - ptr0);
                              if (!isAtomFlipped_[i2]) {
                                 dE -= potential.energy(rsq, t1, t2);
                                 t2 = newTypeIds_[i2];
                                 dE += potential.energy(rsq, t1New, t2);
                              }
                           }

                        }
                     }
                  }
               } // end loop over neighbors
            } // end loop over flipped atoms

            boltzmann = exp(-beta*dE);
            accumulators_[iMol].sample(boltzmann);
            ++iMol;
         } // end loop over molecules
      }
   }