Magnitude

A Python library for computing with physical quantities

Magnitude implements efficient computation with physical quantities. It allows you to do mathematical operations with them as if they were numbers, taking care of the units behind the scenes.

A physical quantity is a number with a unit, like 10 km/h. Units are specified as strings. They can be any of the SI units, plus a bunch of non-SI, bits, dollars, and any combination of them. They can include the standard SI prefixes. Magnitude can operate with physical quantities, parse their units, and print them. You don't have to worry about unit consistency or conversions; everything is handled transparently.

By default output is done either in the unit with which the Magnitude object has been created, or in basic SI units, but you can specify any output unit, as long as it can be reduced to the basic units of the physical quantity.

xkcd kB

All standard prefixes are understood, from yocto to yotta and from kibi to exbi.

Download

It is hosted at Github. Either clone it with git:

git clone git://github.com/juanre/magnitude.git

or download the package.

Installation

In the magnitude directory,

sudo python setup.py install

Examples

>>> from magnitude import mg
>>> print mg(10, 'm/s') ** 2
100.0000 m2 / s2
>>> print (mg(10, 'm') * 2 / (10, 'm/s2')).sqrt()
1.4142 s
>>> tsq = mg(10, 'm') * 2 / (10, 'm/s2')
>>> print tsq ** 0.5
1.4142 s
>>> print mg(1, "lightyear") / mg(1, "c")
>>> 31557600.0000 s
>>> y = mg(1, "lightyear") / (1, "c")
>>> print y.ounit("year")
1.0000 year
>>> print y.ounit('day')
>>> print yd
365.2500 day
>>> power = mg(100, 'kg') * (2, 'gravity') * (5, 'km/h')
>>> print power
2724.0694 m2 kg / s3
>>> print power.ounit('mW')
2724069.4444 mW
>>> print power.ounit('kW')
2.7241 kW

Reference

Available units

The basic units understood by the magnitude module are:

indicator meaning
$ dollar ('dollar' is also acceptable)
A ampere
b bit
cd candela
K degrees Kelvin
kg kilograms
m meters
mol amount of substance
s seconds

From these basic units you can derive many other units. The magnitude package predefines these derived units:

# Magnitudes for the base SI units
new_mag('m', Magnitude(1.0, m=1))
new_mag('s', Magnitude(1.0, s=1))
new_mag('K', Magnitude(1.0, K=1))
new_mag('kg', Magnitude(1.0, kg=1))
new_mag('A', Magnitude(1.0, A=1))
new_mag('mol', Magnitude(1.0, mol=1))
new_mag('cd', Magnitude(1.0, cd=1))
new_mag('$', Magnitude(1.0, dollar=1))
new_mag('dollar', Magnitude(1.0, dollar=1))
new_mag('b', Magnitude(1.0, b=1))           # bit

# Magnitudes for derived SI units
new_mag('B', Magnitude(8.0, b=1))
new_mag('rad', Magnitude(1.0))  # radian
new_mag('sr', Magnitude(1.0))  # steradian
new_mag('Hz', Magnitude(1.0, s=-1))  # hertz
new_mag('g', Magnitude(1e-3, kg=1))  # gram
new_mag('N', Magnitude(1.0, m=1, kg=1, s=-2))  # newton
new_mag('Pa', Magnitude(1.0, m=-1, kg=1, s=-2))  # pascal
new_mag('J', Magnitude(1.0, m=2, kg=1, s=-2))  # joule
new_mag('W', Magnitude(1.0, m=2, kg=1, s=-3))  # watt
new_mag('C', Magnitude(1.0, s=1, A=1))  # coulomb
new_mag('V', Magnitude(1.0, m=2, kg=1, s=-3, A=-1))  # volt
new_mag('F', Magnitude(1.0, m=-2, kg=-1, s=4, A=2))  # farad, C/V
new_mag('ohm', Magnitude(1.0, m=2, kg=1, s=-3, A=-2))  # ohm, V/A
new_mag('S', Magnitude(1.0, m=-2, kg=-1, s=3, A=2))  # siemens, A/V, el cond
new_mag('Wb', Magnitude(1.0, m=2, kg=1, s=-2, A=-1))  # weber, V.s, mag flux
new_mag('T', Magnitude(1.0, kg=1, s=-2, A=-1))  # tesla, Wb/m2, mg flux dens
new_mag('H', Magnitude(1.0, m=2, kg=1, s=-2, A=-2))  # henry, Wb/A, induct.
new_mag('degC', Magnitude(1.0, K=1))  # celsius, !!
new_mag('lm', Magnitude(1.0, cd=1))  # lumen, cd.sr (=cd)), luminous flux
new_mag('lux', Magnitude(1.0, m=-2, cd=1))  # lux, lm/m2, illuminance
new_mag('Bq', Magnitude(1.0, s=-1))  # becquerel, activity of a radionulide
new_mag('Gy', Magnitude(1.0, m=2, s=-2))  # gray, J/kg, absorbed dose
new_mag('Sv', Magnitude(1.0, m=2, s=-2))  # sievert, J/kg, dose equivalent
new_mag('kat', Magnitude(1.0, s=-1, mol=1))  # katal, catalitic activity
# Non-SI but almost:
new_mag('b', Magnitude(8.0, b=1))  # byte, note that B is Bel, as in dB

