Skip to main content

pcb_toolkit/
thermal.rs

1//! Thermal management calculator.
2//!
3//! Computes junction temperature using the thermal resistance model:
4//! T_junction = R_theta_ja × P_dissipated + T_ambient
5
6use serde::{Deserialize, Serialize};
7
8use crate::CalcError;
9
10/// Inputs for thermal management calculation.
11pub struct ThermalInput {
12    /// Thermal resistance, junction-to-ambient (°C/W).
13    pub r_theta_ja: f64,
14    /// Power dissipation (W).
15    pub power_w: f64,
16    /// Ambient temperature (°C).
17    pub t_ambient_c: f64,
18}
19
20/// Result of a thermal management calculation.
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct ThermalResult {
23    /// Junction temperature (°C).
24    pub t_junction_c: f64,
25    /// Junction temperature (°F).
26    pub t_junction_f: f64,
27}
28
29/// Calculate junction temperature.
30pub fn calculate(input: &ThermalInput) -> Result<ThermalResult, CalcError> {
31    if input.r_theta_ja <= 0.0 {
32        return Err(CalcError::OutOfRange {
33            name: "r_theta_ja",
34            value: input.r_theta_ja,
35            expected: "> 0",
36        });
37    }
38    if input.power_w < 0.0 {
39        return Err(CalcError::OutOfRange {
40            name: "power_w",
41            value: input.power_w,
42            expected: ">= 0",
43        });
44    }
45
46    let t_junction_c = input.r_theta_ja * input.power_w + input.t_ambient_c;
47    let t_junction_f = 1.8 * t_junction_c + 32.0;
48
49    Ok(ThermalResult { t_junction_c, t_junction_f })
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use approx::assert_relative_eq;
56
57    #[test]
58    fn basic_junction_temp() {
59        let result = calculate(&ThermalInput {
60            r_theta_ja: 50.0,
61            power_w: 1.0,
62            t_ambient_c: 25.0,
63        })
64        .unwrap();
65
66        assert_relative_eq!(result.t_junction_c, 75.0, epsilon = 1e-10);
67        assert_relative_eq!(result.t_junction_f, 167.0, epsilon = 1e-10);
68    }
69
70    #[test]
71    fn high_thermal_resistance() {
72        let result = calculate(&ThermalInput {
73            r_theta_ja: 100.0,
74            power_w: 0.5,
75            t_ambient_c: 25.0,
76        })
77        .unwrap();
78
79        assert_relative_eq!(result.t_junction_c, 75.0, epsilon = 1e-10);
80    }
81
82    #[test]
83    fn zero_power() {
84        let result = calculate(&ThermalInput {
85            r_theta_ja: 50.0,
86            power_w: 0.0,
87            t_ambient_c: 25.0,
88        })
89        .unwrap();
90
91        assert_relative_eq!(result.t_junction_c, 25.0, epsilon = 1e-10);
92    }
93
94    #[test]
95    fn invalid_r_theta() {
96        assert!(calculate(&ThermalInput {
97            r_theta_ja: -1.0,
98            power_w: 1.0,
99            t_ambient_c: 25.0,
100        })
101        .is_err());
102    }
103}