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