use super::CommonArgs;
use crate::StrError;
use russell_lab::Vector;
pub fn vec_01_ns<F>(a: &mut Vector, args: &mut CommonArgs, mut fn_s: F) -> Result<(), StrError>
where
F: FnMut(usize, &Vector) -> Result<f64, StrError>,
{
let nnode = args.pad.interp.dim();
let ii0 = args.ii0;
if a.dim() < ii0 + nnode {
return Err("a.len() must be ≥ ii0 + nnode");
}
if args.clear {
a.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_jacobian(iota)?;
let nn = &args.pad.interp;
let s = fn_s(p, nn)?;
let coef = 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 {
a[ii0 + m] += nn[m] * coef;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::integ::testing::aux;
use crate::integ::{self, AnalyticalTet4, AnalyticalTri3, CommonArgs, Gauss};
use crate::recovery;
use russell_lab::{vec_approx_eq, Vector};
#[test]
fn capture_some_errors() {
let mut pad = aux::gen_pad_lin2(1.0);
let mut a = Vector::new(2);
let nn = Vector::new(0);
let f = |_: usize, _: &Vector| Ok(0.0);
assert_eq!(f(0, &nn).unwrap(), 0.0);
let gauss = Gauss::new(pad.kind);
let mut args = CommonArgs::new(&mut pad, &gauss);
args.ii0 = 1;
assert_eq!(
integ::vec_01_ns(&mut a, &mut args, f).err(),
Some("a.len() must be ≥ ii0 + nnode")
);
}
#[test]
fn vec_01_ns_works_lin2_linear() {
const L: f64 = 6.0;
let mut pad = aux::gen_pad_lin2(L);
let cf = L / 6.0;
let (xa, xb) = (pad.xxt.get(0, 0), pad.xxt.get(0, 1));
let a_correct = &[cf * (2.0 * xa + xb), cf * (xa + 2.0 * xb)];
let class = pad.kind.class();
let tolerances = [1e-15, 1e-14, 1e-14, 1e-14];
let selection: Vec<_> = [2, 3, 4, 5]
.iter()
.map(|n| Gauss::new_sized(class, *n).unwrap())
.collect();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
let x_ips = recovery::get_points_coords(&mut args.pad, ips).unwrap();
integ::vec_01_ns(&mut a, &mut args, |p, _| Ok(x_ips[p][0])).unwrap();
vec_approx_eq(&a, a_correct, tol);
});
}
#[test]
fn vec_01_ns_works_tri3_constant() {
let mut pad = aux::gen_pad_tri3();
let ana = AnalyticalTri3::new(&pad);
const CS: f64 = 3.0;
let a_correct = ana.vec_01_ns(CS, false);
let class = pad.kind.class();
let tolerances = [1e-14, 1e-14, 1e-14, 1e-13, 1e-14];
let selection: Vec<_> = [1, 3, 4, 12, 16]
.iter()
.map(|n| Gauss::new_sized(class, *n).unwrap())
.collect();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
integ::vec_01_ns(&mut a, &mut args, |_, _| Ok(CS)).unwrap();
vec_approx_eq(&a, &a_correct, tol);
});
}
#[test]
fn vec_01_ns_works_tet4_constant() {
let mut pad = aux::gen_pad_tet4();
const CS: f64 = 120.0;
let ana = AnalyticalTet4::new(&pad);
let a_correct = ana.vec_01_ns(CS);
let class = pad.kind.class();
let tolerances = [1e-13, 1e-13];
let selection: Vec<_> = [1, 4].iter().map(|n| Gauss::new_sized(class, *n).unwrap()).collect();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
integ::vec_01_ns(&mut a, &mut args, |_, _| Ok(CS)).unwrap();
vec_approx_eq(&a, &a_correct, tol);
});
}
#[test]
fn vec_01_ns_works_tet4_linear() {
let mut pad = aux::gen_pad_tet4();
let ana = AnalyticalTet4::new(&pad);
let a_correct = ana.vec_01_ns_linear_along_z(&pad);
let class = pad.kind.class();
let tolerances = [0.56, 1e-15, 1e-14, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15];
let selection: Vec<_> = [1, 4, 5, 8, 14, 15, 24]
.iter()
.map(|n| Gauss::new_sized(class, *n).unwrap())
.collect();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
selection.iter().zip(tolerances).for_each(|(ips, tol)| {
let mut args = CommonArgs::new(&mut pad, ips);
let x_ips = recovery::get_points_coords(&mut args.pad, ips).unwrap();
integ::vec_01_ns(&mut a, &mut args, |p, _| Ok(x_ips[p][2])).unwrap();
vec_approx_eq(&a, &a_correct, tol);
});
}
}