conspire 0.7.7

The Rust interface to conspire.
Documentation
#[cfg(test)]
pub mod test;

use crate::{
    constitutive::{ConstitutiveError, thermal::conduction::ThermalConduction},
    fem::block::element::{
        Element, FiniteElement, FiniteElementError,
        thermal::{ElementNodalTemperatures, ThermalFiniteElement},
    },
    math::{ContractWith, Quantity, Tensor, TensorList},
    mechanics::{HeatFluxTangents, HeatFluxes},
    units::{Power, PowerPerTemperature, PowerTemperature},
};

pub type ElementNodalForcesThermal<const D: usize> = TensorList<Quantity<Power>, D>;
pub type ElementNodalStiffnessesThermal<const D: usize> =
    TensorList<ElementNodalForcesThermalPerTemperature<D>, D>;
pub type ElementNodalForcesThermalPerTemperature<const D: usize> =
    TensorList<Quantity<PowerPerTemperature>, D>;

pub trait ThermalConductionFiniteElement<
    C,
    const G: usize,
    const M: usize,
    const N: usize,
    const P: usize,
> where
    C: ThermalConduction,
    Self: ThermalFiniteElement<G, M, N, P>,
{
    fn potential(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<Quantity<PowerTemperature>, FiniteElementError>;
    fn nodal_forces(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError>;
    fn nodal_stiffnesses(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError>;
}

impl<C, const G: usize, const M: usize, const N: usize, const O: usize, const P: usize>
    ThermalConductionFiniteElement<C, G, M, N, P> for Element<3, G, N, O>
where
    C: ThermalConduction,
    Self: ThermalFiniteElement<G, M, N, P>,
{
    fn potential(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<Quantity<PowerTemperature>, FiniteElementError> {
        self.temperature_gradients(nodal_temperatures)
            .iter()
            .zip(self.integration_weights())
            .map(|(temperature_gradient, integration_weight)| {
                Ok::<_, ConstitutiveError>(
                    constitutive_model.potential(temperature_gradient)? * integration_weight,
                )
            })
            .sum::<Result<_, ConstitutiveError>>()
            .map_err(|error| FiniteElementError::upstream(error, self))
    }
    fn nodal_forces(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError> {
        let heat_fluxes = self
            .temperature_gradients(nodal_temperatures)
            .iter()
            .map(|temperature_gradient| constitutive_model.heat_flux(temperature_gradient))
            .collect::<Result<HeatFluxes<G>, _>>()
            .map_err(|error| FiniteElementError::upstream(error, self))?;
        Ok(heat_fluxes
            .iter()
            .zip(
                self.gradient_vectors()
                    .iter()
                    .zip(self.integration_weights()),
            )
            .map(|(heat_flux, (gradient_vectors, integration_weight))| {
                gradient_vectors
                    .iter()
                    .map(|gradient_vector| {
                        -heat_flux.contract_with(gradient_vector) * integration_weight
                    })
                    .collect()
            })
            .sum())
    }
    fn nodal_stiffnesses(
        &self,
        constitutive_model: &C,
        nodal_temperatures: &ElementNodalTemperatures<N>,
    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError> {
        let heat_flux_tangents = self
            .temperature_gradients(nodal_temperatures)
            .iter()
            .map(|temperature_gradient| constitutive_model.heat_flux_tangent(temperature_gradient))
            .collect::<Result<HeatFluxTangents<G>, _>>()
            .map_err(|error| FiniteElementError::upstream(error, self))?;
        Ok(heat_flux_tangents
            .iter()
            .zip(
                self.gradient_vectors()
                    .iter()
                    .zip(self.integration_weights()),
            )
            .map(
                |(heat_flux_tangent, (gradient_vectors, integration_weight))| {
                    gradient_vectors
                        .iter()
                        .map(|gradient_vector_a| {
                            gradient_vectors
                                .iter()
                                .map(|gradient_vector_b| {
                                    -gradient_vector_a
                                        .contract_with(&(heat_flux_tangent * gradient_vector_b))
                                        * integration_weight
                                })
                                .collect()
                        })
                        .collect()
                },
            )
            .sum())
    }
}