CustomIntegrator.h 32.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
#ifndef OPENMM_CUSTOMINTEGRATOR_H_
#define OPENMM_CUSTOMINTEGRATOR_H_

/* -------------------------------------------------------------------------- *
 *                                   OpenMM                                   *
 * -------------------------------------------------------------------------- *
 * This is part of the OpenMM molecular simulation toolkit originating from   *
 * Simbios, the NIH National Center for Physics-Based Simulation of           *
 * Biological Structures at Stanford, funded under the NIH Roadmap for        *
 * Medical Research, grant U54 GM072970. See https://simtk.org.               *
 *                                                                            *
12
 * Portions copyright (c) 2011-2020 Stanford University and the Authors.      *
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 * Authors: Peter Eastman                                                     *
 * Contributors:                                                              *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,    *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR      *
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE  *
 * USE OR OTHER DEALINGS IN THE SOFTWARE.                                     *
 * -------------------------------------------------------------------------- */

#include "Integrator.h"
36
#include "TabulatedFunction.h"
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "Vec3.h"
#include "openmm/Kernel.h"
#include "internal/windowsExport.h"
#include <string>
#include <vector>

namespace OpenMM {

/**
 * This is an Integrator that can be used to implemented arbitrary, user defined
 * integration algorithms.  It is flexible enough to support a wide range of
 * methods including both deterministic and stochastic integrators, Metropolized
 * integrators, and integrators that must integrate additional quantities along
 * with the particle positions and momenta.
Robert McGibbon's avatar
Robert McGibbon committed
51
 *
52
 * To create an integration algorithm, you first define a set of variables the
peastman's avatar
peastman committed
53
54
 * integrator will compute.  Variables come in two types: global variables
 * have a single value, while per-DOF variables have a value for every
55
56
57
58
59
60
 * degree of freedom (x, y, or z coordinate of a particle).  You can define as
 * many variables as you want of each type.  The value of any variable can be
 * computed by the integration algorithm, or set directly by calling a method on
 * the CustomIntegrator.  All variables are persistent between integration
 * steps; once a value is set, it keeps that value until it is changed by the
 * user or recomputed in a later integration step.
Robert McGibbon's avatar
Robert McGibbon committed
61
 *
62
63
64
65
 * Next, you define the algorithm as a series of computations.  To execute a
 * time step, the integrator performs the list of computations in order.  Each
 * computation updates the value of one global or per-DOF value.  There are
 * several types of computations that can be done:
Robert McGibbon's avatar
Robert McGibbon committed
66
 *
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 * <ul>
 * <li>Global: You provide a mathematical expression involving only global
 * variables.  It is evaluated and stored into a global variable.</li>
 * <li>Per-DOF: You provide a mathematical expression involving both global and
 * per-DOF variables.  It is evaluated once for every degree of freedom, and
 * the values are stored into a per-DOF variable.</li>
 * <li>Sum: You provide a mathematical expression involving both global and
 * per-DOF variables.  It is evaluated once for every degree of freedom.  All
 * of those values are then added together, and the sum is stored into a global
 * variable.</li>
 * <li>Constrain Positions: The particle positions are updated so that all
 * distance constraints are satisfied.</li>
 * <li>Constrain Velocities: The particle velocities are updated so the net
 * velocity along any constrained distance is 0.</li>
 * </ul>
Robert McGibbon's avatar
Robert McGibbon committed
82
 *
Peter Eastman's avatar
Peter Eastman committed
83
84
85
 * Like all integrators, CustomIntegrator ignores any particle whose mass is 0.
 * It is skipped when doing per-DOF computations, and is not included when
 * computing sums over degrees of freedom.
Robert McGibbon's avatar
Robert McGibbon committed
86
 *
87
88
89
 * In addition to the variables you define by calling addGlobalVariable() and
 * addPerDofVariable(), the integrator provides the following pre-defined
 * variables:
Robert McGibbon's avatar
Robert McGibbon committed
90
 *
91
92
93
94
 * <ul>
 * <li>dt: (global) This is the step size being used by the integrator.</li>
 * <li>energy: (global, read-only) This is the current potential energy of the
 * system.</li>
95
96
97
98
 * <li>energy0, energy1, energy2, ...: (global, read-only) This is similar to
 * energy, but includes only the contribution from forces in one force group.
 * A single computation step may only depend on a single energy variable
 * (energy, energy0, energy1, etc.).</li>
99
100
101
102
103
104
 * <li>x: (per-DOF) This is the current value of the degree of freedom (the x,
 * y, or z coordinate of a particle).</li>
 * <li>v: (per-DOF) This is the current velocity associated with the degree of
 * freedom (the x, y, or z component of a particle's velocity).</li>
 * <li>f: (per-DOF, read-only) This is the current force acting on the degree of
 * freedom (the x, y, or z component of the force on a particle).</li>
105
106
107
 * <li>f0, f1, f2, ...: (per-DOF, read-only) This is similar to f, but includes
 * only the contribution from forces in one force group.  A single computation
 * step may only depend on a single force variable (f, f0, f1, etc.).</li>
108
109
110
111
112
113
114
 * <li>m: (per-DOF, read-only) This is the mass of the particle the degree of
 * freedom is associated with.</li>
 * <li>uniform: (either global or per-DOF, read-only) This is a uniformly
 * distributed random number between 0 and 1.  Every time an expression is
 * evaluated, a different value will be used.  When used in a per-DOF
 * expression, a different value will be used for every degree of freedom.
 * Note, however, that if this variable appears multiple times in a single
peastman's avatar
peastman committed
115
 * expression, the same value is used everywhere it appears in that
116
117
118
119
120
121
 * expression.</li>
 * <li>gaussian: (either global or per-DOF, read-only) This is a Gaussian
 * distributed random number with mean 0 and variance 1.  Every time an expression
 * is evaluated, a different value will be used.  When used in a per-DOF
 * expression, a different value will be used for every degree of freedom.
 * Note, however, that if this variable appears multiple times in a single
peastman's avatar
peastman committed
122
 * expression, the same value is used everywhere it appears in that
123
124
125
126
 * expression.</li>
 * <li>A global variable is created for every adjustable parameter defined
 * in the integrator's Context.</li>
 * </ul>
Robert McGibbon's avatar
Robert McGibbon committed
127
 *
128
129
 * The following example uses a CustomIntegrator to implement a velocity Verlet
 * integrator:
Robert McGibbon's avatar
Robert McGibbon committed
130
 *
131
 * <tt><pre>
Peter Eastman's avatar
Peter Eastman committed
132
 * CustomIntegrator integrator(0.001);
133
134
135
136
 * integrator.addComputePerDof("v", "v+0.5*dt*f/m");
 * integrator.addComputePerDof("x", "x+dt*v");
 * integrator.addComputePerDof("v", "v+0.5*dt*f/m");
 * </pre></tt>
Robert McGibbon's avatar
Robert McGibbon committed
137
 *
138
139
140
141
142
143
 * The first step updates the velocities based on the current forces.
 * The second step updates the positions based on the new velocities, and the
 * third step updates the velocities again.  Although the first and third steps
 * look identical, the forces used in them are different.  You do not need to
 * tell the integrator that; it will recognize that the positions have changed
 * and know to recompute the forces automatically.
Robert McGibbon's avatar
Robert McGibbon committed
144
 *
145
146
147
148
149
150
151
 * The above example has two problems.  First, it does not respect distance
 * constraints.  To make the integrator work with constraints, you need to add
 * extra steps to tell it when and how to apply them.  Second, it never gives
 * Forces an opportunity to update the context state.  This should be done every
 * time step so that, for example, an AndersenThermostat can randomize velocities
 * or a MonteCarloBarostat can scale particle positions.  You need to add a
 * step to tell the integrator when to do this.  The following example corrects
152
 * both these problems, using the RATTLE algorithm to apply constraints:
Robert McGibbon's avatar
Robert McGibbon committed
153
 *
154
 * <tt><pre>
Peter Eastman's avatar
Peter Eastman committed
155
 * CustomIntegrator integrator(0.001);
156
157
 * integrator.addPerDofVariable("x1", 0);
 * integrator.addUpdateContextState();
158
159
 * integrator.addComputePerDof("v", "v+0.5*dt*f/m");
 * integrator.addComputePerDof("x", "x+dt*v");
Peter Eastman's avatar
Peter Eastman committed
160
 * integrator.addComputePerDof("x1", "x");
161
 * integrator.addConstrainPositions();
162
 * integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt");
163
164
 * integrator.addConstrainVelocities();
 * </pre></tt>
Robert McGibbon's avatar
Robert McGibbon committed
165
 *
166
167
168
169
 * CustomIntegrator can be used to implement multiple time step integrators.  The
 * following example shows an r-RESPA integrator.  It assumes the quickly changing
 * forces are in force group 0 and the slowly changing ones are in force group 1.
 * It evaluates the "fast" forces four times as often as the "slow" forces.
Robert McGibbon's avatar
Robert McGibbon committed
170
 *
171
172
173
 * <tt><pre>
 * CustomIntegrator integrator(0.004);
 * integrator.addComputePerDof("v", "v+0.5*dt*f1/m");
174
 * for (int i = 0; i &lt; 4; i++) {
175
176
177
178
179
180
 *     integrator.addComputePerDof("v", "v+0.5*(dt/4)*f0/m");
 *     integrator.addComputePerDof("x", "x+(dt/4)*v");
 *     integrator.addComputePerDof("v", "v+0.5*(dt/4)*f0/m");
 * }
 * integrator.addComputePerDof("v", "v+0.5*dt*f1/m");
 * </pre></tt>
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
 *
 * The sequence of computations in a CustomIntegrator can include flow control in
 * the form of "if" and "while" blocks.  The computations inside an "if" block
 * are executed either zero or one times, depending on whether a condition is
 * true.  The computations inside a "while" block are executed repeatedly for as
 * long as the condition remains true.  Be very careful when writing "while"
 * blocks; there is nothing to stop you from creating an infinite loop!
 *
 * For example, suppose you are writing a Monte Carlo algorithm.  Assume you have
 * already computed a new set of particle coordinates "xnew" and a step acceptance
 * probability "acceptanceProbability".  The following lines use an "if" block
 * to decide whether to accept the step, and if it is accepted, store the new
 * positions into "x".
 *
 * <tt><pre>
 * integrator.beginIfBlock("uniform < acceptanceProbability");
197
 * integrator.addComputePerDof("x", "xnew");
198
199
200
201
202
203
204
 * integrator.endBlock();
 * </pre></tt>
 *
 * The condition in an "if" or "while" block is evaluated globally, so it may
 * only involve global variables, not per-DOF ones.  It may use any of the
 * following comparison operators: =, <. >, !=, <=, >=.  Blocks may be nested
 * inside each other.
205
 * 
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
 * "Per-DOF" computations can also be thought of as per-particle computations
 * that operate on three component vectors.  For example, "x+dt*v" means to take
 * the particle's velocity (a vector), multiply it by the step size, and add the
 * position (also a vector).  The result is a new vector that can be stored into
 * a per-DOF variable with addComputePerDof(), or it can be summed over all
 * components of all particles with addComputeSum().  Because the calculation is
 * done on vectors, you can use functions that operate explicitly on vectors
 * rather than just computing each component independently.  For example, the
 * following line uses a cross product to compute the angular momentum of each
 * particle and stores it into a per-DOF variable.
 * 
 * <tt><pre>
 * integrator.addComputePerDof("angularMomentum", "m*cross(x, v)");
 * </pre></tt>
 * 
peastman's avatar
peastman committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
 * Here are two more examples that may be useful as starting points for writing
 * your own integrators.  The first one implements the algorithm used by the
 * standard VerletIntegrator class.  This is a leapfrog algorithm, in contrast
 * to the velocity Verlet algorithm shown above, so it only requires applying
 * constraints once in each time step.
 * 
 * <tt><pre>
 * CustomIntegrator integrator(dt);
 * integrator.addPerDofVariable("x0", 0);
 * integrator.addUpdateContextState();
 * integrator.addComputePerDof("x0", "x");
 * integrator.addComputePerDof("v", "v+dt*f/m");
 * integrator.addComputePerDof("x", "x+dt*v");
 * integrator.addConstrainPositions();
 * integrator.addComputePerDof("v", "(x-x0)/dt");
 * </pre></tt>
 * 
 * The second one implements the algorithm used by the standard
239
 * LangevinMiddleIntegrator class.  kB is Boltzmann's constant.
peastman's avatar
peastman committed
240
241
242
243
244
245
246
247
 * 
 * <tt><pre>
 * CustomIntegrator integrator(dt);
 * integrator.addGlobalVariable("a", exp(-friction*dt));
 * integrator.addGlobalVariable("b", sqrt(1-exp(-2*friction*dt)));
 * integrator.addGlobalVariable("kT", kB*temperature);
 * integrator.addPerDofVariable("x1", 0);
 * integrator.addUpdateContextState();
248
249
 * integrator.addComputePerDof("v", "v + dt*f/m");
 * integrator.addConstrainVelocities();
peastman's avatar
peastman committed
250
251
252
253
254
 * integrator.addComputePerDof("x", "x + 0.5*dt*v");
 * integrator.addComputePerDof("v", "a*v + b*sqrt(kT/m)*gaussian");
 * integrator.addComputePerDof("x", "x + 0.5*dt*v");
 * integrator.addComputePerDof("x1", "x");
 * integrator.addConstrainPositions();
255
 * integrator.addComputePerDof("v", "v + (x-x1)/dt");
peastman's avatar
peastman committed
256
257
 * </pre></tt>
 * 
258
259
260
261
262
263
264
265
266
 * Another feature of CustomIntegrator is that it can use derivatives of the
 * potential energy with respect to context parameters.  These derivatives are
 * typically computed by custom forces, and are only computed if a Force object
 * has been specifically told to compute them by calling addEnergyParameterDerivative()
 * on it.  CustomIntegrator provides a deriv() function for accessing these
 * derivatives in global or per-DOF expressions.  For example, "deriv(energy, lambda)"
 * is the derivative of the total potentially energy with respect to the parameter
 * lambda.  You can also restrict it to a single force group by specifying a different
 * variable for the first argument, such as "deriv(energy1, lambda)".
267
 *
268
269
 * An Integrator has one other job in addition to evolving the equations of motion:
 * it defines how to compute the kinetic energy of the system.  Depending on the
peastman's avatar
peastman committed
270
 * integration method used, simply summing (mv^2)/2 over all degrees of
271
272
273
 * freedom may not give the correct answer.  For example, in a leapfrog integrator
 * the velocities are "delayed" by half a time step, so the above formula would
 * give the kinetic energy half a time step ago, not at the current time.
Robert McGibbon's avatar
Robert McGibbon committed
274
 *
275
276
277
278
 * Call setKineticEnergyExpression() to set an expression for the kinetic energy.
 * It is computed for every degree of freedom (excluding ones whose mass is 0) and
 * the result is summed.  The default expression is "m*v*v/2", which is correct
 * for many integrators.
Robert McGibbon's avatar
Robert McGibbon committed
279
 *
280
281
 * As example, the following line defines the correct way to compute kinetic energy
 * when using a leapfrog algorithm:
Robert McGibbon's avatar
Robert McGibbon committed
282
 *
283
284
285
 * <tt><pre>
 * integrator.setKineticEnergyExpression("m*v1*v1/2; v1=v+0.5*dt*f/m");
 * </pre></tt>
Robert McGibbon's avatar
Robert McGibbon committed
286
 *
287
288
289
 * The kinetic energy expression may depend on the following pre-defined variables:
 * x, v, f, m, dt.  It also may depend on user-defined global and per-DOF variables,
 * and on the values of adjustable parameters defined  in the integrator's Context.
peastman's avatar
peastman committed
290
 * It may not depend on any other variable, such as the potential energy,
291
 * the force from a single force group, or a random number.
Robert McGibbon's avatar
Robert McGibbon committed
292
 *
293
 * Expressions may involve the operators + (add), - (subtract), * (multiply), / (divide), and ^ (power), and the following
294
 * functions: sqrt, exp, log, sin, cos, sec, csc, tan, cot, asin, acos, atan, atan2, sinh, cosh, tanh, erf, erfc, min, max, abs, floor, ceil, step, delta, select.  All trigonometric functions
295
296
 * are defined in radians, and log is the natural logarithm.  step(x) = 0 if x is less than 0, 1 otherwise.  delta(x) = 1 if x is 0, 0 otherwise.
 * select(x,y,z) = z if x = 0, y otherwise.  An expression may also involve intermediate quantities that are defined following the main expression, using ";" as a separator.
297
298
299
300
301
302
303
304
305
306
307
308
 * 
 * Expressions used in ComputePerDof and ComputeSum steps can also use the following
 * functions that operate on vectors: cross(a, b) is the cross product of two
 * vectors; dot(a, b) is the dot product of two vectors; _x(a), _y(a), and _z(a)
 * extract a single component from a vector; and vector(a, b, c) creates a new
 * vector with the x component of the first argument, the y component of the
 * second argument, and the z component of the third argument.  Remember that every
 * quantity appearing in a vector expression is a vector.  Functions that appear
 * to return a scalar really return a vector whose components are all the same.
 * For example, _z(a) returns the vector (a.z, a.z, a.z).  Likewise, wherever a
 * constant appears in the expression, it really means a vector whose components
 * all have the same value.
309
310
311
 *
 * In addition, you can call addTabulatedFunction() to define a new function based on tabulated values.  You specify the function by
 * creating a TabulatedFunction object.  That function can then appear in expressions.
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
 */

class OPENMM_EXPORT CustomIntegrator : public Integrator {
public:
    /**
     * This is an enumeration of the different types of computations that may appear in an integration algorithm.
     */
    enum ComputationType {
        /**
         * Compute an expression and store it in a global variable.
         */
        ComputeGlobal = 0,
        /**
         * Compute an expression for every degree of freedom and store it in a per-DOF variable.
         */
        ComputePerDof = 1,
        /**
         * Compute an expression for every degree of freedom, sum the values, and store the result in a global variable.
         */
        ComputeSum = 2,
        /**
         * Update particle positions so all constraints are satisfied.
         */
        ConstrainPositions = 3,
        /**
         * Update particle velocities so the net velocity along all constraints is 0.
         */
        ConstrainVelocities = 4,
        /**
         * Allow Forces to update the context state.
         */
343
344
345
346
        UpdateContextState = 5,
        /**
         * Begin an "if" block.
         */
347
        IfBlockStart = 6,
348
349
350
        /**
         * Begin a while" block.
         */
351
        WhileBlockStart = 7,
352
353
354
        /**
         * End an "if" or "while" block.
         */
355
        BlockEnd = 8
356
357
358
    };
    /**
     * Create a CustomIntegrator.
Robert McGibbon's avatar
Robert McGibbon committed
359
     *
360
361
362
     * @param stepSize       the step size with which to integrate the system (in picoseconds)
     */
    CustomIntegrator(double stepSize);
363
    ~CustomIntegrator();
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    /**
     * Get the number of global variables that have been defined.
     */
    int getNumGlobalVariables() const {
        return globalNames.size();
    }
    /**
     * Get the number of per-DOF variables that have been defined.
     */
    int getNumPerDofVariables() const {
        return perDofNames.size();
    }
    /**
     * Get the number of computation steps that have been added.
     */
    int getNumComputations() const {
        return computations.size();
    }
382
383
384
385
386
387
    /**
     * Get the number of tabulated functions that have been defined.
     */
    int getNumTabulatedFunctions() const {
        return functions.size();
    }
388
389
    /**
     * Define a new global variable.
Robert McGibbon's avatar
Robert McGibbon committed
390
     *
391
392
393
394
395
396
397
     * @param name          the name of the variable
     * @param initialValue  the variable will initially be set to this value
     * @return the index of the variable that was added
     */
    int addGlobalVariable(const std::string& name, double initialValue);
    /**
     * Get the name of a global variable.
Robert McGibbon's avatar
Robert McGibbon committed
398
     *
399
400
401
     * @param index    the index of the variable to get
     * @return the name of the variable
     */
402
    const std::string& getGlobalVariableName(int index) const;
403
404
    /**
     * Define a new per-DOF variable.
Robert McGibbon's avatar
Robert McGibbon committed
405
     *
406
407
408
409
410
411
412
413
     * @param name          the name of the variable
     * @param initialValue  the variable will initially be set to this value for
     *                      all degrees of freedom
     * @return the index of the variable that was added
     */
    int addPerDofVariable(const std::string& name, double initialValue);
    /**
     * Get the name of a per-DOF variable.
Robert McGibbon's avatar
Robert McGibbon committed
414
     *
415
416
417
     * @param index    the index of the variable to get
     * @return the name of the variable
     */
418
    const std::string& getPerDofVariableName(int index) const;
419
420
    /**
     * Get the current value of a global variable.
Robert McGibbon's avatar
Robert McGibbon committed
421
     *
422
423
424
425
     * @param index   the index of the variable to get
     * @return the current value of the variable
     */
    double getGlobalVariable(int index) const;
426
427
428
429
430
431
    /**
     * Get the current value of a global variable, specified by name.
     *
     * @param name    the name of the variable to get
     * @return the current value of the parameter
     */
Robert McGibbon's avatar
Robert McGibbon committed
432
    double getGlobalVariableByName(const std::string& name) const;
433
434
    /**
     * Set the value of a global variable.
Robert McGibbon's avatar
Robert McGibbon committed
435
     *
436
     * @param index   the index of the variable to set
437
438
439
     * @param value   the new value of the variable
     */
    void setGlobalVariable(int index, double value);
440
441
    /**
     * Set the value of a global variable, specified by name.
Robert McGibbon's avatar
Robert McGibbon committed
442
     *
443
444
445
446
     * @param name    the name of the variable to set
     * @param value   the new value of the variable
     */
    void setGlobalVariableByName(const std::string& name, double value);
447
448
    /**
     * Get the value of a per-DOF variable.
Robert McGibbon's avatar
Robert McGibbon committed
449
     *
450
451
452
453
454
     * @param index   the index of the variable to get
     * @param values  the values of the variable for all degrees of freedom
     *                are stored into this
     */
    void getPerDofVariable(int index, std::vector<Vec3>& values) const;
455
456
457
    /**
     * Get the value of a per-DOF variable, specified by name.
     *
Robert McGibbon's avatar
Robert McGibbon committed
458
459
460
     * @param name         the name of the variable to get
     * @param[out] values  the values of the variable for all degrees of freedom
     *                     are stored into this
461
462
     */
    void getPerDofVariableByName(const std::string& name, std::vector<Vec3>& values) const;
463
464
    /**
     * Set the value of a per-DOF variable.
Robert McGibbon's avatar
Robert McGibbon committed
465
     *
466
     * @param index   the index of the variable to set
467
468
469
     * @param values  the new values of the variable for all degrees of freedom
     */
    void setPerDofVariable(int index, const std::vector<Vec3>& values);
470
471
    /**
     * Set the value of a per-DOF variable, specified by name.
Robert McGibbon's avatar
Robert McGibbon committed
472
     *
473
474
475
476
     * @param name    the name of the variable to set
     * @param values  the new values of the variable for all degrees of freedom
     */
    void setPerDofVariableByName(const std::string& name, const std::vector<Vec3>& values);
477
478
    /**
     * Add a step to the integration algorithm that computes a global value.
Robert McGibbon's avatar
Robert McGibbon committed
479
     *
480
481
482
483
484
485
486
487
488
     * @param variable    the global variable to store the computed value into
     * @param expression  a mathematical expression involving only global variables.
     *                    In each integration step, its value is computed and
     *                    stored into the specified variable.
     * @return the index of the step that was added
     */
    int addComputeGlobal(const std::string& variable, const std::string& expression);
    /**
     * Add a step to the integration algorithm that computes a per-DOF value.
Robert McGibbon's avatar
Robert McGibbon committed
489
     *
490
491
492
493
494
495
496
497
498
499
     * @param variable    the per-DOF variable to store the computed value into
     * @param expression  a mathematical expression involving both global and
     *                    per-DOF variables.  In each integration step, its value
     *                    is computed for every degree of freedom and stored into
     *                    the specified variable.
     * @return the index of the step that was added
     */
    int addComputePerDof(const std::string& variable, const std::string& expression);
    /**
     * Add a step to the integration algorithm that computes a sum over degrees of freedom.
Robert McGibbon's avatar
Robert McGibbon committed
500
     *
501
502
503
504
505
506
507
508
509
510
511
512
     * @param variable    the global variable to store the computed value into
     * @param expression  a mathematical expression involving both global and
     *                    per-DOF variables.  In each integration step, its value
     *                    is computed for every degree of freedom.  Those values
     *                    are then added together, and the sum is stored in the
     *                    specified variable.
     * @return the index of the step that was added
     */
    int addComputeSum(const std::string& variable, const std::string& expression);
    /**
     * Add a step to the integration algorithm that updates particle positions so
     * all constraints are satisfied.
Robert McGibbon's avatar
Robert McGibbon committed
513
     *
514
515
516
517
518
519
     * @return the index of the step that was added
     */
    int addConstrainPositions();
    /**
     * Add a step to the integration algorithm that updates particle velocities
     * so the net velocity along all constraints is 0.
Robert McGibbon's avatar
Robert McGibbon committed
520
     *
521
522
523
524
525
526
     * @return the index of the step that was added
     */
    int addConstrainVelocities();
    /**
     * Add a step to the integration algorithm that allows Forces to update the
     * context state.
Robert McGibbon's avatar
Robert McGibbon committed
527
     *
528
529
530
     * @return the index of the step that was added
     */
    int addUpdateContextState();
531
532
533
    /**
     * Add a step which begins a new "if" block.
     *
Robert McGibbon's avatar
Robert McGibbon committed
534
     * @param condition   a mathematical expression involving a comparison operator
535
536
537
538
539
540
541
542
543
544
     *                    and global variables.  All steps between this one and
     *                    the end of the block are executed only if the condition
     *                    is true.
     *
     * @return the index of the step that was added
     */
    int beginIfBlock(const std::string& condition);
    /**
     * Add a step which begins a new "while" block.
     *
Robert McGibbon's avatar
Robert McGibbon committed
545
     * @param condition   a mathematical expression involving a comparison operator
546
547
548
549
550
551
552
553
554
555
556
557
558
559
     *                    and global variables.  All steps between this one and
     *                    the end of the block are executed repeatedly as long as
     *                    the condition remains true.
     *
     * @return the index of the step that was added
     */
    int beginWhileBlock(const std::string& condition);
    /**
     * Add a step which marks the end of the most recently begun "if" or "while"
     * block.
     *
     * @return the index of the step that was added
     */
    int endBlock();
560
561
    /**
     * Get the details of a computation step that has been added to the integration algorithm.
Robert McGibbon's avatar
Robert McGibbon committed
562
563
564
565
566
567
568
569
570
     *
     * @param      index       the index of the computation step to get
     * @param[out] type        the type of computation this step performs
     * @param[out] variable    the variable into which this step stores its
     *                         result.  If this step does not store a result in
     *                         a variable, this will be an empty string.
     * @param[out] expression  the expression this step evaluates.  If
     *                         this step does not evaluate an expression, this
     *                         will be an empty string.
571
572
     */
    void getComputationStep(int index, ComputationType& type, std::string& variable, std::string& expression) const;
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
    /**
     * Add a tabulated function that may appear in expressions.
     *
     * @param name           the name of the function as it appears in expressions
     * @param function       a TabulatedFunction object defining the function.  The TabulatedFunction
     *                       should have been created on the heap with the "new" operator.  The
     *                       integrator takes over ownership of it, and deletes it when the integrator itself is deleted.
     * @return the index of the function that was added
     */
    int addTabulatedFunction(const std::string& name, TabulatedFunction* function);
    /**
     * Get a const reference to a tabulated function that may appear in expressions.
     *
     * @param index     the index of the function to get
     * @return the TabulatedFunction object defining the function
     */
    const TabulatedFunction& getTabulatedFunction(int index) const;
    /**
     * Get a reference to a tabulated function that may appear in expressions.
     *
     * @param index     the index of the function to get
     * @return the TabulatedFunction object defining the function
     */
    TabulatedFunction& getTabulatedFunction(int index);
    /**
     * Get the name of a tabulated function that may appear in expressions.
     *
     * @param index     the index of the function to get
     * @return the name of the function as it appears in expressions
     */
    const std::string& getTabulatedFunctionName(int index) const;
604
605
606
607
608
609
610
611
612
613
614
615
    /**
     * Get the expression to use for computing the kinetic energy.  The expression is evaluated
     * for every degree of freedom.  Those values are then added together, and the sum
     * is reported as the current kinetic energy.
     */
    const std::string& getKineticEnergyExpression() const;
    /**
     * Set the expression to use for computing the kinetic energy.  The expression is evaluated
     * for every degree of freedom.  Those values are then added together, and the sum
     * is reported as the current kinetic energy.
     */
    void setKineticEnergyExpression(const std::string& expression);
616
617
618
619
620
621
622
623
624
625
626
627
628
    /**
     * Get the random number seed.  See setRandomNumberSeed() for details.
     */
    int getRandomNumberSeed() const {
        return randomNumberSeed;
    }
    /**
     * Set the random number seed.  The precise meaning of this parameter is undefined, and is left up
     * to each Platform to interpret in an appropriate way.  It is guaranteed that if two simulations
     * are run with different random number seeds, the sequence of random numbers will be different.  On
     * the other hand, no guarantees are made about the behavior of simulations that use the same seed.
     * In particular, Platforms are permitted to use non-deterministic algorithms which produce different
     * results on successive runs, even if those runs were initialized identically.
629
     *
630
631
632
     * If seed is set to 0 (which is the default value assigned), a unique seed is chosen when a Context
     * is created from this Force. This is done to ensure that each Context receives unique random seeds
     * without you needing to set them explicitly.
633
634
635
636
637
638
     */
    void setRandomNumberSeed(int seed) {
        randomNumberSeed = seed;
    }
    /**
     * Advance a simulation through time by taking a series of time steps.
Robert McGibbon's avatar
Robert McGibbon committed
639
     *
640
641
642
643
644
645
646
647
648
649
     * @param steps   the number of time steps to take
     */
    void step(int steps);
protected:
    /**
     * This will be called by the Context when it is created.  It informs the Integrator
     * of what context it will be integrating, and gives it a chance to do any necessary initialization.
     * It will also get called again if the application calls reinitialize() on the Context.
     */
    void initialize(ContextImpl& context);
650
651
652
653
654
    /**
     * This will be called by the Context when it is destroyed to let the Integrator do any necessary
     * cleanup.  It will also get called again if the application calls reinitialize() on the Context.
     */
    void cleanup();
655
656
657
658
659
660
661
662
    /**
     * When the user modifies the state, we need to mark that the forces need to be recalculated.
     */
    void stateChanged(State::DataType changed);
    /**
     * Get the names of all Kernels used by this Integrator.
     */
    std::vector<std::string> getKernelNames();
663
664
665
666
    /**
     * Compute the kinetic energy of the system at the current time.
     */
    double computeKineticEnergy();
667
668
669
670
    /**
     * Get whether computeKineticEnergy() expects forces to have been computed.
     */
    bool kineticEnergyRequiresForce() const;
671
672
private:
    class ComputationInfo;
673
    class FunctionInfo;
674
675
676
677
678
    std::vector<std::string> globalNames;
    std::vector<std::string> perDofNames;
    mutable std::vector<double> globalValues;
    std::vector<std::vector<Vec3> > perDofValues;
    std::vector<ComputationInfo> computations;
679
    std::vector<FunctionInfo> functions;
680
    std::string kineticEnergy;
681
682
    mutable bool globalsAreCurrent;
    int randomNumberSeed;
683
    bool forcesAreValid, keNeedsForce;
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
    Kernel kernel;
};

/**
 * This is an internal class used to record information about a computation step.
 * @private
 */
class CustomIntegrator::ComputationInfo {
public:
    ComputationType type;
    std::string variable, expression;
    ComputationInfo() {
    }
    ComputationInfo(ComputationType type, const std::string& variable, const std::string& expression) :
        type(type), variable(variable), expression(expression) {
    }
};

702
703
704
705
706
707
708
709
710
711
712
713
714
715
/**
 * This is an internal class used to record information about a tabulated function.
 * @private
 */
class CustomIntegrator::FunctionInfo {
public:
    std::string name;
    TabulatedFunction* function;
    FunctionInfo() {
    }
    FunctionInfo(const std::string& name, TabulatedFunction* function) : name(name), function(function) {
    }
};

716
717
718
} // namespace OpenMM

#endif /*OPENMM_CUSTOMINTEGRATOR_H_*/