pub mod advance;
pub mod coeffs;
pub mod cpsource;
pub mod psolve;
pub mod source;
pub mod vertical;
use {
crate::{
constants::*,
parameters::Parameters,
spectral::Spectral,
sta2dfft::D2FFT,
utils::{arr2zero, arr3zero, view3d},
},
advance::advance,
anyhow::Result,
byteorder::{ByteOrder, LittleEndian},
log::info,
ndarray::{Array1, Array3, ArrayView1, Axis, ShapeBuilder, Zip},
psolve::psolve,
serde::{Deserialize, Serialize},
source::source,
std::{
f64::consts::PI,
fs::{create_dir_all, File},
io::{Read, Write},
path::Path,
},
};
#[derive(Debug)]
pub struct Output {
pub ecomp: File,
pub monitor: File,
pub spectra: File,
pub d3ql: File,
pub d3d: File,
pub d3g: File,
pub d3r: File,
pub d3w: File,
pub d3pn: File,
pub d2q: File,
pub d2d: File,
pub d2g: File,
pub d2h: File,
pub d2zeta: File,
}
impl Output {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
let d2 = path.as_ref().join("2d/");
let d3 = path.as_ref().join("3d/");
create_dir_all(&d2)?;
create_dir_all(&d3)?;
Ok(Self {
ecomp: File::create(path.as_ref().join("ecomp.asc"))?,
monitor: File::create(path.as_ref().join("monitor.asc"))?,
spectra: File::create(path.as_ref().join("spectra.asc"))?,
d3ql: File::create(d3.join("ql.r4"))?,
d3d: File::create(d3.join("d.r4"))?,
d3g: File::create(d3.join("g.r4"))?,
d3r: File::create(d3.join("r.r4"))?,
d3w: File::create(d3.join("w.r4"))?,
d3pn: File::create(d3.join("pn.r4"))?,
d2q: File::create(d2.join("q.r4"))?,
d2d: File::create(d2.join("d.r4"))?,
d2g: File::create(d2.join("g.r4"))?,
d2h: File::create(d2.join("h.r4"))?,
d2zeta: File::create(d2.join("zeta.r4"))?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct State {
pub spectral: Spectral,
pub u: Array3<f64>,
pub v: Array3<f64>,
pub w: Array3<f64>,
pub z: Array3<f64>,
pub zx: Array3<f64>,
pub zy: Array3<f64>,
pub r: Array3<f64>,
pub ri: Array3<f64>,
pub aa: Array3<f64>,
pub zeta: Array3<f64>,
pub pn: Array3<f64>,
pub dpn: Array3<f64>,
pub ps: Array3<f64>,
pub qs: Array3<f64>,
pub ds: Array3<f64>,
pub gs: Array3<f64>,
pub t: f64,
pub ngsave: usize,
pub itime: usize,
pub jtime: usize,
pub ggen: bool,
}
impl State {
pub fn defragment(&mut self) {
let ds = self.ds.clone();
self.ds = arr3zero(self.spectral.ng, self.spectral.nz);
self.ds.assign(&ds);
let gs = self.gs.clone();
self.gs = arr3zero(self.spectral.ng, self.spectral.nz);
self.gs.assign(&gs);
}
}
pub fn nhswps(parameters: &Parameters) -> Result<()> {
let ng = parameters.numerical.grid_resolution;
let nz = parameters.numerical.vertical_layers;
let dt = parameters.numerical.time_step;
let tgsave = parameters.numerical.save_interval;
let tsim = parameters.numerical.duration;
let out_dir = parameters.environment.output_directory.clone();
let qq = {
let mut f = File::open(out_dir.join("qq_init.r8"))?;
let mut qq = Vec::new();
f.read_to_end(&mut qq)?;
qq.chunks(8)
.skip(1)
.map(LittleEndian::read_f64)
.collect::<Vec<f64>>()
};
let dd = {
let mut f = File::open(out_dir.join("dd_init.r8"))?;
let mut dd = Vec::new();
f.read_to_end(&mut dd)?;
dd.chunks(8)
.skip(1)
.map(LittleEndian::read_f64)
.collect::<Vec<f64>>()
};
let gg = {
let mut f = File::open(out_dir.join("gg_init.r8"))?;
let mut gg = Vec::new();
f.read_to_end(&mut gg)?;
gg.chunks(8)
.skip(1)
.map(LittleEndian::read_f64)
.collect::<Vec<f64>>()
};
let mut state = State {
spectral: Spectral::new(ng, nz),
u: arr3zero(ng, nz),
v: arr3zero(ng, nz),
w: arr3zero(ng, nz),
z: arr3zero(ng, nz),
zx: arr3zero(ng, nz),
zy: arr3zero(ng, nz),
r: arr3zero(ng, nz),
ri: arr3zero(ng, nz),
aa: arr3zero(ng, nz),
zeta: arr3zero(ng, nz),
pn: arr3zero(ng, nz),
dpn: arr3zero(ng, nz),
ps: arr3zero(ng, nz),
qs: arr3zero(ng, nz),
ds: arr3zero(ng, nz),
gs: arr3zero(ng, nz),
t: 0.0,
ngsave: 0,
itime: 0,
jtime: 0,
ggen: false,
};
let mut output = Output::from_path(out_dir)?;
state
.spectral
.ptospc3d(view3d(&qq, ng, ng, nz + 1), state.qs.view_mut(), 0, nz);
{
for i in 0..=nz {
state.qs[[0, 0, i]] = 0.0;
}
};
state
.spectral
.ptospc3d(view3d(&dd, ng, ng, nz + 1), state.ds.view_mut(), 0, nz);
{
for i in 0..=nz {
state.ds[[0, 0, i]] = 0.0;
}
};
state
.spectral
.ptospc3d(view3d(&gg, ng, ng, nz + 1), state.gs.view_mut(), 0, nz);
{
for i in 0..=nz {
state.gs[[0, 0, i]] = 0.0;
}
};
for iz in 0..=nz {
Zip::from(state.qs.index_axis_mut(Axis(2), iz))
.and(&state.spectral.filt)
.apply(|qs, filt| *qs *= filt);
Zip::from(state.ds.index_axis_mut(Axis(2), iz))
.and(&state.spectral.filt)
.apply(|ds, filt| *ds *= filt);
Zip::from(state.gs.index_axis_mut(Axis(2), iz))
.and(&state.spectral.filt)
.apply(|gs, filt| *gs *= filt);
}
state.spectral.main_invert(
state.qs.view(),
state.ds.view(),
state.gs.view(),
state.r.view_mut(),
state.u.view_mut(),
state.v.view_mut(),
state.zeta.view_mut(),
);
state.ngsave = (tgsave / dt).round() as usize;
while state.t <= tsim {
state.itime = (state.t / dt).round() as usize;
state.jtime = state.itime / state.ngsave;
if state.ngsave * state.jtime == state.itime {
state.spectral.main_invert(
state.qs.view(),
state.ds.view(),
state.gs.view(),
state.r.view_mut(),
state.u.view_mut(),
state.v.view_mut(),
state.zeta.view_mut(),
);
psolve(&mut state);
savegrid(&mut state, &mut output)?;
state.ggen = false;
} else {
state.ggen = true;
}
advance(&mut state, &mut output)?;
}
state.itime = (state.t / dt) as usize;
state.jtime = state.itime / state.ngsave;
if state.ngsave * state.jtime == state.itime {
state.spectral.main_invert(
state.qs.view(),
state.ds.view(),
state.gs.view(),
state.r.view_mut(),
state.u.view_mut(),
state.v.view_mut(),
state.zeta.view_mut(),
);
psolve(&mut state);
savegrid(&mut state, &mut output)?;
}
Ok(())
}
pub fn savegrid(state: &mut State, output: &mut Output) -> Result<()> {
let ng = state.spectral.ng;
let nz = state.spectral.nz;
let arr2zero = arr2zero(ng);
let mut v3d = Array3::<f32>::from_shape_vec(
(ng, ng, nz + 1).strides((1, ng, ng * ng)),
vec![0.0; ng * ng * (nz + 1)],
)
.unwrap();
let mut wkp = arr2zero.clone();
let mut wkq = arr2zero.clone();
let mut wks = arr2zero;
let mut zspec = Array1::<f64>::zeros(ng + 1);
let mut dspec = Array1::<f64>::zeros(ng + 1);
let mut gspec = Array1::<f64>::zeros(ng + 1);
Zip::from(&mut wkp)
.and(&state.r.index_axis(Axis(2), 0))
.and(&state.u.index_axis(Axis(2), 0))
.and(&state.v.index_axis(Axis(2), 0))
.par_apply(|wkp, &r, &u, &v| {
*wkp = (1.0 + r) * (u.powf(2.0) + v.powf(2.0));
});
let mut ekin = (1.0 / 2.0) * wkp.sum();
Zip::from(&mut wkp)
.and(&state.r.index_axis(Axis(2), nz))
.and(&state.u.index_axis(Axis(2), nz))
.and(&state.v.index_axis(Axis(2), nz))
.and(&state.w.index_axis(Axis(2), nz))
.par_apply(|wkp, &r, &u, &v, &w| {
*wkp = (1.0 + r) * (u.powf(2.0) + v.powf(2.0) + w.powf(2.0));
});
ekin += (1.0 / 2.0) * wkp.sum();
for iz in 1..nz {
Zip::from(&mut wkp)
.and(&state.r.index_axis(Axis(2), iz))
.and(&state.u.index_axis(Axis(2), iz))
.and(&state.v.index_axis(Axis(2), iz))
.and(&state.w.index_axis(Axis(2), iz))
.par_apply(|wkp, &r, &u, &v, &w| {
*wkp = (1.0 + r) * (u.powf(2.0) + v.powf(2.0) + w.powf(2.0));
});
ekin += wkp.sum();
}
let gl = (2.0 * PI) / ng as f64;
let dz = HBAR / nz as f64;
let gvol = gl * gl * dz;
ekin *= (1.0 / 2.0) * gvol * (1.0 / HBAR);
Zip::from(&mut wkp)
.and(&state.z.index_axis(Axis(2), nz))
.par_apply(|wkp, &z| *wkp = ((1.0 / HBAR) * z - 1.0).powf(2.0));
let epot = (1.0 / 2.0) * (gl * gl) * CSQ * wkp.sum();
let etot = ekin + epot;
write!(
output.ecomp,
"{:.6} {:.9} {:.9} {:.9} {:.9} {:.9}\n",
state.t, 0.0, ekin, ekin, epot, etot
)?;
info!("t = {}, E_tot = {}", state.t, etot);
zspec.fill(0.0);
dspec.fill(0.0);
gspec.fill(0.0);
let d2fft = D2FFT::new(
ng,
ng,
2.0 * PI,
2.0 * PI,
&mut vec![0.0; ng],
&mut vec![0.0; ng],
);
let mut tmpspec = Array1::<f64>::zeros(ng + 1);
for iz in 0..=nz {
wkp.assign(&state.zeta.index_axis(Axis(2), iz));
d2fft.ptospc(wkp.view_mut(), wks.view_mut());
state.spectral.spec1d(
wks.as_slice_memory_order().unwrap(),
tmpspec.as_slice_memory_order_mut().unwrap(),
);
zspec += &(state.spectral.weight[iz] * &tmpspec);
state.spectral.spec1d(
state
.ds
.index_axis(Axis(2), iz)
.as_slice_memory_order()
.unwrap(),
tmpspec.as_slice_memory_order_mut().unwrap(),
);
dspec += &(state.spectral.weight[iz] * &tmpspec);
state.spectral.spec1d(
state
.gs
.index_axis(Axis(2), iz)
.as_slice_memory_order()
.unwrap(),
tmpspec.as_slice_memory_order_mut().unwrap(),
);
gspec += &(state.spectral.weight[iz] * &tmpspec);
}
let spmf = ArrayView1::from_shape(ng + 1, &state.spectral.spmf).unwrap();
zspec *= &spmf;
dspec *= &spmf;
gspec *= &spmf;
write!(
output.spectra,
"{:.6} {}\n",
state.t, state.spectral.kmaxred
)?;
for k in 1..=state.spectral.kmaxred {
write!(
output.spectra,
"{:.8} {:.8} {:.8} {:.8}\n",
state.spectral.alk[k - 1],
zspec[k].log10(),
(dspec[k] + 1.0E-32).log10(),
(gspec[k] + 1.0E-32).log10()
)?;
}
for iz in 0..=nz {
wks.assign(&state.qs.index_axis(Axis(2), iz));
d2fft.spctop(wks.view_mut(), wkp.view_mut());
v3d.index_axis_mut(Axis(2), iz)
.assign(&wkp.mapv(|f| f as f32));
}
append_output(
&mut output.d3ql,
state.t,
v3d.as_slice_memory_order().unwrap(),
)?;
for iz in 0..=nz {
wks.assign(&state.ds.index_axis(Axis(2), iz));
d2fft.spctop(wks.view_mut(), wkp.view_mut());
v3d.index_axis_mut(Axis(2), iz)
.assign(&wkp.mapv(|f| f as f32));
}
append_output(
&mut output.d3d,
state.t,
v3d.as_slice_memory_order().unwrap(),
)?;
for iz in 0..=nz {
wks.assign(&state.gs.index_axis(Axis(2), iz));
d2fft.spctop(wks.view_mut(), wkp.view_mut());
v3d.index_axis_mut(Axis(2), iz)
.assign(&wkp.mapv(|f| f as f32));
}
append_output(
&mut output.d3g,
state.t,
v3d.as_slice_memory_order().unwrap(),
)?;
let r_f32 = state
.r
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d3r, state.t, &r_f32)?;
let w_f32 = state
.w
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d3w, state.t, &w_f32)?;
let pn_f32 = state
.pn
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d3pn, state.t, &pn_f32)?;
Zip::from(&mut wkp)
.and(&state.w.index_axis(Axis(2), nz))
.and(&state.z.index_axis(Axis(2), nz))
.apply(|wkp, &w, &z| {
*wkp = -w / z;
});
let wkp_f32 = wkp
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d2d, state.t, &wkp_f32)?;
wkp.fill(0.0);
for iz in 0..=nz {
Zip::from(&mut wkp)
.and(&state.zeta.index_axis(Axis(2), iz))
.and(&state.r.index_axis(Axis(2), iz))
.apply(|wkp, zeta, r| {
*wkp += state.spectral.weight[iz] * zeta * (1.0 + r);
});
}
Zip::from(&mut wkp)
.and(&state.z.index_axis(Axis(2), nz))
.apply(|wkp, z| {
*wkp *= HBAR / z;
});
let wkp_f32 = wkp
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d2zeta, state.t, &wkp_f32)?;
Zip::from(&mut wkp)
.and(&state.z.index_axis(Axis(2), nz))
.apply(|wkp, z| *wkp = HBAR * (*wkp + COF) / z - COF);
let wkp_f32 = wkp
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d2q, state.t, &wkp_f32)?;
wkp.fill(0.0);
for iz in 0..=nz {
wks.assign(&state.gs.index_axis(Axis(2), iz));
d2fft.spctop(wks.view_mut(), wkq.view_mut());
Zip::from(&mut wkp)
.and(&wkq)
.and(&state.r.index_axis(Axis(2), iz))
.apply(|wkp, wkq, r| *wkp += state.spectral.weight[iz] * wkq * (1.0 + r));
}
Zip::from(&mut wkp)
.and(&state.z.index_axis(Axis(2), nz))
.apply(|wkp, z| *wkp *= HBAR / z);
let wkp_f32 = wkp
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d2g, state.t, &wkp_f32)?;
Zip::from(&mut wkp)
.and(&state.z.index_axis(Axis(2), nz))
.apply(|wkp, z| *wkp = (1.0 / HBAR) * z - 1.0);
let wkp_f32 = wkp
.as_slice_memory_order()
.unwrap()
.iter()
.map(|x| *x as f32)
.collect::<Vec<f32>>();
append_output(&mut output.d2h, state.t, &wkp_f32)?;
Ok(())
}
fn append_output(field: &mut File, t: f64, data: &[f32]) -> Result<()> {
let mut buf = [0u8; 4];
LittleEndian::write_f32(&mut buf, t as f32);
field.write_all(&buf)?;
for e in data {
LittleEndian::write_f32(&mut buf, *e);
field.write_all(&buf)?;
}
Ok(())
}
fn diagnose(state: &mut State, output: &mut Output) -> Result<()> {
let ng = state.spectral.ng;
let nz = state.spectral.nz;
let umax = state
.u
.iter()
.zip(&state.v)
.map(|(u, v)| u.powf(2.0) + v.powf(2.0))
.fold(std::f64::NAN, f64::max)
.sqrt();
let zmax = state
.zeta
.iter()
.map(|x| x.abs())
.fold(std::f64::NAN, f64::max);
let vsumi = 1.0 / (ng * ng * nz) as f64;
let mut sum = 0.0;
for i in 0..ng {
for j in 0..ng {
sum += (1.0 / 2.0) * state.zeta[[i, j, 0]].powf(2.0);
sum += (1.0 / 2.0) * state.zeta[[i, j, nz]].powf(2.0);
for k in 1..nz {
sum += state.zeta[[i, j, k]].powf(2.0);
}
}
}
let zrms = (vsumi * sum).sqrt();
write!(
output.monitor,
"{:.5} {:.6} {:.6} {:.6} {:.6}\n",
state.t,
(1.0 / 2.0) * (zrms.powf(2.0)),
zrms,
zmax,
umax
)?;
Ok(())
}