View Javadoc
1   package ffx.potential.parsers;
2   
3   import ffx.numerics.estimator.MultistateBennettAcceptanceRatio;
4   import ffx.numerics.estimator.MultistateBennettAcceptanceRatio.*;
5   
6   import java.io.*;
7   import java.util.ArrayList;
8   import java.util.Arrays;
9   import java.util.logging.Logger;
10  
11  import static org.apache.commons.lang3.math.NumberUtils.max;
12  import static org.apache.commons.lang3.math.NumberUtils.min;
13  
14  /**
15   * The MBARFilter class parses mbar (*.mbar or *.bar) files. Expected file format is a header
16   * line including the number of snapshots contained, a name, and a temperature. Following the header
17   * is a list of energies for each snapshot at each lambda value being considered with an index to start
18   * the line. Then the energies go from least (0) to greatest (1) lambda value.
19   *
20   * @author Matthew J. Speranza
21   * @since 1.0
22   *
23   */
24  public class MBARFilter {
25      private static final Logger logger = Logger.getLogger(MBARFilter.class.getName());
26      private File tempBarFile;
27      private File[] barFiles;
28      private File fileLocation;
29      private ArrayList<ArrayList<Double>> tempFileEnergies;
30      private double[][] fileEnergies;
31      private double[][][] eAll;
32      private double[] temperatures;
33      private int[] snaps;
34      private int windowsRead;
35      private int windows;
36  
37  
38      public MBARFilter(File fileLocation) {
39          this.fileLocation = fileLocation;
40          barFiles = fileLocation.listFiles((dir, name) -> name.matches("energy_\\d+.mbar") || name.matches("energy_\\d+.bar"));
41          assert barFiles != null;
42          if (barFiles.length == 0 || barFiles.length == 1) {
43              String message = barFiles.length == 0 ?
44                      " No files matching 'energy_\\d+.mbar' or 'energy_\\d+.bar' found in " +
45                              fileLocation.getAbsolutePath() :
46                      " Only one file matching 'energy_\\d+.mbar' or 'energy_\\d+.bar' found in " +
47                              fileLocation.getAbsolutePath() + ". At least two are required.";
48              logger.severe(message);
49          }
50          // Sort files by state number
51          Arrays.sort(barFiles, (f1, f2) -> {
52              int state1 = Integer.parseInt(f1.getName().split("\\.")[0].split("_")[1]);
53              int state2 = Integer.parseInt(f2.getName().split("\\.")[0].split("_")[1]);
54              return Integer.compare(state1, state2);
55          });
56          windows = barFiles.length;
57          temperatures = new double[windows];
58          snaps = new int[windows];
59          this.parseFiles();
60      }
61  
62      public MultistateBennettAcceptanceRatio getMBAR(SeedType seedType){
63          return getMBAR(seedType, 1e-7);
64      }
65  
66      public MultistateBennettAcceptanceRatio getMBAR(SeedType seedType, double tolerance) {
67          double[] lambda = new double[windows];
68          for (int i = 0; i < windows; i++) {
69              lambda[i] = i / (windows - 1.0);
70          }
71          return new MultistateBennettAcceptanceRatio(lambda, eAll, temperatures, tolerance, seedType);
72      }
73      private void parseFiles(){
74          eAll = new double[windows][][];
75          for (int i = 0; i < windows; i++) {
76              eAll[i] = readFile(barFiles[i].getName());
77          }
78          int minSnaps = min(snaps);
79          int maxSnaps = max(snaps);
80  
81          boolean warn = minSnaps != maxSnaps;
82          if (warn) {
83              logger.warning("MINIMUM NUMBER OF SNAPS ACROSS WINDOWS: " + minSnaps);
84              logger.warning("MAXIMUM NUMBER OF SNAPS ACROSS WINDOWS: " + maxSnaps);
85              logger.warning("NOT ALL FILES CONTAINED THE SAME NUMBER OF SNAPSHOTS. " +
86                      "EXTRA SNAPSHOTS WERE REMOVED FROM OTHER SAMPLES TO COMPENSATE!");
87              double[][][] temp = new double[eAll.length][eAll[0].length][minSnaps];
88              for(int j = 0; j < eAll.length; j++){
89                  for(int k = 0; k < eAll[0].length; k++){
90                      System.arraycopy(eAll[j][k], 0, temp[j][k], 0, minSnaps);
91                  }
92              }
93              eAll = temp;
94          }
95          if (windowsRead != windows) {
96              logger.severe("Failed to read all files in " + fileLocation.getAbsolutePath());
97          }
98      }
99  
100     public void writeFiles(File mbarFileLoc, double[][][] energies, double[] temperatures) {
101         if (temperatures.length != windows) {
102             double temp = temperatures[0];
103             temperatures = new double[windows];
104             for (int i = 0; i < windows; i++) {
105                 temperatures[i] = temp;
106             }
107         }
108         for (int i = 0; i < windows; i++) {
109             File file = new File(mbarFileLoc, "energy_" + i + ".mbar");
110             writeFile(energies[i], file, temperatures[i]);
111         }
112     }
113 
114     /**
115      * Parses the file matching the name given in the directory of 'fileLocation'.
116      * @param fileName the name of the file to be parsed matching 'energy_\d+.mbar' or 'energy_\d+.bar'.
117      * @return a double[][] of the energies for each snapshot at each lambda value
118      */
119     private double[][] readFile(String fileName) {
120         tempBarFile = new File(fileLocation, fileName);
121         if (!tempBarFile.exists()) {
122             logger.severe("File " + tempBarFile.getAbsolutePath() + " does not exist.");
123         }
124         int state = Integer.parseInt(fileName.split("\\.")[0].split("_")[1]);
125         tempFileEnergies = new ArrayList<>();
126         for(int i = 0; i < windows; i++) {
127             tempFileEnergies.add(new ArrayList<>());
128         }
129         try (FileReader fr1 = new FileReader(tempBarFile);
130              BufferedReader br1 = new BufferedReader(fr1);) {
131             String line = br1.readLine();
132             String[] tokens = line.trim().split("\\t *| +");
133             int numSnaps = Integer.parseInt(tokens[0]);
134             temperatures[state] = Double.parseDouble(tokens[1]);
135             int count = 0;
136             line = br1.readLine();
137             while (line != null) {
138                 tokens = line.trim().split("\\t *| +");
139                 for (int i = 1; i < tokens.length; i++) {
140                     tempFileEnergies.get(i-1).add(Double.parseDouble(tokens[i]));
141                 }
142                 count++;
143                 line = br1.readLine();
144             }
145             snaps[state] = count;
146         } catch(IOException e){
147             logger.info("Failed to read MBAR file: " + tempBarFile.getAbsolutePath());
148             throw new RuntimeException(e);
149         }
150         fileEnergies = new double[windows][];
151         for (int i = 0; i < windows; i++) {
152             fileEnergies[i] = new double[tempFileEnergies.get(i).size()];
153             for (int j = 0; j < tempFileEnergies.get(i).size(); j++) {
154                 fileEnergies[i][j] = tempFileEnergies.get(i).get(j);
155             }
156         }
157         windowsRead++;
158         return fileEnergies;
159     }
160 
161     public void writeFile(double[][] energies, File file, double temperature) {
162         MultistateBennettAcceptanceRatio.writeFile(energies, file, temperature);
163     }
164 }