#!/usr/bin/python
# -*- coding: utf-8 -*-
"""This module provides basic support for calculations with units."""
import math
import operator
def check_parameter(name, parameter):
if not isinstance(parameter, Quantity):
fmt = 'Parameter "{}" must be of type unit.Quantity.'
raise ValueError(fmt.format(name))
[docs]class Quantity(object):
"""
Quantity base class. A quantity is made up of *magnitude* and
*dimension*.
"""
def __init__(self, magnitude, dimension):
self.magnitude = magnitude
self.dimension = dimension
[docs] def convert(self, new_dimension):
"""
Convert this quantity to a another dimension *new_dimension*
(e.g. to do a common mathematical operation on them). Will raise an
error if the new dimension is not allowd.
"""
if not new_dimension in self.dims.keys():
fmt = 'Unit "{}" can not be converted to unit "{}".'
raise ValueError(fmt.format(self.dimension, new_dimension))
else:
u = self.__class__(self.magnitude, self.dimension)
conv_fac = self.dims[self.dimension] / self.dims[new_dimension]
u.magnitude *= conv_fac
u.dimension = new_dimension
return u
[docs] def free(self):
"""Drop all unit support and just return the quantities magnitude."""
return self.magnitude
[docs] def operate(self, other, op):
"""
Common method to check on the nature of operands.
"""
if isinstance(other, Quantity):
c = self.convert(other.dimension)
operator = op(c.magnitude, other.magnitude)
dimension = other.dimension
else:
operator = op(self.magnitude, other)
dimension = self.dimension
return self.__class__(operator, dimension)
[docs] def compare(self, other, op):
"""
Common method to check on the nature of operands.
"""
if isinstance(other, Quantity):
c = self.convert(other.dimension)
operator = op(c.magnitude, other.magnitude)
else:
operator = op(self.magnitude, other)
return operator
def __add__(self, other):
return self.operate(other, operator.add)
def __radd__(self, other):
return self.operate(other, operator.add)
def __sub__(self, other):
return self.operate(other, operator.sub)
def __mul__(self, other):
return self.operate(other, operator.mul)
def __rmul__(self, other):
return self.operate(other, operator.mul)
def __div__(self, other):
return self.operate(other, operator.div)
def __truediv__(self, other):
return self.operate(other, operator.truediv)
def __lt__(self, other):
return self.compare(other, operator.lt)
def __le__(self, other):
return self.compare(other, operator.le)
def __eq__(self, other):
return self.compare(other, operator.eq)
def __ne__(self, other):
return self.compare(other, operator.ne)
def __ge__(self, other):
return self.compare(other, operator.ge)
def __gt__(self, other):
return self.compare(other, operator.gt)
def __abs__(self):
return abs(self.magnitude)
def __str__(self):
return "{} {}".format(self.magnitude, self.dimension)
[docs]class Angle(Quantity):
"""
Define an angle with unit. Allowed units are
'mdeg', 'deg', 'urad', 'mrad' and 'rad'.
"""
dims = dict(
mdeg = math.pi / 180 * 1e-3,
deg = math.pi / 180,
urad = 1e-6,
mrad = 1e-3,
rad = 1
)
[docs]class SolidAngle(Quantity):
"""
Define an solid angle with unit. Allowed units are
'sr' and 'msr'.
"""
dims = dict(
msr = 1e-3,
sr = 1
)
[docs]class Length(Quantity):
"""
Define a length with unit. Allowed units are
'pm', 'angstrom', 'nm', 'um', 'mm', 'm' and 'km'.
"""
dims = dict(
pm = 1e-12,
angstrom = 1e-10,
nm = 1e-9,
um = 1e-6,
mm = 1e-3,
m = 1,
km = 1e3
)
[docs]class Energy(Quantity):
"""
Define an energy with unit. Allowed units are
'meV', 'eV', 'KeV', 'MeV', 'GeV', 'mJ', 'J', 'KJ', 'MJ' and 'GJ'.
"""
dims = dict(
meV = 1.6021766208e-19 * 1e-3,
eV = 1.6021766208e-19 * 1,
KeV = 1.6021766208e-19 * 1e3,
MeV = 1.6021766208e-19 * 1e6,
GeV = 1.6021766208e-19 * 1e9,
mJ = 1e-3,
J = 1,
KJ = 1e3,
MJ = 1e6,
GJ = 1e9
)