/* * Zero forces on all local atoms and optionally on ghosts. */ void AtomStorage::zeroForces(bool zeroGhosts) { int factor = 2; // Zero forces for all local atoms if (nAtom() > atomCapacity_/factor) { atoms_.zeroForces(); // Optimization to allow sequential access } else { AtomIterator atomIter; begin(atomIter); for( ; atomIter.notEnd(); ++atomIter){ atomIter->force().zero(); } } // If using reverse communication, zero ghost atoms if (zeroGhosts && nGhost()) { if (nGhost() > ghostCapacity_/factor) { ghosts_.zeroForces(); // Optimization to allow sequential access } else { GhostIterator ghostIter; begin(ghostIter); for( ; ghostIter.notEnd(); ++ghostIter){ ghostIter->force().zero(); } } } }
/* * Record current positions of all local atoms and lock storage. */ void AtomStorage::makeSnapshot() { // Precondition if (!isCartesian()) { UTIL_THROW("Error: Coordinates not Cartesian in makeSnapshot"); } AtomIterator iter; int i = 0; for (begin(iter); iter.notEnd(); ++iter) { snapshot_[i] = iter->position(); ++i; } locked_ = true; }
/* * Transform all atomic coordinates from generalized to Cartesian. */ void AtomStorage::transformGenToCart(const Boundary& boundary) { if (isCartesian()) { UTIL_THROW("Error: Coordinates are Cartesian on entry"); } Vector r; if (nAtom()) { AtomIterator atomIter; for (begin(atomIter); atomIter.notEnd(); ++atomIter) { r = atomIter->position(); boundary.transformGenToCart(r, atomIter->position()); } } if (nGhost()) { GhostIterator ghostIter; for (begin(ghostIter); ghostIter.notEnd(); ++ghostIter) { r = ghostIter->position(); boundary.transformGenToCart(r, ghostIter->position()); } } isCartesian_ = true; }
/* * Return max. sq. displacement of local atoms on this node since snapshot. */ double AtomStorage::maxSqDisplacement() { if (!isCartesian()) { UTIL_THROW("Error: Coordinates not Cartesian in maxSqDisplacement"); } if (!locked_) { UTIL_THROW("Error: AtomStorage not locked in maxSqDisplacement"); } Vector dr; double norm; double max = 0.0; AtomIterator iter; int i = 0; for (begin(iter); iter.notEnd(); ++iter) { dr.subtract(iter->position(), snapshot_[i]); norm = dr.square(); if (norm > max) { max = norm; } ++i; } return max; }
/// Increment Structure Factor void AsymmSF::sample(long iStep) { if (isAtInterval(iStep)) { Vector position; std::complex<double> expFactor; double product; AtomIterator atomIter; int i, j, typeId; makeWaveVectors(); // Set all Fourier modes to zero for (i = 0; i < nWave_; ++i) { for (j = 0; j < nMode_; ++j) { fourierModes_(i, j) = std::complex<double>(0.0, 0.0); } } simulation().atomStorage().begin(atomIter); for ( ; atomIter.notEnd(); ++atomIter) { position = atomIter->position(); typeId = atomIter->typeId(); // Loop over wavevectors for (i = 0; i < nWave_; ++i) { product = position.dot(waveVectors_[i]); expFactor = exp( product*Constants::Im ); for (j = 0; j < nMode_; ++j) { fourierModes_(i, j) += modes_(j, typeId)*expFactor; } } } for (i = 0; i < nWave_; ++i) { for (j = 0; j < nMode_; ++j) { totalFourierModes_(i, j) = std::complex<double>(0.0, 0.0); } } #ifdef UTIL_MPI // Loop over wavevectors for (int i = 0; i < nWave_; ++i) { for (int j = 0; j < nMode_; ++j) { //Sum values from all processors. simulation().domain().communicator().Reduce(&fourierModes_(i, j), &totalFourierModes_(i, j), 1, MPI::DOUBLE_COMPLEX, MPI::SUM, 0); } } #else for (int i = 0; i < nWave_; ++i) { for (int j = 0; j < nMode_; ++j) { totalFourierModes_(i, j) = fourierModes_(i, j); } } #endif if (simulation().domain().isMaster()) { // Increment structure factors double volume = simulation().boundary().volume(); double norm; for (j = 0; j < nMode_; ++j) { double maxValue = 0.0; IntVector maxIntVector; double maxQ; for (i = 1; i < nWave_; ++i) { norm = std::norm(totalFourierModes_(i, j)); if ( double(norm/volume) >= maxValue ) { maxValue = double(norm/volume); maxIntVector = waveIntVectors_[i]; maxQ = waveVectors_[i].abs(); } structureFactors_(i, j) += norm/volume; } maximumValue_[j].insert(maximumValue_[j].end(), 1, maxValue); maximumWaveIntVector_[j].insert(maximumWaveIntVector_[j].end(), 1, maxIntVector); maximumQ_[j].insert(maximumQ_[j].end(), 1, maxQ); } } ++nSample_; } }
/* * Second half of two-step velocity-Verlet integrator. */ void NphIntegrator::integrateStep2() { Vector dv; double prefactor; // = 0.5*dt/mass AtomIterator atomIter; Vector v_fac_2 = Vector((1.0/2.0)*(nu_[0]+mtk_term_2_), (1.0/2.0)*(nu_[1]+mtk_term_2_), (1.0/2.0)*(nu_[2]+mtk_term_2_)); Vector exp_v_fac_2 = Vector(exp(-v_fac_2[0]*dt_), exp(-v_fac_2[1]*dt_), exp(-v_fac_2[2]*dt_)); // 2nd half of NPH atomStorage().begin(atomIter); for ( ; atomIter.notEnd(); ++atomIter) { prefactor = prefactors_[atomIter->typeId()]; dv.multiply(atomIter->force(), prefactor); dv[0] = dv[0] * exp_v_fac_[0]*sinhx_fac_v_[0]; dv[1] = dv[1] * exp_v_fac_[1]*sinhx_fac_v_[1]; dv[2] = dv[2] * exp_v_fac_[2]*sinhx_fac_v_[2]; Vector& v = atomIter->velocity(); v[0] = v[0] * exp_v_fac_2[0] + dv[0]; v[1] = v[1] * exp_v_fac_2[1] + dv[1]; v[2] = v[2] * exp_v_fac_2[2] + dv[2]; } Simulation& sys = simulation(); sys.velocitySignal().notify(); sys.computeKineticStress(); sys.computeKineticEnergy(); sys.computeVirialStress(); // Advance barostat if (sys.domain().isMaster()) { T_kinetic_ = sys.kineticEnergy()*2.0/ndof_; Tensor stress = sys.virialStress(); stress += sys.kineticStress(); P_curr_diag_ = Vector(stress(0,0), stress(1,1), stress(2,2)); double P_curr = (1.0/3.0)*stress.trace(); double mtk_term = (1.0/2.0)*dt_*T_kinetic_/W_; double V = sys.boundary().volume(); if (mode_ == Cubic) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr - P_target_) + mtk_term; nu_[1] = nu_[2] = nu_[0]; } else if (mode_ == Tetragonal) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[0] - P_target_) + mtk_term; nu_[1] += (1.0/2.0)*dt_*V/W_*((1.0/2.0)*(P_curr_diag_[1]+P_curr_diag_[2]) - P_target_) + mtk_term; nu_[2] = nu_[1]; } else if (mode_ == Orthorhombic) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[0] - P_target_) + mtk_term; nu_[1] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[1] - P_target_) + mtk_term; nu_[2] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[2] - P_target_) + mtk_term; } } #if 0 // output conserved quantity sys.computePotentialEnergies(); if (sys.domain().isMaster()) { std::ofstream file; file.open("NPH.log", std::ios::out | std::ios::app); double V = sys.boundary().volume(); double barostat_energy = W_*(nu_[0]*nu_[0]+ nu_[1]*nu_[1] + nu_[2]*nu_[2]); double pe = sys.potentialEnergy(); double ke = sys.kineticEnergy(); file << Dbl(V,20) << Dbl(pe,20) << Dbl(ke,20) << Dbl(barostat_energy,20) << std::endl; file.close(); } #endif #ifdef UTIL_MPI bcast(domain().communicator(), nu_,0); #endif }
/* * First half of two-step velocity-Verlet integrator. */ void NphIntegrator::integrateStep1() { Vector dv; AtomIterator atomIter; Simulation& sys = simulation(); sys.computeVirialStress(); sys.computeKineticStress(); sys.computeKineticEnergy(); if (sys.domain().isMaster()) { P_target_ = simulation().boundaryEnsemble().pressure(); T_kinetic_ = sys.kineticEnergy()*2.0/ndof_; Tensor stress = sys.virialStress(); stress += sys.kineticStress(); P_curr_diag_ = Vector(stress(0, 0), stress(1,1), stress(2,2)); double P_curr = (1.0/3.0)*stress.trace(); double mtk_term = (1.0/2.0)*dt_*T_kinetic_/W_; // Advance barostat double V = sys.boundary().volume(); if (mode_ == Cubic) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr - P_target_) + mtk_term; nu_[1] = nu_[2] = nu_[0]; } else if (mode_ == Tetragonal) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[0] - P_target_) + mtk_term; nu_[1] += (1.0/2.0)*dt_*V/W_*((1.0/2.0)*(P_curr_diag_[1]+P_curr_diag_[2]) - P_target_) + mtk_term; nu_[2] = nu_[1]; } else if (mode_ == Orthorhombic) { nu_[0] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[0] - P_target_) + mtk_term; nu_[1] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[1] - P_target_) + mtk_term; nu_[2] += (1.0/2.0)*dt_*V/W_*(P_curr_diag_[2] - P_target_) + mtk_term; } } #ifdef UTIL_MPI bcast(domain().communicator(), nu_,0); #endif // Precompute loop invariant quantities mtk_term_2_ = (nu_[0]+nu_[1]+nu_[2])/ndof_; Vector v_fac = Vector((1.0/4.0)*(nu_[0]+mtk_term_2_), (1.0/4.0)*(nu_[1]+mtk_term_2_), (1.0/4.0)*(nu_[2]+mtk_term_2_)); exp_v_fac_ = Vector(exp(-v_fac[0]*dt_), exp(-v_fac[1]*dt_), exp(-v_fac[2]*dt_)); Vector exp_v_fac_2 = Vector(exp(-(2.0*v_fac[0])*dt_), exp(-(2.0*v_fac[1])*dt_), exp(-(2.0*v_fac[2])*dt_)); Vector r_fac = Vector((1.0/2.0)*nu_[0], (1.0/2.0)*nu_[1], (1.0/2.0)*nu_[2]); Vector exp_r_fac = Vector(exp(r_fac[0]*dt_), exp(r_fac[1]*dt_), exp(r_fac[2]*dt_)); // Coefficients of sinh(x)/x = a_0 + a_2*x^2 + a_4*x^4 + a_6*x^6 + a_8*x^8 + a_10*x^10 const double a[] = {double(1.0), double(1.0/6.0), double(1.0/120.0), double(1.0/5040.0), double(1.0/362880.0), double(1.0/39916800.0)}; Vector arg_v = Vector(v_fac[0]*dt_, v_fac[1]*dt_, v_fac[2]*dt_); Vector arg_r = Vector(r_fac[0]*dt_, r_fac[1]*dt_, r_fac[2]*dt_); sinhx_fac_v_ = Vector(0.0,0.0,0.0); Vector sinhx_fac_r = Vector(0.0,0.0,0.0); Vector term_v = Vector(1.0,1.0,1.0); Vector term_r = Vector(1.0,1.0,1.0); for (unsigned int i = 0; i < 6; i++) { sinhx_fac_v_ += Vector(a[0]*term_v[0], a[1]*term_v[1], a[2]*term_v[2]); sinhx_fac_r += Vector(a[0]*term_r[0], a[1]*term_r[1], a[2]*term_r[2]); term_v = Vector(term_v[0] * arg_v[0] * arg_v[0], term_v[1] * arg_v[1] * arg_v[1], term_v[2] * arg_v[2] * arg_v[2]); term_r = Vector(term_r[0] * arg_r[0] * arg_r[0], term_r[1] * arg_r[1] * arg_r[1], term_r[2] * arg_r[2] * arg_r[2]); } // 1st half of NPH Vector vtmp; double prefactor; // = 0.5*dt/mass atomStorage().begin(atomIter); for ( ; atomIter.notEnd(); ++atomIter) { prefactor = prefactors_[atomIter->typeId()]; dv.multiply(atomIter->force(), prefactor); dv[0] = dv[0] * exp_v_fac_[0]*sinhx_fac_v_[0]; dv[1] = dv[1] * exp_v_fac_[1]*sinhx_fac_v_[1]; dv[2] = dv[2] * exp_v_fac_[2]*sinhx_fac_v_[2]; Vector& v = atomIter->velocity(); v[0] = v[0] * exp_v_fac_2[0] + dv[0]; v[1] = v[1] * exp_v_fac_2[1] + dv[1]; v[2] = v[2] * exp_v_fac_2[2] + dv[2]; vtmp[0] = v[0]*exp_r_fac[0] *sinhx_fac_r[0]; vtmp[1] = v[1]*exp_r_fac[1] *sinhx_fac_r[1]; vtmp[2] = v[2]*exp_r_fac[2] *sinhx_fac_r[2]; Vector& r = atomIter->position(); r[0] = r[0]*exp_r_fac[0]*exp_r_fac[0] + vtmp[0]*dt_; r[1] = r[1]*exp_r_fac[1]*exp_r_fac[1] + vtmp[1]*dt_; r[2] = r[2]*exp_r_fac[2]*exp_r_fac[2] + vtmp[2]*dt_; } // Advance box lengths Vector box_len_scale = Vector(exp(nu_[0]*dt_), exp(nu_[1]*dt_), exp(nu_[2]*dt_)); Vector L = sys.boundary().lengths(); L[0] *= box_len_scale[0]; L[1] *= box_len_scale[1]; L[2] *= box_len_scale[2]; // Update box lengths sys.boundary().setOrthorhombic(L); }