unitforge 0.5.6__tar.gz → 0.5.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. {unitforge-0.5.6 → unitforge-0.5.7}/Cargo.lock +2 -2
  2. {unitforge-0.5.6 → unitforge-0.5.7}/Cargo.toml +2 -2
  3. {unitforge-0.5.6 → unitforge-0.5.7}/PKG-INFO +1 -1
  4. {unitforge-0.5.6 → unitforge-0.5.7}/python/unitforge/__init__.pyi +51 -1
  5. {unitforge-0.5.6 → unitforge-0.5.7}/src/impl_macros.rs +39 -5
  6. {unitforge-0.5.6 → unitforge-0.5.7}/src/internal.rs +7 -0
  7. {unitforge-0.5.6 → unitforge-0.5.7}/src/lib.rs +2 -0
  8. unitforge-0.5.7/src/quantities/inverse_temperature.rs +39 -0
  9. unitforge-0.5.7/src/quantities/temperature.rs +59 -0
  10. {unitforge-0.5.6 → unitforge-0.5.7}/src/relations.rs +1 -0
  11. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantities.rs +56 -0
  12. {unitforge-0.5.6 → unitforge-0.5.7}/unitforge_macros/Cargo.toml +1 -1
  13. unitforge-0.5.6/src/quantities/temperature.rs +0 -38
  14. {unitforge-0.5.6 → unitforge-0.5.7}/.gitignore +0 -0
  15. {unitforge-0.5.6 → unitforge-0.5.7}/.gitlab-ci.yml +0 -0
  16. {unitforge-0.5.6 → unitforge-0.5.7}/Readme.md +0 -0
  17. {unitforge-0.5.6 → unitforge-0.5.7}/Readme_py.md +0 -0
  18. {unitforge-0.5.6 → unitforge-0.5.7}/install_hooks.sh +0 -0
  19. {unitforge-0.5.6 → unitforge-0.5.7}/pyproject.toml +0 -0
  20. {unitforge-0.5.6 → unitforge-0.5.7}/python/unitforge/__init__.py +0 -0
  21. {unitforge-0.5.6 → unitforge-0.5.7}/python/unitforge/py.typed +0 -0
  22. {unitforge-0.5.6 → unitforge-0.5.7}/scripts/generate_python_stubs.py +0 -0
  23. {unitforge-0.5.6 → unitforge-0.5.7}/src/errors.rs +0 -0
  24. {unitforge-0.5.6 → unitforge-0.5.7}/src/prelude.rs +0 -0
  25. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/acceleration.rs +0 -0
  26. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/angle.rs +0 -0
  27. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/angular_acceleration.rs +0 -0
  28. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/angular_velocity.rs +0 -0
  29. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/area.rs +0 -0
  30. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/area_of_moment.rs +0 -0
  31. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/charge.rs +0 -0
  32. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/compliance.rs +0 -0
  33. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/density.rs +0 -0
  34. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/density_rate.rs +0 -0
  35. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/density_temperature.rs +0 -0
  36. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/density_temperature_rate.rs +0 -0
  37. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/distance.rs +0 -0
  38. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force.rs +0 -0
  39. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_area.rs +0 -0
  40. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_distance.rs +0 -0
  41. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_div_distance_power_four.rs +0 -0
  42. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_per_volume.rs +0 -0
  43. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_stress.rs +0 -0
  44. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/force_volume.rs +0 -0
  45. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/frequency.rs +0 -0
  46. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/inverse_angle.rs +0 -0
  47. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/inverse_area.rs +0 -0
  48. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/inverse_distance.rs +0 -0
  49. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/inverse_stress.rs +0 -0
  50. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/mass.rs +0 -0
  51. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/mass_per_distance.rs +0 -0
  52. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/molar_mass.rs +0 -0
  53. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/pressure_rate.rs +0 -0
  54. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/rotational_stiffness.rs +0 -0
  55. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/specific_gas_constant.rs +0 -0
  56. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stiffness.rs +0 -0
  57. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/strain.rs +0 -0
  58. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/strain_squared.rs +0 -0
  59. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress.rs +0 -0
  60. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress_cubed.rs +0 -0
  61. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress_per_temperature.rs +0 -0
  62. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress_rate.rs +0 -0
  63. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress_rate_per_temperature.rs +0 -0
  64. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/stress_squared.rs +0 -0
  65. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/time.rs +0 -0
  66. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/universal_gas_constant.rs +0 -0
  67. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/velocity.rs +0 -0
  68. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/velocity_squared.rs +0 -0
  69. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/voltage.rs +0 -0
  70. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities/volume.rs +0 -0
  71. {unitforge-0.5.6 → unitforge-0.5.7}/src/quantities.template.rs +0 -0
  72. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/bindings/matrix3_py.template.rs +0 -0
  73. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/bindings/vector3_py.template.rs +0 -0
  74. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/matrix2.rs +0 -0
  75. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/matrix2x3.rs +0 -0
  76. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/matrix3.rs +0 -0
  77. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/matrix3x2.rs +0 -0
  78. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/vector2.rs +0 -0
  79. {unitforge-0.5.6 → unitforge-0.5.7}/src/small_linalg/vector3.rs +0 -0
  80. {unitforge-0.5.6 → unitforge-0.5.7}/test.sh +0 -0
  81. {unitforge-0.5.6 → unitforge-0.5.7}/tests/common/mod.rs +0 -0
  82. {unitforge-0.5.6 → unitforge-0.5.7}/tests/matrix2.rs +0 -0
  83. {unitforge-0.5.6 → unitforge-0.5.7}/tests/matrix2x3.rs +0 -0
  84. {unitforge-0.5.6 → unitforge-0.5.7}/tests/matrix3.rs +0 -0
  85. {unitforge-0.5.6 → unitforge-0.5.7}/tests/matrix3x2.rs +0 -0
  86. {unitforge-0.5.6 → unitforge-0.5.7}/tests/ndarray_dot.rs +0 -0
  87. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/constants.rs +0 -0
  88. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/construction.rs +0 -0
  89. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/enum_iter.rs +0 -0
  90. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/metadata.rs +0 -0
  91. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/normalization.rs +0 -0
  92. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/plain_storage.rs +0 -0
  93. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/relations.rs +0 -0
  94. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/runtime_quantity.rs +0 -0
  95. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core/scalar_edges.rs +0 -0
  96. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_core.rs +0 -0
  97. {unitforge-0.5.6 → unitforge-0.5.7}/tests/quantity_serde.rs +0 -0
  98. {unitforge-0.5.6 → unitforge-0.5.7}/tests/small_linalg_edge_cases.rs +0 -0
  99. {unitforge-0.5.6 → unitforge-0.5.7}/tests/small_linalg_interfaces.rs +0 -0
  100. {unitforge-0.5.6 → unitforge-0.5.7}/tests/test_quantities.py +0 -0
  101. {unitforge-0.5.6 → unitforge-0.5.7}/tests/test_small_linalg.py +0 -0
  102. {unitforge-0.5.6 → unitforge-0.5.7}/tests/typecheck/mypy_smoke.py +0 -0
  103. {unitforge-0.5.6 → unitforge-0.5.7}/tests/vector2.rs +0 -0
  104. {unitforge-0.5.6 → unitforge-0.5.7}/tests/vector3.rs +0 -0
  105. {unitforge-0.5.6 → unitforge-0.5.7}/unitforge_macros/src/lib.rs +0 -0
