|
| 1 | +extern crate num; |
| 2 | +extern crate rustfft; |
| 3 | + |
| 4 | +use num::complex::Complex; |
| 5 | +use rustfft::FFTplanner; |
| 6 | +use std::f64::consts::PI; |
| 7 | +use std::fs::File; |
| 8 | +use std::io::Write; |
| 9 | +use std::path::Path; |
| 10 | + |
| 11 | +// This implementation is based on the C and C++ implementations. |
| 12 | + |
| 13 | +#[derive(Clone)] |
| 14 | +struct Parameters { |
| 15 | + xmax: f64, |
| 16 | + res: usize, |
| 17 | + dt: f64, |
| 18 | + timesteps: usize, |
| 19 | + dx: f64, |
| 20 | + x: Vec<f64>, |
| 21 | + dk: f64, |
| 22 | + k: Vec<f64>, |
| 23 | + im_time: bool, |
| 24 | +} |
| 25 | + |
| 26 | +impl Parameters { |
| 27 | + pub fn new(xmax: f64, res: usize, dt: f64, timesteps: usize, im_time: bool) -> Parameters { |
| 28 | + let dx = 2.0_f64 * xmax / (res as f64); |
| 29 | + let mut x: Vec<f64> = Vec::with_capacity(res); |
| 30 | + let dk = PI / xmax; |
| 31 | + let mut k: Vec<f64> = Vec::with_capacity(res); |
| 32 | + for i in 0..res { |
| 33 | + x.push(xmax / (res as f64) - xmax + (i as f64) * dx); |
| 34 | + match i { |
| 35 | + i if (i < res / 2) => k.push((i as f64) * PI / xmax), |
| 36 | + _ => k.push(((i as f64) - (res as f64)) * PI / xmax), |
| 37 | + } |
| 38 | + } |
| 39 | + Parameters { |
| 40 | + xmax, |
| 41 | + res, |
| 42 | + dt, |
| 43 | + timesteps, |
| 44 | + im_time, |
| 45 | + dx, |
| 46 | + x, |
| 47 | + dk, |
| 48 | + k, |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +struct Operators { |
| 54 | + v: Vec<Complex<f64>>, |
| 55 | + pe: Vec<Complex<f64>>, |
| 56 | + ke: Vec<Complex<f64>>, |
| 57 | + wfc: Vec<Complex<f64>>, |
| 58 | +} |
| 59 | + |
| 60 | +impl Operators { |
| 61 | + pub fn new(par: &Parameters, v_offset: f64, wfc_offset: f64) -> Operators { |
| 62 | + let mut v: Vec<Complex<f64>> = Vec::with_capacity(par.res); |
| 63 | + let mut pe: Vec<Complex<f64>> = Vec::with_capacity(par.res); |
| 64 | + let mut ke: Vec<Complex<f64>> = Vec::with_capacity(par.res); |
| 65 | + let mut wfc: Vec<Complex<f64>> = Vec::with_capacity(par.res); |
| 66 | + |
| 67 | + for i in 0..par.res { |
| 68 | + v.push(Complex::new( |
| 69 | + 0.5_f64 * (par.x[i] - v_offset).powi(2), |
| 70 | + 0.0_f64, |
| 71 | + )); |
| 72 | + wfc.push(Complex::new( |
| 73 | + (-((par.x[i] - wfc_offset).powi(2)) / 2.0_f64).exp(), |
| 74 | + 0.0_f64, |
| 75 | + )); |
| 76 | + if par.im_time { |
| 77 | + ke.push(Complex::new( |
| 78 | + (-0.5_f64 * par.dt * par.k[i].powi(2)).exp(), |
| 79 | + 0.0_f64, |
| 80 | + )); |
| 81 | + pe.push(Complex::new((-0.5_f64 * par.dt * v[i].re).exp(), 0.0_f64)); |
| 82 | + } else { |
| 83 | + ke.push(Complex::new( |
| 84 | + 0.0_f64, |
| 85 | + (-0.5_f64 * par.dt * par.k[i].powi(2)).exp(), |
| 86 | + )); |
| 87 | + pe.push(Complex::new(0.0_f64, (-0.5_f64 * par.dt * v[i].re).exp())); |
| 88 | + } |
| 89 | + } |
| 90 | + Operators { v, pe, ke, wfc } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +fn fft(x: &mut Vec<Complex<f64>>, inverse: bool) { |
| 95 | + let mut y = vec![Complex::new(0.0_f64, 0.0_f64); x.len()]; |
| 96 | + let mut p = FFTplanner::new(inverse); |
| 97 | + let fft = p.plan_fft(x.len()); |
| 98 | + fft.process(x, &mut y); |
| 99 | + |
| 100 | + for i in 0..x.len() { |
| 101 | + x[i] = y[i] / (x.len() as f64).sqrt(); |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +fn split_op(par: &Parameters, opr: &mut Operators) { |
| 106 | + let mut density: Vec<f64>; |
| 107 | + |
| 108 | + for i in 0..par.timesteps { |
| 109 | + for j in 0..par.res { |
| 110 | + opr.wfc[j] *= opr.pe[j]; |
| 111 | + } |
| 112 | + |
| 113 | + fft(&mut opr.wfc, false); |
| 114 | + |
| 115 | + for j in 0..par.res { |
| 116 | + opr.wfc[j] *= opr.ke[j]; |
| 117 | + } |
| 118 | + |
| 119 | + fft(&mut opr.wfc, true); |
| 120 | + |
| 121 | + for j in 0..par.res { |
| 122 | + opr.wfc[j] *= opr.pe[j]; |
| 123 | + } |
| 124 | + |
| 125 | + density = opr.wfc.iter().map(|x| x.norm().powi(2)).collect(); |
| 126 | + |
| 127 | + if par.im_time { |
| 128 | + let sum = density.iter().sum::<f64>() * par.dx; |
| 129 | + |
| 130 | + for j in 0..par.res { |
| 131 | + opr.wfc[j] /= sum.sqrt(); |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + // Writing data into a file in the format of: |
| 136 | + // index, density, real potential. |
| 137 | + let path_name = format!("output{}.dat", i); |
| 138 | + let path = Path::new(&path_name); |
| 139 | + let display = path.display(); |
| 140 | + |
| 141 | + let mut file = match File::create(&path) { |
| 142 | + Err(why) => panic!("Couldn't create {}: {}", display, why), |
| 143 | + Ok(good) => good, |
| 144 | + }; |
| 145 | + |
| 146 | + for j in 0..par.res { |
| 147 | + if let Err(why) = writeln!(file, "{}\t{}\t{}", j, density[j], opr.v[j].re) { |
| 148 | + panic!("Couldn't write to {}: {}", display, why) |
| 149 | + } |
| 150 | + if let Err(why) = file.flush() { |
| 151 | + panic!("Couldn't flush {}: {}", display, why) |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +fn calculate_energy(par: &Parameters, opr: &Operators) -> f64 { |
| 158 | + let wfc_r = opr.wfc.clone(); |
| 159 | + let mut wfc_k = opr.wfc.clone(); |
| 160 | + let mut wfc_c = vec![Complex::new(0.0_f64, 0.0_f64); par.res]; |
| 161 | + |
| 162 | + fft(&mut wfc_k, false); |
| 163 | + |
| 164 | + for i in 0..par.res { |
| 165 | + wfc_c[i] = wfc_r[i].conj(); |
| 166 | + } |
| 167 | + |
| 168 | + let mut energy_k = vec![Complex::new(0.0_f64, 0.0_f64); par.res]; |
| 169 | + let mut energy_r = vec![Complex::new(0.0_f64, 0.0_f64); par.res]; |
| 170 | + |
| 171 | + for i in 0..par.res { |
| 172 | + energy_k[i] = wfc_k[i] * Complex::new(par.k[i], 0.0_f64).powi(2); |
| 173 | + } |
| 174 | + |
| 175 | + fft(&mut energy_k, true); |
| 176 | + |
| 177 | + for i in 0..par.res { |
| 178 | + energy_k[i] *= wfc_c[i].scale(0.5_f64); |
| 179 | + energy_r[i] = wfc_c[i] * opr.v[i] * wfc_r[i]; |
| 180 | + } |
| 181 | + |
| 182 | + let energy_final = energy_k |
| 183 | + .into_iter() |
| 184 | + .zip(energy_r.into_iter()) |
| 185 | + .fold(0.0_f64, |acc, x| acc + (x.0 + x.1).re); |
| 186 | + |
| 187 | + energy_final * par.dx |
| 188 | +} |
| 189 | + |
| 190 | +fn main() { |
| 191 | + let par = Parameters::new(5.0, 256, 0.05, 100, true); |
| 192 | + let mut opr = Operators::new(&par, 0.0, -1.0); |
| 193 | + |
| 194 | + split_op(&par, &mut opr); |
| 195 | + |
| 196 | + println!("The energy is {}", calculate_energy(&par, &opr)); |
| 197 | +} |
0 commit comments