Casey W. Stark

Astrophysics, computation, and code

Code Release: Dimensionful

| Comments

I’m releasing a Python package I named dimensionful. It’s a simple library that attaches symbolic units to any sort of data.

The idea is that you can create Unit objects and associate them to any data, using a Quantity object. You can then perform any operation on the Quantity objects and the units are handled behind the scenes for you, with as few conversions as necessary (almost always zero).

Here’s an example to give you an idea of how it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
from dimensionful import Unit, Quantity

# totally fake data
energy_data = np.random.random(4)
time_data = np.random.random(4)

# Create a Unit. Dimensionful knows the unit symbols "g", "cm", and
# "s", but you can provide any string or sympy expression.
energy_units = Unit("g * cm**2 * s**-2")

# Create a Quantity. First argument is the data, second is the units. The unit
# arg can be a Unit object or a string.
energies = Quantity(energy_data, energy_units)
time_intervals = Quantity(time_data, "s")

# Try Quantity operations
power = energies / time_intervals
print power

This outputs [ 0.84771033 1.01795928 0.34694982 0.26888805] cm**2*g/s**3. And a brief version of the same thing:

1
2
3
from dimensionful import Quantity, erg, s

print Quantity(np.random.random(4), erg) / Quantity(np.random.random(4), s)

This package is similar to magnitude and sympy.physics.units, but I think there are some key differences.

Symbolic Units

If I want to express some of my data in “M_solar / yr * s”, I do not want it automatically converted to “kg”. This is at least one unnecessary operation and is almost always not what I want.

Rather than carrying around powers of SI base units or automatically converting to SI, dimensionful understands that your units are mathematical symbols and should be treated as such. They are never reduced to other units until you ask for that.

Example using the Hubble rate:

1
2
3
4
5
6
7
8
9
>>> h = 0.71
>>> H_0 = Quantity(100 * h, "km / s / Mpc")
>>> print H_0
71.0 km/(Mpc*s)
>>> print H_0.get_in("s**-1")
2.30095149205362e-18 1/s
>>> time_interval = Quantity(100, "s")
>>> print H_0 * time_interval
7100.0 km/Mpc

Although km / Mpc is just a number, dimensionful does not convert it until you ask for the Hubble rate in inverse seconds.

Dimensionality-based

Each Unit object has an attribute called dimensions. This a symbolic expression of a Unit’s dimensionality, made up of “mass”, “length”, “time”, and “temperature” symbols.

1
2
3
>>> from dimensionful import erg
>>> erg.dimensions
(length)**2*(mass)/(time)**2

This is what allows dimensionful to understand the relationships between different units. If I have data with dimensions (length)**2*(mass)/(time)**2, I know that I can’t convert to units with dimensions (length)**2*(mass)/(time).

In this system, Units are combinations of dimensions and a reference value. I chose to use cgs for reference values, so each Unit object has the attributes dimensions and cgs_value:

1
2
3
4
5
>>> J = Unit("J")
>>> J.dimensions
(length)**2*(mass)/(time)**2
>>> J.cgs_value
10000000.0

Base units

Most unit libraries are SI based. In my opinion, cgs is a much better system for your base units. The typical reasons are that there are fewer base units and your E&M laws are simpler. See http://en.wikipedia.org/wiki/Gaussian_units for more info.

Check it out

The code is public at http://github.com/caseywstark/dimensionful. I decided license it under BSD to keep the legal stuff simple. There are a decent number of tests included (run them with nosetests), but it’s still fairly new. Let me know what you think if you tinker with it.

Comments