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-2024.
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 com.sun.jna.ptr.PointerByReference;
41  import ffx.openmm.State;
42  import ffx.potential.bonded.Atom;
43  import ffx.potential.utils.EnergyException;
44  
45  import javax.annotation.Nullable;
46  
47  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_AngstromsPerNm;
48  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_KcalPerKJ;
49  import static edu.uiowa.jopenmm.OpenMMAmoebaLibrary.OpenMM_NmPerAngstrom;
50  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Energy;
51  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Forces;
52  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Positions;
53  import static edu.uiowa.jopenmm.OpenMMLibrary.OpenMM_State_DataType.OpenMM_State_Velocities;
54  import static java.lang.Double.isInfinite;
55  import static java.lang.Double.isNaN;
56  import static java.lang.String.format;
57  
58  /**
59   * Retrieve state information from an OpenMM Simulation.
60   */
61  public class OpenMMState extends State {
62  
63    /**
64     * Potential energy (kcal/mol).
65     */
66    public final double potentialEnergy;
67    /**
68     * Kinetic energy (kcal/mol).
69     */
70    public final double kineticEnergy;
71    /**
72     * Total energy (kcal/mol).
73     */
74    public final double totalEnergy;
75    /**
76     * Mask of information to retrieve.
77     */
78    private final int dataTypes;
79    /**
80     * Array of atoms.
81     */
82    private final Atom[] atoms;
83    /**
84     * Degrees of freedom.
85     */
86    private final int n;
87    /**
88     * Number of atoms.
89     */
90    private final int nAtoms;
91  
92    /**
93     * Construct an OpenMM State with the requested information.
94     *
95     * @param pointer Pointer to an OpenMM state.
96     * @param atoms   Array of atoms.
97     * @param dof     Degrees of freedom.
98     */
99    protected OpenMMState(PointerByReference pointer, Atom[] atoms, int dof) {
100     super(pointer);
101     // Set the data types mask using the super class method.
102     this.dataTypes = super.getDataTypes();
103     this.atoms = atoms;
104     this.n = dof;
105     nAtoms = atoms.length;
106     if (stateContains(OpenMM_State_Energy)) {
107       // Set the energy fields using the super class method and convert units.
108       potentialEnergy = super.getPotentialEnergy() * OpenMM_KcalPerKJ;
109       kineticEnergy = super.getKineticEnergy() * OpenMM_KcalPerKJ;
110       totalEnergy = potentialEnergy + kineticEnergy;
111     } else {
112       potentialEnergy = 0.0;
113       kineticEnergy = 0.0;
114       totalEnergy = 0.0;
115     }
116   }
117 
118   /**
119    * The force array contains the OpenMM force information for all atoms. The returned array a
120    * contains accelerations for active atoms only.
121    *
122    * @param a Acceleration components for only active atomic coordinates.
123    * @return The acceleration for each active atomic coordinate.
124    */
125   public double[] getAccelerations(@Nullable double[] a) {
126     if (!stateContains(OpenMM_State_Forces)) {
127       return a;
128     }
129 
130     if (a == null || a.length != n) {
131       a = new double[n];
132     }
133 
134     double[] forces = getForces();
135     for (int i = 0, index = 0; i < nAtoms; i++, index += 3) {
136       Atom atom = atoms[i];
137       if (atom.isActive()) {
138         double mass = atom.getMass();
139         double xx = forces[index] * OpenMM_AngstromsPerNm / mass;
140         double yy = forces[index + 1] * OpenMM_AngstromsPerNm / mass;
141         double zz = forces[index + 2] * OpenMM_AngstromsPerNm / mass;
142         a[index] = xx;
143         a[index + 1] = yy;
144         a[index + 2] = zz;
145         atom.setAcceleration(xx, yy, zz);
146       }
147     }
148     return a;
149   }
150 
151   /**
152    * The force array contains the OpenMM force information for all atoms. The returned array g
153    * contains components for active atoms only.
154    *
155    * @param g Gradient components for only active atomic coordinates.
156    * @return g The gradient includes only active atoms
157    */
158   public double[] getGradient(@Nullable double[] g) {
159     if (!stateContains(OpenMM_State_Forces)) {
160       return g;
161     }
162 
163     if (g == null || g.length != n) {
164       g = new double[n];
165     }
166 
167     double[] forces = getForces();
168     for (int i = 0, index = 0; i < nAtoms; i++, index += 3) {
169       Atom atom = atoms[i];
170       if (atom.isActive()) {
171         double xx = -forces[index] * OpenMM_NmPerAngstrom * OpenMM_KcalPerKJ;
172         double yy = -forces[index + 1] * OpenMM_NmPerAngstrom * OpenMM_KcalPerKJ;
173         double zz = -forces[index + 2] * OpenMM_NmPerAngstrom * OpenMM_KcalPerKJ;
174         if (isNaN(xx) || isInfinite(xx) || isNaN(yy) || isInfinite(yy) || isNaN(zz) || isInfinite(zz)) {
175           StringBuilder sb = new StringBuilder(
176               format(" The gradient of atom %s is (%8.3f,%8.3f,%8.3f).", atom, xx, yy, zz));
177           double[] vals = new double[3];
178           atom.getVelocity(vals);
179           sb.append(format("\n Velocities: %8.3g %8.3g %8.3g", vals[0], vals[1], vals[2]));
180           atom.getAcceleration(vals);
181           sb.append(format("\n Accelerations: %8.3g %8.3g %8.3g", vals[0], vals[1], vals[2]));
182           atom.getPreviousAcceleration(vals);
183           sb.append(
184               format("\n Previous accelerations: %8.3g %8.3g %8.3g", vals[0], vals[1], vals[2]));
185           throw new EnergyException(sb.toString());
186         }
187         g[index] = xx;
188         g[index + 1] = yy;
189         g[index + 2] = zz;
190         atom.setXYZGradient(xx, yy, zz);
191       }
192     }
193     return g;
194   }
195 
196   /**
197    * Read the periodic lattice vectors from a state.
198    *
199    * <p>The crystal instance will be updated, and passed to the ForceFieldEnergy instance.
200    */
201   public double[][] getPeriodicBoxVectors() {
202     if (!stateContains(OpenMM_State_Positions)) {
203       return null;
204     }
205 
206     double[][] latticeVectors = super.getPeriodicBoxVectors();
207     latticeVectors[0][0] *= OpenMM_AngstromsPerNm;
208     latticeVectors[0][1] *= OpenMM_AngstromsPerNm;
209     latticeVectors[0][2] *= OpenMM_AngstromsPerNm;
210     latticeVectors[1][0] *= OpenMM_AngstromsPerNm;
211     latticeVectors[1][1] *= OpenMM_AngstromsPerNm;
212     latticeVectors[1][2] *= OpenMM_AngstromsPerNm;
213     latticeVectors[2][0] *= OpenMM_AngstromsPerNm;
214     latticeVectors[2][1] *= OpenMM_AngstromsPerNm;
215     latticeVectors[2][2] *= OpenMM_AngstromsPerNm;
216     return latticeVectors;
217   }
218 
219   /**
220    * The positions array contains the OpenMM atomic position information for all atoms. The
221    * returned array x contains coordinates only for active atoms.
222    *
223    * @param x Atomic coordinates only for active atoms.
224    * @return x The atomic coordinates for only active atoms.
225    */
226   public double[] getPositions(@Nullable double[] x) {
227     if (!stateContains(OpenMM_State_Positions)) {
228       return x;
229     }
230 
231     if (x == null || x.length != n) {
232       x = new double[n];
233     }
234 
235     double[] pos = getPositions();
236     for (int i = 0, index = 0; i < nAtoms; i++, index += 3) {
237       Atom atom = atoms[i];
238       if (atom.isActive()) {
239         double xx = pos[index] * OpenMM_AngstromsPerNm;
240         double yy = pos[index + 1] * OpenMM_AngstromsPerNm;
241         double zz = pos[index + 2] * OpenMM_AngstromsPerNm;
242         x[index] = xx;
243         x[index + 1] = yy;
244         x[index + 2] = zz;
245         atom.moveTo(xx, yy, zz);
246       }
247     }
248     return x;
249   }
250 
251   /**
252    * The positions array contains the OpenMM atomic position information for all atoms. The
253    * returned array x contains coordinates for active atoms only.
254    *
255    * @param v Velocity only for active atomic coordinates.
256    * @return v The velocity for each active atomic coordinate.
257    */
258   public double[] getVelocities(@Nullable double[] v) {
259     if (!stateContains(OpenMM_State_Velocities)) {
260       return v;
261     }
262 
263     if (v == null || v.length != n) {
264       v = new double[n];
265     }
266 
267     double[] vel = getVelocities();
268     for (int i = 0, index = 0; i < nAtoms; i++, index += 3) {
269       Atom atom = atoms[i];
270       if (atom.isActive()) {
271         double xx = vel[index] * OpenMM_AngstromsPerNm;
272         double yy = vel[index + 1] * OpenMM_AngstromsPerNm;
273         double zz = vel[index + 2] * OpenMM_AngstromsPerNm;
274         v[index] = xx;
275         v[index + 1] = yy;
276         v[index + 2] = zz;
277         atom.setVelocity(xx, yy, zz);
278       }
279     }
280     return v;
281   }
282 
283   /**
284    * Get the periodic box volume.
285    *
286    * @return The periodic box volume.
287    */
288   @Override
289   public double getPeriodicBoxVolume() {
290     return super.getPeriodicBoxVolume()
291         * OpenMM_AngstromsPerNm * OpenMM_AngstromsPerNm * OpenMM_AngstromsPerNm;
292   }
293 
294   /**
295    * Get the potential energy. This field will be zero if the dataTypes mask did not include the energy.
296    *
297    * @return The potential energy.
298    */
299   @Override
300   public double getPotentialEnergy() {
301     return potentialEnergy;
302   }
303 
304   /**
305    * Get the kinetic energy. This field will be zero if the dataTypes mask did not include the energy.
306    *
307    * @return The kinetic energy.
308    */
309   @Override
310   public double getKineticEnergy() {
311     return kineticEnergy;
312   }
313 
314   /**
315    * Get the total energy. This field will be zero if the dataTypes mask did not include the energy.
316    *
317    * @return The total energy.
318    */
319   public double getTotalEnergy() {
320     return totalEnergy;
321   }
322 
323   /**
324    * Get the mask of information contained in the state.
325    *
326    * @return The mask of information contained in the state.
327    */
328   @Override
329   public int getDataTypes() {
330     return dataTypes;
331   }
332 
333   /**
334    * Check to see if the state contains the requested information.
335    *
336    * @param dataType Information to check for.
337    * @return boolean indicating whether the state contains the requested information.
338    */
339   private boolean stateContains(int dataType) {
340     return (dataTypes & dataType) == dataType;
341   }
342 }