1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.numerics.estimator;
39
40 import java.util.Random;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import java.util.stream.IntStream;
44
45 import static ffx.numerics.estimator.EstimateBootstrapper.getBootstrapIndices;
46 import static ffx.utilities.Constants.R;
47 import static java.lang.Double.isInfinite;
48 import static java.lang.Double.isNaN;
49 import static java.lang.String.format;
50 import static java.util.Arrays.copyOf;
51 import static org.apache.commons.math3.util.FastMath.exp;
52 import static org.apache.commons.math3.util.FastMath.log;
53
54
55
56
57
58
59
60
61
62 public class Zwanzig extends SequentialEstimator implements BootstrappableEstimator {
63
64 private static final Logger logger = Logger.getLogger(Zwanzig.class.getName());
65
66
67
68
69 public final Directionality directionality;
70
71
72
73 private final boolean forwards;
74
75
76
77 private final int nWindows;
78
79
80
81 private final double[] freeEnergyDifferences;
82
83
84
85 private final double[] enthalpyDifferences;
86
87
88
89 private final double[] freeEnergyDifferenceUncertainties;
90
91
92
93 private final Random random;
94
95
96
97 private double totalFreeEnergyDifference;
98
99
100
101
102 private double totalFreeEnergyDifferenceUncertainty;
103
104
105
106 private double totalEnthalpyDifference;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 public Zwanzig(double[] lambdaValues, double[][] eLambdaMinusdL, double[][] eLambda,
126 double[][] eLambdaPlusdL, double[] temperature, Directionality directionality) {
127 super(lambdaValues, eLambdaMinusdL, eLambda, eLambdaPlusdL, temperature);
128 this.directionality = directionality;
129 nWindows = nStates - 1;
130
131 freeEnergyDifferences = new double[nWindows];
132 freeEnergyDifferenceUncertainties = new double[nWindows];
133 enthalpyDifferences = new double[nWindows];
134
135 forwards = directionality.equals(Directionality.FORWARDS);
136 random = new Random();
137
138 estimateDG();
139 }
140
141
142
143
144 @Override
145 public Zwanzig copyEstimator() {
146 return new Zwanzig(lamValues, eLambdaMinusdL, eLambda, eLambdaPlusdL, temperatures, directionality);
147 }
148
149
150
151
152 @Override
153 public final void estimateDG(final boolean randomSamples) {
154 double cumDG = 0;
155
156 Level warningLevel = randomSamples ? Level.FINE : Level.WARNING;
157
158 for (int i = 0; i < nWindows; i++) {
159 final int windowIndex = i + (forwards ? 0 : 1);
160 final double[] referenceEnergy = eLambda[windowIndex];
161 final double[] perturbedEnergy = forwards ? eLambdaPlusdL[windowIndex] : eLambdaMinusdL[windowIndex];
162 final double sign = forwards ? 1.0 : -1.0;
163 final double kT = temperatures[windowIndex] * R;
164 final double beta = 1.0 / kT;
165 final int numSamples = referenceEnergy.length;
166 if (numSamples == 0) {
167 logger.log(warningLevel, " Skipping frame " + i + " due to lack of snapshots!");
168 continue;
169 }
170
171
172 int[] sampleIndices = randomSamples ? getBootstrapIndices(numSamples, random) : IntStream.range(0, numSamples).toArray();
173
174 double boltzmannFactorSum = 0.0;
175 double weightedMeanEnergy = 0.0;
176 double meanEnergy = 0.0;
177 for (int j = 0; j < numSamples; j++) {
178 int index = sampleIndices[j];
179 final double dE = perturbedEnergy[index] - referenceEnergy[index];
180 final double weight = exp(-beta * dE);
181 boltzmannFactorSum += weight;
182 if (forwards) {
183 meanEnergy += referenceEnergy[index];
184 weightedMeanEnergy += perturbedEnergy[index] * weight;
185 } else {
186 meanEnergy += perturbedEnergy[index];
187 weightedMeanEnergy += referenceEnergy[index] * weight;
188 }
189 }
190 meanEnergy = meanEnergy / numSamples;
191 double dG = -sign * kT * log(boltzmannFactorSum / numSamples);
192
193 if (isNaN(dG) || isInfinite(dG)) {
194 logger.severe(format(" Change in free energy (%9.4f) for window (%2d of %2d) failed. " +
195 "Sign: %9.4f, Beta: %9.4f, Temp: %9.4f, Sum: %9.4f, Len: %3d, Log: %9.4f",
196 dG, i, nWindows, sign, kT, temperatures[windowIndex], boltzmannFactorSum, numSamples, log(boltzmannFactorSum / numSamples)));
197 }
198
199 freeEnergyDifferences[i] = dG;
200 enthalpyDifferences[i] = sign * ((weightedMeanEnergy / boltzmannFactorSum) - meanEnergy);
201 freeEnergyDifferenceUncertainties[i] = 0.0;
202 totalEnthalpyDifference += enthalpyDifferences[i];
203 cumDG += dG;
204 }
205
206 totalFreeEnergyDifference = cumDG;
207 totalFreeEnergyDifferenceUncertainty = 0.0;
208 }
209
210
211
212
213 @Override
214 public final void estimateDG() {
215 estimateDG(false);
216 }
217
218
219
220
221 @Override
222 public double getTotalFreeEnergyDifference() {
223 return totalFreeEnergyDifference;
224 }
225
226
227
228
229 @Override
230 public double[] getFreeEnergyDifferences() {
231 return copyOf(freeEnergyDifferences, nWindows);
232 }
233
234
235
236
237 @Override
238 public double getTotalFEDifferenceUncertainty() {
239 return totalFreeEnergyDifferenceUncertainty;
240 }
241
242
243
244
245 @Override
246 public double[] getFEDifferenceUncertainties() {
247 return copyOf(freeEnergyDifferenceUncertainties, nWindows);
248 }
249
250
251
252
253 @Override
254 public int getNumberOfBins() {
255 return nWindows;
256 }
257
258
259
260
261 @Override
262 public double getTotalEnthalpyDifference() {
263 return totalEnthalpyDifference;
264 }
265
266
267
268
269 @Override
270 public double[] getEnthalpyDifferences() {
271 return copyOf(enthalpyDifferences, nWindows);
272 }
273
274
275
276
277 public enum Directionality {
278
279
280
281 FORWARDS,
282
283
284
285 BACKWARDS
286 }
287 }