Unverified Commit bce0c133 authored by Peter Eastman's avatar Peter Eastman Committed by GitHub
Browse files

Added computeCurrentPressure() to MonteCarloBarostat (#4881)

* Added computeCurrentPressure() to MonteCarloBarostat

* Use instantaneous temperature to compute pressure

* Added computeCurrentPressure() to MonteCarloAnisotropicBarostat

* Added computeCurrentPressure() to MonteCarloMembraneBarostat

* Fixed compilation error

* Fixed error in typemap

* Added documentation on computing pressure

* Fixed CUDA compilation errors

* Made test case more robust

* Made a test case more robust

* Added computeCurrentPressure() to MonteCarloFlexibleBarostat

* Fixed compilation error

* More documentation on computing pressure
parent 01e99e77
...@@ -630,6 +630,25 @@ probability. It does not itself perform temperature regulation, however. You ...@@ -630,6 +630,25 @@ probability. It does not itself perform temperature regulation, however. You
must use another mechanism along with it to maintain the temperature, such as must use another mechanism along with it to maintain the temperature, such as
LangevinIntegrator or AndersenThermostat. LangevinIntegrator or AndersenThermostat.
Another feature of MonteCarloBarostat is that it can compute the "instantaneous
pressure", which is defined based on the molecular virial:
.. math::
P_{inst} = \frac{1}{3V} (\sum_i m_i |\mathbf{v}_i|^2 ) - \frac{dE}{dV}
where the sum is taken over molecules, :math:`m_i` is the mass of the i'th molecule,
and :math:`\mathbf{v}_i` is the velocity of its center of mass. The derivative
of potential energy with respect to volume is approximated with a finite difference.
In most cases, the time average of the instantaneous pressure should equal the
pressure applied by the barostat. Fluctuations around the average can be extremely
large, however, especially when simulating incompressible materials like water.
A very long simulation may be required to accurately compute the average. There
also are situations where the average instantaneous pressure differs from the
applied pressure. For example, if the system contains immobile massless particles,
they will reduce the kinetic energy below what would be expected based on the
temperature, and hence reduce the calculated pressure.
MonteCarloAnisotropicBarostat MonteCarloAnisotropicBarostat
***************************** *****************************
...@@ -644,6 +663,18 @@ You can specify that the barostat should only be applied to certain axes of the ...@@ -644,6 +663,18 @@ You can specify that the barostat should only be applied to certain axes of the
box, keeping the other axes fixed. This is useful, for example, when doing box, keeping the other axes fixed. This is useful, for example, when doing
constant surface area simulations of membranes. constant surface area simulations of membranes.
Like MonteCarloBarostat, the anisotropic barostat can compute an instantaneous
pressure, but in this case the pressure is computed separately along each axis
according to the formula
.. math::
P_{inst} = \frac{1}{V} \sum_i m_i v_i^2 - \frac{dE}{dV}
where :math:`v_i` is the component of the i'th molecule's velocity along the
specified axis. The derivative :math:`dE/dV` is again computed with a finite
difference, but in this case :math:`dE` refers to the change in potential energy
when scaling the box size along only a single axis.
MonteCarloMembraneBarostat MonteCarloMembraneBarostat
************************** **************************
...@@ -677,6 +708,10 @@ behavior of the periodic box: ...@@ -677,6 +708,10 @@ behavior of the periodic box:
* inversely varying with the X and Y axes (so the total box volume does not * inversely varying with the X and Y axes (so the total box volume does not
change) change)
Like other barostats, MonteCarloMembraneBarostat can compute the instantaneous
pressure. It is computed separately for each axis using the same formula as
MonteCarloAnisotropicBarostat.
MonteCarloFlexibleBarostat MonteCarloFlexibleBarostat
************************** **************************
...@@ -687,6 +722,39 @@ is especially useful for simulations of bulk materials where the shape of a ...@@ -687,6 +722,39 @@ is especially useful for simulations of bulk materials where the shape of a
crystal's unit cell may not be known in advance, or could even change with time crystal's unit cell may not be known in advance, or could even change with time
as it transitions between phases. as it transitions between phases.
Computing the instantaneous pressure with MonteCarloFlexibleBarostat is more
complicated than other barostats. Because the box angles can change, it needs
to compute the full pressure tensor. Let :math:`\mathbf{a}`, :math:`\mathbf{b}`,
and :math:`\mathbf{c}` be the vectors defining the periodic box. They can be
assembled into a 3 by 3 box matrix
.. math::
h = \left[ {\begin{array}{ccc}
a_x & a_y & a_z \\
b_x & b_y & b_z \\
c_x & c_y & c_z \\
\end{array} } \right]
The pressure tensor is similarly represented by a 3 by 3 matrix. Its elements
are given by
.. math::
P_{jk} = \frac{1}{V} \left( \sum_i m_i v_{ij} v_{ik} - \frac{\partial E}{\partial h_{jk}} h_{jk} \right)
Because of the particular reduced form OpenMM uses for the box vectors (see Section
:numref:`periodic-boundary-conditions`), the elements above the diagonal are
always zero. In addition, the elements below the diagonal do not involve any
change to the box volume, and therefore are not affected by the applied pressure.
The six non-zero elements are as follows.
* The diagonal elements reflect the pressure acting to change the box size along
each axis. In equilibrium they should average to the applied pressure, although
fluctuations around the average can be very large.
* The elements below the diagonal reflect the pressure acting to change the box
angles. In equilibrium they should average to zero.
CMMotionRemover CMMotionRemover
*************** ***************
......
...@@ -4,6 +4,8 @@ Other Features ...@@ -4,6 +4,8 @@ Other Features
############## ##############
.. _periodic-boundary-conditions:
Periodic Boundary Conditions Periodic Boundary Conditions
**************************** ****************************
......
...@@ -1476,8 +1476,10 @@ public: ...@@ -1476,8 +1476,10 @@ public:
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param barostat the MonteCarloBarostat this kernel will be used for * @param barostat the MonteCarloBarostat this kernel will be used for
* @param rigidMolecules whether molecules should be kept rigid while scaling coordinates * @param rigidMolecules whether molecules should be kept rigid while scaling coordinates
* @param components the number of box components the barostat operates one (1 for isotropic scaling,
* 3 for anisotropic, 6 for both lengths and angles)
*/ */
virtual void initialize(const System& system, const Force& barostat, bool rigidMolecules=true) = 0; virtual void initialize(const System& system, const Force& barostat, int components, bool rigidMolecules=true) = 0;
/** /**
* Save the coordinates before attempting a Monte Carlo step. This allows us to restore them * Save the coordinates before attempting a Monte Carlo step. This allows us to restore them
* if the step is rejected. * if the step is rejected.
...@@ -1505,6 +1507,16 @@ public: ...@@ -1505,6 +1507,16 @@ public:
* @param context the context in which to execute this kernel * @param context the context in which to execute this kernel
*/ */
virtual void restoreCoordinates(ContextImpl& context) = 0; virtual void restoreCoordinates(ContextImpl& context) = 0;
/**
* Compute the kinetic energy of the system. If initialize() was called with rigidMolecules=true, this
* should include only the translational center of mass motion of molecules. Otherwise it should include
* the total kinetic energy of all particles. This is used when computing instantaneous pressure.
*
* @param context the context in which to execute this kernel
* @param ke a vector to store the kinetic energy components into. On output, its length will
* equal the number of components passed to initialize().
*/
virtual void computeKineticEnergy(ContextImpl& context, std::vector<double>& ke) = 0;
}; };
/** /**
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Lee-Ping Wang * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
...@@ -187,6 +187,21 @@ public: ...@@ -187,6 +187,21 @@ public:
bool usesPeriodicBoundaryConditions() const { bool usesPeriodicBoundaryConditions() const {
return false; return false;
} }
/**
* Compute the instantaneous pressure along each axis of a system to which this barostat
* is applied.
*
* The pressure is computed from the molecular virial, using a finite difference to
* calculate the derivative of potential energy with respect to volume. For most systems
* in equilibrium, the time average of the instantaneous pressure should equal the
* pressure applied by the barostat. Fluctuations around the average value can be
* extremely large, however, and it may take a very long simulation to accurately
* compute the average.
*
* @param context the Context for which to compute the current pressure
* @returns a vector containing the pressure along each axis
*/
Vec3 computeCurrentPressure(Context& context) const;
protected: protected:
ForceImpl* createImpl() const; ForceImpl* createImpl() const;
private: private:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE. * * USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
#include "Context.h"
#include "Force.h" #include "Force.h"
#include <string> #include <string>
#include "internal/windowsExport.h" #include "internal/windowsExport.h"
...@@ -143,6 +144,20 @@ public: ...@@ -143,6 +144,20 @@ public:
bool usesPeriodicBoundaryConditions() const { bool usesPeriodicBoundaryConditions() const {
return false; return false;
} }
/**
* Compute the instantaneous pressure of a system to which this barostat is applied.
*
* The pressure is computed from the molecular virial, using a finite difference to
* calculate the derivative of potential energy with respect to volume. For most systems
* in equilibrium, the time average of the instantaneous pressure should equal the
* pressure applied by the barostat. Fluctuations around the average value can be
* extremely large, however, and it may take a very long simulation to accurately
* compute the average.
*
* @param context the Context for which to compute the current pressure
* @returns the instantaneous pressure
*/
double computeCurrentPressure(Context& context) const;
protected: protected:
ForceImpl* createImpl() const; ForceImpl* createImpl() const;
private: private:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Sander Vandenhaute * * Authors: Peter Eastman, Sander Vandenhaute *
* Contributors: * * Contributors: *
* * * *
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "Force.h" #include "Force.h"
#include <string> #include <string>
#include <vector>
#include "internal/windowsExport.h" #include "internal/windowsExport.h"
namespace OpenMM { namespace OpenMM {
...@@ -172,6 +173,25 @@ public: ...@@ -172,6 +173,25 @@ public:
void setScaleMoleculesAsRigid(bool rigid) { void setScaleMoleculesAsRigid(bool rigid) {
scaleMoleculesAsRigid = rigid; scaleMoleculesAsRigid = rigid;
} }
/**
* Compute the instantaneous pressure of a system to which this barostat is applied.
* All six non-zero components of the pressure tensor are computed, where each one
* corresponds to the derivative of the energy with respect to an element of the
* matrix of box vectors.
*
* The pressure is computed from the molecular virial if getScaleMoleculesAsRigid()
* is true, or the atomic virial if it is false. It uses a finite difference to
* calculate the derivative of potential energy with respect to volume. For most systems
* in equilibrium, the time average of the instantaneous pressure should equal the
* pressure applied by the barostat. Fluctuations around the average value can be
* extremely large, however, and it may take a very long simulation to accurately
* compute the average.
*
* @param context the Context for which to compute the current pressure
* @param pressure on exit, this will contain the six non-zero elements of the
* pressure tensor in the order (XX, YY, ZZ, XY, XZ, YZ)
*/
void computeCurrentPressure(Context& context, std::vector<double>& pressure) const;
protected: protected:
ForceImpl* createImpl() const; ForceImpl* createImpl() const;
private: private:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
#include "Force.h" #include "Force.h"
#include "Vec3.h"
#include <string> #include <string>
#include "internal/windowsExport.h" #include "internal/windowsExport.h"
...@@ -238,6 +239,21 @@ public: ...@@ -238,6 +239,21 @@ public:
bool usesPeriodicBoundaryConditions() const { bool usesPeriodicBoundaryConditions() const {
return false; return false;
} }
/**
* Compute the instantaneous pressure along each axis of a system to which this barostat
* is applied.
*
* The pressure is computed from the molecular virial, using a finite difference to
* calculate the derivative of potential energy with respect to volume. For most systems
* in equilibrium, the time average of the instantaneous pressure should equal the
* pressure applied by the barostat. Fluctuations around the average value can be
* extremely large, however, and it may take a very long simulation to accurately
* compute the average.
*
* @param context the Context for which to compute the current pressure
* @returns a vector containing the pressure along each axis
*/
Vec3 computeCurrentPressure(Context& context) const;
protected: protected:
ForceImpl* createImpl() const; ForceImpl* createImpl() const;
private: private:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2019 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -57,6 +57,7 @@ public: ...@@ -57,6 +57,7 @@ public:
} }
std::map<std::string, double> getDefaultParameters(); std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
Vec3 computeCurrentPressure(ContextImpl& context);
private: private:
const MonteCarloAnisotropicBarostat& owner; const MonteCarloAnisotropicBarostat& owner;
int step, numAttempted[3], numAccepted[3]; int step, numAttempted[3], numAccepted[3];
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2019 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -57,6 +57,7 @@ public: ...@@ -57,6 +57,7 @@ public:
} }
std::map<std::string, double> getDefaultParameters(); std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
double computeCurrentPressure(ContextImpl& context);
private: private:
const MonteCarloBarostat& owner; const MonteCarloBarostat& owner;
int step, numAttempted, numAccepted; int step, numAttempted, numAccepted;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -57,7 +57,10 @@ public: ...@@ -57,7 +57,10 @@ public:
} }
std::map<std::string, double> getDefaultParameters(); std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
void computeCurrentPressure(ContextImpl& context, std::vector<double>& pressure);
private: private:
double computePressureComponent(ContextImpl& context, double delta, int component);
void setBoxVectors(ContextImpl& context, Vec3 a, Vec3 b, Vec3 c);
const MonteCarloFlexibleBarostat& owner; const MonteCarloFlexibleBarostat& owner;
int step, numAttempted, numAccepted; int step, numAttempted, numAccepted;
double lengthScale; double lengthScale;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2019 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -57,6 +57,7 @@ public: ...@@ -57,6 +57,7 @@ public:
} }
std::map<std::string, double> getDefaultParameters(); std::map<std::string, double> getDefaultParameters();
std::vector<std::string> getKernelNames(); std::vector<std::string> getKernelNames();
Vec3 computeCurrentPressure(ContextImpl& context);
private: private:
const MonteCarloMembraneBarostat& owner; const MonteCarloMembraneBarostat& owner;
int step, numAttempted[3], numAccepted[3]; int step, numAttempted[3], numAccepted[3];
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -61,3 +61,7 @@ void MonteCarloAnisotropicBarostat::setDefaultTemperature(double temp) { ...@@ -61,3 +61,7 @@ void MonteCarloAnisotropicBarostat::setDefaultTemperature(double temp) {
ForceImpl* MonteCarloAnisotropicBarostat::createImpl() const { ForceImpl* MonteCarloAnisotropicBarostat::createImpl() const {
return new MonteCarloAnisotropicBarostatImpl(*this); return new MonteCarloAnisotropicBarostatImpl(*this);
} }
Vec3 MonteCarloAnisotropicBarostat::computeCurrentPressure(Context& context) const {
return dynamic_cast<MonteCarloAnisotropicBarostatImpl&>(getImplInContext(context)).computeCurrentPressure(getContextImpl(context));
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2023 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Lee-Ping Wang * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
...@@ -50,7 +50,7 @@ void MonteCarloAnisotropicBarostatImpl::initialize(ContextImpl& context) { ...@@ -50,7 +50,7 @@ void MonteCarloAnisotropicBarostatImpl::initialize(ContextImpl& context) {
if (!context.getSystem().usesPeriodicBoundaryConditions()) if (!context.getSystem().usesPeriodicBoundaryConditions())
throw OpenMMException("A barostat cannot be used with a non-periodic system"); throw OpenMMException("A barostat cannot be used with a non-periodic system");
kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context); kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context);
kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner); kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner, 3);
Vec3 box[3]; Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]); context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2]; double volume = box[0][0]*box[1][1]*box[2][2];
...@@ -158,3 +158,49 @@ vector<string> MonteCarloAnisotropicBarostatImpl::getKernelNames() { ...@@ -158,3 +158,49 @@ vector<string> MonteCarloAnisotropicBarostatImpl::getKernelNames() {
return names; return names;
} }
Vec3 MonteCarloAnisotropicBarostatImpl::computeCurrentPressure(ContextImpl& context) {
Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2];
double delta = 1e-3;
int groups = context.getIntegrator().getIntegrationForceGroups();
kernel.getAs<ApplyMonteCarloBarostatKernel>().saveCoordinates(context);
vector<double> ke;
kernel.getAs<ApplyMonteCarloBarostatKernel>().computeKineticEnergy(context, ke);
double deltaVolume = volume*2*delta;
Vec3 pressure;
// Compute the pressure along each axis.
for (int axis = 0; axis < 3; axis++) {
// Compute the first energy.
Vec3 scale1(1, 1, 1);
scale1[axis] = 1.0+delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale1[0], box[1]*scale1[1], box[2]*scale1[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale1[0], scale1[1], scale1[2]);
double energy1 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Compute the second energy.
Vec3 scale2(1, 1, 1);
scale2[axis] = 1.0-delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale2[0], box[1]*scale2[1], box[2]*scale2[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale2[0]/scale1[0], scale2[1]/scale1[1], scale2[2]/scale1[2]);
double energy2 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Reset the box shape.
context.getOwner().setPeriodicBoxVectors(box[0], box[1], box[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, 1/scale2[0], 1/scale2[1], 1/scale2[2]);
// Compute the pressure.
pressure[axis] = (2.0*ke[axis]/volume - (energy1-energy2)/deltaVolume)/(AVOGADRO*1e-25);
}
// Restore the context to its original state.
kernel.getAs<ApplyMonteCarloBarostatKernel>().restoreCoordinates(context);
return pressure;
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -59,6 +59,10 @@ void MonteCarloBarostat::setDefaultTemperature(double temp) { ...@@ -59,6 +59,10 @@ void MonteCarloBarostat::setDefaultTemperature(double temp) {
defaultTemperature = temp; defaultTemperature = temp;
} }
double MonteCarloBarostat::computeCurrentPressure(Context& context) const {
return dynamic_cast<MonteCarloBarostatImpl&>(getImplInContext(context)).computeCurrentPressure(getContextImpl(context));
}
ForceImpl* MonteCarloBarostat::createImpl() const { ForceImpl* MonteCarloBarostat::createImpl() const {
return new MonteCarloBarostatImpl(*this); return new MonteCarloBarostatImpl(*this);
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2023 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -50,7 +50,7 @@ void MonteCarloBarostatImpl::initialize(ContextImpl& context) { ...@@ -50,7 +50,7 @@ void MonteCarloBarostatImpl::initialize(ContextImpl& context) {
if (!context.getSystem().usesPeriodicBoundaryConditions()) if (!context.getSystem().usesPeriodicBoundaryConditions())
throw OpenMMException("A barostat cannot be used with a non-periodic system"); throw OpenMMException("A barostat cannot be used with a non-periodic system");
kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context); kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context);
kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner); kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner, 1);
Vec3 box[3]; Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]); context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2]; double volume = box[0][0]*box[1][1]*box[2][2];
...@@ -126,3 +126,38 @@ vector<string> MonteCarloBarostatImpl::getKernelNames() { ...@@ -126,3 +126,38 @@ vector<string> MonteCarloBarostatImpl::getKernelNames() {
return names; return names;
} }
double MonteCarloBarostatImpl::computeCurrentPressure(ContextImpl& context) {
Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2];
double delta = 1e-3;
int groups = context.getIntegrator().getIntegrationForceGroups();
kernel.getAs<ApplyMonteCarloBarostatKernel>().saveCoordinates(context);
// Compute the first energy.
double scale1 = 1.0+delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale1, box[1]*scale1, box[2]*scale1);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale1, scale1, scale1);
double energy1 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Compute the second energy.
double scale2 = 1.0-delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale2, box[1]*scale2, box[2]*scale2);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale2/scale1, scale2/scale1, scale2/scale1);
double energy2 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Restore the context to its original state.
context.getOwner().setPeriodicBoxVectors(box[0], box[1], box[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().restoreCoordinates(context);
// Compute the pressure.
vector<double> ke;
kernel.getAs<ApplyMonteCarloBarostatKernel>().computeKineticEnergy(context, ke);
double deltaVolume = volume*(scale1*scale1*scale1 - scale2*scale2*scale2);
double pressure = (2.0/3.0)*ke[0]/volume - (energy1-energy2)/deltaVolume;
return pressure/(AVOGADRO*1e-25);
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Sander Vandenhaute * * Authors: Peter Eastman, Sander Vandenhaute *
* Contributors: * * Contributors: *
* * * *
...@@ -58,6 +58,10 @@ void MonteCarloFlexibleBarostat::setDefaultTemperature(double temp) { ...@@ -58,6 +58,10 @@ void MonteCarloFlexibleBarostat::setDefaultTemperature(double temp) {
defaultTemperature = temp; defaultTemperature = temp;
} }
void MonteCarloFlexibleBarostat::computeCurrentPressure(Context& context, std::vector<double>& pressure) const {
dynamic_cast<MonteCarloFlexibleBarostatImpl&>(getImplInContext(context)).computeCurrentPressure(getContextImpl(context), pressure);
}
ForceImpl* MonteCarloFlexibleBarostat::createImpl() const { ForceImpl* MonteCarloFlexibleBarostat::createImpl() const {
return new MonteCarloFlexibleBarostatImpl(*this); return new MonteCarloFlexibleBarostatImpl(*this);
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2023 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Sander Vandenhaute * * Authors: Peter Eastman, Sander Vandenhaute *
* Contributors: * * Contributors: *
* * * *
...@@ -50,7 +50,7 @@ void MonteCarloFlexibleBarostatImpl::initialize(ContextImpl& context) { ...@@ -50,7 +50,7 @@ void MonteCarloFlexibleBarostatImpl::initialize(ContextImpl& context) {
if (!context.getSystem().usesPeriodicBoundaryConditions()) if (!context.getSystem().usesPeriodicBoundaryConditions())
throw OpenMMException("A barostat cannot be used with a non-periodic system"); throw OpenMMException("A barostat cannot be used with a non-periodic system");
kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context); kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context);
kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner, owner.getScaleMoleculesAsRigid()); kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner, 6, owner.getScaleMoleculesAsRigid());
Vec3 box[3]; Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]); context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2]; double volume = box[0][0]*box[1][1]*box[2][2];
...@@ -152,3 +152,91 @@ vector<string> MonteCarloFlexibleBarostatImpl::getKernelNames() { ...@@ -152,3 +152,91 @@ vector<string> MonteCarloFlexibleBarostatImpl::getKernelNames() {
return names; return names;
} }
void MonteCarloFlexibleBarostatImpl::computeCurrentPressure(ContextImpl& context, vector<double>& pressure) {
pressure.resize(6);
Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2];
double delta = 1e-3;
kernel.getAs<ApplyMonteCarloBarostatKernel>().saveCoordinates(context);
vector<double> ke;
kernel.getAs<ApplyMonteCarloBarostatKernel>().computeKineticEnergy(context, ke);
// Compute each component of the pressure tensor.
for (int component = 0; component < 6; component++)
pressure[component] = (2.0*ke[component] - computePressureComponent(context, delta, component))/(volume*AVOGADRO*1e-25);
// Restore the context to its original state.
kernel.getAs<ApplyMonteCarloBarostatKernel>().restoreCoordinates(context);
}
double MonteCarloFlexibleBarostatImpl::computePressureComponent(ContextImpl& context, double delta, int component) {
Vec3 box[3], newBox[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]);
newBox[0] = box[0];
newBox[1] = box[1];
newBox[2] = box[2];
int scaleVec, scaleComponent;
if (component < 3)
scaleVec = scaleComponent = component;
else if (component == 3) {
scaleVec = 1;
scaleComponent = 0;
}
else if (component == 4) {
scaleVec = 2;
scaleComponent = 0;
}
else if (component == 5) {
scaleVec = 2;
scaleComponent = 1;
}
else
throw OpenMMException("Illegal component index");
int groups = context.getIntegrator().getIntegrationForceGroups();
// Compute the first energy.
Vec3 scale(1, 1, 1);
if (component < 3) {
scale[component] = 1.0+delta;
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale[0], scale[1], scale[2]);
}
newBox[scaleVec][scaleComponent] *= 1.0+delta;
setBoxVectors(context, newBox[0], newBox[1], newBox[2]);
double energy1 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Compute the second energy.
if (component < 3) {
scale[component] = (1.0-delta)/(1.0+delta);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale[0], scale[1], scale[2]);
}
newBox[scaleVec][scaleComponent] = box[scaleVec][scaleComponent]*(1.0-delta);
setBoxVectors(context, newBox[0], newBox[1], newBox[2]);
double energy2 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Reset the box shape.
if (component < 3) {
scale[component] = 1.0/(1.0-delta);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale[0], scale[1], scale[2]);
}
context.getOwner().setPeriodicBoxVectors(box[0], box[1], box[2]);
// Compute the potential energy contribution to this element of the pressure tensor.
double volume = box[0][0]*box[1][1]*box[2][2];
return (energy2-energy1)/(volume*2*delta);
}
void MonteCarloFlexibleBarostatImpl::setBoxVectors(ContextImpl& context, Vec3 a, Vec3 b, Vec3 c) {
// Convert the box vectors to reduced form.
c = c - b*round(c[1]/b[1]);
c = c - a*round(c[0]/a[0]);
b = b - a*round(b[0]/a[0]);
context.getOwner().setPeriodicBoxVectors(a, b, c);
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2021 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -66,3 +66,7 @@ void MonteCarloMembraneBarostat::setDefaultTemperature(double temp) { ...@@ -66,3 +66,7 @@ void MonteCarloMembraneBarostat::setDefaultTemperature(double temp) {
ForceImpl* MonteCarloMembraneBarostat::createImpl() const { ForceImpl* MonteCarloMembraneBarostat::createImpl() const {
return new MonteCarloMembraneBarostatImpl(*this); return new MonteCarloMembraneBarostatImpl(*this);
} }
Vec3 MonteCarloMembraneBarostat::computeCurrentPressure(Context& context) const {
return dynamic_cast<MonteCarloMembraneBarostatImpl&>(getImplInContext(context)).computeCurrentPressure(getContextImpl(context));
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2010-2023 Stanford University and the Authors. * * Portions copyright (c) 2010-2025 Stanford University and the Authors. *
* Authors: Peter Eastman, Lee-Ping Wang * * Authors: Peter Eastman, Lee-Ping Wang *
* Contributors: * * Contributors: *
* * * *
...@@ -50,7 +50,7 @@ void MonteCarloMembraneBarostatImpl::initialize(ContextImpl& context) { ...@@ -50,7 +50,7 @@ void MonteCarloMembraneBarostatImpl::initialize(ContextImpl& context) {
if (!context.getSystem().usesPeriodicBoundaryConditions()) if (!context.getSystem().usesPeriodicBoundaryConditions())
throw OpenMMException("A barostat cannot be used with a non-periodic system"); throw OpenMMException("A barostat cannot be used with a non-periodic system");
kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context); kernel = context.getPlatform().createKernel(ApplyMonteCarloBarostatKernel::Name(), context);
kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner); kernel.getAs<ApplyMonteCarloBarostatKernel>().initialize(context.getSystem(), owner, 3);
Vec3 box[3]; Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]); context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2]; double volume = box[0][0]*box[1][1]*box[2][2];
...@@ -157,3 +157,50 @@ vector<string> MonteCarloMembraneBarostatImpl::getKernelNames() { ...@@ -157,3 +157,50 @@ vector<string> MonteCarloMembraneBarostatImpl::getKernelNames() {
names.push_back(ApplyMonteCarloBarostatKernel::Name()); names.push_back(ApplyMonteCarloBarostatKernel::Name());
return names; return names;
} }
Vec3 MonteCarloMembraneBarostatImpl::computeCurrentPressure(ContextImpl& context) {
Vec3 box[3];
context.getPeriodicBoxVectors(box[0], box[1], box[2]);
double volume = box[0][0]*box[1][1]*box[2][2];
double delta = 1e-3;
int groups = context.getIntegrator().getIntegrationForceGroups();
kernel.getAs<ApplyMonteCarloBarostatKernel>().saveCoordinates(context);
vector<double> ke;
kernel.getAs<ApplyMonteCarloBarostatKernel>().computeKineticEnergy(context, ke);
double deltaVolume = volume*2*delta;
Vec3 pressure;
// Compute the pressure along each axis.
for (int axis = 0; axis < 3; axis++) {
// Compute the first energy.
Vec3 scale1(1, 1, 1);
scale1[axis] = 1.0+delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale1[0], box[1]*scale1[1], box[2]*scale1[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale1[0], scale1[1], scale1[2]);
double energy1 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Compute the second energy.
Vec3 scale2(1, 1, 1);
scale2[axis] = 1.0-delta;
context.getOwner().setPeriodicBoxVectors(box[0]*scale2[0], box[1]*scale2[1], box[2]*scale2[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, scale2[0]/scale1[0], scale2[1]/scale1[1], scale2[2]/scale1[2]);
double energy2 = context.getOwner().getState(State::Energy, false, groups).getPotentialEnergy();
// Reset the box shape.
context.getOwner().setPeriodicBoxVectors(box[0], box[1], box[2]);
kernel.getAs<ApplyMonteCarloBarostatKernel>().scaleCoordinates(context, 1/scale2[0], 1/scale2[1], 1/scale2[2]);
// Compute the pressure.
pressure[axis] = (2.0*ke[axis]/volume - (energy1-energy2)/deltaVolume)/(AVOGADRO*1e-25);
}
// Restore the context to its original state.
kernel.getAs<ApplyMonteCarloBarostatKernel>().restoreCoordinates(context);
return pressure;
}
...@@ -1211,11 +1211,13 @@ public: ...@@ -1211,11 +1211,13 @@ public:
/** /**
* Initialize the kernel. * Initialize the kernel.
* *
* @param system the System this kernel will be applied to * @param system the System this kernel will be applied to
* @param barostat the MonteCarloBarostat this kernel will be used for * @param barostat the MonteCarloBarostat this kernel will be used for
* @param rigidMolecules whether molecules should be kept rigid while scaling coordinates * @param rigidMolecules whether molecules should be kept rigid while scaling coordinates
* @param components the number of box components the barostat operates one (1 for isotropic scaling,
* 3 for anisotropic, 6 for both lengths and angles)
*/ */
void initialize(const System& system, const Force& barostat, bool rigidMolecules=true); void initialize(const System& system, const Force& barostat, int components, bool rigidMolecules=true);
/** /**
* Save the coordinates before attempting a Monte Carlo step. This allows us to restore them * Save the coordinates before attempting a Monte Carlo step. This allows us to restore them
* if the step is rejected. * if the step is rejected.
...@@ -1243,14 +1245,24 @@ public: ...@@ -1243,14 +1245,24 @@ public:
* @param context the context in which to execute this kernel * @param context the context in which to execute this kernel
*/ */
void restoreCoordinates(ContextImpl& context); void restoreCoordinates(ContextImpl& context);
/**
* Compute the kinetic energy of the system. If initialize() was called with rigidMolecules=true, this
* should include only the translational center of mass motion of molecules. Otherwise it should include
* the total kinetic energy of all particles. This is used when computing instantaneous pressure.
*
* @param context the context in which to execute this kernel
* @param ke a vector to store the kinetic energy components into. On output, its length will
* equal the number of components passed to initialize().
*/
void computeKineticEnergy(ContextImpl& context, std::vector<double>& ke);
private: private:
ComputeContext& cc; ComputeContext& cc;
bool hasInitializedKernels, rigidMolecules, atomsWereReordered; bool hasInitializedKernels, rigidMolecules, atomsWereReordered;
int numMolecules; int numMolecules, components;
ComputeArray savedPositions, savedFloatForces, savedLongForces, savedVelocities; ComputeArray savedPositions, savedFloatForces, savedLongForces, savedVelocities;
ComputeArray moleculeAtoms; ComputeArray moleculeAtoms, moleculeStartIndex;
ComputeArray moleculeStartIndex; std::vector<ComputeArray> energyBuffers;
ComputeKernel kernel; ComputeKernel kernel, kineticEnergyKernel;
std::vector<int> lastAtomOrder; std::vector<int> lastAtomOrder;
std::vector<mm_int4> lastPosCellOffsets; std::vector<mm_int4> lastPosCellOffsets;
}; };
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment