unitx 0.14.2__tar.gz → 0.14.4__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.14.2 → unitx-0.14.4}/.github/consumers/arrays.py +5 -0
- {unitx-0.14.2 → unitx-0.14.4}/.github/consumers/scalars.py +17 -1
- {unitx-0.14.2 → unitx-0.14.4}/.github/workflows/ci.yml +1 -0
- unitx-0.14.4/.github/workflows/pages.yml +52 -0
- {unitx-0.14.2 → unitx-0.14.4}/.github/workflows/release.yml +1 -0
- {unitx-0.14.2 → unitx-0.14.4}/CHANGELOG.md +33 -0
- {unitx-0.14.2 → unitx-0.14.4}/CONTRIBUTING.md +11 -3
- {unitx-0.14.2 → unitx-0.14.4}/PKG-INFO +3 -3
- {unitx-0.14.2 → unitx-0.14.4}/README.md +1 -1
- {unitx-0.14.2 → unitx-0.14.4}/conftest.py +1 -1
- {unitx-0.14.2 → unitx-0.14.4}/docs/architecture.md +6 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/logarithmic.md +37 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/typing.md +20 -1
- {unitx-0.14.2 → unitx-0.14.4}/examples/README.md +9 -0
- unitx-0.14.4/examples/impedance.md +66 -0
- unitx-0.14.4/examples/impedance.py +121 -0
- unitx-0.14.4/examples/smith_chart.ipynb +352 -0
- {unitx-0.14.2 → unitx-0.14.4}/mkdocs.yml +1 -0
- {unitx-0.14.2 → unitx-0.14.4}/pyproject.toml +2 -2
- {unitx-0.14.2 → unitx-0.14.4}/pyrightconfig.json +4 -3
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/logunit.py +38 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/unit.py +19 -0
- unitx-0.14.4/tests/numpy_typing/check_asarray.py +13 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/numpy_typing/check_numpy.py +18 -27
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_affine.py +48 -35
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_affinequantity.py +25 -19
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_catalog_collection.py +13 -10
- unitx-0.14.4/tests/test_decibel_call.py +158 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_definition_scope.py +29 -26
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_display.py +36 -28
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_examples.py +7 -3
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_factor.py +6 -1
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_immutable.py +4 -1
- unitx-0.14.4/tests/test_impedance_example.py +118 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_imperial.py +12 -10
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_linear_boundaries.py +10 -7
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_logarithmic_model.py +14 -11
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_math.py +11 -9
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_math_numpy.py +14 -7
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_monomial.py +5 -1
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy.py +65 -50
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_affine.py +23 -14
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_differentiation.py +43 -24
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_integration.py +29 -11
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_multi_outputs.py +30 -19
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_mutation.py +52 -27
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_outputs.py +36 -18
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_products.py +67 -28
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numpy_statistics.py +47 -18
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_quantity.py +94 -57
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_registry.py +32 -29
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_registry_scope.py +19 -17
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_serialization.py +60 -43
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_si.py +7 -2
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_siunitx_display.py +13 -8
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_time.py +25 -23
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_typing.py +25 -16
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unicode_units.py +3 -1
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unit.py +15 -8
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unit_definition.py +56 -18
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unitregistry.py +35 -13
- unitx-0.14.4/tests/typing/check_impedance_example.py +31 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_logarithmic.py +5 -0
- unitx-0.14.2/tests/example_typing/check_blackbody.py +0 -27
- {unitx-0.14.2 → unitx-0.14.4}/.github/consumers/pyrightconfig.json +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/.gitignore +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/.readthedocs.yaml +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/LICENSE +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/benchmarks/README.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/benchmarks/numpy_writes.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/benchmarks/scalars.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/README.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/affine.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/catalogues/README.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/catalogues/imperial.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/catalogues/si.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/custom-units.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/display.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/images/blackbody.svg +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/javascripts/mermaid.mjs +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/migration.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/numpy.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/requirements.txt +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/scalars.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/serialization.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/stdlib.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/docs/units.md +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/arrhenius_fit.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/blackbody.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/damped_oscillator.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/gaussian_wavepacket.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/optical_attenuation.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/examples/pendulum_uncertainty.ipynb +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_constnamespace.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_conversion.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/affine.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/definition.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/logarithmic.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/logarithms.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/points.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_coordinates/resolve.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_display.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_immutable.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_numerics.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_numpy.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_numpy_typing.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_operands.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_registry/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_registry/builtin_units.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_registry/prefixes.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_registry/reconstruction.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_scaling.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/_unicode.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/affinequantity.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/affineunit.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/dimension.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/exceptions.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/factor.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/imperial/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/imperial/units.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/logquantity.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/math.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/monomial.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/py.typed +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/quantity.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/serialization.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/si/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/si/bases.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/si/constants.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/si/units.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/singleunit.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/time.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/unitcatalog.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/unitdefinition.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/unitparser.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/src/unitx/unitregistry.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/__init__.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/_notebooks.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/numpy_typing/check_logarithmic.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/numpy_typing/check_math.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_builtin_definitions.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_dimension.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_import.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_numeric.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_singleunit.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unicode.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_unitparser.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/test_warnings.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_affine.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_imperial.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_math_time.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_quantity.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_registry.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_serialization.py +0 -0
- {unitx-0.14.2 → unitx-0.14.4}/tests/typing/check_si.py +0 -0
|
@@ -17,6 +17,11 @@ def check_arrays(values: NDArray[np.float64]) -> None:
|
|
|
17
17
|
assert level.value is values
|
|
18
18
|
np.testing.assert_allclose(level.to_quantity(si.W).value, [0.001, 0.1, 1.0])
|
|
19
19
|
|
|
20
|
+
voltage_level = LogQuantity(values, si.dB(si.mV))
|
|
21
|
+
assert_type(voltage_level, LogQuantity[NDArray[np.float64]])
|
|
22
|
+
assert voltage_level.value is values
|
|
23
|
+
np.testing.assert_allclose(voltage_level.to_quantity(si.mV).value, 10 ** (values / 20))
|
|
24
|
+
|
|
20
25
|
distance = Quantity(values.copy(), si.m)
|
|
21
26
|
assert_type(distance, Quantity[NDArray[np.float64]])
|
|
22
27
|
assert_type(distance.unit, Unit)
|
|
@@ -12,7 +12,17 @@ from pathlib import Path
|
|
|
12
12
|
from typing import Final, assert_type
|
|
13
13
|
|
|
14
14
|
import unitx
|
|
15
|
-
from unitx import
|
|
15
|
+
from unitx import (
|
|
16
|
+
AffineQuantity,
|
|
17
|
+
LogQuantity,
|
|
18
|
+
LogUnit,
|
|
19
|
+
Quantity,
|
|
20
|
+
Registry,
|
|
21
|
+
Unit,
|
|
22
|
+
UnitCatalog,
|
|
23
|
+
math,
|
|
24
|
+
time,
|
|
25
|
+
)
|
|
16
26
|
from unitx.factor import Factor
|
|
17
27
|
from unitx.imperial import imperial
|
|
18
28
|
from unitx.serialization import from_dict, to_dict
|
|
@@ -70,6 +80,12 @@ assert celsius.to_quantity(si.K) == Fraction(5863, 20) * si.K
|
|
|
70
80
|
assert (32 @ imperial.degF).to(si.degC) == 0 @ si.degC
|
|
71
81
|
assert level.to_quantity(si.W) == 1 * si.W
|
|
72
82
|
assert LogQuantity.from_quantity(1 * si.W, si.dBm) == level
|
|
83
|
+
assert_type(si.dB(si.V), LogUnit)
|
|
84
|
+
assert si.dB(si.mW) is si.dBm
|
|
85
|
+
assert Unit('decibel')(si.V) is si.dBV
|
|
86
|
+
millivolt_level = Decimal(20) @ si.dB(si.mV)
|
|
87
|
+
assert_type(millivolt_level, LogQuantity[Decimal])
|
|
88
|
+
assert millivolt_level.to_quantity(si.mV).value == Decimal(10)
|
|
73
89
|
|
|
74
90
|
registry = Registry([si.m])
|
|
75
91
|
with registry.defining():
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Documentation site
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- 'docs/**'
|
|
8
|
+
- 'mkdocs.yml'
|
|
9
|
+
- '.github/workflows/pages.yml'
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: github-pages
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
- uses: actions/setup-python@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.12'
|
|
28
|
+
- name: Configure GitHub Pages
|
|
29
|
+
uses: actions/configure-pages@v5
|
|
30
|
+
- name: Install documentation dependencies
|
|
31
|
+
run: python -m pip install -r docs/requirements.txt
|
|
32
|
+
- name: Build documentation
|
|
33
|
+
run: python -m mkdocs build --strict
|
|
34
|
+
- name: Upload site
|
|
35
|
+
uses: actions/upload-pages-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
path: site
|
|
38
|
+
|
|
39
|
+
deploy:
|
|
40
|
+
needs: build
|
|
41
|
+
if: github.ref == 'refs/heads/main'
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
permissions:
|
|
44
|
+
pages: write
|
|
45
|
+
id-token: write
|
|
46
|
+
environment:
|
|
47
|
+
name: github-pages
|
|
48
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
49
|
+
steps:
|
|
50
|
+
- name: Deploy GitHub Pages
|
|
51
|
+
id: deployment
|
|
52
|
+
uses: actions/deploy-pages@v4
|
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.14.4] - 2026-10-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Correct the Impedance example's mixed-addition result annotations and retain
|
|
10
|
+
its specialized scalar `+=` result types. Document the subclass boundary and
|
|
11
|
+
check it with runtime and static consumers.
|
|
12
|
+
|
|
13
|
+
### Documentation
|
|
14
|
+
|
|
15
|
+
- Host the MkDocs guides on GitHub Pages with automatic deployment from main.
|
|
16
|
+
Keep Read the Docs deployment pending transfer of the project name.
|
|
17
|
+
|
|
18
|
+
## [0.14.3] - 2026-10-24
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Allow `si.dB(reference, kind=...)` to construct a logarithmic coordinate.
|
|
23
|
+
Named watts and volts, including SI prefixes, supply conventional defaults;
|
|
24
|
+
common references reuse dBm, dBW and dBV. Other units require an explicit kind.
|
|
25
|
+
- Add a standalone Impedance subclass example with series addition, parallel
|
|
26
|
+
`//` composition, unit conversion and a complex RLC circuit calculation,
|
|
27
|
+
plus a notebook that traces its frequency response on a Smith chart.
|
|
28
|
+
|
|
29
|
+
### Internal
|
|
30
|
+
|
|
31
|
+
- Consolidate static consumers under scalar and NumPy typing fixtures, checking
|
|
32
|
+
actual notebook source instead of duplicate blackbody examples. Keep the
|
|
33
|
+
NumPy 2.5 asarray regression separately gated and exclude static fixtures from
|
|
34
|
+
pytest collection.
|
|
35
|
+
- Complete runtime-test signature annotations and check them in the existing CI
|
|
36
|
+
and release lint steps.
|
|
37
|
+
|
|
5
38
|
## [0.14.2] - 2026-10-24
|
|
6
39
|
|
|
7
40
|
### Documentation
|
|
@@ -15,6 +15,7 @@ without NumPy and the built-in factorization path without SymPy.
|
|
|
15
15
|
```sh
|
|
16
16
|
python -m pytest -q
|
|
17
17
|
python -m ruff check .
|
|
18
|
+
python -m ruff check tests conftest.py --select ANN --ignore ANN401
|
|
18
19
|
python -m ruff format --check .
|
|
19
20
|
python -m pyright --pythonpath /path/to/your/python
|
|
20
21
|
```
|
|
@@ -28,6 +29,9 @@ consumers in `.github/consumers/`. It copies those scripts and their independent
|
|
|
28
29
|
Pyright configuration outside the checkout, first checking scalar runtime and
|
|
29
30
|
types without NumPy, then installing NumPy and checking array runtime and types.
|
|
30
31
|
The release workflow runs the same consumers before uploading distributions.
|
|
32
|
+
Test functions and helpers have annotated parameters and return values. The
|
|
33
|
+
dedicated Ruff command checks those signatures; static inference regressions
|
|
34
|
+
use the separate Pyright consumers rather than annotating expected results.
|
|
31
35
|
|
|
32
36
|
Run the executable Markdown examples as well:
|
|
33
37
|
|
|
@@ -54,9 +58,13 @@ python -m mkdocs serve
|
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
The build writes to the ignored `site/` directory. CI checks the strict build,
|
|
57
|
-
including internal links and anchors.
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
including internal links and anchors. The [documentation site](https://daitthu.github.io/unitx/)
|
|
62
|
+
is hosted on GitHub Pages. `.github/workflows/pages.yml` builds and deploys
|
|
63
|
+
changes to `docs/`, `mkdocs.yml` or the workflow on main; it also supports
|
|
64
|
+
manual runs. Repository Settings → Pages must use GitHub Actions as its source.
|
|
65
|
+
`.readthedocs.yaml` retains the same pinned MkDocs requirement for a future
|
|
66
|
+
Read the Docs deployment. Do not connect or publish that project until
|
|
67
|
+
ownership of `unitx.readthedocs.io` has transferred to the project owner.
|
|
60
68
|
Mermaid diagrams load a pinned JavaScript module from jsDelivr in the browser;
|
|
61
69
|
building the HTML does not require that CDN. Repository files outside `docs/`
|
|
62
70
|
use GitHub links so they also work on a hosted documentation site.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: unitx
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.4
|
|
4
4
|
Summary: Physical units, quantities, and unit conversion with exact unit algebra, type hints, and NumPy integration.
|
|
5
5
|
Project-URL: Homepage, https://github.com/DaitTHU/unitx
|
|
6
|
-
Project-URL: Documentation, https://github.
|
|
6
|
+
Project-URL: Documentation, https://daitthu.github.io/unitx/
|
|
7
7
|
Project-URL: Source, https://github.com/DaitTHU/unitx
|
|
8
8
|
Project-URL: Changelog, https://github.com/DaitTHU/unitx/blob/main/CHANGELOG.md
|
|
9
9
|
Project-URL: Issues, https://github.com/DaitTHU/unitx/issues
|
|
@@ -237,7 +237,7 @@ plt.show()
|
|
|
237
237
|
|
|
238
238
|
## Read more
|
|
239
239
|
|
|
240
|
-
Start at the [documentation
|
|
240
|
+
Start at the [documentation site](https://daitthu.github.io/unitx/),
|
|
241
241
|
or go directly to a topic:
|
|
242
242
|
|
|
243
243
|
| Topic | Guide |
|
|
@@ -209,7 +209,7 @@ plt.show()
|
|
|
209
209
|
|
|
210
210
|
## Read more
|
|
211
211
|
|
|
212
|
-
Start at the [documentation
|
|
212
|
+
Start at the [documentation site](https://daitthu.github.io/unitx/),
|
|
213
213
|
or go directly to a topic:
|
|
214
214
|
|
|
215
215
|
| Topic | Guide |
|
|
@@ -7,7 +7,7 @@ from unitx.exceptions import InexactFloatWarning
|
|
|
7
7
|
from unitx.factor import Factor
|
|
8
8
|
|
|
9
9
|
# Static-only fixtures run through Pyright in tests/test_typing.py.
|
|
10
|
-
collect_ignore = ['tests/
|
|
10
|
+
collect_ignore = ['tests/typing', 'tests/numpy_typing']
|
|
11
11
|
if find_spec('numpy') is None:
|
|
12
12
|
collect_ignore.append('src/unitx/_numpy.py')
|
|
13
13
|
|
|
@@ -75,6 +75,12 @@ a physical Quantity; `LogQuantity.from_quantity(quantity, coordinate)` enters a
|
|
|
75
75
|
logarithmic coordinate. These boundaries prevent an implicit exponential or
|
|
76
76
|
logarithm from entering ordinary Quantity conversions.
|
|
77
77
|
|
|
78
|
+
`Unit.__call__` is a narrow convenience boundary for the formal dB unit. It
|
|
79
|
+
delegates lazily to the logarithmic module, which constructs an ordinary
|
|
80
|
+
LogUnit or reuses a named descriptor. Default conventions recognize watt and
|
|
81
|
+
volt definition identities with their prefixes; they are not inferred from
|
|
82
|
+
dimensions or symbol strings. Calling another Unit raises TypeError.
|
|
83
|
+
|
|
78
84
|
The difference-unit factor uses the neper scale. The mapping coefficient is
|
|
79
85
|
`ln(10)/2` for power and `ln(10)` for amplitude, so a reading displacement `x - at`
|
|
80
86
|
represents the ratio `10**((x - at) * delta.factor / coefficient)` relative to
|
|
@@ -54,6 +54,43 @@ True
|
|
|
54
54
|
|
|
55
55
|
## Defining logarithmic coordinates
|
|
56
56
|
|
|
57
|
+
For a zero-reading reference, `si.dB(reference, kind=...)` is a shorthand for
|
|
58
|
+
`LogUnit(reference, delta_unit=si.dB, kind=...)`. A Unit argument means one of
|
|
59
|
+
that unit; a Quantity argument supplies the full reference value. Watts and
|
|
60
|
+
volts, including their SI prefixes and extra exact scales, default to
|
|
61
|
+
`kind='power'` and `kind='amplitude'`, respectively. This convention follows
|
|
62
|
+
the named unit definition, not physical dimension: other units and composite
|
|
63
|
+
expressions require an explicit `kind`. An explicit `kind` always takes precedence.
|
|
64
|
+
|
|
65
|
+
```pycon
|
|
66
|
+
>>> si.dB(si.mW) is si.dBm
|
|
67
|
+
True
|
|
68
|
+
>>> si.dB(si.W) is si.dBW
|
|
69
|
+
True
|
|
70
|
+
>>> si.dB(si.V) is si.dBV
|
|
71
|
+
True
|
|
72
|
+
>>> millivolt_level = si.dB(si.mV)
|
|
73
|
+
>>> (20 @ millivolt_level).to_quantity(si.mV).value
|
|
74
|
+
10
|
|
75
|
+
>>> (0 @ millivolt_level).to(si.dBV).value
|
|
76
|
+
-60
|
|
77
|
+
>>> pressure_level = si.dB(20 * si.uPa, kind='amplitude')
|
|
78
|
+
>>> (0 @ pressure_level).to_quantity(si.uPa).value
|
|
79
|
+
20
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
References with value one and unit mW, W or V, using their usual conventions,
|
|
84
|
+
return the existing named descriptors. Other references return
|
|
85
|
+
anonymous LogUnits without registering new names. The millivolt reference
|
|
86
|
+
corresponds to dBmV; dBm refers to a milliwatt. Voltage-to-power conversion
|
|
87
|
+
requires additional physical information such as impedance.
|
|
88
|
+
|
|
89
|
+
Only the formal dB unit supports this call. Reconstructed dB units behave the
|
|
90
|
+
same way; other Units raise TypeError when called. As a consequence of the
|
|
91
|
+
shared `Unit.__call__` method, `callable(unit)` is true for all Unit objects
|
|
92
|
+
and does not test whether this shorthand is supported.
|
|
93
|
+
|
|
57
94
|
`LogUnit(reference, /, *, delta_unit, kind, at=0)` means reading `at` represents
|
|
58
95
|
`reference`. A reference is a physical Quantity or LogQuantity. Both `delta_unit`
|
|
59
96
|
and `kind='power'|'amplitude'` are required. References must be positive finite
|
|
@@ -36,6 +36,20 @@ seconds. An `Annotated` label alone does not teach a type checker dimensional
|
|
|
36
36
|
algebra. unitx does not currently supply dimension-parameterized quantities
|
|
37
37
|
or a call-validation decorator.
|
|
38
38
|
|
|
39
|
+
## Quantity subclasses
|
|
40
|
+
|
|
41
|
+
Preserving the magnitude parameter T does not preserve a Quantity subclass.
|
|
42
|
+
Quantity's ordinary binary arithmetic, unary operators and `.to()` construct
|
|
43
|
+
ordinary Quantity objects. A subclass must explicitly implement the operations
|
|
44
|
+
that preserve its own invariants. Scalar augmented operators delegate to the
|
|
45
|
+
corresponding binary operation; their annotations must describe the subclass's
|
|
46
|
+
results too.
|
|
47
|
+
For example, the [Impedance extension](https://github.com/DaitTHU/unitx/blob/main/examples/impedance.md)
|
|
48
|
+
preserves its type for addition of two Impedances, parallel composition and
|
|
49
|
+
conversion. Adding an ordinary Quantity returns an ordinary Quantity in either
|
|
50
|
+
operand order. Its local overrides specialize the base class's wider input
|
|
51
|
+
domain; they are not a template for preserving every array or coordinate overload.
|
|
52
|
+
|
|
39
53
|
## NumPy magnitude types
|
|
40
54
|
|
|
41
55
|
`_numerics.py` defines `Scalar`, `Array`, and `Magnitude = Array | Scalar`
|
|
@@ -93,7 +107,12 @@ in static analysis. Result dtype and shape remain determined by NumPy.
|
|
|
93
107
|
|
|
94
108
|
The test suite runs strict scalar fixtures with and without NumPy and checks
|
|
95
109
|
array consumers separately when NumPy is installed. `tests/typing` and
|
|
96
|
-
`tests/numpy_typing` are static fixtures
|
|
110
|
+
`tests/numpy_typing` are static fixtures excluded from pytest collection.
|
|
111
|
+
The small `check_asarray.py` regression requires NumPy >= 2.5 stubs; other array
|
|
112
|
+
fixtures cover the full supported NumPy range. Notebook checks extract the
|
|
113
|
+
actual code and add selected type assertions, rather than maintaining copies
|
|
114
|
+
of scientific examples. Runtime test annotations describe fixtures and test
|
|
115
|
+
data; they do not replace static `assert_type()` checks of public inference.
|
|
97
116
|
|
|
98
117
|
```sh
|
|
99
118
|
python -m pyright --pythonpath /path/to/your/python
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Scientific examples
|
|
2
2
|
|
|
3
|
+
For a small extension without optional dependencies, see
|
|
4
|
+
[Extending Quantity: complex impedances](impedance.md). Its standalone Python
|
|
5
|
+
script uses a Quantity subclass for series `+` and parallel `//` composition
|
|
6
|
+
and calculates an RLC circuit with complex current and power.
|
|
7
|
+
|
|
3
8
|
These notebooks use unitx for dimensional calculations and NumPy for numerical
|
|
4
9
|
work. Each contains a model, assumptions, plots, and numerical checks. The
|
|
5
10
|
material parameters and measurement draws are illustrative, not experimental data.
|
|
@@ -12,6 +17,7 @@ material parameters and measurement draws are illustrative, not experimental dat
|
|
|
12
17
|
| [Optical attenuation](optical_attenuation.ipynb) | Transmission through absorbing layers | prod/multiply.reduce, input masks, Quantity initial, fixed-unit out |
|
|
13
18
|
| [Pendulum uncertainty](pendulum_uncertainty.ipynb) | Correlated input uncertainties and Monte Carlo propagation | Mean/std/var units, mixed measurement units, explicit quantiles |
|
|
14
19
|
| [Arrhenius fit](arrhenius_fit.ipynb) | Estimate activation energy from synthetic reaction rates | Absolute temperatures, regression boundary, parameter units and model-based standard errors |
|
|
20
|
+
| [Smith chart](smith_chart.ipynb) | Series/parallel RLC impedance and reflection trajectories | Quantity subclass, complex arithmetic, explicit normalization and plotting boundary |
|
|
15
21
|
|
|
16
22
|
## Run
|
|
17
23
|
|
|
@@ -25,11 +31,14 @@ jupyter lab examples/
|
|
|
25
31
|
|
|
26
32
|
Open a notebook and run all cells from the beginning. Use the current checkout
|
|
27
33
|
so that all demonstrated APIs are available.
|
|
34
|
+
Keep `smith_chart.ipynb` beside its companion `impedance.py` module.
|
|
28
35
|
The examples do not download datasets or write files. Keep physical quantities in calculations;
|
|
29
36
|
plot their `.value` and derive labels from their `.unit`. Convert only to choose
|
|
30
37
|
a useful display scale or to align units at a numerical boundary. Quantiles use
|
|
31
38
|
an explicit numerical boundary and reattach the input unit, without naming a
|
|
32
39
|
variable after its unit. Display equations use `$$...$$`.
|
|
40
|
+
Generate physical parameter labels from Quantity formatting or `to_latex()`;
|
|
41
|
+
do not repeat their numerical values and unit spellings in plot strings.
|
|
33
42
|
|
|
34
43
|
## Keep Git history small
|
|
35
44
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Extending Quantity: complex impedances
|
|
2
|
+
|
|
3
|
+
[impedance.py](impedance.py) is a standalone example, available from this checkout
|
|
4
|
+
rather than the installed unitx API. Run it without optional dependencies:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
python examples/impedance.py
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Open [the Smith-chart notebook](smith_chart.ipynb) alongside this module to sweep
|
|
11
|
+
an RLC network over frequency and plot its reflection coefficient. Keep both
|
|
12
|
+
files in the same directory; the notebook imports the class from `impedance.py`.
|
|
13
|
+
|
|
14
|
+
`Impedance` subclasses `Quantity` to represent the complex impedance of an ideal
|
|
15
|
+
two-terminal circuit at a given angular frequency. A real value describes an
|
|
16
|
+
ordinary resistor. The class adds `//` for parallel composition, while `+`
|
|
17
|
+
represents series composition. Both return another Impedance and keep the left
|
|
18
|
+
operand's unit. Conversion with `.to()` also returns an Impedance, allowing
|
|
19
|
+
`(z1 + z2).to(si.kohm) // z3` and `z1 // z2 // z3`.
|
|
20
|
+
|
|
21
|
+
For the convention $e^{i\omega t}$, ideal components have impedances
|
|
22
|
+
|
|
23
|
+
$$Z_R=R, \qquad Z_L=i\omega L, \qquad Z_C=\frac{1}{i\omega C}.$$
|
|
24
|
+
|
|
25
|
+
Series and parallel combinations follow
|
|
26
|
+
|
|
27
|
+
$$Z_{\mathrm{series}}=Z_1+Z_2, \qquad
|
|
28
|
+
Z_{\mathrm{parallel}}=\frac{Z_1Z_2}{Z_1+Z_2}.$$
|
|
29
|
+
|
|
30
|
+
All impedances being combined must correspond to the **same frequency**. This
|
|
31
|
+
class stores a value and unit, without recording or checking frequency. It does
|
|
32
|
+
not model a frequency-dependent component or preserve the circuit topology.
|
|
33
|
+
The script evaluates a 100 ohm, 10 mH, 1 microfarad series branch at 1 kHz,
|
|
34
|
+
puts a 300 ohm resistor in parallel and applies a 12 V RMS voltage phasor.
|
|
35
|
+
It calculates the current and complex power $S=VI^*$, retaining quantities
|
|
36
|
+
throughout the circuit calculation. $\operatorname{Re}S$ is active power and
|
|
37
|
+
$\operatorname{Im}S$ is reactive power; the latter retains its sign.
|
|
38
|
+
|
|
39
|
+
The example accepts finite scalar `int`, `float`, `Fraction` and `complex`
|
|
40
|
+
magnitudes, preserving their types at construction. Fraction inputs keep purely
|
|
41
|
+
rational calculations exact; float and complex calculations follow Python's
|
|
42
|
+
rounding rules. Decimal and arrays are outside this small model. Negative real
|
|
43
|
+
and imaginary parts are accepted; the class imposes no passivity assumption.
|
|
44
|
+
|
|
45
|
+
Parallel composition handles zero impedance explicitly, including two shorts.
|
|
46
|
+
It rearranges the formula to avoid multiplying large magnitudes. Nonzero
|
|
47
|
+
impedances with cancelling admittances, such as `100j` and `-100j` ohm at ideal
|
|
48
|
+
parallel resonance, produce an infinite equivalent impedance. Infinity is
|
|
49
|
+
outside this finite-value example, so that operation raises ZeroDivisionError.
|
|
50
|
+
Use parentheses when mixing `+` and `//`: Python gives `//` higher precedence.
|
|
51
|
+
|
|
52
|
+
Inherited multiplication and division return ordinary Quantity: voltage,
|
|
53
|
+
current, admittance and power have their usual units. Ordinary operations such
|
|
54
|
+
as subtraction also retain the base class's result behavior. Only addition of
|
|
55
|
+
two Impedances, parallel composition and conversion preserve the extension
|
|
56
|
+
type. Quantity's general subclass and serialization contracts stay unchanged.
|
|
57
|
+
Mixing an Impedance with an ordinary Quantity in addition returns an ordinary
|
|
58
|
+
Quantity in either operand order. For example, `z + 50 * si.ohm` loses the
|
|
59
|
+
extension's `//` operation; construct `Impedance(50)` to compose circuit elements.
|
|
60
|
+
The type annotations distinguish this mixed result from `z1 + z2`.
|
|
61
|
+
The scalar `+=` operation follows the same rule, returning a new result rather
|
|
62
|
+
than changing the original object. `//=` follows parallel composition.
|
|
63
|
+
The example deliberately narrows the inherited addition and conversion
|
|
64
|
+
signatures to its scalar model and str/Unit targets. Local Pyright annotations
|
|
65
|
+
mark that specialization; it is not a general template for preserving all of
|
|
66
|
+
Quantity's array and coordinate overloads.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""A scalar Quantity extension for series and parallel complex impedances.
|
|
2
|
+
|
|
3
|
+
Run `python examples/impedance.py` from the checkout. See `impedance.md` for the
|
|
4
|
+
fixed-frequency model. Fraction inputs retain exact rational arithmetic;
|
|
5
|
+
float and complex calculations follow ordinary Python numerical rules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import cmath
|
|
11
|
+
import math
|
|
12
|
+
from fractions import Fraction
|
|
13
|
+
from typing import cast, overload
|
|
14
|
+
|
|
15
|
+
from unitx import Quantity, Unit
|
|
16
|
+
from unitx._numerics import Scalar
|
|
17
|
+
from unitx.exceptions import DimensionError
|
|
18
|
+
from unitx.si import si
|
|
19
|
+
|
|
20
|
+
type Number = int | float | Fraction | complex
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Impedance(Quantity[Number]):
|
|
24
|
+
"""A finite scalar impedance with `+` series and `//` parallel composition.
|
|
25
|
+
|
|
26
|
+
Values being combined must describe the same angular frequency. This class
|
|
27
|
+
stores the impedance value and unit, without frequency or circuit topology.
|
|
28
|
+
Open circuits (infinity), Decimal and arrays are outside this small model.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
__slots__ = ()
|
|
32
|
+
|
|
33
|
+
def __init__(self, value: Number, unit: str | Unit = si.ohm) -> None:
|
|
34
|
+
if type(value) not in (int, float, Fraction, complex):
|
|
35
|
+
error_message = 'Impedance requires an int, float, Fraction or complex scalar.'
|
|
36
|
+
raise TypeError(error_message)
|
|
37
|
+
if isinstance(value, (float, complex)) and not cmath.isfinite(value):
|
|
38
|
+
error_message = 'Impedance must be finite.'
|
|
39
|
+
raise ValueError(error_message)
|
|
40
|
+
super().__init__(value, unit)
|
|
41
|
+
if self.dimension != si.ohm.dimension:
|
|
42
|
+
error_message = 'Impedance requires an impedance unit.'
|
|
43
|
+
raise DimensionError(error_message)
|
|
44
|
+
|
|
45
|
+
# This small extension intentionally specializes the broader Quantity
|
|
46
|
+
# signatures to scalar impedances and str/Unit conversion targets.
|
|
47
|
+
def value_in(self, unit: str | Unit, /) -> Number: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
48
|
+
# Scaling the explicitly supported numeric domain stays in that domain.
|
|
49
|
+
return cast(Number, super().value_in(unit))
|
|
50
|
+
|
|
51
|
+
@overload
|
|
52
|
+
def __add__(self, other: Impedance, /) -> Impedance: ...
|
|
53
|
+
|
|
54
|
+
@overload
|
|
55
|
+
def __add__(self, other: Quantity[Scalar] | Scalar, /) -> Quantity[Scalar]: ...
|
|
56
|
+
|
|
57
|
+
def __add__(self, other: object, /) -> Quantity[Scalar]: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
58
|
+
if not isinstance(other, Impedance):
|
|
59
|
+
return NotImplemented
|
|
60
|
+
return Impedance(self.value + other.value_in(self.unit), self.unit)
|
|
61
|
+
|
|
62
|
+
@overload
|
|
63
|
+
def __iadd__(self, other: Impedance, /) -> Impedance: ...
|
|
64
|
+
|
|
65
|
+
@overload
|
|
66
|
+
def __iadd__(self, other: Quantity[Scalar] | Scalar, /) -> Quantity[Scalar]: ...
|
|
67
|
+
|
|
68
|
+
def __iadd__(self, other: Quantity[Scalar] | Scalar, /) -> Quantity[Scalar]: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
69
|
+
return self + other
|
|
70
|
+
|
|
71
|
+
def __floordiv__(self, other: object, /) -> Impedance:
|
|
72
|
+
if not isinstance(other, Impedance):
|
|
73
|
+
return NotImplemented
|
|
74
|
+
left, right = self.value, other.value_in(self.unit)
|
|
75
|
+
if left == 0 or right == 0:
|
|
76
|
+
return Impedance(0, self.unit)
|
|
77
|
+
if left + right == 0:
|
|
78
|
+
error_message = 'Parallel admittances cancel; the equivalent impedance is infinite.'
|
|
79
|
+
raise ZeroDivisionError(error_message)
|
|
80
|
+
# Z1*Z2/(Z1+Z2), avoiding a large product. Complex values use modulus.
|
|
81
|
+
smaller, larger = (left, right) if abs(left) <= abs(right) else (right, left)
|
|
82
|
+
return Impedance(smaller / (1 + smaller / larger), self.unit)
|
|
83
|
+
|
|
84
|
+
def to(self, unit: str | Unit, /) -> Impedance: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
85
|
+
return Impedance(self.value_in(unit), unit)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main() -> None:
|
|
89
|
+
"""Evaluate an RLC branch in parallel with a resistor, using RMS phasors."""
|
|
90
|
+
first = Impedance(Fraction(100))
|
|
91
|
+
second = Impedance(Fraction(1, 5), si.kohm)
|
|
92
|
+
parallel_resistors = first // second
|
|
93
|
+
assert parallel_resistors == Fraction(200, 3) * si.ohm
|
|
94
|
+
print(f'({first}) // ({second.to(first.unit)}) = {parallel_resistors}')
|
|
95
|
+
|
|
96
|
+
frequency = 1 * si.kHz
|
|
97
|
+
omega = 2 * math.pi * frequency
|
|
98
|
+
inductance = 10 * si.mH
|
|
99
|
+
capacitance = 1 * si.uF
|
|
100
|
+
inductor = Impedance(1j * (omega * inductance).value_in(si.ohm))
|
|
101
|
+
capacitor = Impedance(-1j * (1 / (omega * capacitance)).value_in(si.ohm))
|
|
102
|
+
series = first + inductor + capacitor
|
|
103
|
+
shunt = Impedance(300)
|
|
104
|
+
network = series // shunt
|
|
105
|
+
voltage = 12 * si.V # Real RMS voltage phasor; the current may be complex.
|
|
106
|
+
current = voltage / network
|
|
107
|
+
current_value = current.value
|
|
108
|
+
assert isinstance(current_value, complex)
|
|
109
|
+
complex_power = voltage * Quantity(current_value.conjugate(), current.unit)
|
|
110
|
+
|
|
111
|
+
assert network.dimension == si.ohm.dimension
|
|
112
|
+
assert current.dimension == si.A.dimension
|
|
113
|
+
assert complex_power.dimension == si.W.dimension
|
|
114
|
+
assert current_value.real > 0 and current_value.imag > 0 # Net capacitive branch.
|
|
115
|
+
print(f'At {frequency}: series RLC = {series.to(si.ohm):.3f}')
|
|
116
|
+
print(f'With a {shunt} parallel branch: Z = {network.to(si.ohm):.3f}')
|
|
117
|
+
print(f'At {voltage} RMS: I = {current.to(si.mA):.3f}, S = {complex_power.to(si.W):.3f}')
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == '__main__':
|
|
121
|
+
main()
|