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.numerics.estimator;
39  
40  import ffx.numerics.math.BootStrapStatistics;
41  
42  import java.util.logging.Logger;
43  
44  import static ffx.utilities.Constants.NS2SEC;
45  import static java.lang.String.format;
46  import static org.apache.commons.math3.util.FastMath.min;
47  
48  /**
49   * The FreeEnergyDifferenceReporter class
50   * reports free energy differences using the Bennett Acceptance Ratio (BAR) method.
51   *
52   * @author Michael J. Schnieders
53   * @since 1.0
54   */
55  public class FreeEnergyDifferenceReporter {
56  
57    private static final Logger logger = Logger.getLogger(FreeEnergyDifferenceReporter.class.getName());
58  
59    /**
60     * The number of states in the simulation.
61     */
62    private final int nStates;
63  
64    /**
65     * The energiesLow array stores energy values for state L snapshots evaluated at L-dL.
66     * The array is of size [nStates][nSnapshots].
67     */
68    private final double[][] energiesLow;
69  
70    /**
71     * The energiesAt array stores energy values for state L snapshots evaluated at L.
72     * The array is of size [nStates][nSnapshots].
73     */
74    private final double[][] energiesAt;
75  
76    /**
77     * The energiesHigh array stores energy values for state L snapshots evaluated at L + dL.
78     * The array is of size [nStates][nSnapshots].
79     */
80    private final double[][] energiesHigh;
81  
82    /**
83     * The volume array stores volume values for state L at each snapshot.
84     * The array is of size [nStates][nSnapshots].
85     */
86    private final double[][] volume;
87  
88    /**
89     * The lambda values for each state.
90     */
91    private final double[] lambdaValues;
92    /**
93     * The temperature values for each state.
94     */
95    private final double[] temperature;
96    /**
97     * The convergence criterion for the BAR iteration.
98     */
99    private final double eps;
100   /**
101    * The number of iterations to be used for BAR.
102    */
103   private final int nIterations;
104 
105   /**
106    * Minimum number of trials to be used for bootstrap.
107    */
108   final long MIN_BOOTSTRAP_TRIALS = 1000L;
109 
110   /**
111    * Free energy difference from forward FEP.
112    */
113   private double forwardTotalFEDifference;
114   /**
115    * Enthalpy difference from forward FEP.
116    */
117   private double forwardTotalEnthalpyChange;
118   /**
119    * Entropy difference from forward FEP.
120    */
121   private double forwardTotalEntropyChange;
122   /**
123    * Free energy difference from backward FEP.
124    */
125   private double backwardTotalFEDifference;
126   /**
127    * Enthalpy difference from backward FEP.
128    */
129   private double backwardTotalEnthalpyChange;
130   /**
131    * Entropy difference from backward FEP.
132    */
133   private double backwardTotalEntropyChange;
134   /**
135    * Free energy difference from BAR.
136    */
137   private double barIterTotalFEDiff;
138   /**
139    * Free energy difference from BAR using boot-strapping.
140    */
141   private double barBSTotalFEDiff;
142   /**
143    * Enthalpy difference from BAR.
144    */
145   private double barBSTotalEnthalpyChange;
146   /**
147    * Entropy difference from BAR.
148    */
149   private double barBSTotalEntropyChange;
150 
151   /**
152    * Report Free Energy Differences based on a series of states.
153    *
154    * @param nStates      The number of states.
155    * @param lambdaValues The lambda value for each state.
156    * @param temperature  The temperature for each state.
157    * @param eps          The BAR convergence criteria.
158    * @param nIterations  The BAR maximum number of iterations.
159    * @param energiesLow  The energy for each snapshot from state L evaluated at L - dL.
160    * @param energiesAt   The energy for each snapshot from state L evaluated at L.
161    * @param energiesHigh The energy for each snapshot from state L evaluated at L + dL.
162    * @param volume       The volume for each snapshot from state L.
163    */
164   public FreeEnergyDifferenceReporter(int nStates, double[] lambdaValues, double[] temperature, double eps, int nIterations,
165                                       double[][] energiesLow, double[][] energiesAt, double[][] energiesHigh, double[][] volume) {
166     this.nStates = nStates;
167     this.energiesLow = energiesLow;
168     this.energiesAt = energiesAt;
169     this.energiesHigh = energiesHigh;
170     this.volume = volume;
171     this.lambdaValues = lambdaValues;
172     this.temperature = temperature;
173     this.eps = eps;
174     this.nIterations = nIterations;
175   }
176 
177   /**
178    * Report the free energy differences.
179    */
180   public void report() {
181     // Compute the mean and standard deviation of the energy for each state.
182     double[] energyMean = new double[nStates];
183     double[] energySD = new double[nStates];
184     double[] energyVar = new double[nStates];
185     for (int state = 0; state < nStates; state++) {
186       BootStrapStatistics energyStats = new BootStrapStatistics(energiesAt[state]);
187       energyMean[state] = energyStats.mean;
188       energySD[state] = energyStats.sd;
189       energyVar[state] = energyStats.var;
190     }
191 
192     // Create the BAR instance.
193     BennettAcceptanceRatio bar = new BennettAcceptanceRatio(lambdaValues, energiesLow,
194         energiesAt, energiesHigh, temperature, eps, nIterations);
195 
196     barIterTotalFEDiff = bar.getTotalFreeEnergyDifference();
197     double barIterFEDiffTotalUncertainty = bar.getTotalFEDifferenceUncertainty();
198     double[] barIterFEDifferences = bar.getFreeEnergyDifferences();
199     double[] barIterFEDiffUncertainties = bar.getFEDifferenceUncertainties();
200 
201     EstimateBootstrapper barBS = new EstimateBootstrapper(bar);
202     EstimateBootstrapper forwardBS = new EstimateBootstrapper(bar.getInitialForwardsGuess());
203     EstimateBootstrapper backwardBS = new EstimateBootstrapper(bar.getInitialBackwardsGuess());
204 
205     int numSnapshots = energiesAt[0].length;
206     int trials = (int) (1.0e8 / numSnapshots);
207     long bootstrap = min(MIN_BOOTSTRAP_TRIALS, trials);
208     logger.info(format(" Number of bootstrap trials: %d", bootstrap));
209 
210     long time = -System.nanoTime();
211     forwardBS.bootstrap(bootstrap);
212     time += System.nanoTime();
213     logger.fine(format(" Forward FEP Bootstrap Complete:      %7.4f sec", time * NS2SEC));
214     forwardTotalFEDifference = forwardBS.getTotalFreeEnergyDifference();
215     double forwardTotalFEDiffUncertainty = forwardBS.getTotalFEDifferenceUncertainty();
216     forwardTotalEnthalpyChange = forwardBS.getTotalEnthalpyChange();
217     double forwardTotalEnthalpyUncertainty = forwardBS.getTotalEnthalpyUncertainty();
218 
219     time = -System.nanoTime();
220     backwardBS.bootstrap(bootstrap);
221     time += System.nanoTime();
222     logger.fine(format(" Backward FEP Bootstrap Complete:     %7.4f sec", time * NS2SEC));
223     backwardTotalFEDifference = backwardBS.getTotalFreeEnergyDifference();
224     double backwardTotalFEDiffUncertainty = backwardBS.getTotalFEDifferenceUncertainty();
225     backwardTotalEnthalpyChange = backwardBS.getTotalEnthalpyChange();
226     double backwardTotalEnthalpyUncertainty = backwardBS.getTotalEnthalpyUncertainty();
227 
228     time = -System.nanoTime();
229     barBS.bootstrap(bootstrap);
230     time += System.nanoTime();
231     logger.fine(format(" BAR Bootstrap Complete:              %7.4f sec", time * NS2SEC));
232 
233     barBSTotalFEDiff = barBS.getTotalFreeEnergyDifference();
234     double barBSTotalFEDiffUncertainty = barBS.getTotalFEDifferenceUncertainty();
235     barBSTotalEnthalpyChange = barBS.getTotalEnthalpyChange();
236     double barBSTotalEnthalpyUncertainty = barBS.getTotalEnthalpyUncertainty();
237 
238     if (nStates > 2) {
239       logger.info("\n Window Free Energy Differences (kcal/mol)");
240       logger.info("        Forward FEP           Backward FEP          BAR Iteration         BAR Bootstrap");
241       double[] forwardFEDifferences = forwardBS.getFreeEnergyDifferences();
242       double[] forwardFEDUncertainty = forwardBS.getFEDifferenceStdDevs();
243       double[] backwardFEDifferences = backwardBS.getFreeEnergyDifferences();
244       double[] backwardFEDUncertainty = backwardBS.getFEDifferenceStdDevs();
245       double[] barBSFE = barBS.getFreeEnergyDifferences();
246       double[] barBSUncertaintyFE = barBS.getFEDifferenceStdDevs();
247       for (int n = 0; n < nStates - 1; n++) {
248         logger.info(format(" %2d %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
249             n, forwardFEDifferences[n], forwardFEDUncertainty[n],
250             backwardFEDifferences[n], backwardFEDUncertainty[n],
251             barIterFEDifferences[n], barIterFEDiffUncertainties[n],
252             barBSFE[n], barBSUncertaintyFE[n]));
253       }
254     }
255 
256     logger.info("\n Total Free Energy Difference (kcal/mol)");
257     logger.info("        Forward FEP           Backward FEP          BAR Iteration         BAR Bootstrap");
258     logger.info(format("    %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
259         forwardTotalFEDifference, forwardTotalFEDiffUncertainty,
260         backwardTotalFEDifference, backwardTotalFEDiffUncertainty,
261         barIterTotalFEDiff, barIterFEDiffTotalUncertainty,
262         barBSTotalFEDiff, barBSTotalFEDiffUncertainty));
263 
264     logger.info("\n Enthalpy from Potential Energy Averages (kcal/mol)\n");
265     for (int n = 0; n < nStates; n++) {
266       logger.info(format(" State %2d:     %12.4f +/- %6.4f", n, energyMean[n], energySD[n]));
267     }
268     double enthalpyDiff = energyMean[nStates - 1] - energyMean[0];
269     double enthalpyDiffSD = Math.sqrt(energyVar[nStates - 1] + energyVar[0]);
270     logger.info(format(" Enthalpy via Direct Estimate:   %12.4f +/- %6.4f", enthalpyDiff, enthalpyDiffSD));
271 
272     // Enthalpy Differences
273     if (nStates > 2) {
274       logger.info("\n Window Enthalpy Differences (kcal/mol)");
275       logger.info("        Forward FEP           Backward FEP          BAR Bootstrap");
276       double[] forwardEnthalpyDifferences = forwardBS.getEnthalpyChanges();
277       double[] forwardEnthalpyUncertainty = forwardBS.getEnthalpyStdDevs();
278       double[] backwardEnthalpyDifferences = backwardBS.getEnthalpyChanges();
279       double[] backwardEnthalpyUncertainty = backwardBS.getEnthalpyStdDevs();
280       double[] barBSenthalpy = barBS.getEnthalpyChanges();
281       double[] barBSenthalpyUncertainty = barBS.getEnthalpyStdDevs();
282       for (int n = 0; n < nStates - 1; n++) {
283         logger.info(format(" %2d %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
284             n, forwardEnthalpyDifferences[n], forwardEnthalpyUncertainty[n],
285             backwardEnthalpyDifferences[n], backwardEnthalpyUncertainty[n],
286             barBSenthalpy[n], barBSenthalpyUncertainty[n]));
287       }
288     }
289     logger.info("\n Total Enthalpy Difference (kcal/mol)");
290     logger.info("        Forward FEP           Backward FEP          BAR Bootstrap");
291     logger.info(format("    %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
292         forwardTotalEnthalpyChange, forwardTotalEnthalpyUncertainty,
293         backwardTotalEnthalpyChange, backwardTotalEnthalpyUncertainty,
294         barBSTotalEnthalpyChange, barBSTotalEnthalpyUncertainty));
295 
296     // Entropy Differences
297     if (nStates > 2) {
298       logger.info("\n Window Entropy Differences -T*dS (kcal/mol)");
299       logger.info("        Forward FEP           Backward FEP          BAR Bootstrap");
300       double[] forwardEntropyDifferences = forwardBS.getEntropyChanges();
301       double[] forwardEntropyUncertainty = forwardBS.getEntropyStdDevs();
302       double[] backwardEntropyDifferences = backwardBS.getEntropyChanges();
303       double[] backwardEntropyUncertainty = backwardBS.getEntropyStdDevs();
304       double[] barBSEntropy = barBS.getEntropyChanges();
305       double[] barBSEntropyUncertainty = barBS.getEntropyStdDevs();
306       for (int n = 0; n < nStates - 1; n++) {
307         logger.info(format(" %2d %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
308             n, forwardEntropyDifferences[n], forwardEntropyUncertainty[n],
309             backwardEntropyDifferences[n], backwardEntropyUncertainty[n],
310             barBSEntropy[n], barBSEntropyUncertainty[n]));
311         ;
312       }
313     }
314     forwardTotalEntropyChange = forwardBS.getTotalEntropyChange();
315     double forwardTotalEntropyUncertainty = forwardBS.getTotalEntropyUncertainty();
316     backwardTotalEntropyChange = backwardBS.getTotalEntropyChange();
317     double backwardTotalEntropyUncertainty = backwardBS.getTotalEntropyUncertainty();
318     barBSTotalEntropyChange = barBS.getTotalEntropyChange();
319     double barBSTotalEntropyUncertainty = barBS.getTotalEntropyUncertainty();
320     logger.info("\n Total Entropy Difference -T*dS (kcal/mol)");
321     logger.info("        Forward FEP           Backward FEP          BAR Bootstrap");
322     logger.info(format("    %10.4f +/- %6.4f %10.4f +/- %6.4f %10.4f +/- %6.4f",
323         forwardTotalEntropyChange, forwardTotalEntropyUncertainty,
324         backwardTotalEntropyChange, backwardTotalEntropyUncertainty,
325         barBSTotalEntropyChange, barBSTotalEntropyUncertainty));
326   }
327 
328   /**
329    * Free energy difference from forward FEP.
330    *
331    * @return the forward total free energy difference.
332    */
333   public double getForwardTotalFEDifference() {
334     return forwardTotalFEDifference;
335   }
336 
337   /**
338    * Enthalpy difference from forward FEP.
339    *
340    * @return the forward total enthalpy change.
341    */
342   public double getForwardTotalEnthalpyChange() {
343     return forwardTotalEnthalpyChange;
344   }
345 
346   /**
347    * Entropy difference from forward FEP.
348    *
349    * @return the forward total entropy change.
350    */
351   public double getForwardTotalEntropyChange() {
352     return forwardTotalEntropyChange;
353   }
354 
355   /**
356    * Free energy difference from backward FEP.
357    *
358    * @return the backward total free energy difference.
359    */
360   public double getBackwardTotalFEDifference() {
361     return backwardTotalFEDifference;
362   }
363 
364   /**
365    * Enthalpy difference from backward FEP.
366    *
367    * @return the backward total enthalpy change.
368    */
369   public double getBackwardTotalEnthalpyChange() {
370     return backwardTotalEnthalpyChange;
371   }
372 
373   /**
374    * Entropy difference from backward FEP.
375    *
376    * @return the backward total entropy change.
377    */
378   public double getBackwardTotalEntropyChange() {
379     return backwardTotalEntropyChange;
380   }
381 
382   /**
383    * Free energy difference from BAR.
384    *
385    * @return the BAR iteration total free energy difference.
386    */
387   public double getBarIterTotalFEDiff() {
388     return barIterTotalFEDiff;
389   }
390 
391   /**
392    * Free energy difference from BAR using boot-strapping.
393    *
394    * @return the BAR boot-strap total free energy difference.
395    */
396   public double getBarBSTotalFEDiff() {
397     return barBSTotalFEDiff;
398   }
399 
400   /**
401    * Enthalpy difference from BAR.
402    *
403    * @return the BAR boot-strap total enthalpy change.
404    */
405   public double getBarBSTotalEnthalpyChange() {
406     return barBSTotalEnthalpyChange;
407   }
408 
409   /**
410    * Entropy difference from BAR.
411    *
412    * @return the BAR boot-strap total entropy change.
413    */
414   public double getBarBSTotalEntropyChange() {
415     return barBSTotalEntropyChange;
416   }
417 }