use cartan_core::bundle::{CovLaplacian, EdgeTransport3D};
pub fn cartesian_3d_connection(
nx: usize,
ny: usize,
nz: usize,
dx: f64,
) -> (EdgeTransport3D, CovLaplacian) {
let n = nx * ny * nz;
let idx = |i: usize, j: usize, k: usize| -> usize {
((i % nx) * ny + (j % ny)) * nz + (k % nz)
};
let identity_3x3: [f64; 9] = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
let mut edges = Vec::with_capacity(3 * n);
let mut transports = Vec::with_capacity(3 * n);
for i in 0..nx {
for j in 0..ny {
for k in 0..nz {
let v0 = idx(i, j, k);
edges.push([v0, idx(i + 1, j, k)]);
transports.push(identity_3x3);
edges.push([v0, idx(i, j + 1, k)]);
transports.push(identity_3x3);
edges.push([v0, idx(i, j, k + 1)]);
transports.push(identity_3x3);
}
}
}
let transport = EdgeTransport3D { edges: edges.clone(), transports };
let cot_weights = vec![1.0_f64; edges.len()];
let dual_areas = vec![dx * dx; n];
let cov_lap = CovLaplacian::new(n, &edges, &cot_weights, &dual_areas);
(transport, cov_lap)
}
#[cfg(test)]
mod tests {
use super::*;
use cartan_core::fiber::{NematicFiber3D, Section, VecSection};
#[test]
fn cartesian_3d_uniform_laplacian_zero() {
let (transport, cov_lap) = cartesian_3d_connection(4, 4, 4, 1.0);
let n = 4 * 4 * 4;
let field = VecSection::<NematicFiber3D>::from_vec(
vec![[0.1, 0.2, 0.3, 0.15, 0.25]; n]
);
let result = cov_lap.apply::<NematicFiber3D, 3, _>(&field, &transport);
for v in 0..n {
for c in 0..5 {
assert!(
result.at(v)[c].abs() < 1e-12,
"lap[{v}][{c}] = {}",
result.at(v)[c]
);
}
}
}
#[test]
fn cartesian_3d_laplacian_positive_at_peak() {
let (transport, cov_lap) = cartesian_3d_connection(4, 4, 4, 1.0);
let n = 4 * 4 * 4;
let mut data = vec![[0.0_f64; 5]; n];
let peak_idx = (2 * 4 + 2) * 4 + 2;
data[peak_idx] = [1.0, 0.0, 0.0, 0.0, 0.0];
let field = VecSection::<NematicFiber3D>::from_vec(data);
let result = cov_lap.apply::<NematicFiber3D, 3, _>(&field, &transport);
assert!(result.at(peak_idx)[0] > 0.0);
}
#[test]
fn cartesian_3d_matches_finite_difference() {
let nx = 8;
let dx = 0.5;
let (transport, cov_lap) = cartesian_3d_connection(nx, nx, nx, dx);
let n = nx * nx * nx;
let l = nx as f64 * dx;
let mut data = vec![[0.0_f64; 5]; n];
for i in 0..nx {
for j in 0..nx {
for k in 0..nx {
let idx = (i * nx + j) * nx + k;
let x = i as f64 * dx;
data[idx][0] = (2.0 * std::f64::consts::PI * x / l).sin();
}
}
}
let field = VecSection::<NematicFiber3D>::from_vec(data.clone());
let result = cov_lap.apply::<NematicFiber3D, 3, _>(&field, &transport);
let peak_i = nx / 4;
let peak_idx = (peak_i * nx + 0) * nx + 0;
let cov_val = result.at(peak_idx)[0];
let field_val = data[peak_idx][0];
assert!(
cov_val > 0.0,
"CovLap should be positive at sinusoidal peak, got {cov_val}"
);
assert!(cov_val.is_finite());
}
}