use super::{FnDeriv, FnInterp, GeoKind};
use crate::StrError;
use russell_lab::{Matrix, Vector};
#[derive(Clone)]
pub struct Scratchpad {
pub kind: GeoKind,
pub interp: Vector,
pub deriv: Matrix,
pub jacobian: Matrix,
pub inv_jacobian: Matrix,
pub gradient: Matrix,
pub xxt: Matrix,
pub ok_xxt: bool,
pub fn_interp: FnInterp,
pub fn_deriv: FnDeriv,
}
impl Scratchpad {
pub fn new(space_ndim: usize, kind: GeoKind) -> Result<Self, StrError> {
if space_ndim < 2 || space_ndim > 3 {
return Err("space_ndim must be 2 or 3");
}
let geo_ndim = kind.ndim();
if geo_ndim > space_ndim {
return Err("geo_ndim cannot be greater than space_ndim");
}
let nnode = kind.nnode();
let (fn_interp, fn_deriv) = kind.functions();
Ok(Scratchpad {
kind,
interp: Vector::new(nnode),
deriv: Matrix::new(nnode, geo_ndim),
jacobian: Matrix::new(space_ndim, geo_ndim),
inv_jacobian: if geo_ndim == space_ndim {
Matrix::new(space_ndim, space_ndim)
} else {
Matrix::new(0, 0)
},
gradient: if geo_ndim == space_ndim {
Matrix::new(nnode, space_ndim)
} else {
Matrix::new(0, 0)
},
xxt: Matrix::new(space_ndim, nnode),
ok_xxt: false,
fn_interp,
fn_deriv,
})
}
pub fn get_space_ndim(&self) -> usize {
self.xxt.nrow()
}
pub fn set_xx(&mut self, m: usize, j: usize, value: f64) {
self.ok_xxt = false;
self.xxt.set(j, m, value);
let (space_ndim, nnode) = self.xxt.dims();
if m == nnode - 1 && j == space_ndim - 1 {
self.ok_xxt = true;
}
}
#[inline]
pub fn calc_interp(&mut self, ksi: &[f64]) {
(self.fn_interp)(&mut self.interp, ksi);
}
}
#[cfg(test)]
mod tests {
use super::Scratchpad;
use crate::shapes::{GeoClass, GeoKind};
#[test]
fn derive_works() {
let pad = Scratchpad::new(2, GeoKind::Qua4).unwrap();
let pad_clone = pad.clone();
assert_eq!(pad_clone.kind, pad.kind);
}
#[test]
fn new_fails_on_wrong_input() {
assert_eq!(
Scratchpad::new(1, GeoKind::Lin2).err(),
Some("space_ndim must be 2 or 3")
);
assert_eq!(
Scratchpad::new(2, GeoKind::Hex8).err(),
Some("geo_ndim cannot be greater than space_ndim")
);
}
#[test]
fn new_works() {
let nnode = [
2, 3, 4, 5, 3, 6, 10, 15, 4, 8, 9, 12, 16, 17, 4, 10, 20, 8, 20, 32, ];
let geo_ndim = [
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, ];
for i in 0..GeoKind::VALUES.len() {
let kind = GeoKind::VALUES[i];
let space_ndim = usize::max(2, kind.ndim());
let pad = Scratchpad::new(space_ndim, kind).unwrap();
assert_eq!(pad.get_space_ndim(), space_ndim);
assert_eq!(pad.kind, kind);
assert_eq!(pad.interp.dim(), nnode[i]);
assert_eq!(pad.deriv.dims(), (nnode[i], geo_ndim[i]));
assert_eq!(pad.jacobian.dims(), (space_ndim, geo_ndim[i]));
if kind.class() == GeoClass::Lin {
assert_eq!(pad.inv_jacobian.dims(), (0, 0));
assert_eq!(pad.gradient.dims(), (0, 0));
} else {
assert_eq!(pad.inv_jacobian.dims(), (space_ndim, space_ndim));
assert_eq!(pad.gradient.dims(), (nnode[i], space_ndim));
}
assert_eq!(pad.xxt.dims(), (space_ndim, nnode[i]));
assert_eq!(pad.ok_xxt, false);
}
}
#[test]
fn set_xx_works() {
let mut pad = Scratchpad::new(2, GeoKind::Lin2).unwrap();
assert_eq!(pad.ok_xxt, false);
pad.set_xx(0, 0, 1.0);
assert_eq!(pad.ok_xxt, false);
pad.set_xx(0, 1, 2.0);
assert_eq!(pad.ok_xxt, false);
pad.set_xx(1, 0, 3.0);
assert_eq!(pad.ok_xxt, false);
pad.set_xx(1, 1, 4.0);
assert_eq!(pad.ok_xxt, true);
pad.set_xx(0, 0, 1.0); assert_eq!(pad.ok_xxt, false);
}
}