Skip to main content

thermalcomfort/
lib.rs

1//! # Thermal Comfort Library
2//!
3//! A comprehensive Rust port of the pythermalcomfort Python package for thermal comfort calculations.
4//! This library is `no_std` compatible and can run in WASM environments.
5//!
6//! This library provides tools for calculating thermal comfort indices, heat/cold stress metrics,
7//! and thermophysiological responses using multiple models including:
8//!
9//! - PMV/PPD (Predicted Mean Vote and Predicted Percentage Dissatisfied) - ISO 7730 & ASHRAE 55
10//! - Adaptive comfort models (ASHRAE 55 and EN 16798)
11//! - UTCI (Universal Thermal Climate Index)
12//! - SET (Standard Effective Temperature)
13//! - Heat stress indices (WBGT, Heat Index, etc.)
14//! - And many more...
15//!
16//! ## Example
17//!
18//! ```
19//! use thermalcomfort::{pmv_ppd_iso, v_relative, Temperature, Speed, Humidity, MetabolicRate, ClothingInsulation};
20//!
21//! let tdb = Temperature::from_celsius(25.0);
22//! let tr = Temperature::from_celsius(25.0);
23//! let rh = Humidity::from_percent(50.0);
24//! let v = Speed::from_meters_per_second(0.1);
25//! let met = MetabolicRate::from_met(1.4);
26//! let clo = ClothingInsulation::from_clo(0.5);
27//!
28//! // Calculate relative air speed
29//! let vr = v_relative(v, met);
30//!
31//! // Calculate PMV and PPD
32//! let result = pmv_ppd_iso(
33//!     tdb,
34//!     tr,
35//!     vr,
36//!     rh,
37//!     met,
38//!     clo,
39//!     Default::default()
40//! );
41//! ```
42
43#![no_std]
44
45pub mod constants;
46pub mod models;
47pub mod numerical;
48pub mod psychrometrics;
49pub mod utilities;
50
51// Re-export commonly used items
52pub use models::pmv::{PmvPpdResult, pmv_ppd_iso};
53pub use utilities::{
54    CLO_INDIVIDUAL_GARMENTS, CLO_TYPICAL_ENSEMBLES, clo_individual_garment, clo_typical_ensemble,
55    v_relative,
56};
57
58// Re-export measurements types for convenience
59// Users should import these from thermalcomfort instead of directly from measurements
60pub use measurements::{Area, Humidity, Length, Mass, Power, Pressure, Speed, Temperature};
61
62/// Clothing insulation measurement.
63///
64/// Represents thermal resistance of clothing per unit body surface area.
65/// 1 clo = 0.155 m²·K/W ≈ the insulation of a typical business suit.
66///
67/// # Constructors
68///
69/// - [`from_clo(0.5)`](ClothingInsulation::from_clo) — primary; clo values found in ASHRAE 55 / ISO 7730 clothing tables
70/// - [`from_tog(0.775)`](ClothingInsulation::from_tog) — tog units common in bedding industry (1 clo = 1.55 tog)
71/// - [`from_m2_k_per_w(0.0775)`](ClothingInsulation::from_m2_k_per_w) — SI thermal resistance (1 clo = 0.155 m²·K/W)
72///
73/// # Common Values (clo)
74///
75/// | Ensemble | clo |
76/// |----------|-----|
77/// | Nude | ≈ 0 |
78/// | Light summer (shorts, t-shirt) | 0.3–0.5 |
79/// | Typical business suit | 1.0 |
80/// | Heavy winter clothing | 1.5 |
81///
82/// # Examples
83///
84/// ```
85/// use thermalcomfort::ClothingInsulation;
86///
87/// let clo = ClothingInsulation::from_clo(1.0);
88/// assert!((clo.as_m2_k_per_w() - 0.155).abs() < 1e-10);
89/// assert!((clo.as_tog() - 1.55).abs() < 1e-10);
90/// ```
91#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
92pub struct ClothingInsulation(f64);
93
94impl ClothingInsulation {
95    /// Create from clo units (1 clo = 0.155 m²·K/W)
96    #[inline]
97    pub const fn from_clo(value: f64) -> Self {
98        Self(value)
99    }
100
101    /// Create from tog units (1 clo = 1.55 tog)
102    #[inline]
103    pub fn from_tog(value: f64) -> Self {
104        Self(value / 1.55)
105    }
106
107    /// Create from SI thermal resistance (m²·K/W) (1 clo = 0.155 m²·K/W)
108    #[inline]
109    pub fn from_m2_k_per_w(value: f64) -> Self {
110        Self(value / 0.155)
111    }
112
113    /// Get value in clo units
114    #[inline]
115    pub const fn as_clo(&self) -> f64 {
116        self.0
117    }
118
119    /// Get value in tog units (1 clo = 1.55 tog)
120    #[inline]
121    pub fn as_tog(&self) -> f64 {
122        self.0 * 1.55
123    }
124
125    /// Get value in SI thermal resistance (m²·K/W) (1 clo = 0.155 m²·K/W)
126    #[inline]
127    pub fn as_m2_k_per_w(&self) -> f64 {
128        self.0 * 0.155
129    }
130}
131
132impl Default for ClothingInsulation {
133    fn default() -> Self {
134        Self(0.0)
135    }
136}
137
138/// Metabolic rate measurement.
139///
140/// Represents metabolic heat production per unit body surface area.
141/// 1 met = 58.15 W/m², the resting metabolic rate of a seated person.
142///
143/// # Constructors
144///
145/// - [`from_met(1.4)`](MetabolicRate::from_met) — primary; met values found in ASHRAE 55 / ISO 7730 activity tables
146/// - [`from_w_per_m2(81.41)`](MetabolicRate::from_w_per_m2) — SI heat flux per body surface area (1 met = 58.15 W/m²)
147/// - [`from_btu_per_h_ft2(25.76)`](MetabolicRate::from_btu_per_h_ft2) — Imperial equivalent (1 met = 18.4 Btu/(h·ft²))
148///
149/// # Common Values (met)
150///
151/// | Activity | met |
152/// |----------|-----|
153/// | Seated, quiet | 1.0 |
154/// | Standing, relaxed | 1.2 |
155/// | Walking 3.2 km/h | 2.0 |
156/// | Heavy work | 3.0+ |
157///
158/// # Examples
159///
160/// ```
161/// use thermalcomfort::MetabolicRate;
162///
163/// let met = MetabolicRate::from_met(1.0);
164/// assert!((met.as_w_per_m2() - 58.15).abs() < 1e-10);
165/// ```
166#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
167pub struct MetabolicRate(f64);
168
169impl MetabolicRate {
170    /// Conversion factor: 1 met = 58.15 W/m²
171    pub const MET_TO_W_M2: f64 = 58.15;
172
173    /// Conversion factor: 1 met = 18.4 Btu/(h·ft²)
174    pub const MET_TO_BTU_H_FT2: f64 = 18.4;
175
176    /// Create from met units (1 met = 58.15 W/m²)
177    #[inline]
178    pub const fn from_met(value: f64) -> Self {
179        Self(value)
180    }
181
182    /// Create from W/m² (1 met = 58.15 W/m²)
183    #[inline]
184    pub fn from_w_per_m2(value: f64) -> Self {
185        Self(value / Self::MET_TO_W_M2)
186    }
187
188    /// Create from Btu/(h·ft²) (1 met = 18.4 Btu/(h·ft²))
189    #[inline]
190    pub fn from_btu_per_h_ft2(value: f64) -> Self {
191        Self(value / Self::MET_TO_BTU_H_FT2)
192    }
193
194    /// Get value in met units
195    #[inline]
196    pub const fn as_met(&self) -> f64 {
197        self.0
198    }
199
200    /// Get value in W/m² (1 met = 58.15 W/m²)
201    #[inline]
202    pub fn as_w_per_m2(&self) -> f64 {
203        self.0 * Self::MET_TO_W_M2
204    }
205
206    /// Get value in Btu/(h·ft²) (1 met = 18.4 Btu/(h·ft²))
207    #[inline]
208    pub fn as_btu_per_h_ft2(&self) -> f64 {
209        self.0 * Self::MET_TO_BTU_H_FT2
210    }
211}
212
213impl Default for MetabolicRate {
214    fn default() -> Self {
215        Self(0.0)
216    }
217}
218
219/// Biological sex for physiological calculations
220///
221/// Used in models that differentiate physiological responses by sex,
222/// such as PET (basal metabolism) and ridge regression (body temperature prediction).
223#[derive(Debug, Clone, Copy, PartialEq, Eq)]
224pub enum Sex {
225    Male,
226    Female,
227}
228
229impl Sex {
230    /// Get numeric value (0.0 for Male, 1.0 for Female)
231    ///
232    /// Used internally by ridge regression and other models.
233    pub fn as_value(&self) -> f64 {
234        match self {
235            Sex::Male => 0.0,
236            Sex::Female => 1.0,
237        }
238    }
239}