1 // ****************************************************************************** 2 // 3 // Title: Force Field X. 4 // Description: Force Field X - Software for Molecular Biophysics. 5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025. 6 // 7 // This file is part of Force Field X. 8 // 9 // Force Field X is free software; you can redistribute it and/or modify it 10 // under the terms of the GNU General Public License version 3 as published by 11 // the Free Software Foundation. 12 // 13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT 14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 // details. 17 // 18 // You should have received a copy of the GNU General Public License along with 19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple 20 // Place, Suite 330, Boston, MA 02111-1307 USA 21 // 22 // Linking this library statically or dynamically with other modules is making a 23 // combined work based on this library. Thus, the terms and conditions of the 24 // GNU General Public License cover the whole combination. 25 // 26 // As a special exception, the copyright holders of this library give you 27 // permission to link this library with independent modules to produce an 28 // executable, regardless of the license terms of these independent modules, and 29 // to copy and distribute the resulting executable under terms of your choice, 30 // provided that you also meet, for each linked independent module, the terms 31 // and conditions of the license of that module. An independent module is a 32 // module which is not derived from or based on this library. If you modify this 33 // library, you may extend this exception to your version of the library, but 34 // you are not obligated to do so. If you do not wish to do so, delete this 35 // exception statement from your version. 36 // 37 // ****************************************************************************** 38 package ffx.openmm; 39 40 import com.sun.jna.Pointer; 41 import com.sun.jna.ptr.PointerByReference; 42 import edu.uiowa.jopenmm.OpenMM_Vec3; 43 44 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_applyConstraints; 45 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_applyVelocityConstraints; 46 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_computeVirtualSites; 47 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_create_2; 48 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_destroy; 49 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getParameter; 50 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getParameters; 51 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getState; 52 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getState_2; 53 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getStepCount; 54 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getSystem; 55 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_getTime; 56 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_reinitialize; 57 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setParameter; 58 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setPeriodicBoxVectors; 59 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setPositions; 60 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setState; 61 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setStepCount; 62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setTime; 63 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setVelocities; 64 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Context_setVelocitiesToTemperature; 65 import static ffx.openmm.Vec3Array.toVec3Array; 66 67 /** 68 * A Context stores the complete state of a simulation. More specifically, it includes: 69 * 70 * <ul> 71 * <li>The current time</li> 72 * <li>The position of each particle</li> 73 * <li>The velocity of each particle</li> 74 * <li>The values of configurable parameters defined by Force objects in the System</li> 75 * </ul> 76 * <p> 77 * You can retrieve a snapshot of the current state at any time by calling getState(). This 78 * allows you to record the state of the simulation at various points, either for analysis 79 * or for checkpointing. getState() can also be used to retrieve the current forces on each 80 * particle and the current energy of the System. 81 */ 82 public class Context { 83 84 /** 85 * Context pointer. 86 */ 87 private PointerByReference pointer; 88 89 /** 90 * The integrator used for this context. 91 */ 92 protected Integrator integrator; 93 94 /** 95 * The platform used for this context. 96 */ 97 protected Platform platform; 98 99 /** 100 * Constructor. 101 */ 102 public Context() { 103 pointer = null; 104 integrator = null; 105 platform = null; 106 } 107 108 /** 109 * Construct a new Context in which to run a simulation, explicitly specifying what Platform should be used 110 * to perform calculations. 111 * 112 * @param system the System which will be simulated 113 * @param integrator the Integrator which will be used to simulate the System 114 * @param platform the Platform to use for calculations 115 */ 116 public Context(System system, Integrator integrator, Platform platform) { 117 pointer = OpenMM_Context_create_2(system.getPointer(), integrator.getPointer(), platform.getPointer()); 118 this.integrator = integrator; 119 this.platform = platform; 120 } 121 122 /** 123 * Update the positions of particles so that all distance constraints are satisfied. This also recomputes 124 * the locations of all virtual sites. 125 * 126 * @param tol the distance tolerance within which constraints must be satisfied. 127 */ 128 public void applyConstraints(double tol) { 129 OpenMM_Context_applyConstraints(pointer, tol); 130 } 131 132 /** 133 * Update the velocities of particles so the net velocity of each constrained distance is zero. 134 * 135 * @param tol the velocity tolerance within which constraints must be satisfied. 136 */ 137 public void applyVelocityConstraints(double tol) { 138 OpenMM_Context_applyVelocityConstraints(pointer, tol); 139 } 140 141 /** 142 * Recompute the locations of all virtual sites. There is rarely a reason to call 143 * this, since virtual sites are also updated by applyConstraints(). This is only 144 * for the rare situations when you want to enforce virtual sites but <i>not</i> 145 * constraints. 146 */ 147 public void computeVirtualSites() { 148 OpenMM_Context_computeVirtualSites(pointer); 149 } 150 151 /** 152 * Destroy the context. 153 */ 154 public void destroy() { 155 if (integrator != null) { 156 integrator.destroy(); 157 integrator = null; 158 } 159 if (pointer != null) { 160 OpenMM_Context_destroy(pointer); 161 pointer = null; 162 // The platform is handled by the Context destroy method. 163 platform = null; 164 } 165 } 166 167 /** 168 * Get the value of an adjustable parameter defined by a Force object in the System. 169 * 170 * @param name the name of the parameter to get 171 * @return the value of the parameter 172 */ 173 public double getParameter(String name) { 174 return OpenMM_Context_getParameter(pointer, name); 175 } 176 177 /** 178 * Get the value of an adjustable parameter defined by a Force object in the System. 179 * 180 * @param name the name of the parameter to get 181 * @return the value of the parameter 182 */ 183 public double getParameter(Pointer name) { 184 return OpenMM_Context_getParameter(pointer, name); 185 } 186 187 /** 188 * Get all adjustable parameters that have been defined by Force objects in the System, along 189 * with their current values. 190 * 191 * @return a PointerByReference to a map containing the values of all parameters 192 */ 193 public PointerByReference getParameters() { 194 return OpenMM_Context_getParameters(pointer); 195 } 196 197 /** 198 * Get the Platform being used for calculations. 199 * 200 * @return the Platform being used for calculations 201 */ 202 public Platform getPlatform() { 203 return platform; 204 } 205 206 /** 207 * Get Integrator being used by this context. 208 * 209 * @return the Integrator being used by this context 210 */ 211 public Integrator getIntegrator() { 212 return integrator; 213 } 214 215 /** 216 * Get the pointer to the context. 217 * 218 * @return The pointer to the context. 219 */ 220 public PointerByReference getPointer() { 221 return pointer; 222 } 223 224 /** 225 * Get a State object recording the current state information stored in this context. 226 * 227 * @param types the set of data types which should be stored in the State object. This 228 * should be a union of DataType values, e.g. (State::Positions | State::Velocities). 229 * @param enforcePeriodicBox if false, the position of each particle will be whatever position 230 * is stored in the Context, regardless of periodic boundary conditions. If true, particle 231 * positions will be translated so the center of every molecule lies in the same periodic box. 232 * @return a State object recording the current state information 233 */ 234 public State getState(int types, int enforcePeriodicBox) { 235 return new State(OpenMM_Context_getState(pointer, types, enforcePeriodicBox)); 236 } 237 238 /** 239 * Get a State object recording the current state information stored in this context. 240 * 241 * @param types the set of data types which should be stored in the State object. This 242 * should be a union of DataType values, e.g. (State::Positions | State::Velocities). 243 * @param enforcePeriodicBox if false, the position of each particle will be whatever position 244 * is stored in the Context, regardless of periodic boundary conditions. If true, particle 245 * positions will be translated so the center of every molecule lies in the same periodic box. 246 * @param groups a set of bit flags for which force groups to include when computing forces 247 * and energies. Group i will be included if (groups&(1<<i)) != 0. The default value includes all groups. 248 * @return a State object recording the current state information 249 */ 250 public State getState(int types, int enforcePeriodicBox, int groups) { 251 return new State(OpenMM_Context_getState_2(pointer, types, enforcePeriodicBox, groups)); 252 } 253 254 /** 255 * Get the current step count. 256 * 257 * @return the current step count 258 */ 259 public long getStepCount() { 260 return OpenMM_Context_getStepCount(pointer); 261 } 262 263 /** 264 * Get System being simulated in this context. 265 * 266 * @return the System being simulated in this context 267 */ 268 public System getSystem() { 269 PointerByReference systemPointer = OpenMM_Context_getSystem(pointer); 270 return new System(systemPointer); 271 } 272 273 /** 274 * Get the current time of the simulation (in picoseconds). 275 * 276 * @return the current time of the simulation (in picoseconds) 277 */ 278 public double getTime() { 279 return OpenMM_Context_getTime(pointer); 280 } 281 282 /** 283 * Does the context have a pointer? 284 * 285 * @return True if the context pointer is not null. 286 */ 287 public boolean hasContextPointer() { 288 return pointer != null; 289 } 290 291 /** 292 * When a Context is created, it caches information about the System being simulated 293 * and the Force objects contained in it. This means that, if the System or Forces are then 294 * modified, the Context does not see the changes. Call reinitialize() to force 295 * the Context to rebuild its internal representation of the System and pick up any changes 296 * that have been made. 297 * <p> 298 * This is an expensive operation, so you should try to avoid calling it too frequently. 299 * Most Force classes have an updateParametersInContext() method that provides a less expensive 300 * way of updating certain types of information. However, this method is the only way to 301 * make some types of changes, so it is sometimes necessary to call it. 302 * <p> 303 * By default, reinitializing a Context causes all state information (positions, velocities, 304 * etc.) to be discarded. You can optionally tell it to try to preserve state information. 305 * It does this by internally creating a checkpoint, then reinitializing the Context, then 306 * loading the checkpoint. Be aware that if the System has changed in a way that prevents 307 * the checkpoint from being loaded (such as changing the number of particles), this will 308 * throw an exception and the state information will be lost. 309 * 310 * @param preserveState if true, try to preserve state information; if false, discard all state information 311 */ 312 public void reinitialize(int preserveState) { 313 if (pointer != null) { 314 OpenMM_Context_reinitialize(pointer, preserveState); 315 } 316 } 317 318 /** 319 * Set the value of an adjustable parameter defined by a Force object in the System. 320 * 321 * @param name the name of the parameter to set 322 * @param value the value of the parameter 323 */ 324 public void setParameter(String name, double value) { 325 OpenMM_Context_setParameter(pointer, name, value); 326 } 327 328 /** 329 * Set the value of an adjustable parameter defined by a Force object in the System. 330 * 331 * @param name the name of the parameter to set 332 * @param value the value of the parameter 333 */ 334 public void setParameter(Pointer name, double value) { 335 OpenMM_Context_setParameter(pointer, name, value); 336 } 337 338 /** 339 * Set the vectors defining the axes of the periodic box (measured in nm). They will affect 340 * any Force that uses periodic boundary conditions. 341 * <p> 342 * Triclinic boxes are supported, but the vectors must satisfy certain requirements. In particular, 343 * a must point in the x direction, b must point "mostly" in the y direction, and c must point "mostly" 344 * in the z direction. See the documentation for details. 345 * 346 * @param a the vector defining the first edge of the periodic box 347 * @param b the vector defining the second edge of the periodic box 348 * @param c the vector defining the third edge of the periodic box 349 */ 350 public void setPeriodicBoxVectors(OpenMM_Vec3 a, OpenMM_Vec3 b, OpenMM_Vec3 c) { 351 OpenMM_Context_setPeriodicBoxVectors(pointer, a, b, c); 352 } 353 354 /** 355 * Set the positions of all particles in the System (measured in nm). This method simply sets the positions 356 * without checking to see whether they satisfy distance constraints. If you want constraints to be 357 * enforced, call applyConstraints() after setting the positions. 358 * 359 * @param positions a vector whose length equals the number of particles in the System. The i'th element 360 * contains the position of the i'th particle. 361 */ 362 public void setPositions(double[] positions) { 363 Vec3Array vec3Array = toVec3Array(positions); 364 OpenMM_Context_setPositions(pointer, vec3Array.getPointer()); 365 vec3Array.destroy(); 366 } 367 368 /** 369 * Copy information from a State object into this Context. This restores the Context to 370 * approximately the same state it was in when the State was created. If the State does not include 371 * a piece of information (e.g. positions or velocities), that aspect of the Context is 372 * left unchanged. 373 * <p> 374 * Even when all possible information is included in the State, the effect of calling this method 375 * is still less complete than loadCheckpoint(). For example, it does not restore the internal 376 * states of random number generators. On the other hand, it has the advantage of not being hardware 377 * specific. 378 * 379 * @param state the State object to copy information from 380 */ 381 public void setState(State state) { 382 OpenMM_Context_setState(pointer, state.getPointer()); 383 } 384 385 /** 386 * Set the current step count. 387 * 388 * @param steps the current step count 389 */ 390 public void setStepCount(long steps) { 391 OpenMM_Context_setStepCount(pointer, steps); 392 } 393 394 /** 395 * Set the current time of the simulation (in picoseconds). 396 * 397 * @param time the current time of the simulation (in picoseconds) 398 */ 399 public void setTime(double time) { 400 OpenMM_Context_setTime(pointer, time); 401 } 402 403 /** 404 * Set the velocities of all particles in the System (measured in nm/picosecond). 405 * 406 * @param velocities a vector whose length equals the number of particles in the System. The i'th element 407 * contains the velocity of the i'th particle. 408 */ 409 public void setVelocities(double[] velocities) { 410 Vec3Array velArray = toVec3Array(velocities); 411 OpenMM_Context_setVelocities(pointer, velArray.getPointer()); 412 velArray.destroy(); 413 } 414 415 /** 416 * Set the velocities of all particles in the System to random values chosen from a Boltzmann 417 * distribution at a given temperature. 418 * 419 * @param temperature the temperature for which to select the velocities (measured in Kelvin) 420 * @param randomSeed the random number seed to use when selecting velocities 421 */ 422 public void setVelocitiesToTemperature(double temperature, int randomSeed) { 423 OpenMM_Context_setVelocitiesToTemperature(pointer, temperature, randomSeed); 424 } 425 426 /** 427 * Update the context. 428 * 429 * @param system The system to simulate. 430 * @param integrator The integrator to use for simulating the system. 431 * @param platform The platform to use for performing computations. 432 */ 433 public void updateContext(System system, Integrator integrator, Platform platform) { 434 // Destroy the old context. 435 destroy(); 436 pointer = OpenMM_Context_create_2(system.getPointer(), integrator.getPointer(), platform.getPointer()); 437 this.integrator = integrator; 438 this.platform = platform; 439 } 440 }