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 ˆ (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 }