1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2026.
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.IntByReference;
42 import com.sun.jna.ptr.PointerByReference;
43
44 import java.nio.IntBuffer;
45
46 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addComputeGlobal;
47 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addComputePerDof;
48 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addComputeSum;
49 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addConstrainPositions;
50 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addConstrainVelocities;
51 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addGlobalVariable;
52 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addPerDofVariable;
53 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addTabulatedFunction;
54 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_addUpdateContextState;
55 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_beginIfBlock;
56 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_beginWhileBlock;
57 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_create;
58 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_destroy;
59 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_endBlock;
60 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getComputationStep;
61 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getGlobalVariable;
62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getGlobalVariableByName;
63 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getGlobalVariableName;
64 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getKineticEnergyExpression;
65 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getNumComputations;
66 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getNumGlobalVariables;
67 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getNumPerDofVariables;
68 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getNumTabulatedFunctions;
69 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getPerDofVariable;
70 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getPerDofVariableByName;
71 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getPerDofVariableName;
72 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getRandomNumberSeed;
73 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getTabulatedFunction;
74 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_getTabulatedFunctionName;
75 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setGlobalVariable;
76 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setGlobalVariableByName;
77 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setKineticEnergyExpression;
78 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setPerDofVariable;
79 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setPerDofVariableByName;
80 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_setRandomNumberSeed;
81 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomIntegrator_step;
82
83 /**
84 * This is an Integrator that can be used to implemented arbitrary, user defined
85 * integration algorithms. It is flexible enough to support a wide range of
86 * methods including both deterministic and stochastic integrators, Metropolized
87 * integrators, and integrators that must integrate additional quantities along
88 * with the particle positions and momenta.
89 *
90 * <p>To create an integration algorithm, you first define a set of variables the
91 * integrator will compute. Variables come in two types: global variables
92 * have a single value, while per-DOF variables have a value for every
93 * degree of freedom (x, y, or z coordinate of a particle). You can define as
94 * many variables as you want of each type. The value of any variable can be
95 * computed by the integration algorithm, or set directly by calling a method on
96 * the CustomIntegrator. All variables are persistent between integration
97 * steps; once a value is set, it keeps that value until it is changed by the
98 * user or recomputed in a later integration step.
99 *
100 * <p>Next, you define the algorithm as a series of computations. To execute a
101 * time step, the integrator performs the list of computations in order. Each
102 * computation updates the value of one global or per-DOF value. There are
103 * several types of computations that can be done:
104 *
105 * <ul>
106 * <li>Global: You provide a mathematical expression involving only global
107 * variables. It is evaluated and stored into a global variable.</li>
108 * <li>Per-DOF: You provide a mathematical expression involving both global and
109 * per-DOF variables. It is evaluated once for every degree of freedom, and
110 * the values are stored into a per-DOF variable.</li>
111 * <li>Sum: You provide a mathematical expression involving both global and
112 * per-DOF variables. It is evaluated once for every degree of freedom. All
113 * of those values are then added together, and the sum is stored into a global
114 * variable.</li>
115 * <li>Constrain Positions: The particle positions are updated so that all
116 * distance constraints are satisfied.</li>
117 * <li>Constrain Velocities: The particle velocities are updated so the net
118 * velocity along any constrained distance is 0.</li>
119 * </ul>
120 *
121 * <p>Like all integrators, CustomIntegrator ignores any particle whose mass is 0.
122 * It is skipped when doing per-DOF computations, and is not included when
123 * computing sums over degrees of freedom.
124 *
125 * <p>In addition to the variables you define by calling addGlobalVariable() and
126 * addPerDofVariable(), the integrator provides the following pre-defined
127 * variables:
128 *
129 * <ul>
130 * <li>dt: (global) This is the step size being used by the integrator.</li>
131 * <li>energy: (global, read-only) This is the current potential energy of the
132 * system.</li>
133 * <li>energy0, energy1, energy2, ...: (global, read-only) This is similar to
134 * energy, but includes only the contribution from forces in one force group.
135 * A single computation step may only depend on a single energy variable
136 * (energy, energy0, energy1, etc.).</li>
137 * <li>x: (per-DOF) This is the current value of the degree of freedom (the x,
138 * y, or z coordinate of a particle).</li>
139 * <li>v: (per-DOF) This is the current velocity associated with the degree of
140 * freedom (the x, y, or z component of a particle's velocity).</li>
141 * <li>f: (per-DOF, read-only) This is the current force acting on the degree of
142 * freedom (the x, y, or z component of the force on a particle).</li>
143 * <li>f0, f1, f2, ...: (per-DOF, read-only) This is similar to f, but includes
144 * only the contribution from forces in one force group. A single computation
145 * step may only depend on a single force variable (f, f0, f1, etc.).</li>
146 * <li>m: (per-DOF, read-only) This is the mass of the particle the degree of
147 * freedom is associated with.</li>
148 * <li>uniform: (either global or per-DOF, read-only) This is a uniformly
149 * distributed random number between 0 and 1. Every time an expression is
150 * evaluated, a different value will be used. When used in a per-DOF
151 * expression, a different value will be used for every degree of freedom.
152 * Note, however, that if this variable appears multiple times in a single
153 * expression, the same value is used everywhere it appears in that
154 * expression.</li>
155 * <li>gaussian: (either global or per-DOF, read-only) This is a Gaussian
156 * distributed random number with mean 0 and variance 1. Every time an expression
157 * is evaluated, a different value will be used. When used in a per-DOF
158 * expression, a different value will be used for every degree of freedom.
159 * Note, however, that if this variable appears multiple times in a single
160 * expression, the same value is used everywhere it appears in that
161 * expression.</li>
162 * <li>A global variable is created for every adjustable parameter defined
163 * in the integrator's Context.</li>
164 * </ul>
165 *
166 * <p>The following example uses a CustomIntegrator to implement a velocity Verlet
167 * integrator:
168 *
169 * <pre>{@code
170 * CustomIntegrator integrator = new CustomIntegrator(0.001);
171 * integrator.addComputePerDof("v", "v+0.5*dt*f/m");
172 * integrator.addComputePerDof("x", "x+dt*v");
173 * integrator.addComputePerDof("v", "v+0.5*dt*f/m");
174 * }</pre>
175 *
176 * <p>The first step updates the velocities based on the current forces.
177 * The second step updates the positions based on the new velocities, and the
178 * third step updates the velocities again. Although the first and third steps
179 * look identical, the forces used in them are different. You do not need to
180 * tell the integrator that; it will recognize that the positions have changed
181 * and know to recompute the forces automatically.
182 */
183 public class CustomIntegrator extends Integrator {
184
185 /**
186 * Constructor.
187 *
188 * @param dt The time step.
189 */
190 public CustomIntegrator(double dt) {
191 super(OpenMM_CustomIntegrator_create(dt));
192 }
193
194 /**
195 * Add a computation that computes a global value.
196 *
197 * @param variable The name of the variable to store the computed value in.
198 * @param expression The expression to evaluate.
199 * @return The index of the computation that was added.
200 */
201 public int addComputeGlobal(String variable, String expression) {
202 return OpenMM_CustomIntegrator_addComputeGlobal(pointer, variable, expression);
203 }
204
205 /**
206 * Add a per-DOF computation to this Integrator.
207 *
208 * @param variable The name of the variable to store the computed value in.
209 * @param expression The expression to evaluate.
210 * @return The index of the computation that was added.
211 */
212 public int addComputePerDof(String variable, String expression) {
213 return OpenMM_CustomIntegrator_addComputePerDof(pointer, variable, expression);
214 }
215
216 /**
217 * Add a computation that computes a sum over degrees of freedom.
218 *
219 * @param variable The name of the variable to store the computed value in.
220 * @param expression The expression to evaluate and sum.
221 * @return The index of the computation that was added.
222 */
223 public int addComputeSum(String variable, String expression) {
224 return OpenMM_CustomIntegrator_addComputeSum(pointer, variable, expression);
225 }
226
227 /**
228 * Add a position constraint to this Integrator.
229 *
230 * @return The index of the computation that was added.
231 */
232 public int addConstrainPositions() {
233 return OpenMM_CustomIntegrator_addConstrainPositions(pointer);
234 }
235
236 /**
237 * Add a velocity constraint to this Integrator.
238 *
239 * @return The index of the computation that was added.
240 */
241 public int addConstrainVelocities() {
242 return OpenMM_CustomIntegrator_addConstrainVelocities(pointer);
243 }
244
245 /**
246 * Add a global variable to this Integrator.
247 *
248 * @param name The name of the variable to create.
249 * @param initialValue The initial value of the variable.
250 * @return The index of the variable that was added.
251 */
252 public int addGlobalVariable(String name, double initialValue) {
253 return OpenMM_CustomIntegrator_addGlobalVariable(pointer, name, initialValue);
254 }
255
256 /**
257 * Add a per-DOF variable to this Integrator.
258 *
259 * @param name The name of the variable to create.
260 * @param initialValue The initial value of the variable.
261 * @return The index of the variable that was added.
262 */
263 public int addPerDofVariable(String name, double initialValue) {
264 return OpenMM_CustomIntegrator_addPerDofVariable(pointer, name, initialValue);
265 }
266
267 /**
268 * Add a tabulated function that may appear in expressions.
269 *
270 * @param name The name of the function as it appears in expressions.
271 * @param function A TabulatedFunction object defining the function.
272 * @return The index of the function that was added.
273 */
274 public int addTabulatedFunction(String name, PointerByReference function) {
275 return OpenMM_CustomIntegrator_addTabulatedFunction(pointer, name, function);
276 }
277
278 /**
279 * Add an update context state to this Integrator.
280 *
281 * @return The index of the computation that was added.
282 */
283 public int addUpdateContextState() {
284 return OpenMM_CustomIntegrator_addUpdateContextState(pointer);
285 }
286
287 /**
288 * Begin a new if block.
289 *
290 * @param condition The condition under which the block should be executed.
291 * @return The index of the computation that was added.
292 */
293 public int beginIfBlock(String condition) {
294 return OpenMM_CustomIntegrator_beginIfBlock(pointer, condition);
295 }
296
297 /**
298 * Begin a new while block.
299 *
300 * @param condition The condition under which the block should be executed repeatedly.
301 * @return The index of the computation that was added.
302 */
303 public int beginWhileBlock(String condition) {
304 return OpenMM_CustomIntegrator_beginWhileBlock(pointer, condition);
305 }
306
307 /**
308 * Destroy the integrator.
309 */
310 @Override
311 public void destroy() {
312 if (pointer != null) {
313 OpenMM_CustomIntegrator_destroy(pointer);
314 pointer = null;
315 }
316 }
317
318 /**
319 * End the most recently begun if or while block.
320 *
321 * @return The index of the computation that was added.
322 */
323 public int endBlock() {
324 return OpenMM_CustomIntegrator_endBlock(pointer);
325 }
326
327 /**
328 * Get information about a computation in the integrator.
329 *
330 * @param index The index of the computation to get.
331 * @param type The type of the computation (output).
332 * @param variable The variable the computation is stored in (output).
333 * @param expression The expression to evaluate (output).
334 */
335 public void getComputationStep(int index, IntByReference type, PointerByReference variable, PointerByReference expression) {
336 OpenMM_CustomIntegrator_getComputationStep(pointer, index, type, variable, expression);
337 }
338
339 /**
340 * Get information about a computation in the integrator.
341 *
342 * @param index The index of the computation to get.
343 * @param type The type of the computation (output).
344 * @param variable The variable the computation is stored in (output).
345 * @param expression The expression to evaluate (output).
346 */
347 public void getComputationStep(int index, IntBuffer type, PointerByReference variable, PointerByReference expression) {
348 OpenMM_CustomIntegrator_getComputationStep(pointer, index, type, variable, expression);
349 }
350
351 /**
352 * Get the value of a global variable.
353 *
354 * @param index The index of the variable to get.
355 * @return The value of the variable.
356 */
357 public double getGlobalVariable(int index) {
358 return OpenMM_CustomIntegrator_getGlobalVariable(pointer, index);
359 }
360
361 /**
362 * Get the value of a global variable, specified by name.
363 *
364 * @param name The name of the variable to get.
365 * @return The value of the variable.
366 */
367 public double getGlobalVariableByName(String name) {
368 return OpenMM_CustomIntegrator_getGlobalVariableByName(pointer, name);
369 }
370
371 /**
372 * Get the name of a global variable.
373 *
374 * @param index The index of the variable to get.
375 * @return The name of the variable.
376 */
377 public String getGlobalVariableName(int index) {
378 Pointer p = OpenMM_CustomIntegrator_getGlobalVariableName(pointer, index);
379 if (p == null) {
380 return null;
381 }
382 return p.getString(0);
383 }
384
385 /**
386 * Get the expression used to compute the kinetic energy.
387 *
388 * @return The expression used to compute the kinetic energy.
389 */
390 public String getKineticEnergyExpression() {
391 Pointer p = OpenMM_CustomIntegrator_getKineticEnergyExpression(pointer);
392 if (p == null) {
393 return null;
394 }
395 return p.getString(0);
396 }
397
398 /**
399 * Get the number of computation steps defined for this integrator.
400 *
401 * @return The number of computation steps.
402 */
403 public int getNumComputations() {
404 return OpenMM_CustomIntegrator_getNumComputations(pointer);
405 }
406
407 /**
408 * Get the number of global variables that have been defined.
409 *
410 * @return The number of global variables.
411 */
412 public int getNumGlobalVariables() {
413 return OpenMM_CustomIntegrator_getNumGlobalVariables(pointer);
414 }
415
416 /**
417 * Get the number of per-DOF variables that have been defined.
418 *
419 * @return The number of per-DOF variables.
420 */
421 public int getNumPerDofVariables() {
422 return OpenMM_CustomIntegrator_getNumPerDofVariables(pointer);
423 }
424
425 /**
426 * Get the number of tabulated functions that have been defined.
427 *
428 * @return The number of tabulated functions.
429 */
430 public int getNumTabulatedFunctions() {
431 return OpenMM_CustomIntegrator_getNumTabulatedFunctions(pointer);
432 }
433
434 /**
435 * Get the values of a per-DOF variable.
436 *
437 * @param index The index of the variable to get.
438 * @param variable The values of the variable (output).
439 */
440 public void getPerDofVariable(int index, PointerByReference variable) {
441 OpenMM_CustomIntegrator_getPerDofVariable(pointer, index, variable);
442 }
443
444 /**
445 * Get the values of a per-DOF variable, specified by name.
446 *
447 * @param name The name of the variable to get.
448 * @param variable The values of the variable (output).
449 */
450 public void getPerDofVariableByName(String name, PointerByReference variable) {
451 OpenMM_CustomIntegrator_getPerDofVariableByName(pointer, name, variable);
452 }
453
454 /**
455 * Get the name of a per-DOF variable.
456 *
457 * @param index The index of the variable to get.
458 * @return The name of the variable.
459 */
460 public String getPerDofVariableName(int index) {
461 Pointer p = OpenMM_CustomIntegrator_getPerDofVariableName(pointer, index);
462 if (p == null) {
463 return null;
464 }
465 return p.getString(0);
466 }
467
468 /**
469 * Get the random number seed. See setRandomNumberSeed() for details.
470 *
471 * @return The random number seed.
472 */
473 public int getRandomNumberSeed() {
474 return OpenMM_CustomIntegrator_getRandomNumberSeed(pointer);
475 }
476
477 /**
478 * Get a reference to a tabulated function that may appear in expressions.
479 *
480 * @param index The index of the function to get.
481 * @return The TabulatedFunction object defining the function.
482 */
483 public PointerByReference getTabulatedFunction(int index) {
484 return OpenMM_CustomIntegrator_getTabulatedFunction(pointer, index);
485 }
486
487 /**
488 * Get the name of a tabulated function that may appear in expressions.
489 *
490 * @param index The index of the function to get.
491 * @return The name of the function as it appears in expressions.
492 */
493 public String getTabulatedFunctionName(int index) {
494 Pointer p = OpenMM_CustomIntegrator_getTabulatedFunctionName(pointer, index);
495 if (p == null) {
496 return null;
497 }
498 return p.getString(0);
499 }
500
501 /**
502 * Set the value of a global variable.
503 *
504 * @param index The index of the variable to set.
505 * @param value The new value of the variable.
506 */
507 public void setGlobalVariable(int index, double value) {
508 OpenMM_CustomIntegrator_setGlobalVariable(pointer, index, value);
509 }
510
511 /**
512 * Set the value of a global variable, specified by name.
513 *
514 * @param name The name of the variable to set.
515 * @param value The new value of the variable.
516 */
517 public void setGlobalVariableByName(String name, double value) {
518 OpenMM_CustomIntegrator_setGlobalVariableByName(pointer, name, value);
519 }
520
521 /**
522 * Set the expression used to compute the kinetic energy.
523 *
524 * @param expression The expression used to compute the kinetic energy.
525 */
526 public void setKineticEnergyExpression(String expression) {
527 OpenMM_CustomIntegrator_setKineticEnergyExpression(pointer, expression);
528 }
529
530 /**
531 * Set the values of a per-DOF variable.
532 *
533 * @param index The index of the variable to set.
534 * @param variable The new values of the variable.
535 */
536 public void setPerDofVariable(int index, PointerByReference variable) {
537 OpenMM_CustomIntegrator_setPerDofVariable(pointer, index, variable);
538 }
539
540 /**
541 * Set the values of a per-DOF variable, specified by name.
542 *
543 * @param name The name of the variable to set.
544 * @param variable The new values of the variable.
545 */
546 public void setPerDofVariableByName(String name, PointerByReference variable) {
547 OpenMM_CustomIntegrator_setPerDofVariableByName(pointer, name, variable);
548 }
549
550 /**
551 * Set the random number seed. The precise meaning of this parameter is undefined, and is left up
552 * to each Platform to interpret in an appropriate way. It is guaranteed that if two simulations
553 * are run with different random number seeds, the sequence of random numbers will be different.
554 * On the other hand, no guarantees are made about the behavior of simulations that use the same
555 * seed. In particular, Platforms are permitted to use non-deterministic algorithms which produce
556 * different results on successive runs, even if those runs were initialized identically.
557 * <p>
558 * If seed is set to 0 (which is the default value assigned), a unique seed is chosen when a
559 * Context is created from this Force. This is done to ensure that each Context receives unique
560 * random seeds without you needing to set them explicitly.
561 *
562 * @param seed The random number seed.
563 */
564 public void setRandomNumberSeed(int seed) {
565 OpenMM_CustomIntegrator_setRandomNumberSeed(pointer, seed);
566 }
567
568 /**
569 * Advance a simulation through time by taking a series of time steps.
570 *
571 * @param steps The number of time steps to take.
572 */
573 public void step(int steps) {
574 OpenMM_CustomIntegrator_step(pointer, steps);
575 }
576 }