use super::CommonArgs;
use crate::StrError;
use russell_lab::{Matrix, Vector};
pub fn mat_01_nsn<F>(kk: &mut Matrix, args: &mut CommonArgs, mut fn_s: F) -> Result<(), StrError>
where
F: FnMut(usize, &Vector, &Matrix) -> Result<f64, StrError>,
{
let nnode = args.pad.interp.dim();
let (nrow_kk, ncol_kk) = kk.dims();
let (ii0, jj0) = (args.ii0, args.jj0);
if nrow_kk < ii0 + nnode {
return Err("nrow(K) must be ≥ ii0 + nnode");
}
if ncol_kk < jj0 + nnode {
return Err("ncol(K) must be ≥ jj0 + nnode");
}
if args.clear {
kk.fill(0.0);
}
for p in 0..args.gauss.npoint() {
let iota = args.gauss.coords(p);
let weight = args.gauss.weight(p);
(args.pad.fn_interp)(&mut args.pad.interp, iota); let det_jac = args.pad.calc_gradient(iota)?;
let nn = &args.pad.interp;
let bb = &args.pad.gradient;
let s = fn_s(p, 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);
}
s * det_jac * weight * args.alpha * r
} else {
s * det_jac * weight * args.alpha
};
for m in 0..nnode {
for n in 0..nnode {
kk.add(ii0 + m, jj0 + n, nn[m] * c * nn[n]);
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::integ::testing::aux;
use crate::integ::{self, AnalyticalQua4, AnalyticalQua8, AnalyticalTet4, AnalyticalTri3, CommonArgs, Gauss};
use russell_lab::{mat_approx_eq, Matrix, Vector};
#[test]
fn capture_some_errors() {
let mut pad = aux::gen_pad_lin2(1.0);
let mut kk = Matrix::new(2, 2);
let nn = Vector::new(0);
let bb = Matrix::new(0, 0);
let f = |_p: usize, _nn: &Vector, _bb: &Matrix| Ok(0.0);
assert_eq!(f(0, &nn, &bb).unwrap(), 0.0);
let gauss = Gauss::new(pad.kind);
let mut args = CommonArgs::new(&mut pad, &gauss);
args.ii0 = 1;
assert_eq!(
integ::mat_01_nsn(&mut kk, &mut args, f).err(),
Some("nrow(K) must be ≥ ii0 + nnode")
);
args.ii0 = 0;
args.jj0 = 1;
assert_eq!(
integ::mat_01_nsn(&mut kk, &mut args, f).err(),
Some("ncol(K) must be ≥ jj0 + nnode")
);
args.jj0 = 0;
assert_eq!(
integ::mat_01_nsn(&mut kk, &mut args, f).err(),
Some("calc_gradient requires that geo_ndim = space_ndim")
);
let mut pad = aux::gen_pad_tri3();
let mut kk = Matrix::new(3, 3);
let gauss = Gauss::new(pad.kind);
let mut args = CommonArgs::new(&mut pad, &gauss);
assert_eq!(
integ::mat_01_nsn(&mut kk, &mut args, |_, _, _| Err("stop")).err(),
Some("stop")
);
}
#[test]
fn tri3_works() {
let mut pad = aux::gen_pad_tri3();
let mut kk = Matrix::new(3, 3);
let s = 12.0;
let ana = AnalyticalTri3::new(&pad);
let kk_correct = ana.mat_01_nsn(s, 1.0);
let class = pad.kind.class();
let tolerances = [8.34, 1e-14, 1e-14, 1e-14, 1e-12, 1e-13]; let selection: Vec<_> = [1, 3, 6, 7, 12, 16]
.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_01_nsn(&mut kk, &mut args, |_, _, _| Ok(s)).unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
#[test]
fn qua4_works() {
let (a, b) = (2.0, 1.5);
let mut pad = aux::gen_pad_qua4(2.0, 1.0, a, b);
let mut kk = Matrix::new(4, 4);
let s = 12.0;
let ana = AnalyticalQua4::new(a, b);
let kk_correct = ana.mat_01_nsn(s, 1.0);
let class = pad.kind.class();
let tolerances = [7.01, 1e-14, 1e-14, 1e-14]; let selection: Vec<_> = [1, 4, 9, 16]
.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_01_nsn(&mut kk, &mut args, |_, _, _| Ok(s)).unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
#[test]
fn qua8_works() {
let (a, b) = (2.0, 1.5);
let mut pad = aux::gen_pad_qua8(2.0, 1.0, a, b);
let mut kk = Matrix::new(8, 8);
let s = 3.0;
let ana = AnalyticalQua8::new(a, b);
let kk_correct = ana.mat_01_nsn(s, 1.0);
let class = pad.kind.class();
let tolerances = [1e-14, 1e-14];
let selection: Vec<_> = [9, 16].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_01_nsn(&mut kk, &mut args, |_, _, _| Ok(s)).unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
#[test]
fn tet4_works() {
let mut pad = aux::gen_pad_tet4();
let mut kk = Matrix::new(4, 4);
let s = 3.0;
let ana = AnalyticalTet4::new(&pad);
let kk_correct = ana.mat_01_nsn(s);
let class = pad.kind.class();
let tolerances = [1e-15];
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_01_nsn(&mut kk, &mut args, |_, _, _| Ok(s)).unwrap();
mat_approx_eq(&kk, &kk_correct, tol);
});
}
}