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 }