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 }