View Javadoc
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&amp;(1&lt;&lt;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 }