# Commits

committed 29ff76b

Preliminary support for the "uncertainties" package. May not work in all cases yet.

# physics.py

import numpy as np

+# allow uncertain values if the "uncertainties" package is available
+try:
+    from uncertainties import ufloat, Variable, AffineScalarFunc
+    import uncertainties.umath as unp
+    uncertain = (Variable, AffineScalarFunc)
+    def valuetype((v, u)):
+        if isinstance(v, uncertain):
+            return v
+        return ufloat((v, u))
+except ImportError:
+    uncertain = ()
+    valuetype = lambda (v, u): v
+    unp = np
+

class UnitError(ValueError):
pass

global_precision = 8

-    _number = re.compile('[+-]?[0-9]+(\\.[0-9]*)?([eE][+-]?[0-9]+)?')
+    _number = re.compile(r'([+-]?[0-9]+(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?)'
+                         r'(?:\s+\+\/-\s+([+-]?[0-9]+(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?))?')

-    def __init__(self, value, unit=None):
+    def __init__(self, value, unit=None, stdev=None):
"""There are two constructor calling patterns:

1. PhysicalQuantity(value, unit), where value is any number and unit is
is provided for more convenient interactive use.
"""
if unit is not None:
-            self.value = value
+            self.value = valuetype((value, stdev or 0))
self.unit = _findUnit(unit)
else:
s = value.strip()
-            match = PhysicalQuantity._number.match(s)
+            match = self._number.match(s)
if match is None:
raise UnitError('No number found in %r' % value)
-            self.value = float(match.group(0))
+            self.value = valuetype((float(match.group(1)),
+                                    float(match.group(2) or 0)))
self.unit = _findUnit(s[match.end(0):])

def __str__(self):
-        return '%.*g %s' % (self.global_precision, self.value,
-                            self.unit.name().replace('**', '^'))
+        prec = self.global_precision
+        unit = self.unit.name().replace('**', '^')
+        if isinstance(self.value, uncertain):
+            stdev = self.value.std_dev()
+            if stdev:
+                return '%.*g +/- %.*g %s' % (prec, self.value.nominal_value,
+                                             prec, stdev, unit)
+            return '%.*g %s' % (prec, self.value.nominal_value, unit)
+        return '%.*g %s' % (prec, self.value, unit)

def __repr__(self):
return self.__str__()
num = ''
denom = ''
for i in xrange(9):
-            unit = _base_names[i]
+            unit = _base_names[i]
power = self.unit.powers[i]
if power < 0:
denom += '/' + unit

def sin(self):
if self.unit.is_angle:
-            return np.sin(self.value *
+            return unp.sin(self.value *
else:
raise UnitError('Argument of sin must be an angle')

def cos(self):
if self.unit.is_angle:
-            return np.cos(self.value *
+            return unp.cos(self.value *
else:
raise UnitError('Argument of cos must be an angle')

def tan(self):
if self.unit.is_angle:
-            return np.tan(self.value *