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.Force;
41  import ffx.openmm.PeriodicTorsionForce;
42  import ffx.potential.ForceFieldEnergy;
43  import ffx.potential.bonded.ImproperTorsion;
44  import ffx.potential.parameters.ImproperTorsionType;
45  import ffx.potential.terms.ImproperTorsionPotentialEnergy;
46  
47  import java.util.logging.Logger;
48  
49  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
50  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_RadiansPerDegree;
51  import static java.lang.String.format;
52  
53  /**
54   * OpenMM Improper Torsion Force.
55   */
56  public class ImproperTorsionForce extends PeriodicTorsionForce {
57  
58    private static final Logger logger = Logger.getLogger(ImproperTorsionForce.class.getName());
59  
60    /**
61     * Create an OpenMM Improper Torsion Force.
62     *
63     * @param improperTorsionPotentialEnergy The ImproperTorsionPotentialEnergy instance that contains the improper torsions.
64     */
65    public ImproperTorsionForce(ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy) {
66      ImproperTorsion[] improperTorsions = improperTorsionPotentialEnergy.getImproperTorsionArray();
67      for (ImproperTorsion improperTorsion : improperTorsions) {
68        int a1 = improperTorsion.getAtom(0).getArrayIndex();
69        int a2 = improperTorsion.getAtom(1).getArrayIndex();
70        int a3 = improperTorsion.getAtom(2).getArrayIndex();
71        int a4 = improperTorsion.getAtom(3).getArrayIndex();
72        ImproperTorsionType type = improperTorsion.improperType;
73        double forceConstant = OpenMM_KJPerKcal * type.impTorUnit * improperTorsion.scaleFactor * type.k;
74        addTorsion(a1, a2, a3, a4, type.periodicity, type.phase * OpenMM_RadiansPerDegree, forceConstant);
75      }
76      int forceGroup = improperTorsionPotentialEnergy.getForceGroup();
77      setForceGroup(forceGroup);
78      logger.info(format("  Improper Torsions:                 %10d", improperTorsions.length));
79      logger.fine(format("   Force Group:                      %10d", forceGroup));
80    }
81  
82    /**
83     * Create a Dual Topology OpenMM Improper Torsion Force.
84     *
85     * @param improperTorsionPotentialEnergy The ImproperTorsionPotentialEnergy instance that contains the improper torsions.
86     * @param topology                       The topology index for the OpenMM System.
87     * @param openMMDualTopologyEnergy       The OpenMMDualTopologyEnergy instance.
88     */
89    public ImproperTorsionForce(ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy,
90                                int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
91      ImproperTorsion[] improperTorsions = improperTorsionPotentialEnergy.getImproperTorsionArray();
92      double scale = openMMDualTopologyEnergy.getTopologyScale(topology);
93  
94      for (ImproperTorsion improperTorsion : improperTorsions) {
95        int a1 = improperTorsion.getAtom(0).getArrayIndex();
96        int a2 = improperTorsion.getAtom(1).getArrayIndex();
97        int a3 = improperTorsion.getAtom(2).getArrayIndex();
98        int a4 = improperTorsion.getAtom(3).getArrayIndex();
99        a1 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a1);
100       a2 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a2);
101       a3 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a3);
102       a4 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a4);
103       ImproperTorsionType type = improperTorsion.improperType;
104       double forceConstant = OpenMM_KJPerKcal * type.impTorUnit * improperTorsion.scaleFactor * type.k;
105       // Don't apply lambda scale to alchemical improper torsion
106       if (!improperTorsion.applyLambda()) {
107         forceConstant *= scale;
108       }
109       addTorsion(a1, a2, a3, a4, type.periodicity, type.phase * OpenMM_RadiansPerDegree, forceConstant);
110     }
111 
112     int forceGroup = improperTorsionPotentialEnergy.getForceGroup();
113     setForceGroup(forceGroup);
114     logger.info(format("  Improper Torsions:                 %10d", improperTorsions.length));
115     logger.fine(format("   Force Group:                      %10d", forceGroup));
116   }
117 
118   /**
119    * Convenience method to construct an OpenMM Improper Torsion Force.
120    *
121    * @param openMMEnergy The OpenMM Energy instance that contains the improper torsions.
122    * @return An Improper Torsion Force, or null if there are no improper torsions.
123    */
124   public static Force constructForce(OpenMMEnergy openMMEnergy) {
125     ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy = openMMEnergy.getImproperTorsionPotentialEnergy();
126     if (improperTorsionPotentialEnergy == null) {
127       return null;
128     }
129     return new ImproperTorsionForce(improperTorsionPotentialEnergy);
130   }
131 
132   /**
133    * Convenience method to construct a Dual Topology OpenMM Improper Torsion Force.
134    *
135    * @param topology                 The topology index for the OpenMM System.
136    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
137    * @return A Torsion Force, or null if there are no torsions.
138    */
139   public static Force constructForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
140     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
141     ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy = forceFieldEnergy.getImproperTorsionPotentialEnergy();
142     if (improperTorsionPotentialEnergy == null) {
143       return null;
144     }
145     return new ImproperTorsionForce(improperTorsionPotentialEnergy, topology, openMMDualTopologyEnergy);
146   }
147 
148   /**
149    * Update the Improper Torsion force.
150    *
151    * @param openMMEnergy The OpenMM Energy that contains the improper torsions.
152    */
153   public void updateForce(OpenMMEnergy openMMEnergy) {
154     ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy = openMMEnergy.getImproperTorsionPotentialEnergy();
155     if (improperTorsionPotentialEnergy == null) {
156       return;
157     }
158     ImproperTorsion[] improperTorsions = improperTorsionPotentialEnergy.getImproperTorsionArray();
159     int nImproperTorsions = improperTorsions.length;
160     for (int i = 0; i < nImproperTorsions; i++) {
161       ImproperTorsion improperTorsion = improperTorsions[i];
162       int a1 = improperTorsion.getAtom(0).getXyzIndex() - 1;
163       int a2 = improperTorsion.getAtom(1).getXyzIndex() - 1;
164       int a3 = improperTorsion.getAtom(2).getXyzIndex() - 1;
165       int a4 = improperTorsion.getAtom(3).getXyzIndex() - 1;
166       ImproperTorsionType type = improperTorsion.improperType;
167       double forceConstant = OpenMM_KJPerKcal * type.impTorUnit * improperTorsion.scaleFactor * type.k;
168       setTorsionParameters(i, a1, a2, a3, a4, type.periodicity, type.phase * OpenMM_RadiansPerDegree, forceConstant);
169     }
170 
171     updateParametersInContext(openMMEnergy.getContext());
172   }
173 
174   /**
175    * Update the Dual Topology Improper Torsion force.
176    *
177    * @param topology                 The topology index for the OpenMM System.
178    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
179    */
180   public void updateForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
181     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
182     ImproperTorsionPotentialEnergy improperTorsionPotentialEnergy = forceFieldEnergy.getImproperTorsionPotentialEnergy();
183     if (improperTorsionPotentialEnergy == null) {
184       return;
185     }
186     ImproperTorsion[] improperTorsions = improperTorsionPotentialEnergy.getImproperTorsionArray();
187     double scale = openMMDualTopologyEnergy.getTopologyScale(topology);
188 
189     int nImproperTorsions = improperTorsions.length;
190     for (int i = 0; i < nImproperTorsions; i++) {
191       ImproperTorsion improperTorsion = improperTorsions[i];
192       int a1 = improperTorsion.getAtom(0).getArrayIndex();
193       int a2 = improperTorsion.getAtom(1).getArrayIndex();
194       int a3 = improperTorsion.getAtom(2).getArrayIndex();
195       int a4 = improperTorsion.getAtom(3).getArrayIndex();
196       a1 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a1);
197       a2 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a2);
198       a3 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a3);
199       a4 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, a4);
200       ImproperTorsionType type = improperTorsion.improperType;
201       double forceConstant = OpenMM_KJPerKcal * type.impTorUnit * improperTorsion.scaleFactor * type.k;
202       // Don't apply lambda scale to alchemical improper torsion
203       if (!improperTorsion.applyLambda()) {
204         forceConstant *= scale;
205       }
206       setTorsionParameters(i, a1, a2, a3, a4, type.periodicity, type.phase * OpenMM_RadiansPerDegree, forceConstant);
207     }
208 
209     updateParametersInContext(openMMDualTopologyEnergy.getContext());
210   }
211 
212 }