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 }