use super::CommonArgs;
use crate::StrError;
use russell_lab::Vector;
pub fn vec_01_ns_bry<F>(a: &mut Vector, args: &mut CommonArgs, mut fn_s: F) -> Result<(), StrError>
where
F: FnMut(usize, &Vector, &Vector) -> Result<f64, StrError>,
{
let (space_ndim, nnode) = args.pad.xxt.dims();
let geo_ndim = args.pad.deriv.dims().1;
if space_ndim == 2 && geo_ndim != 1 {
return Err("in 2D, geometry ndim must be equal to 1 (a line)");
}
if space_ndim == 3 && geo_ndim != 2 {
return Err("in 3D, geometry ndim must be equal to 2 (a surface)");
}
let ii0 = args.ii0;
if a.dim() < ii0 + nnode {
return Err("a.len() must be ≥ ii0 + nnode");
}
let mut un = Vector::new(space_ndim);
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 mag_n = args.pad.calc_normal_vector(&mut un, iota)?;
let nn = &args.pad.interp;
let s = fn_s(p, &un, 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 * mag_n * weight * args.alpha * r
} else {
s * mag_n * 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, CommonArgs, Gauss};
use crate::mesh::{GeoKind, Mesh};
use crate::recovery;
use crate::shapes::Scratchpad;
use russell_lab::{vec_approx_eq, vec_inner, 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 un = Vector::new(0);
let f = |_: usize, _: &Vector, _: &Vector| Ok(0.0);
assert_eq!(f(0, &un, &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_bry(&mut a, &mut args, f).err(),
Some("a.len() must be ≥ ii0 + nnode")
);
}
#[test]
fn vec_01_ns_bry_works_lin2_ignoring_un() {
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 ips = Gauss::new_sized(class, 2).unwrap();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
let mut args = CommonArgs::new(&mut pad, &ips);
let x_ips = recovery::get_points_coords(&mut args.pad, &ips).unwrap();
integ::vec_01_ns_bry(&mut a, &mut args, |p, _, _| Ok(x_ips[p][0])).unwrap();
vec_approx_eq(&a, a_correct, 1e-15);
}
#[test]
fn vec_01_ns_bry_works_lin2_constant_w() {
let mut pad = Scratchpad::new(2, GeoKind::Lin2).unwrap();
let (xa, ya) = (1.0, 2.0);
let (xb, yb) = (4.0, 6.0);
pad.set_xx(0, 0, xa);
pad.set_xx(0, 1, ya);
pad.set_xx(1, 0, xb);
pad.set_xx(1, 1, yb);
let (w0, w1) = (3.0, 5.0);
let dx = xb - xa;
let dy = yb - ya;
let a_correct = &[(w1 * dx - w0 * dy) / 2.0, (w1 * dx - w0 * dy) / 2.0];
let class = pad.kind.class();
let ips = Gauss::new_sized(class, 2).unwrap();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
let mut args = CommonArgs::new(&mut pad, &ips);
integ::vec_01_ns_bry(&mut a, &mut args, |_, un, _| {
let s = w0 * un[0] + w1 * un[1];
Ok(s)
})
.unwrap();
vec_approx_eq(&a, a_correct, 1e-15);
}
#[test]
fn vec_01_ns_bry_works_lin2_linear_w() {
let mut pad = Scratchpad::new(2, GeoKind::Lin2).unwrap();
let (xa, ya) = (1.0, 2.0);
let (xb, yb) = (4.0, 6.0);
pad.set_xx(0, 0, xa);
pad.set_xx(0, 1, ya);
pad.set_xx(1, 0, xb);
pad.set_xx(1, 1, yb);
let dx = xb - xa;
let dy = yb - ya;
let aa = dx - dy;
let bb = dx * dx + dy * dy;
let cc = dx + dy;
let mm = 3.0 * aa * ya - 3.0 * xa * cc;
let a_correct = &[(mm - bb) / 6.0, (mm - 2.0 * bb) / 6.0];
let class = pad.kind.class();
let ips = Gauss::new_sized(class, 2).unwrap();
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
let mut args = CommonArgs::new(&mut pad, &ips);
let x_ips = recovery::get_points_coords(&mut args.pad, &ips).unwrap();
integ::vec_01_ns_bry(&mut a, &mut args, |p, un, _| {
let x = x_ips[p][0];
let y = x_ips[p][1];
let s = (x + y) * un[0] + (y - x) * un[1];
Ok(s)
})
.unwrap();
vec_approx_eq(&a, a_correct, 1e-14);
}
#[test]
fn vec_01_ns_bry_works_3d_plane() {
let mesh = Mesh::read("data/blender/single-plane.blend.msh").unwrap();
let un_correct = Vector::from(&[8.0 / 9.0, -(1.0 / 9.0), 4.0 / 9.0]);
let mut pad = mesh.get_pad(0);
let class = pad.kind.class();
let ips = Gauss::new_sized(class, 4).unwrap();
let w = Vector::from(&[1.0, 2.0, 3.0]);
let s = vec_inner(&w, &un_correct);
let mut a = Vector::filled(pad.kind.nnode(), aux::NOISE);
let mut args = CommonArgs::new(&mut pad, &ips);
integ::vec_01_ns_bry(&mut a, &mut args, |_, un, _| {
vec_approx_eq(&un, &un_correct, 1e-7); Ok(vec_inner(&w, un))
})
.unwrap();
vec_approx_eq(&a, &[s, s, s, s], 1e-7);
}
}