### Other
# length
new_mag("'", Magnitude(0.3048, m=1))  # feet
new_mag('ft', Magnitude(0.3048, m=1))  # feet
new_mag('inch', Magnitude(0.0254, m=1))  # inch
new_mag('"', Magnitude(0.0254, m=1))  # inch
new_mag('lightyear', Magnitude(2.99792458e8 * 365.25 * 86400, m=1))

# volume
new_mag('l', Magnitude(0.001, m=3))

# time
# year is tropical year, "the mean interval between vernal
# equinoxes.  Differs from the sidereal year by 1 part in 26000
# due to precession of the earth about its rotational axis
# combined with precession of the perihelion of the earth's orbit"
# (from units.dat).
new_mag('year', Magnitude(31556925.974678401, s=1))
new_mag('day', Magnitude(86400, s=1))
new_mag('h', Magnitude(3600, s=1))
new_mag('min', Magnitude(60, s=1))

# Resolution
new_mag('dpi', Magnitude(1.0 / 0.0254, m=-1))
new_mag('lpi', Magnitude(1.0 / 0.0254, m=-1))

# Velocity
new_mag('ips', Magnitude(0.0254, m=1, s=-1))
new_mag('c', Magnitude(2.99792458e8, m=1, s=-1))

# Acceleration
new_mag('gravity', Magnitude(9.80665, m=1, s=-2))

Two magnitudes have no units, 'rad' (radian - unit of plane angle) and 'sr' (steradian - unit of solid angle).

Any of the above units can be augmented with the following set of scale prefixes:

_prefix = {'y': 1e-24,  # yocto
           'z': 1e-21,  # zepto
           'a': 1e-18,  # atto
           'f': 1e-15,  # femto
           'p': 1e-12,  # pico
           'n': 1e-9,   # nano
           'u': 1e-6,   # micro
           'm': 1e-3,   # mili
           'c': 1e-2,   # centi
           'd': 1e-1,   # deci
           'k': 1e3,    # kilo
           'M': 1e6,    # mega
           'G': 1e9,    # giga
           'T': 1e12,   # tera
           'P': 1e15,   # peta
           'E': 1e18,   # exa
           'Z': 1e21,   # zetta
           'Y': 1e24,   # yotta

           # Binary prefixes, approved by the International
           # Electrotechnical Comission in 1998.  Since then, kb means
           # 1000 bytes; for 1024 bytes use Kib (note the capital K in
           # the binary version, and the lower case for the b of byte,
           # see comment in byte definition below).
           'Ki': 2 ** 10, # Kibi (<- kilo, 10^3)
           'Mi': 2 ** 20, # Mebi (<- mega, 10^6)
           'Gi': 2 ** 30, # Gibi (<- giga, 10^9)
           'Ti': 2 ** 40, # Tebi (<- tera, 10^12)
           'Pi': 2 ** 50, # Pebi (<- peta, 10^15)
           'Ei': 2 ** 60  # Exbi (<- exa, 10^18)
           }

Exported symbols

  • Magnitude [class] — Numbers with units; math operations are overloaded
  • mg(number, unit, ounit='') — Construct a Magnitude
  • ensmg(m, unit='') — Tries to build a Magnitude out of something
  • newmag(indicator, mag) — Intern a new magnitude with its name
  • MagnitudeError [class] — Magnitude error handling

Defining new magnitudes

You can define new magnitudes by instantiating the Magnitude class. Suppose you want to define pounds as a magnitude and associate with it the unit 'lb'. A pound is 0.45359237 kilograms, so we have

>>> lb = Magnitude(0.45359237, kg=1)

To make it recognized automatically you also have to introduce it to the system with new_mag. You can then use it as you would any other predefined physical quantity:

>>> new_mag('lb', lb)
>>> me = mg(180, 'lb')
>>> print me.ounit('kg').toval()
81.6466266
>>> new_mag('mile', mg(160934.4, 'cm'))
>>> print mg(100, 'mile/h').ounit('km/h')
160.9344 km/h

Bits and bytes (2009-11-03)

A previous version of the library used "bit" for bit and "b" for byte, leaving B for Bel. Following Michael Scheper's suggestion we follow now IEEE 1541 and use "b" for bit and "B" for byte. If the need arises I'll implement ad-hoc esupport for dB, but for the time being there is none.

More on units

Alternative unit libraries for Python

I haven't tested them; I wrote these comments several years ago after looking at the documentation.

  1. Unum looks well finished and is thoroughly documented. Good: allows you to define arbitrary units (magnitude only supports user-defined units as long as they are a combination of the base units). Bad: it looks like it doesn't handle prefixes, and it clutters your namespace with all its unit definitions (you end up with variables named M, S, etc. in your namespace).
  2. Scalar looks very similar to Unum, maybe not as well documented. Looks like it has exactly the same problems and advantages, plus the ability of running with units disabled (which should be good for performance).
  3. PhyisicalQuantities, part of ScientificPython, is very similar to magnitude. Same approach, essentially the same functionality, very similar API. Looks like it also clutters your namespace with unit names.
Juan Reyero Barcelona, 2009-11-07
 

blog comments powered by Disqus