xpint 0.1.0b1__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.
- xpint/__init__.py +32 -0
- xpint/_adapters.py +155 -0
- xpint/_formatter.py +38 -0
- xpint/_matplotlib.py +27 -0
- xpint/_measurement.py +73 -0
- xpint/_policies.py +160 -0
- xpint/designations.py +305 -0
- xpint/py.typed +1 -0
- xpint/registry.py +3148 -0
- xpint/units.py +74 -0
- xpint-0.1.0b1.dist-info/METADATA +410 -0
- xpint-0.1.0b1.dist-info/RECORD +14 -0
- xpint-0.1.0b1.dist-info/WHEEL +4 -0
- xpint-0.1.0b1.dist-info/licenses/LICENSE +29 -0
xpint/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""xpint — extended semantic quantities for Pint."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
|
|
5
|
+
from .registry import (
|
|
6
|
+
AffineRole,
|
|
7
|
+
DesignationSignature,
|
|
8
|
+
DesignationTerm,
|
|
9
|
+
KindDefinition,
|
|
10
|
+
SemanticQuantity,
|
|
11
|
+
SemanticRegistry,
|
|
12
|
+
SemanticUnit,
|
|
13
|
+
get_application_registry,
|
|
14
|
+
get_default_registry,
|
|
15
|
+
set_application_registry,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__version__ = version("xpint")
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AffineRole",
|
|
22
|
+
"DesignationSignature",
|
|
23
|
+
"DesignationTerm",
|
|
24
|
+
"KindDefinition",
|
|
25
|
+
"SemanticQuantity",
|
|
26
|
+
"SemanticRegistry",
|
|
27
|
+
"SemanticUnit",
|
|
28
|
+
"__version__",
|
|
29
|
+
"get_application_registry",
|
|
30
|
+
"get_default_registry",
|
|
31
|
+
"set_application_registry",
|
|
32
|
+
]
|
xpint/_adapters.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Guard Pint's numerical adapters before they strip semantic objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from functools import wraps
|
|
7
|
+
from inspect import Parameter, signature
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pint import registry_helpers
|
|
11
|
+
from pint.compat import np # type: ignore[attr-defined]
|
|
12
|
+
from pint.util import UnitsContainer, pi_theorem
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _require_plain(registry: Any, value: Any, operation: str) -> None:
|
|
16
|
+
registry._require_owned(value, name=f"{operation} input")
|
|
17
|
+
if isinstance(value, (registry.Quantity, registry.Unit)):
|
|
18
|
+
if registry.Quantity._operands_have_semantics(value):
|
|
19
|
+
raise TypeError(
|
|
20
|
+
f"{operation} cannot represent classified quantity metadata; "
|
|
21
|
+
"explicitly remove it or operate on magnitude first"
|
|
22
|
+
)
|
|
23
|
+
elif isinstance(value, dict):
|
|
24
|
+
for item in value.values():
|
|
25
|
+
_require_plain(registry, item, operation)
|
|
26
|
+
elif isinstance(value, (tuple, list)):
|
|
27
|
+
for item in value:
|
|
28
|
+
_require_plain(registry, item, operation)
|
|
29
|
+
elif np is not None and isinstance(value, np.ndarray) and value.dtype == np.dtype(object):
|
|
30
|
+
for item in value.flat:
|
|
31
|
+
_require_plain(registry, item, operation)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _require_plain_unit_spec(registry: Any, value: Any, operation: str) -> None:
|
|
35
|
+
if value is None:
|
|
36
|
+
return
|
|
37
|
+
if isinstance(value, str) and "=" not in value and "[" not in value:
|
|
38
|
+
value = registry.Unit(value)
|
|
39
|
+
_require_plain(registry, value, operation)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _require_plain_wraps_input(registry: Any, value: Any, strict: bool) -> None:
|
|
43
|
+
if strict and isinstance(value, str):
|
|
44
|
+
value = registry.parse_expression(value)
|
|
45
|
+
registry._require_owned(value, name="wraps input")
|
|
46
|
+
if isinstance(value, (registry.Quantity, registry.Unit)):
|
|
47
|
+
_require_plain(registry, value, "wraps")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _wraps_preserves_variadic_passthrough(registry: Any) -> bool:
|
|
51
|
+
"""Check Pint's binding behavior without calling the user's function."""
|
|
52
|
+
probe = registry.Quantity(1, "")
|
|
53
|
+
|
|
54
|
+
def preserves_probe(*values: Any, scale: Any) -> bool:
|
|
55
|
+
return values[1] is probe
|
|
56
|
+
|
|
57
|
+
wrapped = registry_helpers.wraps(registry, None, (None, ""))(preserves_probe)
|
|
58
|
+
return bool(wrapped(None, probe, scale=probe))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _wraps(
|
|
62
|
+
registry: Any, ret: Any, args: Any, strict: bool
|
|
63
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
64
|
+
argument_specs = args if isinstance(args, (list, tuple)) else (args,)
|
|
65
|
+
return_specs = ret if isinstance(ret, (list, tuple)) else (ret,)
|
|
66
|
+
for spec in argument_specs:
|
|
67
|
+
_require_plain_unit_spec(registry, spec, "wraps")
|
|
68
|
+
for spec in return_specs:
|
|
69
|
+
registry._require_owned(spec, name="wraps return unit")
|
|
70
|
+
if isinstance(spec, registry.Unit) and spec.has_designation:
|
|
71
|
+
_require_plain(registry, spec, "wraps")
|
|
72
|
+
pint_decorator = registry_helpers.wraps(registry, ret, args, strict)
|
|
73
|
+
|
|
74
|
+
def decorate(function: Callable[..., Any]) -> Callable[..., Any]:
|
|
75
|
+
wrapped = pint_decorator(function)
|
|
76
|
+
sig = signature(function)
|
|
77
|
+
# Pint validates declaration counts; an omitted **kwargs specification
|
|
78
|
+
# means pass-through in converters that support omitting it.
|
|
79
|
+
declarations = tuple(zip(sig.parameters.values(), argument_specs, strict=False))
|
|
80
|
+
# Every value bound to a declared unit is a conversion candidate: a converter
|
|
81
|
+
# that binds by parameter name strips each *args element and each **kwargs
|
|
82
|
+
# value under that parameter's declaration, not just the first one.
|
|
83
|
+
declared = tuple(
|
|
84
|
+
(parameter.name, parameter.kind) for parameter, spec in declarations if spec is not None
|
|
85
|
+
)
|
|
86
|
+
# Pint 0.25.3 addresses declarations by positional slot instead, so a declared
|
|
87
|
+
# parameter after *args strips the variadic element occupying its index. Guard
|
|
88
|
+
# those slots unless the declaration covers them or Pint binds them correctly.
|
|
89
|
+
misbound_kinds = (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD)
|
|
90
|
+
misbound_slots = tuple(
|
|
91
|
+
index
|
|
92
|
+
for index, (parameter, spec) in enumerate(declarations)
|
|
93
|
+
if spec is not None and parameter.kind in misbound_kinds
|
|
94
|
+
)
|
|
95
|
+
if any(kind is Parameter.VAR_POSITIONAL for _name, kind in declared) or (
|
|
96
|
+
misbound_slots
|
|
97
|
+
and any(
|
|
98
|
+
parameter.kind is Parameter.VAR_POSITIONAL for parameter in sig.parameters.values()
|
|
99
|
+
)
|
|
100
|
+
and _wraps_preserves_variadic_passthrough(registry)
|
|
101
|
+
):
|
|
102
|
+
misbound_slots = ()
|
|
103
|
+
|
|
104
|
+
@wraps(function)
|
|
105
|
+
def guarded(*values: Any, **kwargs: Any) -> Any:
|
|
106
|
+
bound = sig.bind(*values, **kwargs)
|
|
107
|
+
bound.apply_defaults()
|
|
108
|
+
for name, kind in declared:
|
|
109
|
+
value = bound.arguments[name]
|
|
110
|
+
if kind is Parameter.VAR_POSITIONAL:
|
|
111
|
+
for item in value:
|
|
112
|
+
_require_plain_wraps_input(registry, item, strict)
|
|
113
|
+
elif kind is Parameter.VAR_KEYWORD:
|
|
114
|
+
for item in value.values():
|
|
115
|
+
_require_plain_wraps_input(registry, item, strict)
|
|
116
|
+
else:
|
|
117
|
+
_require_plain_wraps_input(registry, value, strict)
|
|
118
|
+
for index in misbound_slots:
|
|
119
|
+
if index < len(values):
|
|
120
|
+
_require_plain_wraps_input(registry, values[index], strict)
|
|
121
|
+
return wrapped(*values, **kwargs)
|
|
122
|
+
|
|
123
|
+
return guarded
|
|
124
|
+
|
|
125
|
+
return decorate
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _check(
|
|
129
|
+
registry: Any, dimensions: tuple[Any, ...]
|
|
130
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
|
131
|
+
for dimension in dimensions:
|
|
132
|
+
registry._require_owned(dimension, name="check dimension")
|
|
133
|
+
return registry_helpers.check(registry, *dimensions)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _pi_theorem(registry: Any, quantities: dict[str, Any]) -> Any:
|
|
137
|
+
for value in quantities.values():
|
|
138
|
+
if isinstance(value, (dict, UnitsContainer)):
|
|
139
|
+
value = registry.Unit(registry.UnitsContainer(value))
|
|
140
|
+
_require_plain_unit_spec(registry, value, "pi_theorem")
|
|
141
|
+
return pi_theorem(quantities, registry)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _setup_matplotlib(registry: Any, enable: bool) -> None:
|
|
145
|
+
try:
|
|
146
|
+
import matplotlib.units
|
|
147
|
+
from pint.matplotlib import setup_matplotlib_handlers
|
|
148
|
+
|
|
149
|
+
from ._matplotlib import _SemanticConverter
|
|
150
|
+
except ImportError as exc:
|
|
151
|
+
raise ImportError("setup_matplotlib requires matplotlib; install matplotlib first") from exc
|
|
152
|
+
setup_matplotlib_handlers(registry, enable) # type: ignore[no-untyped-call]
|
|
153
|
+
if enable:
|
|
154
|
+
converters = matplotlib.units.registry
|
|
155
|
+
converters[registry.Quantity] = _SemanticConverter(registry) # type: ignore[no-untyped-call]
|
xpint/_formatter.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Guard registry formatter delegates that cannot represent designations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, override
|
|
7
|
+
|
|
8
|
+
from pint.delegates import Formatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _require_undesignated(value: Any) -> None:
|
|
12
|
+
if getattr(value, "has_designation", False):
|
|
13
|
+
raise TypeError(
|
|
14
|
+
"Pint formatter APIs cannot represent designated objects; "
|
|
15
|
+
"use str() or format() on the xpint quantity or unit instead"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class _GuardedFormatter:
|
|
21
|
+
delegate: Any
|
|
22
|
+
|
|
23
|
+
def format_quantity(self, quantity: Any, *args: Any, **kwargs: Any) -> Any:
|
|
24
|
+
_require_undesignated(quantity)
|
|
25
|
+
return self.delegate.format_quantity(quantity, *args, **kwargs)
|
|
26
|
+
|
|
27
|
+
def format_unit(self, unit: Any, *args: Any, **kwargs: Any) -> Any:
|
|
28
|
+
_require_undesignated(unit)
|
|
29
|
+
return self.delegate.format_unit(unit, *args, **kwargs)
|
|
30
|
+
|
|
31
|
+
def __getattr__(self, name: str) -> Any:
|
|
32
|
+
return getattr(self.delegate, name)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _SemanticFormatter(Formatter):
|
|
36
|
+
@override
|
|
37
|
+
def get_formatter(self, spec: str) -> Any:
|
|
38
|
+
return _GuardedFormatter(super().get_formatter(spec))
|
xpint/_matplotlib.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Optional Pint plotting adapter that rejects semantic metadata loss."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, override
|
|
6
|
+
|
|
7
|
+
from pint.matplotlib import PintConverter
|
|
8
|
+
|
|
9
|
+
from ._adapters import _require_plain, _require_plain_unit_spec
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _SemanticConverter(PintConverter):
|
|
13
|
+
@override
|
|
14
|
+
def convert(self, value: Any, unit: Any, axis: Any) -> Any: # type: ignore[override]
|
|
15
|
+
_require_plain(self._reg, value, "matplotlib")
|
|
16
|
+
_require_plain_unit_spec(self._reg, unit, "matplotlib")
|
|
17
|
+
return super().convert(value, unit, axis) # type: ignore[no-untyped-call]
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def _convert_value(self, value: Any, unit: Any, axis: Any) -> Any:
|
|
21
|
+
_require_plain(self._reg, value, "matplotlib")
|
|
22
|
+
return super()._convert_value(value, unit, axis) # type: ignore[no-untyped-call]
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
def axisinfo(self, unit: Any, axis: Any) -> Any: # type: ignore[override]
|
|
26
|
+
_require_plain_unit_spec(self._reg, unit, "matplotlib")
|
|
27
|
+
return super().axisinfo(unit, axis) # type: ignore[no-untyped-call]
|
xpint/_measurement.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Keep Pint measurements within the unclassified quantity surface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Self, override
|
|
6
|
+
|
|
7
|
+
from pint.facets.measurement import Measurement
|
|
8
|
+
from pint.facets.measurement.objects import MISSING
|
|
9
|
+
from pint.util import UnitsContainer
|
|
10
|
+
|
|
11
|
+
from ._adapters import _require_plain
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _require_measurement_units(registry: Any, units: Any) -> None:
|
|
15
|
+
if isinstance(units, (str, UnitsContainer)):
|
|
16
|
+
units = registry.Unit(units)
|
|
17
|
+
_require_plain(registry, units, "Measurement")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _SemanticMeasurement(Measurement):
|
|
21
|
+
"""Reject semantic inputs before Pint extracts magnitudes and physical units."""
|
|
22
|
+
|
|
23
|
+
__hash__ = Measurement.__hash__
|
|
24
|
+
|
|
25
|
+
def __new__(cls, value: Any, error: Any = MISSING, units: Any = MISSING) -> Self:
|
|
26
|
+
registry = cls._REGISTRY
|
|
27
|
+
_require_plain(registry, value, "Measurement")
|
|
28
|
+
_require_plain(registry, error, "Measurement")
|
|
29
|
+
# Pint accepts Measurement(ufloat, units) as well as (value, error, units).
|
|
30
|
+
if units is MISSING and hasattr(value, "nominal_value"):
|
|
31
|
+
_require_measurement_units(registry, error)
|
|
32
|
+
else:
|
|
33
|
+
_require_measurement_units(registry, units)
|
|
34
|
+
return super().__new__(cls, value, error, units) # type: ignore[no-untyped-call,no-any-return]
|
|
35
|
+
|
|
36
|
+
@override
|
|
37
|
+
def _check(self, other: Any) -> bool:
|
|
38
|
+
_require_plain(self._REGISTRY, other, "Measurement arithmetic")
|
|
39
|
+
return super()._check(other)
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
def __pow__(self, other: Any) -> Any:
|
|
43
|
+
_require_plain(self._REGISTRY, other, "Measurement exponent")
|
|
44
|
+
return super().__pow__(other)
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
def __ipow__(self, other: Any) -> Any:
|
|
48
|
+
_require_plain(self._REGISTRY, other, "Measurement exponent")
|
|
49
|
+
return super().__ipow__(other)
|
|
50
|
+
|
|
51
|
+
@override
|
|
52
|
+
def to(self, other: Any = None, *contexts: Any, **ctx_kwargs: Any) -> Any:
|
|
53
|
+
_require_measurement_units(self._REGISTRY, other)
|
|
54
|
+
return super().to(other, *contexts, **ctx_kwargs)
|
|
55
|
+
|
|
56
|
+
@override
|
|
57
|
+
def ito(self, other: Any = None, *contexts: Any, **ctx_kwargs: Any) -> None:
|
|
58
|
+
_require_measurement_units(self._REGISTRY, other)
|
|
59
|
+
super().ito(other, *contexts, **ctx_kwargs)
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
def compare(self, other: Any, op: Any) -> Any:
|
|
63
|
+
_require_plain(self._REGISTRY, other, "Measurement comparison")
|
|
64
|
+
return super().compare(other, op)
|
|
65
|
+
|
|
66
|
+
@override
|
|
67
|
+
def __eq__(self, other: Any) -> Any:
|
|
68
|
+
registry: Any = self._REGISTRY
|
|
69
|
+
if getattr(other, "_REGISTRY", registry) is not registry:
|
|
70
|
+
return False
|
|
71
|
+
if registry.Quantity._operands_have_semantics(other):
|
|
72
|
+
return False
|
|
73
|
+
return super().__eq__(other)
|
xpint/_policies.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Immutable semantic dispatch tables and built-in registry catalogs."""
|
|
2
|
+
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
|
|
5
|
+
_NUMPY_METHOD_PRESERVING = frozenset(
|
|
6
|
+
{
|
|
7
|
+
"astype",
|
|
8
|
+
"compress",
|
|
9
|
+
"conj",
|
|
10
|
+
"conjugate",
|
|
11
|
+
"copy",
|
|
12
|
+
"diagonal",
|
|
13
|
+
"flatten",
|
|
14
|
+
"item",
|
|
15
|
+
"max",
|
|
16
|
+
"mean",
|
|
17
|
+
"min",
|
|
18
|
+
"ravel",
|
|
19
|
+
"repeat",
|
|
20
|
+
"reshape",
|
|
21
|
+
"round",
|
|
22
|
+
"squeeze",
|
|
23
|
+
"swapaxes",
|
|
24
|
+
"take",
|
|
25
|
+
"transpose",
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
_NUMPY_METHOD_ADDITIVE = frozenset({"sum", "cumsum"})
|
|
29
|
+
# These methods share argument names with the corresponding NumPy functions.
|
|
30
|
+
_NUMPY_METHOD_REDUCTIONS = frozenset({"sum", "max", "min", "mean", "std", "var", "cumsum"})
|
|
31
|
+
_NUMPY_METHOD_SUPPORTED = (
|
|
32
|
+
_NUMPY_METHOD_PRESERVING | _NUMPY_METHOD_ADDITIVE | frozenset({"std", "var"})
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
_NUMPY_UFUNC_BINARY_METHODS = MappingProxyType(
|
|
36
|
+
{
|
|
37
|
+
"add": ("__add__", "__radd__"),
|
|
38
|
+
"subtract": ("__sub__", "__rsub__"),
|
|
39
|
+
"multiply": ("__mul__", "__rmul__"),
|
|
40
|
+
"divide": ("__truediv__", "__rtruediv__"),
|
|
41
|
+
"true_divide": ("__truediv__", "__rtruediv__"),
|
|
42
|
+
"power": ("__pow__", "__rpow__"),
|
|
43
|
+
"equal": ("__eq__", "__eq__"),
|
|
44
|
+
"not_equal": ("__ne__", "__ne__"),
|
|
45
|
+
"less": ("__lt__", "__gt__"),
|
|
46
|
+
"less_equal": ("__le__", "__ge__"),
|
|
47
|
+
"greater": ("__gt__", "__lt__"),
|
|
48
|
+
"greater_equal": ("__ge__", "__le__"),
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
_NUMPY_UFUNC_MAGNITUDE_PREDICATES = frozenset({"isfinite", "isinf", "isnan", "signbit"})
|
|
52
|
+
|
|
53
|
+
# Trigonometry consumes or produces an angle, so an input kind never describes the
|
|
54
|
+
# output. Pint converts the argument to radian and rejects a non-angle; xpint only has
|
|
55
|
+
# to prevent its own metadata from crossing the boundary silently.
|
|
56
|
+
_ANGLE_UFUNCS = frozenset(
|
|
57
|
+
{
|
|
58
|
+
"sin",
|
|
59
|
+
"cos",
|
|
60
|
+
"tan",
|
|
61
|
+
"arcsin",
|
|
62
|
+
"arccos",
|
|
63
|
+
"arctan",
|
|
64
|
+
"arctan2",
|
|
65
|
+
"degrees",
|
|
66
|
+
"radians",
|
|
67
|
+
"deg2rad",
|
|
68
|
+
"rad2deg",
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
_NUMPY_FUNCTION_PRESERVING = frozenset({"mean", "nanmean", "max", "min", "amax", "amin"})
|
|
73
|
+
_NUMPY_FUNCTION_ADDITIVE = frozenset({"sum", "nansum", "cumsum", "nancumsum"})
|
|
74
|
+
_NUMPY_STATISTICS = frozenset({"std", "nanstd", "var", "nanvar"})
|
|
75
|
+
_NUMPY_VARIANCES = frozenset({"var", "nanvar"})
|
|
76
|
+
_NUMPY_MAGNITUDE_INDICES = frozenset({"argmax", "argmin", "argsort", "nonzero"})
|
|
77
|
+
_NUMPY_UFUNC_POWERS = MappingProxyType({"sqrt": 0.5, "square": 2, "cbrt": 1 / 3, "reciprocal": -1})
|
|
78
|
+
_NUMPY_FUNCTION_REDUCTIONS = (
|
|
79
|
+
_NUMPY_FUNCTION_PRESERVING | _NUMPY_FUNCTION_ADDITIVE | _NUMPY_STATISTICS
|
|
80
|
+
)
|
|
81
|
+
_NUMPY_FUNCTION_LIKE_CONSTRUCTORS = frozenset({"zeros_like"})
|
|
82
|
+
_NUMPY_FUNCTION_COMPOSITION = frozenset({"concatenate", "stack", "where"})
|
|
83
|
+
|
|
84
|
+
# Canonical unit per angular dimension, used to tell a caller what a rejected value
|
|
85
|
+
# actually carries and which factor clears it.
|
|
86
|
+
_ANGULAR_UNITS = MappingProxyType({"[angle]": "radian"})
|
|
87
|
+
|
|
88
|
+
# Pint declares [luminous_flux] and [illuminance] independently of the units carrying
|
|
89
|
+
# them, so redefining steradian alone leaves those named dimensions inconsistent.
|
|
90
|
+
_ANGLE_DEFINITIONS = (
|
|
91
|
+
"radian = [angle] = rad",
|
|
92
|
+
# A small patch on a sphere is a rectangle whose sides are arcs, so its solid angle
|
|
93
|
+
# is a product of two plane angles with the radius and every constant cancelling.
|
|
94
|
+
# Leonard (Metrologia 58 052001, 2021) and Kalinin (Metrologia 56 065009, 2019) both
|
|
95
|
+
# reach the same place: the steradian is a coherent derived unit, sr = rad**2.
|
|
96
|
+
"steradian = radian ** 2 = sr",
|
|
97
|
+
"[luminous_flux] = [luminosity] * [angle] ** 2",
|
|
98
|
+
"[illuminance] = [luminous_flux] / [length] ** 2",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Conjugate forms. Generalized force is energy per coordinate and conjugate momentum is
|
|
102
|
+
# action per coordinate, so once angle is a coordinate the dimension appears on its own;
|
|
103
|
+
# newton * meter carries no angle and would leave torque indistinguishable from energy.
|
|
104
|
+
_ANGLE_KIND_OVERRIDES = MappingProxyType(
|
|
105
|
+
{
|
|
106
|
+
"Dimensionless": "dimensionless",
|
|
107
|
+
"Torque": "joule / radian",
|
|
108
|
+
"AngularMomentum": "joule * second / radian",
|
|
109
|
+
"MomentOfInertia": "kilogram * meter ** 2 / radian ** 2",
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
_DEFAULT_KINDS = MappingProxyType(
|
|
114
|
+
{
|
|
115
|
+
"Dimensionless": "radian",
|
|
116
|
+
"Energy": "joule",
|
|
117
|
+
"Torque": "newton * meter",
|
|
118
|
+
"PlaneAngle": "radian",
|
|
119
|
+
"SolidAngle": "steradian",
|
|
120
|
+
"Frequency": "hertz",
|
|
121
|
+
"AngularVelocity": "radian / second",
|
|
122
|
+
"Action": "joule * second",
|
|
123
|
+
"AngularMomentum": "kilogram * meter ** 2 / second",
|
|
124
|
+
"Temperature": "kelvin",
|
|
125
|
+
"TemperatureDifference": "kelvin",
|
|
126
|
+
"Length": "meter",
|
|
127
|
+
"Time": "second",
|
|
128
|
+
"Mass": "kilogram",
|
|
129
|
+
"ElectricCurrent": "ampere",
|
|
130
|
+
"AmountOfSubstance": "mole",
|
|
131
|
+
"LuminousIntensity": "candela",
|
|
132
|
+
"Force": "newton",
|
|
133
|
+
"Pressure": "pascal",
|
|
134
|
+
"EnergyDensity": "joule / meter ** 3",
|
|
135
|
+
"Power": "watt",
|
|
136
|
+
"ElectricCharge": "coulomb",
|
|
137
|
+
"ElectricPotential": "volt",
|
|
138
|
+
"Capacitance": "farad",
|
|
139
|
+
"Resistance": "ohm",
|
|
140
|
+
"Conductance": "siemens",
|
|
141
|
+
"MagneticFlux": "weber",
|
|
142
|
+
"MagneticFluxDensity": "tesla",
|
|
143
|
+
"Inductance": "henry",
|
|
144
|
+
"LuminousFlux": "lumen",
|
|
145
|
+
"Illuminance": "lux",
|
|
146
|
+
"Radioactivity": "becquerel",
|
|
147
|
+
"AbsorbedDose": "gray",
|
|
148
|
+
"EquivalentDose": "sievert",
|
|
149
|
+
"CatalyticActivity": "katal",
|
|
150
|
+
"Velocity": "meter / second",
|
|
151
|
+
"Acceleration": "meter / second ** 2",
|
|
152
|
+
"Area": "meter ** 2",
|
|
153
|
+
"Volume": "meter ** 3",
|
|
154
|
+
"Density": "kilogram / meter ** 3",
|
|
155
|
+
"Entropy": "joule / kelvin",
|
|
156
|
+
"HeatCapacity": "joule / kelvin",
|
|
157
|
+
"KinematicViscosity": "meter ** 2 / second",
|
|
158
|
+
"Diffusivity": "meter ** 2 / second",
|
|
159
|
+
}
|
|
160
|
+
)
|