Skip to main content

space_units/quantities/
energy.rs

1use super::DisplayWithUnit;
2#[cfg(feature = "std")]
3use super::Velocity;
4
5// -------------------------
6// Energy
7// -------------------------
8
9/// An energy quantity, stored canonically in joules (J).
10///
11/// # Construction
12/// ```
13/// use space_units::Energy;
14/// let e = Energy::from_kj(4.2);
15/// ```
16///
17/// # Typed arithmetic
18/// - [`Force`](crate::Force) * [`Length`](crate::Length) → [`Energy`]
19/// - [`Energy`] / [`Time`](crate::Time) → [`Power`]
20/// - [`Energy`] / [`Mass`](crate::Mass) → [`SpecificEnergy`]
21#[must_use]
22#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
23pub struct Energy(pub(crate) f64);
24
25/// Display/conversion units for [`Energy`].
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum EnergyUnit {
28    /// Joules (J) -- SI base unit for energy.
29    Joule,
30    /// Kilojoules (kJ).
31    KiloJoule,
32    /// Megajoules (MJ).
33    MegaJoule,
34    /// Watt-hours (Wh).
35    WattHour,
36    /// Kilowatt-hours (kWh).
37    KiloWattHour,
38    /// Electronvolts (eV).
39    ElectronVolt,
40    /// Kilo-electronvolts (keV).
41    KiloElectronVolt,
42    /// Mega-electronvolts (`MeV`).
43    MegaElectronVolt,
44    /// Ergs (erg) -- CGS unit.
45    Erg,
46    /// British thermal units (BTU).
47    Btu,
48    /// Thermochemical calories (cal).
49    Calorie,
50}
51
52impl EnergyUnit {
53    const fn symbol(self) -> &'static str {
54        match self {
55            Self::Joule => "J",
56            Self::KiloJoule => "kJ",
57            Self::MegaJoule => "MJ",
58            Self::WattHour => "Wh",
59            Self::KiloWattHour => "kWh",
60            Self::ElectronVolt => "eV",
61            Self::KiloElectronVolt => "keV",
62            Self::MegaElectronVolt => "MeV",
63            Self::Erg => "erg",
64            Self::Btu => "BTU",
65            Self::Calorie => "cal",
66        }
67    }
68
69    const fn joules_per_unit(self) -> f64 {
70        match self {
71            Self::Joule => 1.0,
72            Self::KiloJoule => 1e3,
73            Self::MegaJoule => 1e6,
74            Self::WattHour => 3_600.0,
75            Self::KiloWattHour => 3.6e6,
76            Self::ElectronVolt => 1.602_176_634e-19,
77            Self::KiloElectronVolt => 1.602_176_634e-16,
78            Self::MegaElectronVolt => 1.602_176_634e-13,
79            Self::Erg => 1e-7,
80            Self::Btu => 1_055.06,
81            Self::Calorie => 4.184,
82        }
83    }
84}
85
86impl Energy {
87    /// Create from joules (J).
88    pub const fn from_j(val: f64) -> Self {
89        Self(val)
90    }
91
92    /// Create from kilojoules (kJ).
93    pub const fn from_kj(val: f64) -> Self {
94        Self(val * 1e3)
95    }
96
97    /// Create from megajoules (MJ).
98    pub const fn from_mj(val: f64) -> Self {
99        Self(val * 1e6)
100    }
101
102    /// Create from watt-hours (Wh).
103    pub const fn from_wh(val: f64) -> Self {
104        Self(val * 3_600.0)
105    }
106
107    /// Create from kilowatt-hours (kWh).
108    pub const fn from_kwh(val: f64) -> Self {
109        Self(val * 3.6e6)
110    }
111
112    /// Create from electronvolts (eV).
113    pub const fn from_ev(val: f64) -> Self {
114        Self(val * 1.602_176_634e-19)
115    }
116
117    /// Create from kilo-electronvolts (keV).
118    pub const fn from_kev(val: f64) -> Self {
119        Self(val * 1.602_176_634e-16)
120    }
121
122    /// Create from mega-electronvolts (`MeV`).
123    pub const fn from_mev(val: f64) -> Self {
124        Self(val * 1.602_176_634e-13)
125    }
126
127    /// Create from ergs (erg).
128    pub const fn from_erg(val: f64) -> Self {
129        Self(val * 1e-7)
130    }
131
132    /// Create from British thermal units (BTU).
133    pub const fn from_btu(val: f64) -> Self {
134        Self(val * 1_055.06)
135    }
136
137    /// Create from thermochemical calories (cal).
138    pub const fn from_cal(val: f64) -> Self {
139        Self(val * 4.184)
140    }
141
142    /// Get value in joules (J).
143    pub const fn in_j(self) -> f64 {
144        self.0
145    }
146
147    /// Get value in kilojoules (kJ).
148    pub const fn in_kj(self) -> f64 {
149        self.0 / 1e3
150    }
151
152    /// Get value in megajoules (MJ).
153    pub const fn in_mj(self) -> f64 {
154        self.0 / 1e6
155    }
156
157    /// Get value in watt-hours (Wh).
158    pub const fn in_wh(self) -> f64 {
159        self.0 / 3_600.0
160    }
161
162    /// Get value in kilowatt-hours (kWh).
163    pub const fn in_kwh(self) -> f64 {
164        self.0 / 3.6e6
165    }
166
167    /// Get value in electronvolts (eV).
168    pub const fn in_ev(self) -> f64 {
169        self.0 / 1.602_176_634e-19
170    }
171
172    /// Get value in kilo-electronvolts (keV).
173    pub const fn in_kev(self) -> f64 {
174        self.0 / 1.602_176_634e-16
175    }
176
177    /// Get value in mega-electronvolts (`MeV`).
178    pub const fn in_mev(self) -> f64 {
179        self.0 / 1.602_176_634e-13
180    }
181
182    /// Get value in ergs (erg).
183    pub const fn in_erg(self) -> f64 {
184        self.0 / 1e-7
185    }
186
187    /// Get value in British thermal units (BTU).
188    pub const fn in_btu(self) -> f64 {
189        self.0 / 1_055.06
190    }
191
192    /// Get value in thermochemical calories (cal).
193    pub const fn in_cal(self) -> f64 {
194        self.0 / 4.184
195    }
196
197    /// Get value in the specified [`EnergyUnit`].
198    pub fn in_unit(self, unit: EnergyUnit) -> f64 {
199        self.0 / unit.joules_per_unit()
200    }
201
202    /// Return a display wrapper that formats this energy in the given unit.
203    pub fn display_as(self, unit: EnergyUnit) -> DisplayWithUnit {
204        DisplayWithUnit {
205            value: self.in_unit(unit),
206            symbol: unit.symbol(),
207        }
208    }
209
210    /// Return the absolute value of this energy.
211    pub fn abs(self) -> Self {
212        Self(self.0.abs())
213    }
214}
215
216impl_quantity_display!(Energy, "J");
217
218impl_common_ops!(Energy);
219
220// -------------------------
221// Power
222// -------------------------
223
224/// A power quantity, stored canonically in watts (W).
225///
226/// # Construction
227/// ```
228/// use space_units::Power;
229/// let p = Power::from_kw(1.5);
230/// ```
231///
232/// # Typed arithmetic
233/// - [`Power`] * [`Time`](crate::Time) → [`Energy`]
234/// - [`Power`] / [`ElectricCurrent`](crate::ElectricCurrent) → [`Voltage`](crate::Voltage)
235/// - [`Power`] / [`Area`](crate::Area) → [`HeatFlux`](crate::HeatFlux)
236#[must_use]
237#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
238pub struct Power(pub(crate) f64);
239
240/// Display/conversion units for [`Power`].
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum PowerUnit {
243    /// Watts (W) -- SI base unit for power.
244    Watt,
245    /// Kilowatts (kW).
246    KiloWatt,
247    /// Megawatts (MW).
248    MegaWatt,
249    /// Mechanical horsepower (hp).
250    Horsepower,
251}
252
253impl PowerUnit {
254    const fn symbol(self) -> &'static str {
255        match self {
256            Self::Watt => "W",
257            Self::KiloWatt => "kW",
258            Self::MegaWatt => "MW",
259            Self::Horsepower => "hp",
260        }
261    }
262
263    const fn watts_per_unit(self) -> f64 {
264        match self {
265            Self::Watt => 1.0,
266            Self::KiloWatt => 1e3,
267            Self::MegaWatt => 1e6,
268            Self::Horsepower => 745.7,
269        }
270    }
271}
272
273impl Power {
274    /// Create from watts (W).
275    pub const fn from_w(val: f64) -> Self {
276        Self(val)
277    }
278
279    /// Create from kilowatts (kW).
280    pub const fn from_kw(val: f64) -> Self {
281        Self(val * 1e3)
282    }
283
284    /// Create from megawatts (MW).
285    pub const fn from_mw(val: f64) -> Self {
286        Self(val * 1e6)
287    }
288
289    /// Create from mechanical horsepower (hp).
290    pub const fn from_hp(val: f64) -> Self {
291        Self(val * 745.7)
292    }
293
294    /// Get value in watts (W).
295    pub const fn in_w(self) -> f64 {
296        self.0
297    }
298
299    /// Get value in kilowatts (kW).
300    pub const fn in_kw(self) -> f64 {
301        self.0 / 1e3
302    }
303
304    /// Get value in megawatts (MW).
305    pub const fn in_mw(self) -> f64 {
306        self.0 / 1e6
307    }
308
309    /// Get value in mechanical horsepower (hp).
310    pub const fn in_hp(self) -> f64 {
311        self.0 / 745.7
312    }
313
314    /// Get value in the specified [`PowerUnit`].
315    pub fn in_unit(self, unit: PowerUnit) -> f64 {
316        self.0 / unit.watts_per_unit()
317    }
318
319    /// Return a display wrapper that formats this power in the given unit.
320    pub fn display_as(self, unit: PowerUnit) -> DisplayWithUnit {
321        DisplayWithUnit {
322            value: self.in_unit(unit),
323            symbol: unit.symbol(),
324        }
325    }
326
327    /// Return the absolute value of this power.
328    pub fn abs(self) -> Self {
329        Self(self.0.abs())
330    }
331}
332
333impl_quantity_display!(Power, "W");
334
335impl_common_ops!(Power);
336
337// -------------------------
338// Pressure
339// -------------------------
340
341/// A pressure quantity, stored canonically in pascals (Pa).
342///
343/// # Construction
344/// ```
345/// use space_units::Pressure;
346/// let p = Pressure::from_atm(1.0);
347/// ```
348///
349/// # Typed arithmetic
350/// - [`Force`](crate::Force) / [`Area`](crate::Area) → [`Pressure`]
351/// - [`Pressure`] * [`Area`](crate::Area) → [`Force`](crate::Force)
352#[must_use]
353#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
354pub struct Pressure(pub(crate) f64);
355
356/// Display/conversion units for [`Pressure`].
357#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub enum PressureUnit {
359    /// Pascals (Pa) -- SI base unit for pressure.
360    Pascal,
361    /// Kilopascals (kPa).
362    KiloPascal,
363    /// Megapascals (`MPa`).
364    MegaPascal,
365    /// Gigapascals (`GPa`).
366    GigaPascal,
367    /// Bar (bar).
368    Bar,
369    /// Millibar (mbar).
370    MilliBar,
371    /// Standard atmospheres (atm).
372    Atmosphere,
373    /// Pounds per square inch (psi).
374    Psi,
375    /// Torr (torr).
376    Torr,
377    /// Millimeters of mercury (mmHg).
378    MmHg,
379}
380
381impl PressureUnit {
382    const fn symbol(self) -> &'static str {
383        match self {
384            Self::Pascal => "Pa",
385            Self::KiloPascal => "kPa",
386            Self::MegaPascal => "MPa",
387            Self::GigaPascal => "GPa",
388            Self::Bar => "bar",
389            Self::MilliBar => "mbar",
390            Self::Atmosphere => "atm",
391            Self::Psi => "psi",
392            Self::Torr => "torr",
393            Self::MmHg => "mmHg",
394        }
395    }
396
397    const fn pa_per_unit(self) -> f64 {
398        match self {
399            Self::Pascal => 1.0,
400            Self::KiloPascal => 1e3,
401            Self::MegaPascal => 1e6,
402            Self::GigaPascal => 1e9,
403            Self::Bar => 1e5,
404            Self::MilliBar => 100.0,
405            Self::Atmosphere => 101_325.0,
406            Self::Psi => 6_894.757,
407            Self::Torr | Self::MmHg => 133.322,
408        }
409    }
410}
411
412impl Pressure {
413    /// Create from pascals (Pa).
414    pub const fn from_pa(val: f64) -> Self {
415        Self(val)
416    }
417
418    /// Create from bar (bar).
419    pub const fn from_bar(val: f64) -> Self {
420        Self(val * 1e5)
421    }
422
423    /// Create from kilopascals (kPa).
424    pub const fn from_kpa(val: f64) -> Self {
425        Self(val * 1e3)
426    }
427
428    /// Create from megapascals (`MPa`).
429    pub const fn from_mpa(val: f64) -> Self {
430        Self(val * 1e6)
431    }
432
433    /// Create from gigapascals (`GPa`).
434    pub const fn from_gpa(val: f64) -> Self {
435        Self(val * 1e9)
436    }
437
438    /// Create from millibar (mbar).
439    pub const fn from_mbar(val: f64) -> Self {
440        Self(val * 100.0)
441    }
442
443    /// Create from standard atmospheres (atm).
444    pub const fn from_atm(val: f64) -> Self {
445        Self(val * 101_325.0)
446    }
447
448    /// Create from pounds per square inch (psi).
449    pub const fn from_psi(val: f64) -> Self {
450        Self(val * 6_894.757)
451    }
452
453    /// Create from torr (torr).
454    pub const fn from_torr(val: f64) -> Self {
455        Self(val * 133.322)
456    }
457
458    /// Create from millimeters of mercury (mmHg).
459    pub const fn from_mmhg(val: f64) -> Self {
460        Self(val * 133.322)
461    }
462
463    /// Get value in pascals (Pa).
464    pub const fn in_pa(self) -> f64 {
465        self.0
466    }
467
468    /// Get value in bar (bar).
469    pub const fn in_bar(self) -> f64 {
470        self.0 / 1e5
471    }
472
473    /// Get value in kilopascals (kPa).
474    pub const fn in_kpa(self) -> f64 {
475        self.0 / 1e3
476    }
477
478    /// Get value in megapascals (`MPa`).
479    pub const fn in_mpa(self) -> f64 {
480        self.0 / 1e6
481    }
482
483    /// Get value in gigapascals (`GPa`).
484    pub const fn in_gpa(self) -> f64 {
485        self.0 / 1e9
486    }
487
488    /// Get value in millibar (mbar).
489    pub const fn in_mbar(self) -> f64 {
490        self.0 / 100.0
491    }
492
493    /// Get value in standard atmospheres (atm).
494    pub const fn in_atm(self) -> f64 {
495        self.0 / 101_325.0
496    }
497
498    /// Get value in pounds per square inch (psi).
499    pub const fn in_psi(self) -> f64 {
500        self.0 / 6_894.757
501    }
502
503    /// Get value in torr (torr).
504    pub const fn in_torr(self) -> f64 {
505        self.0 / 133.322
506    }
507
508    /// Get value in millimeters of mercury (mmHg).
509    pub const fn in_mmhg(self) -> f64 {
510        self.0 / 133.322
511    }
512
513    /// Get value in the specified [`PressureUnit`].
514    pub fn in_unit(self, unit: PressureUnit) -> f64 {
515        self.0 / unit.pa_per_unit()
516    }
517
518    /// Return a display wrapper that formats this pressure in the given unit.
519    pub fn display_as(self, unit: PressureUnit) -> DisplayWithUnit {
520        DisplayWithUnit {
521            value: self.in_unit(unit),
522            symbol: unit.symbol(),
523        }
524    }
525
526    /// Return the absolute value of this pressure.
527    pub fn abs(self) -> Self {
528        Self(self.0.abs())
529    }
530}
531
532impl_quantity_display!(Pressure, "Pa");
533
534impl_common_ops!(Pressure);
535
536// -------------------------
537// Specific energy
538// -------------------------
539
540/// A specific energy quantity, stored canonically in J/kg (= m²/s²).
541///
542/// # Construction
543/// ```
544/// use space_units::SpecificEnergy;
545/// let se = SpecificEnergy::from_km2ps2(-29.5);
546/// ```
547///
548/// # Typed arithmetic
549/// - [`Energy`] / [`Mass`](crate::Mass) → [`SpecificEnergy`]
550/// - [`Velocity`](crate::Velocity) * [`Velocity`](crate::Velocity) → [`SpecificEnergy`]
551///
552/// # Special methods
553/// - [`SpecificEnergy::sqrt`] returns a [`Velocity`](crate::Velocity) (requires `std` feature).
554#[must_use]
555#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
556pub struct SpecificEnergy(pub(crate) f64);
557
558/// Display/conversion units for [`SpecificEnergy`].
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
560pub enum SpecificEnergyUnit {
561    /// Joules per kilogram (J/kg).
562    JoulePerKg,
563    /// Kilojoules per kilogram (kJ/kg).
564    KiloJoulePerKg,
565    /// Megajoules per kilogram (MJ/kg).
566    MegaJoulePerKg,
567    /// Square kilometers per second squared (km²/s²) -- common in astrodynamics.
568    Km2PerS2,
569}
570
571impl SpecificEnergyUnit {
572    const fn symbol(self) -> &'static str {
573        match self {
574            Self::JoulePerKg => "J/kg",
575            Self::KiloJoulePerKg => "kJ/kg",
576            Self::MegaJoulePerKg => "MJ/kg",
577            Self::Km2PerS2 => "km^2/s^2",
578        }
579    }
580
581    const fn jpkg_per_unit(self) -> f64 {
582        match self {
583            Self::JoulePerKg => 1.0,
584            Self::KiloJoulePerKg => 1e3,
585            Self::MegaJoulePerKg | Self::Km2PerS2 => 1e6,
586        }
587    }
588}
589
590impl SpecificEnergy {
591    /// Create from joules per kilogram (J/kg).
592    pub const fn from_j_per_kg(val: f64) -> Self {
593        Self(val)
594    }
595
596    /// Create from km²/s² (common astrodynamics unit).
597    pub const fn from_km2ps2(val: f64) -> Self {
598        Self(val * 1e6)
599    }
600
601    /// Create from kilojoules per kilogram (kJ/kg).
602    pub const fn from_kj_per_kg(val: f64) -> Self {
603        Self(val * 1e3)
604    }
605
606    /// Create from megajoules per kilogram (MJ/kg).
607    pub const fn from_mj_per_kg(val: f64) -> Self {
608        Self(val * 1e6)
609    }
610
611    /// Get value in joules per kilogram (J/kg).
612    pub const fn in_j_per_kg(self) -> f64 {
613        self.0
614    }
615
616    /// Get value in km²/s².
617    pub const fn in_km2ps2(self) -> f64 {
618        self.0 / 1e6
619    }
620
621    /// Get value in kilojoules per kilogram (kJ/kg).
622    pub const fn in_kj_per_kg(self) -> f64 {
623        self.0 / 1e3
624    }
625
626    /// Get value in megajoules per kilogram (MJ/kg).
627    pub const fn in_mj_per_kg(self) -> f64 {
628        self.0 / 1e6
629    }
630
631    /// Get value in the specified [`SpecificEnergyUnit`].
632    pub fn in_unit(self, unit: SpecificEnergyUnit) -> f64 {
633        self.0 / unit.jpkg_per_unit()
634    }
635
636    /// Return a display wrapper that formats this specific energy in the given unit.
637    pub fn display_as(self, unit: SpecificEnergyUnit) -> DisplayWithUnit {
638        DisplayWithUnit {
639            value: self.in_unit(unit),
640            symbol: unit.symbol(),
641        }
642    }
643
644    /// Return the absolute value of this specific energy.
645    pub fn abs(self) -> Self {
646        Self(self.0.abs())
647    }
648
649    /// Take the square root, returning a [`Velocity`](crate::Velocity) (since sqrt(m²/s²) = m/s).
650    #[cfg(feature = "std")]
651    pub fn sqrt(self) -> Velocity {
652        Velocity(self.0.sqrt())
653    }
654}
655
656impl_quantity_display!(SpecificEnergy, "J/kg");
657
658impl_common_ops!(SpecificEnergy);