View Javadoc
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 }