use crate::physics::solenoid_stress::types::{DOF_PER_NODE, Real, Structural2dFormulation};
pub fn build_axisymmetric_b_matrix<
F: Real,
const NODES_PER_ELEMENT: usize,
const DOF_PER_ELEMENT: usize,
>(
n: &[F; NODES_PER_ELEMENT],
grad_phys: &[[F; 2]; NODES_PER_ELEMENT],
radius: F,
) -> Result<[[F; DOF_PER_ELEMENT]; 4], String> {
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
if radius <= F::epsilon() {
return Err(format!(
"quadrature radius {radius:?} is too close to zero for the axisymmetric hoop-strain term"
));
}
let mut b = [[F::zero(); DOF_PER_ELEMENT]; 4];
for i in 0..NODES_PER_ELEMENT {
let col_r = 2 * i;
let col_z = col_r + 1;
let dndr = grad_phys[i][0];
let dndz = grad_phys[i][1];
b[0][col_r] = dndr;
b[1][col_z] = dndz;
b[2][col_r] = n[i] / radius;
b[3][col_r] = dndz;
b[3][col_z] = dndr;
}
Ok(b)
}
pub fn build_plane_strain_b_matrix<
F: Real,
const NODES_PER_ELEMENT: usize,
const DOF_PER_ELEMENT: usize,
>(
grad_phys: &[[F; 2]; NODES_PER_ELEMENT],
) -> [[F; DOF_PER_ELEMENT]; 4] {
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
let mut b = [[F::zero(); DOF_PER_ELEMENT]; 4];
for (i, grad) in grad_phys.iter().enumerate() {
let col_x = 2 * i;
let col_y = col_x + 1;
let dndx = grad[0];
let dndy = grad[1];
b[0][col_x] = dndx;
b[1][col_y] = dndy;
b[3][col_x] = dndy;
b[3][col_y] = dndx;
}
b
}
pub fn build_b_matrix<F: Real, const NODES_PER_ELEMENT: usize, const DOF_PER_ELEMENT: usize>(
formulation: Structural2dFormulation<F>,
n: &[F; NODES_PER_ELEMENT],
grad_phys: &[[F; 2]; NODES_PER_ELEMENT],
point: [F; 2],
) -> Result<[[F; DOF_PER_ELEMENT]; 4], String> {
match formulation {
Structural2dFormulation::Axisymmetric => {
build_axisymmetric_b_matrix::<F, NODES_PER_ELEMENT, DOF_PER_ELEMENT>(
n, grad_phys, point[0],
)
}
Structural2dFormulation::PlaneStrain { .. } => Ok(build_plane_strain_b_matrix::<
F,
NODES_PER_ELEMENT,
DOF_PER_ELEMENT,
>(grad_phys)),
}
}
pub fn accumulate_stiffness<F: Real, const DOF_PER_ELEMENT: usize>(
ke: &mut [[F; DOF_PER_ELEMENT]; DOF_PER_ELEMENT],
d: &[[F; 4]; 4],
b: &[[F; DOF_PER_ELEMENT]; 4],
scale: F,
) {
let db = constitutive_times_b(d, b);
for row in 0..DOF_PER_ELEMENT {
for col in 0..DOF_PER_ELEMENT {
let mut value = F::zero();
for k in 0..4 {
value = value + b[k][row] * db[k][col];
}
ke[row][col] = ke[row][col] + scale * value;
}
}
}
pub fn constitutive_times_b<F: Real, const DOF_PER_ELEMENT: usize>(
d: &[[F; 4]; 4],
b: &[[F; DOF_PER_ELEMENT]; 4],
) -> [[F; DOF_PER_ELEMENT]; 4] {
let mut db = [[F::zero(); DOF_PER_ELEMENT]; 4];
for row in 0..4 {
for col in 0..DOF_PER_ELEMENT {
let mut value = F::zero();
for k in 0..4 {
value = value + d[row][k] * b[k][col];
}
db[row][col] = value;
}
}
db
}
pub fn constitutive_times_strain<F: Real>(d: &[[F; 4]; 4], strain: &[F; 4]) -> [F; 4] {
let mut out = [F::zero(); 4];
for row in 0..4 {
let mut value = F::zero();
for k in 0..4 {
value = value + d[row][k] * strain[k];
}
out[row] = value;
}
out
}
pub fn accumulate_b_transpose_vector<F: Real, const DOF_PER_ELEMENT: usize>(
fe: &mut [F; DOF_PER_ELEMENT],
b: &[[F; DOF_PER_ELEMENT]; 4],
sigma: &[F; 4],
scale: F,
) {
for dof in 0..DOF_PER_ELEMENT {
let mut value = F::zero();
for component in 0..4 {
value = value + b[component][dof] * sigma[component];
}
fe[dof] = fe[dof] + scale * value;
}
}