/* * 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); } } } }
/// 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); } } } }
/* * 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 }
/* * 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); } }
// 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 }
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; }
/* * 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 } }
/* * 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; }
/* * Empty implementation, to be filled by derived classes. */ bool ReplicaMove::move() { MPI::Request request[8]; MPI::Status status; double ptParam, myWeight, ptWeight; int isLeft, iAccept, myPort, ptPort; System::MoleculeIterator molIter; Molecule::AtomIterator atomPtr; int iA; // Default value for idle processor isLeft = -1; iAccept = 0; // Idenfity active processor and its parter ID; left one has smaller ID. if ((myId_ + xFlag_)%2 == 0 && myId_ < nProcs_-1) { isLeft = 1; ptId_ = myId_ + 1; } else if ((myId_ + xFlag_)%2 == 1 && myId_ > 0) { isLeft = 0; ptId_ = myId_ - 1; } // Start to talk with partner if (isLeft == 1 || isLeft == 0) { // Set the port value for message tag myPort = myId_%2; ptPort = ptId_%2; // Update accumulator repxAttempt_[isLeft] += 1; // Exchange coupling parameters with partner request[0] = communicatorPtr_->Irecv(&ptParam, 1, MPI::DOUBLE, ptId_, TagParam[ptPort]); request[1] = communicatorPtr_->Isend(&myParam_, 1, MPI::DOUBLE, ptId_, TagParam[myPort]); // Synchronizing request[0].Wait(); request[1].Wait(); // Evaluating energy change. 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(); // Metropolis test iAccept = system().simulation().random(). metropolis(exp(-myWeight - ptWeight)) ? 1 : 0; // Output the two weights and the metropolis of their summation. // energyFile_ << setw(10) << myWeight << setw(10) // << ptWeight << setw(2) << iAccept << std::endl; // Send decision to the right box request[3] = communicatorPtr_->Isend(&iAccept, 1, MPI::INT, ptId_, TagDecision[myPort]); request[3].Wait(); } else { // Send energy difference to the left box request[2] = communicatorPtr_->Isend(&myWeight, 1, MPI::DOUBLE, ptId_, TagDecision[myPort]); request[2].Wait(); // 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; // Pack atomic positions and types. iA = 0; for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){ for (system().begin(iSpec, molIter); !molIter.isEnd(); ++molIter){ for (molIter->begin(atomPtr); !atomPtr.isEnd(); ++atomPtr) { myPositionPtr_[iA] = atomPtr->position(); iA++; } } } // Accomodate new configuration. request[4] = communicatorPtr_->Irecv(ptPositionPtr_, iA, MpiTraits<Vector>::type, ptId_, TagConfig[ptPort]); // Send old configuration. request[5] = communicatorPtr_->Isend(myPositionPtr_, iA, MpiTraits<Vector>::type, ptId_, TagConfig[myPort]); request[4].Wait(); request[5].Wait(); // Adopt the new atomic positions. iA = 0; for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){ for (system().begin(iSpec, molIter); !molIter.isEnd(); ++molIter){ for (molIter->begin(atomPtr); !atomPtr.isEnd(); ++atomPtr) { atomPtr->position() = ptPositionPtr_[iA]; ++iA; } } } // Notify component observers. //notifyObservers(ptId_); Notifier<int>::notifyObservers(ptId_); } else { } } // Output results needed to build exchange profile. outputFile_ << ((isLeft != -1 && iAccept == 1) ? ptId_ : myId_) << std::endl; // Flip the value of xFlag_ before exit. xFlag_ = (xFlag_ == 0 ? 1 : 0); return (iAccept == 1 ? true : false); }
/* * 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); }
/* * 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(); }
/* * 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; }