use crate::block::Block;
use crate::Float;
#[derive(Clone, Debug)]
pub struct FaceMetrics {
pub si_x: Vec<Float>,
pub si_y: Vec<Float>,
pub si_z: Vec<Float>,
pub ci_x: Vec<Float>,
pub ci_y: Vec<Float>,
pub ci_z: Vec<Float>,
pub sj_x: Vec<Float>,
pub sj_y: Vec<Float>,
pub sj_z: Vec<Float>,
pub cj_x: Vec<Float>,
pub cj_y: Vec<Float>,
pub cj_z: Vec<Float>,
pub sk_x: Vec<Float>,
pub sk_y: Vec<Float>,
pub sk_z: Vec<Float>,
pub ck_x: Vec<Float>,
pub ck_y: Vec<Float>,
pub ck_z: Vec<Float>,
}
pub fn compute_cell_volumes(block: &Block) -> Vec<Float> {
let ni = block.imax;
let nj = block.jmax;
let nk = block.kmax;
let nci = ni - 1; let ncj = nj - 1;
let nck = nk - 1;
let ncells = nci * ncj * nck;
let mut volumes = vec![0.0 as Float; ncells];
let nidx = |i: usize, j: usize, k: usize| -> usize { (k * nj + j) * ni + i };
let cidx = |i: usize, j: usize, k: usize| -> usize { (k * ncj + j) * nci + i };
for k in 0..nck {
for j in 0..ncj {
for i in 0..nci {
let p = |ii: usize, jj: usize, kk: usize| -> [Float; 3] {
let id = nidx(ii, jj, kk);
[block.x[id], block.y[id], block.z[id]]
};
let n0 = p(i, j, k);
let n1 = p(i + 1, j, k);
let n2 = p(i, j + 1, k);
let n3 = p(i + 1, j + 1, k);
let n4 = p(i, j, k + 1);
let n5 = p(i + 1, j, k + 1);
let n6 = p(i, j + 1, k + 1);
let n7 = p(i + 1, j + 1, k + 1);
let mut vol = 0.0 as Float;
let faces: [([Float; 3], [Float; 3], [Float; 3], [Float; 3]); 6] = [
(n0, n4, n6, n2),
(n1, n3, n7, n5),
(n0, n1, n5, n4),
(n2, n6, n7, n3),
(n0, n2, n3, n1),
(n4, n5, n7, n6),
];
for (a, b, c, d) in &faces {
let cx = a[0] + b[0] + c[0] + d[0];
let cy = a[1] + b[1] + c[1] + d[1];
let cz = a[2] + b[2] + c[2] + d[2];
let d1 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
let d2 = [d[0] - b[0], d[1] - b[1], d[2] - b[2]];
let sx = 0.5 * (d1[1] * d2[2] - d1[2] * d2[1]);
let sy = 0.5 * (d1[2] * d2[0] - d1[0] * d2[2]);
let sz = 0.5 * (d1[0] * d2[1] - d1[1] * d2[0]);
vol += cx * sx + cy * sy + cz * sz;
}
volumes[cidx(i, j, k)] = (vol / 12.0).abs();
}
}
}
volumes
}
pub fn compute_face_metrics(block: &Block) -> FaceMetrics {
let ni = block.imax;
let nj = block.jmax;
let nk = block.kmax;
let nidx = |i: usize, j: usize, k: usize| -> usize { (k * nj + j) * ni + i };
let n_ifaces = ni * (nj - 1) * (nk - 1);
let mut si_x = vec![0.0 as Float; n_ifaces];
let mut si_y = vec![0.0 as Float; n_ifaces];
let mut si_z = vec![0.0 as Float; n_ifaces];
let mut ci_x = vec![0.0 as Float; n_ifaces];
let mut ci_y = vec![0.0 as Float; n_ifaces];
let mut ci_z = vec![0.0 as Float; n_ifaces];
for k in 0..(nk - 1) {
for j in 0..(nj - 1) {
for i in 0..ni {
let p0 = nidx(i, j, k);
let p1 = nidx(i, j + 1, k);
let p2 = nidx(i, j + 1, k + 1);
let p3 = nidx(i, j, k + 1);
let d1x = block.x[p2] - block.x[p0];
let d1y = block.y[p2] - block.y[p0];
let d1z = block.z[p2] - block.z[p0];
let d2x = block.x[p3] - block.x[p1];
let d2y = block.y[p3] - block.y[p1];
let d2z = block.z[p3] - block.z[p1];
let fid = i + ni * j + ni * (nj - 1) * k;
si_x[fid] = 0.5 * (d1y * d2z - d1z * d2y);
si_y[fid] = 0.5 * (d1z * d2x - d1x * d2z);
si_z[fid] = 0.5 * (d1x * d2y - d1y * d2x);
ci_x[fid] = 0.25 * (block.x[p0] + block.x[p1] + block.x[p2] + block.x[p3]);
ci_y[fid] = 0.25 * (block.y[p0] + block.y[p1] + block.y[p2] + block.y[p3]);
ci_z[fid] = 0.25 * (block.z[p0] + block.z[p1] + block.z[p2] + block.z[p3]);
}
}
}
let n_jfaces = (ni - 1) * nj * (nk - 1);
let mut sj_x = vec![0.0 as Float; n_jfaces];
let mut sj_y = vec![0.0 as Float; n_jfaces];
let mut sj_z = vec![0.0 as Float; n_jfaces];
let mut cj_x = vec![0.0 as Float; n_jfaces];
let mut cj_y = vec![0.0 as Float; n_jfaces];
let mut cj_z = vec![0.0 as Float; n_jfaces];
for k in 0..(nk - 1) {
for j in 0..nj {
for i in 0..(ni - 1) {
let p0 = nidx(i, j, k);
let p1 = nidx(i, j, k + 1);
let p2 = nidx(i + 1, j, k + 1);
let p3 = nidx(i + 1, j, k);
let d1x = block.x[p2] - block.x[p0];
let d1y = block.y[p2] - block.y[p0];
let d1z = block.z[p2] - block.z[p0];
let d2x = block.x[p3] - block.x[p1];
let d2y = block.y[p3] - block.y[p1];
let d2z = block.z[p3] - block.z[p1];
let fid = i + (ni - 1) * j + (ni - 1) * nj * k;
sj_x[fid] = 0.5 * (d1y * d2z - d1z * d2y);
sj_y[fid] = 0.5 * (d1z * d2x - d1x * d2z);
sj_z[fid] = 0.5 * (d1x * d2y - d1y * d2x);
cj_x[fid] = 0.25 * (block.x[p0] + block.x[p1] + block.x[p2] + block.x[p3]);
cj_y[fid] = 0.25 * (block.y[p0] + block.y[p1] + block.y[p2] + block.y[p3]);
cj_z[fid] = 0.25 * (block.z[p0] + block.z[p1] + block.z[p2] + block.z[p3]);
}
}
}
let n_kfaces = (ni - 1) * (nj - 1) * nk;
let mut sk_x = vec![0.0 as Float; n_kfaces];
let mut sk_y = vec![0.0 as Float; n_kfaces];
let mut sk_z = vec![0.0 as Float; n_kfaces];
let mut ck_x = vec![0.0 as Float; n_kfaces];
let mut ck_y = vec![0.0 as Float; n_kfaces];
let mut ck_z = vec![0.0 as Float; n_kfaces];
for k in 0..nk {
for j in 0..(nj - 1) {
for i in 0..(ni - 1) {
let p0 = nidx(i, j, k);
let p1 = nidx(i + 1, j, k);
let p2 = nidx(i + 1, j + 1, k);
let p3 = nidx(i, j + 1, k);
let d1x = block.x[p2] - block.x[p0];
let d1y = block.y[p2] - block.y[p0];
let d1z = block.z[p2] - block.z[p0];
let d2x = block.x[p3] - block.x[p1];
let d2y = block.y[p3] - block.y[p1];
let d2z = block.z[p3] - block.z[p1];
let fid = i + (ni - 1) * j + (ni - 1) * (nj - 1) * k;
sk_x[fid] = 0.5 * (d1y * d2z - d1z * d2y);
sk_y[fid] = 0.5 * (d1z * d2x - d1x * d2z);
sk_z[fid] = 0.5 * (d1x * d2y - d1y * d2x);
ck_x[fid] = 0.25 * (block.x[p0] + block.x[p1] + block.x[p2] + block.x[p3]);
ck_y[fid] = 0.25 * (block.y[p0] + block.y[p1] + block.y[p2] + block.y[p3]);
ck_z[fid] = 0.25 * (block.z[p0] + block.z[p1] + block.z[p2] + block.z[p3]);
}
}
}
FaceMetrics {
si_x, si_y, si_z, ci_x, ci_y, ci_z,
sj_x, sj_y, sj_z, cj_x, cj_y, cj_z,
sk_x, sk_y, sk_z, ck_x, ck_y, ck_z,
}
}
pub fn compute_cell_centers(block: &Block) -> (Vec<Float>, Vec<Float>, Vec<Float>) {
let ni = block.imax;
let nj = block.jmax;
let nk = block.kmax;
let nci = ni - 1;
let ncj = nj - 1;
let nck = nk - 1;
let ncells = nci * ncj * nck;
let mut xc = vec![0.0 as Float; ncells];
let mut yc = vec![0.0 as Float; ncells];
let mut zc = vec![0.0 as Float; ncells];
let nidx = |i: usize, j: usize, k: usize| -> usize { (k * nj + j) * ni + i };
let cidx = |i: usize, j: usize, k: usize| -> usize { (k * ncj + j) * nci + i };
let eighth: Float = 0.125;
for k in 0..nck {
for j in 0..ncj {
for i in 0..nci {
let cid = cidx(i, j, k);
let n0 = nidx(i, j, k);
let n1 = nidx(i + 1, j, k);
let n2 = nidx(i, j + 1, k);
let n3 = nidx(i + 1, j + 1, k);
let n4 = nidx(i, j, k + 1);
let n5 = nidx(i + 1, j, k + 1);
let n6 = nidx(i, j + 1, k + 1);
let n7 = nidx(i + 1, j + 1, k + 1);
xc[cid] = eighth
* (block.x[n0] + block.x[n1] + block.x[n2] + block.x[n3]
+ block.x[n4] + block.x[n5] + block.x[n6] + block.x[n7]);
yc[cid] = eighth
* (block.y[n0] + block.y[n1] + block.y[n2] + block.y[n3]
+ block.y[n4] + block.y[n5] + block.y[n6] + block.y[n7]);
zc[cid] = eighth
* (block.z[n0] + block.z[n1] + block.z[n2] + block.z[n3]
+ block.z[n4] + block.z[n5] + block.z[n6] + block.z[n7]);
}
}
}
(xc, yc, zc)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::block::Block;
fn unit_cube_block(n: usize) -> Block {
let total = n * n * n;
let mut x = Vec::with_capacity(total);
let mut y = Vec::with_capacity(total);
let mut z = Vec::with_capacity(total);
let h = 1.0 / (n as f64 - 1.0);
for k in 0..n {
for j in 0..n {
for i in 0..n {
x.push(i as f64 * h);
y.push(j as f64 * h);
z.push(k as f64 * h);
}
}
}
Block::new(n, n, n, x, y, z)
}
#[test]
fn test_cell_volumes_unit_cube() {
let block = unit_cube_block(2);
let vols = compute_cell_volumes(&block);
assert_eq!(vols.len(), 1);
assert!((vols[0] - 1.0).abs() < 1e-12, "Expected volume 1.0, got {}", vols[0]);
}
#[test]
fn test_cell_volumes_subdivided() {
let block = unit_cube_block(3);
let vols = compute_cell_volumes(&block);
assert_eq!(vols.len(), 8);
for (idx, v) in vols.iter().enumerate() {
assert!(
(v - 0.125).abs() < 1e-12,
"Cell {} expected volume 0.125, got {}",
idx,
v
);
}
}
#[test]
fn test_cell_volumes_total() {
let block = unit_cube_block(4);
let vols = compute_cell_volumes(&block);
assert_eq!(vols.len(), 27);
let total: f64 = vols.iter().sum();
assert!((total - 1.0).abs() < 1e-12, "Total volume {}", total);
}
#[test]
fn test_cell_centers_unit_cube() {
let block = unit_cube_block(2);
let (xc, yc, zc) = compute_cell_centers(&block);
assert_eq!(xc.len(), 1);
assert!((xc[0] - 0.5).abs() < 1e-12);
assert!((yc[0] - 0.5).abs() < 1e-12);
assert!((zc[0] - 0.5).abs() < 1e-12);
}
#[test]
fn test_cell_centers_subdivided() {
let block = unit_cube_block(3);
let (xc, yc, zc) = compute_cell_centers(&block);
assert_eq!(xc.len(), 8);
assert!((xc[0] - 0.25).abs() < 1e-12);
assert!((yc[0] - 0.25).abs() < 1e-12);
assert!((zc[0] - 0.25).abs() < 1e-12);
assert!((xc[7] - 0.75).abs() < 1e-12);
assert!((yc[7] - 0.75).abs() < 1e-12);
assert!((zc[7] - 0.75).abs() < 1e-12);
}
#[test]
fn test_face_metrics_unit_cube() {
let block = unit_cube_block(2);
let fm = compute_face_metrics(&block);
assert_eq!(fm.si_x.len(), 2);
assert_eq!(fm.sj_x.len(), 2);
assert_eq!(fm.sk_x.len(), 2);
for idx in 0..2 {
assert!(
(fm.si_x[idx].abs() - 1.0).abs() < 1e-12,
"I-face {} si_x = {}",
idx,
fm.si_x[idx]
);
assert!(fm.si_y[idx].abs() < 1e-12);
assert!(fm.si_z[idx].abs() < 1e-12);
}
for idx in 0..2 {
assert!(fm.sj_x[idx].abs() < 1e-12);
assert!(
(fm.sj_y[idx].abs() - 1.0).abs() < 1e-12,
"J-face {} sj_y = {}",
idx,
fm.sj_y[idx]
);
assert!(fm.sj_z[idx].abs() < 1e-12);
}
for idx in 0..2 {
assert!(fm.sk_x[idx].abs() < 1e-12);
assert!(fm.sk_y[idx].abs() < 1e-12);
assert!(
(fm.sk_z[idx].abs() - 1.0).abs() < 1e-12,
"K-face {} sk_z = {}",
idx,
fm.sk_z[idx]
);
}
}
#[test]
fn test_face_metrics_count_subdivided() {
let ni = 4;
let nj = 3;
let nk = 5;
let total = ni * nj * nk;
let x: Vec<f64> = (0..total).map(|_| 0.0).collect();
let y = x.clone();
let z = x.clone();
let block = Block::new(ni, nj, nk, x, y, z);
let fm = compute_face_metrics(&block);
assert_eq!(fm.si_x.len(), ni * (nj - 1) * (nk - 1));
assert_eq!(fm.sj_x.len(), (ni - 1) * nj * (nk - 1));
assert_eq!(fm.sk_x.len(), (ni - 1) * (nj - 1) * nk);
}
}