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.CustomBondForce;
41  import ffx.openmm.DoubleArray;
42  import ffx.openmm.Force;
43  import ffx.potential.ForceFieldEnergy;
44  import ffx.potential.bonded.Bond;
45  import ffx.potential.parameters.BondType;
46  import ffx.potential.terms.BondPotentialEnergy;
47  
48  import java.util.logging.Logger;
49  
50  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KJPerKcal;
51  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
52  import static java.lang.String.format;
53  
54  /**
55   * Bond Force.
56   */
57  public class BondForce extends CustomBondForce {
58  
59    private static final Logger logger = Logger.getLogger(BondForce.class.getName());
60  
61    /**
62     * Bond Force constructor.
63     *
64     * @param bondPotentialEnergy BondPotentialEnergy that contains the Bond instances
65     */
66    public BondForce(BondPotentialEnergy bondPotentialEnergy) {
67      super(bondPotentialEnergy.getBondEnergyString());
68      Bond[] bonds = bondPotentialEnergy.getBondArray();
69      addPerBondParameter("r0");
70      addPerBondParameter("k");
71      setName("AmoebaBond");
72  
73      double kParameterConversion = OpenMM_KJPerKcal / (OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom);
74      DoubleArray parameters = new DoubleArray(0);
75      for (Bond bond : bonds) {
76        int i1 = bond.getAtom(0).getArrayIndex();
77        int i2 = bond.getAtom(1).getArrayIndex();
78        BondType bondType = bond.bondType;
79        double r0 = bondType.distance * OpenMM_NmPerAngstrom;
80        double k = kParameterConversion * bondType.forceConstant * bond.bondType.bondUnit;
81        parameters.append(r0);
82        parameters.append(k);
83        addBond(i1, i2, parameters);
84        parameters.resize(0);
85      }
86      parameters.destroy();
87  
88      int forceGroup = bondPotentialEnergy.getForceGroup();
89      setForceGroup(forceGroup);
90      logger.info(format("  Bonds:                             %10d", bonds.length));
91      logger.fine(format("   Force Group:                      %10d", forceGroup));
92    }
93  
94    /**
95     * Bond Force constructor.
96     *
97     * @param bondPotentialEnergy      BondPotentialEnergy that contains the Bond instances
98     * @param topology                 The topology index for the OpenMM System.
99     * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
100    */
101   public BondForce(BondPotentialEnergy bondPotentialEnergy,
102                    int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
103     super(bondPotentialEnergy.getBondEnergyString());
104     Bond[] bonds = bondPotentialEnergy.getBondArray();
105     addPerBondParameter("r0");
106     addPerBondParameter("k");
107     setName("AmoebaBond");
108 
109     double scale = openMMDualTopologyEnergy.getTopologyScale(topology);
110 
111     double kParameterConversion = OpenMM_KJPerKcal / (OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom);
112     DoubleArray parameters = new DoubleArray(0);
113     for (Bond bond : bonds) {
114       int i1 = bond.getAtom(0).getArrayIndex();
115       int i2 = bond.getAtom(1).getArrayIndex();
116       BondType bondType = bond.bondType;
117       double r0 = bondType.distance * OpenMM_NmPerAngstrom;
118       double k = kParameterConversion * bondType.forceConstant * bond.bondType.bondUnit;
119       // Don't apply lambda scale to alchemical bond
120       if (!bond.applyLambda()) {
121         k = k * scale;
122       }
123       parameters.append(r0);
124       parameters.append(k);
125       i1 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, i1);
126       i2 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, i2);
127       addBond(i1, i2, parameters);
128       parameters.resize(0);
129     }
130     parameters.destroy();
131 
132     int forceGroup = bondPotentialEnergy.getForceGroup();
133     setForceGroup(forceGroup);
134     logger.info(format("  Bonds:                             %10d", bonds.length));
135     logger.fine(format("   Force Group:                      %10d", forceGroup));
136   }
137 
138   /**
139    * Creat a bond force for the OpenMM System.
140    *
141    * @param openMMEnergy OpenMM Energy that contains the Bond instances.
142    */
143   public static Force constructForce(OpenMMEnergy openMMEnergy) {
144     BondPotentialEnergy bondPotentialEnergy = openMMEnergy.getBondPotentialEnergy();
145     if (bondPotentialEnergy == null) {
146       return null;
147     }
148     return new BondForce(bondPotentialEnergy);
149   }
150 
151   /**
152    * Add a bond force to the OpenMM System.
153    *
154    * @param topology                 The topology index for the OpenMM System.
155    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
156    */
157   public static Force constructForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
158     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
159     BondPotentialEnergy bondPotentialEnergy = forceFieldEnergy.getBondPotentialEnergy();
160     if (bondPotentialEnergy == null) {
161       return null;
162     }
163     return new BondForce(bondPotentialEnergy, topology, openMMDualTopologyEnergy);
164   }
165 
166   /**
167    * Update an existing bond force for the OpenMM System.
168    */
169   public void updateForce(OpenMMEnergy openMMEnergy) {
170     BondPotentialEnergy bondPotentialEnergy = openMMEnergy.getBondPotentialEnergy();
171     if (bondPotentialEnergy == null) {
172       return;
173     }
174     Bond[] bonds = bondPotentialEnergy.getBondArray();
175 
176     double kParameterConversion = OpenMM_KJPerKcal / (OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom);
177     DoubleArray parameters = new DoubleArray(0);
178     int index = 0;
179     for (Bond bond : bonds) {
180       int i1 = bond.getAtom(0).getArrayIndex();
181       int i2 = bond.getAtom(1).getArrayIndex();
182       BondType bondType = bond.bondType;
183       double r0 = bondType.distance * OpenMM_NmPerAngstrom;
184       double k = kParameterConversion * bondType.forceConstant * bondType.bondUnit;
185       parameters.append(r0);
186       parameters.append(k);
187       setBondParameters(index++, i1, i2, parameters);
188       parameters.resize(0);
189     }
190     parameters.destroy();
191     updateParametersInContext(openMMEnergy.getContext());
192   }
193 
194   /**
195    * Update an existing bond force for the OpenMM System.
196    *
197    * @param topology                 The topology index for the OpenMM System.
198    * @param openMMDualTopologyEnergy The OpenMMDualTopologyEnergy instance.
199    */
200   public void updateForce(int topology, OpenMMDualTopologyEnergy openMMDualTopologyEnergy) {
201     ForceFieldEnergy forceFieldEnergy = openMMDualTopologyEnergy.getForceFieldEnergy(topology);
202     BondPotentialEnergy bondPotentialEnergy = forceFieldEnergy.getBondPotentialEnergy();
203     if (bondPotentialEnergy == null) {
204       return;
205     }
206     Bond[] bonds = bondPotentialEnergy.getBondArray();
207 
208     double scale = openMMDualTopologyEnergy.getTopologyScale(topology);
209 
210     double kParameterConversion = OpenMM_KJPerKcal / (OpenMM_NmPerAngstrom * OpenMM_NmPerAngstrom);
211     DoubleArray parameters = new DoubleArray(0);
212     int index = 0;
213     for (Bond bond : bonds) {
214       int i1 = bond.getAtom(0).getArrayIndex();
215       int i2 = bond.getAtom(1).getArrayIndex();
216       BondType bondType = bond.bondType;
217       double r0 = bondType.distance * OpenMM_NmPerAngstrom;
218       double k = kParameterConversion * bondType.forceConstant * bondType.bondUnit;
219       // Don't apply lambda scale to alchemical bond
220       if (!bond.applyLambda()) {
221         k = k * scale;
222       }
223       parameters.append(r0);
224       parameters.append(k);
225       i1 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, i1);
226       i2 = openMMDualTopologyEnergy.mapToDualTopologyIndex(topology, i2);
227       setBondParameters(index++, i1, i2, parameters);
228       parameters.resize(0);
229     }
230     parameters.destroy();
231     updateParametersInContext(openMMDualTopologyEnergy.getContext());
232   }
233 
234 }