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 }