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, 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}