use super::CommonArgs;
use crate::shapes::Scratchpad;
use crate::StrError;
use russell_lab::math::SQRT_2;
use russell_lab::{Matrix, Vector};
use russell_tensor::{Mandel, Tensor2};
pub fn mat_05_btn<F>(
kk: &mut Matrix,
pad_b: &mut Scratchpad,
args: &mut CommonArgs,
mut fn_tt: F,
) -> Result<(), StrError>
where
F: FnMut(&mut Tensor2, usize, &Matrix, &Vector, &Matrix) -> Result<(), StrError>,
{
let nnode_b = pad_b.interp.dim();
let (space_ndim, nnode) = args.pad.xxt.dims();
let (nrow_kk, ncol_kk) = kk.dims();
let (ii0, jj0) = (args.ii0, args.jj0);
if nrow_kk < ii0 + nnode_b {
return Err("nrow(K) must be ≥ ii0 + pad_b.nnode");
}
if ncol_kk < jj0 + nnode * space_ndim {
return Err("ncol(K) must be ≥ jj0 + pad.nnode ⋅ space_ndim");
}
let mut tt = Tensor2::new(Mandel::new(2 * space_ndim));
if args.clear {
kk.fill(0.0);
}
let s = SQRT_2;
for p in 0..args.gauss.npoint() {
let iota = args.gauss.coords(p);
let weight = args.gauss.weight(p);
pad_b.calc_gradient(iota)?; (args.pad.fn_interp)(&mut args.pad.interp, iota); let det_jac = args.pad.calc_gradient(iota)?;
let ggb = &pad_b.gradient;
let nn = &args.pad.interp;
let bb = &args.pad.gradient;
fn_tt(&mut tt, p, ggb, nn, bb)?;
let c = if args.axisymmetric {
let mut r = 0.0; for m in 0..nnode {
r += nn[m] * args.pad.xxt.get(0, m);
}
det_jac * weight * args.alpha * r
} else {
det_jac * weight * args.alpha
};
let t = tt.vector();
if space_ndim == 2 {
for m in 0..nnode_b {
for n in 0..nnode {
kk.add(
ii0 + m,
jj0 + 0 + n * 2,
c * (ggb.get(m, 0) * t[0] + ggb.get(m, 1) * t[3] / s) * nn[n],
);
kk.add(
ii0 + m,
jj0 + 1 + n * 2,
c * (ggb.get(m, 0) * t[3] / s + ggb.get(m, 1) * t[1]) * nn[n],
);
}
}
} else {
for m in 0..nnode_b {
for n in 0..nnode {
kk.add(
ii0 + m,
jj0 + 0 + n * 3,
c * (ggb.get(m, 0) * t[0] + ggb.get(m, 1) * t[3] / s + ggb.get(m, 2) * t[5] / s) * nn[n],
);
kk.add(
ii0 + m,
jj0 + 1 + n * 3,
c * (ggb.get(m, 0) * t[3] / s + ggb.get(m, 1) * t[1] + ggb.get(m, 2) * t[4] / s) * nn[n],
);
kk.add(
ii0 + m,
jj0 + 2 + n * 3,
c * (ggb.get(m, 0) * t[5] / s + ggb.get(m, 1) * t[4] / s + ggb.get(m, 2) * t[2]) * nn[n],
);
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::integ::testing::aux;
use crate::integ::{self, AnalyticalQua8, AnalyticalTet4, CommonArgs, Gauss};
use russell_lab::{mat_approx_eq, Matrix, Vector};
use russell_tensor::{Mandel, Tensor2};
#[test]
fn capture_some_errors() {
let (a, b) = (2.0, 3.0);
let mut pad_b = aux::gen_pad_qua4(0.0, 0.0, a, b);
let mut pad = aux::gen_pad_qua8(0.0, 0.0, a, b);
let mut kk = Matrix::new(4, 8 * 2);
let mut tt = Tensor2::new(Mandel::Symmetric2D);
let ggb = Matrix::new(0, 0);
let nn = Vector::new(0);
let bb = Matrix::new(0, 0);
let f = |_tt: &mut Tensor2, _p: usize, _ggb: &Matrix, _nn: &Vector, _bb: &Matrix| Ok(());
f(&mut tt, 0, &ggb, &nn, &bb).unwrap();
let gauss = Gauss::new(pad.kind);
let mut args = CommonArgs::new(&mut pad, &gauss);
args.ii0 = 1;
assert_eq!(
integ::mat_05_btn(&mut kk, &mut pad_b, &mut args, f).err(),
Some("nrow(K) must be ≥ ii0 + pad_b.nnode")
);
args.ii0 = 0;
args.jj0 = 1;
assert_eq!(
integ::mat_05_btn(&mut kk, &mut pad_b, &mut args, f).err(),
Some("ncol(K) must be ≥ jj0 + pad.nnode ⋅ space_ndim")
);
}
#[test]
fn qua4_qua8_works() {
let (a, b) = (2.0, 3.0);
let mut pad_b = aux::gen_pad_qua4(0.0, 0.0, a, b);
let mut pad = aux::gen_pad_qua8(0.0, 0.0, a, b);
let mut kk = Matrix::new(4, 8 * 2);
let ana = AnalyticalQua8::new(a, b);
#[rustfmt::skip]
let tt = Tensor2::from_matrix(&[
[2.0, 5.0, 0.0],
[5.0, 3.0, 0.0],
[0.0, 0.0, 4.0]],
Mandel::Symmetric2D).unwrap();
let kk_correct = ana.mat_05_btn(&tt);
let class = pad.kind.class();
let tolerances = [1e-14, 1e-14];
let selection: Vec<_> = [4, 9].iter().map(|n| Gauss::new_sized(class, *n).unwrap()).collect();
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
integ::mat_05_btn(&mut kk, &mut pad_b, &mut args, |ten, _, _, _, _| {
ten.set_tensor(1.0, &tt);
Ok(())
})
.unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
#[test]
fn tet4_tet4_works() {
let mut pad_b = aux::gen_pad_tet4();
let mut pad = pad_b.clone();
let mut kk = Matrix::new(4, 4 * 3);
let ana = AnalyticalTet4::new(&pad);
#[rustfmt::skip]
let tt = Tensor2::from_matrix(&[
[1.0, 4.0, 6.0],
[4.0, 2.0, 5.0],
[6.0, 5.0, 3.0]],
Mandel::Symmetric).unwrap();
let kk_correct = ana.mat_05_btn(&tt);
let class = pad.kind.class();
let tolerances = [1e-14];
let selection: Vec<_> = [4].iter().map(|n| Gauss::new_sized(class, *n).unwrap()).collect();
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
integ::mat_05_btn(&mut kk, &mut pad_b, &mut args, |ten, _, _, _, _| {
ten.set_tensor(1.0, &tt);
Ok(())
})
.unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
}