use crate::{
fem::{
ElementModel, ElementModelError, Elements, Model, NodalCoordinates,
block::{finalize_node_neighbors, solver_from_neighbors},
solid::{
NodalForcesSolid, NodalStiffnessesSolid,
elastic::internal_variables::{
CarriedInternalVariables, ElasticIVElements, InternalVariablesField,
SolvedInternalVariables,
},
},
},
math::{
Quantity, Scalar, Tensor, Vector,
optimize::{
EqualityConstraint, OptimizationError, SecondOrderOptimization,
SecondOrderOptimizationIncremental, SolveStrategy,
},
},
units::Energy,
};
pub trait HyperelasticIVElements<const G: usize, V, const D: usize>
where
Self: ElasticIVElements<G, V, D>,
V: Tensor,
{
fn helmholtz_free_energy(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<Quantity<Energy>, ElementModelError>;
}
impl<B, const G: usize, V, const D: usize> HyperelasticIVElements<G, V, D> for Model<B, D>
where
B: HyperelasticIVElements<G, V, D>,
V: Tensor,
{
fn helmholtz_free_energy(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<Quantity<Energy>, ElementModelError> {
self.blocks
.helmholtz_free_energy(nodal_coordinates, internal_variables)
}
}
pub trait SecondOrderMinimizeIV<const G: usize, V, const D: usize>
where
V: Tensor,
{
fn minimize(
&self,
equality_constraint: EqualityConstraint,
solver: impl SecondOrderOptimization<
Quantity<Energy>,
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
> + SecondOrderOptimizationIncremental<
Quantity<Energy>,
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
>,
strategy: SolveStrategy,
) -> Result<NodalCoordinates<D>, OptimizationError>;
}
impl<B, const G: usize, V, const D: usize> SecondOrderMinimizeIV<G, V, D> for Model<B, D>
where
B: HyperelasticIVElements<G, V, D>,
V: Tensor,
{
fn minimize(
&self,
equality_constraint: EqualityConstraint,
solver: impl SecondOrderOptimization<
Quantity<Energy>,
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
> + SecondOrderOptimizationIncremental<
Quantity<Energy>,
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
>,
strategy: SolveStrategy,
) -> Result<NodalCoordinates<D>, OptimizationError> {
let initial = self.internal_variables_initial();
let mut neighbors = vec![Vec::new(); self.coordinates().len()];
self.node_neighbors(&mut neighbors);
finalize_node_neighbors(&mut neighbors);
let sparse = solver_from_neighbors(&neighbors, &equality_constraint, D, true);
match strategy {
SolveStrategy::Condensed(ref local_solver) => {
let solved = SolvedInternalVariables::new(self, local_solver, initial);
solver.minimize(
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self.helmholtz_free_energy(
nodal_coordinates,
&solved.at(nodal_coordinates)?,
)?)
},
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self.nodal_forces(nodal_coordinates, &solved.at(nodal_coordinates)?)?)
},
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self
.nodal_stiffnesses(nodal_coordinates, &solved.at(nodal_coordinates)?)?)
},
self.coordinates().clone().into(),
equality_constraint,
Some(sparse),
)
}
SolveStrategy::Monolithic { elimination: true } => {
let carried = CarriedInternalVariables::new(self, initial);
solver.minimize_incremental(
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self.helmholtz_free_energy(nodal_coordinates, &carried.stepped())?)
},
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self.nodal_forces_eliminated(nodal_coordinates, &carried.stepped())?)
},
|nodal_coordinates: &NodalCoordinates<D>| {
Ok(self.nodal_stiffnesses(nodal_coordinates, &carried.stepped())?)
},
|nodal_coordinates: &NodalCoordinates<D>,
decrement: &Vector,
step: Scalar,
commit: bool| {
Ok(carried.step(nodal_coordinates, decrement, step, commit)?)
},
self.coordinates().clone().into(),
equality_constraint,
Some(sparse),
)
}
SolveStrategy::Monolithic { elimination: false } => unimplemented!(
"The internal variables must be unknowns of the solver to be solved with it."
),
}
}
}