use crate::mesh::{QuadMeshView2d, QuadratureRule};
use crate::physics::solenoid_stress::family::QuadElementFamily;
use crate::physics::solenoid_stress::geometry::{FaceSample, validate_structural_2d_mesh};
use crate::physics::solenoid_stress::types::{
DOF_PER_NODE, PressureLoad, Real, Structural2dFormulation, local_dofs,
};
use super::{SparseOperator, scatter_local_vector};
fn pressure_face_kernel<F: Real, const NODES_PER_ELEMENT: usize, const DOF_PER_ELEMENT: usize>(
samples: &[FaceSample<F, NODES_PER_ELEMENT>],
formulation: Structural2dFormulation<F>,
) -> Result<[F; DOF_PER_ELEMENT], String> {
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
let mut local = [F::zero(); DOF_PER_ELEMENT];
for sample in samples {
let normal_area = [sample.tangent[1], -sample.tangent[0]];
let scale = -formulation.face_scale(sample.point, F::one(), sample.weight)?;
for local_node in 0..NODES_PER_ELEMENT {
local[2 * local_node] =
local[2 * local_node] + scale * sample.n[local_node] * normal_area[0];
local[2 * local_node + 1] =
local[2 * local_node + 1] + scale * sample.n[local_node] * normal_area[1];
}
}
Ok(local)
}
pub(crate) fn pressure_operator_for_family<
F: Real,
Family,
const NODES_PER_ELEMENT: usize,
const DOF_PER_ELEMENT: usize,
>(
mesh: QuadMeshView2d<'_, F, NODES_PER_ELEMENT>,
pressure_faces: &[PressureLoad],
formulation: Structural2dFormulation<F>,
quadrature: QuadratureRule,
) -> Result<SparseOperator<F>, String>
where
Family: QuadElementFamily<NODES_PER_ELEMENT>,
{
const {
assert!(DOF_PER_ELEMENT == DOF_PER_NODE * NODES_PER_ELEMENT);
}
validate_structural_2d_mesh(mesh, formulation)?;
let ndof = mesh.num_nodes() * 2;
let ncol = pressure_faces.len();
let mut rows = Vec::with_capacity(pressure_faces.len() * DOF_PER_ELEMENT);
let mut cols = Vec::with_capacity(pressure_faces.len() * DOF_PER_ELEMENT);
let mut vals = Vec::with_capacity(pressure_faces.len() * DOF_PER_ELEMENT);
for (load_index, load) in pressure_faces.iter().enumerate() {
if load.element >= mesh.num_elements() {
return Err(format!(
"pressure load references element {}, but mesh has only {} elements",
load.element,
mesh.num_elements()
));
}
let coords = mesh.element_coords(load.element)?;
let nodes = mesh.element_nodes(load.element)?;
let samples = Family::face_samples::<F>(&coords, load.local_face, quadrature)?;
let local =
pressure_face_kernel::<F, NODES_PER_ELEMENT, DOF_PER_ELEMENT>(&samples, formulation)?;
let global_rows = local_dofs::<NODES_PER_ELEMENT, DOF_PER_ELEMENT>(&nodes);
scatter_local_vector(
&mut rows,
&mut cols,
&mut vals,
&global_rows,
load_index,
&local,
);
}
Ok(SparseOperator {
rows,
cols,
vals,
nrow: ndof,
ncol,
})
}