conspire/domain/fem/block/element/thermal/conduction/
mod.rs1#[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}