unitx 0.7.0__tar.gz → 0.7.1__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.
- {unitx-0.7.0 → unitx-0.7.1}/CHANGELOG.md +24 -0
- {unitx-0.7.0 → unitx-0.7.1}/PKG-INFO +35 -14
- {unitx-0.7.0 → unitx-0.7.1}/README.md +34 -13
- {unitx-0.7.0 → unitx-0.7.1}/pyproject.toml +1 -1
- unitx-0.7.1/src/unitx/_constnamespace.py +33 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/si/bases.py +9 -7
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/si/constants.py +14 -22
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/si/units.py +125 -70
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/unit.py +4 -3
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/unitregistry.py +4 -4
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_si.py +62 -1
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_singleunit.py +6 -2
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_unitregistry.py +28 -4
- {unitx-0.7.0 → unitx-0.7.1}/tests/typing/check_quantity.py +6 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/typing/check_si.py +9 -1
- unitx-0.7.0/src/unitx/_constclass.py +0 -34
- {unitx-0.7.0 → unitx-0.7.1}/.github/workflows/ci.yml +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/.github/workflows/release.yml +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/.gitignore +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/LICENSE +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/conftest.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/pyrightconfig.json +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/__init__.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/_numerics.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/_unicode.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/dimension.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/exceptions.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/factor.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/monomial.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/py.typed +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/quantity.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/si/__init__.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/singleunit.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/src/unitx/unitparser.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/__init__.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_builtin_definitions.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_dimension.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_factor.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_import.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_monomial.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_numeric.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_quantity.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_typing.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_unicode.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_unit.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_unit_definition.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_unitparser.py +0 -0
- {unitx-0.7.0 → unitx-0.7.1}/tests/test_warnings.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1] - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Accept a single exact spelling string for `prefixable`, equivalent to a
|
|
8
|
+
one-element tuple, with the same spelling validation before registration.
|
|
9
|
+
- Expand the `si` namespace's prefixed shortcuts and common units, including
|
|
10
|
+
percent, permille, permyriad, rotational frequency (`rpm`), speed of light
|
|
11
|
+
(`c`), gram-force, volt-ampere, ampere-hour, mho, and nit. Enable Torr prefixes.
|
|
12
|
+
- Add `SI.kappa` and the wavelength and frequency Wien displacement constants,
|
|
13
|
+
`SI.b` and `SI.b_`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Use fixed Decimal approximations with about 50 significant digits for pi and
|
|
18
|
+
the positive roots in the Wien displacement laws. Derived constants still use
|
|
19
|
+
the Decimal context active at import.
|
|
20
|
+
- Treat `u` and `Da` as spellings of one formal unit, with prefixes enabled only
|
|
21
|
+
on `Da`. Rebuild stored units when migrating from the separate `u` identity;
|
|
22
|
+
the mass conversion scale is unchanged. Use `kDa` rather than `kilodalton`.
|
|
23
|
+
- Keep ASCII micro spellings such as `um` and `us` convenient to type without
|
|
24
|
+
warnings. The explicit product `u m` retains its separate mass-times-length
|
|
25
|
+
meaning.
|
|
26
|
+
|
|
3
27
|
## [0.7.0] - 2026-10-19
|
|
4
28
|
|
|
5
29
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: unitx
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: A modern, intuitive Python library for units, quantities, and dimensional analysis.
|
|
5
5
|
Project-URL: Homepage, https://github.com/DaitTHU/unitx
|
|
6
6
|
Project-URL: Issues, https://github.com/DaitTHU/unitx/issues
|
|
@@ -22,11 +22,15 @@ Description-Content-Type: text/markdown
|
|
|
22
22
|
|
|
23
23
|
# unitx
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
[](https://pypi.org/project/unitx/)
|
|
26
|
+
[](https://pypi.org/project/unitx/)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
|
|
29
|
+
Write Python formulas with ready-to-use units and physical constants. `unitx`
|
|
26
30
|
handles scalar quantities, unit conversion, and dimensional analysis, with
|
|
27
31
|
exact rational unit scales and exponents.
|
|
28
32
|
|
|
29
|
-
unitx is in early development. Public APIs may change during the 0.x series.
|
|
33
|
+
`unitx` is in early development. Public APIs may change during the 0.x series.
|
|
30
34
|
|
|
31
35
|
## Installation
|
|
32
36
|
|
|
@@ -101,15 +105,17 @@ astronomical units are included. Units and constants can be combined directly:
|
|
|
101
105
|
|
|
102
106
|
Both namespaces are read-only and do not need to be instantiated. `si` exposes
|
|
103
107
|
a selection of convenient attributes; other registered prefix combinations
|
|
104
|
-
remain available through `Unit`, for example `Unit('Tm')`. Importing unitx
|
|
108
|
+
remain available through `Unit`, for example `Unit('Tm')`. Importing `unitx`
|
|
105
109
|
initializes the predefined unit catalogue. Defining a custom unit registers its
|
|
106
110
|
text spellings without adding an attribute to `si`.
|
|
107
111
|
|
|
108
112
|
SI defining constants retain their exact decimal values. Measured constants
|
|
109
113
|
are stored nominal values; measurement uncertainties are not propagated.
|
|
110
114
|
Derived Decimal constants are evaluated in the Decimal context active at
|
|
111
|
-
import.
|
|
112
|
-
|
|
115
|
+
import. Pi and the positive roots used by the Wien displacement constants
|
|
116
|
+
(`SI.b` for wavelength and `SI.b_` for frequency) use fixed Decimal
|
|
117
|
+
approximations with about 50 significant digits. Raising the context precision
|
|
118
|
+
does not add accuracy beyond those stored approximations.
|
|
113
119
|
|
|
114
120
|
## Constructing units and quantities
|
|
115
121
|
|
|
@@ -267,7 +273,7 @@ meaning rather than acting as string delimiters. Bare compound names are
|
|
|
267
273
|
not guessed from adjacent words, and ordinary parentheses still group products.
|
|
268
274
|
|
|
269
275
|
The unit table contains `s`, `m`, `g`, `A`, `K`, `mol`, and `cd`, plus the
|
|
270
|
-
dimensionless identity. Importing unitx also initializes the common units in
|
|
276
|
+
dimensionless identity. Importing `unitx` also initializes the common units in
|
|
271
277
|
`unitx.si`, including named derived units such as `N`. Mass scales are referenced
|
|
272
278
|
to kilograms: `g` has factor `1/1000`, and `kg` has factor `1`.
|
|
273
279
|
|
|
@@ -337,8 +343,9 @@ Bare multiword names are recognized only as complete inputs. For example,
|
|
|
337
343
|
as a factor; use `[astronomical unit] / arcsec`, symbols, or Unit objects instead.
|
|
338
344
|
|
|
339
345
|
SI prefixes are enabled by default. `prefixable: PrefixPolicy = True` accepts
|
|
340
|
-
`True` for all spellings, `False` for none,
|
|
341
|
-
and aliases supplied in that definition.
|
|
346
|
+
`True` for all spellings, `False` for none, a single spelling string, or a tuple
|
|
347
|
+
of exact symbols, names, and aliases supplied in that definition. A string such
|
|
348
|
+
as `prefixable='Da'` is equivalent to `prefixable=('Da',)`. Only the selected
|
|
342
349
|
spellings accept prefixes; an empty tuple disables prefixes. Unknown spellings
|
|
343
350
|
and non-string entries are errors. For example,
|
|
344
351
|
`prefixable=('arcsec', 'arcsecond')` enables all SI prefixes on those two
|
|
@@ -347,7 +354,7 @@ spellings, while other spellings of that unit remain unprefixable.
|
|
|
347
354
|
The tuple selects **unit spellings**, not prefix symbols. Prefix names attach
|
|
348
355
|
directly to the first word of a multiword name, as in `kiloastronomical unit`;
|
|
349
356
|
a separate `kilo astronomical unit` is not accepted. `PrefixPolicy` is available
|
|
350
|
-
from `unitx.unitregistry` for annotations and means `bool | tuple[str, ...]`.
|
|
357
|
+
from `unitx.unitregistry` for annotations and means `bool | str | tuple[str, ...]`.
|
|
351
358
|
|
|
352
359
|
```pycon
|
|
353
360
|
>>> tick = si.s.define(
|
|
@@ -390,10 +397,24 @@ form; `760 * si.mmHg` converts to `si.atm` with value 1 under the library's
|
|
|
390
397
|
existing pressure convention. Prefixes do not stack: `mmmHg` and `kmmHg` are
|
|
391
398
|
invalid. `kt` retains its meaning as a knot alias; tonne remains unprefixable.
|
|
392
399
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
400
|
+
`si.mas` and `si.uas` provide milli- and microarcseconds as namespace shortcuts;
|
|
401
|
+
their registered expressions are `marcsec` and `µarcsec`. The strings `mas`
|
|
402
|
+
and `uas` are not registered aliases. `si.Da` and `si.u` now share one formal
|
|
403
|
+
unit identity; only the spelling `Da` accepts prefixes, such as `kDa`.
|
|
404
|
+
`si.mTorr` is available, and Torr accepts SI prefixes. Tonne uses `t` or
|
|
405
|
+
`tonne`; the ambiguous spelling `ton` is not registered.
|
|
406
|
+
|
|
407
|
+
`si.rpm` represents rotational frequency: `60 * si.rpm` converts to `si.Hz`
|
|
408
|
+
with value 1. For angular velocity, multiply that frequency by `2 * PI` and
|
|
409
|
+
convert to `si.rad / si.s`; directly converting frequency to `rad/s` does
|
|
410
|
+
not insert the physical factor of 2π. `si.c` is a speed unit equal to the
|
|
411
|
+
speed of light, while `SI.c` is the corresponding physical quantity.
|
|
412
|
+
|
|
413
|
+
The spelling `u` denotes the unified atomic mass unit when used on its own.
|
|
414
|
+
ASCII micro spellings such as `um` and `us` remain supported without warnings,
|
|
415
|
+
so entering non-ASCII characters is optional. `um`, `µm`, `μm`, and
|
|
416
|
+
`micrometre` all mean micrometres; the explicit product `u m` instead means
|
|
417
|
+
unified atomic mass unit times metre.
|
|
397
418
|
|
|
398
419
|
Registration raises `UnitRegistryError` if a new spelling, including a generated
|
|
399
420
|
prefixed form, already resolves to a unit or conflicts with another spelling
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# unitx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://pypi.org/project/unitx/)
|
|
4
|
+
[](https://pypi.org/project/unitx/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Write Python formulas with ready-to-use units and physical constants. `unitx`
|
|
4
8
|
handles scalar quantities, unit conversion, and dimensional analysis, with
|
|
5
9
|
exact rational unit scales and exponents.
|
|
6
10
|
|
|
7
|
-
unitx is in early development. Public APIs may change during the 0.x series.
|
|
11
|
+
`unitx` is in early development. Public APIs may change during the 0.x series.
|
|
8
12
|
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
@@ -79,15 +83,17 @@ astronomical units are included. Units and constants can be combined directly:
|
|
|
79
83
|
|
|
80
84
|
Both namespaces are read-only and do not need to be instantiated. `si` exposes
|
|
81
85
|
a selection of convenient attributes; other registered prefix combinations
|
|
82
|
-
remain available through `Unit`, for example `Unit('Tm')`. Importing unitx
|
|
86
|
+
remain available through `Unit`, for example `Unit('Tm')`. Importing `unitx`
|
|
83
87
|
initializes the predefined unit catalogue. Defining a custom unit registers its
|
|
84
88
|
text spellings without adding an attribute to `si`.
|
|
85
89
|
|
|
86
90
|
SI defining constants retain their exact decimal values. Measured constants
|
|
87
91
|
are stored nominal values; measurement uncertainties are not propagated.
|
|
88
92
|
Derived Decimal constants are evaluated in the Decimal context active at
|
|
89
|
-
import.
|
|
90
|
-
|
|
93
|
+
import. Pi and the positive roots used by the Wien displacement constants
|
|
94
|
+
(`SI.b` for wavelength and `SI.b_` for frequency) use fixed Decimal
|
|
95
|
+
approximations with about 50 significant digits. Raising the context precision
|
|
96
|
+
does not add accuracy beyond those stored approximations.
|
|
91
97
|
|
|
92
98
|
## Constructing units and quantities
|
|
93
99
|
|
|
@@ -245,7 +251,7 @@ meaning rather than acting as string delimiters. Bare compound names are
|
|
|
245
251
|
not guessed from adjacent words, and ordinary parentheses still group products.
|
|
246
252
|
|
|
247
253
|
The unit table contains `s`, `m`, `g`, `A`, `K`, `mol`, and `cd`, plus the
|
|
248
|
-
dimensionless identity. Importing unitx also initializes the common units in
|
|
254
|
+
dimensionless identity. Importing `unitx` also initializes the common units in
|
|
249
255
|
`unitx.si`, including named derived units such as `N`. Mass scales are referenced
|
|
250
256
|
to kilograms: `g` has factor `1/1000`, and `kg` has factor `1`.
|
|
251
257
|
|
|
@@ -315,8 +321,9 @@ Bare multiword names are recognized only as complete inputs. For example,
|
|
|
315
321
|
as a factor; use `[astronomical unit] / arcsec`, symbols, or Unit objects instead.
|
|
316
322
|
|
|
317
323
|
SI prefixes are enabled by default. `prefixable: PrefixPolicy = True` accepts
|
|
318
|
-
`True` for all spellings, `False` for none,
|
|
319
|
-
and aliases supplied in that definition.
|
|
324
|
+
`True` for all spellings, `False` for none, a single spelling string, or a tuple
|
|
325
|
+
of exact symbols, names, and aliases supplied in that definition. A string such
|
|
326
|
+
as `prefixable='Da'` is equivalent to `prefixable=('Da',)`. Only the selected
|
|
320
327
|
spellings accept prefixes; an empty tuple disables prefixes. Unknown spellings
|
|
321
328
|
and non-string entries are errors. For example,
|
|
322
329
|
`prefixable=('arcsec', 'arcsecond')` enables all SI prefixes on those two
|
|
@@ -325,7 +332,7 @@ spellings, while other spellings of that unit remain unprefixable.
|
|
|
325
332
|
The tuple selects **unit spellings**, not prefix symbols. Prefix names attach
|
|
326
333
|
directly to the first word of a multiword name, as in `kiloastronomical unit`;
|
|
327
334
|
a separate `kilo astronomical unit` is not accepted. `PrefixPolicy` is available
|
|
328
|
-
from `unitx.unitregistry` for annotations and means `bool | tuple[str, ...]`.
|
|
335
|
+
from `unitx.unitregistry` for annotations and means `bool | str | tuple[str, ...]`.
|
|
329
336
|
|
|
330
337
|
```pycon
|
|
331
338
|
>>> tick = si.s.define(
|
|
@@ -368,10 +375,24 @@ form; `760 * si.mmHg` converts to `si.atm` with value 1 under the library's
|
|
|
368
375
|
existing pressure convention. Prefixes do not stack: `mmmHg` and `kmmHg` are
|
|
369
376
|
invalid. `kt` retains its meaning as a knot alias; tonne remains unprefixable.
|
|
370
377
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
378
|
+
`si.mas` and `si.uas` provide milli- and microarcseconds as namespace shortcuts;
|
|
379
|
+
their registered expressions are `marcsec` and `µarcsec`. The strings `mas`
|
|
380
|
+
and `uas` are not registered aliases. `si.Da` and `si.u` now share one formal
|
|
381
|
+
unit identity; only the spelling `Da` accepts prefixes, such as `kDa`.
|
|
382
|
+
`si.mTorr` is available, and Torr accepts SI prefixes. Tonne uses `t` or
|
|
383
|
+
`tonne`; the ambiguous spelling `ton` is not registered.
|
|
384
|
+
|
|
385
|
+
`si.rpm` represents rotational frequency: `60 * si.rpm` converts to `si.Hz`
|
|
386
|
+
with value 1. For angular velocity, multiply that frequency by `2 * PI` and
|
|
387
|
+
convert to `si.rad / si.s`; directly converting frequency to `rad/s` does
|
|
388
|
+
not insert the physical factor of 2π. `si.c` is a speed unit equal to the
|
|
389
|
+
speed of light, while `SI.c` is the corresponding physical quantity.
|
|
390
|
+
|
|
391
|
+
The spelling `u` denotes the unified atomic mass unit when used on its own.
|
|
392
|
+
ASCII micro spellings such as `um` and `us` remain supported without warnings,
|
|
393
|
+
so entering non-ASCII characters is optional. `um`, `µm`, `μm`, and
|
|
394
|
+
`micrometre` all mean micrometres; the explicit product `u m` instead means
|
|
395
|
+
unified atomic mass unit times metre.
|
|
375
396
|
|
|
376
397
|
Registration raises `UnitRegistryError` if a new spelling, including a generated
|
|
377
398
|
prefixed form, already resolves to a unit or conflicts with another spelling
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Class-based namespaces for constants accessed directly as class attributes.
|
|
2
|
+
|
|
3
|
+
Namespaces cannot be instantiated, and their attributes cannot be rebound or
|
|
4
|
+
deleted. Objects referenced by those attributes are not recursively frozen.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, NoReturn
|
|
8
|
+
|
|
9
|
+
__all__ = ['ConstNamespace']
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _ConstNamespaceMeta(type):
|
|
13
|
+
def __call__(metacls, *args: Any, **kwds: Any) -> NoReturn:
|
|
14
|
+
"""
|
|
15
|
+
This namespace cannot be instantiated; access constants via class attributes.
|
|
16
|
+
"""
|
|
17
|
+
raise TypeError(metacls.__call__.__doc__)
|
|
18
|
+
|
|
19
|
+
def __setattr__(cls, name, value) -> NoReturn:
|
|
20
|
+
"""
|
|
21
|
+
Constant namespace attributes cannot be assigned.
|
|
22
|
+
"""
|
|
23
|
+
raise AttributeError(cls.__class__.__setattr__.__doc__)
|
|
24
|
+
|
|
25
|
+
def __delattr__(cls, name) -> NoReturn:
|
|
26
|
+
"""
|
|
27
|
+
Constant namespace attributes cannot be deleted.
|
|
28
|
+
"""
|
|
29
|
+
raise AttributeError(cls.__class__.__delattr__.__doc__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ConstNamespace(metaclass=_ConstNamespaceMeta):
|
|
33
|
+
"""Base for constant namespaces with protected class attribute bindings."""
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Final
|
|
4
4
|
|
|
5
|
-
from ..
|
|
6
|
-
from ..factor import ONE
|
|
5
|
+
from .._constnamespace import ConstNamespace
|
|
7
6
|
from ..quantity import Quantity, quantity
|
|
8
7
|
from ..unit import Unit
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
class _si(
|
|
10
|
+
class _si(ConstNamespace):
|
|
12
11
|
# SI base unit
|
|
13
12
|
|
|
14
13
|
one: Final = Unit()
|
|
@@ -34,7 +33,7 @@ class _si(ConstClass):
|
|
|
34
33
|
"""radian, plane angle"""
|
|
35
34
|
sr: Final = one.define('sr', name='steradian')
|
|
36
35
|
"""steradian, solid angle"""
|
|
37
|
-
Hz: Final = (
|
|
36
|
+
Hz: Final = (s**-1).define('Hz', name='hertz')
|
|
38
37
|
"""hertz, frequency"""
|
|
39
38
|
N: Final = (kg * m / s**2).define('N', name='newton')
|
|
40
39
|
"""newton, force/weight"""
|
|
@@ -64,7 +63,7 @@ class _si(ConstClass):
|
|
|
64
63
|
"""lumen, luminous flux"""
|
|
65
64
|
lx: Final = (lm / m**2).define('lx', name='lux')
|
|
66
65
|
"""lux, illuminance"""
|
|
67
|
-
Bq: Final = (
|
|
66
|
+
Bq: Final = (s**-1).define('Bq', name='becquerel')
|
|
68
67
|
"""becquerel, activity referred to a radionuclide"""
|
|
69
68
|
Gy: Final = (J / kg).define('Gy', name='gray')
|
|
70
69
|
"""gray, absorbed dose"""
|
|
@@ -74,9 +73,9 @@ class _si(ConstClass):
|
|
|
74
73
|
"""katal, catalytic activity"""
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
class _SI(
|
|
76
|
+
class _SI(ConstNamespace):
|
|
78
77
|
DeltanuCs: Final = Quantity(9_192_631_770, _si.Hz)
|
|
79
|
-
"""hyperfine transition frequency of Cs-133, definition of second."""
|
|
78
|
+
"""hyperfine transition frequency ∆ν of Cs-133, definition of second."""
|
|
80
79
|
c: Final = Quantity(299_792_458, _si.m / _si.s)
|
|
81
80
|
"""speed of light in vacuum"""
|
|
82
81
|
h: Final = quantity(6.626_070_15e-34, _si.J * _si.s)
|
|
@@ -89,3 +88,6 @@ class _SI(ConstClass):
|
|
|
89
88
|
"""Avogadro constant"""
|
|
90
89
|
Kcd: Final = Quantity(683, _si.lm / _si.W)
|
|
91
90
|
"""luminous efficacy of 540 THz radiation, definition of candela"""
|
|
91
|
+
|
|
92
|
+
g: Final = quantity(9.80665, _si.m / _si.s**2) # defined here for si.kgf
|
|
93
|
+
"""standard acceleration of gravity"""
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import math
|
|
2
1
|
from decimal import Decimal
|
|
3
2
|
from typing import Final
|
|
4
3
|
|
|
5
|
-
from ..
|
|
4
|
+
from .._constnamespace import ConstNamespace
|
|
6
5
|
from ..quantity import quantity
|
|
7
6
|
from .bases import _SI
|
|
8
7
|
from .units import si
|
|
9
8
|
|
|
10
9
|
__all__ = ['SI', 'Particle']
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
_PI = Decimal('3.1415926535897932384626433832795028841971693993751')
|
|
12
|
+
"""the ratio of the circumference of a circle to its diameter"""
|
|
13
|
+
_WIEN_W = Decimal('4.9651142317442763036987591313228939440555849867973')
|
|
14
|
+
"""Approximate positive root of x = 5*(1 - exp(-x))."""
|
|
15
|
+
_WIEN_F = Decimal('2.8214393721220788934031913302944851953458817440731')
|
|
16
|
+
"""Approximate positive root of x = 3*(1 - exp(-x))."""
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class SI(_SI):
|
|
@@ -31,8 +34,6 @@ class SI(_SI):
|
|
|
31
34
|
|
|
32
35
|
hbar: Final = _SI.h / (2 * _PI)
|
|
33
36
|
"""reduced Planck constant, ħ: Final = h / 2π"""
|
|
34
|
-
g: Final = quantity(9.80665, 'm/s2')
|
|
35
|
-
"""standard acceleration of gravity"""
|
|
36
37
|
T0: Final = quantity(273.15, si.K)
|
|
37
38
|
"""standard temperature"""
|
|
38
39
|
|
|
@@ -40,12 +41,8 @@ class SI(_SI):
|
|
|
40
41
|
|
|
41
42
|
G: Final = quantity(6.674_30e-11, 'm3/(kg*s2)')
|
|
42
43
|
"""Newtonian constant of gravitation"""
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# H0: Final = Quantity(70, 'km/s*Mpc')
|
|
46
|
-
# """Hubble constant, approx"""
|
|
47
|
-
# Lambda: Final = quantity(1.089e-52, 'm-2')
|
|
48
|
-
# """cosmological constant, approx"""
|
|
44
|
+
kappa: Final = (8 * _PI * G / _SI.c**4).to(si.N**-1)
|
|
45
|
+
"""Einstein gravitational constant"""
|
|
49
46
|
|
|
50
47
|
# electromagnetic constants
|
|
51
48
|
|
|
@@ -104,7 +101,6 @@ class SI(_SI):
|
|
|
104
101
|
"""molar gas constant"""
|
|
105
102
|
F: Final = _SI.NA * _SI.e
|
|
106
103
|
"""Faraday constant"""
|
|
107
|
-
|
|
108
104
|
sigma: Final = (_PI**2 * _SI.kB**4 / (60 * hbar**3 * _SI.c**2)).to('W/(m2*K4)')
|
|
109
105
|
"""Stefan-Boltzmann constant"""
|
|
110
106
|
c1L: Final = (2 * _SI.h * _SI.c**2).to('W*m2/sr')
|
|
@@ -113,22 +109,18 @@ class SI(_SI):
|
|
|
113
109
|
"""first radiation constant"""
|
|
114
110
|
c2: Final = _SI.h * _SI.c / _SI.kB
|
|
115
111
|
"""second radiation constant"""
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
b: Final = c2 / _WIEN_W
|
|
113
|
+
"""Wien wavelength displacement law constant"""
|
|
114
|
+
b_: Final = (_WIEN_F * _SI.c / c2).to('Hz/K')
|
|
115
|
+
"""Wien frequency displacement law constant"""
|
|
120
116
|
|
|
121
117
|
Vm: Final = (R * T0 / si.bar).to('m3/mol')
|
|
122
118
|
"""molar volume of ideal gas (273.15 K, 100 kPa)"""
|
|
123
|
-
# Vmatm: Final = (R * T0 / si.atm).to(Vm.unit)
|
|
124
|
-
# """molar volume of ideal gas (273.15 K, 101.325 kPa)"""
|
|
125
119
|
n0: Final = _SI.NA / Vm
|
|
126
120
|
"""Loschmidt constant (273.15 K, 100 kPa)"""
|
|
127
|
-
# n0atm: Final = _SI.NA / Vmatm
|
|
128
|
-
# """Loschmidt constant (273.15 K, 101.32 kPa)"""
|
|
129
121
|
|
|
130
122
|
|
|
131
|
-
class Particle(
|
|
123
|
+
class Particle(ConstNamespace):
|
|
132
124
|
"""
|
|
133
125
|
Common particle data, including mass and magnetic moment of
|
|
134
126
|
electron (e), muon (μ), tau (τ), proton (p), neutron (n),
|
|
@@ -20,7 +20,7 @@ def _prefixed_units(unit: str, prefixes: str | list[str]) -> Iterator[Unit]:
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class si(_si):
|
|
23
|
-
"""The `si`
|
|
23
|
+
"""The `si` namespace offers a comprehensive set of common units
|
|
24
24
|
such as meters (m), kilograms (kg), and more.
|
|
25
25
|
|
|
26
26
|
These units can be directly used in formulas or code.
|
|
@@ -32,72 +32,67 @@ class si(_si):
|
|
|
32
32
|
|
|
33
33
|
# SI base unit
|
|
34
34
|
|
|
35
|
-
as_, fs, ps, ns, us, ms = _prefixed_units('s', '
|
|
35
|
+
ys, zs, as_, fs, ps, ns, us, ms = _prefixed_units('s', 'yzafpnum')
|
|
36
36
|
"""second, time"""
|
|
37
|
-
fm, pm, nm, um, mm, cm, dm, km = _prefixed_units('m', '
|
|
37
|
+
am, fm, pm, nm, um, mm, cm, dm, km, Mm, Gm = _prefixed_units('m', 'afpnumcdkMG')
|
|
38
38
|
"""metre/meter, length"""
|
|
39
|
-
pg, ng, ug, mg, g = _prefixed_units('g', '
|
|
39
|
+
fg, pg, ng, ug, mg, g, Mg, Gg = _prefixed_units('g', 'fpnum MG')
|
|
40
40
|
"""gram, mass"""
|
|
41
|
-
pA, nA, uA, mA, kA = _prefixed_units('A', '
|
|
41
|
+
fA, pA, nA, uA, mA, kA, MA = _prefixed_units('A', 'fpnumkM')
|
|
42
42
|
"""ampere, electric current"""
|
|
43
43
|
nK, uK, mK = _prefixed_units('K', 'num')
|
|
44
44
|
"""kelvin, thermodynamic temperature"""
|
|
45
|
-
pmol, nmol, umol, mmol, kmol = _prefixed_units('mol', '
|
|
45
|
+
fmol, pmol, nmol, umol, mmol, kmol = _prefixed_units('mol', 'fpnumk')
|
|
46
46
|
"""mole, amount of substance"""
|
|
47
|
-
mcd
|
|
47
|
+
ucd, mcd, kcd = _prefixed_units('cd', 'umk')
|
|
48
48
|
"""candela, luminous intensity"""
|
|
49
49
|
|
|
50
50
|
# SI derived unit
|
|
51
51
|
|
|
52
|
-
urad, mrad = _prefixed_units('rad', '
|
|
52
|
+
nrad, urad, mrad = _prefixed_units('rad', 'num')
|
|
53
53
|
"""radian, plane angle"""
|
|
54
|
-
kHz, MHz, GHz, THz, PHz = _prefixed_units('Hz', '
|
|
54
|
+
nHz, uHz, mHz, kHz, MHz, GHz, THz, PHz, EHz = _prefixed_units('Hz', 'numkMGTPE')
|
|
55
55
|
"""hertz, frequency"""
|
|
56
|
-
pN, nN, uN, mN, kN, MN = _prefixed_units('N', '
|
|
56
|
+
fN, pN, nN, uN, mN, kN, MN, GN = _prefixed_units('N', 'fpnumkMG')
|
|
57
57
|
"""newton, force/weight"""
|
|
58
|
-
mPa, hPa, kPa, MPa, GPa = _prefixed_units('Pa', '
|
|
58
|
+
fPa, nPa, uPa, mPa, hPa, kPa, MPa, GPa, TPa = _prefixed_units('Pa', 'fnumhkMGT')
|
|
59
59
|
"""pascal, pressure/stress"""
|
|
60
|
-
nJ, uJ, mJ, kJ, MJ, GJ = _prefixed_units('J', '
|
|
60
|
+
fJ, pJ, nJ, uJ, mJ, kJ, MJ, GJ, TJ, PJ = _prefixed_units('J', 'fpnumkMGTP')
|
|
61
61
|
"""joule, energy/work/heat"""
|
|
62
|
-
pW, nW, uW, mW, kW, MW, GW, TW = _prefixed_units('W', '
|
|
62
|
+
fW, pW, nW, uW, mW, kW, MW, GW, TW, PW = _prefixed_units('W', 'fpnumkMGTP')
|
|
63
63
|
"""watt, power/radiant flux"""
|
|
64
|
-
fC, pC, nC, uC, mC = _prefixed_units('C', '
|
|
64
|
+
aC, fC, pC, nC, uC, mC, kC = _prefixed_units('C', 'afpnumk')
|
|
65
65
|
"""coulomb, electric charge"""
|
|
66
|
-
nV, uV, mV, kV, MV = _prefixed_units('V', '
|
|
66
|
+
pV, nV, uV, mV, kV, MV, GV = _prefixed_units('V', 'pnumkMG')
|
|
67
67
|
"""volt, electric potential/voltage/emf"""
|
|
68
|
-
fF, pF, nF, uF, mF = _prefixed_units('F', '
|
|
68
|
+
aF, fF, pF, nF, uF, mF, kF = _prefixed_units('F', 'afpnumk')
|
|
69
69
|
"""farad, capacitance"""
|
|
70
|
-
uohm, mohm, kohm, Mohm, Gohm = _prefixed_units('Ω', '
|
|
70
|
+
nohm, uohm, mohm, kohm, Mohm, Gohm, Tohm = _prefixed_units('Ω', 'numkMGT')
|
|
71
71
|
"""ohm, resistance/impedance/reactance"""
|
|
72
|
-
uS, mS = _prefixed_units('S', '
|
|
72
|
+
pS, nS, uS, mS, kS = _prefixed_units('S', 'pnumk')
|
|
73
73
|
"""siemens, electrical conductance"""
|
|
74
|
-
uWb, mWb = _prefixed_units('Wb', '
|
|
74
|
+
nWb, uWb, mWb, kWb = _prefixed_units('Wb', 'numk')
|
|
75
75
|
"""weber, magnetic flux"""
|
|
76
|
-
nT, uT, mT = _prefixed_units('T', '
|
|
76
|
+
fT, pT, nT, uT, mT, kT = _prefixed_units('T', 'fpnumk')
|
|
77
77
|
"""tesla, magnetic flux density"""
|
|
78
|
-
nH, uH, mH = _prefixed_units('H', '
|
|
78
|
+
pH, nH, uH, mH, kH = _prefixed_units('H', 'pnumk')
|
|
79
79
|
"""henry, inductance"""
|
|
80
|
-
|
|
80
|
+
ulm, mlm, klm, Mlm = _prefixed_units('lm', 'umkM')
|
|
81
|
+
"""lumen, luminous flux"""
|
|
82
|
+
mlx, klx = _prefixed_units('lx', 'mk')
|
|
83
|
+
"""lux, illuminance"""
|
|
84
|
+
mBq, kBq, MBq, GBq, TBq, PBq = _prefixed_units('Bq', 'mkMGTP')
|
|
81
85
|
"""becquerel, activity referred to a radionuclide"""
|
|
82
|
-
uGy, mGy = _prefixed_units('Gy', '
|
|
86
|
+
nGy, uGy, mGy, kGy, MGy = _prefixed_units('Gy', 'numkM')
|
|
83
87
|
"""gray, absorbed dose"""
|
|
84
|
-
nSv, uSv, mSv = _prefixed_units('Sv', '
|
|
88
|
+
pSv, nSv, uSv, mSv, kSv = _prefixed_units('Sv', 'pnumk')
|
|
85
89
|
"""sievert, equivalent dose"""
|
|
90
|
+
nkat, ukat, mkat = _prefixed_units('kat', 'num')
|
|
91
|
+
"""katal, catalytic activity"""
|
|
86
92
|
|
|
87
|
-
#
|
|
93
|
+
# other common units
|
|
88
94
|
|
|
89
|
-
|
|
90
|
-
"""minute, 1 min = 60 s, time"""
|
|
91
|
-
h: Final = (60 * min).define('h', name='hour', prefixable=False)
|
|
92
|
-
"""hour, 1 h = 60 min, time"""
|
|
93
|
-
d: Final = (24 * h).define('d', name='day', prefixable=False)
|
|
94
|
-
"""day, 1 d = 24 h, time"""
|
|
95
|
-
yr: Final = (365 * d).define('yr', name='year', prefixable=False)
|
|
96
|
-
"""simple year, 1 yr = 365 d, time"""
|
|
97
|
-
a_j: Final = (365.25 * d).define('a_j', name='Julian year')
|
|
98
|
-
"""Julian year, 1 a_j = 365.25 d, time"""
|
|
99
|
-
mo: Final = (a_j / 12).define('mo', name='month', prefixable=False)
|
|
100
|
-
"""month, 1 mo = 1/12 a_j, time"""
|
|
95
|
+
# dimensionless
|
|
101
96
|
deg: Final = (PI / 180 * _si.rad).define(
|
|
102
97
|
'°', name='degree', symbol_aliases='deg', prefixable=False
|
|
103
98
|
)
|
|
@@ -113,66 +108,126 @@ class si(_si):
|
|
|
113
108
|
prefixable=('arcsec', 'arcsecond'),
|
|
114
109
|
)
|
|
115
110
|
"""arcsecond, plane angle"""
|
|
111
|
+
mas, uas = _prefixed_units('arcsec', 'mu')
|
|
112
|
+
"""arcsecond, plane angle"""
|
|
113
|
+
percent: Final = (_si.one / 100).define(
|
|
114
|
+
'%', name='percent', symbol_aliases='٪', prefixable=False
|
|
115
|
+
)
|
|
116
|
+
"""percent, 1/100, dimensionless"""
|
|
117
|
+
permille: Final = (_si.one / 1000).define('‰', name='permille', prefixable=False)
|
|
118
|
+
"""permille, 1/1000, dimensionless"""
|
|
119
|
+
permyriad: Final = (_si.one / 10000).define(
|
|
120
|
+
'‱', 'permyriad', 'per ten thousand', prefixable=False
|
|
121
|
+
)
|
|
122
|
+
"""permyriad, 1/10000, dimensionless"""
|
|
123
|
+
# time/frequency related
|
|
124
|
+
min: Final = (60 * _si.s).define('min', name='minute', prefixable=False)
|
|
125
|
+
"""minute, 1 min = 60 s, time"""
|
|
126
|
+
h: Final = (60 * min).define('h', name='hour', prefixable=False)
|
|
127
|
+
"""hour, 1 h = 60 min, time"""
|
|
128
|
+
d: Final = (24 * h).define('d', name='day', prefixable=False)
|
|
129
|
+
"""day, 1 d = 24 h, time"""
|
|
130
|
+
yr: Final = (365 * d).define('yr', name='year', prefixable=False)
|
|
131
|
+
"""simple year, 1 yr = 365 d, time"""
|
|
132
|
+
a_j: Final = (365.25 * d).define('a_j', 'Julian year', 'annum')
|
|
133
|
+
"""Julian year, 1 a_j = 365.25 d, time"""
|
|
134
|
+
mo: Final = (a_j / 12).define('mo', name='month', prefixable=False)
|
|
135
|
+
"""month, 1 mo = 1/12 a_j, time"""
|
|
136
|
+
rpm: Final = (1 / min).define('rpm', name='revolutions per minute', prefixable=False)
|
|
137
|
+
"""revoltions per minute, 60 rpm = 1 Hz, rotational speed"""
|
|
138
|
+
# length/area/volume related
|
|
139
|
+
au: Final = (149_597_870_700 * _si.m).define(
|
|
140
|
+
'au', name='astronomical unit', symbol_aliases='AU'
|
|
141
|
+
)
|
|
142
|
+
"""astronomical unit, length"""
|
|
143
|
+
pc: Final = (au / arcsec).define('pc', name='parsec')
|
|
144
|
+
"""parsec, length"""
|
|
145
|
+
kpc, Mpc, Gpc, Tpc = _prefixed_units('pc', 'kMGT')
|
|
146
|
+
"""parsec, length"""
|
|
147
|
+
ly: Final = (_SI.c * a_j).define('ly', name='light year', prefixable=False)
|
|
148
|
+
"""light year, length"""
|
|
149
|
+
nmile: Final = (1852 * _si.m).define('nmi', 'nautical mile', symbol_aliases='NM')
|
|
150
|
+
"""nautical mile, length in marine and aerial navigation"""
|
|
151
|
+
angstrom: Final = (_si.m / TEN**10).define(
|
|
152
|
+
'Å', 'ångström', 'angstrom', symbol_aliases=('Å', 'Å')
|
|
153
|
+
) # symbol: chr(0xC5), chr(0x212B), 'A'+chr(0x30A)
|
|
154
|
+
"""ångström, 1 Å = 10⁻¹⁰ m,
|
|
155
|
+
lengths of chemical bonds, sizes of atoms and molecules, and wavelengths.
|
|
156
|
+
"""
|
|
116
157
|
a: Final = (100 * _si.m**2).define('a', name='are', prefixable=False)
|
|
117
158
|
"""are, 1 a = 100 m², land area"""
|
|
118
159
|
ha: Final = (100 * a).define('ha', name='hectare', prefixable=False)
|
|
119
160
|
"""hectare, 1 ha = 100 a = 10000 m², land area"""
|
|
120
161
|
b: Final = (_si.m**2 / TEN**28).define('b', name='barn')
|
|
121
162
|
"""barn, 1 b = 10⁻²⁸ m², effective cross sections."""
|
|
122
|
-
L: Final = (dm**3).define('L', '
|
|
123
|
-
"""liter
|
|
124
|
-
nL, uL, mL, cL, dL = _prefixed_units('L', '
|
|
125
|
-
"""liter
|
|
163
|
+
L: Final = (dm**3).define('L', 'litre', 'liter', symbol_aliases='l')
|
|
164
|
+
"""litre/liter, 1 L = 1 dm³, volume"""
|
|
165
|
+
fL, pL, nL, uL, mL, cL, dL, kL, ML = _prefixed_units('L', 'fpnumcdkM')
|
|
166
|
+
"""litre/liter, 1 L = 1 dm³, volume"""
|
|
167
|
+
# mass related
|
|
126
168
|
t: Final = (1000 * _si.kg).define('t', name='tonne', prefixable=False)
|
|
127
169
|
"""tonne, 1 t = 1000 kg, mass"""
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
'Å', 'ångström', 'angstrom', symbol_aliases=('Å', 'Å')
|
|
170
|
+
Da: Final = (Decimal('1.660_539_068_92e-27') * _si.kg).define(
|
|
171
|
+
'Da', 'dalton', 'unified atomic mass unit', symbol_aliases='u', prefixable='Da'
|
|
131
172
|
)
|
|
132
|
-
"""ångström, 1 Å = 10⁻¹⁰ m,
|
|
133
|
-
lengths of chemical bonds, sizes of atoms and molecules, and wavelengths.
|
|
134
|
-
"""
|
|
135
|
-
Gal: Final = (cm / _si.s**2).define('Gal', name='gal')
|
|
136
|
-
"""gal, acceleration"""
|
|
137
|
-
bar: Final = (TEN**5 * _si.Pa).define('bar', name='bar')
|
|
138
|
-
"""bar, standard-state pressure"""
|
|
139
|
-
mbar: Final = Unit('mbar')
|
|
140
|
-
"""millibar, standard-state pressure"""
|
|
141
|
-
Da: Final = (Decimal('1.660_539_068_92e-27') * _si.kg).define('Da', name='dalton')
|
|
142
173
|
"""dalton, unified atomic mass unit, 1 Da = m(¹²C) / 12, mass"""
|
|
143
|
-
|
|
174
|
+
kDa, MDa = _prefixed_units('Da', 'kM')
|
|
175
|
+
"""dalton, unified atomic mass unit, 1 Da = m(¹²C) / 12, mass"""
|
|
176
|
+
u: Final = Unit('u')
|
|
144
177
|
"""unified atomic mass unit, 1 u = m(¹²C) / 12, mass"""
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"""light year, length"""
|
|
151
|
-
nmile: Final = (1852 * _si.m).define('nmi', 'nautical mile', symbol_aliases='NM')
|
|
152
|
-
"""nautical mile, length in marine and aerial navigation"""
|
|
178
|
+
# kinetic related
|
|
179
|
+
c: Final = _SI.c.define('c', name='speed of light', prefixable=False)
|
|
180
|
+
"""speed of light in vacuum, 299_792_458 m/s"""
|
|
181
|
+
Gal: Final = (cm / _si.s**2).define('Gal', 'gal', 'galileo')
|
|
182
|
+
"""gal, acceleration"""
|
|
153
183
|
knot: Final = (nmile / h).define('kn', 'knot', symbol_aliases='kt')
|
|
154
184
|
"""knot, speed in marine and aerial navigation"""
|
|
185
|
+
# dynamic related
|
|
186
|
+
gf: Final = (_SI.g * g).define('gf', 'gram-force', 'gram force')
|
|
187
|
+
"""gram-force, 1 gf = 1 g * 9.80665 m/s², force"""
|
|
188
|
+
kgf: Final = Unit('kgf')
|
|
189
|
+
"""kilogram-force, 1 kgf = 1 kg * 9.80665 m/s², force"""
|
|
190
|
+
bar: Final = (TEN**5 * _si.Pa).define('bar', name='bar')
|
|
191
|
+
"""bar, standard-state pressure"""
|
|
192
|
+
ubar, mbar = _prefixed_units('bar', 'um')
|
|
193
|
+
"""bar, standard-state pressure"""
|
|
155
194
|
atm: Final = (101_325 * _si.Pa).define('atm', name='atmosphere')
|
|
156
195
|
"""standard atmosphere"""
|
|
157
196
|
mHg: Final = (1000 * (atm / 760)).define('mHg', name='metre of mercury')
|
|
158
197
|
"""metre of mercury, 0.760 mHg = 1 atm, pressure"""
|
|
159
198
|
mmHg: Final = Unit('mmHg')
|
|
160
199
|
"""millimetre of mercury, 760 mmHg = 1 atm, pressure"""
|
|
161
|
-
Torr: Final = mmHg.define('Torr', 'torr', 'torricelli'
|
|
200
|
+
Torr: Final = mmHg.define('Torr', 'torr', 'torricelli')
|
|
162
201
|
"""torr, 760 Torr = 1 atm, pressure"""
|
|
202
|
+
mTorr: Final = Unit('mTorr')
|
|
203
|
+
"""millitorr, pressure"""
|
|
163
204
|
Wh: Final = (_si.W * h).define('Wh', name='watthour')
|
|
164
205
|
"""watthour, energy"""
|
|
165
|
-
mWh, kWh, MWh, GWh, TWh = _prefixed_units('Wh', '
|
|
206
|
+
uWh, mWh, kWh, MWh, GWh, TWh, PWh = _prefixed_units('Wh', 'umkMGTP')
|
|
166
207
|
"""watthour, energy"""
|
|
167
208
|
eV: Final = (_SI.e * _si.V).define('eV', name='electronvolt')
|
|
168
209
|
"""electron volt, energy"""
|
|
169
|
-
meV, keV, MeV, GeV, TeV, PeV = _prefixed_units('eV', '
|
|
210
|
+
ueV, meV, keV, MeV, GeV, TeV, PeV, EeV = _prefixed_units('eV', 'umkMGTPE')
|
|
170
211
|
"""electron volt, energy"""
|
|
171
|
-
cal: Final = (
|
|
212
|
+
cal: Final = (4184 * mJ).define('cal', name='calorie')
|
|
172
213
|
"""calorie, energy"""
|
|
173
214
|
kcal: Final = Unit('kcal')
|
|
174
215
|
"""kilocalorie, energy"""
|
|
175
|
-
|
|
216
|
+
VA: Final = (_si.V * _si.A).define('VA', name='volt-ampere')
|
|
217
|
+
"""volt-ampere, apparent power"""
|
|
218
|
+
kVA, MVA = _prefixed_units('VA', 'kM')
|
|
219
|
+
"""volt-ampere, apparent power"""
|
|
220
|
+
var: Final = (_si.V * _si.A).define('var', name='volt-ampere reactive', symbol_aliases='VAr')
|
|
221
|
+
"""volt-ampere reactive, reactive power"""
|
|
222
|
+
kvar, Mvar = _prefixed_units('var', 'kM')
|
|
176
223
|
"""volt-ampere reactive, reactive power"""
|
|
177
|
-
|
|
178
|
-
|
|
224
|
+
# electromagnetic related
|
|
225
|
+
Ah: Final = (_si.A * h).define('Ah', name='ampere-hour')
|
|
226
|
+
"""ampere-hour, electric charge"""
|
|
227
|
+
mAh, kAh, MAh = _prefixed_units('Ah', 'mkM')
|
|
228
|
+
"""ampere-hour, electric charge"""
|
|
229
|
+
mho: Final = _si.S.define('mho', name='mho', symbol_aliases='℧')
|
|
230
|
+
"""mho, electrical conductance"""
|
|
231
|
+
# others
|
|
232
|
+
nt: Final = (_si.cd / _si.m**2).define('nt', name='nit')
|
|
233
|
+
"""nit, luminance"""
|
|
@@ -285,10 +285,11 @@ class Unit:
|
|
|
285
285
|
All new texts, including prefixed forms, must be unambiguous and must
|
|
286
286
|
not already resolve to registered units or implicit unit products.
|
|
287
287
|
Complex names work standalone or in ``[name]`` references in expressions.
|
|
288
|
-
``prefixable`` accepts True for all spellings, False for none,
|
|
289
|
-
tuple of exact symbols, names, or aliases
|
|
288
|
+
``prefixable`` accepts True for all spellings, False for none, a single
|
|
289
|
+
spelling string, or a tuple of exact symbols, names, or aliases, such as
|
|
290
290
|
``('arcsec', 'arcsecond')``. Only listed spellings accept prefixes;
|
|
291
|
-
|
|
291
|
+
``'arcsec'`` is equivalent to ``('arcsec',)``. An empty tuple disables
|
|
292
|
+
all prefixes. Unknown or empty spellings are errors.
|
|
292
293
|
Invalid definitions raise TypeError, ValueError, or UnitRegistryError
|
|
293
294
|
before changing the registry.
|
|
294
295
|
"""
|
|
@@ -22,11 +22,11 @@ from .factor import ONE, TEN, Factor
|
|
|
22
22
|
|
|
23
23
|
__all__ = ['PrefixPolicy']
|
|
24
24
|
|
|
25
|
-
type PrefixPolicy = bool | tuple[str, ...]
|
|
25
|
+
type PrefixPolicy = bool | str | tuple[str, ...]
|
|
26
26
|
|
|
27
27
|
_UNIT_SYMBOL_PATTERN = r'[^\s\d' + re.escape('*^()/+-[]' + SUPERSCRIPTS) + ']+'
|
|
28
28
|
_UNIT_SYMBOL_RE = re.compile(_UNIT_SYMBOL_PATTERN)
|
|
29
|
-
_UNIT_NAME_RE = re.compile(_UNIT_SYMBOL_PATTERN
|
|
29
|
+
_UNIT_NAME_RE = re.compile(f'{_UNIT_SYMBOL_PATTERN}(?:[ -]{_UNIT_SYMBOL_PATTERN})*')
|
|
30
30
|
_PREFIXED_NAME_EXCEPTIONS = {('M', 'ohm'): 'megohm', ('k', 'ohm'): 'kilohm'}
|
|
31
31
|
|
|
32
32
|
|
|
@@ -113,7 +113,7 @@ class UnitDefinition:
|
|
|
113
113
|
self.factor = factor
|
|
114
114
|
self.names = (name, *name_aliases)
|
|
115
115
|
self.dimension = dimension
|
|
116
|
-
self.prefixable = prefixable
|
|
116
|
+
self.prefixable = (prefixable,) if isinstance(prefixable, str) else prefixable
|
|
117
117
|
self.symbols: tuple[str, ...] = ()
|
|
118
118
|
|
|
119
119
|
def allows_prefix(self, spelling: str) -> bool:
|
|
@@ -229,7 +229,7 @@ def _preferred_unit_spelling(
|
|
|
229
229
|
def _register_unit(symbol: str, definition: UnitDefinition, /, *symbol_aliases: str) -> None:
|
|
230
230
|
"""Validate every binding and generated spelling before updating indexes."""
|
|
231
231
|
if not isinstance(definition.prefixable, (bool, tuple)):
|
|
232
|
-
raise TypeError('Prefixable must be bool or tuple of spellings.')
|
|
232
|
+
raise TypeError('Prefixable must be bool, str or tuple of spellings.')
|
|
233
233
|
for kind, texts in (
|
|
234
234
|
('symbol', (symbol,)),
|
|
235
235
|
('name', (definition.name,)),
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Public SI namespaces, conversions, and constant values."""
|
|
2
2
|
|
|
3
|
+
import copy
|
|
3
4
|
import os
|
|
5
|
+
import pickle
|
|
4
6
|
import subprocess
|
|
5
7
|
import sys
|
|
8
|
+
import warnings
|
|
6
9
|
from decimal import Decimal, localcontext
|
|
7
10
|
from fractions import Fraction
|
|
8
11
|
from pathlib import Path
|
|
@@ -79,6 +82,18 @@ def test_si_coherent_units(attribute: str, expression: str) -> None:
|
|
|
79
82
|
('Wh', 'J', 3600),
|
|
80
83
|
('eV', 'J', Decimal('1.602176634e-19')),
|
|
81
84
|
('cal', 'J', Decimal('4.184')),
|
|
85
|
+
('percent', '', Fraction(1, 100)),
|
|
86
|
+
('permille', '', Fraction(1, 1000)),
|
|
87
|
+
('permyriad', '', Fraction(1, 10000)),
|
|
88
|
+
('rpm', 'Hz', Fraction(1, 60)),
|
|
89
|
+
('c', 'm/s', 299792458),
|
|
90
|
+
('gf', 'N', Decimal('0.00980665')),
|
|
91
|
+
('kgf', 'N', Decimal('9.80665')),
|
|
92
|
+
('Ah', 'C', 3600),
|
|
93
|
+
('VA', 'W', 1),
|
|
94
|
+
('mho', 'S', 1),
|
|
95
|
+
('nt', 'cd/m2', 1),
|
|
96
|
+
('mTorr', 'Pa', Fraction(4053, 30400)),
|
|
82
97
|
],
|
|
83
98
|
)
|
|
84
99
|
def test_si_unit_conversions(attribute: str, target: str, value: int | Fraction | Decimal) -> None:
|
|
@@ -151,6 +166,20 @@ def test_si_hbar_value() -> None:
|
|
|
151
166
|
assert float(SI.hbar.value) == pytest.approx(1.0545718176461565e-34, rel=1e-15, abs=0)
|
|
152
167
|
|
|
153
168
|
|
|
169
|
+
@pytest.mark.parametrize(
|
|
170
|
+
('attribute', 'target', 'value'),
|
|
171
|
+
[
|
|
172
|
+
('kappa', '1/N', 2.076647442844972e-43),
|
|
173
|
+
('b', 'm*K', 2.8977719551851727e-3),
|
|
174
|
+
('b_', 'Hz/K', 5.878925757646825e10),
|
|
175
|
+
],
|
|
176
|
+
)
|
|
177
|
+
def test_si_derived_constants(attribute: str, target: str, value: float) -> None:
|
|
178
|
+
constant = getattr(SI, attribute)
|
|
179
|
+
assert constant.unit.dimension == Unit(target).dimension
|
|
180
|
+
assert float(constant.value_in(target)) == pytest.approx(value, rel=1e-14, abs=0)
|
|
181
|
+
|
|
182
|
+
|
|
154
183
|
@pytest.mark.parametrize(
|
|
155
184
|
('attribute', 'target'),
|
|
156
185
|
[('Phi0', si.Wb), ('G0', si.S), ('RK', si.ohm), ('a0', si.m), ('lambdaC', si.m), ('Eh', si.J)],
|
|
@@ -247,7 +276,8 @@ def test_si_mercury_prefixes() -> None:
|
|
|
247
276
|
assert Unit('kt') == si.knot
|
|
248
277
|
for unit in (si.mHg, si.mmHg, si.Torr):
|
|
249
278
|
assert Unit(unit.expression()) == unit
|
|
250
|
-
|
|
279
|
+
assert Quantity(1, 'kTorr').to(si.Torr).value == 1000
|
|
280
|
+
for text in ('mmmHg', 'kmmHg', 'millimillimetre of mercury'):
|
|
251
281
|
with pytest.raises(UnitSymbolError):
|
|
252
282
|
Unit(text)
|
|
253
283
|
|
|
@@ -257,6 +287,37 @@ def test_si_light_year() -> None:
|
|
|
257
287
|
assert 1 * si.eV == SI.e * si.V
|
|
258
288
|
|
|
259
289
|
|
|
290
|
+
def test_si_dalton_aliases() -> None:
|
|
291
|
+
assert si.u == si.Da == Unit('unified atomic mass unit')
|
|
292
|
+
assert hash(si.u) == hash(si.Da)
|
|
293
|
+
assert {si.Da: 'found'}[si.u] == 'found'
|
|
294
|
+
assert Quantity(1, si.kDa).to(si.Da).value == 1000
|
|
295
|
+
for text in ('mu', 'kilodalton', 'kilounified atomic mass unit', 'ton'):
|
|
296
|
+
with pytest.raises(UnitSymbolError):
|
|
297
|
+
Unit(text)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_si_ascii_micro() -> None:
|
|
301
|
+
with warnings.catch_warnings(record=True) as recorded:
|
|
302
|
+
warnings.simplefilter('always')
|
|
303
|
+
assert Unit('um') == si.um
|
|
304
|
+
assert Unit('us') == si.us
|
|
305
|
+
assert Unit('u m') == si.u * si.m
|
|
306
|
+
assert Unit('u m') != Unit('um')
|
|
307
|
+
assert not recorded
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@pytest.mark.parametrize('attribute', ['mas', 'uas', 'u', 'Da', 'mTorr', 'c', 'gf', 'VA', 'Ah'])
|
|
311
|
+
def test_si_unit_roundtrip(attribute: str) -> None:
|
|
312
|
+
unit = getattr(si, attribute)
|
|
313
|
+
assert Unit(unit.expression()) == unit
|
|
314
|
+
assert eval(repr(unit), {'Unit': Unit}) == unit
|
|
315
|
+
assert copy.copy(unit) == copy.deepcopy(unit) == unit
|
|
316
|
+
restored = pickle.loads(pickle.dumps(unit))
|
|
317
|
+
assert restored == unit
|
|
318
|
+
assert hash(restored) == hash(unit)
|
|
319
|
+
|
|
320
|
+
|
|
260
321
|
@pytest.mark.parametrize('name', ['volt-ampere reactive', 'kilovolt-ampere reactive'])
|
|
261
322
|
def test_si_reactive_power_name(name: str) -> None:
|
|
262
323
|
assert Unit('[' + name + '] / s') == Unit(name) / si.s
|
|
@@ -53,6 +53,9 @@ def test_single_unit_combinations() -> None:
|
|
|
53
53
|
('M', 'Ω'): 'megohm',
|
|
54
54
|
('k', 'Ω'): 'kilohm',
|
|
55
55
|
}.get((prefix, symbol), prefix_definition.name + unit_definition.name)
|
|
56
|
+
if prefix and not unit_definition.allows_prefix(unit_definition.name):
|
|
57
|
+
# Da permits prefixed symbols but no prefixed names.
|
|
58
|
+
expected_name = unit.symbol
|
|
56
59
|
assert unit.name == expected_name
|
|
57
60
|
assert SingleUnit(unit.name) == unit
|
|
58
61
|
assert unit.dimension == unit_definition.dimension
|
|
@@ -96,7 +99,6 @@ def test_single_unit_name_alias(prefix: str) -> None:
|
|
|
96
99
|
('KK', 'kK', 'kilokelvin', Dimension(Theta=1), 1000),
|
|
97
100
|
('Kmol', 'kmol', 'kilomole', Dimension(N=1), 1000),
|
|
98
101
|
('Kcd', 'kcd', 'kilocandela', Dimension(J=1), 1000),
|
|
99
|
-
('um', 'µm', 'micrometre', Dimension(L=1), Fraction(1, 10**6)),
|
|
100
102
|
],
|
|
101
103
|
)
|
|
102
104
|
def test_single_unit_symbol_warning(
|
|
@@ -120,7 +122,9 @@ def test_single_unit_symbol_warning(
|
|
|
120
122
|
assert type(unit.factor.value) is type(factor)
|
|
121
123
|
|
|
122
124
|
|
|
123
|
-
@pytest.mark.parametrize(
|
|
125
|
+
@pytest.mark.parametrize(
|
|
126
|
+
'text', ['', 'K', 'kelvin', 'kK', 'km', 'ms', 'Ms', 'um', 'us', 'µm', 'μm']
|
|
127
|
+
)
|
|
124
128
|
def test_single_unit_no_warning(text: str) -> None:
|
|
125
129
|
with warnings.catch_warnings(record=True) as recorded:
|
|
126
130
|
warnings.simplefilter('always')
|
|
@@ -116,9 +116,30 @@ def test_unit_define_duplicate_spellings() -> None:
|
|
|
116
116
|
Unit('millireview policy')
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
@pytest.mark.parametrize('
|
|
119
|
+
@pytest.mark.parametrize('source', [Unit('m'), PI * Unit('m'), Quantity(Fraction(3, 7), 'm')])
|
|
120
|
+
@pytest.mark.parametrize('spelling', ['rvselect', 'rvselected', 'review selected', 'review alias'])
|
|
121
|
+
def test_unit_define_string_policy(source, spelling: str) -> None:
|
|
122
|
+
unit = source.define(
|
|
123
|
+
'rvselect',
|
|
124
|
+
'review selected',
|
|
125
|
+
'review alias',
|
|
126
|
+
symbol_aliases='rvselected',
|
|
127
|
+
prefixable=spelling,
|
|
128
|
+
)
|
|
129
|
+
for text in ('rvselect', 'rvselected', 'review selected', 'review alias'):
|
|
130
|
+
assert Unit(text) == unit
|
|
131
|
+
prefixed = ('kilo' if ' ' in text else 'k') + text
|
|
132
|
+
if text == spelling:
|
|
133
|
+
assert Unit(prefixed).factor == 1000 * unit.factor
|
|
134
|
+
assert Unit(Unit(prefixed).expression()) == Unit(prefixed)
|
|
135
|
+
else:
|
|
136
|
+
with pytest.raises(UnitSymbolError):
|
|
137
|
+
Unit(prefixed)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@pytest.mark.parametrize('policy', [None, [], ['rvpolicy'], {}, {'rvpolicy': True}, 1])
|
|
120
141
|
def test_unit_define_invalid_policy(policy, unit_registry) -> None:
|
|
121
|
-
with pytest.raises(TypeError, match='Prefixable must be bool or tuple of spellings'):
|
|
142
|
+
with pytest.raises(TypeError, match='Prefixable must be bool, str or tuple of spellings'):
|
|
122
143
|
Unit('m').define('rvpolicy', 'review policy', prefixable=policy)
|
|
123
144
|
assert (UNIT_DEFINITIONS, UNIT_NAME_TO_SYMBOL, UNIT_SYMBOL_ALIAS_TO_SYMBOL) == unit_registry
|
|
124
145
|
|
|
@@ -131,9 +152,12 @@ def test_unit_define_invalid_policy_type(text, unit_registry) -> None:
|
|
|
131
152
|
|
|
132
153
|
|
|
133
154
|
@pytest.mark.parametrize('text', ['unknown', '', 'mrvpolicy', 'm', 'µ'])
|
|
134
|
-
|
|
155
|
+
@pytest.mark.parametrize('as_tuple', [False, True])
|
|
156
|
+
def test_unit_define_invalid_policy_spelling(text: str, as_tuple: bool, unit_registry) -> None:
|
|
135
157
|
with pytest.raises(ValueError, match='is not a registered spelling'):
|
|
136
|
-
Unit('m').define(
|
|
158
|
+
Unit('m').define(
|
|
159
|
+
'rvpolicy', 'review policy', prefixable=('rvpolicy', text) if as_tuple else text
|
|
160
|
+
)
|
|
137
161
|
assert (UNIT_DEFINITIONS, UNIT_NAME_TO_SYMBOL, UNIT_SYMBOL_ALIAS_TO_SYMBOL) == unit_registry
|
|
138
162
|
|
|
139
163
|
|
|
@@ -124,6 +124,12 @@ def test_unit_numeric_scaling(unit: Unit, scaled: ScaledUnit) -> None:
|
|
|
124
124
|
def test_unit_define() -> None:
|
|
125
125
|
policy: PrefixPolicy = ('rvpolicy',)
|
|
126
126
|
assert_type(Unit('m').define('rvpolicy', 'review policy', prefixable=policy), Unit)
|
|
127
|
+
single_policy: PrefixPolicy = 'rvpolicy'
|
|
128
|
+
assert_type(Unit('m').define('rvpolicy', 'review policy', prefixable=single_policy), Unit)
|
|
129
|
+
assert_type((PI * Unit('m')).define('rvpolicy', 'review policy', prefixable='rvpolicy'), Unit)
|
|
130
|
+
assert_type(
|
|
131
|
+
Quantity(2, 'm').define('rvpolicy', 'review policy', prefixable='review policy'), Unit
|
|
132
|
+
)
|
|
127
133
|
assert_type(
|
|
128
134
|
Unit('m').define(
|
|
129
135
|
'rvbound',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from decimal import Decimal
|
|
4
4
|
from typing import assert_type
|
|
5
5
|
|
|
6
|
-
from unitx.quantity import Quantity
|
|
6
|
+
from unitx.quantity import Magnitude, Quantity
|
|
7
7
|
from unitx.si import SI, si
|
|
8
8
|
from unitx.unit import Unit
|
|
9
9
|
|
|
@@ -16,6 +16,10 @@ def test_si_unit_types() -> None:
|
|
|
16
16
|
assert_type(si.mHg, Unit)
|
|
17
17
|
assert_type(si.mmHg, Unit)
|
|
18
18
|
assert_type(si.cd, Unit)
|
|
19
|
+
assert_type(si.mas, Unit)
|
|
20
|
+
assert_type(si.kDa, Unit)
|
|
21
|
+
assert_type(si.c, Unit)
|
|
22
|
+
assert_type(si.kgf, Unit)
|
|
19
23
|
assert_type(2 * si.m, Quantity[int])
|
|
20
24
|
assert_type(Decimal('0.5') * si.s, Quantity[Decimal])
|
|
21
25
|
|
|
@@ -24,5 +28,9 @@ def test_si_constant_types() -> None:
|
|
|
24
28
|
assert_type(SI.c, Quantity[int])
|
|
25
29
|
assert_type(SI.h, Quantity[Decimal])
|
|
26
30
|
assert_type(SI.hbar, Quantity[Decimal])
|
|
31
|
+
# Integer powers currently have a conservative numeric return type.
|
|
32
|
+
assert_type(SI.kappa, Quantity[Magnitude])
|
|
33
|
+
assert_type(SI.b, Quantity[Decimal])
|
|
34
|
+
assert_type(SI.b_, Quantity[Decimal])
|
|
27
35
|
assert_type(SI.e, Quantity[Decimal])
|
|
28
36
|
assert_type(SI.h.to(si.J * si.s), Quantity[Decimal])
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
ConstClass is a class storing static constants.
|
|
3
|
-
It's similar to Enum, but members of Enum need an extra value attribute `value`
|
|
4
|
-
to access its value, and I think it's unnecessary, so I use ConstClass.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from typing import Any, NoReturn
|
|
8
|
-
|
|
9
|
-
__all__ = ['ConstMeta', 'ConstClass']
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ConstMeta(type):
|
|
13
|
-
def __call__(metacls, *args: Any, **kwds: Any) -> NoReturn:
|
|
14
|
-
"""
|
|
15
|
-
No, this class restores constants, you can NOT instantiate objects.
|
|
16
|
-
please access constants via class attributes.
|
|
17
|
-
"""
|
|
18
|
-
raise TypeError(metacls.__call__.__doc__)
|
|
19
|
-
|
|
20
|
-
def __setattr__(cls, name, value) -> NoReturn:
|
|
21
|
-
"""
|
|
22
|
-
No, this class restores constants, you can NOT modify them.
|
|
23
|
-
"""
|
|
24
|
-
raise AttributeError(cls.__class__.__setattr__.__doc__)
|
|
25
|
-
|
|
26
|
-
def __delattr__(cls, name) -> NoReturn:
|
|
27
|
-
"""
|
|
28
|
-
No, this class restores constants, you can NOT delete them.
|
|
29
|
-
"""
|
|
30
|
-
raise AttributeError(cls.__class__.__delattr__.__doc__)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class ConstClass(metaclass=ConstMeta):
|
|
34
|
-
pass
|
|
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
|
|
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
|