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.potential.openmm;
39  
40  import ffx.openmm.CustomCompoundBondForce;
41  import ffx.openmm.DoubleArray;
42  import ffx.openmm.Force;
43  import ffx.openmm.IntArray;
44  import ffx.potential.ForceFieldEnergy;
45  import ffx.potential.bonded.Atom;
46  import ffx.potential.bonded.StretchTorsion;
47  import ffx.potential.terms.StretchTorsionPotentialEnergy;
48  
49  import java.util.logging.Logger;
50  
51  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
52  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
53  import static java.lang.String.format;
54  
55  /**
56   * OpenMM Stretch-Torsion Force.
57   */
58  public class StretchTorsionForce extends CustomCompoundBondForce {
59  
60    private static final Logger logger = Logger.getLogger(StretchTorsionForce.class.getName());
61  
62    /**
63     * Create an OpenMM Stretch-Torsion Force.
64     *
65     * @param stretchTorsionPotentialEnergy The StretchTorsionPotentialEnergy instance that contains the stretch-torsions.
66     */
67    public StretchTorsionForce(StretchTorsionPotentialEnergy stretchTorsionPotentialEnergy) {
68      super(4, StretchTorsion.stretchTorsionForm());
69      StretchTorsion[] stretchTorsions = stretchTorsionPotentialEnergy.getStretchTorsionArray();
70      addGlobalParameter("phi1", 0);
71      addGlobalParameter("phi2", Math.PI);
72      addGlobalParameter("phi3", 0);
73      for (int m = 1; m < 4; m++) {
74        for (int n = 1; n < 4; n++) {
75          addPerBondParameter(format("k%d%d", m, n));
76        }
77      }
78      for (int m = 1; m < 4; m++) {
79        addPerBondParameter(format("b%d", m));
80      }
81  
82      final double unitConv = OpenMM_KJPerKcal / OpenMM_NmPerAngstrom;
83  
84      for (StretchTorsion stretchTorsion : stretchTorsions) {
85        double[] constants = stretchTorsion.getConstants();
86        DoubleArray parameters = new DoubleArray(0);
87        for (int m = 0; m < 3; m++) {
88          for (int n = 0; n < 3; n++) {
89            int index = (3 * m) + n;
90            parameters.append(constants[index] * unitConv);
91          }
92        }
93        parameters.append(stretchTorsion.bondType1.distance * OpenMM_NmPerAngstrom);
94        parameters.append(stretchTorsion.bondType2.distance * OpenMM_NmPerAngstrom);
95        parameters.append(stretchTorsion.bondType3.distance * OpenMM_NmPerAngstrom);
96  
97        IntArray particles = new IntArray(0);
98        Atom[] atoms = stretchTorsion.getAtomArray(true);
99        for (int i = 0; i < 4; i++) {
100         particles.append(atoms[i].getArrayIndex());
101       }
102 
103       addBond(particles, parameters);
104       parameters.destroy();
105       particles.destroy();
106     }
107 
108     int forceGroup = stretchTorsionPotentialEnergy.getForceGroup();
109     setForceGroup(forceGroup);
110     logger.info(format("  Stretch-Torsions:                  %10d", stretchTorsions.length));
111     logger.fine(format("   Force Group:                      %10d", forceGroup));
112   }
113 
114   /**
115    * Create a Dual Topology OpenMM Stretch-Torsion Force.
116    *
117    * @param stretchPotentialEnergy   The StretchTorsionPotentialEnergy instance that contains the stretch-torsions.
118    * @param topology                 The topology index for the OpenMM System.
119    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
120    */
121   public StretchTorsionForce(StretchTorsionPotentialEnergy stretchPotentialEnergy,
122                              int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
123     super(4, StretchTorsion.stretchTorsionForm());
124     StretchTorsion[] stretchTorsions = stretchPotentialEnergy.getStretchTorsionArray();
125     addGlobalParameter("phi1", 0);
126     addGlobalParameter("phi2", Math.PI);
127     addGlobalParameter("phi3", 0);
128     for (int m = 1; m < 4; m++) {
129       for (int n = 1; n < 4; n++) {
130         addPerBondParameter(format("k%d%d", m, n));
131       }
132     }
133     for (int m = 1; m < 4; m++) {
134       addPerBondParameter(format("b%d", m));
135     }
136 
137     final double unitConv = OpenMM_KJPerKcal / OpenMM_NmPerAngstrom;
138     double scaleDT = openMMDualTopologyEnergy.getTopologyScale(topology);
139 
140     for (StretchTorsion stretchTorsion : stretchTorsions) {
141       double scale = 1.0;
142       // Don't apply lambda scale to alchemical stretch-torsion
143       if (!stretchTorsion.applyLambda()) {
144         scale = scaleDT;
145       }
146       double[] constants = stretchTorsion.getConstants();
147       DoubleArray parameters = new DoubleArray(0);
148       for (int m = 0; m < 3; m++) {
149         for (int n = 0; n < 3; n++) {
150           int index = (3 * m) + n;
151           parameters.append(constants[index] * unitConv * scale);
152         }
153       }
154       parameters.append(stretchTorsion.bondType1.distance * OpenMM_NmPerAngstrom);
155       parameters.append(stretchTorsion.bondType2.distance * OpenMM_NmPerAngstrom);
156       parameters.append(stretchTorsion.bondType3.distance * OpenMM_NmPerAngstrom);
157 
158       IntArray particles = new IntArray(0);
159       Atom[] atoms = stretchTorsion.getAtomArray(true);
160       for (int i = 0; i < 4; i++) {
161         int atomIndex = atoms[i].getArrayIndex();
162         atomIndex = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, atomIndex);
163         particles.append(atomIndex);
164       }
165 
166       addBond(particles, parameters);
167       parameters.destroy();
168       particles.destroy();
169     }
170 
171     int forceGroup = stretchPotentialEnergy.getForceGroup();
172     setForceGroup(forceGroup);
173     logger.info(format("  Stretch-Torsions:                  %10d", stretchTorsions.length));
174     logger.fine(format("   Force Group:                      %10d", forceGroup));
175   }
176 
177   /**
178    * Convenience method to construct an OpenMM Stretch-Torsion Force.
179    *
180    * @param openMMEnergy The OpenMM Energy instance that contains the stretch-torsions.
181    * @return An OpenMM Stretch-Bend Force, or null if there are no stretch-torsion.
182    */
183   public static Force constructForce(OpenMMEnergy openMMEnergy) {
184     StretchTorsionPotentialEnergy stretchTorsionPotentialEnergy =
185         openMMEnergy.getStretchTorsionPotentialEnergy();
186     if (stretchTorsionPotentialEnergy == null) {
187       return null;
188     }
189     return new StretchTorsionForce(stretchTorsionPotentialEnergy);
190   }
191 
192   /**
193    * Convenience method to construct a Dual Topology OpenMM Stretch-Torsion Force.
194    *
195    * @param topology                 The topology index for the OpenMM System.
196    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
197    * @return An OpenMM Stretch-Bend Force, or null if there are no stretch-torsion.
198    */
199   public static Force constructForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
200     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
201     StretchTorsionPotentialEnergy stretchTorsionPotentialEnergy =
202         forceFieldEnergy.getStretchTorsionPotentialEnergy();
203     if (stretchTorsionPotentialEnergy == null) {
204       return null;
205     }
206     return new StretchTorsionForce(stretchTorsionPotentialEnergy, topology, openMMDualTopologyEnergy);
207   }
208 
209   /**
210    * Update the Dual Topology Stretch-Torsion Force.
211    *
212    * @param topology                 The topology index for the OpenMM System.
213    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
214    */
215   public void updateForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
216     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
217     StretchTorsionPotentialEnergy stretchTorsionPotentialEnergy =
218         forceFieldEnergy.getStretchTorsionPotentialEnergy();
219     if (stretchTorsionPotentialEnergy == null) {
220       return;
221     }
222     StretchTorsion[] stretchTorsions = stretchTorsionPotentialEnergy.getStretchTorsionArray();
223     final double unitConv = OpenMM_KJPerKcal / OpenMM_NmPerAngstrom;
224     double scaleDT = openMMDualTopologyEnergy.getTopologyScale(topology);
225 
226     int stIndex = 0;
227     for (StretchTorsion stretchTorsion : stretchTorsions) {
228       double scale = 1.0;
229       // Don't apply lambda scale to alchemical stretch-torsion
230       if (!stretchTorsion.applyLambda()) {
231         scale = scaleDT;
232       }
233       double[] constants = stretchTorsion.getConstants();
234       DoubleArray parameters = new DoubleArray(0);
235       for (int m = 0; m < 3; m++) {
236         for (int n = 0; n < 3; n++) {
237           int index = (3 * m) + n;
238           parameters.append(constants[index] * unitConv * scale);
239         }
240       }
241       parameters.append(stretchTorsion.bondType1.distance * OpenMM_NmPerAngstrom);
242       parameters.append(stretchTorsion.bondType2.distance * OpenMM_NmPerAngstrom);
243       parameters.append(stretchTorsion.bondType3.distance * OpenMM_NmPerAngstrom);
244 
245       IntArray particles = new IntArray(0);
246       Atom[] atoms = stretchTorsion.getAtomArray(true);
247       for (int i = 0; i < 4; i++) {
248         int atomIndex = atoms[i].getArrayIndex();
249         atomIndex = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, atomIndex);
250         particles.append(atomIndex);
251       }
252 
253       setBondParameters(stIndex++, particles, parameters);
254       parameters.destroy();
255       particles.destroy();
256     }
257 
258     updateParametersInContext(openMMDualTopologyEnergy.getContext());
259   }
260 }