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.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 }