unxt 0.1.dev463__py3-none-any.whl
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.
Potentially problematic release.
This version of unxt might be problematic. Click here for more details.
- unxt/__init__.py +79 -0
- unxt/_interop/__init__.py +18 -0
- unxt/_interop/optional_deps.py +13 -0
- unxt/_interop/unxt_interop_astropy/__init__.py +5 -0
- unxt/_interop/unxt_interop_astropy/custom_types.py +12 -0
- unxt/_interop/unxt_interop_astropy/dimensions.py +53 -0
- unxt/_interop/unxt_interop_astropy/quantity.py +308 -0
- unxt/_interop/unxt_interop_astropy/units.py +111 -0
- unxt/_interop/unxt_interop_gala/__init__.py +5 -0
- unxt/_interop/unxt_interop_gala/unitsystems.py +108 -0
- unxt/_interop/unxt_interop_mpl/__init__.py +81 -0
- unxt/_src/__init__.py +6 -0
- unxt/_src/dimensions.py +400 -0
- unxt/_src/experimental.py +234 -0
- unxt/_src/quantity/__init__.py +24 -0
- unxt/_src/quantity/angle.py +58 -0
- unxt/_src/quantity/base.py +1160 -0
- unxt/_src/quantity/base_angle.py +89 -0
- unxt/_src/quantity/base_parametric.py +290 -0
- unxt/_src/quantity/flag.py +137 -0
- unxt/_src/quantity/mixins.py +238 -0
- unxt/_src/quantity/quantity.py +123 -0
- unxt/_src/quantity/register_api.py +383 -0
- unxt/_src/quantity/register_conversions.py +95 -0
- unxt/_src/quantity/register_dispatches.py +246 -0
- unxt/_src/quantity/register_primitives.py +5075 -0
- unxt/_src/quantity/static_quantity.py +90 -0
- unxt/_src/quantity/unchecked.py +45 -0
- unxt/_src/quantity/value.py +289 -0
- unxt/_src/units.py +103 -0
- unxt/_src/unitsystems/__init__.py +40 -0
- unxt/_src/unitsystems/base.py +258 -0
- unxt/_src/unitsystems/builtin.py +190 -0
- unxt/_src/unitsystems/builtin_dimensions.py +21 -0
- unxt/_src/unitsystems/compare.py +13 -0
- unxt/_src/unitsystems/core.py +307 -0
- unxt/_src/unitsystems/flags.py +80 -0
- unxt/_src/unitsystems/realizations.py +73 -0
- unxt/_src/unitsystems/utils.py +95 -0
- unxt/_src/utils.py +112 -0
- unxt/_version.py +34 -0
- unxt/_version.pyi +2 -0
- unxt/dims.py +19 -0
- unxt/py.typed +0 -0
- unxt/quantity.py +172 -0
- unxt/setup_package.py +60 -0
- unxt/units.py +19 -0
- unxt/unitsystems.py +67 -0
- unxt-0.1.dev463.dist-info/METADATA +533 -0
- unxt-0.1.dev463.dist-info/RECORD +52 -0
- unxt-0.1.dev463.dist-info/WHEEL +4 -0
- unxt-0.1.dev463.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Tools for representing systems of units using ``astropy.units``."""
|
|
2
|
+
|
|
3
|
+
__all__ = ("UNITSYSTEMS_REGISTRY", "AbstractUnitSystem")
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
from typing import ClassVar, get_args, get_type_hints
|
|
9
|
+
|
|
10
|
+
import jax.tree_util as jtu
|
|
11
|
+
from astropy.units import PhysicalType, UnitBase as AstropyUnitBase
|
|
12
|
+
from astropy.units.physical import _physical_unit_mapping
|
|
13
|
+
|
|
14
|
+
from is_annotated import isannotated
|
|
15
|
+
|
|
16
|
+
from .utils import parse_dimlike_name
|
|
17
|
+
from unxt.dims import AbstractDimension, dimension
|
|
18
|
+
from unxt.units import AbstractUnit, unit
|
|
19
|
+
|
|
20
|
+
Unit = AstropyUnitBase
|
|
21
|
+
|
|
22
|
+
_UNITSYSTEMS_REGISTRY: dict[
|
|
23
|
+
tuple[AbstractDimension, ...], type["AbstractUnitSystem"]
|
|
24
|
+
] = {}
|
|
25
|
+
UNITSYSTEMS_REGISTRY = MappingProxyType(_UNITSYSTEMS_REGISTRY)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Register AbstractUnitSystem as a static PyTree node
|
|
29
|
+
# This ensures unit systems are treated as constants in JAX transformations
|
|
30
|
+
@jtu.register_static
|
|
31
|
+
@dataclass(frozen=True, slots=True, eq=True)
|
|
32
|
+
class AbstractUnitSystem:
|
|
33
|
+
"""Represents a system of units.
|
|
34
|
+
|
|
35
|
+
This class behaves like a dictionary with keys set by physical types (i.e.
|
|
36
|
+
"length", "velocity", "energy", etc.). If a unit for a particular physical
|
|
37
|
+
type is not specified on creation, a composite unit will be created with the
|
|
38
|
+
base units. See the examples below for some demonstrations.
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
If only base units are specified, any physical type specified as a key to
|
|
43
|
+
this object will be composed out of the base units:
|
|
44
|
+
|
|
45
|
+
>>> from unxt import unitsystem
|
|
46
|
+
>>> usys = unitsystem("m", "s", "kg", "radian")
|
|
47
|
+
>>> usys
|
|
48
|
+
unitsystem(m, s, kg, rad)
|
|
49
|
+
|
|
50
|
+
>>> usys["velocity"]
|
|
51
|
+
Unit("m / s")
|
|
52
|
+
|
|
53
|
+
This unit system defines energy:
|
|
54
|
+
|
|
55
|
+
>>> usys = unitsystem("m", "s", "kg", "radian", "erg")
|
|
56
|
+
>>> usys["energy"]
|
|
57
|
+
Unit("erg")
|
|
58
|
+
|
|
59
|
+
This is useful for Galactic dynamics where lengths and times are usually
|
|
60
|
+
given in terms of ``kpc`` and ``Myr``, but velocities are often specified in
|
|
61
|
+
``km/s``:
|
|
62
|
+
|
|
63
|
+
>>> usys = unitsystem("kpc", "Myr", "Msun", "radian", "km / s")
|
|
64
|
+
>>> usys["velocity"]
|
|
65
|
+
Unit("km / s")
|
|
66
|
+
|
|
67
|
+
Unit systems can be hashed:
|
|
68
|
+
|
|
69
|
+
>>> isinstance(hash(usys), int)
|
|
70
|
+
True
|
|
71
|
+
|
|
72
|
+
And iterated over:
|
|
73
|
+
|
|
74
|
+
>>> [x for x in usys]
|
|
75
|
+
[Unit("kpc"), Unit("Myr"), Unit("solMass"), Unit("rad"), Unit("km / s")]
|
|
76
|
+
|
|
77
|
+
With length equal to the number of base units
|
|
78
|
+
|
|
79
|
+
>>> len(usys)
|
|
80
|
+
5
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
# ===============================================================
|
|
85
|
+
# Class-level
|
|
86
|
+
|
|
87
|
+
_base_field_names: ClassVar[tuple[str, ...]]
|
|
88
|
+
_base_dimensions: ClassVar[tuple[AbstractDimension, ...]]
|
|
89
|
+
|
|
90
|
+
def __init_subclass__(cls) -> None:
|
|
91
|
+
# Register class with a tuple of it's dimensions.
|
|
92
|
+
# This requires processing the type hints, not the dataclass fields
|
|
93
|
+
# since those are made after the original class is defined.
|
|
94
|
+
field_names, dims = parse_field_names_and_dimensions(cls)
|
|
95
|
+
|
|
96
|
+
# Check the unitsystem is not already registered
|
|
97
|
+
# If `make_dataclass(slots=True)` then the class is made twice, the
|
|
98
|
+
# second time adding the `__slots__` attribute
|
|
99
|
+
if dims in _UNITSYSTEMS_REGISTRY and "__slots__" not in cls.__dict__:
|
|
100
|
+
msg = f"Unit system with dimensions {dims} already exists."
|
|
101
|
+
raise ValueError(msg)
|
|
102
|
+
|
|
103
|
+
# Add attributes to the class
|
|
104
|
+
cls._base_field_names = tuple(field_names)
|
|
105
|
+
cls._base_dimensions = dims
|
|
106
|
+
|
|
107
|
+
_UNITSYSTEMS_REGISTRY[dims] = cls
|
|
108
|
+
|
|
109
|
+
# ===============================================================
|
|
110
|
+
# USys API
|
|
111
|
+
|
|
112
|
+
@property # TODO: classproperty
|
|
113
|
+
def base_dimensions(self) -> tuple[AbstractDimension, ...]:
|
|
114
|
+
"""Dimensions required for the unit system."""
|
|
115
|
+
return self._base_dimensions
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def base_units(self) -> tuple[AbstractUnit, ...]:
|
|
119
|
+
"""List of core units."""
|
|
120
|
+
return tuple(getattr(self, k) for k in self._base_field_names)
|
|
121
|
+
|
|
122
|
+
# ===============================================================
|
|
123
|
+
# Python stuff
|
|
124
|
+
|
|
125
|
+
def __getitem__(self, key: AbstractDimension | str) -> AbstractUnit:
|
|
126
|
+
"""Get the unit for a given physical type.
|
|
127
|
+
|
|
128
|
+
Examples
|
|
129
|
+
--------
|
|
130
|
+
>>> from unxt import unitsystem
|
|
131
|
+
>>> usys = unitsystem("m", "s", "kg", "radian")
|
|
132
|
+
|
|
133
|
+
Something in the base dimensions:
|
|
134
|
+
|
|
135
|
+
>>> usys["length"]
|
|
136
|
+
Unit("m")
|
|
137
|
+
|
|
138
|
+
Something not in the base dimensions:
|
|
139
|
+
|
|
140
|
+
>>> usys["velocity"]
|
|
141
|
+
Unit("m / s")
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
key = dimension(key)
|
|
145
|
+
if key in self.base_dimensions:
|
|
146
|
+
return getattr(self, parse_dimlike_name(key))
|
|
147
|
+
|
|
148
|
+
out: AbstractUnit
|
|
149
|
+
for k, v in _physical_unit_mapping.items():
|
|
150
|
+
if v == key:
|
|
151
|
+
out = unit(" ".join([f"{x}**{y}" for x, y in k]))
|
|
152
|
+
break
|
|
153
|
+
# IDK if it's possible to get here
|
|
154
|
+
else:
|
|
155
|
+
msg = f"Physical type {key!r} doesn't exist in unit registry." # pragma: no cover # noqa: E501
|
|
156
|
+
raise ValueError(msg) # pragma: no cover
|
|
157
|
+
|
|
158
|
+
out = out.decompose(self.base_units)
|
|
159
|
+
out._scale = 1.0 # noqa: SLF001
|
|
160
|
+
return out
|
|
161
|
+
|
|
162
|
+
def __len__(self) -> int:
|
|
163
|
+
"""Return the number of base units in the system.
|
|
164
|
+
|
|
165
|
+
Examples
|
|
166
|
+
--------
|
|
167
|
+
>>> from unxt import unitsystem
|
|
168
|
+
>>> usys = unitsystem("m", "s", "kg", "radian")
|
|
169
|
+
>>> len(usys)
|
|
170
|
+
4
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
# Note: This is required for q.decompose(usys) to work, where q is a Quantity
|
|
174
|
+
return len(self.base_dimensions)
|
|
175
|
+
|
|
176
|
+
# TODO: should this be changed to _base_field_names -> Iterator[str]?
|
|
177
|
+
def __iter__(self) -> Iterator[AbstractUnit]:
|
|
178
|
+
"""Iterate over the base units.
|
|
179
|
+
|
|
180
|
+
This is different than a dictionary, which would iterate over the
|
|
181
|
+
physical types (the keys).
|
|
182
|
+
|
|
183
|
+
Examples
|
|
184
|
+
--------
|
|
185
|
+
>>> from unxt import unitsystem
|
|
186
|
+
>>> usys = unitsystem("m", "s", "kg", "radian")
|
|
187
|
+
>>> list(iter(usys))
|
|
188
|
+
[Unit("m"), Unit("s"), Unit("kg"), Unit("rad")]
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
yield from self.base_units
|
|
192
|
+
|
|
193
|
+
def __str__(self) -> str:
|
|
194
|
+
"""Return a string representation of the unit system.
|
|
195
|
+
|
|
196
|
+
Examples
|
|
197
|
+
--------
|
|
198
|
+
>>> from unxt import unitsystem
|
|
199
|
+
>>> usys = unitsystem("m", "s", "kg", "radian")
|
|
200
|
+
>>> str(usys)
|
|
201
|
+
'LTMAUnitSystem(length, time, mass, angle)'
|
|
202
|
+
|
|
203
|
+
"""
|
|
204
|
+
fs = ", ".join(map(str, self._base_field_names))
|
|
205
|
+
return f"{type(self).__name__}({fs})"
|
|
206
|
+
|
|
207
|
+
# ===============================================================
|
|
208
|
+
# Plum stuff
|
|
209
|
+
|
|
210
|
+
#: This tells `plum` that this type can be efficiently cached.
|
|
211
|
+
__faithful__: ClassVar[bool] = True
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# ---------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def parse_field_names_and_dimensions(
|
|
218
|
+
cls: type,
|
|
219
|
+
) -> tuple[tuple[str, ...], tuple[AbstractDimension, ...]]:
|
|
220
|
+
# Register class with a tuple of it's dimensions.
|
|
221
|
+
# This requires processing the type hints, not the dataclass fields
|
|
222
|
+
# since those are made after the original class is defined.
|
|
223
|
+
type_hints = get_type_hints(cls, include_extras=True)
|
|
224
|
+
|
|
225
|
+
field_names = []
|
|
226
|
+
dims = []
|
|
227
|
+
for name, type_hint in type_hints.items():
|
|
228
|
+
# Check it's Annotated
|
|
229
|
+
if not isannotated(type_hint):
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
# Get the arguments to Annotated
|
|
233
|
+
origin, *f_args = get_args(type_hint)
|
|
234
|
+
|
|
235
|
+
# Check that the first argument is a UnitBase
|
|
236
|
+
if not issubclass(origin, Unit):
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# Need for one of the arguments to be a Dimension
|
|
240
|
+
f_dim = [x for x in f_args if isinstance(x, PhysicalType)]
|
|
241
|
+
if not f_dim:
|
|
242
|
+
msg = f"Field {name!r} must be an Annotated with a dimension."
|
|
243
|
+
raise TypeError(msg)
|
|
244
|
+
if len(f_dim) > 1:
|
|
245
|
+
msg = (
|
|
246
|
+
f"Field {name!r} must be an Annotated with only one dimension; "
|
|
247
|
+
f"got {f_dim}"
|
|
248
|
+
)
|
|
249
|
+
raise TypeError(msg)
|
|
250
|
+
|
|
251
|
+
field_names.append(parse_dimlike_name(name))
|
|
252
|
+
dims.append(f_dim[0])
|
|
253
|
+
|
|
254
|
+
if len(set(dims)) < len(dims):
|
|
255
|
+
msg = "Some dimensions are repeated."
|
|
256
|
+
raise ValueError(msg)
|
|
257
|
+
|
|
258
|
+
return tuple(field_names), tuple(dims)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Built-in unit systems."""
|
|
2
|
+
|
|
3
|
+
__all__ = ("DimensionlessUnitSystem", "LTMAUnitSystem")
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Annotated, TypeAlias, final
|
|
7
|
+
from typing_extensions import override
|
|
8
|
+
|
|
9
|
+
import jax.tree_util as jtu
|
|
10
|
+
from astropy.units import UnitBase as AstropyUnitBase, dimensionless_unscaled
|
|
11
|
+
|
|
12
|
+
from . import builtin_dimensions as ud
|
|
13
|
+
from .base import AbstractUnitSystem
|
|
14
|
+
from unxt._src.utils import SingletonMixin
|
|
15
|
+
|
|
16
|
+
Unit: TypeAlias = AstropyUnitBase
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@jtu.register_static
|
|
20
|
+
@final
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class DimensionlessUnitSystem(SingletonMixin, AbstractUnitSystem):
|
|
23
|
+
"""A unit system with only dimensionless units.
|
|
24
|
+
|
|
25
|
+
This is a singleton class.
|
|
26
|
+
|
|
27
|
+
Examples
|
|
28
|
+
--------
|
|
29
|
+
>>> from unxt.unitsystems import DimensionlessUnitSystem
|
|
30
|
+
>>> dims1 = DimensionlessUnitSystem()
|
|
31
|
+
>>> dims2 = DimensionlessUnitSystem()
|
|
32
|
+
>>> dims1 is dims2
|
|
33
|
+
True
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
#: The dimensionless unit.
|
|
38
|
+
dimensionless: Annotated[Unit, ud.dimensionless] = dimensionless_unscaled
|
|
39
|
+
|
|
40
|
+
def __repr__(self) -> str:
|
|
41
|
+
return "DimensionlessUnitSystem()"
|
|
42
|
+
|
|
43
|
+
@override
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
return self.__repr__()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@jtu.register_static
|
|
49
|
+
@final
|
|
50
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
51
|
+
class LTMAUnitSystem(AbstractUnitSystem):
|
|
52
|
+
"""Length, time, mass, angle unit system."""
|
|
53
|
+
|
|
54
|
+
#: Units for the length dimension.
|
|
55
|
+
length: Annotated[Unit, ud.length]
|
|
56
|
+
|
|
57
|
+
#: Units for the time dimension.
|
|
58
|
+
time: Annotated[Unit, ud.time]
|
|
59
|
+
|
|
60
|
+
#: Units for the mass dimension.
|
|
61
|
+
mass: Annotated[Unit, ud.mass]
|
|
62
|
+
|
|
63
|
+
#: Units for the angle 'dimension'.
|
|
64
|
+
angle: Annotated[Unit, ud.angle]
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
fs = ", ".join(map(str, self.base_units))
|
|
68
|
+
return f"unitsystem({fs})"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@jtu.register_static
|
|
72
|
+
@final
|
|
73
|
+
@dataclass(frozen=True, slots=True)
|
|
74
|
+
class SIUnitSystem(SingletonMixin, AbstractUnitSystem):
|
|
75
|
+
"""SI unit system + angles.
|
|
76
|
+
|
|
77
|
+
Note: this is not part of the public API! Use the `si` instance (realization) from
|
|
78
|
+
`unxt.unitsystems` instead.
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
>>> import unxt as u
|
|
83
|
+
>>> from unxt._src.unitsystems.builtin import SIUnitSystem
|
|
84
|
+
>>> usys = SIUnitSystem(
|
|
85
|
+
... length=u.unit("meter"),
|
|
86
|
+
... time=u.unit("second"),
|
|
87
|
+
... mass=u.unit("kilogram"),
|
|
88
|
+
... electric_current=u.unit("ampere"),
|
|
89
|
+
... temperature=u.unit("Kelvin"),
|
|
90
|
+
... amount=u.unit("mole"),
|
|
91
|
+
... luminous_intensity=u.unit("candela"),
|
|
92
|
+
... angle=u.unit("radian"),
|
|
93
|
+
... )
|
|
94
|
+
>>> usys
|
|
95
|
+
unitsystem(m, kg, s, mol, A, K, cd, rad)
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
# Base SI dimensions
|
|
100
|
+
#: Units for the length dimension.
|
|
101
|
+
length: Annotated[Unit, ud.length]
|
|
102
|
+
|
|
103
|
+
#: Units for the mass dimension.
|
|
104
|
+
mass: Annotated[Unit, ud.mass]
|
|
105
|
+
|
|
106
|
+
#: Units for the time dimension.
|
|
107
|
+
time: Annotated[Unit, ud.time]
|
|
108
|
+
|
|
109
|
+
#: Units for the amount of substance dimension.
|
|
110
|
+
amount: Annotated[Unit, ud.amount]
|
|
111
|
+
|
|
112
|
+
#: Units for the electric current dimension.
|
|
113
|
+
electric_current: Annotated[Unit, ud.current]
|
|
114
|
+
|
|
115
|
+
#: Units for the temperature dimension.
|
|
116
|
+
temperature: Annotated[Unit, ud.temperature]
|
|
117
|
+
|
|
118
|
+
#: Units for the luminous intensity dimension.
|
|
119
|
+
luminous_intensity: Annotated[Unit, ud.luminous_intensity]
|
|
120
|
+
|
|
121
|
+
# + angles
|
|
122
|
+
#: Units for the angle 'dimension'.
|
|
123
|
+
angle: Annotated[Unit, ud.angle]
|
|
124
|
+
|
|
125
|
+
def __repr__(self) -> str:
|
|
126
|
+
fs = ", ".join(map(str, self.base_units))
|
|
127
|
+
return f"unitsystem({fs})"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@jtu.register_static
|
|
131
|
+
@final
|
|
132
|
+
@dataclass(frozen=True, slots=True)
|
|
133
|
+
class CGSUnitSystem(SingletonMixin, AbstractUnitSystem):
|
|
134
|
+
"""CGS unit system + angles.
|
|
135
|
+
|
|
136
|
+
Note: this is not part of the public API! Use the `cgs` instance (realization) from
|
|
137
|
+
`unxt.unitsystems` instead.
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> import unxt as u
|
|
142
|
+
>>> from unxt._src.unitsystems.builtin import CGSUnitSystem
|
|
143
|
+
>>> usys = CGSUnitSystem(
|
|
144
|
+
... length=u.unit("centimeter"),
|
|
145
|
+
... time=u.unit("second"),
|
|
146
|
+
... mass=u.unit("gram"),
|
|
147
|
+
... angle=u.unit("radian"),
|
|
148
|
+
... force=u.unit("dyne"),
|
|
149
|
+
... energy=u.unit("erg"),
|
|
150
|
+
... pressure=u.unit("barye"),
|
|
151
|
+
... dynamic_viscosity=u.unit("poise"),
|
|
152
|
+
... kinematic_viscosity=u.unit("stokes"),
|
|
153
|
+
... )
|
|
154
|
+
>>> usys
|
|
155
|
+
unitsystem(cm, g, s, dyn, erg, Ba, P, St, rad)
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
# Base CGS dimensions
|
|
160
|
+
#: Units for the length dimension.
|
|
161
|
+
length: Annotated[Unit, ud.length]
|
|
162
|
+
|
|
163
|
+
#: Units for the mass dimension.
|
|
164
|
+
mass: Annotated[Unit, ud.mass]
|
|
165
|
+
|
|
166
|
+
#: Units for the time dimension.
|
|
167
|
+
time: Annotated[Unit, ud.time]
|
|
168
|
+
|
|
169
|
+
#: Units for the force dimension.
|
|
170
|
+
force: Annotated[Unit, ud.force]
|
|
171
|
+
|
|
172
|
+
#: Units for the energy dimension.
|
|
173
|
+
energy: Annotated[Unit, ud.energy]
|
|
174
|
+
|
|
175
|
+
#: Units for the pressure dimension.
|
|
176
|
+
pressure: Annotated[Unit, ud.pressure]
|
|
177
|
+
|
|
178
|
+
#: Units for the dynamic viscosity dimension.
|
|
179
|
+
dynamic_viscosity: Annotated[Unit, ud.dynamic_viscosity]
|
|
180
|
+
|
|
181
|
+
#: Units for the kinematic viscosity dimension.
|
|
182
|
+
kinematic_viscosity: Annotated[Unit, ud.kinematic_viscosity]
|
|
183
|
+
|
|
184
|
+
# + angles
|
|
185
|
+
#: Units for the angle 'dimension'.
|
|
186
|
+
angle: Annotated[Unit, ud.angle]
|
|
187
|
+
|
|
188
|
+
def __repr__(self) -> str:
|
|
189
|
+
fs = ", ".join(map(str, self.base_units))
|
|
190
|
+
return f"unitsystem({fs})"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Built-in dimensions."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
from unxt._src.dimensions import dimension
|
|
6
|
+
|
|
7
|
+
dimensionless = dimension("dimensionless")
|
|
8
|
+
length = dimension("length")
|
|
9
|
+
mass = dimension("mass")
|
|
10
|
+
time = dimension("time")
|
|
11
|
+
speed = dimension("speed")
|
|
12
|
+
angle = dimension("angle")
|
|
13
|
+
amount = dimension("amount of substance")
|
|
14
|
+
current = dimension("electrical current")
|
|
15
|
+
temperature = dimension("temperature")
|
|
16
|
+
luminous_intensity = dimension("luminous intensity")
|
|
17
|
+
pressure = dimension("pressure")
|
|
18
|
+
force = dimension("force")
|
|
19
|
+
energy = dimension("energy")
|
|
20
|
+
dynamic_viscosity = dimension("dynamic viscosity")
|
|
21
|
+
kinematic_viscosity = dimension("kinematic viscosity")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Comparison functions."""
|
|
2
|
+
|
|
3
|
+
__all__ = ("equivalent",)
|
|
4
|
+
|
|
5
|
+
from plum import dispatch
|
|
6
|
+
|
|
7
|
+
from .base import AbstractUnitSystem
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dispatch
|
|
11
|
+
def equivalent(a: AbstractUnitSystem, b: AbstractUnitSystem, /) -> bool:
|
|
12
|
+
"""Check if two unit systems are equivalent."""
|
|
13
|
+
return set(a.base_dimensions) == set(b.base_dimensions)
|