use crate::mesh::Scalar;
pub fn map_point<F: Scalar, const NODES_PER_ELEMENT: usize>(
coords: &[[F; 2]; NODES_PER_ELEMENT],
n: &[F; NODES_PER_ELEMENT],
) -> [F; 2] {
let mut point = [F::zero(); 2];
for i in 0..NODES_PER_ELEMENT {
point[0] = point[0] + n[i] * coords[i][0];
point[1] = point[1] + n[i] * coords[i][1];
}
point
}
pub fn jacobian<F: Scalar, const NODES_PER_ELEMENT: usize>(
coords: &[[F; 2]; NODES_PER_ELEMENT],
grad: &[[F; 2]; NODES_PER_ELEMENT],
) -> [[F; 2]; 2] {
let mut jac = [[F::zero(); 2]; 2];
for i in 0..NODES_PER_ELEMENT {
jac[0][0] = jac[0][0] + coords[i][0] * grad[i][0];
jac[0][1] = jac[0][1] + coords[i][0] * grad[i][1];
jac[1][0] = jac[1][0] + coords[i][1] * grad[i][0];
jac[1][1] = jac[1][1] + coords[i][1] * grad[i][1];
}
jac
}
pub fn det_j<F: Scalar>(jac: &[[F; 2]; 2]) -> F {
jac[0][0] * jac[1][1] - jac[0][1] * jac[1][0]
}
pub fn inv_j<F: Scalar>(jac: &[[F; 2]; 2]) -> Result<[[F; 2]; 2], String> {
let det = det_j(jac);
if det <= F::zero() {
return Err(format!(
"encountered non-positive element Jacobian determinant {det:?}; quadrilateral elements must be non-degenerate and use counter-clockwise corner ordering in the (r, z) plane"
));
}
let inv_det = F::one() / det;
Ok([
[jac[1][1] * inv_det, -jac[0][1] * inv_det],
[-jac[1][0] * inv_det, jac[0][0] * inv_det],
])
}
pub fn grad_phys<F: Scalar, const NODES_PER_ELEMENT: usize>(
grad_reference: &[[F; 2]; NODES_PER_ELEMENT],
inv_jac: &[[F; 2]; 2],
) -> [[F; 2]; NODES_PER_ELEMENT] {
let mut out = [[F::zero(); 2]; NODES_PER_ELEMENT];
for i in 0..NODES_PER_ELEMENT {
let dxi = grad_reference[i][0];
let deta = grad_reference[i][1];
out[i][0] = inv_jac[0][0] * dxi + inv_jac[1][0] * deta;
out[i][1] = inv_jac[0][1] * dxi + inv_jac[1][1] * deta;
}
out
}
pub fn face_tangent<F: Scalar>(jac: &[[F; 2]; 2], ds_reference: [F; 2]) -> [F; 2] {
[
jac[0][0] * ds_reference[0] + jac[0][1] * ds_reference[1],
jac[1][0] * ds_reference[0] + jac[1][1] * ds_reference[1],
]
}