use crate::physics::solenoid_stress::types::{DOF_PER_NODE, Structural2dFormulation};
pub fn build_axisymmetric_b_matrix<const NODES_PER_ELEMENT: usize, const DOF_PER_ELEMENT: usize>(
n: &[f64; NODES_PER_ELEMENT],
grad_phys: &[[f64; 2]; NODES_PER_ELEMENT],
radius: f64,
) -> Result<[[f64; DOF_PER_ELEMENT]; 4], String> {
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
if radius <= f64::EPSILON {
return Err(format!(
"quadrature radius {radius:?} is too close to zero for the axisymmetric hoop-strain term"
));
}
let mut b = [[0.0; 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<const NODES_PER_ELEMENT: usize, const DOF_PER_ELEMENT: usize>(
grad_phys: &[[f64; 2]; NODES_PER_ELEMENT],
) -> [[f64; DOF_PER_ELEMENT]; 4] {
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
let mut b = [[0.0; 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<const NODES_PER_ELEMENT: usize, const DOF_PER_ELEMENT: usize>(
formulation: Structural2dFormulation,
n: &[f64; NODES_PER_ELEMENT],
grad_phys: &[[f64; 2]; NODES_PER_ELEMENT],
point: [f64; 2],
) -> Result<[[f64; DOF_PER_ELEMENT]; 4], String> {
match formulation {
Structural2dFormulation::Axisymmetric => build_axisymmetric_b_matrix::<
NODES_PER_ELEMENT,
DOF_PER_ELEMENT,
>(n, grad_phys, point[0]),
Structural2dFormulation::PlaneStrain { .. } => Ok(build_plane_strain_b_matrix::<
NODES_PER_ELEMENT,
DOF_PER_ELEMENT,
>(grad_phys)),
}
}
pub fn accumulate_stiffness<const DOF_PER_ELEMENT: usize>(
ke: &mut [[f64; DOF_PER_ELEMENT]; DOF_PER_ELEMENT],
d: &[[f64; 4]; 4],
b: &[[f64; DOF_PER_ELEMENT]; 4],
scale: f64,
) {
let db = constitutive_times_b(d, b);
for row in 0..DOF_PER_ELEMENT {
for col in 0..DOF_PER_ELEMENT {
let mut value = 0.0;
for k in 0..4 {
value += b[k][row] * db[k][col];
}
ke[row][col] += scale * value;
}
}
}
pub fn constitutive_times_b<const DOF_PER_ELEMENT: usize>(
d: &[[f64; 4]; 4],
b: &[[f64; DOF_PER_ELEMENT]; 4],
) -> [[f64; DOF_PER_ELEMENT]; 4] {
let mut db = [[0.0; DOF_PER_ELEMENT]; 4];
for row in 0..4 {
for col in 0..DOF_PER_ELEMENT {
let mut value = 0.0;
for k in 0..4 {
value += d[row][k] * b[k][col];
}
db[row][col] = value;
}
}
db
}
pub fn constitutive_times_strain(d: &[[f64; 4]; 4], strain: &[f64; 4]) -> [f64; 4] {
let mut out = [0.0; 4];
for row in 0..4 {
let mut value = 0.0;
for k in 0..4 {
value += d[row][k] * strain[k];
}
out[row] = value;
}
out
}
pub fn accumulate_b_transpose_vector<const DOF_PER_ELEMENT: usize>(
fe: &mut [f64; DOF_PER_ELEMENT],
b: &[[f64; DOF_PER_ELEMENT]; 4],
sigma: &[f64; 4],
scale: f64,
) {
for dof in 0..DOF_PER_ELEMENT {
let mut value = 0.0;
for component in 0..4 {
value += b[component][dof] * sigma[component];
}
fe[dof] += scale * value;
}
}