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, 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<C, const G: usize, const N: usize>
18where
19    C: ThermalConduction,
20    Self: ThermalFiniteElement<G, N>,
21{
22    fn potential(
23        &self,
24        constitutive_model: &C,
25        nodal_temperatures: &ElementNodalTemperatures<N>,
26    ) -> Result<Scalar, FiniteElementError>;
27    fn nodal_forces(
28        &self,
29        constitutive_model: &C,
30        nodal_temperatures: &ElementNodalTemperatures<N>,
31    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError>;
32    fn nodal_stiffnesses(
33        &self,
34        constitutive_model: &C,
35        nodal_temperatures: &ElementNodalTemperatures<N>,
36    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError>;
37}
38
39impl<C, const G: usize, const N: usize> ThermalConductionFiniteElement<C, G, N> for Element<G, N>
40where
41    C: ThermalConduction,
42    Self: ThermalFiniteElement<G, N>,
43{
44    fn potential(
45        &self,
46        constitutive_model: &C,
47        nodal_temperatures: &ElementNodalTemperatures<N>,
48    ) -> Result<Scalar, FiniteElementError> {
49        match self
50            .temperature_gradients(nodal_temperatures)
51            .iter()
52            .zip(self.integration_weights().iter())
53            .map(|(temperature_gradient, integration_weight)| {
54                Ok::<_, ConstitutiveError>(
55                    constitutive_model.potential(temperature_gradient)? * integration_weight,
56                )
57            })
58            .sum()
59        {
60            Ok(potential) => Ok(potential),
61            Err(error) => Err(FiniteElementError::Upstream(
62                format!("{error}"),
63                format!("{self:?}"),
64            )),
65        }
66    }
67    fn nodal_forces(
68        &self,
69        constitutive_model: &C,
70        nodal_temperatures: &ElementNodalTemperatures<N>,
71    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError> {
72        match self
73            .temperature_gradients(nodal_temperatures)
74            .iter()
75            .map(|temperature_gradient| constitutive_model.heat_flux(temperature_gradient))
76            .collect::<Result<HeatFluxes<G>, _>>()
77        {
78            Ok(heat_fluxes) => Ok(heat_fluxes
79                .iter()
80                .zip(
81                    self.gradient_vectors()
82                        .iter()
83                        .zip(self.integration_weights().iter()),
84                )
85                .map(|(heat_flux, (gradient_vectors, integration_weight))| {
86                    gradient_vectors
87                        .iter()
88                        .map(|gradient_vector| -(heat_flux * gradient_vector) * integration_weight)
89                        .collect()
90                })
91                .sum()),
92            Err(error) => Err(FiniteElementError::Upstream(
93                format!("{error}"),
94                format!("{self:?}"),
95            )),
96        }
97    }
98    fn nodal_stiffnesses(
99        &self,
100        constitutive_model: &C,
101        nodal_temperatures: &ElementNodalTemperatures<N>,
102    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError> {
103        match self
104            .temperature_gradients(nodal_temperatures)
105            .iter()
106            .map(|temperature_gradient| constitutive_model.heat_flux_tangent(temperature_gradient))
107            .collect::<Result<HeatFluxTangents<G>, _>>()
108        {
109            Ok(heat_flux_tangents) => Ok(heat_flux_tangents
110                .iter()
111                .zip(
112                    self.gradient_vectors()
113                        .iter()
114                        .zip(self.integration_weights().iter()),
115                )
116                .map(
117                    |(heat_flux_tangent, (gradient_vectors, integration_weight))| {
118                        gradient_vectors
119                            .iter()
120                            .map(|gradient_vector_a| {
121                                gradient_vectors
122                                    .iter()
123                                    .map(|gradient_vector_b| {
124                                        -(gradient_vector_a
125                                            * (heat_flux_tangent * gradient_vector_b))
126                                            * integration_weight
127                                    })
128                                    .collect()
129                            })
130                            .collect()
131                    },
132                )
133                .sum()),
134            Err(error) => Err(FiniteElementError::Upstream(
135                format!("{error}"),
136                format!("{self:?}"),
137            )),
138        }
139    }
140}