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_CustomExternalForce_addGlobalParameter;
47  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_addParticle;
48  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_addPerParticleParameter;
49  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_create;
50  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_destroy;
51  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getEnergyFunction;
52  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getGlobalParameterDefaultValue;
53  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getGlobalParameterName;
54  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getNumGlobalParameters;
55  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getNumParticles;
56  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getNumPerParticleParameters;
57  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getParticleParameters;
58  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_getPerParticleParameterName;
59  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_setEnergyFunction;
60  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_setGlobalParameterDefaultValue;
61  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_setGlobalParameterName;
62  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_setParticleParameters;
63  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_setPerParticleParameterName;
64  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_updateParametersInContext;
65  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_CustomExternalForce_usesPeriodicBoundaryConditions;
66  
67  /**
68   * This class implements an "external" force on particles.  The force may be applied to any subset of the particles
69   * in the System.  The force on each particle is specified by an arbitrary algebraic expression, which may depend
70   * on the current position of the particle as well as on arbitrary global and per-particle parameters.
71   * <p>
72   * To use this class, create a CustomExternalForce object, passing an algebraic expression to the constructor
73   * that defines the potential energy of each affected particle.  The expression may depend on the particle's x, y, and
74   * z coordinates, as well as on any parameters you choose.  Then call addPerParticleParameter() to define per-particle
75   * parameters, and addGlobalParameter() to define global parameters.  The values of per-particle parameters are specified as
76   * part of the system definition, while values of global parameters may be modified during a simulation by calling Context::setParameter().
77   * Finally, call addParticle() once for each particle that should be affected by the force.  After a particle has been added,
78   * you can modify its parameters by calling setParticleParameters().  This will have no effect on Contexts that already exist unless
79   * you call updateParametersInContext().
80   * <p>
81   * As an example, the following code creates a CustomExternalForce that attracts each particle to a target position (x0, y0, z0)
82   * via a harmonic potential:
83   * <pre>
84   *   {@code
85   *    CustomExternalForce* force = new CustomExternalForce("k*((x-x0)^2+(y-y0)^2+(z-z0)^2)");
86   *   }
87   * </pre>
88   * <p>
89   * This force depends on four parameters: the spring constant k and equilibrium coordinates x0, y0, and z0.  The following code defines these parameters:
90   * <pre>
91   *   {@code
92   *    force->addGlobalParameter("k", 100.0);
93   *    force->addPerParticleParameter("x0");
94   *    force->addPerParticleParameter("y0");
95   *    force->addPerParticleParameter("z0");
96   *   }
97   * </pre>
98   * <p>
99   * Special care is needed in systems that use periodic boundary conditions.  In that case, each particle really represents
100  * an infinite set of particles repeating through space.  The variables x, y, and z contain the coordinates of one of those
101  * periodic copies, but there is no guarantee about which.  It might even change from one time step to the next.  You can handle
102  * this situation by using the function periodicdistance(x1, y1, z1, x2, y2, z2), which returns the minimum distance between
103  * periodic copies of the points (x1, y1, z1) and (x2, y2, z2).  For example, the force given above would be rewritten as
104  * <pre>
105  *   {@code
106  *    CustomExternalForce* force = new CustomExternalForce("k*periodicdistance(x, y, z, x0, y0, z0)^2");
107  *   }
108  * </pre>
109  * <p>
110  * Expressions may involve the operators + (add), - (subtract), * (multiply), / (divide), and &circ; (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 is less than 0, 1 otherwise.  delta(x) = 1 if x is 0, 0 otherwise.
113  * select(x,y,z) = z if x = 0, y otherwise.
114  */
115 public class CustomExternalForce extends Force {
116 
117   /**
118    * Create a CustomExternalForce.
119    *
120    * @param energy The energy expression for the force.
121    */
122   public CustomExternalForce(String energy) {
123     super(OpenMM_CustomExternalForce_create(energy));
124   }
125 
126   /**
127    * Add a global parameter that the interaction may depend on.
128    *
129    * @param name         The name of the parameter.
130    * @param defaultValue The default value of the parameter.
131    * @return The index of the parameter that was added.
132    */
133   public int addGlobalParameter(String name, double defaultValue) {
134     return OpenMM_CustomExternalForce_addGlobalParameter(pointer, name, defaultValue);
135   }
136 
137   /**
138    * Add a particle to the force.
139    *
140    * @param index              The particle index.
141    * @param particleParameters The particle parameters.
142    * @return The index of the particle that was added.
143    */
144   public int addParticle(int index, DoubleArray particleParameters) {
145     return OpenMM_CustomExternalForce_addParticle(pointer, index, particleParameters.getPointer());
146   }
147 
148   /**
149    * Add a per-particle parameter that the interaction may depend on.
150    *
151    * @param name The name of the parameter.
152    * @return The index of the parameter that was added.
153    */
154   public int addPerParticleParameter(String name) {
155     return OpenMM_CustomExternalForce_addPerParticleParameter(pointer, name);
156   }
157 
158   /**
159    * Destroy the force.
160    */
161   @Override
162   public void destroy() {
163     if (pointer != null) {
164       OpenMM_CustomExternalForce_destroy(pointer);
165       pointer = null;
166     }
167   }
168 
169   /**
170    * Get the energy expression for the force.
171    *
172    * @return The energy expression for the force.
173    */
174   public String getEnergyFunction() {
175     Pointer p = OpenMM_CustomExternalForce_getEnergyFunction(pointer);
176     if (p == null) {
177       return null;
178     }
179     return p.getString(0);
180   }
181 
182   /**
183    * Get the default value of a global parameter.
184    *
185    * @param index The index of the parameter.
186    * @return The default value of the parameter.
187    */
188   public double getGlobalParameterDefaultValue(int index) {
189     return OpenMM_CustomExternalForce_getGlobalParameterDefaultValue(pointer, index);
190   }
191 
192   /**
193    * Get the name of a global parameter.
194    *
195    * @param index The index of the parameter.
196    * @return The name of the parameter.
197    */
198   public String getGlobalParameterName(int index) {
199     Pointer p = OpenMM_CustomExternalForce_getGlobalParameterName(pointer, index);
200     if (p == null) {
201       return null;
202     }
203     return p.getString(0);
204   }
205 
206   /**
207    * Get the number of global parameters.
208    *
209    * @return The number of global parameters.
210    */
211   public int getNumGlobalParameters() {
212     return OpenMM_CustomExternalForce_getNumGlobalParameters(pointer);
213   }
214 
215   /**
216    * Get the number of particles.
217    *
218    * @return The number of particles.
219    */
220   public int getNumParticles() {
221     return OpenMM_CustomExternalForce_getNumParticles(pointer);
222   }
223 
224   /**
225    * Get the number of per-particle parameters.
226    *
227    * @return The number of per-particle parameters.
228    */
229   public int getNumPerParticleParameters() {
230     return OpenMM_CustomExternalForce_getNumPerParticleParameters(pointer);
231   }
232 
233   /**
234    * Get the parameters for a particle.
235    *
236    * @param index              The index of the particle.
237    * @param particle           The index of the particle (output).
238    * @param particleParameters The particle parameters (output).
239    */
240   public void getParticleParameters(int index, IntBuffer particle, DoubleArray particleParameters) {
241     OpenMM_CustomExternalForce_getParticleParameters(pointer, index, particle, particleParameters.getPointer());
242   }
243 
244   /**
245    * Get the parameters for a particle.
246    *
247    * @param index              The index of the particle.
248    * @param particle           The index of the particle (output).
249    * @param particleParameters The particle parameters (output).
250    */
251   public void getParticleParameters(int index, IntByReference particle, DoubleArray particleParameters) {
252     OpenMM_CustomExternalForce_getParticleParameters(pointer, index, particle, particleParameters.getPointer());
253   }
254 
255   /**
256    * Get the name of a per-particle parameter.
257    *
258    * @param index The index of the parameter.
259    * @return The name of the parameter.
260    */
261   public String getPerParticleParameterName(int index) {
262     Pointer p = OpenMM_CustomExternalForce_getPerParticleParameterName(pointer, index);
263     if (p == null) {
264       return null;
265     }
266     return p.getString(0);
267   }
268 
269   /**
270    * Set the energy expression for the force.
271    *
272    * @param energy The energy expression for the force.
273    */
274   public void setEnergyFunction(String energy) {
275     OpenMM_CustomExternalForce_setEnergyFunction(pointer, energy);
276   }
277 
278   /**
279    * Set the default value of a global parameter.
280    *
281    * @param index        The index of the parameter.
282    * @param defaultValue The default value of the parameter.
283    */
284   public void setGlobalParameterDefaultValue(int index, double defaultValue) {
285     OpenMM_CustomExternalForce_setGlobalParameterDefaultValue(pointer, index, defaultValue);
286   }
287 
288   /**
289    * Set the name of a global parameter.
290    *
291    * @param index The index of the parameter.
292    * @param name  The name of the parameter.
293    */
294   public void setGlobalParameterName(int index, String name) {
295     OpenMM_CustomExternalForce_setGlobalParameterName(pointer, index, name);
296   }
297 
298   /**
299    * Set the parameters for a particle.
300    *
301    * @param index              The index of the particle.
302    * @param particle           The index of the particle.
303    * @param particleParameters The particle parameters.
304    */
305   public void setParticleParameters(int index, int particle, DoubleArray particleParameters) {
306     OpenMM_CustomExternalForce_setParticleParameters(pointer, index, particle, particleParameters.getPointer());
307   }
308 
309   /**
310    * Set the name of a per-particle parameter.
311    *
312    * @param index The index of the parameter.
313    * @param name  The name of the parameter.
314    */
315   public void setPerParticleParameterName(int index, String name) {
316     OpenMM_CustomExternalForce_setPerParticleParameterName(pointer, index, name);
317   }
318 
319   /**
320    * Update the parameters in the context.
321    *
322    * @param context The context to update.
323    */
324   public void updateParametersInContext(Context context) {
325     if (context.hasContextPointer()) {
326       OpenMM_CustomExternalForce_updateParametersInContext(pointer, context.getPointer());
327     }
328   }
329 
330   /**
331    * Check if the force uses periodic boundary conditions.
332    *
333    * @return True if the force uses periodic boundary conditions.
334    */
335   @Override
336   public boolean usesPeriodicBoundaryConditions() {
337     int pbc = OpenMM_CustomExternalForce_usesPeriodicBoundaryConditions(pointer);
338     return pbc == OpenMM_True;
339   }
340 }