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
43 import java.nio.IntBuffer;
44
45 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_Boolean.OpenMM_True;
46 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_addBond;
47 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_addEnergyParameterDerivative;
48 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_addGlobalParameter;
49 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_addPerBondParameter;
50 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_create;
51 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_destroy;
52 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getBondParameters;
53 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getEnergyFunction;
54 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getEnergyParameterDerivativeName;
55 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getGlobalParameterDefaultValue;
56 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getGlobalParameterName;
57 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getNumBonds;
58 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getNumEnergyParameterDerivatives;
59 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getNumGlobalParameters;
60 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getNumPerBondParameters;
61 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_getPerBondParameterName;
62 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setBondParameters;
63 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setEnergyFunction;
64 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setGlobalParameterDefaultValue;
65 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setGlobalParameterName;
66 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setPerBondParameterName;
67 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_setUsesPeriodicBoundaryConditions;
68 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_updateParametersInContext;
69 import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomBondForce_usesPeriodicBoundaryConditions;
70
71 /**
72 * This class implements bonded interactions between pairs of particles. Unlike HarmonicBondForce, the functional form
73 * of the interaction is completely customizable, and may involve arbitrary algebraic expressions.
74 * It may depend on the distance between particles, as well as on arbitrary global and
75 * per-bond parameters.
76 * <p>
77 * To use this class, create a CustomBondForce object, passing an algebraic expression to the constructor
78 * that defines the interaction energy between each pair of bonded particles. The expression may depend on r, the distance
79 * between the particles, as well as on any parameters you choose. Then call addPerBondParameter() to define per-bond
80 * parameters, and addGlobalParameter() to define global parameters. The values of per-bond parameters are specified as
81 * part of the system definition, while values of global parameters may be modified during a simulation by calling Context::setParameter().
82 * Finally, call addBond() once for each bond. After a bond has been added, you can modify its parameters by calling setBondParameters().
83 * This will have no effect on Contexts that already exist unless you call updateParametersInContext().
84 * <p>
85 * As an example, the following code creates a CustomBondForce that implements a harmonic potential:
86 * <p>
87 * <pre>
88 * {@code
89 * CustomBondForce force = new CustomBondForce("0.5*k*(r-r0)^2");
90 * }
91 * </pre>
92 * <p>
93 * This force depends on two parameters: the spring constant k and equilibrium distance r0. The following code defines these parameters:
94 * <p>
95 * <pre>
96 * {@code
97 * force.addPerBondParameter("k");
98 * force.addPerBondParameter("r0");
99 * }
100 * </pre>
101 * <p>
102 * This class also has the ability to compute derivatives of the potential energy with respect to global parameters.
103 * Call addEnergyParameterDerivative() to request that the derivative with respect to a particular parameter be
104 * computed. You can then query its value in a Context by calling getState() on it.
105 * <p>
106 * Expressions may involve the operators + (add), - (subtract), * (multiply), / (divide), and ^ (power), and the following
107 * 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
108 * 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.
109 * select(x,y,z) = z if x = 0, y otherwise.
110 */
111 public class CustomBondForce extends Force {
112
113 /**
114 * Create a CustomBondForce.
115 *
116 * @param energy an algebraic expression giving the interaction energy between two bonded particles as a function
117 * of r, the distance between them
118 */
119 public CustomBondForce(String energy) {
120 super(OpenMM_CustomBondForce_create(energy));
121 }
122
123 /**
124 * Add a bond term to the force field.
125 *
126 * @param i1 the index of the first particle connected by the bond
127 * @param i2 the index of the second particle connected by the bond
128 * @param parameters the list of parameters for the new bond
129 * @return the index of the bond that was added
130 */
131 public int addBond(int i1, int i2, DoubleArray parameters) {
132 return OpenMM_CustomBondForce_addBond(pointer, i1, i2, parameters.getPointer());
133 }
134
135 /**
136 * Request that this Force compute the derivative of its energy with respect to a global parameter.
137 * The parameter must have already been added with addGlobalParameter().
138 *
139 * @param name the name of the parameter
140 */
141 public void addEnergyParameterDerivative(String name) {
142 OpenMM_CustomBondForce_addEnergyParameterDerivative(pointer, name);
143 }
144
145 /**
146 * Add a new global parameter that the interaction may depend on. The default value provided to
147 * this method is the initial value of the parameter in newly created Contexts. You can change
148 * the value at any time by calling setParameter() on the Context.
149 *
150 * @param name the name of the parameter
151 * @param value the default value of the parameter
152 * @return the index of the parameter that was added
153 */
154 public int addGlobalParameter(String name, double value) {
155 return OpenMM_CustomBondForce_addGlobalParameter(pointer, name, value);
156 }
157
158 /**
159 * Add a new per-bond parameter that the interaction may depend on.
160 *
161 * @param name the name of the parameter
162 * @return the index of the parameter that was added
163 */
164 public int addPerBondParameter(String name) {
165 return OpenMM_CustomBondForce_addPerBondParameter(pointer, name);
166 }
167
168 /**
169 * Destroy the OpenMM CustomBondForce.
170 */
171 @Override
172 public void destroy() {
173 if (pointer != null) {
174 OpenMM_CustomBondForce_destroy(pointer);
175 pointer = null;
176 }
177 }
178
179 /**
180 * Get the force field parameters for a bond term.
181 *
182 * @param index the index of the bond for which to get parameters
183 * @param i1 the index of the first particle connected by the bond
184 * @param i2 the index of the second particle connected by the bond
185 * @param parameters the list of parameters for the bond
186 */
187 public void getBondParameters(int index, IntBuffer i1, IntBuffer i2, DoubleArray parameters) {
188 OpenMM_CustomBondForce_getBondParameters(pointer, index, i1, i2, parameters.getPointer());
189 }
190
191 /**
192 * Get the force field parameters for a bond term.
193 *
194 * @param index the index of the bond for which to get parameters
195 * @param i1 the index of the first particle connected by the bond
196 * @param i2 the index of the second particle connected by the bond
197 * @param parameters the list of parameters for the bond
198 */
199 public void getBondParameters(int index, IntByReference i1, IntByReference i2, DoubleArray parameters) {
200 OpenMM_CustomBondForce_getBondParameters(pointer, index, i1, i2, parameters.getPointer());
201 }
202
203 /**
204 * Get the algebraic expression that gives the interaction energy for each bond
205 *
206 * @return the energy function expression
207 */
208 public String getEnergyFunction() {
209 Pointer p = OpenMM_CustomBondForce_getEnergyFunction(pointer);
210 if (p == null) {
211 return null;
212 }
213 return p.getString(0);
214 }
215
216 /**
217 * Get the name of a global parameter with respect to which this Force should compute the
218 * derivative of the energy.
219 *
220 * @param index the index of the parameter derivative, between 0 and getNumEnergyParameterDerivatives()
221 * @return the parameter name
222 */
223 public String getEnergyParameterDerivativeName(int index) {
224 Pointer p = OpenMM_CustomBondForce_getEnergyParameterDerivativeName(pointer, index);
225 if (p == null) {
226 return null;
227 }
228 return p.getString(0);
229 }
230
231 /**
232 * Get the default value of a global parameter.
233 *
234 * @param index The index of the parameter.
235 * @return The default value of the parameter.
236 */
237 public double getGlobalParameterDefaultValue(int index) {
238 return OpenMM_CustomBondForce_getGlobalParameterDefaultValue(pointer, index);
239 }
240
241 /**
242 * Get the name of a global parameter.
243 *
244 * @param index The index of the parameter.
245 * @return The name of the parameter.
246 */
247 public String getGlobalParameterName(int index) {
248 Pointer p = OpenMM_CustomBondForce_getGlobalParameterName(pointer, index);
249 if (p == null) {
250 return null;
251 }
252 return p.getString(0);
253 }
254
255 /**
256 * Get the number of bonds for which force field parameters have been defined.
257 *
258 * @return the number of bonds
259 */
260 public int getNumBonds() {
261 return OpenMM_CustomBondForce_getNumBonds(pointer);
262 }
263
264 /**
265 * Get the number of parameters with respect to which the derivative of the energy should be computed.
266 *
267 * @return The number of parameters.
268 */
269 public int getNumEnergyParameterDerivatives() {
270 return OpenMM_CustomBondForce_getNumEnergyParameterDerivatives(pointer);
271 }
272
273 /**
274 * Get the number of global parameters.
275 *
276 * @return The number of global parameters.
277 */
278 public int getNumGlobalParameters() {
279 return OpenMM_CustomBondForce_getNumGlobalParameters(pointer);
280 }
281
282 /**
283 * Get the number of per-bond parameters.
284 *
285 * @return The number of per-bond parameters.
286 */
287 public int getNumPerBondParameters() {
288 return OpenMM_CustomBondForce_getNumPerBondParameters(pointer);
289 }
290
291 /**
292 * Get the name of a per-bond parameter.
293 *
294 * @param index The index of the parameter.
295 * @return The name of the parameter.
296 */
297 public String getPerBondParameterName(int index) {
298 Pointer p = OpenMM_CustomBondForce_getPerBondParameterName(pointer, index);
299 if (p == null) {
300 return null;
301 }
302 return p.getString(0);
303 }
304
305 /**
306 * Set the parameters for one bond in the OpenMM System.
307 *
308 * @param index The index of the bond.
309 * @param i1 The index of the first atom.
310 * @param i2 The index of the second atom.
311 * @param parameters The bond parameters.
312 */
313 public void setBondParameters(int index, int i1, int i2, DoubleArray parameters) {
314 OpenMM_CustomBondForce_setBondParameters(pointer, index, i1, i2, parameters.getPointer());
315 }
316
317 /**
318 * Set the energy function expression.
319 *
320 * @param energy The energy function expression.
321 */
322 public void setEnergyFunction(String energy) {
323 OpenMM_CustomBondForce_setEnergyFunction(pointer, energy);
324 }
325
326 /**
327 * Set the default value of a global parameter.
328 *
329 * @param index The index of the parameter.
330 * @param value The default value of the parameter.
331 */
332 public void setGlobalParameterDefaultValue(int index, double value) {
333 OpenMM_CustomBondForce_setGlobalParameterDefaultValue(pointer, index, value);
334 }
335
336 /**
337 * Set the name of a global parameter.
338 *
339 * @param index The index of the parameter.
340 * @param name The name of the parameter.
341 */
342 public void setGlobalParameterName(int index, String name) {
343 OpenMM_CustomBondForce_setGlobalParameterName(pointer, index, name);
344 }
345
346 /**
347 * Set the name of a per-bond parameter.
348 *
349 * @param index The index of the parameter.
350 * @param name The name of the parameter.
351 */
352 public void setPerBondParameterName(int index, String name) {
353 OpenMM_CustomBondForce_setPerBondParameterName(pointer, index, name);
354 }
355
356 /**
357 * Set whether this force uses periodic boundary conditions.
358 *
359 * @param periodic If true, periodic boundary conditions will be used.
360 */
361 public void setUsesPeriodicBoundaryConditions(boolean periodic) {
362 OpenMM_CustomBondForce_setUsesPeriodicBoundaryConditions(pointer, periodic ? 1 : 0);
363 }
364
365 /**
366 * Update the parameters in the OpenMM Context.
367 *
368 * @param context The OpenMM Context.
369 */
370 public void updateParametersInContext(Context context) {
371 if (context.hasContextPointer()) {
372 OpenMM_CustomBondForce_updateParametersInContext(pointer, context.getPointer());
373 }
374 }
375
376 /**
377 * Check if the force uses periodic boundary conditions.
378 *
379 * @return True if the force uses periodic boundary conditions.
380 */
381 @Override
382 public boolean usesPeriodicBoundaryConditions() {
383 int pbc = OpenMM_CustomBondForce_usesPeriodicBoundaryConditions(pointer);
384 return pbc == OpenMM_True;
385 }
386 }