use nalgebra::DMatrix;
use sprs::{CsMat, TriMat};
use cartan_core::Manifold;
use crate::mesh::{FlatMesh, Mesh};
pub struct ExteriorDerivative {
pub d: Vec<CsMat<f64>>,
}
impl ExteriorDerivative {
pub fn from_mesh(mesh: &FlatMesh) -> Self {
Self::from_mesh_sparse_generic(mesh)
}
pub fn from_mesh_sparse<M: Manifold>(mesh: &Mesh<M, 3, 2>) -> Self {
Self::from_mesh_sparse_generic(mesh)
}
pub fn from_mesh_sparse_generic<M: Manifold, const K: usize, const B: usize>(
mesh: &Mesh<M, K, B>,
) -> Self {
let nv = mesh.n_vertices();
let nb = mesh.n_boundaries();
let ns = mesh.n_simplices();
let mut tri0 = TriMat::new((nb, nv));
for (b, boundary) in mesh.boundaries.iter().enumerate() {
for (k, &v) in boundary.iter().enumerate() {
let sign = if k % 2 == 0 { -1.0 } else { 1.0 };
tri0.add_triplet(b, v, sign);
}
}
let d0 = tri0.to_csc();
let mut tri1 = TriMat::new((ns, nb));
for (s, (local_b, local_s)) in mesh
.simplex_boundary_ids
.iter()
.zip(mesh.boundary_signs.iter())
.enumerate()
{
for k in 0..K {
tri1.add_triplet(s, local_b[k], local_s[k]);
}
}
let d1 = tri1.to_csc();
Self { d: vec![d0, d1] }
}
#[deprecated(since = "0.2.0", note = "use from_mesh_sparse or from_mesh instead")]
pub fn from_mesh_generic_dense<M: Manifold>(
mesh: &Mesh<M, 3, 2>,
) -> (DMatrix<f64>, DMatrix<f64>) {
let nv = mesh.n_vertices();
let ne = mesh.n_boundaries();
let nt = mesh.n_simplices();
let mut d0 = DMatrix::<f64>::zeros(ne, nv);
for (e, &[i, j]) in mesh.boundaries.iter().enumerate() {
d0[(e, i)] = -1.0;
d0[(e, j)] = 1.0;
}
let mut d1 = DMatrix::<f64>::zeros(nt, ne);
for (t, (local_e, local_s)) in mesh
.simplex_boundary_ids
.iter()
.zip(mesh.boundary_signs.iter())
.enumerate()
{
for k in 0..3 {
d1[(t, local_e[k])] = local_s[k];
}
}
(d0, d1)
}
pub fn d0(&self) -> &CsMat<f64> {
&self.d[0]
}
pub fn d1(&self) -> &CsMat<f64> {
&self.d[1]
}
pub fn degree(&self) -> usize {
self.d.len()
}
pub fn check_exactness(&self) -> f64 {
let mut max_err = 0.0f64;
for k in 0..self.d.len().saturating_sub(1) {
let prod = &self.d[k + 1] * &self.d[k];
for &val in prod.data() {
max_err = max_err.max(val.abs());
}
}
max_err
}
}