use crate::constitutive::cohesive::elastic::StiffnessCohesive;
use crate::{
constitutive::cohesive::elastic::Elastic,
fem::block::element::{
ElementNodalCoordinates, FiniteElement, FiniteElementError,
cohesive::{CohesiveElement, CohesiveFiniteElement},
solid::{ElementNodalForcesSolid, ElementNodalStiffnessesSolid},
surface::SurfaceFiniteElement,
},
math::{Rank2, Tensor, TensorList, TensorTuple},
mechanics::TractionList,
};
pub type StiffnessCohesiveList<const N: usize> = TensorList<StiffnessCohesive, N>;
pub trait ElasticCohesiveElement<C, const G: usize, const N: usize, const P: usize>
where
C: Elastic,
Self: CohesiveFiniteElement<G, N, P>,
{
fn nodal_forces(
&self,
constitutive_model: &C,
nodal_coordinates: &ElementNodalCoordinates<N>,
) -> Result<ElementNodalForcesSolid<N>, FiniteElementError>;
fn nodal_stiffnesses(
&self,
constitutive_model: &C,
nodal_coordinates: &ElementNodalCoordinates<N>,
) -> Result<ElementNodalStiffnessesSolid<N>, FiniteElementError>;
}
impl<C, const G: usize, const N: usize, const O: usize, const P: usize>
ElasticCohesiveElement<C, G, N, P> for CohesiveElement<G, N, O>
where
C: Elastic,
Self: CohesiveFiniteElement<G, N, P>,
{
fn nodal_forces(
&self,
constitutive_model: &C,
nodal_coordinates: &ElementNodalCoordinates<N>,
) -> Result<ElementNodalForcesSolid<N>, FiniteElementError> {
let normals = Self::normals(&Self::nodal_mid_surface(nodal_coordinates));
let tractions = Self::separations(nodal_coordinates)
.into_iter()
.zip(normals)
.map(|(separation, normal)| constitutive_model.traction(separation, normal))
.collect::<Result<TractionList<G>, _>>()
.map_err(|error| FiniteElementError::upstream(error, self))?;
Ok(tractions
.into_iter()
.zip(
Self::signed_shape_functions()
.into_iter()
.zip(self.integration_weights()),
)
.map(|(traction, (signed_shape_functions, integration_weight))| {
signed_shape_functions
.iter()
.map(|signed_shape_function| {
&traction * (signed_shape_function * integration_weight)
})
.collect()
})
.sum())
}
fn nodal_stiffnesses(
&self,
constitutive_model: &C,
nodal_coordinates: &ElementNodalCoordinates<N>,
) -> Result<ElementNodalStiffnessesSolid<N>, FiniteElementError> {
let nodal_mid_surface = Self::nodal_mid_surface(nodal_coordinates);
let normals = Self::normals(&nodal_mid_surface);
let normal_gradients = Self::normal_gradients_full(&nodal_mid_surface);
let stiffnesses = Self::separations(nodal_coordinates)
.into_iter()
.zip(normals)
.map(|(separation, normal)| constitutive_model.stiffness(separation, normal))
.collect::<Result<StiffnessCohesiveList<G>, _>>()
.map_err(|error| FiniteElementError::upstream(error, self))?;
Ok(stiffnesses
.into_iter()
.zip(
Self::signed_shape_functions()
.into_iter()
.zip(normal_gradients.into_iter().zip(self.integration_weights())),
)
.map(
|(stiffness, (signed_shape_functions, (normal_gradient, integration_weight)))| {
let TensorTuple(stiffness_u, stiffness_n) = stiffness;
signed_shape_functions
.iter()
.map(|signed_shape_function_a| {
signed_shape_functions
.iter()
.zip(normal_gradient.iter())
.map(|(signed_shape_function_b, normal_gradient_b)| {
(&stiffness_u * signed_shape_function_b
+ (&stiffness_n * normal_gradient_b.transpose()) * 0.5)
* (signed_shape_function_a * integration_weight)
})
.collect()
})
.collect()
},
)
.sum())
}
}