Skip to main content

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::{ContractWith, Quantity, Tensor, TensorList},
11    mechanics::{HeatFluxTangents, HeatFluxes},
12    units::{Power, PowerPerTemperature, PowerTemperature},
13};
14
15pub type ElementNodalForcesThermal<const D: usize> = TensorList<Quantity<Power>, D>;
16pub type ElementNodalStiffnessesThermal<const D: usize> =
17    TensorList<ElementNodalForcesThermalPerTemperature<D>, D>;
18pub type ElementNodalForcesThermalPerTemperature<const D: usize> =
19    TensorList<Quantity<PowerPerTemperature>, D>;
20
21pub trait ThermalConductionFiniteElement<
22    C,
23    const G: usize,
24    const M: usize,
25    const N: usize,
26    const P: usize,
27> where
28    C: ThermalConduction,
29    Self: ThermalFiniteElement<G, M, N, P>,
30{
31    fn potential(
32        &self,
33        constitutive_model: &C,
34        nodal_temperatures: &ElementNodalTemperatures<N>,
35    ) -> Result<Quantity<PowerTemperature>, FiniteElementError>;
36    fn nodal_forces(
37        &self,
38        constitutive_model: &C,
39        nodal_temperatures: &ElementNodalTemperatures<N>,
40    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError>;
41    fn nodal_stiffnesses(
42        &self,
43        constitutive_model: &C,
44        nodal_temperatures: &ElementNodalTemperatures<N>,
45    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError>;
46}
47
48impl<C, const G: usize, const M: usize, const N: usize, const O: usize, const P: usize>
49    ThermalConductionFiniteElement<C, G, M, N, P> for Element<3, G, N, O>
50where
51    C: ThermalConduction,
52    Self: ThermalFiniteElement<G, M, N, P>,
53{
54    fn potential(
55        &self,
56        constitutive_model: &C,
57        nodal_temperatures: &ElementNodalTemperatures<N>,
58    ) -> Result<Quantity<PowerTemperature>, FiniteElementError> {
59        self.temperature_gradients(nodal_temperatures)
60            .iter()
61            .zip(self.integration_weights())
62            .map(|(temperature_gradient, integration_weight)| {
63                Ok::<_, ConstitutiveError>(
64                    constitutive_model.potential(temperature_gradient)? * integration_weight,
65                )
66            })
67            .sum::<Result<_, ConstitutiveError>>()
68            .map_err(|error| FiniteElementError::upstream(error, self))
69    }
70    fn nodal_forces(
71        &self,
72        constitutive_model: &C,
73        nodal_temperatures: &ElementNodalTemperatures<N>,
74    ) -> Result<ElementNodalForcesThermal<N>, FiniteElementError> {
75        let heat_fluxes = self
76            .temperature_gradients(nodal_temperatures)
77            .iter()
78            .map(|temperature_gradient| constitutive_model.heat_flux(temperature_gradient))
79            .collect::<Result<HeatFluxes<G>, _>>()
80            .map_err(|error| FiniteElementError::upstream(error, self))?;
81        Ok(heat_fluxes
82            .iter()
83            .zip(
84                self.gradient_vectors()
85                    .iter()
86                    .zip(self.integration_weights()),
87            )
88            .map(|(heat_flux, (gradient_vectors, integration_weight))| {
89                gradient_vectors
90                    .iter()
91                    .map(|gradient_vector| {
92                        -heat_flux.contract_with(gradient_vector) * integration_weight
93                    })
94                    .collect()
95            })
96            .sum())
97    }
98    fn nodal_stiffnesses(
99        &self,
100        constitutive_model: &C,
101        nodal_temperatures: &ElementNodalTemperatures<N>,
102    ) -> Result<ElementNodalStiffnessesThermal<N>, FiniteElementError> {
103        let heat_flux_tangents = 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            .map_err(|error| FiniteElementError::upstream(error, self))?;
109        Ok(heat_flux_tangents
110            .iter()
111            .zip(
112                self.gradient_vectors()
113                    .iter()
114                    .zip(self.integration_weights()),
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                                        .contract_with(&(heat_flux_tangent * gradient_vector_b))
126                                        * integration_weight
127                                })
128                                .collect()
129                        })
130                        .collect()
131                },
132            )
133            .sum())
134    }
135}