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