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
unxt/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""``unxt``: Quantities in JAX.
|
|
2
|
+
|
|
3
|
+
``unxt`` is a library for working with physical quantities in JAX, supporting
|
|
4
|
+
JAX's autodiff and JIT compilation, and easy integration with existing codes. If
|
|
5
|
+
you're seeing this then you're in the main module of the ``unxt`` package, where
|
|
6
|
+
we provide exports for the main functionality of the library. Sub-modules are
|
|
7
|
+
available for more specialized functionality, such as unit systems and
|
|
8
|
+
experimental features.
|
|
9
|
+
|
|
10
|
+
Note that `unxt` uses multiple-dispatch to provide a flexible and extensible
|
|
11
|
+
interface. In the docs you'll see the function signatures without type
|
|
12
|
+
annotation and then subsections for specific function dispatches based on the
|
|
13
|
+
type annotations. However dispatches registered from other modules may not be
|
|
14
|
+
included in the rendered docs. To see all the dispatches execute ``<func or
|
|
15
|
+
class>.methods`` in an interactive Python session. For more information on
|
|
16
|
+
multiple-dispatch see the [`plum`](https://beartype.github.io/plum/intro.html)
|
|
17
|
+
documentation.
|
|
18
|
+
|
|
19
|
+
-----
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
"__version__",
|
|
25
|
+
# dimensions
|
|
26
|
+
"dims", # module
|
|
27
|
+
"AbstractDimension", # dimension type
|
|
28
|
+
"dimension", # convenience constructor
|
|
29
|
+
"dimension_of", # get the dimensions
|
|
30
|
+
# units
|
|
31
|
+
"units", # module
|
|
32
|
+
"AbstractUnit", # unit type
|
|
33
|
+
"unit",
|
|
34
|
+
"unit_of",
|
|
35
|
+
# units systems
|
|
36
|
+
"unitsystems", # module
|
|
37
|
+
"AbstractUnitSystem", # base class
|
|
38
|
+
"unitsystem", # convenience constructor
|
|
39
|
+
"unitsystem_of", # get the unit system
|
|
40
|
+
# quantities
|
|
41
|
+
"quantity", # module
|
|
42
|
+
"Quantity", # main class
|
|
43
|
+
"Q", # convenience alias
|
|
44
|
+
"StaticQuantity", # static value quantity
|
|
45
|
+
"AbstractQuantity", # base class
|
|
46
|
+
"Angle", # angular quantity
|
|
47
|
+
"uconvert_value", # convert values
|
|
48
|
+
"uconvert", # convert units
|
|
49
|
+
"ustrip", # strip units
|
|
50
|
+
"is_unit_convertible", # check if units can be converted
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
from .setup_package import install_import_hook
|
|
54
|
+
|
|
55
|
+
with install_import_hook("unxt"):
|
|
56
|
+
from . import dims, quantity, units, unitsystems
|
|
57
|
+
from ._version import version as __version__
|
|
58
|
+
from .dims import AbstractDimension, dimension, dimension_of
|
|
59
|
+
from .quantity import (
|
|
60
|
+
AbstractQuantity,
|
|
61
|
+
Angle,
|
|
62
|
+
Q,
|
|
63
|
+
Quantity,
|
|
64
|
+
StaticQuantity,
|
|
65
|
+
is_unit_convertible,
|
|
66
|
+
uconvert,
|
|
67
|
+
uconvert_value,
|
|
68
|
+
ustrip,
|
|
69
|
+
)
|
|
70
|
+
from .units import AbstractUnit, unit, unit_of
|
|
71
|
+
from .unitsystems import AbstractUnitSystem, unitsystem, unitsystem_of
|
|
72
|
+
|
|
73
|
+
from ._src import experimental # noqa: F401
|
|
74
|
+
|
|
75
|
+
# isort: split
|
|
76
|
+
from . import _interop # noqa: F401 # register interop
|
|
77
|
+
|
|
78
|
+
# Clean up the namespace
|
|
79
|
+
del install_import_hook
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""`unxt` interoperability with other libraries."""
|
|
2
|
+
# ruff:noqa: F401
|
|
3
|
+
|
|
4
|
+
__all__: tuple[str, ...] = ()
|
|
5
|
+
|
|
6
|
+
from .optional_deps import OptDeps
|
|
7
|
+
|
|
8
|
+
# Register interoperability
|
|
9
|
+
if OptDeps.ASTROPY.installed:
|
|
10
|
+
from . import unxt_interop_astropy
|
|
11
|
+
|
|
12
|
+
if OptDeps.GALA.installed:
|
|
13
|
+
from . import unxt_interop_gala
|
|
14
|
+
|
|
15
|
+
if OptDeps.MATPLOTLIB.installed:
|
|
16
|
+
from . import unxt_interop_mpl as interop_mpl
|
|
17
|
+
|
|
18
|
+
interop_mpl.setup_matplotlib_support_for_unxt(enable=True)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Optional dependencies. Internal use only."""
|
|
2
|
+
|
|
3
|
+
__all__ = ("OptDeps",)
|
|
4
|
+
|
|
5
|
+
from optional_dependencies import OptionalDependencyEnum, auto
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OptDeps(OptionalDependencyEnum): # type: ignore[misc] # pylint: disable=invalid-enum-extension
|
|
9
|
+
"""Optional dependencies for ``unxt``."""
|
|
10
|
+
|
|
11
|
+
ASTROPY = auto()
|
|
12
|
+
GALA = auto()
|
|
13
|
+
MATPLOTLIB = auto()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Dimensions compatibility."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from dataclasses import MISSING, Field
|
|
7
|
+
|
|
8
|
+
import astropy.units as apyu
|
|
9
|
+
import plum
|
|
10
|
+
|
|
11
|
+
from unxt.dims import AbstractDimension
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@plum.dispatch
|
|
15
|
+
def dimension_of(obj: apyu.Quantity, /) -> AbstractDimension:
|
|
16
|
+
"""Return the dimension of a quantity.
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
>>> import unxt as u
|
|
21
|
+
>>> import astropy.units as apyu
|
|
22
|
+
|
|
23
|
+
>>> q = apyu.Quantity(1, "m")
|
|
24
|
+
>>> u.dimension_of(q)
|
|
25
|
+
PhysicalType('length')
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
return dimension_of(obj.unit)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ===================================================
|
|
32
|
+
# `Dataclassish` support
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@plum.dispatch
|
|
36
|
+
def fields(obj: AbstractDimension, /) -> tuple[Field, ...]:
|
|
37
|
+
"""Return the fields of a dimension.
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
--------
|
|
41
|
+
>>> import dataclassish as dc
|
|
42
|
+
>>> import astropy.units as apyu
|
|
43
|
+
|
|
44
|
+
>>> dim = apyu.get_physical_type("length")
|
|
45
|
+
>>> dc.fields(dim)
|
|
46
|
+
(Field(name='_unit',...), Field(name='_physical_type',...))
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
unit_field = Field(MISSING, MISSING, True, False, False, False, {}, False) # noqa: FBT003
|
|
50
|
+
unit_field.name = "_unit"
|
|
51
|
+
physical_type_field = Field(MISSING, MISSING, True, False, False, False, {}, False) # noqa: FBT003
|
|
52
|
+
physical_type_field.name = "_physical_type"
|
|
53
|
+
return (unit_field, physical_type_field)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""Quantity compatibility."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
from typing import Any, NoReturn
|
|
6
|
+
|
|
7
|
+
import plum
|
|
8
|
+
from astropy.coordinates import Angle as AstropyAngle, Distance as AstropyDistance
|
|
9
|
+
from astropy.units import Quantity as AstropyQuantity
|
|
10
|
+
from jaxtyping import ArrayLike
|
|
11
|
+
|
|
12
|
+
import dataclassish as dc
|
|
13
|
+
import quaxed.numpy as jnp
|
|
14
|
+
|
|
15
|
+
import unxt_api as uapi
|
|
16
|
+
from .custom_types import APYUnits
|
|
17
|
+
from unxt.quantity import AbstractQuantity, AllowValue, BareQuantity, Quantity, ustrip
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Value Converter
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@plum.dispatch
|
|
24
|
+
def convert_to_quantity_value(obj: AstropyQuantity, /) -> NoReturn:
|
|
25
|
+
"""Disallow conversion of `AstropyQuantity` to a value.
|
|
26
|
+
|
|
27
|
+
>>> import astropy.units as apyu
|
|
28
|
+
>>> from unxt.quantity import convert_to_quantity_value
|
|
29
|
+
|
|
30
|
+
>>> try:
|
|
31
|
+
... convert_to_quantity_value(apyu.Quantity(1, "m"))
|
|
32
|
+
... except TypeError as e:
|
|
33
|
+
... print(e)
|
|
34
|
+
Cannot convert 'Quantity' to a value.
|
|
35
|
+
For a Quantity, use the `.from_` constructor instead.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
msg = (
|
|
39
|
+
f"Cannot convert {type(obj).__name__!r} to a value. "
|
|
40
|
+
"For a Quantity, use the `.from_` constructor instead."
|
|
41
|
+
)
|
|
42
|
+
raise TypeError(msg)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ============================================================================
|
|
46
|
+
# AbstractQuantity
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# -----------------
|
|
50
|
+
# Constructor
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@AbstractQuantity.from_.dispatch
|
|
54
|
+
def from_(
|
|
55
|
+
cls: type[AbstractQuantity], value: AstropyQuantity, /, **kwargs: Any
|
|
56
|
+
) -> AbstractQuantity:
|
|
57
|
+
"""Construct a `Quantity` from another `Quantity`.
|
|
58
|
+
|
|
59
|
+
The `value` is converted to the new `unit`.
|
|
60
|
+
|
|
61
|
+
Examples
|
|
62
|
+
--------
|
|
63
|
+
>>> import unxt as u
|
|
64
|
+
>>> import astropy.units as apyu
|
|
65
|
+
|
|
66
|
+
>>> u.Quantity.from_(apyu.Quantity(1, "m"))
|
|
67
|
+
Quantity(Array(1., dtype=float32), unit='m')
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
u = uapi.unit_of(value)
|
|
71
|
+
value = jnp.asarray(uapi.ustrip(u, value), **kwargs)
|
|
72
|
+
return cls(value, u)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@AbstractQuantity.from_.dispatch
|
|
76
|
+
def from_(
|
|
77
|
+
cls: type[AbstractQuantity], value: AstropyQuantity, u: Any, /, **kwargs: Any
|
|
78
|
+
) -> AbstractQuantity:
|
|
79
|
+
"""Construct a `Quantity` from another `Quantity`.
|
|
80
|
+
|
|
81
|
+
The `value` is converted to the new `unit`.
|
|
82
|
+
|
|
83
|
+
Examples
|
|
84
|
+
--------
|
|
85
|
+
>>> import unxt as u
|
|
86
|
+
>>> import astropy.units as apyu
|
|
87
|
+
|
|
88
|
+
>>> u.Quantity.from_(apyu.Quantity(1, "m"), "cm")
|
|
89
|
+
Quantity(Array(100., dtype=float32), unit='cm')
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
u = uapi.unit(u)
|
|
93
|
+
value = jnp.asarray(uapi.ustrip(u, value), **kwargs)
|
|
94
|
+
return cls(value, u)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# -----------------
|
|
98
|
+
# Conversion Methods
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@plum.conversion_method(type_from=AbstractQuantity, type_to=AstropyQuantity) # type: ignore[arg-type]
|
|
102
|
+
def convert_unxt_quantity_to_astropy_quantity(
|
|
103
|
+
q: AbstractQuantity, /
|
|
104
|
+
) -> AstropyQuantity:
|
|
105
|
+
"""Convert a `unxt.AbstractQuantity` to a `astropy.units.Quantity`.
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
>>> import unxt as u
|
|
110
|
+
>>> from astropy.units import Quantity as AstropyQuantity
|
|
111
|
+
>>> from plum import convert
|
|
112
|
+
|
|
113
|
+
>>> convert(u.Q(1.0, "cm"), AstropyQuantity)
|
|
114
|
+
<Quantity 1. cm>
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
u = uapi.unit_of(q)
|
|
118
|
+
return AstropyQuantity(uapi.ustrip(u, q), u)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@plum.conversion_method(type_from=AbstractQuantity, type_to=AstropyDistance) # type: ignore[arg-type]
|
|
122
|
+
def convert_unxt_quantity_to_astropy_distance(
|
|
123
|
+
q: AbstractQuantity, /
|
|
124
|
+
) -> AstropyDistance:
|
|
125
|
+
"""Convert a `unxt.AbstractQuantity` to a `astropy.coordinates.Distance`.
|
|
126
|
+
|
|
127
|
+
Examples
|
|
128
|
+
--------
|
|
129
|
+
>>> import unxt as u
|
|
130
|
+
>>> from astropy.coordinates import Distance as AstropyDistance
|
|
131
|
+
>>> from plum import convert
|
|
132
|
+
|
|
133
|
+
>>> convert(u.Q(1.0, "cm"), AstropyDistance)
|
|
134
|
+
<Distance 1. cm>
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
u = uapi.unit_of(q)
|
|
138
|
+
return AstropyDistance(uapi.ustrip(u, q), u)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@plum.conversion_method(type_from=AbstractQuantity, type_to=AstropyAngle) # type: ignore[arg-type]
|
|
142
|
+
def convert_unxt_quantity_to_astropy_angle(q: AbstractQuantity, /) -> AstropyAngle:
|
|
143
|
+
"""Convert a `unxt.quantity.AbstractQuantity` to a `astropy.coordinates.Angle`.
|
|
144
|
+
|
|
145
|
+
Examples
|
|
146
|
+
--------
|
|
147
|
+
>>> import unxt as u
|
|
148
|
+
>>> from astropy.coordinates import Angle as AstropyAngle
|
|
149
|
+
>>> from plum import convert
|
|
150
|
+
|
|
151
|
+
>>> convert(u.Q(1.0, "rad"), AstropyAngle)
|
|
152
|
+
<Angle 1. rad>
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
u = uapi.unit_of(q)
|
|
156
|
+
return AstropyAngle(uapi.ustrip(u, q), u)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ============================================================================
|
|
160
|
+
# Quantity
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@plum.conversion_method(type_from=AstropyQuantity, type_to=Quantity)
|
|
164
|
+
def convert_astropy_quantity_to_unxt_quantity(q: AstropyQuantity, /) -> Quantity:
|
|
165
|
+
"""Convert a `astropy.units.Quantity` to a `unxt.Quantity`.
|
|
166
|
+
|
|
167
|
+
Examples
|
|
168
|
+
--------
|
|
169
|
+
>>> from astropy.units import Quantity as AstropyQuantity
|
|
170
|
+
>>> from plum import convert
|
|
171
|
+
>>> from unxt import Quantity
|
|
172
|
+
|
|
173
|
+
>>> convert(AstropyQuantity(1.0, "cm"), Quantity)
|
|
174
|
+
Quantity(Array(1., dtype=float32), unit='cm')
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
u = uapi.unit_of(q)
|
|
178
|
+
return Quantity(uapi.ustrip(u, q), u)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ============================================================================
|
|
182
|
+
# BareQuantity
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@plum.conversion_method(type_from=AstropyQuantity, type_to=BareQuantity) # type: ignore[arg-type]
|
|
186
|
+
def convert_astropy_quantity_to_unxt_barequantity(
|
|
187
|
+
q: AstropyQuantity, /
|
|
188
|
+
) -> BareQuantity:
|
|
189
|
+
"""Convert a `astropy.units.Quantity` to a `unxt.BareQuantity`.
|
|
190
|
+
|
|
191
|
+
Examples
|
|
192
|
+
--------
|
|
193
|
+
>>> from astropy.units import Quantity as AstropyQuantity
|
|
194
|
+
>>> from plum import convert
|
|
195
|
+
>>> from unxt.quantity import BareQuantity
|
|
196
|
+
|
|
197
|
+
>>> convert(AstropyQuantity(1.0, "cm"), BareQuantity)
|
|
198
|
+
BareQuantity(Array(1., dtype=float32), unit='cm')
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
u = uapi.unit_of(q)
|
|
202
|
+
return BareQuantity(uapi.ustrip(u, q), u)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
###############################################################################
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@plum.dispatch
|
|
209
|
+
def uconvert_value(uto: APYUnits, ufrom: APYUnits, x: ArrayLike, /) -> ArrayLike:
|
|
210
|
+
"""Convert the value to the specified units.
|
|
211
|
+
|
|
212
|
+
Examples
|
|
213
|
+
--------
|
|
214
|
+
>>> import astropy.units as apyu
|
|
215
|
+
>>> import unxt as u
|
|
216
|
+
|
|
217
|
+
>>> u.uconvert_value(apyu.Unit("km"), apyu.Unit("m"), 1000)
|
|
218
|
+
1.0
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
# Hot-path: if no unit conversion is necessary
|
|
222
|
+
if ufrom == uto:
|
|
223
|
+
return x
|
|
224
|
+
|
|
225
|
+
# Compute the new value.
|
|
226
|
+
return ufrom.to(uto, x)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@plum.dispatch
|
|
230
|
+
def uconvert(u: APYUnits, x: AbstractQuantity, /) -> AbstractQuantity:
|
|
231
|
+
"""Convert the quantity to the specified units.
|
|
232
|
+
|
|
233
|
+
Examples
|
|
234
|
+
--------
|
|
235
|
+
>>> import astropy.units as apyu
|
|
236
|
+
>>> import unxt as u
|
|
237
|
+
|
|
238
|
+
>>> x = u.Quantity(1000, "m")
|
|
239
|
+
>>> u.uconvert(u.unit("km"), x)
|
|
240
|
+
Quantity(Array(1., dtype=float32, ...), unit='km')
|
|
241
|
+
|
|
242
|
+
>>> x = u.Quantity([1, 2, 3], "Kelvin")
|
|
243
|
+
>>> with apyu.add_enabled_equivalencies(apyu.temperature()):
|
|
244
|
+
... y = x.uconvert("deg_C")
|
|
245
|
+
>>> y
|
|
246
|
+
Quantity(Array([-272.15, -271.15, -270.15], dtype=float32, ...), unit='deg_C')
|
|
247
|
+
|
|
248
|
+
>>> x = Quantity([1, 2, 3], "radian")
|
|
249
|
+
>>> with apyu.add_enabled_equivalencies(apyu.dimensionless_angles()):
|
|
250
|
+
... y = x.uconvert("")
|
|
251
|
+
>>> y
|
|
252
|
+
Quantity(Array([1., 2., 3.], dtype=float32, ...), unit='')
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
# Hot-path: if no unit conversion is necessary
|
|
256
|
+
if x.unit == u:
|
|
257
|
+
return x
|
|
258
|
+
|
|
259
|
+
value = x.unit.to(u, uapi.ustrip(x))
|
|
260
|
+
|
|
261
|
+
# If the dimensions are the same, we can just replace the value and unit.
|
|
262
|
+
if uapi.dimension_of(x.unit) == uapi.dimension_of(u):
|
|
263
|
+
return dc.replace(x, value=value, unit=u)
|
|
264
|
+
|
|
265
|
+
# If the dimensions are different, we need to create a new quantity since
|
|
266
|
+
# the dimensions can be part of the type. This won't work if the Quantity
|
|
267
|
+
# type itself needs to be changed, e.g. `unxt.Angle` -> `unxt.Quantity`.
|
|
268
|
+
# These cases are handled separately, in other dispatches.
|
|
269
|
+
fs = dict(dc.field_items(x)) # pylint: disable=unreachable
|
|
270
|
+
fs["value"] = value
|
|
271
|
+
fs["unit"] = u
|
|
272
|
+
|
|
273
|
+
return plum.type_unparametrized(x)(**fs)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ============================================================================
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@plum.dispatch
|
|
280
|
+
def ustrip(u: Any, x: AstropyQuantity) -> Any:
|
|
281
|
+
"""Strip the units from the quantity.
|
|
282
|
+
|
|
283
|
+
Examples
|
|
284
|
+
--------
|
|
285
|
+
>>> import astropy.units as apyu
|
|
286
|
+
>>> import unxt as u
|
|
287
|
+
|
|
288
|
+
>>> x = apyu.Quantity(1000, "m")
|
|
289
|
+
>>> float(u.ustrip(u.unit("m"), x))
|
|
290
|
+
1000.0
|
|
291
|
+
|
|
292
|
+
"""
|
|
293
|
+
return x.to_value(u)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@plum.dispatch # TODO: type annotate by value
|
|
297
|
+
def ustrip(flag: type[AllowValue], u: Any, x: AstropyQuantity, /) -> Any:
|
|
298
|
+
"""Strip the units from a quantity.
|
|
299
|
+
|
|
300
|
+
Examples
|
|
301
|
+
--------
|
|
302
|
+
>>> import unxt as u
|
|
303
|
+
>>> q = u.Quantity(1000, "m")
|
|
304
|
+
>>> u.ustrip(AllowValue, "km", q)
|
|
305
|
+
Array(1., dtype=float32, ...)
|
|
306
|
+
|
|
307
|
+
"""
|
|
308
|
+
return uapi.ustrip(u, x)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Astropy units compatibility."""
|
|
2
|
+
|
|
3
|
+
__all__: tuple[str, ...] = ()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import dataclasses
|
|
7
|
+
|
|
8
|
+
import astropy.units as apyu
|
|
9
|
+
import plum
|
|
10
|
+
|
|
11
|
+
from .custom_types import APYUnits
|
|
12
|
+
|
|
13
|
+
# ===================================================================
|
|
14
|
+
# Register dispatches
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@plum.dispatch
|
|
18
|
+
def unit(obj: apyu.UnitBase | apyu.Unit, /) -> APYUnits:
|
|
19
|
+
"""Construct the units from an Astropy unit.
|
|
20
|
+
|
|
21
|
+
Examples
|
|
22
|
+
--------
|
|
23
|
+
>>> import astropy.units as apyu
|
|
24
|
+
>>> import unxt as u
|
|
25
|
+
>>> u.unit(apyu.km)
|
|
26
|
+
Unit("km")
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
return apyu.Unit(obj)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@plum.dispatch
|
|
33
|
+
def unit(obj: apyu.Quantity, /) -> APYUnits:
|
|
34
|
+
"""Construct the units from an Astropy quantity.
|
|
35
|
+
|
|
36
|
+
Examples
|
|
37
|
+
--------
|
|
38
|
+
>>> import astropy.units as apyu
|
|
39
|
+
>>> import unxt as u
|
|
40
|
+
>>> u.unit(apyu.Quantity(2, "km"))
|
|
41
|
+
Unit("2 km")
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
return apyu.Unit(obj)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# -------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@plum.dispatch
|
|
51
|
+
def unit_of(obj: apyu.UnitBase | apyu.Unit, /) -> APYUnits:
|
|
52
|
+
"""Return the units of an object.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> import astropy.units as apyu
|
|
57
|
+
>>> import unxt as u
|
|
58
|
+
|
|
59
|
+
>>> u.unit_of(apyu.km)
|
|
60
|
+
Unit("km")
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
return obj
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@plum.dispatch
|
|
67
|
+
def unit_of(obj: apyu.Quantity, /) -> APYUnits:
|
|
68
|
+
"""Return the units of an Astropy quantity.
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
>>> import astropy.units as apyu
|
|
73
|
+
>>> import unxt as u
|
|
74
|
+
|
|
75
|
+
>>> u.unit_of(apyu.Quantity(1, "km"))
|
|
76
|
+
Unit("km")
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
return unit_of(obj.unit)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ===================================================
|
|
83
|
+
# `Dataclassish` support
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@plum.dispatch
|
|
87
|
+
def fields(obj: APYUnits, /) -> tuple[dataclasses.Field, ...]:
|
|
88
|
+
"""Return the fields of a dimension.
|
|
89
|
+
|
|
90
|
+
Examples
|
|
91
|
+
--------
|
|
92
|
+
>>> import dataclassish as dc
|
|
93
|
+
>>> import astropy.units as apyu
|
|
94
|
+
|
|
95
|
+
>>> dim = apyu.Unit("m")
|
|
96
|
+
>>> dc.fields(dim)
|
|
97
|
+
(Field(name='_names',...)
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
st_field = dataclasses.Field(
|
|
101
|
+
dataclasses.MISSING,
|
|
102
|
+
dataclasses.MISSING,
|
|
103
|
+
True, # noqa: FBT003
|
|
104
|
+
True, # noqa: FBT003
|
|
105
|
+
True, # noqa: FBT003
|
|
106
|
+
True, # noqa: FBT003
|
|
107
|
+
{},
|
|
108
|
+
False, # noqa: FBT003
|
|
109
|
+
)
|
|
110
|
+
st_field.name = "_names"
|
|
111
|
+
return (st_field,)
|