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.parsers;
39  
40  import static java.lang.Double.parseDouble;
41  import static java.lang.String.format;
42  import static java.lang.System.arraycopy;
43  
44  import ffx.numerics.math.RunningStatistics;
45  import java.io.BufferedReader;
46  import java.io.BufferedWriter;
47  import java.io.File;
48  import java.io.FileReader;
49  import java.io.FileWriter;
50  import java.io.IOException;
51  import java.util.Arrays;
52  import java.util.Collections;
53  import java.util.List;
54  import java.util.logging.Logger;
55  
56  /**
57   * The DistanceMatrixFilter class parses a Distance Matrix (*.DST) files.
58   *
59   * @author Michael J. Schnieders
60   * @since 1.0
61   */
62  public class DistanceMatrixFilter {
63  
64    private static final Logger logger = Logger.getLogger(DistanceMatrixFilter.class.getName());
65  
66    /**
67     * No public constructor for DistanceMatrixFilter.
68     */
69    public DistanceMatrixFilter() {
70    }
71  
72    private int nRows = 0;
73    private int nColumns = 0;
74    private boolean fillDistanceMatrix = false;
75    private double[][] distanceMatrix = null;
76  
77    /**
78     * Get the number of rows read in.
79     *
80     * @return The number of rows read in.
81     */
82    public int getRestartRow() {
83      return nRows;
84    }
85  
86    /**
87     * Get the number of columns in the last row that was read in.
88     *
89     * @return The number of columns in the last row that was read in.
90     */
91    public int getRestartColumn() {
92      return nColumns;
93    }
94  
95    /**
96     * Read in the distance matrix from a file. This method is used from the Clustering code, where the
97     * size of the distance matrix is unknown, and the full N^2 matrix is needed.
98     *
99     * @param filename The filename to read from.
100    * @param distanceList The distance matrix (if null, only the last row is returned).
101    * @return Statistics for the parsed values.
102    */
103   public RunningStatistics readDistanceMatrix(String filename, List<double[]> distanceList) {
104     fillDistanceMatrix = true;
105     RunningStatistics runningStatistics = readDistanceMatrix(filename, -1, -1);
106     if (distanceMatrix != null) {
107       int size = distanceMatrix[0].length;
108       boolean square = true;
109       for (int i = 0; i < size; i++) {
110         if (distanceMatrix[i] == null || distanceMatrix[i].length != size) {
111           square = false;
112           break;
113         }
114       }
115       // The full NxN matrix has been read in.
116       if (square) {
117         // Add all rows of the distanceMatrix into the list
118         Collections.addAll(distanceList, distanceMatrix);
119       } else {
120         // Assume only the upper triangle has been read in.
121         for (int i = 0; i < size; i++) {
122           double[] row = new double[size];
123           // Fill the lower triangle from previous rows.
124           for (int j = 0; j < i; j++) {
125             double[] previousRow = distanceList.get(j);
126             row[j] = previousRow[i];
127           }
128           // Fill the upper triangle using the current row of the distanceMatrix.
129           arraycopy(distanceMatrix[i], 0, row, i, size - i);
130           distanceList.add(row);
131         }
132       }
133     }
134 
135     // Reset the DistanceMatrixFilter for reuse.
136     distanceMatrix = null;
137     fillDistanceMatrix = false;
138     return runningStatistics;
139   }
140 
141   /**
142    * Read in the distance matrix from a file.
143    *
144    * @param filename The filename to read from.
145    * @param expectedRows The number of rows to expect.
146    * @param expectedColumns The number of columns to expect.
147    * @return Statistics for the parsed values.
148    */
149   public RunningStatistics readDistanceMatrix(String filename, int expectedRows,
150       int expectedColumns) {
151 
152     if (filename == null) {
153       return null;
154     }
155 
156     File distanceMatrixFile = new File(filename);
157     if (!distanceMatrixFile.exists() || !distanceMatrixFile.canRead()) {
158       return null;
159     }
160 
161     // Read in the RMSD matrix.
162     try (FileReader fr = new FileReader(distanceMatrixFile); BufferedReader br = new BufferedReader(
163         fr)) {
164 
165       String data = br.readLine();
166 
167       // Check for blank lines at the top of the file
168       while (data != null && data.trim().isEmpty()) {
169         data = br.readLine();
170       }
171 
172       if (data == null) {
173         logger.info(format("\n No data in RMSD file %s.", distanceMatrixFile));
174         return null;
175       }
176 
177       String[] tokens = data.trim().split(" +");
178       nColumns = tokens.length;
179 
180       // If the expectedRows is unknown (i.e. this is called from the Cluster script), set
181       // the number of rows to the initial number of parsed tokens.
182       if (expectedRows == -1) {
183         expectedRows = nColumns;
184       }
185       if (expectedColumns == -1) {
186         expectedColumns = nColumns;
187       }
188 
189       if (nColumns != expectedColumns) {
190         logger.info(
191             format("\n Unexpected number of entries (%d) in the first row of the RMSD file %s.",
192                 nColumns, distanceMatrixFile));
193         return null;
194       }
195 
196       if (fillDistanceMatrix) {
197         distanceMatrix = new double[expectedRows][];
198       }
199 
200       RunningStatistics runningStatistics = new RunningStatistics();
201 
202       // Loop over the number of expected rows.
203       for (int i = 0; i < expectedRows; i++) {
204         double[] row = new double[nColumns];
205         for (int j = 0; j < nColumns; j++) {
206           row[j] = parseDouble(tokens[j]);
207           runningStatistics.addValue(row[j]);
208         }
209 
210         // Are we storing all rows?
211         if (distanceMatrix != null && distanceMatrix.length > i) {
212           distanceMatrix[i] = row;
213         }
214 
215         nRows = i + 1;
216 
217         // Read the next line.
218         data = br.readLine();
219         if (data != null) {
220           tokens = data.trim().split(" +");
221         } else {
222           break;
223         }
224 
225         nColumns = tokens.length;
226       }
227       return runningStatistics;
228 
229     } catch (IOException e) {
230       logger.info(format(" Exception reading %s:\n %s", distanceMatrixFile, e));
231       return null;
232     }
233   }
234 
235   /**
236    * Convert a distance matrix to a String.
237    *
238    * @param distanceMatrix The distance matrix.
239    * @return Return a String representation (or null if the distanceMatrix is null).
240    */
241   public static String toDistanceMatrixString(List<double[]> distanceMatrix) {
242 
243     if (distanceMatrix == null) {
244       return null;
245     }
246 
247     StringBuilder sb = new StringBuilder("\n Distance Matrix:\n");
248     for (double[] row : distanceMatrix) {
249       sb.append("  ");
250       for (int j = 0; j < row.length; j++) {
251         if (row[j] == -2.0) {
252           sb.append(format("%6.4f", Double.NaN));
253         } else {
254           sb.append(format("%6.4f", row[j]));
255         }
256         if (j == row.length - 1) {
257           sb.append("\n");
258         } else {
259           sb.append(" ");
260         }
261       }
262     }
263 
264     return sb.toString();
265   }
266 
267   /**
268    * Convert a distance matrix to a String.
269    *
270    * @param distanceMatrix The distance matrix.
271    * @return Return a String representation (or null if the distanceMatrix is null).
272    */
273   public static String toDistanceMatrixString(double[][] distanceMatrix) {
274     return toDistanceMatrixString(Arrays.asList(distanceMatrix));
275   }
276 
277   /**
278    * Write the distance matrix to a file.
279    *
280    * @param filename The filename to write to.
281    * @param distanceMatrix a list containing rows of the distance matrix.
282    * @return a boolean.
283    */
284   public static boolean writeDistanceMatrix(String filename, List<double[]> distanceMatrix) {
285     // Check for null arguments.
286     if (distanceMatrix == null) {
287       return false;
288     }
289 
290     for (double[] row : distanceMatrix) {
291       boolean success = writeDistanceMatrixRow(filename, row, 0);
292       if (!success) {
293         return false;
294       }
295     }
296 
297     return true;
298   }
299 
300   /**
301    * Write the distance matrix to a file.
302    *
303    * @param filename The filename to write to.
304    * @param distanceMatrixRow A row of the distance matrix.
305    * @param firstColumn The first column of the distance matrix row to write.
306    * @return a boolean.
307    */
308   public static boolean writeDistanceMatrixRow(String filename, double[] distanceMatrixRow,
309       int firstColumn) {
310 
311     if (filename == null) {
312       return false;
313     }
314 
315     File distanceMatrixFile = new File(filename);
316 
317     // Check for null arguments.
318     if (distanceMatrixFile == null || distanceMatrixRow == null) {
319       return false;
320     }
321 
322     // Write one row.
323     try (FileWriter fw = new FileWriter(distanceMatrixFile,
324         true); BufferedWriter bw = new BufferedWriter(fw)) {
325       int nColumn = distanceMatrixRow.length;
326       for (int column = firstColumn; column < nColumn; column++) {
327         bw.write(format("%16.14f", distanceMatrixRow[column]));
328         if (column < nColumn - 1) {
329           bw.write(" ");
330         }
331       }
332       bw.write("\n");
333     } catch (Exception e) {
334       logger.info(format(" Exception writing to %s:\n %s", distanceMatrixFile, e));
335       return false;
336     }
337 
338     return true;
339   }
340 }