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.bonded;
39  
40  import static java.lang.String.format;
41  
42  import java.util.Arrays;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.logging.Level;
46  import java.util.logging.Logger;
47  
48  /**
49   * The ResidueState class encodes the current chemical and coordinate state of a Residue,
50   * particularly a MultiResidue, for ease of reverting coordinates. Should not be too difficult to get
51   * it to also store velocities, etc.
52   *
53   * @author Michael J. Schnieders
54   * @author Jacob M. Litman
55   * @since 1.0
56   */
57  public class ResidueState {
58  
59    private static final Logger logger = Logger.getLogger(ResidueState.class.getName());
60  
61    /**
62     * For a MultiResidue, parent is the MultiResidue, while res is the chemical state to which this
63     * ResidueState belongs. For a regular Residue, both are just the Residue. In general, use res to
64     * store a chemical or other state, and parent to store the Residue to which this belongs.
65     */
66    private final Residue parent;
67  
68    private final Residue residue;
69    private final HashMap<Atom, double[]> atomMap;
70    private final Atom[] atoms;
71  
72    /**
73     * Constructor for ResidueState.
74     *
75     * @param parent a {@link ffx.potential.bonded.Residue} object.
76     * @param residue a {@link ffx.potential.bonded.Residue} object.
77     */
78    public ResidueState(Residue parent, Residue residue) {
79      this.parent = parent;
80      this.residue = residue;
81  
82      List<Atom> atomList = residue.getAtomList();
83      int nAtoms = atomList.size();
84      atomMap = new HashMap<>(nAtoms);
85      atoms = new Atom[nAtoms];
86  
87      atomList.toArray(atoms);
88      for (Atom atom : atoms) {
89        double[] atXYZ = new double[3];
90        atom.getXYZ(atXYZ);
91        atomMap.put(atom, atXYZ);
92      }
93    }
94  
95    /**
96     * Constructor for ResidueState.
97     *
98     * @param residue a {@link ffx.potential.bonded.Residue} object.
99     */
100   public ResidueState(Residue residue) {
101     this(residue, residue);
102     if (residue instanceof MultiResidue) {
103       throw new IllegalArgumentException(format(
104           " Residue %s is a MultiResidue: this ResidueState has been incorrectly constructed!",
105           residue));
106     }
107   }
108 
109   /**
110    * revertAllCoordinates.
111    *
112    * @param residueList a {@link java.util.List} object.
113    * @param states an array of {@link ffx.potential.bonded.ResidueState} objects.
114    */
115   public static void revertAllCoordinates(List<Residue> residueList, ResidueState[] states) {
116     revertAllCoordinates(residueList.toArray(new Residue[0]), states);
117   }
118 
119   /**
120    * Uses a double[nAtoms][3] to revert the coordinates of an array of atoms. Does not check to see
121    * if the arrays are properly ordered; make sure you are using the correct arrays.
122    *
123    * @param atoms To revert coordinates of
124    * @param coords Coordinates
125    */
126   public static void revertAtomicCoordinates(Atom[] atoms, double[][] coords) {
127     int nAtoms = atoms.length;
128     if (coords.length != nAtoms) {
129       throw new IllegalArgumentException(
130           format(" Length %d of atoms " + "array does not match length %d of coordinates array",
131               nAtoms, coords.length));
132     }
133     for (int i = 0; i < nAtoms; i++) {
134       atoms[i].setXYZ(coords[i]);
135     }
136   }
137 
138   /**
139    * storeAllCoordinates.
140    *
141    * @param residueList a {@link java.util.List} object.
142    * @return an array of {@link ffx.potential.bonded.ResidueState} objects.
143    */
144   public static ResidueState[] storeAllCoordinates(List<Residue> residueList) {
145     return storeAllCoordinates(residueList.toArray(new Residue[0]));
146   }
147 
148   /**
149    * storeAllCoordinates.
150    *
151    * @param residues an array of {@link ffx.potential.bonded.Residue} objects.
152    * @return an array of {@link ffx.potential.bonded.ResidueState} objects.
153    */
154   public static ResidueState[] storeAllCoordinates(Residue[] residues) {
155     int nResidues = residues.length;
156     ResidueState[] states = new ResidueState[nResidues];
157     for (int i = 0; i < nResidues; i++) {
158       states[i] = residues[i].storeState();
159     }
160     return states;
161   }
162 
163   /**
164    * Returns a new double[nAtoms][3] with the coordinates of an array of atoms.
165    *
166    * @param atoms To store coordinates of
167    * @return Coordinates
168    */
169   public static double[][] storeAtomicCoordinates(Atom[] atoms) {
170     int nAtoms = atoms.length;
171     double[][] coords = new double[nAtoms][3];
172     for (int i = 0; i < nAtoms; i++) {
173       atoms[i].getXYZ(coords[i]);
174     }
175     return coords;
176   }
177 
178   /**
179    * revertAllCoordinates.
180    *
181    * @param residueArray an array of {@link ffx.potential.bonded.Residue} objects.
182    * @param states an array of {@link ffx.potential.bonded.ResidueState} objects.
183    */
184   private static void revertAllCoordinates(Residue[] residueArray, ResidueState[] states) {
185     int nResidues = residueArray.length;
186     if (nResidues != states.length) {
187       throw new IllegalArgumentException(
188           format("Length of residue " + "array %d and residue state array %d do not match.",
189               nResidues, states.length));
190     }
191 
192     for (int i = 0; i < nResidues; i++) {
193       Residue resi = residueArray[i];
194       // If indexing did not get thrown off:
195       if (resi.equals(states[i].getParent())) {
196         resi.revertState(states[i]);
197       } else {
198         // Else must search states[]
199         boolean matchFound = false;
200         for (int j = 0; j < nResidues; j++) {
201           if (resi.equals(states[j].getParent())) {
202             matchFound = true;
203             resi.revertState(states[j]);
204             break;
205           }
206         }
207 
208         if (!matchFound) {
209           throw new IllegalArgumentException(
210               format("Could not " + "find match for residue %s among residue states array.", resi));
211         }
212       }
213     }
214   }
215 
216   public double compareTo(ResidueState residueState) {
217     logger.info("Comparing rotamers using the compareTo method in ResidueState");
218     double[][] tempx1 = new double[this.atoms.length][3];
219     double[][] tempx2 = new double[residueState.atoms.length][3];
220     double[] x1 = new double[this.atoms.length * 3];
221     double[] x2 = new double[residueState.atoms.length * 3];
222     // Create arrays of atoms for keptRotamer[k] ResidueState and newResState
223     // Here, each residue state is the same residue with a different rotamer applied.
224     Atom[] x1atoms = this.atoms;
225     Atom[] x2atoms = residueState.atoms;
226     // logger.info("Total number of atoms for current residue state: " + x1atoms.length);
227     // logger.info("Total number of atoms for previously kept residue state to be tested against: "+
228     // x2atoms.length);
229 
230     // Get the coordinate arrays for each atom in the current residue state using the atomMap
231     for (int atomCount = 0; atomCount < x1atoms.length; atomCount++) {
232       Atom exampleAtom = x1atoms[atomCount];
233       tempx1[atomCount] = this.atomMap.get(exampleAtom);
234     }
235 
236     // Add coordinates from tempx1[][] to x1[] for RMSD calculations
237     int x1count = 0;
238     for (double[] coordSet : tempx1) {
239       for (int coordCount = 0; coordCount < 3; coordCount++) {
240         x1[x1count] = coordSet[coordCount];
241         x1count++;
242       }
243     }
244 
245     // Get the coorinate arrays for each atom in the previously kept residue state to be
246     // tested against using the atomMap for that residue state
247     for (int atomCount = 0; atomCount < x2atoms.length; atomCount++) {
248       Atom exampleAtom = x2atoms[atomCount];
249       tempx2[atomCount] = residueState.atomMap.get(exampleAtom);
250     }
251 
252     // Add coordinate from tempx2[][] to x2[] for RMSD calculations
253     int x2count = 0;
254     for (double[] coordSet : tempx2) {
255       for (int coordCount = 0; coordCount < 3; coordCount++) {
256         x2[x2count] = coordSet[coordCount];
257         x2count++;
258       }
259     }
260 
261     // Create a mass array of ones so the RMSD isn't mass weighted
262     double[] mass = new double[x1.length];
263     Arrays.fill(mass, 1);
264     return ffx.potential.utils.Superpose.rmsd(x1, x2, mass);
265   }
266 
267   /**
268    * Getter for the field <code>atoms</code>.
269    *
270    * @return an array of {@link ffx.potential.bonded.Atom} objects.
271    */
272   public Atom[] getAtoms() {
273     return atoms;
274   }
275 
276   /**
277    * Getter for the field <code>parent</code>.
278    *
279    * @return a {@link ffx.potential.bonded.Residue} object.
280    */
281   public Residue getParent() {
282     return parent;
283   }
284 
285   /** {@inheritDoc} */
286   @Override
287   public String toString() {
288     return format(" ResidueState with parent residue %s, state residue " + "%s, number of atoms %d",
289         parent, residue, atoms.length);
290   }
291 
292   /**
293    * getStateResidue.
294    *
295    * @return a {@link ffx.potential.bonded.Residue} object.
296    */
297   Residue getStateResidue() {
298     return residue;
299   }
300 
301   /**
302    * Returns the coordinates of the selected atom; or null if atom not contained.
303    *
304    * @param atom An Atom.
305    * @return xyz coordinates.
306    */
307   double[] getAtomCoords(Atom atom) {
308     double[] xyz = new double[3];
309     if (!atomMap.containsKey(atom)) {
310       logger.info(
311           format(" Illegal call to ResidueState.getAtomCoords: atom %s not found: hashcode %d", atom,
312               atom.hashCode()));
313       for (Atom ratom : residue.getAtomList()) {
314         logger.info(format(" Atoms in residue: %s hashcode: %d", ratom, ratom.hashCode()));
315       }
316       for (Atom matom : atomMap.keySet()) {
317         logger.info(
318             format(" Atoms in ResidueState atom cache: %s hashcode: %d", matom, matom.hashCode()));
319       }
320       logger.log(Level.SEVERE, " Error in ResidueState.getAtomCoords.", new IllegalStateException());
321     }
322     System.arraycopy(atomMap.get(atom), 0, xyz, 0, 3);
323     return xyz;
324   }
325 }