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_Boolean.OpenMM_True;
47 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_addEnergyParameterDerivative;
48 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_addGlobalParameter;
49 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_addPerTorsionParameter;
50 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_addTorsion;
51 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_create;
52 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_destroy;
53 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getEnergyFunction;
54 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getEnergyParameterDerivativeName;
55 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getGlobalParameterDefaultValue;
56 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getGlobalParameterName;
57 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getNumEnergyParameterDerivatives;
58 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getNumGlobalParameters;
59 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getNumPerTorsionParameters;
60 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getNumTorsions;
61 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getPerTorsionParameterName;
62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_getTorsionParameters;
63 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setEnergyFunction;
64 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setGlobalParameterDefaultValue;
65 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setGlobalParameterName;
66 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setPerTorsionParameterName;
67 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setTorsionParameters;
68 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_setUsesPeriodicBoundaryConditions;
69 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_updateParametersInContext;
70 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomTorsionForce_usesPeriodicBoundaryConditions;
71
72 /**
73 * This class implements interactions between sets of four particles that depend on the torsion angle between them.
74 * Unlike PeriodicTorsionForce, the functional form of the interaction is completely customizable, and may
75 * involve arbitrary algebraic expressions. In addition to the angle formed by the particles, it may depend
76 * on arbitrary global and per-torsion parameters.
77 *
78 * <p>To use this class, create a CustomTorsionForce object, passing an algebraic expression to the constructor
79 * that defines the interaction energy between each set of particles. The expression may depend on theta, the torsion angle
80 * formed by the particles, as well as on any parameters you choose. Then call addPerTorsionParameter() to define per-torsion
81 * parameters, and addGlobalParameter() to define global parameters. The values of per-torsion parameters are specified as
82 * part of the system definition, while values of global parameters may be modified during a simulation by calling Context::setParameter().
83 * Finally, call addTorsion() once for each torsion. After an torsion has been added, you can modify its parameters by calling setTorsionParameters().
84 * This will have no effect on Contexts that already exist unless you call updateParametersInContext().
85 * Note that theta is guaranteed to be in the range [-pi,+pi], which may cause issues with force discontinuities if the energy function does not respect this domain.
86 *
87 * <p>As an example, the following code creates a CustomTorsionForce that implements a periodic potential:
88 *
89 * <pre>{@code
90 * CustomTorsionForce force = new CustomTorsionForce("0.5*k*(1-cos(theta-theta0))");
91 * }</pre>
92 *
93 * <p>This force depends on two parameters: the spring constant k and equilibrium angle theta0. The following code defines these parameters:
94 *
95 * <pre>{@code
96 * force.addPerTorsionParameter("k");
97 * force.addPerTorsionParameter("theta0");
98 * }</pre>
99 *
100 * <p>If a harmonic restraint is desired, it is important to be careful of the domain for theta, using an idiom like this:
101 *
102 * <pre>{@code
103 * CustomTorsionForce force = new CustomTorsionForce("0.5*k*min(dtheta, 2*pi-dtheta)^2; dtheta = abs(theta-theta0); pi = 3.1415926535");
104 * }</pre>
105 *
106 * <p>This class also has the ability to compute derivatives of the potential energy with respect to global parameters.
107 * Call addEnergyParameterDerivative() to request that the derivative with respect to a particular parameter be
108 * computed. You can then query its value in a Context by calling getState() on it.
109 *
110 * <p>Expressions may involve the operators + (add), - (subtract), * (multiply), / (divide), and ^ (power), and the following
111 * 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
112 * are defined in radians, and log is the natural logarithm. step(x) = 0 if x < 0, 1 otherwise. delta(x) = 1 if x = 0, 0 otherwise.
113 * select(x,y,z) = z if x = 0, y otherwise.
114 */
115 public class CustomTorsionForce extends Force {
116
117 /**
118 * Create a CustomTorsionForce.
119 *
120 * @param energy The algebraic expression that gives the interaction energy of each torsion as a function of theta, the torsion angle.
121 */
122 public CustomTorsionForce(String energy) {
123 super(OpenMM_CustomTorsionForce_create(energy));
124 }
125
126 /**
127 * Request that this Force compute the derivative of its energy with respect to a global parameter.
128 *
129 * @param name The name of the parameter.
130 */
131 public void addEnergyParameterDerivative(String name) {
132 OpenMM_CustomTorsionForce_addEnergyParameterDerivative(pointer, name);
133 }
134
135 /**
136 * Request that this Force compute the derivative of its energy with respect to a global parameter.
137 *
138 * @param name The name of the parameter.
139 */
140 public void addEnergyParameterDerivative(Pointer name) {
141 OpenMM_CustomTorsionForce_addEnergyParameterDerivative(pointer, name);
142 }
143
144 /**
145 * Add a new global parameter that the interaction may depend on.
146 *
147 * @param name The name of the parameter.
148 * @param defaultValue The default value of the parameter.
149 * @return The index of the parameter that was added.
150 */
151 public int addGlobalParameter(String name, double defaultValue) {
152 return OpenMM_CustomTorsionForce_addGlobalParameter(pointer, name, defaultValue);
153 }
154
155 /**
156 * Add a new global parameter that the interaction may depend on.
157 *
158 * @param name The name of the parameter.
159 * @param defaultValue The default value of the parameter.
160 * @return The index of the parameter that was added.
161 */
162 public int addGlobalParameter(Pointer name, double defaultValue) {
163 return OpenMM_CustomTorsionForce_addGlobalParameter(pointer, name, defaultValue);
164 }
165
166 /**
167 * Add a new per-torsion parameter that the interaction may depend on.
168 *
169 * @param name The name of the parameter.
170 * @return The index of the parameter that was added.
171 */
172 public int addPerTorsionParameter(String name) {
173 return OpenMM_CustomTorsionForce_addPerTorsionParameter(pointer, name);
174 }
175
176 /**
177 * Add a new per-torsion parameter that the interaction may depend on.
178 *
179 * @param name The name of the parameter.
180 * @return The index of the parameter that was added.
181 */
182 public int addPerTorsionParameter(Pointer name) {
183 return OpenMM_CustomTorsionForce_addPerTorsionParameter(pointer, name);
184 }
185
186 /**
187 * Add a torsion to the Force.
188 *
189 * @param particle1 The index of the first particle forming the torsion.
190 * @param particle2 The index of the second particle forming the torsion.
191 * @param particle3 The index of the third particle forming the torsion.
192 * @param particle4 The index of the fourth particle forming the torsion.
193 * @param parameters The list of parameters for the new torsion.
194 * @return The index of the torsion that was added.
195 */
196 public int addTorsion(int particle1, int particle2, int particle3, int particle4, PointerByReference parameters) {
197 return OpenMM_CustomTorsionForce_addTorsion(pointer, particle1, particle2, particle3, particle4, parameters);
198 }
199
200 /**
201 * Destroy the force.
202 */
203 @Override
204 public void destroy() {
205 if (pointer != null) {
206 OpenMM_CustomTorsionForce_destroy(pointer);
207 pointer = null;
208 }
209 }
210
211 /**
212 * Get the algebraic expression that gives the interaction energy of each torsion.
213 *
214 * @return The energy expression.
215 */
216 public String getEnergyFunction() {
217 Pointer p = OpenMM_CustomTorsionForce_getEnergyFunction(pointer);
218 if (p == null) {
219 return null;
220 }
221 return p.getString(0);
222 }
223
224 /**
225 * Get the name of a parameter with respect to which the derivative of the energy should be computed.
226 *
227 * @param index The index of the parameter derivative, between 0 and getNumEnergyParameterDerivatives().
228 * @return The parameter name.
229 */
230 public String getEnergyParameterDerivativeName(int index) {
231 Pointer p = OpenMM_CustomTorsionForce_getEnergyParameterDerivativeName(pointer, index);
232 if (p == null) {
233 return null;
234 }
235 return p.getString(0);
236 }
237
238 /**
239 * Get the default value of a global parameter.
240 *
241 * @param index The index of the parameter for which to get the default value.
242 * @return The parameter default value.
243 */
244 public double getGlobalParameterDefaultValue(int index) {
245 return OpenMM_CustomTorsionForce_getGlobalParameterDefaultValue(pointer, index);
246 }
247
248 /**
249 * Get the name of a global parameter.
250 *
251 * @param index The index of the parameter for which to get the name.
252 * @return The parameter name.
253 */
254 public String getGlobalParameterName(int index) {
255 Pointer p = OpenMM_CustomTorsionForce_getGlobalParameterName(pointer, index);
256 if (p == null) {
257 return null;
258 }
259 return p.getString(0);
260 }
261
262 /**
263 * Get the number of parameters with respect to which the derivative of the energy should be computed.
264 *
265 * @return The number of parameters.
266 */
267 public int getNumEnergyParameterDerivatives() {
268 return OpenMM_CustomTorsionForce_getNumEnergyParameterDerivatives(pointer);
269 }
270
271 /**
272 * Get the number of global parameters that the interaction depends on.
273 *
274 * @return The number of parameters.
275 */
276 public int getNumGlobalParameters() {
277 return OpenMM_CustomTorsionForce_getNumGlobalParameters(pointer);
278 }
279
280 /**
281 * Get the number of per-torsion parameters that the interaction depends on.
282 *
283 * @return The number of parameters.
284 */
285 public int getNumPerTorsionParameters() {
286 return OpenMM_CustomTorsionForce_getNumPerTorsionParameters(pointer);
287 }
288
289 /**
290 * Get the number of torsions for which force field parameters have been defined.
291 *
292 * @return The number of torsions.
293 */
294 public int getNumTorsions() {
295 return OpenMM_CustomTorsionForce_getNumTorsions(pointer);
296 }
297
298 /**
299 * Get the name of a per-torsion parameter.
300 *
301 * @param index The index of the parameter for which to get the name.
302 * @return The parameter name.
303 */
304 public String getPerTorsionParameterName(int index) {
305 Pointer p = OpenMM_CustomTorsionForce_getPerTorsionParameterName(pointer, index);
306 if (p == null) {
307 return null;
308 }
309 return p.getString(0);
310 }
311
312 /**
313 * Get the force field parameters for a torsion.
314 *
315 * @param index The index of the torsion for which to get parameters.
316 * @param particle1 The index of the first particle forming the torsion (output).
317 * @param particle2 The index of the second particle forming the torsion (output).
318 * @param particle3 The index of the third particle forming the torsion (output).
319 * @param particle4 The index of the fourth particle forming the torsion (output).
320 * @param parameters The list of parameters (output).
321 */
322 public void getTorsionParameters(int index, IntByReference particle1, IntByReference particle2,
323 IntByReference particle3, IntByReference particle4, PointerByReference parameters) {
324 OpenMM_CustomTorsionForce_getTorsionParameters(pointer, index, particle1, particle2, particle3, particle4, parameters);
325 }
326
327 /**
328 * Get the force field parameters for a torsion.
329 *
330 * @param index The index of the torsion for which to get parameters.
331 * @param particle1 The index of the first particle forming the torsion (output).
332 * @param particle2 The index of the second particle forming the torsion (output).
333 * @param particle3 The index of the third particle forming the torsion (output).
334 * @param particle4 The index of the fourth particle forming the torsion (output).
335 * @param parameters The list of parameters (output).
336 */
337 public void getTorsionParameters(int index, IntBuffer particle1, IntBuffer particle2,
338 IntBuffer particle3, IntBuffer particle4, PointerByReference parameters) {
339 OpenMM_CustomTorsionForce_getTorsionParameters(pointer, index, particle1, particle2, particle3, particle4, parameters);
340 }
341
342 /**
343 * Set the algebraic expression that gives the interaction energy of each torsion.
344 *
345 * @param energy The energy expression.
346 */
347 public void setEnergyFunction(String energy) {
348 OpenMM_CustomTorsionForce_setEnergyFunction(pointer, energy);
349 }
350
351 /**
352 * Set the algebraic expression that gives the interaction energy of each torsion.
353 *
354 * @param energy The energy expression.
355 */
356 public void setEnergyFunction(Pointer energy) {
357 OpenMM_CustomTorsionForce_setEnergyFunction(pointer, energy);
358 }
359
360 /**
361 * Set the default value of a global parameter.
362 *
363 * @param index The index of the parameter for which to set the default value.
364 * @param defaultValue The default value of the parameter.
365 */
366 public void setGlobalParameterDefaultValue(int index, double defaultValue) {
367 OpenMM_CustomTorsionForce_setGlobalParameterDefaultValue(pointer, index, defaultValue);
368 }
369
370 /**
371 * Set the name of a global parameter.
372 *
373 * @param index The index of the parameter for which to set the name.
374 * @param name The name of the parameter.
375 */
376 public void setGlobalParameterName(int index, String name) {
377 OpenMM_CustomTorsionForce_setGlobalParameterName(pointer, index, name);
378 }
379
380 /**
381 * Set the name of a global parameter.
382 *
383 * @param index The index of the parameter for which to set the name.
384 * @param name The name of the parameter.
385 */
386 public void setGlobalParameterName(int index, Pointer name) {
387 OpenMM_CustomTorsionForce_setGlobalParameterName(pointer, index, name);
388 }
389
390 /**
391 * Set the name of a per-torsion parameter.
392 *
393 * @param index The index of the parameter for which to set the name.
394 * @param name The name of the parameter.
395 */
396 public void setPerTorsionParameterName(int index, String name) {
397 OpenMM_CustomTorsionForce_setPerTorsionParameterName(pointer, index, name);
398 }
399
400 /**
401 * Set the name of a per-torsion parameter.
402 *
403 * @param index The index of the parameter for which to set the name.
404 * @param name The name of the parameter.
405 */
406 public void setPerTorsionParameterName(int index, Pointer name) {
407 OpenMM_CustomTorsionForce_setPerTorsionParameterName(pointer, index, name);
408 }
409
410 /**
411 * Set the force field parameters for a torsion.
412 *
413 * @param index The index of the torsion for which to set parameters.
414 * @param particle1 The index of the first particle forming the torsion.
415 * @param particle2 The index of the second particle forming the torsion.
416 * @param particle3 The index of the third particle forming the torsion.
417 * @param particle4 The index of the fourth particle forming the torsion.
418 * @param parameters The list of parameters for the torsion.
419 */
420 public void setTorsionParameters(int index, int particle1, int particle2, int particle3, int particle4, PointerByReference parameters) {
421 OpenMM_CustomTorsionForce_setTorsionParameters(pointer, index, particle1, particle2, particle3, particle4, parameters);
422 }
423
424 /**
425 * Set whether this force should apply periodic boundary conditions when calculating displacements.
426 *
427 * @param periodic If true, periodic boundary conditions will be applied.
428 */
429 public void setUsesPeriodicBoundaryConditions(boolean periodic) {
430 OpenMM_CustomTorsionForce_setUsesPeriodicBoundaryConditions(pointer, periodic ? 1 : 0);
431 }
432
433 /**
434 * Update the per-torsion parameters in a Context to match those stored in this Force object.
435 *
436 * @param context The Context in which to update the parameters.
437 */
438 public void updateParametersInContext(Context context) {
439 if (context.hasContextPointer()) {
440 OpenMM_CustomTorsionForce_updateParametersInContext(pointer, context.getPointer());
441 }
442 }
443
444 /**
445 * Check if the force uses periodic boundary conditions.
446 *
447 * @return True if the force uses periodic boundary conditions.
448 */
449 @Override
450 public boolean usesPeriodicBoundaryConditions() {
451 int pbc = OpenMM_CustomTorsionForce_usesPeriodicBoundaryConditions(pointer);
452 return pbc == OpenMM_True;
453 }
454 }