mirror of
https://github.com/dnlbauer/WHAM.git
synced 2026-09-11 06:35:30 +00:00
Merge branch 'precalc'
This commit is contained in:
@@ -1,101 +1,101 @@
|
|||||||
#x Free Energy Probability
|
#x Free Energy Probability
|
||||||
-3.108600 7.118164 0.003561
|
-3.108600 7.118166 0.003561
|
||||||
-3.045800 5.331288 0.007290
|
-3.045800 5.331290 0.007290
|
||||||
-2.983000 3.879239 0.013048
|
-2.983000 3.879241 0.013048
|
||||||
-2.920200 2.968658 0.018796
|
-2.920200 2.968659 0.018796
|
||||||
-2.857400 1.942493 0.028362
|
-2.857400 1.942494 0.028362
|
||||||
-2.794600 1.418407 0.034994
|
-2.794600 1.418408 0.034994
|
||||||
-2.731800 1.169397 0.038668
|
-2.731800 1.169398 0.038668
|
||||||
-2.669000 0.843023 0.044073
|
-2.669000 0.843024 0.044073
|
||||||
-2.606200 0.636020 0.047887
|
-2.606200 0.636021 0.047887
|
||||||
-2.543400 0.720123 0.046299
|
-2.543400 0.720123 0.046299
|
||||||
-2.480600 1.066434 0.040297
|
-2.480600 1.066434 0.040297
|
||||||
-2.417800 1.509855 0.033734
|
-2.417800 1.509855 0.033734
|
||||||
-2.355000 2.015549 0.027544
|
-2.355000 2.015549 0.027544
|
||||||
-2.292200 2.226943 0.025306
|
-2.292200 2.226942 0.025306
|
||||||
-2.229400 2.573176 0.022026
|
-2.229400 2.573176 0.022026
|
||||||
-2.166600 2.591946 0.021861
|
-2.166600 2.591946 0.021861
|
||||||
-2.103800 2.476836 0.022893
|
-2.103800 2.476835 0.022893
|
||||||
-2.041000 2.513953 0.022555
|
-2.041000 2.513953 0.022555
|
||||||
-1.978200 2.470033 0.022956
|
-1.978200 2.470032 0.022956
|
||||||
-1.915400 2.227585 0.025299
|
-1.915400 2.227585 0.025299
|
||||||
-1.852600 2.105310 0.026570
|
-1.852600 2.105310 0.026570
|
||||||
-1.789800 1.778780 0.030286
|
-1.789800 1.778779 0.030286
|
||||||
-1.727000 1.447127 0.034593
|
-1.727000 1.447127 0.034593
|
||||||
-1.664200 0.906384 0.042968
|
-1.664200 0.906384 0.042968
|
||||||
-1.601400 0.304263 0.054699
|
-1.601400 0.304263 0.054699
|
||||||
-1.538600 0.247397 0.055960
|
-1.538600 0.247397 0.055960
|
||||||
-1.475800 0.000000 0.061795
|
-1.475800 0.000000 0.061795
|
||||||
-1.413000 0.536141 0.049843
|
-1.413000 0.536142 0.049843
|
||||||
-1.350200 1.428046 0.034859
|
-1.350200 1.428046 0.034859
|
||||||
-1.287400 2.398149 0.023627
|
-1.287400 2.398149 0.023627
|
||||||
-1.224600 3.784647 0.013552
|
-1.224600 3.784647 0.013552
|
||||||
-1.161800 5.696582 0.006297
|
-1.161800 5.696583 0.006297
|
||||||
-1.099000 7.656157 0.002870
|
-1.099000 7.656158 0.002870
|
||||||
-1.036200 9.954999 0.001142
|
-1.036200 9.955000 0.001142
|
||||||
-0.973400 12.357406 0.000436
|
-0.973400 12.357407 0.000436
|
||||||
-0.910600 14.954253 0.000154
|
-0.910600 14.954254 0.000154
|
||||||
-0.847800 17.745325 0.000050
|
-0.847800 17.745327 0.000050
|
||||||
-0.785000 20.559459 0.000016
|
-0.785000 20.559461 0.000016
|
||||||
-0.722200 22.783557 0.000007
|
-0.722200 22.783560 0.000007
|
||||||
-0.659400 25.175862 0.000003
|
-0.659400 25.175865 0.000003
|
||||||
-0.596600 26.419977 0.000002
|
-0.596600 26.419980 0.000002
|
||||||
-0.533800 27.885996 0.000001
|
-0.533800 27.885999 0.000001
|
||||||
-0.471000 29.029330 0.000001
|
-0.471000 29.029334 0.000001
|
||||||
-0.408200 30.366552 0.000000
|
-0.408200 30.366556 0.000000
|
||||||
-0.345400 31.615248 0.000000
|
-0.345400 31.615252 0.000000
|
||||||
-0.282600 32.781711 0.000000
|
-0.282600 32.781716 0.000000
|
||||||
-0.219800 33.731373 0.000000
|
-0.219800 33.731378 0.000000
|
||||||
-0.157000 34.476008 0.000000
|
-0.157000 34.476014 0.000000
|
||||||
-0.094200 35.387294 0.000000
|
-0.094200 35.387300 0.000000
|
||||||
-0.031400 35.579750 0.000000
|
-0.031400 35.579756 0.000000
|
||||||
0.031400 35.520188 0.000000
|
0.031400 35.520194 0.000000
|
||||||
0.094200 35.344757 0.000000
|
0.094200 35.344763 0.000000
|
||||||
0.157000 34.886453 0.000000
|
0.157000 34.886460 0.000000
|
||||||
0.219800 33.633992 0.000000
|
0.219800 33.633998 0.000000
|
||||||
0.282600 32.686504 0.000000
|
0.282600 32.686511 0.000000
|
||||||
0.345400 31.211770 0.000000
|
0.345400 31.211777 0.000000
|
||||||
0.408200 29.680443 0.000000
|
0.408200 29.680451 0.000000
|
||||||
0.471000 28.032233 0.000001
|
0.471000 28.032242 0.000001
|
||||||
0.533800 26.439591 0.000002
|
0.533800 26.439599 0.000002
|
||||||
0.596600 24.432458 0.000003
|
0.596600 24.432467 0.000003
|
||||||
0.659400 22.309884 0.000008
|
0.659400 22.309893 0.000008
|
||||||
0.722200 20.186031 0.000019
|
0.722200 20.186040 0.000019
|
||||||
0.785000 18.295037 0.000040
|
0.785000 18.295046 0.000040
|
||||||
0.847800 16.221223 0.000093
|
0.847800 16.221232 0.000093
|
||||||
0.910600 14.251901 0.000204
|
0.910600 14.251911 0.000204
|
||||||
0.973400 12.562343 0.000402
|
0.973400 12.562353 0.000402
|
||||||
1.036200 11.184019 0.000698
|
1.036200 11.184029 0.000698
|
||||||
1.099000 10.040667 0.001103
|
1.099000 10.040677 0.001103
|
||||||
1.161800 9.369054 0.001444
|
1.161800 9.369064 0.001444
|
||||||
1.224600 9.095382 0.001612
|
1.224600 9.095392 0.001612
|
||||||
1.287400 9.256025 0.001511
|
1.287400 9.256035 0.001511
|
||||||
1.350200 9.814058 0.001208
|
1.350200 9.814068 0.001208
|
||||||
1.413000 10.945197 0.000768
|
1.413000 10.945207 0.000768
|
||||||
1.475800 12.518888 0.000409
|
1.475800 12.518898 0.000409
|
||||||
1.538600 14.409735 0.000191
|
1.538600 14.409744 0.000191
|
||||||
1.601400 16.451956 0.000084
|
1.601400 16.451965 0.000084
|
||||||
1.664200 18.589157 0.000036
|
1.664200 18.589166 0.000036
|
||||||
1.727000 20.669496 0.000016
|
1.727000 20.669504 0.000016
|
||||||
1.789800 22.804783 0.000007
|
1.789800 22.804791 0.000007
|
||||||
1.852600 24.531682 0.000003
|
1.852600 24.531690 0.000003
|
||||||
1.915400 26.182604 0.000002
|
1.915400 26.182612 0.000002
|
||||||
1.978200 27.279432 0.000001
|
1.978200 27.279439 0.000001
|
||||||
2.041000 28.571373 0.000001
|
2.041000 28.571380 0.000001
|
||||||
2.103800 29.328963 0.000000
|
2.103800 29.328969 0.000000
|
||||||
2.166600 29.897732 0.000000
|
2.166600 29.897739 0.000000
|
||||||
2.229400 30.320901 0.000000
|
2.229400 30.320908 0.000000
|
||||||
2.292200 30.065385 0.000000
|
2.292200 30.065391 0.000000
|
||||||
2.355000 29.809130 0.000000
|
2.355000 29.809137 0.000000
|
||||||
2.417800 29.340595 0.000000
|
2.417800 29.340601 0.000000
|
||||||
2.480600 28.473341 0.000001
|
2.480600 28.473346 0.000001
|
||||||
2.543400 27.679348 0.000001
|
2.543400 27.679353 0.000001
|
||||||
2.606200 26.453155 0.000002
|
2.606200 26.453160 0.000002
|
||||||
2.669000 24.424644 0.000003
|
2.669000 24.424649 0.000003
|
||||||
2.731800 22.285929 0.000008
|
2.731800 22.285933 0.000008
|
||||||
2.794600 19.958403 0.000021
|
2.794600 19.958407 0.000021
|
||||||
2.857400 17.606287 0.000053
|
2.857400 17.606291 0.000053
|
||||||
2.920200 15.420135 0.000128
|
2.920200 15.420138 0.000128
|
||||||
2.983000 13.069596 0.000328
|
2.983000 13.069599 0.000328
|
||||||
3.045800 11.036083 0.000740
|
3.045800 11.036085 0.000740
|
||||||
3.108600 9.015655 0.001664
|
3.108600 9.015657 0.001664
|
||||||
|
|||||||
20000
example/2d/wham.out
20000
example/2d/wham.out
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,7 @@ impl Dataset {
|
|||||||
}
|
}
|
||||||
bias_sum += 0.5 * bias_fc[i] * dist * dist
|
bias_sum += 0.5 * bias_fc[i] * dist * dist
|
||||||
}
|
}
|
||||||
|
let bias_sum = (-bias_sum/self.kT).exp();
|
||||||
cache[ndx] = Some(bias_sum);
|
cache[ndx] = Some(bias_sum);
|
||||||
bias_sum
|
bias_sum
|
||||||
}
|
}
|
||||||
@@ -149,6 +150,12 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use super::super::k_B;
|
use super::super::k_B;
|
||||||
|
|
||||||
|
macro_rules! assert_delta {
|
||||||
|
($x:expr, $y:expr, $d:expr) => {
|
||||||
|
assert!(($x-$y).abs() < $d, "{} != {}", $x, $y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_hist() -> Histogram {
|
fn build_hist() -> Histogram {
|
||||||
Histogram::new(
|
Histogram::new(
|
||||||
22, // num_points
|
22, // num_points
|
||||||
@@ -186,38 +193,40 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn calc_bias() {
|
fn calc_bias() {
|
||||||
let ds = build_hist_set();
|
let ds = build_hist_set(); // k = 10
|
||||||
|
|
||||||
// 7th element -> x=7.5, x0=7.5
|
// 7th element -> x=7.5, x0=7.5
|
||||||
assert_eq!(0.0, ds.calc_bias(7, 0));
|
assert_delta!(1.0, ds.calc_bias(7, 0), 0.00000001);
|
||||||
|
|
||||||
// 8th element -> x=8.5, x0=7.5
|
// 8th element -> x=8.5, x0=7.5
|
||||||
assert_eq!(5.0, ds.calc_bias(8, 0));
|
assert_delta!(0.13472233779, ds.calc_bias(8,0), 0.00000001);
|
||||||
|
|
||||||
// 1st element -> x=0.5, x0=7.5. non-cyclic!
|
// 1st element -> x=0.5, x0=7.5. non-cyclic!
|
||||||
assert_eq!(245.0, ds.calc_bias(0, 0));
|
assert_delta!(0.0, ds.calc_bias(0,0), 0.0000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn calc_bias_offset_cyclic() {
|
fn calc_biascyclic() {
|
||||||
let mut ds = build_hist_set();
|
let mut ds = build_hist_set();
|
||||||
ds.cyclic = true;
|
ds.cyclic = true;
|
||||||
|
|
||||||
// 7th element -> x=7.5, x0=7.5
|
// 7th element -> x=7.5, x0=7.5
|
||||||
assert_eq!(0.0, ds.calc_bias(7, 0));
|
assert_delta!(1.0, ds.calc_bias(7, 0), 0.00000001);
|
||||||
|
|
||||||
// 8th element -> x=8.5, x0=7.5
|
// 8th element -> x=8.5, x0=7.5
|
||||||
assert_eq!(5.0, ds.calc_bias(8, 0));
|
assert_delta!(0.13472233779, ds.calc_bias(8, 0), 0.00000001);
|
||||||
|
|
||||||
|
|
||||||
// 1th element -> x=0.5, x0=7.5
|
// 1th element -> x=0.5, x0=7.5
|
||||||
// cyclic flag makes bin 0 neighboring bin 9, so the distance is actually 2
|
// cyclic flag makes bin 0 neighboring bin 9, so the distance is actually 2
|
||||||
assert_eq!(20.0, ds.calc_bias(0, 0));
|
assert_delta!(0.00032942643, ds.calc_bias(0, 0), 0.00000001);
|
||||||
|
|
||||||
// 2nd element -> x=1.5, x0=7.5
|
// 2nd element -> x=1.5, x0=7.5
|
||||||
assert_eq!(45.0, ds.calc_bias(1, 0));
|
assert_delta!(0.00000001, ds.calc_bias(1, 0), 0.00000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -236,13 +236,6 @@ mod tests {
|
|||||||
println!("{:?}", ds);
|
println!("{:?}", ds);
|
||||||
assert_eq!(2, ds.num_windows);
|
assert_eq!(2, ds.num_windows);
|
||||||
assert_eq!(cfg.num_bins[0], ds.dimens_lengths[0]);
|
assert_eq!(cfg.num_bins[0], ds.dimens_lengths[0]);
|
||||||
// fields are private
|
|
||||||
// assert_eq!(cfg.hist_min[0], ds.hist_min[0]);
|
|
||||||
// assert_eq!(cfg.hist_max[0], ds.hist_max[0]);
|
|
||||||
// let expected_bin_width = (cfg.hist_max[0] - cfg.hist_min[0])/cfg.num_bins[0] as f64;
|
|
||||||
// assert_eq!(expected_bin_width, ds.bin_width);
|
|
||||||
// assert_eq!(vec![0.0, 1.0], ds.bias_pos);
|
|
||||||
// assert_eq!(vec![100.0, 200.0], ds.bias_fc);
|
|
||||||
assert_eq!(cfg.temperature * k_B, ds.kT);
|
assert_eq!(cfg.temperature * k_B, ds.kT);
|
||||||
assert_eq!(2, ds.histograms.len())
|
assert_eq!(2, ds.histograms.len())
|
||||||
}
|
}
|
||||||
|
|||||||
302
src/lib.rs
302
src/lib.rs
@@ -31,92 +31,52 @@ pub struct Config {
|
|||||||
|
|
||||||
impl fmt::Display for Config {
|
impl fmt::Display for Config {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Metadata={}, hist_min={:?}, hist_max={:?}, bins={:?}\nverbose={}, tolerance={}, iterations={}, temperature={}, cyclic={:?}" , self.metadata_file, self.hist_min,
|
write!(f, "Metadata={}, hist_min={:?}, hist_max={:?}, bins={:?} verbose={}, tolerance={}, iterations={}, temperature={}, cyclic={:?}", self.metadata_file, self.hist_min, self.hist_max, self.num_bins,
|
||||||
self.hist_max, self.num_bins, self.verbose, self.tolerance,
|
self.verbose, self.tolerance, self.max_iterations, self.temperature, self.cyclic)
|
||||||
self.max_iterations, self.temperature, self.cyclic)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for convergence between two WHAM iterations. WHAM is considered as
|
// Checks for convergence between two WHAM iterations. WHAM is considered as
|
||||||
// converged if the absolute difference for the calculated bias offset is
|
// converged if the maximal difference for the calculated bias offsets is
|
||||||
// smaller then a tolerance value for every simulation window.
|
// smaller then a tolerance value.
|
||||||
fn is_converged(old_F: &[f64], new_F: &[f64], tolerance: f64) -> bool {
|
fn is_converged(old_F: &[f64], new_F: &[f64], tolerance: f64) -> bool {
|
||||||
!new_F.iter().zip(old_F.iter())
|
!new_F.iter().zip(old_F.iter())
|
||||||
.map(|x| { (x.0-x.1).abs() })
|
.map(|x| { (x.0-x.1).abs() })
|
||||||
.any(|diff| { diff > tolerance })
|
.any(|diff| { diff > tolerance })
|
||||||
}
|
}
|
||||||
|
|
||||||
// estimate the probability of a bin of the histogram set based on F values
|
// estimate the probability of a bin of the histogram set based on given bias offsets (F)
|
||||||
// This evaluates the first WHAM equation for each bin
|
// This evaluates the first WHAM equation for each bin.
|
||||||
fn calc_bin_probability(bin: usize, ds: &Dataset, F: &[f64]) -> f64 {
|
fn calc_bin_probability(bin: usize, ds: &Dataset, F: &[f64]) -> f64 {
|
||||||
let mut denom_sum: f64 = 0.0;
|
let mut denom_sum: f64 = 0.0;
|
||||||
let mut bin_count: f64 = 0.0;
|
let mut bin_count: f64 = 0.0;
|
||||||
for (window, h) in ds.histograms.iter().enumerate() {
|
for (window, h) in ds.histograms.iter().enumerate() {
|
||||||
bin_count += h.bins[bin];
|
bin_count += h.bins[bin];
|
||||||
let bias = ds.calc_bias(bin, window);
|
let bias = ds.calc_bias(bin, window);
|
||||||
let bias_offset = ((F[window] - bias) / ds.kT).exp();
|
denom_sum += (h.num_points as f64) * bias * F[window];
|
||||||
denom_sum += (h.num_points as f64) * bias_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bin_count / denom_sum
|
bin_count / denom_sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// estimate the bias offset F of the histogram based on given probabilities
|
// estimate the bias offset F of the histogram based on given probabilities.
|
||||||
// This evaluates the second WHAM equation for each window
|
// This evaluates the second WHAM equation for each window and returns exp(F/kT)
|
||||||
fn calc_window_F(window: usize, ds: &Dataset, P: &[f64]) -> f64 {
|
fn calc_window_F(window: usize, ds: &Dataset, P: &[f64]) -> f64 {
|
||||||
let bf_sum: f64 = (0..ds.num_bins).zip(P.iter()) // zip bins and P
|
let f: f64 = (0..ds.num_bins).zip(P.iter()) // zip bins and P
|
||||||
.filter_map(|count_and_prob: (usize, &f64)| {
|
.filter_map(|bin_and_prob: (usize, &f64)| {
|
||||||
if count_and_prob.1 == &0.0 { // skip zeros for speed
|
if bin_and_prob.1 == &0.0 { // skip zeros for speed
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(count_and_prob.1 * (-ds.calc_bias(count_and_prob.0, window) / ds.kT).exp())
|
let bias = ds.calc_bias(bin_and_prob.0, window);
|
||||||
|
Some(bin_and_prob.1 * bias)
|
||||||
}
|
}
|
||||||
}).sum();
|
}).sum();
|
||||||
-ds.kT * bf_sum.ln()
|
1.0/f
|
||||||
}
|
}
|
||||||
|
|
||||||
// One full WHAM iteration includes calculation of new probabilities P and
|
// One full WHAM iteration includes calculation of new probabilities P and
|
||||||
// new bias offsets F based on previous bias offsets F_prev. This updates
|
// new bias offsets F based on previous bias offsets F_prev. This updates
|
||||||
// the values in vectors F and P
|
// the values in vectors F and P
|
||||||
fn perform_wham_iteration(ds: &Dataset, F_prev: &[f64], F: &mut [f64], P: &mut [f64]) {
|
fn perform_wham_iteration(ds: &Dataset, F_prev: &[f64], F: &mut [f64], P: &mut [f64]) {
|
||||||
// reset bias offsets
|
|
||||||
for window in 0..ds.num_windows {
|
|
||||||
F[window] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for bin in 0..ds.num_bins {
|
|
||||||
// let x = get_x_for_bin(bin, ds.hist_min, ds.bin_width);
|
|
||||||
// let mut num = 0.0;
|
|
||||||
// let mut denom = 0.0;
|
|
||||||
|
|
||||||
// for window in 0..ds.num_windows {
|
|
||||||
// match ds.histograms[window].get_bin_count(bin) {
|
|
||||||
// Some(c) => num += c,
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
// let bias = calc_bias(
|
|
||||||
// ds.bias_fc[window],
|
|
||||||
// ds.bias_pos[window],
|
|
||||||
// x);
|
|
||||||
// let bf = ((F_prev[window]-bias) / ds.kT).exp();
|
|
||||||
// denom += ds.histograms[window].num_points as f64* bf
|
|
||||||
// }
|
|
||||||
// P[bin] = num / denom;
|
|
||||||
|
|
||||||
// for window in 0..ds.num_windows {
|
|
||||||
// let bias = calc_bias(
|
|
||||||
// ds.bias_fc[window],
|
|
||||||
// ds.bias_pos[window],
|
|
||||||
// x);
|
|
||||||
// let bf = (-bias/ds.kT).exp() * P[bin];
|
|
||||||
// F[window] += bf;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for window in 0..ds.num_windows {
|
|
||||||
// F[window] = -ds.kT * F[window].ln();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// evaluate first WHAM equation for each bin to
|
// evaluate first WHAM equation for each bin to
|
||||||
// estimage probabilities based on previous offsets (F_prev)
|
// estimage probabilities based on previous offsets (F_prev)
|
||||||
for bin in 0..ds.num_bins {
|
for bin in 0..ds.num_bins {
|
||||||
@@ -128,9 +88,78 @@ fn perform_wham_iteration(ds: &Dataset, F_prev: &[f64], F: &mut [f64], P: &mut [
|
|||||||
for window in 0..ds.num_windows {
|
for window in 0..ds.num_windows {
|
||||||
F[window] = calc_window_F(window, ds, P);
|
F[window] = calc_window_F(window, ds, P);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run(cfg: &Config) -> Result<(), Box<Error>>{
|
||||||
|
println!("Supplied WHAM options: {}", &cfg);
|
||||||
|
|
||||||
|
println!("Reading input files.");
|
||||||
|
// TODO Better error handling with nice error messages instead of a panic!
|
||||||
|
let histograms = io::read_data(&cfg)
|
||||||
|
.expect("No datapoints in histogram boundaries.");
|
||||||
|
println!("{}",&histograms);
|
||||||
|
|
||||||
|
// allocate required vectors.
|
||||||
|
let mut P: Vec<f64> = vec![f64::NAN; histograms.num_bins]; // bin probability
|
||||||
|
let mut F: Vec<f64> = vec![1.0; histograms.num_windows]; // bias offset exp(F/kT)
|
||||||
|
let mut F_prev: Vec<f64> = vec![f64::NAN; histograms.num_windows]; // previous bias offset
|
||||||
|
let mut F_tmp: Vec<f64> = vec![f64::NAN; histograms.num_windows]; // temp storage for F
|
||||||
|
|
||||||
|
let mut iteration = 0;
|
||||||
|
let mut converged = false;
|
||||||
|
|
||||||
|
// perform WHAM until convergence
|
||||||
|
while !converged && iteration < cfg.max_iterations {
|
||||||
|
iteration += 1;
|
||||||
|
|
||||||
|
// store F values before the next iteration
|
||||||
|
F_prev.copy_from_slice(&F);
|
||||||
|
|
||||||
|
// perform wham iteration (this updates F and P)
|
||||||
|
perform_wham_iteration(&histograms, &F_prev, &mut F, &mut P);
|
||||||
|
|
||||||
|
// convergence check
|
||||||
|
if iteration % 10 == 0 {
|
||||||
|
// This backups exp(F/kT) in a temporary vector and calculates true F and F_prev for
|
||||||
|
// convergence. Finally, F is restored. F_prev does not need to be restored because
|
||||||
|
// its overwritten for the next iteration.
|
||||||
|
F_tmp.copy_from_slice(&F);
|
||||||
|
for window in 0..histograms.num_windows {
|
||||||
|
F[window] = -histograms.kT * F[window].ln();
|
||||||
|
F_prev[window] = -histograms.kT * F_prev[window].ln();
|
||||||
|
}
|
||||||
|
converged = is_converged(&F_prev, &F, cfg.tolerance);
|
||||||
|
|
||||||
|
println!("Iteration {}: dF={}", &iteration, &diff_avg(&F_prev, &F));
|
||||||
|
F.copy_from_slice(&F_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump free energy and bias offsets
|
||||||
|
//if iteration % 100 == 0 {
|
||||||
|
// free_energy(&histograms, &mut P, &mut A);
|
||||||
|
// dump_state(&histograms, &F, &F_prev, &P, &A);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize P to sum(P) = 1.0
|
||||||
|
let P_sum: f64 = P.iter().sum();
|
||||||
|
P.iter_mut().map(|p| *p /= P_sum).count();
|
||||||
|
|
||||||
|
// calculate free energy and dump state
|
||||||
|
println!("Finished. Dumping final PMF");
|
||||||
|
let free_energy = calc_free_energy(&histograms, &P);
|
||||||
|
dump_state(&histograms, &F, &F_prev, &P, &free_energy);
|
||||||
|
|
||||||
|
if iteration == cfg.max_iterations {
|
||||||
|
println!("!!!!! WHAM not converged! (max iterations reached) !!!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
io::write_results(&cfg.output, &histograms, &free_energy, &P)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// get average difference between two bias offset sets
|
// get average difference between two bias offset sets
|
||||||
fn diff_avg(F: &[f64], F_prev: &[f64]) -> f64 {
|
fn diff_avg(F: &[f64], F_prev: &[f64]) -> f64 {
|
||||||
let mut F_sum: f64 = 0.0;
|
let mut F_sum: f64 = 0.0;
|
||||||
@@ -140,88 +169,34 @@ fn diff_avg(F: &[f64], F_prev: &[f64]) -> f64 {
|
|||||||
F_sum / F.len() as f64
|
F_sum / F.len() as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculate the normalized free energy from probability values
|
||||||
// calculate the normalized free energy from normalized probability values
|
fn calc_free_energy(ds: &Dataset, P: &[f64]) -> Vec<f64> {
|
||||||
fn free_energy(ds: &Dataset, P: &[f64], A: &mut [f64]) {
|
let mut minimum = f64::MAX;
|
||||||
let mut bin_min = f64::MAX;
|
let mut free_energy: Vec<f64> = P.iter()
|
||||||
|
.map(|p| {
|
||||||
// Free energy calculation
|
-ds.kT * p.ln()
|
||||||
for bin in 0..ds.num_bins {
|
})
|
||||||
A[bin] = -ds.kT*P[bin].ln();
|
.inspect(|free_e| {
|
||||||
if A[bin] < bin_min {
|
if free_e < &minimum {
|
||||||
bin_min = A[bin];
|
minimum = *free_e;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Make A relative to minimum
|
for e in free_energy.iter_mut() {
|
||||||
for bin in 0..ds.num_bins {
|
*e -= minimum
|
||||||
A[bin] -= bin_min;
|
|
||||||
}
|
}
|
||||||
|
free_energy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(cfg: &Config) -> Result<(), Box<Error>>{
|
// TODO print nice headers for N dimensions
|
||||||
println!("Supplied WHAM options: {}", &cfg);
|
|
||||||
|
|
||||||
// read input data into the histograms object
|
|
||||||
println!("Reading input files.");
|
|
||||||
|
|
||||||
let histograms = io::read_data(&cfg) // TODO nicer error handling for this
|
|
||||||
.expect("No datapoints in histogram boundaries.");
|
|
||||||
println!("{}",&histograms);
|
|
||||||
|
|
||||||
// allocate only once for better performance
|
|
||||||
let mut F_prev: Vec<f64> = vec![f64::INFINITY; histograms.num_windows];
|
|
||||||
let mut F: Vec<f64> = vec![0.0; histograms.num_windows];
|
|
||||||
let mut P: Vec<f64> = vec![f64::NAN; histograms.num_bins];
|
|
||||||
let mut A: Vec<f64> = vec![f64::NAN; histograms.num_bins];
|
|
||||||
|
|
||||||
// perform WHAM until convergence
|
|
||||||
let mut iteration = 0;
|
|
||||||
while !is_converged(&F_prev, &F, cfg.tolerance) && iteration < cfg.max_iterations {
|
|
||||||
iteration += 1;
|
|
||||||
// store F values before the next iteration
|
|
||||||
F_prev.copy_from_slice(&F[..]);
|
|
||||||
|
|
||||||
// perform wham iteration and update F
|
|
||||||
perform_wham_iteration(&histograms, &F_prev, &mut F, &mut P);
|
|
||||||
|
|
||||||
// output some stats during calculation
|
|
||||||
if iteration % 10 == 0 {
|
|
||||||
println!("Iteration {}: dF={}", &iteration, &diff_avg(&F_prev, &F));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump free energy and bias offsets
|
|
||||||
if iteration % 100 == 0 {
|
|
||||||
free_energy(&histograms, &mut P, &mut A);
|
|
||||||
dump_state(&histograms, &F, &F_prev, &P, &A);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize P
|
|
||||||
let P_sum: f64 = P.iter().sum();
|
|
||||||
P.iter_mut().map(|p| *p /= P_sum).count();
|
|
||||||
|
|
||||||
// final free energy calculation and state dump
|
|
||||||
println!("Finished. Dumping final PMF");
|
|
||||||
free_energy(&histograms, &mut P, &mut A);
|
|
||||||
dump_state(&histograms, &F, &F_prev, &P, &A);
|
|
||||||
|
|
||||||
if iteration == cfg.max_iterations {
|
|
||||||
println!("!!!!! WHAM not converged! (max iterations reached) !!!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
io::write_results(&cfg.output, &histograms, &A, &P)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_state(ds: &Dataset, F: &[f64], F_prev: &[f64], P: &[f64], A: &[f64]) {
|
fn dump_state(ds: &Dataset, F: &[f64], F_prev: &[f64], P: &[f64], A: &[f64]) {
|
||||||
let out = std::io::stdout();
|
let out = std::io::stdout();
|
||||||
let mut lock = out.lock();
|
let mut lock = out.lock();
|
||||||
writeln!(lock, "# PMF");
|
writeln!(lock, "# PMF");
|
||||||
writeln!(lock, "#x\t\tFree Energy\t\tP(x)");
|
writeln!(lock, "#x\t\tFree Energy\t\tP(x)");
|
||||||
for bin in 0..ds.num_bins {
|
for bin in 0..ds.num_bins {
|
||||||
let x = ds.get_coords_for_bin(bin)[0]; // TODO
|
let x = ds.get_coords_for_bin(bin)[0];
|
||||||
writeln!(lock, "{:9.5}\t{:9.5}\t{:9.5}", x, A[bin], P[bin]);
|
writeln!(lock, "{:9.5}\t{:9.5}\t{:9.5}", x, A[bin], P[bin]);
|
||||||
}
|
}
|
||||||
writeln!(lock, "# Bias offsets");
|
writeln!(lock, "# Bias offsets");
|
||||||
@@ -231,10 +206,26 @@ fn dump_state(ds: &Dataset, F: &[f64], F_prev: &[f64], P: &[f64], A: &[f64]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::histogram::{Dataset,Histogram};
|
use super::histogram::{Dataset,Histogram};
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
use super::k_B;
|
||||||
|
|
||||||
|
macro_rules! assert_delta {
|
||||||
|
($x:expr, $y:expr, $d:expr) => {
|
||||||
|
assert!(($x-$y).abs() < $d, "{} != {}", $x, $y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn create_test_ds() -> Dataset {
|
||||||
|
let h1 = Histogram::new(10, vec![0.0, 1.0, 1.0, 8.0, 0.0]);
|
||||||
|
let h2 = Histogram::new(10, vec![0.0, 0.0, 8.0, 1.0, 1.0]);
|
||||||
|
Dataset::new(5, vec![5], vec![1.0], vec![0.0], vec![4.0],
|
||||||
|
vec![1.0, 1.0], vec![10.0, 10.0], 300.0*k_B, vec![h1, h2], false)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_converged() {
|
fn is_converged() {
|
||||||
@@ -249,63 +240,44 @@ mod tests {
|
|||||||
assert!(!converged);
|
assert!(!converged);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_test_ds() -> Dataset {
|
#[test]
|
||||||
let h1 = Histogram::new(10, vec![0.0, 0.0, 3.0, 4.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
|
fn calc_bin_probability() {
|
||||||
let h2 = Histogram::new(20, vec![0.0, 0.0, 0.0, 3.0, 2.0, 5.0, 10.0, 0.0, 0.0, 0.0, 0.0]);
|
let ds = create_test_ds();
|
||||||
Dataset::new(4, vec![1], vec![1.0], vec![0.0], vec![4.0],
|
let F = vec![1.0; ds.num_bins] ;
|
||||||
vec![1.0, 2.0], vec![10.0, 10.0], 2.479, vec![h1, h2], false)
|
let expected = vec!(0.0, 0.0825296687031316, 40.92355847097493,
|
||||||
|
124226.70003377, 2308526035.5283747);
|
||||||
|
for b in 0..ds.num_bins {
|
||||||
|
let p = super::calc_bin_probability(b, &ds, &F);
|
||||||
|
assert_delta!(expected[b], p, 0.0000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_near(a: f64, b: f64, tolerance: f64) {
|
|
||||||
let d = (a-b).abs();
|
|
||||||
assert!(d <= tolerance, "Values are not close: {}, {}, d={}", &a, &b, &d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn calc_bias_offset() {
|
fn calc_bias_offset() {
|
||||||
let ds = create_test_ds();
|
let ds = create_test_ds();
|
||||||
let probability = vec!(0.959, 0.331, 0.656, 46.750);
|
let probability = vec!(0.0, 0.1, 0.2, 0.3, 0.4);
|
||||||
let expected = vec!(0.596, -0.250);
|
let expected = vec!(15.927477169990633, 15.927477169990633);
|
||||||
for window in 0..ds.num_windows {
|
for window in 0..ds.num_windows {
|
||||||
let F = super::calc_window_F(window, &ds, &probability);
|
let F = super::calc_window_F(window, &ds, &probability);
|
||||||
assert_near(expected[window], F, 0.001);
|
assert_delta!(expected[window], F, 0.0000001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] // TODO
|
|
||||||
fn calc_bin_probability() {
|
|
||||||
let ds = create_test_ds();
|
|
||||||
let F = vec!(0.0, 0.0);
|
|
||||||
let expected = vec!(0.959, 0.331, 0.656, 46.750);
|
|
||||||
for b in 0..4 {
|
|
||||||
let p = super::calc_bin_probability(b, &ds, &F);
|
|
||||||
assert_near(expected[b], p, 0.001);
|
|
||||||
}
|
|
||||||
|
|
||||||
let F = vec!(1.0, 1.0);
|
|
||||||
let expected = vec!(0.641, 0.221, 0.439, 31.232);
|
|
||||||
for b in 0..4 {
|
|
||||||
let p = super::calc_bin_probability(b, &ds, &F);
|
|
||||||
assert_near(expected[b], p, 0.001);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore] // TODO
|
|
||||||
fn perform_wham_iteration() {
|
fn perform_wham_iteration() {
|
||||||
let ds = create_test_ds();
|
let ds = create_test_ds();
|
||||||
let prev_F = vec![0.0; ds.num_windows];
|
let prev_F = vec![1.0; ds.num_windows];
|
||||||
let mut F = vec![0.0; ds.num_windows];
|
let mut F = vec![f64::NAN; ds.num_windows];
|
||||||
let mut P = vec![f64::NAN; ds.num_bins];
|
let mut P = vec![f64::NAN; ds.num_bins];
|
||||||
super::perform_wham_iteration(&ds, &prev_F, &mut F, &mut P);
|
super::perform_wham_iteration(&ds, &prev_F, &mut F, &mut P);
|
||||||
let expected_F = vec!(0.5948, -0.2513);
|
let expected_F = vec!(1.0, 1.0);
|
||||||
let expected_P = vec!(0.959, 0.331, 0.656, 46.750);
|
let expected_P = vec!(0.0, 0.0825296687031316, 40.92355847097493,
|
||||||
|
124226.70003377, 2308526035.5283747);
|
||||||
for bin in 0..ds.num_bins {
|
for bin in 0..ds.num_bins {
|
||||||
assert_near(expected_P[bin], P[bin], 0.01)
|
assert_delta!(expected_P[bin], P[bin], 0.01)
|
||||||
}
|
}
|
||||||
for window in 0..ds.num_windows {
|
for window in 0..ds.num_windows {
|
||||||
assert_near(expected_F[window], F[window], 0.01)
|
assert_delta!(expected_F[window], F[window], 0.01)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user