1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct ThermalConductivity {
27 watts_per_meter_kelvin: f64,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub struct ElectricalConductivity {
32 siemens_per_meter: f64,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum ConductivityError {
37 InvalidConductivity,
38 InvalidArea,
39 InvalidThickness,
40 InvalidTemperatureDifference,
41 InvalidResistivity,
42}
43
44fn validate_positive(value: f64, error: ConductivityError) -> Result<f64, ConductivityError> {
45 if !value.is_finite() || value <= 0.0 {
46 Err(error)
47 } else {
48 Ok(value)
49 }
50}
51
52fn validate_finite(value: f64, error: ConductivityError) -> Result<f64, ConductivityError> {
53 if !value.is_finite() {
54 Err(error)
55 } else {
56 Ok(value)
57 }
58}
59
60impl ThermalConductivity {
61 pub fn new(watts_per_meter_kelvin: f64) -> Result<Self, ConductivityError> {
62 Ok(Self {
63 watts_per_meter_kelvin: validate_positive(
64 watts_per_meter_kelvin,
65 ConductivityError::InvalidConductivity,
66 )?,
67 })
68 }
69
70 #[must_use]
71 pub fn watts_per_meter_kelvin(&self) -> f64 {
72 self.watts_per_meter_kelvin
73 }
74}
75
76impl ElectricalConductivity {
77 pub fn new(siemens_per_meter: f64) -> Result<Self, ConductivityError> {
78 Ok(Self {
79 siemens_per_meter: validate_positive(
80 siemens_per_meter,
81 ConductivityError::InvalidConductivity,
82 )?,
83 })
84 }
85
86 #[must_use]
87 pub fn siemens_per_meter(&self) -> f64 {
88 self.siemens_per_meter
89 }
90}
91
92pub fn thermal_resistance(
93 thickness_m: f64,
94 area_m2: f64,
95 conductivity_w_per_mk: f64,
96) -> Result<f64, ConductivityError> {
97 Ok(
98 validate_positive(thickness_m, ConductivityError::InvalidThickness)?
99 / (validate_positive(area_m2, ConductivityError::InvalidArea)?
100 * validate_positive(
101 conductivity_w_per_mk,
102 ConductivityError::InvalidConductivity,
103 )?),
104 )
105}
106
107pub fn heat_flow_rate(
108 conductivity_w_per_mk: f64,
109 area_m2: f64,
110 delta_temp_k: f64,
111 thickness_m: f64,
112) -> Result<f64, ConductivityError> {
113 Ok(validate_positive(
114 conductivity_w_per_mk,
115 ConductivityError::InvalidConductivity,
116 )? * validate_positive(area_m2, ConductivityError::InvalidArea)?
117 * validate_finite(
118 delta_temp_k,
119 ConductivityError::InvalidTemperatureDifference,
120 )?
121 / validate_positive(thickness_m, ConductivityError::InvalidThickness)?)
122}
123
124pub fn resistivity_from_conductivity(conductivity_s_per_m: f64) -> Result<f64, ConductivityError> {
125 Ok(1.0 / validate_positive(conductivity_s_per_m, ConductivityError::InvalidConductivity)?)
126}
127
128pub fn conductivity_from_resistivity(resistivity_ohm_m: f64) -> Result<f64, ConductivityError> {
129 Ok(1.0 / validate_positive(resistivity_ohm_m, ConductivityError::InvalidResistivity)?)
130}
131
132#[cfg(test)]
133mod tests {
134 use super::{
135 ConductivityError, ElectricalConductivity, ThermalConductivity,
136 conductivity_from_resistivity, heat_flow_rate, resistivity_from_conductivity,
137 thermal_resistance,
138 };
139
140 #[test]
141 fn computes_conductivity_and_resistivity_values() {
142 let thermal = ThermalConductivity::new(50.0).unwrap();
143 let electrical = ElectricalConductivity::new(5.8e7).unwrap();
144
145 assert_eq!(thermal.watts_per_meter_kelvin(), 50.0);
146 assert_eq!(electrical.siemens_per_meter(), 5.8e7);
147 assert_eq!(thermal_resistance(0.1, 2.0, 50.0).unwrap(), 0.001);
148 assert_eq!(heat_flow_rate(50.0, 2.0, 10.0, 0.1).unwrap(), 10_000.0);
149 assert_eq!(resistivity_from_conductivity(2.0).unwrap(), 0.5);
150 assert_eq!(conductivity_from_resistivity(0.5).unwrap(), 2.0);
151 }
152
153 #[test]
154 fn allows_negative_temperature_differences_for_heat_flow_direction() {
155 assert_eq!(heat_flow_rate(50.0, 2.0, -10.0, 0.1).unwrap(), -10_000.0);
156 }
157
158 #[test]
159 fn rejects_invalid_conductivity_inputs() {
160 assert_eq!(
161 ThermalConductivity::new(0.0),
162 Err(ConductivityError::InvalidConductivity)
163 );
164 assert_eq!(
165 ElectricalConductivity::new(f64::NAN),
166 Err(ConductivityError::InvalidConductivity)
167 );
168 assert_eq!(
169 thermal_resistance(0.1, 0.0, 50.0),
170 Err(ConductivityError::InvalidArea)
171 );
172 assert_eq!(
173 heat_flow_rate(50.0, 2.0, f64::NAN, 0.1),
174 Err(ConductivityError::InvalidTemperatureDifference)
175 );
176 assert_eq!(
177 conductivity_from_resistivity(-1.0),
178 Err(ConductivityError::InvalidResistivity)
179 );
180 }
181}