use crate::{
assemble::assemble_galvec,
operators::SourceElVec,
whitney_complex::{BoundaryWhitneyComplex, RelativeWhitneyComplex},
};
use {
crate::linalg::faer::FaerCholesky,
derham::{cochain::Cochain, section::Section},
exterior::{Covariant, ExteriorGrade},
simplicial::{
atlas::SimplexQuadRule,
linalg::{CsrMatrix, Vector},
},
};
pub struct LiftedSystem {
system: CsrMatrix,
inclusion: CsrMatrix,
lift: Vector,
cholesky: FaerCholesky,
grade: usize,
}
impl LiftedSystem {
pub fn new(
relative: &RelativeWhitneyComplex,
boundary: &BoundaryWhitneyComplex,
system: CsrMatrix,
boundary_values: &Cochain,
) -> Self {
let grade = boundary_values.grade();
let lift = boundary.extend_cochain(boundary_values).into_coeffs();
let inclusion = relative.inclusion(grade);
let system_relative = inclusion.transpose() * &system * &inclusion;
let cholesky = FaerCholesky::new(system_relative);
Self {
system,
inclusion,
lift,
cholesky,
grade,
}
}
pub fn solve(&self, rhs: &Vector) -> Cochain {
let rhs_relative = self.inclusion.transpose() * (rhs - &self.system * &self.lift);
let solution_relative = self.cholesky.solve(&rhs_relative);
Cochain::new(self.grade, &self.inclusion * solution_relative + &self.lift)
}
}
pub fn solve_with_essential_bc(
relative: &RelativeWhitneyComplex,
boundary: &BoundaryWhitneyComplex,
system: CsrMatrix,
rhs: &Vector,
boundary_values: &Cochain,
) -> Cochain {
LiftedSystem::new(relative, boundary, system, boundary_values).solve(rhs)
}
pub fn neumann_load(
boundary: &BoundaryWhitneyComplex,
data: &(impl Section<Covariant> + Sync),
qr: Option<SimplexQuadRule>,
) -> Vector {
assert_eq!(data.dim(), boundary.topology().dim());
let elvec = SourceElVec::new(data, qr);
let load = assemble_galvec(boundary.topology(), boundary.geometry(), elvec);
boundary.trace(data.grade()).transpose() * load
}
pub fn boundary_mass(boundary: &BoundaryWhitneyComplex, grade: ExteriorGrade) -> CsrMatrix {
let trace = boundary.trace(grade);
let mass = CsrMatrix::from(&boundary.whitney_complex().mass(grade));
trace.transpose() * mass * trace
}