Skip to main content

qtty_core/units/
pressure.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Pressure units.
5//!
6//! The canonical scaling unit for this dimension is the **pascal** (`Pascal::RATIO == 1.0`).
7//! All other pressure units are expressed as exact ratios to pascals.
8//!
9//! Pressure has dimension M¹ · L⁻¹ · T⁻² (equivalently, force per unit area: N/m²).
10//!
11//! This module provides:
12//!
13//! - **SI pascal** and the commonly used SI-prefixed variants.
14//! - **Bar** — non-SI unit accepted for use with SI (1 bar = 100 000 Pa exactly).
15//!
16//! ```rust
17//! use qtty_core::pressure::{Pascals, Hectopascal};
18//!
19//! let p = Pascals::new(101_325.0);
20//! let hpa = p.to::<Hectopascal>();
21//! assert!((hpa.value() - 1013.25).abs() < 1e-9);
22//! ```
23
24use crate::{Quantity, Unit};
25use qtty_derive::Unit;
26
27/// Re-export the pressure dimension from the dimension module.
28pub use crate::dimension::Pressure;
29
30/// Marker trait for any [`Unit`] whose dimension is [`Pressure`].
31pub trait PressureUnit: Unit<Dim = Pressure> {}
32impl<T: Unit<Dim = Pressure>> PressureUnit for T {}
33
34// ─────────────────────────────────────────────────────────────────────────────
35// SI pascal
36// ─────────────────────────────────────────────────────────────────────────────
37
38/// Pascal — SI coherent derived unit of pressure (kg·m⁻¹·s⁻², equivalently N/m²).
39///
40/// BIPM SI brochure 9th ed., Table 4: 1 Pa = 1 N m⁻¹ = 1 kg m⁻¹ s⁻².
41#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
42#[unit(symbol = "Pa", dimension = Pressure, ratio = 1.0)]
43pub struct Pascal;
44/// Type alias shorthand for [`Pascal`].
45pub type Pa = Pascal;
46/// A quantity measured in pascals.
47pub type Pascals = Quantity<Pa>;
48/// One pascal.
49pub const PASCAL: Pascals = Pascals::new(1.0);
50
51macro_rules! si_pascal {
52    ($name:ident, $sym:literal, $ratio:expr, $alias:ident, $qty:ident, $one:ident) => {
53        #[doc = concat!("SI-prefixed pascal unit (", stringify!($ratio), " Pa).")]
54        #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
55        #[unit(symbol = $sym, dimension = Pressure, ratio = $ratio)]
56        pub struct $name;
57        #[doc = concat!("Type alias shorthand for [`", stringify!($name), "`].")]
58        pub type $alias = $name;
59        #[doc = concat!("A quantity measured in ", stringify!($name), "s.")]
60        pub type $qty = Quantity<$alias>;
61        #[doc = concat!("One ", stringify!($name), ".")]
62        pub const $one: $qty = $qty::new(1.0);
63    };
64}
65
66/// Hectopascal — SI-prefixed pascal unit (100 Pa).
67///
68/// 1 hPa = 100 Pa exactly. Widely used in meteorology and observatory/site
69/// metadata to report atmospheric pressure (standard atmosphere ≈ 1013.25 hPa).
70#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
71#[unit(symbol = "hPa", dimension = Pressure, ratio = 1e2)]
72pub struct Hectopascal;
73/// Type alias shorthand for [`Hectopascal`].
74pub type HPa = Hectopascal;
75/// A quantity measured in hectopascals.
76pub type Hectopascals = Quantity<HPa>;
77/// One hectopascal.
78pub const HECTOPASCAL: Hectopascals = Hectopascals::new(1.0);
79
80si_pascal!(Millipascal, "mPa", 1e-3, MilliPa, Millipascals, MILLIPASCAL);
81si_pascal!(Kilopascal, "kPa", 1e3, KPa, Kilopascals, KILOPASCAL);
82si_pascal!(Megapascal, "MPa", 1e6, MPa, Megapascals, MEGAPASCAL);
83si_pascal!(Gigapascal, "GPa", 1e9, GPa, Gigapascals, GIGAPASCAL);
84
85// ─────────────────────────────────────────────────────────────────────────────
86// Non-SI but common: bar
87// ─────────────────────────────────────────────────────────────────────────────
88
89/// Bar — non-SI unit of pressure accepted for use with the SI (1 bar = 10⁵ Pa exactly).
90///
91/// BIPM SI brochure 9th ed., Table 8. Widely used in engineering, oceanography,
92/// and observatory metadata. The millibar (mbar) is numerically identical to the
93/// hectopascal (hPa).
94#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
95#[unit(symbol = "bar", dimension = Pressure, ratio = 1e5)]
96pub struct Bar;
97/// A quantity measured in bars.
98pub type Bars = Quantity<Bar>;
99/// One bar.
100pub const BAR: Bars = Bars::new(1.0);
101
102// ─────────────────────────────────────────────────────────────────────────────
103// Customary / engineering pressure units
104// ─────────────────────────────────────────────────────────────────────────────
105
106/// Standard atmosphere — 1 atm = 101 325 Pa (exact, defined by ISO 2533).
107#[cfg(feature = "customary")]
108#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
109#[unit(symbol = "atm", dimension = Pressure, ratio = 101_325.0)]
110pub struct Atmosphere;
111/// Type alias shorthand for [`Atmosphere`].
112#[cfg(feature = "customary")]
113pub type Atm = Atmosphere;
114/// A quantity measured in standard atmospheres.
115#[cfg(feature = "customary")]
116pub type Atmospheres = Quantity<Atm>;
117/// One standard atmosphere.
118#[cfg(feature = "customary")]
119pub const ATMOSPHERE: Atmospheres = Atmospheres::new(1.0);
120
121/// Torr — 1 Torr = 101 325/760 Pa ≈ 133.322 Pa.
122///
123/// Defined as exactly 1/760 of a standard atmosphere.
124#[cfg(feature = "customary")]
125#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
126#[unit(symbol = "Torr", dimension = Pressure, ratio = 101_325.0 / 760.0)]
127pub struct Torr;
128/// A quantity measured in torr.
129#[cfg(feature = "customary")]
130pub type Torrs = Quantity<Torr>;
131/// One torr.
132#[cfg(feature = "customary")]
133pub const TORR: Torrs = Torrs::new(1.0);
134
135/// Conventional millimetre of mercury — 1 mmHg = 101 325/760 Pa (same as Torr).
136#[cfg(feature = "customary")]
137#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
138#[unit(symbol = "mmHg", dimension = Pressure, ratio = 101_325.0 / 760.0)]
139pub struct MillimeterOfMercury;
140/// Type alias shorthand for [`MillimeterOfMercury`].
141#[cfg(feature = "customary")]
142pub type MmHg = MillimeterOfMercury;
143/// A quantity measured in millimetres of mercury.
144#[cfg(feature = "customary")]
145pub type MillimetersOfMercury = Quantity<MmHg>;
146/// One millimetre of mercury.
147#[cfg(feature = "customary")]
148pub const MILLIMETER_OF_MERCURY: MillimetersOfMercury = MillimetersOfMercury::new(1.0);
149
150/// Pound-force per square inch — 1 psi ≈ 6 894.757 293 Pa.
151#[cfg(feature = "customary")]
152#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
153#[unit(symbol = "psi", dimension = Pressure, ratio = 6_894.757_293)]
154pub struct PoundPerSquareInch;
155/// Type alias shorthand for [`PoundPerSquareInch`].
156#[cfg(feature = "customary")]
157pub type Psi = PoundPerSquareInch;
158/// A quantity measured in pounds per square inch.
159#[cfg(feature = "customary")]
160pub type PoundsPerSquareInch = Quantity<Psi>;
161/// One pound per square inch.
162#[cfg(feature = "customary")]
163pub const PSI: PoundsPerSquareInch = PoundsPerSquareInch::new(1.0);
164
165/// Inch of mercury — 1 inHg ≈ 3 386.389 Pa (25.4 × mmHg).
166#[cfg(feature = "customary")]
167#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Unit)]
168#[unit(symbol = "inHg", dimension = Pressure, ratio = 3_386.389)]
169pub struct InchOfMercury;
170/// Type alias shorthand for [`InchOfMercury`].
171#[cfg(feature = "customary")]
172pub type InHg = InchOfMercury;
173/// A quantity measured in inches of mercury.
174#[cfg(feature = "customary")]
175pub type InchesOfMercury = Quantity<InHg>;
176/// One inch of mercury.
177#[cfg(feature = "customary")]
178pub const INCH_OF_MERCURY: InchesOfMercury = InchesOfMercury::new(1.0);
179
180// ─────────────────────────────────────────────────────────────────────────────
181// Unit inventory macro
182// ─────────────────────────────────────────────────────────────────────────────
183
184/// Canonical list of always-available pressure units.
185#[macro_export]
186#[doc(hidden)]
187macro_rules! pressure_units {
188    ($cb:path) => {
189        $cb!(
190            Pascal,
191            Millipascal,
192            Hectopascal,
193            Kilopascal,
194            Megapascal,
195            Gigapascal,
196            Bar
197        );
198    };
199}
200
201/// Canonical list of `customary`-gated pressure units.
202#[cfg(feature = "customary")]
203#[macro_export]
204#[doc(hidden)]
205macro_rules! pressure_customary_units {
206    ($cb:path) => {
207        $cb!(
208            Atmosphere,
209            Torr,
210            MillimeterOfMercury,
211            PoundPerSquareInch,
212            InchOfMercury
213        );
214    };
215}
216
217// Generate bidirectional From impls between all pressure units.
218pressure_units!(crate::impl_unit_from_conversions);
219
220#[cfg(feature = "cross-unit-ops")]
221pressure_units!(crate::impl_unit_cross_unit_ops);
222
223// Cross-feature: customary pressure conversions (SI ↔ customary and intra-customary).
224// impl_unit_from_conversions_between! and impl_unit_cross_unit_ops_between! each
225// also handle intra-extra pairs, so we do NOT call pressure_customary_units! separately.
226#[cfg(feature = "customary")]
227crate::impl_unit_from_conversions_between!(
228    Pascal, Millipascal, Hectopascal, Kilopascal, Megapascal, Gigapascal, Bar;
229    Atmosphere, Torr, MillimeterOfMercury, PoundPerSquareInch, InchOfMercury
230);
231
232#[cfg(all(feature = "customary", feature = "cross-unit-ops"))]
233crate::impl_unit_cross_unit_ops_between!(
234    Pascal, Millipascal, Hectopascal, Kilopascal, Megapascal, Gigapascal, Bar;
235    Atmosphere, Torr, MillimeterOfMercury, PoundPerSquareInch, InchOfMercury
236);
237
238// Compile-time check: every pressure unit is registered as BuiltinUnit.
239#[cfg(test)]
240pressure_units!(crate::assert_units_are_builtin);
241
242#[cfg(all(test, feature = "std"))]
243mod tests {
244    use super::*;
245    use approx::assert_abs_diff_eq;
246
247    #[test]
248    fn pascal_to_hectopascal() {
249        let p = Pascals::new(101_325.0);
250        let hpa: Hectopascals = p.to();
251        assert_abs_diff_eq!(hpa.value(), 1013.25, epsilon = 1e-9);
252    }
253
254    #[test]
255    fn hectopascal_to_pascal() {
256        let hpa = Hectopascals::new(1013.25);
257        let p: Pascals = hpa.to();
258        assert_abs_diff_eq!(p.value(), 101_325.0, epsilon = 1e-9);
259    }
260
261    #[test]
262    fn pascal_to_kilopascal() {
263        let p = Pascals::new(1000.0);
264        let kpa: Kilopascals = p.to();
265        assert_abs_diff_eq!(kpa.value(), 1.0, epsilon = 1e-12);
266    }
267
268    #[test]
269    fn pascal_to_bar() {
270        let p = Pascals::new(100_000.0);
271        let bar: Bars = p.to();
272        assert_abs_diff_eq!(bar.value(), 1.0, epsilon = 1e-12);
273    }
274
275    #[test]
276    fn bar_to_hectopascal() {
277        let bar = Bars::new(1.0);
278        let hpa: Hectopascals = bar.to();
279        assert_abs_diff_eq!(hpa.value(), 1000.0, epsilon = 1e-9);
280    }
281
282    #[test]
283    fn pascals_addition() {
284        let a = Pascals::new(50.0);
285        let b = Pascals::new(50.0);
286        let c = a + b;
287        assert_abs_diff_eq!(c.value(), 100.0, epsilon = 1e-12);
288    }
289
290    #[test]
291    #[cfg(feature = "customary")]
292    fn atmosphere_to_pascal() {
293        let atm = Atmospheres::new(1.0);
294        let p: Pascals = atm.to();
295        assert_abs_diff_eq!(p.value(), 101_325.0, epsilon = 1e-9);
296    }
297
298    #[test]
299    #[cfg(feature = "customary")]
300    fn torr_to_atmosphere() {
301        // 760 Torr = 1 atm (exact)
302        let t = Torrs::new(760.0);
303        let a: Atmospheres = t.to();
304        assert_abs_diff_eq!(a.value(), 1.0, epsilon = 1e-12);
305    }
306
307    #[test]
308    #[cfg(feature = "customary")]
309    fn psi_to_pascal() {
310        let p = PoundsPerSquareInch::new(1.0);
311        let pa: Pascals = p.to();
312        assert_abs_diff_eq!(pa.value(), 6_894.757_293, epsilon = 1e-6);
313    }
314}