conspire/domain/fem/block/element/thermal/conduction/
mod.rs

1#[cfg(test)]
2pub mod test;
3
4use crate::{
5    constitutive::{ConstitutiveError, thermal::conduction::ThermalConduction},
6    fem::block::element::{
7        Element, FiniteElement, FiniteElementError,
8        thermal::{ElementNodalTemperatures, ThermalFiniteElement},
9    },
10    math::{Scalar, Tensor, TensorRank0List, TensorRank0List2D},
11    mechanics::{HeatFluxTangents, HeatFluxes},
12};
13
14pub type ElementNodalForcesThermal<const D: usize> = TensorRank0List<D>;
15pub type ElementNodalStiffnessesThermal<const D: usize> = TensorRank0List2D<D>;
16
17pub trait ThermalConductionFiniteElement<
18    C,
19    const G: usize,
20    const M: usize,
21    const N: usize,
22    const P: usize,
23> where
24    C: ThermalConduction,
25    Self: ThermalFiniteElement<G, M, N, P>,
26{
27    fn potential(
28        &self,
29        constitutive_model: &C,
30        nodal_temperatures: &ElementNodalTemperatures<N>,
31    ) -> Result<Scalar, FiniteElementError>;
32    fn nodal_forces(
33        &self,
34        constitutive_model: &C,
35        nodal_temperatures: &ElementNodalTemperatures<N>,
36    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError>;
37    fn nodal_stiffnesses(
38        &self,
39        constitutive_model: &C,
40        nodal_temperatures: &ElementNodalTemperatures<N>,
41    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError>;
42}
43
44impl<C, const G: usize, const M: usize, const N: usize, const O: usize, const P: usize>
45    ThermalConductionFiniteElement<C, G, M, N, P> for Element<G, N, O>
46where
47    C: ThermalConduction,
48    Self: ThermalFiniteElement<G, M, N, P>,
49{
50    fn potential(
51        &self,
52        constitutive_model: &C,
53        nodal_temperatures: &ElementNodalTemperatures<N>,
54    ) -> Result<Scalar, FiniteElementError> {
55        match self
56            .temperature_gradients(nodal_temperatures)
57            .iter()
58            .zip(self.integration_weights())
59            .map(|(temperature_gradient, integration_weight)| {
60                Ok::<_, ConstitutiveError>(
61                    constitutive_model.potential(temperature_gradient)? * integration_weight,
62                )
63            })
64            .sum()
65        {
66            Ok(potential) => Ok(potential),
67            Err(error) => Err(FiniteElementError::Upstream(
68                format!("{error}"),
69                format!("{self:?}"),
70            )),
71        }
72    }
73    fn nodal_forces(
74        &self,
75        constitutive_model: &C,
76        nodal_temperatures: &ElementNodalTemperatures<N>,
77    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError> {
78        match self
79            .temperature_gradients(nodal_temperatures)
80            .iter()
81            .map(|temperature_gradient| constitutive_model.heat_flux(temperature_gradient))
82            .collect::<Result<HeatFluxes<G>, _>>()
83        {
84            Ok(heat_fluxes) => Ok(heat_fluxes
85                .iter()
86                .zip(
87                    self.gradient_vectors()
88                        .iter()
89                        .zip(self.integration_weights()),
90                )
91                .map(|(heat_flux, (gradient_vectors, integration_weight))| {
92                    gradient_vectors
93                        .iter()
94                        .map(|gradient_vector| -(heat_flux * gradient_vector) * integration_weight)
95                        .collect()
96                })
97                .sum()),
98            Err(error) => Err(FiniteElementError::Upstream(
99                format!("{error}"),
100                format!("{self:?}"),
101            )),
102        }
103    }
104    fn nodal_stiffnesses(
105        &self,
106        constitutive_model: &C,
107        nodal_temperatures: &ElementNodalTemperatures<N>,
108    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError> {
109        match self
110            .temperature_gradients(nodal_temperatures)
111            .iter()
112            .map(|temperature_gradient| constitutive_model.heat_flux_tangent(temperature_gradient))
113            .collect::<Result<HeatFluxTangents<G>, _>>()
114        {
115            Ok(heat_flux_tangents) => Ok(heat_flux_tangents
116                .iter()
117                .zip(
118                    self.gradient_vectors()
119                        .iter()
120                        .zip(self.integration_weights()),
121                )
122                .map(
123                    |(heat_flux_tangent, (gradient_vectors, integration_weight))| {
124                        gradient_vectors
125                            .iter()
126                            .map(|gradient_vector_a| {
127                                gradient_vectors
128                                    .iter()
129                                    .map(|gradient_vector_b| {
130                                        -(gradient_vector_a
131                                            * (heat_flux_tangent * gradient_vector_b))
132                                            * integration_weight
133                                    })
134                                    .collect()
135                            })
136                            .collect()
137                    },
138                )
139                .sum()),
140            Err(error) => Err(FiniteElementError::Upstream(
141                format!("{error}"),
142                format!("{self:?}"),
143            )),
144        }
145    }
146}