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.DoubleArray;
41  import ffx.openmm.Force;
42  import ffx.openmm.amoeba.DoubleArray3D;
43  import ffx.openmm.amoeba.TorsionTorsionForce;
44  import ffx.potential.ForceFieldEnergy;
45  import ffx.potential.bonded.Atom;
46  import ffx.potential.bonded.TorsionTorsion;
47  import ffx.potential.parameters.TorsionTorsionType;
48  import ffx.potential.terms.TorsionTorsionPotentialEnergy;
49  
50  import java.util.LinkedHashMap;
51  import java.util.Map;
52  import java.util.logging.Logger;
53  
54  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
55  import static java.lang.String.format;
56  
57  /**
58   * OpenMM TorsionTorsion Force.
59   */
60  public class AmoebaTorsionTorsionForce extends TorsionTorsionForce {
61  
62    private static final Logger logger = Logger.getLogger(AmoebaTorsionTorsionForce.class.getName());
63  
64    /**
65     * Create an OpenMM TorsionTorsion Force.
66     *
67     * @param torsionTorsionPotentialEnergy The TorsionTorsionPotentialEnergy instance.
68     */
69    public AmoebaTorsionTorsionForce(TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy) {
70      TorsionTorsion[] torsionTorsions = torsionTorsionPotentialEnergy.getTorsionTorsionArray();
71  
72      // Load the torsion-torsions.
73      Map<String, TorsionTorsionType> torTorTypes = new LinkedHashMap<>();
74      for (TorsionTorsion torsionTorsion : torsionTorsions) {
75        int ia = torsionTorsion.getAtom(0).getArrayIndex();
76        int ib = torsionTorsion.getAtom(1).getArrayIndex();
77        int ic = torsionTorsion.getAtom(2).getArrayIndex();
78        int id = torsionTorsion.getAtom(3).getArrayIndex();
79        int ie = torsionTorsion.getAtom(4).getArrayIndex();
80  
81        TorsionTorsionType torsionTorsionType = torsionTorsion.torsionTorsionType;
82        String key = torsionTorsionType.getKey();
83  
84        // Check if the TorTor parameters have already been added to the Hash.
85        int gridIndex = 0;
86        if (torTorTypes.containsKey(key)) {
87  
88          // If the TorTor has been added, get its (ordered) index in the Hash.
89          int index = 0;
90          for (String entry : torTorTypes.keySet()) {
91            if (entry.equalsIgnoreCase(key)) {
92              gridIndex = index;
93              break;
94            } else {
95              index++;
96            }
97          }
98        } else {
99          // Add the new TorTor.
100         torTorTypes.put(key, torsionTorsionType);
101         gridIndex = torTorTypes.size() - 1;
102       }
103 
104       Atom atom = torsionTorsion.getChiralAtom();
105       int iChiral = -1;
106       if (atom != null) {
107         iChiral = atom.getArrayIndex();
108       }
109       addTorsionTorsion(ia, ib, ic, id, ie, iChiral, gridIndex);
110     }
111 
112     // Load the Torsion-Torsion parameters.
113     DoubleArray values = new DoubleArray(6);
114     int gridIndex = 0;
115     for (String key : torTorTypes.keySet()) {
116       TorsionTorsionType torTorType = torTorTypes.get(key);
117       int nx = torTorType.nx;
118       int ny = torTorType.ny;
119       double[] tx = torTorType.tx;
120       double[] ty = torTorType.ty;
121       double[] f = torTorType.energy;
122       double[] dx = torTorType.dx;
123       double[] dy = torTorType.dy;
124       double[] dxy = torTorType.dxy;
125 
126       // Create the 3D grid.
127       DoubleArray3D grid3D = new DoubleArray3D(nx, ny, 6);
128       int xIndex = 0;
129       int yIndex = 0;
130       for (int j = 0; j < nx * ny; j++) {
131         int addIndex = 0;
132         values.set(addIndex++, tx[xIndex]);
133         values.set(addIndex++, ty[yIndex]);
134         values.set(addIndex++, OpenMM_KJPerKcal * f[j]);
135         values.set(addIndex++, OpenMM_KJPerKcal * dx[j]);
136         values.set(addIndex++, OpenMM_KJPerKcal * dy[j]);
137         values.set(addIndex, OpenMM_KJPerKcal * dxy[j]);
138         grid3D.set(yIndex, xIndex, values);
139         xIndex++;
140         if (xIndex == nx) {
141           xIndex = 0;
142           yIndex++;
143         }
144       }
145       setTorsionTorsionGrid(gridIndex++, grid3D.getPointer());
146       grid3D.destroy();
147     }
148     values.destroy();
149 
150     int forceGroup = torsionTorsionPotentialEnergy.getForceGroup();
151     setForceGroup(forceGroup);
152     logger.info(format("  Torsion-Torsions:                  %10d", torsionTorsions.length));
153     logger.fine(format("   Force Group:                      %10d", forceGroup));
154   }
155 
156   /**
157    * Create a Dual Topology OpenMM TorsionTorsion Force.
158    *
159    * @param topology                 The topology index for the OpenMM System.
160    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
161    */
162   public AmoebaTorsionTorsionForce(TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy,
163                                    int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
164     TorsionTorsion[] torsionTorsions = torsionTorsionPotentialEnergy.getTorsionTorsionArray();
165 
166     // ToDo: There is no support in OpenMM yet to updateParametersInContext for AmoebaTorsionTorsionForce.
167     // double scale = openMMDualTopologyEnergy.getTopologyScale(topology);
168 
169     Map<String, TorsionTorsionType> torTorTypes = new LinkedHashMap<>();
170     for (TorsionTorsion torsionTorsion : torsionTorsions) {
171       int ia = torsionTorsion.getAtom(0).getArrayIndex();
172       int ib = torsionTorsion.getAtom(1).getArrayIndex();
173       int ic = torsionTorsion.getAtom(2).getArrayIndex();
174       int id = torsionTorsion.getAtom(3).getArrayIndex();
175       int ie = torsionTorsion.getAtom(4).getArrayIndex();
176 
177       TorsionTorsionType torsionTorsionType = torsionTorsion.torsionTorsionType;
178       String key = torsionTorsionType.getKey();
179 
180       // Check if the TorTor parameters have already been added to the Hash.
181       int gridIndex = 0;
182       if (torTorTypes.containsKey(key)) {
183         // If the TorTor has been added, get its (ordered) index in the Hash.
184         int index = 0;
185         for (String entry : torTorTypes.keySet()) {
186           if (entry.equalsIgnoreCase(key)) {
187             gridIndex = index;
188             break;
189           } else {
190             index++;
191           }
192         }
193       } else {
194         // Add the new TorTor.
195         torTorTypes.put(key, torsionTorsionType);
196         gridIndex = torTorTypes.size() - 1;
197       }
198 
199       Atom atom = torsionTorsion.getChiralAtom();
200       int iChiral = -1;
201       if (atom != null) {
202         iChiral = atom.getArrayIndex();
203         iChiral = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, iChiral);
204       }
205       ia = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ia);
206       ib = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ib);
207       ic = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ic);
208       id = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, id);
209       ie = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, ie);
210       addTorsionTorsion(ia, ib, ic, id, ie, iChiral, gridIndex);
211     }
212 
213     // Load the Torsion-Torsion parameters.
214     DoubleArray values = new DoubleArray(6);
215     int gridIndex = 0;
216     for (String key : torTorTypes.keySet()) {
217       TorsionTorsionType torTorType = torTorTypes.get(key);
218       int nx = torTorType.nx;
219       int ny = torTorType.ny;
220       double[] tx = torTorType.tx;
221       double[] ty = torTorType.ty;
222       double[] f = torTorType.energy;
223       double[] dx = torTorType.dx;
224       double[] dy = torTorType.dy;
225       double[] dxy = torTorType.dxy;
226 
227       // Create the 3D grid.
228       DoubleArray3D grid3D = new DoubleArray3D(nx, ny, 6);
229       int xIndex = 0;
230       int yIndex = 0;
231       for (int j = 0; j < nx * ny; j++) {
232         int addIndex = 0;
233         values.set(addIndex++, tx[xIndex]);
234         values.set(addIndex++, ty[yIndex]);
235         values.set(addIndex++, OpenMM_KJPerKcal * f[j]);
236         values.set(addIndex++, OpenMM_KJPerKcal * dx[j]);
237         values.set(addIndex++, OpenMM_KJPerKcal * dy[j]);
238         values.set(addIndex, OpenMM_KJPerKcal * dxy[j]);
239         grid3D.set(yIndex, xIndex, values);
240         xIndex++;
241         if (xIndex == nx) {
242           xIndex = 0;
243           yIndex++;
244         }
245       }
246       setTorsionTorsionGrid(gridIndex++, grid3D.getPointer());
247       grid3D.destroy();
248     }
249     values.destroy();
250 
251     int forceGroup = torsionTorsionPotentialEnergy.getForceGroup();
252     setForceGroup(forceGroup);
253     logger.info(format("  Torsion-Torsions:                  %10d", torsionTorsions.length));
254     logger.fine(format("   Force Group:                      %10d", forceGroup));
255   }
256 
257   /**
258    * Convenience method to construct an OpenMM Torsion-Torsion Force.
259    *
260    * @param openMMEnergy The OpenMMEnergy instance.
261    * @return A Torsion-Torsion Force, or null if there are no torsion-torsions.
262    */
263   public static Force constructForce(OpenMMEnergy openMMEnergy) {
264     TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy = openMMEnergy.getTorsionTorsionPotentialEnergy();
265     if (torsionTorsionPotentialEnergy == null) {
266       return null;
267     }
268     return new AmoebaTorsionTorsionForce(torsionTorsionPotentialEnergy);
269   }
270 
271   /**
272    * Convenience method to construct a Dual Topology OpenMM Torsion-Torsion Force.
273    *
274    * @param topology                 The topology index for the OpenMM System.
275    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
276    * @return A Torsion-Torsion Force, or null if there are no torsion-torsions.
277    */
278   public static Force constructForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
279     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
280     TorsionTorsionPotentialEnergy torsionTorsionPotentialEnergy = forceFieldEnergy.getTorsionTorsionPotentialEnergy();
281     if (torsionTorsionPotentialEnergy == null) {
282       return null;
283     }
284     return new AmoebaTorsionTorsionForce(torsionTorsionPotentialEnergy, topology, openMMDualTopologyEnergy);
285   }
286 }