use crate::{
fem::{
ElementModel, ElementModelError, Elements, Model, NodalCoordinates,
block::{
element::solid::elastic::internal_variables::InternalVariables,
finalize_node_neighbors, solver_from_neighbors,
},
solid::{NodalForcesSolid, NodalStiffnessesSolid},
},
math::{
Scalar, Tensor, TensorVector, Vector,
optimize::{
EqualityConstraint, FirstOrderRootFinding, FirstOrderRootFindingIncremental,
NewtonRaphson, OptimizationError, SolveStrategy,
},
},
};
use std::cell::{Ref, RefCell};
pub type InternalVariablesField<const G: usize, V> = TensorVector<InternalVariables<G, V>>;
pub trait ElasticIVElements<const G: usize, V, const D: usize>
where
Self: Elements,
V: Tensor,
{
fn internal_variables_initial(&self) -> InternalVariablesField<G, V>;
fn internal_variables_increment(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_decrement: &NodalCoordinates<D>,
step: Scalar,
) -> Result<InternalVariablesField<G, V>, ElementModelError>;
fn internal_variables_root(
&self,
local_solver: &NewtonRaphson,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<InternalVariablesField<G, V>, ElementModelError>;
fn nodal_forces_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_forces: &mut NodalForcesSolid<D>,
) -> Result<(), ElementModelError>;
fn nodal_forces(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<NodalForcesSolid<D>, ElementModelError> {
let mut nodal_forces = NodalForcesSolid::zero(nodal_coordinates.len());
self.nodal_forces_into(nodal_coordinates, internal_variables, &mut nodal_forces)?;
Ok(nodal_forces)
}
fn nodal_forces_eliminated_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_forces: &mut NodalForcesSolid<D>,
) -> Result<(), ElementModelError>;
fn nodal_forces_eliminated(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<NodalForcesSolid<D>, ElementModelError> {
let mut nodal_forces = NodalForcesSolid::zero(nodal_coordinates.len());
self.nodal_forces_eliminated_into(
nodal_coordinates,
internal_variables,
&mut nodal_forces,
)?;
Ok(nodal_forces)
}
fn nodal_stiffnesses_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
) -> Result<(), ElementModelError>;
fn nodal_stiffnesses(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<NodalStiffnessesSolid<D>, ElementModelError> {
let mut nodal_stiffnesses = NodalStiffnessesSolid::zero(nodal_coordinates.len());
self.nodal_stiffnesses_into(
nodal_coordinates,
internal_variables,
&mut nodal_stiffnesses,
)?;
Ok(nodal_stiffnesses)
}
}
impl<B, const G: usize, V, const D: usize> ElasticIVElements<G, V, D> for Model<B, D>
where
B: ElasticIVElements<G, V, D>,
V: Tensor,
{
fn internal_variables_initial(&self) -> InternalVariablesField<G, V> {
self.blocks.internal_variables_initial()
}
fn internal_variables_increment(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_decrement: &NodalCoordinates<D>,
step: Scalar,
) -> Result<InternalVariablesField<G, V>, ElementModelError> {
self.blocks.internal_variables_increment(
nodal_coordinates,
internal_variables,
nodal_decrement,
step,
)
}
fn internal_variables_root(
&self,
local_solver: &NewtonRaphson,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
) -> Result<InternalVariablesField<G, V>, ElementModelError> {
self.blocks
.internal_variables_root(local_solver, nodal_coordinates, internal_variables)
}
fn nodal_forces_eliminated_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_forces: &mut NodalForcesSolid<D>,
) -> Result<(), ElementModelError> {
self.blocks.nodal_forces_eliminated_into(
nodal_coordinates,
internal_variables,
nodal_forces,
)
}
fn nodal_forces_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_forces: &mut NodalForcesSolid<D>,
) -> Result<(), ElementModelError> {
self.blocks
.nodal_forces_into(nodal_coordinates, internal_variables, nodal_forces)
}
fn nodal_stiffnesses_into(
&self,
nodal_coordinates: &NodalCoordinates<D>,
internal_variables: &InternalVariablesField<G, V>,
nodal_stiffnesses: &mut NodalStiffnessesSolid<D>,
) -> Result<(), ElementModelError> {
self.blocks
.nodal_stiffnesses_into(nodal_coordinates, internal_variables, nodal_stiffnesses)
}
}
pub struct SolvedInternalVariables<'a, B, const G: usize, V, const D: usize>
where
V: Tensor,
{
initial: InternalVariablesField<G, V>,
local_solver: &'a NewtonRaphson,
model: &'a Model<B, D>,
solved: RefCell<Option<(NodalCoordinates<D>, InternalVariablesField<G, V>)>>,
}
impl<'a, B, const G: usize, V, const D: usize> SolvedInternalVariables<'a, B, G, V, D>
where
B: ElasticIVElements<G, V, D>,
V: Tensor,
{
pub fn new(
model: &'a Model<B, D>,
local_solver: &'a NewtonRaphson,
initial: InternalVariablesField<G, V>,
) -> Self {
Self {
initial,
local_solver,
model,
solved: RefCell::new(None),
}
}
pub fn at(
&self,
nodal_coordinates: &NodalCoordinates<D>,
) -> Result<InternalVariablesField<G, V>, ElementModelError> {
if let Some((ref at, ref variables)) = *self.solved.borrow()
&& at == nodal_coordinates
{
return Ok(variables.clone());
}
let warm = match *self.solved.borrow() {
Some((_, ref variables)) => variables.clone(),
None => self.initial.clone(),
};
let variables =
self.model
.internal_variables_root(self.local_solver, nodal_coordinates, &warm)?;
*self.solved.borrow_mut() = Some((nodal_coordinates.clone(), variables.clone()));
Ok(variables)
}
}
pub struct CarriedInternalVariables<'a, B, const G: usize, V, const D: usize>
where
V: Tensor,
{
committed: RefCell<InternalVariablesField<G, V>>,
model: &'a Model<B, D>,
stepped: RefCell<InternalVariablesField<G, V>>,
}
impl<'a, B, const G: usize, V, const D: usize> CarriedInternalVariables<'a, B, G, V, D>
where
B: ElasticIVElements<G, V, D>,
V: Tensor,
{
pub fn new(model: &'a Model<B, D>, initial: InternalVariablesField<G, V>) -> Self {
Self {
committed: RefCell::new(initial.clone()),
model,
stepped: RefCell::new(initial),
}
}
pub fn stepped(&self) -> Ref<'_, InternalVariablesField<G, V>> {
self.stepped.borrow()
}
pub fn step(
&self,
nodal_coordinates: &NodalCoordinates<D>,
nodal_decrement: &Vector,
step: Scalar,
commit: bool,
) -> Result<(), ElementModelError> {
let stepped = self.model.internal_variables_increment(
nodal_coordinates,
&self.committed.borrow(),
&NodalCoordinates::from(nodal_decrement.clone()),
step,
)?;
if commit {
*self.committed.borrow_mut() = stepped.clone()
}
*self.stepped.borrow_mut() = stepped;
Ok(())
}
}
pub trait FirstOrderRootIV<const G: usize, V, const D: usize>
where
V: Tensor,
{
fn root(
&self,
equality_constraint: EqualityConstraint,
solver: impl FirstOrderRootFinding<
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
> + FirstOrderRootFindingIncremental<
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
>,
strategy: SolveStrategy,
) -> Result<NodalCoordinates<D>, OptimizationError>;
}
impl<B, const G: usize, V, const D: usize> FirstOrderRootIV<G, V, D> for Model<B, D>
where
B: ElasticIVElements<G, V, D>,
V: Tensor,
{
fn root(
&self,
equality_constraint: EqualityConstraint,
solver: impl FirstOrderRootFinding<
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
> + FirstOrderRootFindingIncremental<
NodalForcesSolid<D>,
NodalStiffnessesSolid<D>,
NodalCoordinates<D>,
>,
strategy: SolveStrategy,
) -> Result<NodalCoordinates<D>, OptimizationError> {
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, false);
let initial = self.internal_variables_initial();
match strategy {
SolveStrategy::Condensed(ref local_solver) => {
let solved = SolvedInternalVariables::new(self, local_solver, initial);
solver.root(
|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.root_incremental(
|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."
),
}
}
}