#[derive(Debug, Clone)]
pub struct LduMatrix {
pub n_cells: usize,
pub n_internal_faces: usize,
pub diag: Vec<f64>,
pub lower: Vec<f64>,
pub upper: Vec<f64>,
pub owner: Vec<usize>,
pub neighbour: Vec<usize>,
}
impl LduMatrix {
pub fn new(n_cells: usize, owner: Vec<usize>, neighbour: Vec<usize>) -> Self {
let n_int = owner.len();
debug_assert_eq!(neighbour.len(), n_int);
Self {
n_cells,
n_internal_faces: n_int,
diag: vec![0.0; n_cells],
lower: vec![0.0; n_int],
upper: vec![0.0; n_int],
owner,
neighbour,
}
}
pub fn multiply(&self, x: &[f64]) -> Vec<f64> {
debug_assert_eq!(x.len(), self.n_cells);
let mut y = vec![0.0_f64; self.n_cells];
for c in 0..self.n_cells {
y[c] += self.diag[c] * x[c];
}
for f in 0..self.n_internal_faces {
let o = self.owner[f];
let n = self.neighbour[f];
y[o] += self.upper[f] * x[n];
y[n] += self.lower[f] * x[o];
}
y
}
pub fn residual(&self, x: &[f64], b: &[f64]) -> Vec<f64> {
let ax = self.multiply(x);
b.iter().zip(ax.iter()).map(|(bi, ai)| bi - ai).collect()
}
pub fn normalised_residual(&self, x: &[f64], b: &[f64]) -> f64 {
let ax = self.multiply(x);
let r_norm: f64 = b
.iter()
.zip(ax.iter())
.map(|(bi, ai)| (bi - ai).abs())
.sum();
let ax_norm: f64 =
ax.iter().map(|a| a.abs()).sum::<f64>() + b.iter().map(|bi| bi.abs()).sum::<f64>();
let denom = ax_norm * 0.5;
if denom < f64::EPSILON {
r_norm
} else {
r_norm / denom
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
fn identity_2x2() -> LduMatrix {
let mut m = LduMatrix::new(2, vec![], vec![]);
m.diag[0] = 1.0;
m.diag[1] = 1.0;
m
}
fn tridiag_3x3() -> LduMatrix {
let mut m = LduMatrix::new(3, vec![0, 1], vec![1, 2]);
m.diag = vec![2.0, 2.0, 2.0];
m.upper = vec![-1.0, -1.0];
m.lower = vec![-1.0, -1.0];
m
}
#[test]
fn identity_multiply() {
let m = identity_2x2();
let x = vec![3.0, 7.0];
let y = m.multiply(&x);
assert_relative_eq!(y[0], 3.0);
assert_relative_eq!(y[1], 7.0);
}
#[test]
fn tridiag_multiply() {
let m = tridiag_3x3();
let x = vec![1.0, 1.0, 1.0];
let y = m.multiply(&x);
assert_relative_eq!(y[0], 2.0 * 1.0 + (-1.0) * 1.0, epsilon = 1e-12);
assert_relative_eq!(
y[1],
(-1.0) * 1.0 + 2.0 * 1.0 + (-1.0) * 1.0,
epsilon = 1e-12
);
assert_relative_eq!(y[2], (-1.0) * 1.0 + 2.0 * 1.0, epsilon = 1e-12);
}
#[test]
fn residual_exact_solution() {
let m = identity_2x2();
let b = vec![3.0, 7.0];
let x = vec![3.0, 7.0]; let r = m.residual(&x, &b);
assert_relative_eq!(r[0], 0.0, epsilon = 1e-15);
assert_relative_eq!(r[1], 0.0, epsilon = 1e-15);
}
}