python-units 0.3.0__tar.gz → 0.4.0__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.
Files changed (31) hide show
  1. {python_units-0.3.0 → python_units-0.4.0}/PKG-INFO +183 -2
  2. {python_units-0.3.0 → python_units-0.4.0}/README.md +182 -1
  3. {python_units-0.3.0 → python_units-0.4.0}/pyproject.toml +1 -1
  4. {python_units-0.3.0 → python_units-0.4.0}/src/api/public.py +50 -0
  5. {python_units-0.3.0 → python_units-0.4.0}/src/api/si.py +51 -1
  6. {python_units-0.3.0 → python_units-0.4.0}/src/core/__init__.py +8 -0
  7. {python_units-0.3.0 → python_units-0.4.0}/src/core/quantity.py +226 -12
  8. {python_units-0.3.0 → python_units-0.4.0}/src/core/unit_definitions.py +170 -17
  9. {python_units-0.3.0 → python_units-0.4.0}/src/python_units.egg-info/PKG-INFO +183 -2
  10. {python_units-0.3.0 → python_units-0.4.0}/src/units/unit.py +16 -0
  11. {python_units-0.3.0 → python_units-0.4.0}/LICENSE +0 -0
  12. {python_units-0.3.0 → python_units-0.4.0}/setup.cfg +0 -0
  13. {python_units-0.3.0 → python_units-0.4.0}/setup.py +0 -0
  14. {python_units-0.3.0 → python_units-0.4.0}/src/adapters/__init__.py +0 -0
  15. {python_units-0.3.0 → python_units-0.4.0}/src/api/__init__.py +0 -0
  16. {python_units-0.3.0 → python_units-0.4.0}/src/core/deprecations.py +0 -0
  17. {python_units-0.3.0 → python_units-0.4.0}/src/core/errors.py +0 -0
  18. {python_units-0.3.0 → python_units-0.4.0}/src/models/__init__.py +0 -0
  19. {python_units-0.3.0 → python_units-0.4.0}/src/models/dimension.py +0 -0
  20. {python_units-0.3.0 → python_units-0.4.0}/src/python_units.egg-info/SOURCES.txt +0 -0
  21. {python_units-0.3.0 → python_units-0.4.0}/src/python_units.egg-info/dependency_links.txt +0 -0
  22. {python_units-0.3.0 → python_units-0.4.0}/src/python_units.egg-info/requires.txt +0 -0
  23. {python_units-0.3.0 → python_units-0.4.0}/src/python_units.egg-info/top_level.txt +0 -0
  24. {python_units-0.3.0 → python_units-0.4.0}/src/services/__init__.py +0 -0
  25. {python_units-0.3.0 → python_units-0.4.0}/src/units/__init__.py +0 -0
  26. {python_units-0.3.0 → python_units-0.4.0}/src/units/dimension.py +0 -0
  27. {python_units-0.3.0 → python_units-0.4.0}/src/units/errors.py +0 -0
  28. {python_units-0.3.0 → python_units-0.4.0}/src/units/quantity.py +0 -0
  29. {python_units-0.3.0 → python_units-0.4.0}/src/units/si.py +0 -0
  30. {python_units-0.3.0 → python_units-0.4.0}/src/utils/__init__.py +0 -0
  31. {python_units-0.3.0 → python_units-0.4.0}/src/utils/numbers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-units
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Python library to represent numbers with units
5
5
  Author-email: "Paul K. Korir, PhD" <paul.korir@gmail.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -30,7 +30,74 @@ Dynamic: license-file
30
30
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://pypi.org/project/python-units/)
31
31
  [![Coverage 92%](https://img.shields.io/badge/coverage-92%25-brightgreen.svg)](/Users/paulkorir/PycharmProjects/python-units/tests/unit/test_units.py)
32
32
 
33
- Python library to represent quantities with units.
33
+ # The Price of Unitless Arithmetic
34
+
35
+ On September 23, 1999, flight controllers expected NASA's Mars Climate Orbiter
36
+ to pass behind Mars, fire its engine, and come back into radio contact after
37
+ orbit insertion. It never came back. When engineers reviewed the final hours of
38
+ flight data, the trajectory was not where the navigation system thought it was:
39
+ the spacecraft had approached Mars far lower than planned. The investigation
40
+ traced the loss to a unit boundary that software had failed to defend. One side
41
+ of the system handled "small forces" data in English units; the navigation side
42
+ expected metric units.
43
+
44
+ That is the kind of bug this package is meant to stop. Without units, the
45
+ mistake is just arithmetic:
46
+
47
+ ```python
48
+ # A navigation routine expects impulse in newton-seconds.
49
+ expected_impulse_ns = 120
50
+
51
+ # A supplier routine accidentally sends a value in a different force unit.
52
+ # The number is still just a number, so Python accepts it.
53
+ reported_impulse_other_units = 120
54
+
55
+ trajectory_impulse = expected_impulse_ns + reported_impulse_other_units
56
+ print(trajectory_impulse) # 240
57
+ ```
58
+
59
+ There is nothing in `240` that tells you a spacecraft trajectory may now be
60
+ wrong. With units attached, the mismatch stops at the boundary:
61
+
62
+ ```python
63
+ from units import CustomUnitBase
64
+ from units.dimension import DimensionSystem
65
+ from units.si import newton, second
66
+
67
+ class EnglishImpulseUnit(CustomUnitBase):
68
+ dimension_system = DimensionSystem("english-impulse", ("lbf_s",))
69
+
70
+ pound_force_second = EnglishImpulseUnit.define("lbf_s")
71
+
72
+ expected_impulse = 120 * newton * second
73
+ reported_impulse = 120 * pound_force_second
74
+
75
+ trajectory_impulse = expected_impulse + reported_impulse
76
+ # UnitCompatibilityError: units mismatch: m·kg·s^-1 and lbf_s
77
+ ```
78
+
79
+ That failure is the feature. A bug that would otherwise move through a program
80
+ as an ordinary number is stopped before it contaminates mission-critical
81
+ calculations.
82
+
83
+ Background: NASA/JPL describe the Mars Climate Orbiter loss as a navigation
84
+ error caused by a failure to translate English units to metric, sending the
85
+ spacecraft too close to Mars.
86
+
87
+ Source: https://en.wikipedia.org/wiki/Mars_Climate_Orbiter
88
+
89
+ # About
90
+
91
+ `python-units` is a Python package for unit-aware arithmetic. It provides:
92
+ - a `Quantity` type that combines numeric values with unit information
93
+ - a registry of SI base and derived units
94
+ - algebraic unit manipulation and compatibility checks
95
+ - explicit multiplicative conversions between compatible units
96
+ - a public API that prioritizes scalar-by-unit construction and SI unit imports
97
+ - a migration path from the legacy `Unit` constructor and compatibility helpers
98
+ - a Python 3-only codebase with no Python 2 compatibility shims
99
+ - a project structure that separates public API, core logic, data models, and utilities
100
+ - comprehensive unit tests and documentation
34
101
 
35
102
  Supported Python versions: 3.10+
36
103
 
@@ -139,12 +206,18 @@ Stable top-level imports:
139
206
 
140
207
  * `Quantity`
141
208
  * `Unit` (compatibility alias for `Quantity`)
209
+ * `convert`
210
+ * `value`
211
+ * `unit`
212
+ * `multiplier`
142
213
  * `UnitsError`, `InvalidUnitError`, `InvalidValueError`,
143
214
  `UnitCompatibilityError`, `UnitOperandError`
144
215
 
145
216
  Canonical unit imports:
146
217
 
147
218
  * `from units.si import metre, second, newton`
219
+ * prefixed and scaled units such as `kilometre`, `centimetre`, `gram`,
220
+ `minute`, `hour`, `kilowatt`, and `millivolt`
148
221
 
149
222
  Legacy compatibility helpers:
150
223
 
@@ -166,11 +239,119 @@ deprecated compatibility paths are scheduled for removal in `1.0.0`.
166
239
 
167
240
  * Addition and subtraction require identical units.
168
241
  * Multiplication and division combine units algebraically.
242
+ * Explicit scale-only conversions are available through `quantity.to(unit)` and
243
+ `convert(quantity, unit)`.
169
244
  * Integer powers of units and unit-bearing quantities are supported.
170
245
  * Unitless quantities are supported explicitly.
246
+ * Affine conversions, such as `degree_celcius <-> kelvin`, are intentionally not
247
+ implemented yet.
171
248
  * The core quantity model allows signed values. Domain-specific constraints such
172
249
  as non-negative lengths should be enforced by higher-level types or validators.
173
250
 
251
+ # Conversion foundations
252
+
253
+ `0.4.0` adds explicit multiplicative conversions. Conversion never happens
254
+ silently during addition or subtraction; you choose the target unit.
255
+
256
+ ```python
257
+ from units import convert, multiplier, unit, value
258
+ from units.si import gram, hour, kilogram, kilometre, metre, minute, second
259
+
260
+ distance = 1.5 * kilometre
261
+ print(distance.to(metre)) # 1500 m
262
+ print(convert(2500 * metre, kilometre)) # 2.5 km
263
+
264
+ duration = 2 * hour
265
+ print(duration.to(minute)) # 120 min
266
+ print((1500 * gram).to(kilogram)) # 1.5 kg
267
+
268
+ speed = (72 * kilometre) / (2 * hour)
269
+ print(speed) # 10.0 m·s^-1
270
+
271
+ print(value(distance)) # 1.5
272
+ print(unit(distance)) # km
273
+ print(multiplier(kilometre)) # 1000.0
274
+ ```
275
+
276
+ The conversion model is scale-only in this release. Celsius is a named
277
+ temperature unit, but converting between Celsius and kelvin requires an offset
278
+ and is reserved for a later affine-conversion release.
279
+
280
+ # Prefixed and scaled units
281
+
282
+ Common SI prefixes and practical time units are available from `units.si`:
283
+
284
+ ```python
285
+ from units.si import (
286
+ centimetre,
287
+ gram,
288
+ hour,
289
+ kiloampere,
290
+ kilometre,
291
+ kilovolt,
292
+ kilowatt,
293
+ megawatt,
294
+ micrometre,
295
+ microsecond,
296
+ milliampere,
297
+ milligram,
298
+ millimetre,
299
+ millisecond,
300
+ millivolt,
301
+ milliwatt,
302
+ minute,
303
+ nanometre,
304
+ nanosecond,
305
+ tonne,
306
+ )
307
+ ```
308
+
309
+ Scaled units participate correctly in multiplication, division, and powers:
310
+
311
+ ```python
312
+ from units.si import hour, kilometre, metre
313
+
314
+ area = (2 * kilometre) * (3 * metre)
315
+ print(area) # 6000 m^2
316
+
317
+ square = (2 * kilometre) ** 2
318
+ print(square) # 4000000 m^2
319
+
320
+ speed = (72 * kilometre) / (2 * hour)
321
+ print(speed) # 10.0 m·s^-1
322
+ ```
323
+
324
+ # Familiar composite units
325
+
326
+ Composite unit expressions such as `kilometre / hour` are algebraic unit
327
+ definitions. They carry the correct scale factor, but anonymous composite units
328
+ render in canonical SI base form:
329
+
330
+ ```python
331
+ from units.si import hour, kilometre
332
+
333
+ speed = 30 * kilometre / hour
334
+ print(speed) # 8.333333333333334 m·s^-1
335
+ ```
336
+
337
+ When you want a semantically familiar display unit, give that composite unit an
338
+ explicit name and convert to it:
339
+
340
+ ```python
341
+ from units import DerivedUnit, convert
342
+ from units.si import hour, kilometre
343
+
344
+ kilometres_per_hour = DerivedUnit.define("km·hr^-1", kilometre / hour)
345
+
346
+ speed = 30 * kilometre / hour
347
+ print(convert(speed, kilometres_per_hour)) # 30 km·hr^-1
348
+ print(30 * kilometres_per_hour) # 30 km·hr^-1
349
+ ```
350
+
351
+ This keeps the arithmetic deterministic while letting application code choose
352
+ domain-specific display names such as `km·hr^-1`, `N·m`, or any other familiar
353
+ derived unit form.
354
+
174
355
  # Real-world examples
175
356
 
176
357
  ## Electrical engineering: from resistance to power dissipation
@@ -4,7 +4,74 @@
4
4
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://pypi.org/project/python-units/)
5
5
  [![Coverage 92%](https://img.shields.io/badge/coverage-92%25-brightgreen.svg)](/Users/paulkorir/PycharmProjects/python-units/tests/unit/test_units.py)
6
6
 
7
- Python library to represent quantities with units.
7
+ # The Price of Unitless Arithmetic
8
+
9
+ On September 23, 1999, flight controllers expected NASA's Mars Climate Orbiter
10
+ to pass behind Mars, fire its engine, and come back into radio contact after
11
+ orbit insertion. It never came back. When engineers reviewed the final hours of
12
+ flight data, the trajectory was not where the navigation system thought it was:
13
+ the spacecraft had approached Mars far lower than planned. The investigation
14
+ traced the loss to a unit boundary that software had failed to defend. One side
15
+ of the system handled "small forces" data in English units; the navigation side
16
+ expected metric units.
17
+
18
+ That is the kind of bug this package is meant to stop. Without units, the
19
+ mistake is just arithmetic:
20
+
21
+ ```python
22
+ # A navigation routine expects impulse in newton-seconds.
23
+ expected_impulse_ns = 120
24
+
25
+ # A supplier routine accidentally sends a value in a different force unit.
26
+ # The number is still just a number, so Python accepts it.
27
+ reported_impulse_other_units = 120
28
+
29
+ trajectory_impulse = expected_impulse_ns + reported_impulse_other_units
30
+ print(trajectory_impulse) # 240
31
+ ```
32
+
33
+ There is nothing in `240` that tells you a spacecraft trajectory may now be
34
+ wrong. With units attached, the mismatch stops at the boundary:
35
+
36
+ ```python
37
+ from units import CustomUnitBase
38
+ from units.dimension import DimensionSystem
39
+ from units.si import newton, second
40
+
41
+ class EnglishImpulseUnit(CustomUnitBase):
42
+ dimension_system = DimensionSystem("english-impulse", ("lbf_s",))
43
+
44
+ pound_force_second = EnglishImpulseUnit.define("lbf_s")
45
+
46
+ expected_impulse = 120 * newton * second
47
+ reported_impulse = 120 * pound_force_second
48
+
49
+ trajectory_impulse = expected_impulse + reported_impulse
50
+ # UnitCompatibilityError: units mismatch: m·kg·s^-1 and lbf_s
51
+ ```
52
+
53
+ That failure is the feature. A bug that would otherwise move through a program
54
+ as an ordinary number is stopped before it contaminates mission-critical
55
+ calculations.
56
+
57
+ Background: NASA/JPL describe the Mars Climate Orbiter loss as a navigation
58
+ error caused by a failure to translate English units to metric, sending the
59
+ spacecraft too close to Mars.
60
+
61
+ Source: https://en.wikipedia.org/wiki/Mars_Climate_Orbiter
62
+
63
+ # About
64
+
65
+ `python-units` is a Python package for unit-aware arithmetic. It provides:
66
+ - a `Quantity` type that combines numeric values with unit information
67
+ - a registry of SI base and derived units
68
+ - algebraic unit manipulation and compatibility checks
69
+ - explicit multiplicative conversions between compatible units
70
+ - a public API that prioritizes scalar-by-unit construction and SI unit imports
71
+ - a migration path from the legacy `Unit` constructor and compatibility helpers
72
+ - a Python 3-only codebase with no Python 2 compatibility shims
73
+ - a project structure that separates public API, core logic, data models, and utilities
74
+ - comprehensive unit tests and documentation
8
75
 
9
76
  Supported Python versions: 3.10+
10
77
 
@@ -113,12 +180,18 @@ Stable top-level imports:
113
180
 
114
181
  * `Quantity`
115
182
  * `Unit` (compatibility alias for `Quantity`)
183
+ * `convert`
184
+ * `value`
185
+ * `unit`
186
+ * `multiplier`
116
187
  * `UnitsError`, `InvalidUnitError`, `InvalidValueError`,
117
188
  `UnitCompatibilityError`, `UnitOperandError`
118
189
 
119
190
  Canonical unit imports:
120
191
 
121
192
  * `from units.si import metre, second, newton`
193
+ * prefixed and scaled units such as `kilometre`, `centimetre`, `gram`,
194
+ `minute`, `hour`, `kilowatt`, and `millivolt`
122
195
 
123
196
  Legacy compatibility helpers:
124
197
 
@@ -140,11 +213,119 @@ deprecated compatibility paths are scheduled for removal in `1.0.0`.
140
213
 
141
214
  * Addition and subtraction require identical units.
142
215
  * Multiplication and division combine units algebraically.
216
+ * Explicit scale-only conversions are available through `quantity.to(unit)` and
217
+ `convert(quantity, unit)`.
143
218
  * Integer powers of units and unit-bearing quantities are supported.
144
219
  * Unitless quantities are supported explicitly.
220
+ * Affine conversions, such as `degree_celcius <-> kelvin`, are intentionally not
221
+ implemented yet.
145
222
  * The core quantity model allows signed values. Domain-specific constraints such
146
223
  as non-negative lengths should be enforced by higher-level types or validators.
147
224
 
225
+ # Conversion foundations
226
+
227
+ `0.4.0` adds explicit multiplicative conversions. Conversion never happens
228
+ silently during addition or subtraction; you choose the target unit.
229
+
230
+ ```python
231
+ from units import convert, multiplier, unit, value
232
+ from units.si import gram, hour, kilogram, kilometre, metre, minute, second
233
+
234
+ distance = 1.5 * kilometre
235
+ print(distance.to(metre)) # 1500 m
236
+ print(convert(2500 * metre, kilometre)) # 2.5 km
237
+
238
+ duration = 2 * hour
239
+ print(duration.to(minute)) # 120 min
240
+ print((1500 * gram).to(kilogram)) # 1.5 kg
241
+
242
+ speed = (72 * kilometre) / (2 * hour)
243
+ print(speed) # 10.0 m·s^-1
244
+
245
+ print(value(distance)) # 1.5
246
+ print(unit(distance)) # km
247
+ print(multiplier(kilometre)) # 1000.0
248
+ ```
249
+
250
+ The conversion model is scale-only in this release. Celsius is a named
251
+ temperature unit, but converting between Celsius and kelvin requires an offset
252
+ and is reserved for a later affine-conversion release.
253
+
254
+ # Prefixed and scaled units
255
+
256
+ Common SI prefixes and practical time units are available from `units.si`:
257
+
258
+ ```python
259
+ from units.si import (
260
+ centimetre,
261
+ gram,
262
+ hour,
263
+ kiloampere,
264
+ kilometre,
265
+ kilovolt,
266
+ kilowatt,
267
+ megawatt,
268
+ micrometre,
269
+ microsecond,
270
+ milliampere,
271
+ milligram,
272
+ millimetre,
273
+ millisecond,
274
+ millivolt,
275
+ milliwatt,
276
+ minute,
277
+ nanometre,
278
+ nanosecond,
279
+ tonne,
280
+ )
281
+ ```
282
+
283
+ Scaled units participate correctly in multiplication, division, and powers:
284
+
285
+ ```python
286
+ from units.si import hour, kilometre, metre
287
+
288
+ area = (2 * kilometre) * (3 * metre)
289
+ print(area) # 6000 m^2
290
+
291
+ square = (2 * kilometre) ** 2
292
+ print(square) # 4000000 m^2
293
+
294
+ speed = (72 * kilometre) / (2 * hour)
295
+ print(speed) # 10.0 m·s^-1
296
+ ```
297
+
298
+ # Familiar composite units
299
+
300
+ Composite unit expressions such as `kilometre / hour` are algebraic unit
301
+ definitions. They carry the correct scale factor, but anonymous composite units
302
+ render in canonical SI base form:
303
+
304
+ ```python
305
+ from units.si import hour, kilometre
306
+
307
+ speed = 30 * kilometre / hour
308
+ print(speed) # 8.333333333333334 m·s^-1
309
+ ```
310
+
311
+ When you want a semantically familiar display unit, give that composite unit an
312
+ explicit name and convert to it:
313
+
314
+ ```python
315
+ from units import DerivedUnit, convert
316
+ from units.si import hour, kilometre
317
+
318
+ kilometres_per_hour = DerivedUnit.define("km·hr^-1", kilometre / hour)
319
+
320
+ speed = 30 * kilometre / hour
321
+ print(convert(speed, kilometres_per_hour)) # 30 km·hr^-1
322
+ print(30 * kilometres_per_hour) # 30 km·hr^-1
323
+ ```
324
+
325
+ This keeps the arithmetic deterministic while letting application code choose
326
+ domain-specific display names such as `km·hr^-1`, `N·m`, or any other familiar
327
+ derived unit form.
328
+
148
329
  # Real-world examples
149
330
 
150
331
  ## Electrical engineering: from resistance to power dissipation
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-units"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "Python library to represent numbers with units"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.10"
@@ -5,20 +5,40 @@ from api.si import (
5
5
  ampere,
6
6
  becquerel,
7
7
  candela,
8
+ centimetre,
8
9
  coulomb,
9
10
  degree_celcius,
10
11
  farad,
12
+ gram,
11
13
  gray,
12
14
  henry,
13
15
  hertz,
16
+ hour,
14
17
  joule,
15
18
  katal,
16
19
  kelvin,
17
20
  kilogram,
21
+ kiloampere,
22
+ kilometre,
23
+ kilovolt,
24
+ kilowatt,
18
25
  lumen,
19
26
  lux,
27
+ megawatt,
20
28
  metre,
29
+ microgram,
30
+ micrometre,
31
+ microsecond,
32
+ milliampere,
33
+ milligram,
34
+ millimetre,
35
+ millisecond,
36
+ millivolt,
37
+ milliwatt,
38
+ minute,
21
39
  mole,
40
+ nanometre,
41
+ nanosecond,
22
42
  newton,
23
43
  ohm,
24
44
  pascal,
@@ -28,6 +48,7 @@ from api.si import (
28
48
  sievert,
29
49
  steradian,
30
50
  tesla,
51
+ tonne,
31
52
  volt,
32
53
  watt,
33
54
  weber,
@@ -43,12 +64,16 @@ from core.quantity import (
43
64
  Quantity,
44
65
  complex_quantity,
45
66
  complex_unit,
67
+ convert,
46
68
  float_quantity,
47
69
  float_unit,
48
70
  int_quantity,
49
71
  int_unit,
50
72
  long_quantity,
51
73
  long_unit,
74
+ multiplier,
75
+ unit,
76
+ value,
52
77
  )
53
78
  from core.unit_definitions import BaseUnit, CustomUnitBase, DerivedUnit, SIUnit
54
79
  from models.dimension import Dimension, DimensionSystem
@@ -73,28 +98,50 @@ __all__ = [
73
98
  "ampere",
74
99
  "becquerel",
75
100
  "candela",
101
+ "centimetre",
76
102
  "complex_quantity",
77
103
  "complex_unit",
104
+ "convert",
78
105
  "coulomb",
79
106
  "degree_celcius",
80
107
  "farad",
81
108
  "float_quantity",
82
109
  "float_unit",
110
+ "gram",
83
111
  "gray",
84
112
  "henry",
85
113
  "hertz",
114
+ "hour",
86
115
  "int_quantity",
87
116
  "int_unit",
88
117
  "joule",
89
118
  "katal",
90
119
  "kelvin",
91
120
  "kilogram",
121
+ "kiloampere",
122
+ "kilometre",
123
+ "kilovolt",
124
+ "kilowatt",
92
125
  "long_quantity",
93
126
  "long_unit",
94
127
  "lumen",
95
128
  "lux",
129
+ "megawatt",
96
130
  "metre",
131
+ "microgram",
132
+ "micrometre",
133
+ "microsecond",
134
+ "milliampere",
135
+ "milligram",
136
+ "millimetre",
137
+ "millisecond",
138
+ "millivolt",
139
+ "milliwatt",
140
+ "minute",
141
+ "multiplier",
97
142
  "mole",
143
+ "nanometre",
144
+ "nanosecond",
98
145
  "newton",
99
146
  "ohm",
100
147
  "pascal",
@@ -104,6 +151,9 @@ __all__ = [
104
151
  "sievert",
105
152
  "steradian",
106
153
  "tesla",
154
+ "tonne",
155
+ "unit",
156
+ "value",
107
157
  "volt",
108
158
  "watt",
109
159
  "weber",
@@ -29,7 +29,11 @@ siemens = DerivedUnit.define("S", ampere / volt)
29
29
  weber = DerivedUnit.define("Wb", volt * second)
30
30
  tesla = DerivedUnit.define("T", weber / metre / metre)
31
31
  henry = DerivedUnit.define("H", weber / ampere)
32
- degree_celcius = DerivedUnit.define("°C", kelvin)
32
+ degree_celcius = DerivedUnit.define(
33
+ "°C",
34
+ kelvin,
35
+ supports_multiplicative_conversion=False,
36
+ )
33
37
  lumen = DerivedUnit.define("lm", candela * steradian)
34
38
  lux = DerivedUnit.define("lx", lumen / metre / metre)
35
39
  becquerel = DerivedUnit.define("Bq", SIUnit() / second)
@@ -37,6 +41,31 @@ gray = DerivedUnit.define("Gy", joule / kilogram)
37
41
  sievert = DerivedUnit.define("Sv", joule / kilogram)
38
42
  katal = DerivedUnit.define("kat", mole / second)
39
43
 
44
+ kilometre = DerivedUnit.define("km", metre, conversion_factor=1000.0)
45
+ centimetre = DerivedUnit.define("cm", metre, conversion_factor=0.01)
46
+ millimetre = DerivedUnit.define("mm", metre, conversion_factor=0.001)
47
+ micrometre = DerivedUnit.define("µm", metre, conversion_factor=0.000001)
48
+ nanometre = DerivedUnit.define("nm", metre, conversion_factor=0.000000001)
49
+
50
+ gram = DerivedUnit.define("g", kilogram, conversion_factor=0.001)
51
+ milligram = DerivedUnit.define("mg", kilogram, conversion_factor=0.000001)
52
+ microgram = DerivedUnit.define("µg", kilogram, conversion_factor=0.000000001)
53
+ tonne = DerivedUnit.define("t", kilogram, conversion_factor=1000.0)
54
+
55
+ minute = DerivedUnit.define("min", second, conversion_factor=60.0)
56
+ hour = DerivedUnit.define("h", second, conversion_factor=3600.0)
57
+ millisecond = DerivedUnit.define("ms", second, conversion_factor=0.001)
58
+ microsecond = DerivedUnit.define("µs", second, conversion_factor=0.000001)
59
+ nanosecond = DerivedUnit.define("ns", second, conversion_factor=0.000000001)
60
+
61
+ milliampere = DerivedUnit.define("mA", ampere, conversion_factor=0.001)
62
+ kiloampere = DerivedUnit.define("kA", ampere, conversion_factor=1000.0)
63
+ millivolt = DerivedUnit.define("mV", volt, conversion_factor=0.001)
64
+ kilovolt = DerivedUnit.define("kV", volt, conversion_factor=1000.0)
65
+ milliwatt = DerivedUnit.define("mW", watt, conversion_factor=0.001)
66
+ kilowatt = DerivedUnit.define("kW", watt, conversion_factor=1000.0)
67
+ megawatt = DerivedUnit.define("MW", watt, conversion_factor=1000000.0)
68
+
40
69
  for unit in (
41
70
  newton,
42
71
  pascal,
@@ -59,20 +88,40 @@ __all__ = [
59
88
  "ampere",
60
89
  "becquerel",
61
90
  "candela",
91
+ "centimetre",
62
92
  "coulomb",
63
93
  "degree_celcius",
64
94
  "farad",
95
+ "gram",
65
96
  "gray",
66
97
  "henry",
67
98
  "hertz",
99
+ "hour",
68
100
  "joule",
69
101
  "katal",
70
102
  "kelvin",
71
103
  "kilogram",
104
+ "kiloampere",
105
+ "kilometre",
106
+ "kilovolt",
107
+ "kilowatt",
72
108
  "lumen",
73
109
  "lux",
110
+ "megawatt",
74
111
  "metre",
112
+ "microgram",
113
+ "micrometre",
114
+ "microsecond",
115
+ "milliampere",
116
+ "milligram",
117
+ "millimetre",
118
+ "millisecond",
119
+ "millivolt",
120
+ "milliwatt",
121
+ "minute",
75
122
  "mole",
123
+ "nanometre",
124
+ "nanosecond",
76
125
  "newton",
77
126
  "ohm",
78
127
  "pascal",
@@ -82,6 +131,7 @@ __all__ = [
82
131
  "sievert",
83
132
  "steradian",
84
133
  "tesla",
134
+ "tonne",
85
135
  "volt",
86
136
  "watt",
87
137
  "weber",
@@ -11,12 +11,16 @@ from .quantity import (
11
11
  Quantity,
12
12
  complex_quantity,
13
13
  complex_unit,
14
+ convert,
14
15
  float_quantity,
15
16
  float_unit,
16
17
  int_quantity,
17
18
  int_unit,
18
19
  long_quantity,
19
20
  long_unit,
21
+ multiplier,
22
+ unit,
23
+ value,
20
24
  )
21
25
  from .unit_definitions import BaseUnit, CustomUnitBase, DerivedUnit, SIUnit
22
26
 
@@ -33,10 +37,14 @@ __all__ = [
33
37
  "UnitsError",
34
38
  "complex_quantity",
35
39
  "complex_unit",
40
+ "convert",
36
41
  "float_quantity",
37
42
  "float_unit",
38
43
  "int_quantity",
39
44
  "int_unit",
40
45
  "long_quantity",
41
46
  "long_unit",
47
+ "multiplier",
48
+ "unit",
49
+ "value",
42
50
  ]