#[path = "util/mod.rs"]
mod util;
use {
derham::{cochain::Cochain, project::derham_map, section::CoordFieldExt},
formoniq::{
linalg::quadratic_form_sparse, problems::heat::solve_heat, whitney_complex::WhitneyComplex,
},
simplicial::{
gen::cartesian::CartesianGrid,
linalg::{CsrMatrix, Vector},
},
util::{BoundaryCondition, BoxEigenform},
};
use std::f64::consts::PI;
fn main() {
tracing_subscriber::fmt::init();
const NBOXES: usize = 8;
const NSTEPS: usize = 40;
const FINAL_TIME: f64 = 1.0;
println!("Heat u_t = -Δu on [0,π]^n, relative (Dirichlet) BC — Radau IIA.");
println!("Energy E = ½‖u‖²_L² dissipates monotonically.\n");
println!(
"| {:>3} | {:>5} | {:>10} | {:>10} | {:>10} | {:>7} |",
"dim", "grade", "E(0)", "E(½T)", "E(T)", "E(T)/E0",
);
for dim in 1..=3 {
let grid = CartesianGrid::new_unit_scaled(dim, NBOXES, PI);
let (topology, coords) = grid.triangulate();
let metric = coords.to_edge_lengths_sq(&topology);
let whitney = WhitneyComplex::new(&topology, &metric);
for grade in 0..=dim {
let mass = CsrMatrix::from(&whitney.mass(grade));
let form = BoxEigenform::new(dim, grade, BoundaryCondition::Relative);
let initial = derham_map(
&form.solution().pullback_on(&topology, &coords),
&topology,
3,
);
let source = Cochain::new(grade, Vector::zeros(whitney.ndofs(grade)));
let relative = whitney.relative();
let dt = FINAL_TIME / NSTEPS as f64;
let solution = solve_heat(&relative, grade, NSTEPS, dt, &initial, &source, 1.0);
let energy = |c: &Cochain| 0.5 * quadratic_form_sparse(&mass, c.coeffs());
let e0 = energy(&solution[0]);
let e_half = energy(&solution[NSTEPS / 2]);
let e_final = energy(solution.last().unwrap());
println!(
"| {dim:>3} | {grade:>5} | {e0:>10.3e} | {e_half:>10.3e} | {e_final:>10.3e} | {:>7.4} |",
e_final / e0,
);
}
}
}