/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit. *
* See https://openmm.org/development. *
* *
* Portions copyright (c) 2008-2025 Stanford University and the Authors. *
* Authors: Peter Eastman *
* Contributors: *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as published *
* by the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see . *
* -------------------------------------------------------------------------- */
#include "openmm/common/CommonIntegrateNoseHooverStepKernel.h"
#include "openmm/common/CommonKernelUtilities.h"
#include "openmm/common/ContextSelector.h"
#include "openmm/Context.h"
#include "openmm/internal/ContextImpl.h"
#include "CommonKernelSources.h"
#include "SimTKOpenMMRealType.h"
using namespace OpenMM;
using namespace std;
void CommonIntegrateNoseHooverStepKernel::initialize(const System& system, const NoseHooverIntegrator& integrator) {
cc.initializeContexts();
ContextSelector selector(cc);
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
map defines;
defines["BOLTZ"] = cc.doubleToString(BOLTZ, true);
ComputeProgram program = cc.compileProgram(CommonKernelSources::noseHooverIntegrator, defines);
kernel1 = program->createKernel("integrateNoseHooverMiddlePart1");
kernel2 = program->createKernel("integrateNoseHooverMiddlePart2");
kernel3 = program->createKernel("integrateNoseHooverMiddlePart3");
kernel4 = program->createKernel("integrateNoseHooverMiddlePart4");
if (useDouble) {
oldDelta.initialize(cc, cc.getPaddedNumAtoms(), "oldDelta");
} else {
oldDelta.initialize(cc, cc.getPaddedNumAtoms(), "oldDelta");
}
kernelHardWall = program->createKernel("integrateNoseHooverHardWall");
prevMaxPairDistance = -1.0f;
maxPairDistanceBuffer.initialize(cc, 1, "maxPairDistanceBuffer");
int workGroupSize = std::min(cc.getMaxThreadBlockSize(), 512);
defines["WORK_GROUP_SIZE"] = std::to_string(workGroupSize);
defines["BEGIN_YS_LOOP"] = "const real arr[1] = {1.0};"
"for(int i=0;i<1;++i) {"
"const real ys = arr[i];";
defines["END_YS_LOOP"] = "}";
program = cc.compileProgram(CommonKernelSources::noseHooverChain, defines);
propagateKernels[1] = program->createKernel("propagateNoseHooverChain");
defines["BEGIN_YS_LOOP"] = "const real arr[3] = {0.828981543588751, -0.657963087177502, 0.828981543588751};"
"for(int i=0;i<3;++i) {"
"const real ys = arr[i];";
program = cc.compileProgram(CommonKernelSources::noseHooverChain, defines);
propagateKernels[3] = program->createKernel("propagateNoseHooverChain");
defines["BEGIN_YS_LOOP"] = "const real arr[5] = {0.2967324292201065, 0.2967324292201065, -0.186929716880426, 0.2967324292201065, 0.2967324292201065};"
"for(int i=0;i<5;++i) {"
"const real ys = arr[i];";
program = cc.compileProgram(CommonKernelSources::noseHooverChain, defines);
propagateKernels[5] = program->createKernel("propagateNoseHooverChain");
defines["BEGIN_YS_LOOP"] = "const real arr[7] = {0.784513610477560, 0.235573213359357, -1.17767998417887, 1.31518632068391,-1.17767998417887, 0.235573213359357, 0.784513610477560};"
"for(int i=0;i<7;++i) {"
"const real ys = arr[i];";
program = cc.compileProgram(CommonKernelSources::noseHooverChain, defines);
propagateKernels[7] = program->createKernel("propagateNoseHooverChain");
program = cc.compileProgram(CommonKernelSources::noseHooverChain, defines);
reduceEnergyKernel = program->createKernel("reduceEnergyPair");
computeHeatBathEnergyKernel = program->createKernel("computeHeatBathEnergy");
computeAtomsKineticEnergyKernel = program->createKernel("computeAtomsKineticEnergy");
computePairsKineticEnergyKernel = program->createKernel("computePairsKineticEnergy");
scaleAtomsVelocitiesKernel = program->createKernel("scaleAtomsVelocities");
scalePairsVelocitiesKernel = program->createKernel("scalePairsVelocities");
int energyBufferSize = cc.getEnergyBuffer().getSize();
if (cc.getUseDoublePrecision() || cc.getUseMixedPrecision())
energyBuffer.initialize(cc, energyBufferSize, "energyBuffer");
else
energyBuffer.initialize(cc, energyBufferSize, "energyBuffer");
}
void CommonIntegrateNoseHooverStepKernel::execute(ContextImpl& context, const NoseHooverIntegrator& integrator) {
// If the atom reordering has occured, the forces from the previous step are permuted and thus invalid.
// They need to be either sorted or recomputed; here we choose the latter.
if (cc.getAtomsWereReordered())
context.calcForcesAndEnergy(true, false, integrator.getIntegrationForceGroups());
ContextSelector selector(cc);
IntegrationUtilities& integration = cc.getIntegrationUtilities();
int paddedNumAtoms = cc.getPaddedNumAtoms();
double dt = integrator.getStepSize();
cc.getIntegrationUtilities().setNextStepSize(dt);
const auto& atomList = integrator.getAllThermostatedIndividualParticles();
const auto& pairList = integrator.getAllThermostatedPairs();
int numAtoms = atomList.size();
int numPairs = pairList.size();
int numParticles = numAtoms + 2*numPairs;
float maxPairDistance = integrator.getMaximumPairDistance();
// Make sure atom and pair metadata is uploaded and has the correct dimensions
if (prevMaxPairDistance != maxPairDistance) {
std::vector tmp(1, maxPairDistance);
maxPairDistanceBuffer.upload(tmp);
prevMaxPairDistance = maxPairDistance;
}
if (numAtoms !=0 && (!atomListBuffer.isInitialized() || atomListBuffer.getSize() != numAtoms)) {
if (atomListBuffer.isInitialized())
atomListBuffer.resize(atomList.size());
else
atomListBuffer.initialize(cc, atomList.size(), "atomListBuffer");
atomListBuffer.upload(atomList);
}
if (numPairs !=0 && (!pairListBuffer.isInitialized() || pairListBuffer.getSize() != numPairs)) {
if (pairListBuffer.isInitialized()) {
pairListBuffer.resize(pairList.size());
pairTemperatureBuffer.resize(pairList.size());
}
else {
pairListBuffer.initialize(cc, pairList.size(), "pairListBuffer");
pairTemperatureBuffer.initialize(cc, pairList.size(), "pairTemperatureBuffer");
}
std::vector tmp;
std::vector tmp2;
for(const auto &pair : pairList) {
tmp.push_back(mm_int2(std::get<0>(pair), std::get<1>(pair)));
tmp2.push_back(std::get<2>(pair));
}
pairListBuffer.upload(tmp);
pairTemperatureBuffer.upload(tmp2);
}
int totalAtoms = cc.getNumAtoms();
if (!hasInitializedKernels) {
hasInitializedKernels = true;
kernel1->addArg(numAtoms);
kernel1->addArg(numPairs);
kernel1->addArg(paddedNumAtoms);
kernel1->addArg(cc.getVelm());
kernel1->addArg(cc.getLongForceBuffer());
kernel1->addArg(integration.getStepSize());
kernel1->addArg(numAtoms > 0 ? atomListBuffer : cc.getEnergyBuffer()); // The array is not used if num == 0
kernel1->addArg(numPairs > 0 ? pairListBuffer : cc.getEnergyBuffer()); // The array is not used if num == 0
kernel2->addArg(totalAtoms);
kernel2->addArg(cc.getVelm());
kernel2->addArg(integration.getPosDelta());
kernel2->addArg(oldDelta);
kernel2->addArg(integration.getStepSize());
kernel3->addArg(totalAtoms);
kernel3->addArg(cc.getVelm());
kernel3->addArg(integration.getPosDelta());
kernel3->addArg(oldDelta);
kernel3->addArg(integration.getStepSize());
kernel4->addArg(totalAtoms);
kernel4->addArg(cc.getPosq());
kernel4->addArg(cc.getVelm());
kernel4->addArg(integration.getPosDelta());
kernel4->addArg(oldDelta);
kernel4->addArg(integration.getStepSize());
if (cc.getUseMixedPrecision())
kernel4->addArg(cc.getPosqCorrection());
if (numPairs > 0) {
kernelHardWall->addArg(numPairs);
kernelHardWall->addArg(maxPairDistanceBuffer);
kernelHardWall->addArg(integration.getStepSize());
kernelHardWall->addArg(cc.getPosq());
kernelHardWall->addArg(cc.getVelm());
kernelHardWall->addArg(pairListBuffer);
kernelHardWall->addArg(pairTemperatureBuffer);
if (cc.getUseMixedPrecision())
kernelHardWall->addArg(cc.getPosqCorrection());
}
}
/*
* Carry out the LF-middle integration (c.f. J. Phys. Chem. A 2019, 123, 6056−6079)
*/
// Velocity update
kernel1->execute(std::max(numAtoms, numPairs));
integration.applyVelocityConstraints(integrator.getConstraintTolerance());
// Position update
kernel2->execute(numParticles);
// Apply the thermostat
int numChains = integrator.getNumThermostats();
for(int chain = 0; chain < numChains; ++chain) {
const auto &thermostatChain = integrator.getThermostat(chain);
auto KEs = computeMaskedKineticEnergy(context, thermostatChain, false);
auto scaleFactors = propagateChain(context, thermostatChain, KEs, dt);
scaleVelocities(context, thermostatChain, scaleFactors);
}
// Position update
kernel3->execute(numParticles);
integration.applyConstraints(integrator.getConstraintTolerance());
// Apply constraint forces
kernel4->execute(numAtoms);
// Make sure any Drude-like particles have not wandered too far from home
if (numPairs > 0) kernelHardWall->execute(numPairs);
integration.computeVirtualSites();
// Update the time and step count.
cc.setTime(cc.getTime()+dt);
cc.setStepCount(cc.getStepCount()+1);
cc.reorderAtoms();
// Reduce UI lag.
flushPeriodically(cc);
}
double CommonIntegrateNoseHooverStepKernel::computeKineticEnergy(ContextImpl& context, const NoseHooverIntegrator& integrator) {
return cc.getIntegrationUtilities().computeKineticEnergy(0);
}
std::pair CommonIntegrateNoseHooverStepKernel::propagateChain(ContextImpl& context, const NoseHooverChain &nhc, std::pair kineticEnergies, double timeStep) {
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
int chainID = nhc.getChainID();
int nAtoms = nhc.getThermostatedAtoms().size();
int nPairs = nhc.getThermostatedPairs().size();
int chainLength = nhc.getChainLength();
int numYS = nhc.getNumYoshidaSuzukiTimeSteps();
int numMTS = nhc.getNumMultiTimeSteps();
if (numYS != 1 && numYS != 3 && numYS != 5 && numYS != 7) {
throw OpenMMException("Number of Yoshida Suzuki time steps has to be 1, 3, 5, or 7.");
}
if (!scaleFactorBuffer.isInitialized() || scaleFactorBuffer.getSize() == 0) {
if (useDouble) {
std::vector zeros{{0,0}};
if (scaleFactorBuffer.isInitialized())
scaleFactorBuffer.resize(1);
else
scaleFactorBuffer.initialize(cc, 1, "scaleFactorBuffer");
scaleFactorBuffer.upload(zeros);
}
else {
std::vector zeros{{0,0}};
if (scaleFactorBuffer.isInitialized())
scaleFactorBuffer.resize(1);
else
scaleFactorBuffer.initialize(cc, 1, "scaleFactorBuffer");
scaleFactorBuffer.upload(zeros);
}
}
if (!chainForces.isInitialized() || !chainMasses.isInitialized()) {
if (useDouble) {
std::vector zeros(chainLength,0);
if (chainForces.isInitialized()) {
chainMasses.resize(chainLength);
chainForces.resize(chainLength);
}
else {
chainMasses.initialize(cc, chainLength, "chainMasses");
chainForces.initialize(cc, chainLength, "chainForces");
}
chainMasses.upload(zeros);
chainForces.upload(zeros);
}
else {
std::vector zeros(chainLength,0);
if (chainForces.isInitialized()) {
chainMasses.resize(chainLength);
chainForces.resize(chainLength);
}
else {
chainMasses.initialize(cc, chainLength, "chainMasses");
chainForces.initialize(cc, chainLength, "chainForces");
}
chainMasses.upload(zeros);
chainForces.upload(zeros);
}
}
if (chainForces.getSize() < chainLength)
chainForces.resize(chainLength);
if (chainMasses.getSize() < chainLength)
chainMasses.resize(chainLength);
// N.B. We ignore the incoming kineticEnergy and grab it from the device buffer instead
if (nAtoms) {
if (!chainState.count(2*chainID))
chainState[2*chainID] = ComputeArray();
if (!chainState.at(2*chainID).isInitialized() || chainState.at(2*chainID).getSize() != chainLength) {
// We need to upload the Common array
if (useDouble) {
if (chainState.at(2*chainID).isInitialized())
chainState.at(2*chainID).resize(chainLength);
else
chainState.at(2*chainID).initialize(cc, chainLength, "chainState" + std::to_string(2*chainID));
std::vector zeros(chainLength, mm_double2(0.0, 0.0));
chainState.at(2*chainID).upload(zeros.data());
}
else {
if (chainState.at(2*chainID).isInitialized())
chainState.at(2*chainID).resize(chainLength);
else
chainState.at(2*chainID).initialize(cc, chainLength, "chainState" + std::to_string(2*chainID));
std::vector zeros(chainLength, mm_float2(0.0f, 0.0f));
chainState.at(2*chainID).upload(zeros.data());
}
}
}
if (nPairs) {
if (!chainState.count(2*chainID+1))
chainState[2*chainID+1] = ComputeArray();
if (!chainState.at(2*chainID+1).isInitialized() || chainState.at(2*chainID+1).getSize() != chainLength) {
// We need to upload the Common array
if (useDouble) {
if (chainState.at(2*chainID+1).isInitialized())
chainState.at(2*chainID+1).resize(chainLength);
else
chainState.at(2*chainID+1).initialize(cc, chainLength, "chainState" + std::to_string(2*chainID+1));
std::vector zeros(chainLength, mm_double2(0.0, 0.0));
chainState.at(2*chainID+1).upload(zeros.data());
}
else {
if (chainState.at(2*chainID+1).isInitialized())
chainState.at(2*chainID+1).resize(chainLength);
else
chainState.at(2*chainID+1).initialize(cc, chainLength, "chainState" + std::to_string(2*chainID+1));
std::vector zeros(chainLength, mm_float2(0.0f, 0.0f));
chainState.at(2*chainID+1).upload(zeros.data());
}
}
}
if (!hasInitializedPropagateKernel) {
hasInitializedPropagateKernel = true;
propagateKernels[numYS]->addArg(); // ChainState
propagateKernels[numYS]->addArg(kineticEnergyBuffer);
propagateKernels[numYS]->addArg(scaleFactorBuffer);
propagateKernels[numYS]->addArg(chainMasses);
propagateKernels[numYS]->addArg(chainForces);
propagateKernels[numYS]->addArg(); // ChainType
propagateKernels[numYS]->addArg(chainLength);
propagateKernels[numYS]->addArg(numMTS);
propagateKernels[numYS]->addArg(); // numDoFs
propagateKernels[numYS]->addArg((float)timeStep);
propagateKernels[numYS]->addArg(); // kT
propagateKernels[numYS]->addArg(); // frequency
}
if (nAtoms) {
int chainType = 0;
double temperature = nhc.getTemperature();
float frequency = nhc.getCollisionFrequency();
double kT = BOLTZ * temperature;
int numDOFs = nhc.getNumDegreesOfFreedom();
propagateKernels[numYS]->setArg(0, chainState[2*chainID]);
propagateKernels[numYS]->setArg(5, chainType);
propagateKernels[numYS]->setArg(8, numDOFs);
if (useDouble) {
propagateKernels[numYS]->setArg(10, kT);
} else {
propagateKernels[numYS]->setArg(10, (float)kT);
}
propagateKernels[numYS]->setArg(11, frequency);
propagateKernels[numYS]->execute(1, 1);
}
if (nPairs) {
int chainType = 1;
double relativeTemperature = nhc.getRelativeTemperature();
float relativeFrequency = nhc.getRelativeCollisionFrequency();
double kT = BOLTZ * relativeTemperature;
int ndf = 3*nPairs;
propagateKernels[numYS]->setArg(0, chainState[2*chainID+1]);
propagateKernels[numYS]->setArg(5, chainType);
propagateKernels[numYS]->setArg(8, ndf);
if (useDouble) {
propagateKernels[numYS]->setArg(10, kT);
} else {
propagateKernels[numYS]->setArg(10, (float)kT);
}
propagateKernels[numYS]->setArg(11, relativeFrequency);
propagateKernels[numYS]->execute(1, 1);
}
return {0, 0};
}
double CommonIntegrateNoseHooverStepKernel::computeHeatBathEnergy(ContextImpl& context, const NoseHooverChain &nhc) {
ContextSelector selector(cc);
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
int chainID = nhc.getChainID();
int chainLength = nhc.getChainLength();
bool absChainIsValid = chainState.count(2*chainID) != 0 &&
chainState[2*chainID].isInitialized() &&
chainState[2*chainID].getSize() == chainLength;
bool relChainIsValid = chainState.count(2*chainID+1) != 0 &&
chainState[2*chainID+1].isInitialized() &&
chainState[2*chainID+1].getSize() == chainLength;
if (!absChainIsValid && !relChainIsValid) return 0.0;
if (!heatBathEnergy.isInitialized() || heatBathEnergy.getSize() == 0) {
if (useDouble) {
std::vector one(1);
heatBathEnergy.initialize(cc, 1, "heatBathEnergy");
heatBathEnergy.upload(one);
}
else {
std::vector one(1);
heatBathEnergy.initialize(cc, 1, "heatBathEnergy");
heatBathEnergy.upload(one);
}
}
cc.clearBuffer(heatBathEnergy);
if(!hasInitializedHeatBathEnergyKernel) {
hasInitializedHeatBathEnergyKernel = true;
computeHeatBathEnergyKernel->addArg(heatBathEnergy);
computeHeatBathEnergyKernel->addArg(chainLength);
computeHeatBathEnergyKernel->addArg(); // numDOFs
computeHeatBathEnergyKernel->addArg(); // kT
computeHeatBathEnergyKernel->addArg(); // frequency
computeHeatBathEnergyKernel->addArg(); // chainstate
}
if (absChainIsValid) {
int numDOFs = nhc.getNumDegreesOfFreedom();
double temperature = nhc.getTemperature();
float frequency = nhc.getCollisionFrequency();
double kT = BOLTZ * temperature;
computeHeatBathEnergyKernel->setArg(2, numDOFs);
if (useDouble) {
computeHeatBathEnergyKernel->setArg(3, kT);
} else {
computeHeatBathEnergyKernel->setArg(3, (float)kT);
}
computeHeatBathEnergyKernel->setArg(4, frequency);
computeHeatBathEnergyKernel->setArg(5, chainState[2*chainID]);
computeHeatBathEnergyKernel->execute(1, 1);
}
if (relChainIsValid) {
int numDOFs = 3 * nhc.getThermostatedPairs().size();
double temperature = nhc.getRelativeTemperature();
float frequency = nhc.getRelativeCollisionFrequency();
double kT = BOLTZ * temperature;
computeHeatBathEnergyKernel->setArg(2, numDOFs);
if (useDouble) {
computeHeatBathEnergyKernel->setArg(3, kT);
} else {
computeHeatBathEnergyKernel->setArg(3, (float)kT);
}
computeHeatBathEnergyKernel->setArg(4, frequency);
computeHeatBathEnergyKernel->setArg(5, chainState[2*chainID+1]);
computeHeatBathEnergyKernel->execute(1, 1);
}
void * pinnedBuffer = cc.getPinnedBuffer();
heatBathEnergy.download(pinnedBuffer);
if (useDouble)
return *((double*) pinnedBuffer);
else
return *((float*) pinnedBuffer);
}
std::pair CommonIntegrateNoseHooverStepKernel::computeMaskedKineticEnergy(ContextImpl& context, const NoseHooverChain &nhc, bool downloadValue) {
ContextSelector selector(cc);
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
int chainID = nhc.getChainID();
const auto & nhcAtoms = nhc.getThermostatedAtoms();
const auto & nhcPairs = nhc.getThermostatedPairs();
int nAtoms = nhcAtoms.size();
int nPairs = nhcPairs.size();
if (nAtoms) {
if (!atomlists.count(chainID)) {
// We need to upload the Common array
atomlists[chainID] = ComputeArray();
atomlists[chainID].initialize(cc, nAtoms, "atomlist" + std::to_string(chainID));
atomlists[chainID].upload(nhcAtoms);
}
if (atomlists[chainID].getSize() != nAtoms) {
throw OpenMMException("Number of atoms changed. Cannot be handled by the same Nose-Hoover thermostat.");
}
}
if (nPairs) {
if (!pairlists.count(chainID)) {
// We need to upload the Common array
pairlists[chainID] = ComputeArray();
pairlists[chainID].initialize(cc, nPairs, "pairlist" + std::to_string(chainID));
std::vector int2vec;
for(const auto &p : nhcPairs) int2vec.push_back(mm_int2(p.first, p.second));
pairlists[chainID].upload(int2vec);
}
if (pairlists[chainID].getSize() != nPairs) {
throw OpenMMException("Number of thermostated pairs changed. Cannot be handled by the same Nose-Hoover thermostat.");
}
}
if (!kineticEnergyBuffer.isInitialized() || kineticEnergyBuffer.getSize() == 0) {
if (useDouble) {
std::vector zeros{{0,0}};
kineticEnergyBuffer.initialize(cc, 1, "kineticEnergyBuffer");
kineticEnergyBuffer.upload(zeros);
}
else {
std::vector zeros{{0,0}};
kineticEnergyBuffer.initialize(cc, 1, "kineticEnergyBuffer");
kineticEnergyBuffer.upload(zeros);
}
}
int workGroupSize = std::min(cc.getMaxThreadBlockSize(), 512);
if (!hasInitializedKineticEnergyKernel) {
hasInitializedKineticEnergyKernel = true;
computeAtomsKineticEnergyKernel->addArg(energyBuffer);
computeAtomsKineticEnergyKernel->addArg(); // nAtoms
computeAtomsKineticEnergyKernel->addArg(cc.getVelm());
computeAtomsKineticEnergyKernel->addArg(); // atom list
computePairsKineticEnergyKernel->addArg(energyBuffer);
computePairsKineticEnergyKernel->addArg(); // nPairs
computePairsKineticEnergyKernel->addArg(cc.getVelm());
computePairsKineticEnergyKernel->addArg(); // pair list
reduceEnergyKernel->addArg(energyBuffer);
reduceEnergyKernel->addArg(kineticEnergyBuffer);
reduceEnergyKernel->addArg((int) energyBuffer.getSize());
}
cc.clearBuffer(energyBuffer);
if (nAtoms) {
computeAtomsKineticEnergyKernel->setArg(1, nAtoms);
computeAtomsKineticEnergyKernel->setArg(3, atomlists[chainID]);
computeAtomsKineticEnergyKernel->execute(nAtoms);
}
if (nPairs) {
computePairsKineticEnergyKernel->setArg(1, nPairs);
computePairsKineticEnergyKernel->setArg(3, pairlists[chainID]);
computePairsKineticEnergyKernel->execute(nPairs);
}
reduceEnergyKernel->execute(workGroupSize, workGroupSize);
std::pair KEs = {0, 0};
if (downloadValue) {
if (useDouble) {
mm_double2 tmp;
kineticEnergyBuffer.download(&tmp);
KEs.first = tmp.x;
KEs.second = tmp.y;
}
else {
mm_float2 tmp;
kineticEnergyBuffer.download(&tmp);
KEs.first = tmp.x;
KEs.second = tmp.y;
}
}
return KEs;
}
void CommonIntegrateNoseHooverStepKernel::scaleVelocities(ContextImpl& context, const NoseHooverChain &nhc, std::pair scaleFactor) {
// For now we assume that the atoms and pairs info is valid, because compute{Atoms|Pairs}KineticEnergy must have been
// called before this kernel. If that ever ceases to be true, some sanity checks are needed here.
int chainID = nhc.getChainID();
int nAtoms = nhc.getThermostatedAtoms().size();
int nPairs = nhc.getThermostatedPairs().size();
if (!hasInitializedScaleVelocitiesKernel) {
hasInitializedScaleVelocitiesKernel = true;
scaleAtomsVelocitiesKernel->addArg(scaleFactorBuffer);
scaleAtomsVelocitiesKernel->addArg(); // nAtoms
scaleAtomsVelocitiesKernel->addArg(cc.getVelm());
scaleAtomsVelocitiesKernel->addArg(); // atom list
scalePairsVelocitiesKernel->addArg(scaleFactorBuffer);
scalePairsVelocitiesKernel->addArg(); // nPairs
scalePairsVelocitiesKernel->addArg(cc.getVelm());
scalePairsVelocitiesKernel->addArg(); // pair list
}
if (nAtoms) {
scaleAtomsVelocitiesKernel->setArg(1, nAtoms);
scaleAtomsVelocitiesKernel->setArg(3, atomlists[chainID]);
scaleAtomsVelocitiesKernel->execute(nAtoms);
}
if (nPairs) {
scalePairsVelocitiesKernel->setArg(1, nPairs);
scalePairsVelocitiesKernel->setArg(3, pairlists[chainID]);
scalePairsVelocitiesKernel->execute(nPairs);
}
}
void CommonIntegrateNoseHooverStepKernel::createCheckpoint(ContextImpl& context, ostream& stream) const {
ContextSelector selector(cc);
int numChains = chainState.size();
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
stream.write((char*) &numChains, sizeof(int));
for (auto& state : chainState){
int chainID = state.first;
int chainLength = state.second.getSize();
stream.write((char*) &chainID, sizeof(int));
stream.write((char*) &chainLength, sizeof(int));
if (useDouble) {
vector stateVec;
state.second.download(stateVec);
stream.write((char*) stateVec.data(), sizeof(mm_double2)*chainLength);
}
else {
vector stateVec;
state.second.download(stateVec);
stream.write((char*) stateVec.data(), sizeof(mm_float2)*chainLength);
}
}
}
void CommonIntegrateNoseHooverStepKernel::loadCheckpoint(ContextImpl& context, istream& stream) {
ContextSelector selector(cc);
int numChains;
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
stream.read((char*) &numChains, sizeof(int));
chainState.clear();
for (int i = 0; i < numChains; i++) {
int chainID, chainLength;
stream.read((char*) &chainID, sizeof(int));
stream.read((char*) &chainLength, sizeof(int));
if (useDouble) {
chainState[chainID] = ComputeArray();
chainState[chainID].initialize(cc, chainLength, "chainState" + to_string(chainID));
vector stateVec(chainLength);
stream.read((char*) &stateVec[0], sizeof(mm_double2)*chainLength);
chainState[chainID].upload(stateVec);
}
else {
chainState[chainID] = ComputeArray();
chainState[chainID].initialize(cc, chainLength, "chainState" + to_string(chainID));
vector stateVec(chainLength);
stream.read((char*) &stateVec[0], sizeof(mm_float2)*chainLength);
chainState[chainID].upload(stateVec);
}
}
}
void CommonIntegrateNoseHooverStepKernel::getChainStates(ContextImpl& context, vector >& positions, vector >& velocities) const {
ContextSelector selector(cc);
int numChains = chainState.size();
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
positions.clear();
velocities.clear();
positions.resize(numChains);
velocities.resize(numChains);
for (int i = 0; i < numChains; i++) {
const ComputeArray& state = chainState.at(i);
if (useDouble) {
vector stateVec;
state.download(stateVec);
for (int j = 0; j < stateVec.size(); j++) {
positions[i].push_back(stateVec[j].x);
velocities[i].push_back(stateVec[j].y);
}
}
else {
vector stateVec;
state.download(stateVec);
for (int j = 0; j < stateVec.size(); j++) {
positions[i].push_back((float) stateVec[j].x);
velocities[i].push_back((float) stateVec[j].y);
}
}
}
}
void CommonIntegrateNoseHooverStepKernel::setChainStates(ContextImpl& context, const vector >& positions, const vector >& velocities) {
ContextSelector selector(cc);
int numChains = positions.size();
bool useDouble = cc.getUseDoublePrecision() || cc.getUseMixedPrecision();
chainState.clear();
for (int i = 0; i < numChains; i++) {
int chainLength = positions[i].size();
chainState[i] = ComputeArray();
if (useDouble) {
chainState[i].initialize(cc, chainLength, "chainState"+cc.intToString(i));
vector stateVec;
for (int j = 0; j < chainLength; j++)
stateVec.push_back(mm_double2(positions[i][j], velocities[i][j]));
chainState[i].upload(stateVec);
}
else {
chainState[i].initialize(cc, chainLength, "chainState"+cc.intToString(i));
vector stateVec;
for (int j = 0; j < chainLength; j++)
stateVec.push_back(mm_float2((float) positions[i][j], (float) velocities[i][j]));
chainState[i].upload(stateVec);
}
}
}