@@ -351,7 +351,7 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
351
351
 
352
352
  [[package]]
353
353
  name = "unitforge"
354
- version = "0.5.6"
354
+ version = "0.5.7"
355
355
  dependencies = [
356
356
  "ndarray",
357
357
  "num-traits",
@@ -367,7 +367,7 @@ dependencies = [
367
367
 
368
368
  [[package]]
369
369
  name = "unitforge_macros"
370
- version = "0.5.6"
370
+ version = "0.5.7"
371
371
  dependencies = [
372
372
  "regex",
373
373
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "unitforge"
3
- version = "0.5.6"
3
+ version = "0.5.7"
4
4
  edition = "2021"
5
5
  authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
6
6
  description = "A library for unit and quantity consistent computations in Rust"
@@ -20,7 +20,7 @@ pyo3 = { version = "0.28.3", features = ["extension-module", "auto-initialize",
20
20
  numpy = { version = "0.28.0", optional = true }
21
21
  strum = { version = "0.28.0", optional = true }
22
22
  strum_macros = { version = "0.28.0", optional = true }
23
- unitforge_macros = { version = "0.5.6", path = "unitforge_macros" }
23
+ unitforge_macros = { version = "0.5.7", path = "unitforge_macros" }
24
24
 
25
25
  [features]
26
26
  default = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitforge
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1707,6 +1707,45 @@ class InverseStress:
1707
1707
  def __str__(self) -> str: ...
1708
1708
  def __rtruediv__(self, lhs: float) -> "Stress": ...
1709
1709
 
1710
+ class InverseTemperatureUnit:
1711
+ _K: ClassVar["InverseTemperatureUnit"]
1712
+ _mK: ClassVar["InverseTemperatureUnit"]
1713
+
1714
+ class InverseTemperature:
1715
+ def __init__(self, value: float, unit: "InverseTemperatureUnit") -> None: ...
1716
+ @classmethod
1717
+ def zero(cls: Type["InverseTemperature"]) -> "InverseTemperature": ...
1718
+ def to(self, unit: "InverseTemperatureUnit") -> float: ...
1719
+ def close_abs(self, other: "InverseTemperature", threshold: "InverseTemperature") -> bool: ...
1720
+ def close_rel(self, other: "InverseTemperature", threshold: float) -> bool: ...
1721
+ def __neg__(self) -> "InverseTemperature": ...
1722
+ def __abs__(self) -> "InverseTemperature": ...
1723
+ def __add__(self, rhs: "InverseTemperature") -> "InverseTemperature": ...
1724
+ def __sub__(self, rhs: "InverseTemperature") -> "InverseTemperature": ...
1725
+ @overload
1726
+ def __mul__(self, rhs: float) -> "InverseTemperature": ...
1727
+ @overload
1728
+ def __mul__(self, rhs: Vector3) -> Vector3: ...
1729
+ @overload
1730
+ def __mul__(self, rhs: "Temperature") -> "Strain": ...
1731
+ @overload
1732
+ def __mul__(self, rhs: Any) -> Any: ...
1733
+ @overload
1734
+ def __truediv__(self, rhs: float) -> "InverseTemperature": ...
1735
+ @overload
1736
+ def __truediv__(self, rhs: "InverseTemperature") -> float: ...
1737
+ @overload
1738
+ def __truediv__(self, rhs: Any) -> Any: ...
1739
+ @overload
1740
+ def __mod__(self, rhs: float) -> "InverseTemperature": ...
1741
+ @overload
1742
+ def __mod__(self, rhs: "InverseTemperature") -> float: ...
1743
+ @overload
1744
+ def __mod__(self, rhs: Any) -> Any: ...
1745
+ def __rmul__(self, rhs: float) -> "InverseTemperature": ...
1746
+ def __repr__(self) -> str: ...
1747
+ def __str__(self) -> str: ...
1748
+
1710
1749
  class MassUnit:
1711
1750
  mg: ClassVar["MassUnit"]
1712
1751
  g: ClassVar["MassUnit"]
@@ -2074,14 +2113,22 @@ class Strain:
2074
2113
  @overload
2075
2114
  def __truediv__(self, rhs: float) -> "Strain": ...
2076
2115
  @overload
2116
+ def __truediv__(self, rhs: "InverseTemperature") -> "Temperature": ...
2117
+ @overload
2077
2118
  def __truediv__(self, rhs: "Strain") -> float: ...
2078
2119
  @overload
2120
+ def __truediv__(self, rhs: "Temperature") -> "InverseTemperature": ...
2121
+ @overload
2079
2122
  def __truediv__(self, rhs: Any) -> Any: ...
2080
2123
  @overload
2081
2124
  def __mod__(self, rhs: float) -> "Strain": ...
2082
2125
  @overload
2126
+ def __mod__(self, rhs: "InverseTemperature") -> "Temperature": ...
2127
+ @overload
2083
2128
  def __mod__(self, rhs: "Strain") -> float: ...
2084
2129
  @overload
2130
+ def __mod__(self, rhs: "Temperature") -> "InverseTemperature": ...
2131
+ @overload
2085
2132
  def __mod__(self, rhs: Any) -> Any: ...
2086
2133
  def __rmul__(self, rhs: float) -> "Strain": ...
2087
2134
  def __repr__(self) -> str: ...
@@ -2520,7 +2567,8 @@ class StressSquared:
2520
2567
  class TemperatureUnit:
2521
2568
  K: ClassVar["TemperatureUnit"]
2522
2569
  mK: ClassVar["TemperatureUnit"]
2523
- kK: ClassVar["TemperatureUnit"]
2570
+ C: ClassVar["TemperatureUnit"]
2571
+ F: ClassVar["TemperatureUnit"]
2524
2572
 
2525
2573
  class Temperature:
2526
2574
  def __init__(self, value: float, unit: "TemperatureUnit") -> None: ...
@@ -2542,6 +2590,8 @@ class Temperature:
2542
2590
  @overload
2543
2591
  def __mul__(self, rhs: "DensityRate") -> "DensityTemperatureRate": ...
2544
2592
  @overload
2593
+ def __mul__(self, rhs: "InverseTemperature") -> "Strain": ...
2594
+ @overload
2545
2595
  def __mul__(self, rhs: "SpecificGasConstant") -> "VelocitySquared": ...
2546
2596
  @overload
2547
2597
  def __mul__(self, rhs: "StressPerTemperature") -> "Stress": ...
@@ -1,6 +1,18 @@
1
1
  #[macro_export]
2
2
  macro_rules! impl_quantity {
3
3
  ($type:ident, $unit:ident, $display_units:expr) => {
4
+ $crate::impl_quantity!(
5
+ $type,
6
+ $unit,
7
+ $display_units,
8
+ $crate::internal::zero_offset_const
9
+ );
10
+ };
11
+ // `$offset_fn` converts a unit variant to the additive term (in base SI
12
+ // units) needed by affine units such as Celsius/Fahrenheit, where
13
+ // `base_value = value * scale + offset`. It defaults to always-zero above,
14
+ // so every purely multiplicative quantity is unaffected.
15
+ ($type:ident, $unit:ident, $display_units:expr, $offset_fn:path) => {
4
16
  use core::cmp::Ordering;
5
17
  use core::fmt;
6
18
  use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
@@ -136,6 +148,16 @@ macro_rules! impl_quantity {
136
148
  impl $type {
137
149
  pub const fn new(value: f64, unit: $unit) -> Self {
138
150
  let (unit_multiplier, unit_power) = unit.base_per_x_const();
151
+ // Affine units fold their offset in here: shifting `value` by
152
+ // `offset / scale` before the existing pure-scale math below
153
+ // yields `value * scale + offset` in base SI units. A no-op
154
+ // (adds 0.0) for every purely multiplicative unit.
155
+ let value = value
156
+ + $offset_fn(unit)
157
+ / $crate::internal::quantity_parts_to_value_const(
158
+ unit_multiplier,
159
+ unit_power,
160
+ );
139
161
  #[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
140
162
  {
141
163
  let (multiplier, power) =
@@ -288,19 +310,31 @@ macro_rules! impl_quantity {
288
310
  }
289
311
 
290
312
  fn to(&self, unit: Self::Unit) -> f64 {
313
+ let offset = $offset_fn(unit);
291
314
  #[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
292
315
  {
293
- if self.multiplier == 0.0 {
294
- return 0.0;
316
+ if offset == 0.0 {
317
+ if self.multiplier == 0.0 {
318
+ return 0.0;
319
+ }
320
+ let (unit_multiplier, unit_power) = unit.base_per_x();
321
+ return self.multiplier / unit_multiplier
322
+ * $crate::internal::pow10_delta(self.power, unit_power);
295
323
  }
324
+ // Affine units (non-zero offset) go through `as_f64()`
325
+ // instead of the mantissa/power fast path above, since
326
+ // subtracting `offset` needs the plain base-SI value.
296
327
  let (unit_multiplier, unit_power) = unit.base_per_x();
297
- self.multiplier / unit_multiplier
298
- * $crate::internal::pow10_delta(self.power, unit_power)
328
+ (self.as_f64() - offset)
329
+ / $crate::internal::quantity_parts_to_value_const(
330
+ unit_multiplier,
331
+ unit_power,
332
+ )
299
333
  }
300
334
  #[cfg(any(feature = "storage-f64", feature = "storage-f32"))]
301
335
  {
302
336
  let (unit_multiplier, unit_power) = unit.base_per_x();
303
- self.as_f64()
337
+ (self.as_f64() - offset)
304
338
  / $crate::internal::quantity_parts_to_value(unit_multiplier, unit_power)
305
339
  }
306
340
  }
@@ -7,6 +7,13 @@ const SQRT_10: f64 = 3.162_277_660_168_379;
7
7
  #[cfg(not(any(feature = "storage-f64", feature = "storage-f32")))]
8
8
  const INV_SQRT_10: f64 = 0.31622776601683794;
9
9
 
10
+ /// Default `PhysicsUnit` offset hook used by `impl_quantity!` for purely
11
+ /// multiplicative units. Affine units (e.g. temperature's Celsius/Fahrenheit)
12
+ /// supply their own `const fn(Unit) -> f64` instead.
13
+ pub const fn zero_offset_const<U: Copy>(_unit: U) -> f64 {
14
+ 0.0
15
+ }
16
+
10
17
  const fn clamp_power_const(power: i64) -> i32 {
11
18
  if power < -(MAX_ABS_QUANTITY_POWER as i64) {
12
19
  -MAX_ABS_QUANTITY_POWER
@@ -135,6 +135,8 @@ pub mod quantities {
135
135
  pub use stress_squared::*;
136
136
  pub mod temperature;
137
137
  pub use temperature::*;
138
+ pub mod inverse_temperature;
139
+ pub use inverse_temperature::*;
138
140
  pub mod time;
139
141
  pub use time::*;
140
142
  pub mod universal_gas_constant;
@@ -0,0 +1,39 @@
1
+ use crate::impl_macros::macros::*;
2
+
3
+ #[cfg_attr(feature = "strum", derive(EnumIter))]
4
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5
+ #[derive(Copy, Clone, PartialEq, Debug, Hash, Eq)]
6
+ #[cfg_attr(feature = "pyo3", pyclass(eq, eq_int, from_py_object))]
7
+ pub enum InverseTemperatureUnit {
8
+ _K,
9
+ _mK,
10
+ }
11
+
12
+ impl InverseTemperatureUnit {
13
+ pub const fn base_per_x_const(self) -> (f64, i32) {
14
+ match self {
15
+ InverseTemperatureUnit::_K => (1., 0),
16
+ InverseTemperatureUnit::_mK => (1., 3),
17
+ }
18
+ }
19
+ }
20
+
21
+ impl PhysicsUnit for InverseTemperatureUnit {
22
+ fn name(&self) -> &str {
23
+ match &self {
24
+ InverseTemperatureUnit::_K => "1/K",
25
+ InverseTemperatureUnit::_mK => "1/mK",
26
+ }
27
+ }
28
+
29
+ fn base_per_x(&self) -> (f64, i32) {
30
+ (*self).base_per_x_const()
31
+ }
32
+ }
33
+
34
+ impl_quantity!(
35
+ InverseTemperature,
36
+ InverseTemperatureUnit,
37
+ [InverseTemperatureUnit::_K]
38
+ );
39
+ impl_div_with_self_to_f64!(InverseTemperature);
@@ -0,0 +1,59 @@
1
+ use crate::impl_macros::macros::*;
2
+
3
+ #[cfg_attr(feature = "strum", derive(EnumIter))]
4
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5
+ #[derive(Copy, Clone, PartialEq, Debug, Hash, Eq)]
6
+ #[cfg_attr(feature = "pyo3", pyclass(eq, eq_int, from_py_object))]
7
+ pub enum TemperatureUnit {
8
+ K,
9
+ mK,
10
+ C,
11
+ F,
12
+ }
13
+
14
+ impl TemperatureUnit {
15
+ /// Scale only, in the sense of `kelvin = value * multiplier * 10^power + base_offset_const()`.
16
+ /// Celsius shares Kelvin's scale; Fahrenheit's degree is 5/9 of a Kelvin.
17
+ pub const fn base_per_x_const(self) -> (f64, i32) {
18
+ match self {
19
+ TemperatureUnit::K => (1., 0),
20
+ TemperatureUnit::mK => (1., -3),
21
+ TemperatureUnit::C => (1., 0),
22
+ TemperatureUnit::F => (5. / 9., 0),
23
+ }
24
+ }
25
+
26
+ /// Additive term (kelvin) applied on top of `base_per_x_const`'s scaling,
27
+ /// i.e. `kelvin = value * scale + base_offset_const()`. Zero for every
28
+ /// linear unit; only Celsius/Fahrenheit are affine.
29
+ pub const fn base_offset_const(self) -> f64 {
30
+ match self {
31
+ TemperatureUnit::K | TemperatureUnit::mK => 0.0,
32
+ TemperatureUnit::C => 273.15,
33
+ TemperatureUnit::F => 273.15 - 32. * 5. / 9.,
34
+ }
35
+ }
36
+ }
37
+
38
+ impl PhysicsUnit for TemperatureUnit {
39
+ fn name(&self) -> &str {
40
+ match &self {
41
+ TemperatureUnit::K => "K",
42
+ TemperatureUnit::mK => "mK",
43
+ TemperatureUnit::C => "°C",
44
+ TemperatureUnit::F => "°F",
45
+ }
46
+ }
47
+
48
+ fn base_per_x(&self) -> (f64, i32) {
49
+ (*self).base_per_x_const()
50
+ }
51
+ }
52
+
53
+ impl_quantity!(
54
+ Temperature,
55
+ TemperatureUnit,
56
+ [TemperatureUnit::K],
57
+ TemperatureUnit::base_offset_const
58
+ );
59
+ impl_div_with_self_to_f64!(Temperature);
@@ -72,6 +72,7 @@ impl_mul_relation_with_other!(DensityRate, Temperature, DensityTemperatureRate);
72
72
  impl_mul_relation_with_other!(SpecificGasConstant, DensityTemperatureRate, StressRate);
73
73
  impl_mul_relation_with_other!(DensityRate, SpecificGasConstant, StressRatePerTemperature);
74
74
  impl_mul_relation_with_other!(StressRatePerTemperature, Temperature, StressRate);
75
+ impl_mul_relation_with_other!(InverseTemperature, Temperature, Strain);
75
76
  impl_mul!(InverseDistance, Distance, f64);
76
77
  impl_mul!(InverseAngle, Angle, f64);
77
78
  impl_mul!(InverseStress, Stress, f64);
@@ -262,6 +262,62 @@ mod test_thermo_and_rates {
262
262
  ));
263
263
  }
264
264
 
265
+ #[test]
266
+ fn temperature_converts_affine_celsius_and_fahrenheit_units() {
267
+ let boiling = Temperature::new(100.0, TemperatureUnit::C);
268
+ assert!(crate::approx_eq(
269
+ boiling.to(TemperatureUnit::K),
270
+ 373.15,
271
+ 1e-9
272
+ ));
273
+ assert!(crate::approx_eq(
274
+ boiling.to(TemperatureUnit::F),
275
+ 212.0,
276
+ 1e-9
277
+ ));
278
+
279
+ let freezing = Temperature::new(32.0, TemperatureUnit::F);
280
+ assert!(crate::approx_eq(freezing.to(TemperatureUnit::C), 0.0, 1e-9));
281
+ assert!(crate::approx_eq(
282
+ freezing.to(TemperatureUnit::K),
283
+ 273.15,
284
+ 1e-9
285
+ ));
286
+
287
+ let absolute_zero = Temperature::new(-273.15, TemperatureUnit::C);
288
+ assert!(crate::approx_eq(
289
+ absolute_zero.to(TemperatureUnit::K),
290
+ 0.0,
291
+ 1e-9
292
+ ));
293
+
294
+ let minus_forty = Temperature::new(-40.0, TemperatureUnit::C);
295
+ assert!(crate::approx_eq(
296
+ minus_forty.to(TemperatureUnit::F),
297
+ -40.0,
298
+ 1e-9
299
+ ));
300
+
301
+ // Arithmetic stays defined purely in Kelvin, unaffected by the unit
302
+ // used at construction time.
303
+ let diff = boiling - freezing;
304
+ assert!(crate::approx_eq(diff.to(TemperatureUnit::K), 100.0, 1e-9));
305
+ }
306
+
307
+ #[test]
308
+ fn inverse_temperature_converts_units_and_relates_to_temperature() {
309
+ let expansion_coefficient = InverseTemperature::new(2.0, InverseTemperatureUnit::_mK);
310
+ assert!(crate::approx_eq(
311
+ expansion_coefficient.to(InverseTemperatureUnit::_K),
312
+ 2000.0,
313
+ 1e-9
314
+ ));
315
+
316
+ let temperature = Temperature::new(0.5, TemperatureUnit::mK);
317
+ let strain = expansion_coefficient * temperature;
318
+ assert!(crate::approx_eq(strain.to(StrainUnit::m_m), 1.0, 1e-9));
319
+ }
320
+
265
321
  #[test]
266
322
  fn molar_mass_converts_common_units() {
267
323
  let molar_mass = MolarMass::new(28.97, MolarMassUnit::g_mol);
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "unitforge_macros"
3
- version = "0.5.6"
3
+ version = "0.5.7"
4
4
  edition = "2021"
5
5
  authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
6
6
  description = "Proc macros for generating units, quantities, and Python bindings in unitforge"
@@ -1,38 +0,0 @@
1
- use crate::impl_macros::macros::*;
2
-
3
- #[cfg_attr(feature = "strum", derive(EnumIter))]
4
- #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5
- #[derive(Copy, Clone, PartialEq, Debug, Hash, Eq)]
6
- #[cfg_attr(feature = "pyo3", pyclass(eq, eq_int, from_py_object))]
7
- pub enum TemperatureUnit {
8
- K,
9
- mK,
10
- kK,
11
- }
12
-
13
- impl TemperatureUnit {
14
- pub const fn base_per_x_const(self) -> (f64, i32) {
15
- match self {
16
- TemperatureUnit::K => (1., 0),
17
- TemperatureUnit::mK => (1., -3),
18
- TemperatureUnit::kK => (1., 3),
19
- }
20
- }
21
- }
22
-
23
- impl PhysicsUnit for TemperatureUnit {
24
- fn name(&self) -> &str {
25
- match &self {
26
- TemperatureUnit::K => "K",
27
- TemperatureUnit::mK => "mK",
28
- TemperatureUnit::kK => "kK",
29
- }
30
- }
31
-
32
- fn base_per_x(&self) -> (f64, i32) {
33
- (*self).base_per_x_const()
34
- }
35
- }
36
-
37
- impl_quantity!(Temperature, TemperatureUnit, [TemperatureUnit::K]);
38
- impl_div_with_self_to_f64!(Temperature);
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes