Cython-powered quantities.
Cyantities ships two Python classes: Unit
and Quantity
. The Unit
class
represents a physical unit, that is, a reference vector in a basis of physical
dimensions. In Cyantities, everything is based upon the SI (internally all
units are represented as an array of integers, each of which represents the
powers of an SI basic unit).
The Unit
class can be initialized by passing a string representation of the
unit:
from cyantities import Unit
unit0 = Unit('km')
unit1 = Unit('m/(s^2)')
unit2 = Unit('kg m s^-2')
The Quantity
class represents numbers that are associated with a unit: physical
quantities.
from cyantities import Quantity
For convenience and efficiency, the numbers can be either a single
float
(essentially leading to a (float,Unit)
tuple) or a NumPy array. See,
for instance, the following code excerpt from the example of a ball throw with
air friction (examples/parabola/run.py)
t = Quantity(np.linspace(0.0, 6.0), 's')
x0 = Quantity(0.0, 'm')
y0 = Quantity(2.1, 'm')
v = Quantity(145.0, 'km h^-1')
Here, the first line creates an equidistantly spaced set of time points between 0 and 6 seconds. The second and third line set the initial position of the ball, two scalars with unit metre, to above head height of an average human. The last line sets the initial velocity to 145 kilometers per hour.
To convert quantities back to pure numbers, unit dimensions need to be canceled out through multiplication or division. See, for instance, the following lines of examples/parabola/run.py that plot the trajectory of the ball thrown with firction:
import matplotlib as plt
# ... more code here, resulting in the trajetories 'x' and 'y' ...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(np.array(x / Unit('m')), np.array(y / Unit('m')), marker='.')
The last line highlights an important feature of the Quantity
class: if, and only
if, a Quantity
instance is dimensionless, it can be converted to a NumPy array.
This conversion can be automatic via the NumPy __array__
interface. This special
method is added dynamically to dimensionless Quantity
instances, allowing automatic
conversions from the NumPy side like
import numpy as np
z = np.exp(Quantity(np.arange(3), 'm') / Unit('cm'))
but preventing numeric operations on quantities with a physical dimension:
z = np.exp(Quantity(np.arange(3), 'm')) # raises an exception
Besides multiplication and division with other quantities and units, Quantity
instances can be added to and subtracted from quantities of the same unit
dimension, taking into account potential scale differences in the physical units.
Two methods (rules) are available to specify units. Both methods accept a string representation of the unit and parse that string assuming a certain formatting. A description of the two rules follows.
The coherent SI-style string representation has to be of the form
'u0 u1 u3^2 u4^-1 u5^-3'
. Here, units are demarked by spaces (multiplication
signs *
can also be used). Integer unit powers, including negative, follow
the unit representation and are indicated by the caret ^
.
Note: Any order of the input units is acceptable.
The nominator-denominator rule string representation has to be of the form
'u0*u1*u3^2/(u4*u5^3)'
, where u0
is the first unit including prefix (e.g.
km
), and so forth. Units are demarked by multiplication signs *
, integer
unit powers follow the unit representation and are indicated by the caret ^
.
All negative powers of units have to follow a single slash /
, be enclosed in
parantheses, and be positive therein.
The main reason for developing Cyantities was to have a translation utility of unit-associated quantities from the Python world to the Boost.Units library. The canonical means to do so with Cyantities is through an intermediary Cython step (Python → Cython → C++).
Users will create units and quantities using the Unit
and Quantities
units of
the Cyantities package. Importing the Cyantities Cython API, the cyantities::Unit
C++ class, which is backing both Python classes, is exposed. This C++ class can
then be transformed into a Boost.Units quantity, performing runtime checks of the
dimensional correctness of the data passed from the Python level. Once this is done,
the numerical data can similarly be transformed from the Python objects to the
Boost.Units-powered C++ library.
The interaction of Cyantities with Boost.Units is best explained through an example. See the example of a ball throw with gravity and friction in examples/parabola for a blueprint of how to use Cyantities with Boost.Units, and the example of gravitational force on different masses in examples/gravity for different methods to iterate vector-valued quantities in C++.
The following basic units are currently implemented in Cyantities and can be used to compose units based on the coherent SI or the nominator-denominator rule:
Python string | Unit | Comment |
---|---|---|
"1" |
dimensionless | no prefix allowed |
"m" |
metre | |
"kg" |
kilogram | |
"s" |
second | |
"A" |
Ampère | |
"K" |
Kelvin | |
"mol" |
mole | |
"cd" |
candela | |
"rad" |
radian | Follow Boost.Units |
"sr" |
steradian | Follow Boost.Units |
The following SI-derived units are similarly available:
Python string | Unit | Comment |
---|---|---|
"Pa" |
Pascal | |
"J" |
Joule | |
"Hz" |
Hertz | |
"N" |
Newton | |
"W" |
Watt | |
"C" |
Coulomb | |
"V" |
Volt | |
"F" |
Farad | |
"Ω" |
Ohm | |
"S" |
Siemens | |
"Wb" |
Weber | |
"T" |
Tesla | |
"H" |
Henry | |
"lm" |
lumen | |
"lx" |
lux | |
"Bq" |
Becquerel | |
"Gy" |
Gray | |
"Sv" |
Sievert | |
"kat" |
katal |
Other units include:
Python string | Unit | Comment |
---|---|---|
"erg" |
erg | (CGS units) |
"g" |
gram | |
"h" |
hour |
The temperature scales °C and °F are not supported as Python strings since they are not proportional to Kelvin and require an offset. Please define all your temperatures in K.
This software is licensed under the European Public License (EUPL) version 1.2 or later.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Support for
dtype
andcopy
parameters inQuantity._array
. - Added typing stubs for
Unit
andQuantity
.
- Remove use of deprecated
numpy.array
withcopy=False
. - Removed internal inconsistency in how scalar and array-valued Quantities
were handled in the
Quantity.wrapper()
routine. Now, scalar-valued quantities can similarly be filled from the C++ side. - Prevent NumPy from creating an object array on left-hand multiplication
by setting
__array_ufunc__ = None
.
- Indexing of matrix-valued
Quantity
instances. - Absolute for
Quantity
instance.
- Add computation of unit powers in C++ and Python.
- Add unary negation operator to
Quantity
.
- Fix array values of
Quantity
with dimension larger than one causing runtime errors. - Use
_val_object
instead of_val_array
to obtainNDArray
string representation. - Fix
conv
factor not honored when callingUnit(dec_exp, conv)
constructor. - Remove the internal
_val_array
field entirely due to its (apparent?) inability to handle variable dimension.
- Fix check in
Quantity
not considering integers as valid scalars.
- Add
shape
method forQuantity
, which allows to query the (array-) shape of the underlying data.
- Add
zeros_like
generator function forQuantity
(Cython only) - Add the
iter()
andconst_iter()
templated methods to C++QuantityWrapper
class, allowing for the use of range-based for loops and range adaptor closures (|
-operator syntax) in compile-time provided units. - Add the
gravity
example that showcases different methods to iterate vector-valued quantities in C++. - Add benchmark for different iteration methods.
- Fixed the installation requirements and source distribution manifest.
- Add version coherence test script.
- First release