Example #1
0
 def __init__(self, names, factor, powers, offset=0):
     """
     @param names: a dictionary mapping each name component to its
                   associated integer power (e.g. C{{'m': 1, 's': -1}})
                   for M{m/s}). As a shorthand, a string may be passed
                   which is assigned an implicit power 1.
     @type names: C{dict} or C{str}
     @param factor: a scaling factor
     @type factor: C{float}
     @param powers: the integer powers for each of the nine base units
     @type powers: C{list} of C{int}
     @param offset: an additive offset to the base unit (used only for
                    temperatures)
     @type offset: C{float}
     """
     if type(names) == type(''):
         self.names = NumberDict()
         self.names[names] = 1
         pass
     else:
         self.names = names
         pass
     self.factor = factor
     self.offset = offset
     self.powers = powers
Example #2
0
 def __pow__(self, other):
     if self.offset != 0:
         raise TypeError("cannot exponentiate units with non-zero offset")
     if isinstance(other, int):
         return PhysicalUnit(other * self.names, pow(self.factor, other),
                             map(lambda x, p=other: x * p, self.powers))
     if isinstance(other, float):
         inv_exp = 1. / other
         rounded = int(N.floor(inv_exp + 0.5))
         if abs(inv_exp - rounded) < 1.e-10:
             if reduce(lambda a, b: a and b,
                       map(lambda x, e=rounded: x % e == 0, self.powers)):
                 f = pow(self.factor, other)
                 p = map(lambda x, p=rounded: x / p, self.powers)
                 if reduce(
                         lambda a, b: a and b,
                         map(lambda x, e=rounded: x % e == 0,
                             self.names.values())):
                     names = self.names / rounded
                 else:
                     names = NumberDict()
                     if f != 1.:
                         names[str(f)] = 1
                     for i in range(len(p)):
                         names[_base_names[i]] = p[i]
                 return PhysicalUnit(names, f, p)
             else:
                 raise TypeError('Illegal exponent')
     raise TypeError('Only integer and inverse integer exponents allowed')
Example #3
0
 def __init__(self, names, factor, powers, offset=0):
     """
     @param names: a dictionary mapping each name component to its
                   associated integer power (e.g. C{{'m': 1, 's': -1}})
                   for M{m/s}). As a shorthand, a string may be passed
                   which is assigned an implicit power 1.
     @type names: C{dict} or C{str}
     @param factor: a scaling factor
     @type factor: C{float}
     @param powers: the integer powers for each of the nine base units
     @type powers: C{list} of C{int}
     @param offset: an additive offset to the base unit (used only for
                    temperatures)
     @type offset: C{float}
     """
     if type(names) == type(""):
         self.names = NumberDict()
         self.names[names] = 1
         pass
     else:
         self.names = names
         pass
     self.factor = factor
     self.offset = offset
     self.powers = powers
    def __init__(self, names, factor, powers, offset=0):
	if type(names) == type(''):
	    self.names = NumberDict()
	    self.names[names] = 1
	else:
	    self.names = names
	self.factor = factor
	self.offset = offset
	self.powers = powers
Example #5
0
 def __init__(self, names, factor, powers, offset=0, logbase=None, reference=None):
     if type(names) == type(''):
         self.names = NumberDict()
         self.names[names] = 1
     else:
         self.names = names
     self.factor = factor
     self.offset = offset
     self.powers = powers
     self.logbase = logbase
     self.reference = reference # logarithmic unit base reference
Example #6
0
 def setName(self, name):
     self.names = NumberDict()
     self.names[name] = 1
Example #7
0
class PhysicalUnit:
    """
    Physical unit

    A physical unit is defined by a name (possibly composite), a scaling
    factor, and the exponentials of each of the SI base units that enter into
    it. Units can be multiplied, divided, and raised to integer powers.
    """
    def __init__(self, names, factor, powers, offset=0):
        """
        @param names: a dictionary mapping each name component to its
                      associated integer power (e.g. C{{'m': 1, 's': -1}})
                      for M{m/s}). As a shorthand, a string may be passed
                      which is assigned an implicit power 1.
        @type names: C{dict} or C{str}
        @param factor: a scaling factor
        @type factor: C{float}
        @param powers: the integer powers for each of the nine base units
        @type powers: C{list} of C{int}
        @param offset: an additive offset to the base unit (used only for
                       temperatures)
        @type offset: C{float}
        """
        if type(names) == type(''):
            self.names = NumberDict()
            self.names[names] = 1
        else:
            self.names = names
        self.factor = factor
        self.offset = offset
        self.powers = powers

    def __repr__(self):
        return '<PhysicalUnit ' + self.name() + '>'

    __str__ = __repr__

    def __cmp__(self, other):
        if self.powers != other.powers:
            raise TypeError('Incompatible units')
        return cmp(self.factor, other.factor)

    def __mul__(self, other):
        if self.offset != 0 or (isPhysicalUnit(other) and other.offset != 0):
            raise TypeError("cannot multiply units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(
                self.names + other.names, self.factor * other.factor,
                map(lambda a, b: a + b, self.powers, other.powers))
        else:
            return PhysicalUnit(self.names + {str(other): 1},
                                self.factor * other, self.powers,
                                self.offset * other)

    __rmul__ = __mul__

    def __div__(self, other):
        if self.offset != 0 or (isPhysicalUnit(other) and other.offset != 0):
            raise TypeError("cannot divide units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(
                self.names - other.names, self.factor / other.factor,
                map(lambda a, b: a - b, self.powers, other.powers))
        else:
            return PhysicalUnit(self.names + {str(other): -1},
                                self.factor / other, self.powers)

    def __rdiv__(self, other):
        if self.offset != 0 or (isPhysicalUnit(other) and other.offset != 0):
            raise TypeError("cannot divide units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(
                other.names - self.names, other.factor / self.factor,
                map(lambda a, b: a - b, other.powers, self.powers))
        else:
            return PhysicalUnit({str(other): 1} - self.names,
                                other / self.factor,
                                map(lambda x: -x, self.powers))

    def __pow__(self, other):
        if self.offset != 0:
            raise TypeError("cannot exponentiate units with non-zero offset")
        if isinstance(other, int):
            return PhysicalUnit(other * self.names, pow(self.factor, other),
                                map(lambda x, p=other: x * p, self.powers))
        if isinstance(other, float):
            inv_exp = 1. / other
            rounded = int(N.floor(inv_exp + 0.5))
            if abs(inv_exp - rounded) < 1.e-10:
                if reduce(lambda a, b: a and b,
                          map(lambda x, e=rounded: x % e == 0, self.powers)):
                    f = pow(self.factor, other)
                    p = map(lambda x, p=rounded: x / p, self.powers)
                    if reduce(
                            lambda a, b: a and b,
                            map(lambda x, e=rounded: x % e == 0,
                                self.names.values())):
                        names = self.names / rounded
                    else:
                        names = NumberDict()
                        if f != 1.:
                            names[str(f)] = 1
                        for i in range(len(p)):
                            names[_base_names[i]] = p[i]
                    return PhysicalUnit(names, f, p)
                else:
                    raise TypeError('Illegal exponent')
        raise TypeError('Only integer and inverse integer exponents allowed')

    def conversionFactorTo(self, other):
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: the conversion factor from this unit to another unit
        @rtype: C{float}
        @raises TypeError: if the units are not compatible
        """
        if self.powers != other.powers:
            raise TypeError('Incompatible units')
        if self.offset != other.offset and self.factor != other.factor:
            raise TypeError(('Unit conversion (%s to %s) cannot be expressed ' +
                             'as a simple multiplicative factor') % \
                             (self.name(), other.name()))
        return self.factor / other.factor

    def conversionTupleTo(self, other):  # added 1998/09/29 GPW
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: the conversion factor and offset from this unit to
                  another unit
        @rtype: (C{float}, C{float})
        @raises TypeError: if the units are not compatible
        """
        if self.powers != other.powers:
            raise TypeError('Incompatible units')

        # let (s1,d1) be the conversion tuple from 'self' to base units
        #   (ie. (x+d1)*s1 converts a value x from 'self' to base units,
        #   and (x/s1)-d1 converts x from base to 'self' units)
        # and (s2,d2) be the conversion tuple from 'other' to base units
        # then we want to compute the conversion tuple (S,D) from
        #   'self' to 'other' such that (x+D)*S converts x from 'self'
        #   units to 'other' units
        # the formula to convert x from 'self' to 'other' units via the
        #   base units is (by definition of the conversion tuples):
        #     ( ((x+d1)*s1) / s2 ) - d2
        #   = ( (x+d1) * s1/s2) - d2
        #   = ( (x+d1) * s1/s2 ) - (d2*s2/s1) * s1/s2
        #   = ( (x+d1) - (d1*s2/s1) ) * s1/s2
        #   = (x + d1 - d2*s2/s1) * s1/s2
        # thus, D = d1 - d2*s2/s1 and S = s1/s2
        factor = self.factor / other.factor
        offset = self.offset - (other.offset * other.factor / self.factor)
        return (factor, offset)

    def isCompatible(self, other):  # added 1998/10/01 GPW
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: C{True} if the units are compatible, i.e. if the powers of
                  the base units are the same
        @rtype: C{bool}
        """
        return self.powers == other.powers

    def isDimensionless(self):
        return not reduce(lambda a, b: a or b, self.powers)

    def isAngle(self):
        return self.powers[7] == 1 and \
               reduce(lambda a,b: a + b, self.powers) == 1

    def setName(self, name):
        self.names = NumberDict()
        self.names[name] = 1

    def name(self):
        num = ''
        denom = ''
        for unit in self.names.keys():
            power = self.names[unit]
            if power < 0:
                denom = denom + '/' + unit
                if power < -1:
                    denom = denom + '**' + str(-power)
            elif power > 0:
                num = num + '*' + unit
                if power > 1:
                    num = num + '**' + str(power)
        if len(num) == 0:
            num = '1'
        else:
            num = num[1:]
        return num + denom
Example #8
0
 def setName(self, name):
     self.names = NumberDict()
     self.names[name] = 1
Example #9
0
class PhysicalUnit(object):
    __slots__ = [ "names", "factor", "offset", "powers", "logbase", "reference"]

    def __init__(self, names, factor, powers, offset=0, logbase=None, reference=None):
        if type(names) == type(''):
            self.names = NumberDict()
            self.names[names] = 1
        else:
            self.names = names
        self.factor = factor
        self.offset = offset
        self.powers = powers
        self.logbase = logbase
        self.reference = reference # logarithmic unit base reference

    def __repr__(self):
        return '<PhysicalUnit ' + self.name() + '>'
    __str__ = __repr__

    def __cmp__(self, other):
        if self.powers != other.powers:
            raise TypeError, 'Incompatible units'
        return cmp(self.factor, other.factor)

    def __mul__(self, other):
        if self.offset != 0 or (isPhysicalUnit(other) and other.offset != 0):
            raise TypeError, "cannot multiply units with non-zero offset"
        if self.logbase is not None or (isPhysicalUnit(other) and other.logbase is not None):
            raise TypeError, "cannot multiply logarithmic units"
        if isPhysicalUnit(other):
            return PhysicalUnit(self.names+other.names,
                                self.factor*other.factor,
                                map(lambda a,b: a+b,
                                    self.powers, other.powers))
        else:
            return PhysicalUnit(self.names+{str(other): 1},
                                self.factor*other,
                                self.powers,
                                self.offset * other)

    __rmul__ = __mul__

    def __div__(self, other):
        if self.offset != 0 or (isPhysicalUnit (other) and other.offset != 0):
            raise TypeError, "cannot divide units with non-zero offset"
        if self.logbase is not None or (isPhysicalUnit(other) and other.logbase is not None):
            raise TypeError, "cannot divide logarithmic units"
        if isPhysicalUnit(other):
            return PhysicalUnit(self.names-other.names,
                                self.factor/other.factor,
                                map(lambda a,b: a-b,
                                    self.powers, other.powers))
        else:
            return PhysicalUnit(self.names+{str(other): -1},
                                self.factor/other, self.powers)

    def __rdiv__(self, other):
        if self.offset != 0 or (isPhysicalUnit (other) and other.offset != 0):
            raise TypeError, "cannot divide units with non-zero offset"
        if self.logbase is not None or (isPhysicalUnit (other) and other.logbase is not None):
            raise TypeError, "cannot divide logarithmic units"
        if isPhysicalUnit(other):
            return PhysicalUnit(other.names-self.names,
                                other.factor/self.factor,
                                map(lambda a,b: a-b,
                                    other.powers, self.powers))
        else:
            return PhysicalUnit({str(other): 1}-self.names,
                                other/self.factor,
                                map(lambda x: -x, self.powers))

    def __pow__(self, other):
        if self.offset != 0:
            raise TypeError, "cannot exponentiate units with non-zero offset"
        if self.logbase is not None:
            raise TypeError, "cannot exponentiate logarithmic units"
        if type(other) == type(0):
            return PhysicalUnit(other*self.names, pow(self.factor, other),
                                map(lambda x,p=other: x*p, self.powers))
        if type(other) == type(0.):
            inv_exp = 1./other
            rounded = int(umath.floor(inv_exp+0.5))
            if abs(inv_exp-rounded) < 1.e-10:
                if reduce(lambda a, b: a and b,
                          map(lambda x, e=rounded: x%e == 0, self.powers)):
                    f = pow(self.factor, other)
                    p = map(lambda x,p=rounded: x/p, self.powers)
                    if reduce(lambda a, b: a and b,
                              map(lambda x, e=rounded: x%e == 0,
                                  self.names.values())):
                        names = self.names/rounded
                    else:
                        names = NumberDict()
                        if f != 1.:
                            names[str(f)] = 1
                        for i in range(len(p)):
                            names[_base_names[i]] = p[i]
                    return PhysicalUnit(names, f, p)
                else:
                    raise TypeError, 'Illegal exponent'
        raise TypeError, 'Only integer and inverse integer exponents allowed'

    def conversionFactorTo(self, other):
        if self.powers != other.powers:
            raise TypeError, 'Incompatible units'
        if self.offset != other.offset and self.factor != other.factor:
            raise TypeError, \
                  ('Unit conversion (%s to %s) cannot be expressed ' +
                   'as a simple multiplicative factor') % \
                  (self.name(), other.name())
#        if self.logbase is not None: # logarithmic units are assumed to be dimensionless
#            raise TypeError, "Cannot convert dimensionless values."
        return self.factor/other.factor

    def conversionTupleTo(self, other): # added 1998/09/29 GPW
        if self.powers != other.powers:
            raise TypeError, 'Incompatible units'
        if self.logbase != other.logbase:
            raise TypeError, "Cannot convert dimensionless values with different bases."

        # let (s1,d1) be the conversion tuple from 'self' to base units
        #   (ie. (x+d1)*s1 converts a value x from 'self' to base units,
        #   and (x/s1)-d1 converts x from base to 'self' units)
        # and (s2,d2) be the conversion tuple from 'other' to base units
        # then we want to compute the conversion tuple (S,D) from
        #   'self' to 'other' such that (x+D)*S converts x from 'self'
        #   units to 'other' units
        # the formula to convert x from 'self' to 'other' units via the
        #   base units is (by definition of the conversion tuples):
        #     ( ((x+d1)*s1) / s2 ) - d2
        #   = ( (x+d1) * s1/s2) - d2
        #   = ( (x+d1) * s1/s2 ) - (d2*s2/s1) * s1/s2
        #   = ( (x+d1) - (d1*s2/s1) ) * s1/s2
        #   = (x + d1 - d2*s2/s1) * s1/s2
        # thus, D = d1 - d2*s2/s1 and S = s1/s2
        factor = self.factor / other.factor
        offset = self.offset - (other.offset * other.factor / self.factor)
        return (factor, offset)

    def isCompatible(self, other):     # added 1998/10/01 GPW
        return self.powers == other.powers

    def isDimensionless(self):
        return not reduce(lambda a,b: a or b, self.powers)

    def isAngle(self):
        return self.powers[7] == 1 and \
               reduce(lambda a,b: a + b, self.powers) == 1

    def isLogarithmic(self):
        return self.logbase is not None

    def setName(self, name):
        self.names = NumberDict()
        self.names[name] = 1

    def name(self):
        num = ''
        denom = ''
        for unit in self.names.keys():
            power = self.names[unit]
            if power < 0:
                denom = denom + '/' + unit
                if power < -1:
                    denom = denom + '**' + str(-power)
            elif power > 0:
                num = num + '*' + unit
                if power > 1:
                    num = num + '**' + str(power)
        if len(num) == 0:
            num = '1'
        else:
            num = num[1:]
        return num + denom
class PhysicalUnit:

    """
    Physical unit

    A physical unit is defined by a name (possibly composite), a scaling
    factor, and the exponentials of each of the SI base units that enter into
    it. Units can be multiplied, divided, and raised to integer powers.
    """
    
    def __init__(self, names, factor, powers, offset=0):
        """
        @param names: a dictionary mapping each name component to its
                      associated integer power (e.g. C{{'m': 1, 's': -1}})
                      for M{m/s}). As a shorthand, a string may be passed
                      which is assigned an implicit power 1.
        @type names: C{dict} or C{str}
        @param factor: a scaling factor
        @type factor: C{float}
        @param powers: the integer powers for each of the nine base units
        @type powers: C{list} of C{int}
        @param offset: an additive offset to the base unit (used only for
                       temperatures)
        @type offset: C{float}
        """
        if type(names) == type(''):
            self.names = NumberDict()
            self.names[names] = 1
        else:
            self.names = names
        self.factor = factor
        self.offset = offset
        self.powers = powers

    def __repr__(self):
        return '<PhysicalUnit ' + self.name() + '>'

    __str__ = __repr__

    def __cmp__(self, other):
        if self.powers != other.powers:
            raise TypeError('Incompatible units')
        return cmp(self.factor, other.factor)

    def __mul__(self, other):
        if self.offset != 0 or (isPhysicalUnit (other) and other.offset != 0):
            raise TypeError("cannot multiply units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(self.names+other.names,
                                self.factor*other.factor,
                                map(lambda a,b: a+b,
                                    self.powers, other.powers))
        else:
            return PhysicalUnit(self.names+{str(other): 1},
                                self.factor*other,
                                self.powers,
                                self.offset * other)

    __rmul__ = __mul__

    def __div__(self, other):
        if self.offset != 0 or (isPhysicalUnit (other) and other.offset != 0):
            raise TypeError("cannot divide units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(self.names-other.names,
                                self.factor/other.factor,
                                map(lambda a,b: a-b,
                                    self.powers, other.powers))
        else:
            return PhysicalUnit(self.names+{str(other): -1},
                                self.factor/other, self.powers)

    def __rdiv__(self, other):
        if self.offset != 0 or (isPhysicalUnit (other) and other.offset != 0):
            raise TypeError("cannot divide units with non-zero offset")
        if isPhysicalUnit(other):
            return PhysicalUnit(other.names-self.names,
                                other.factor/self.factor,
                                map(lambda a,b: a-b,
                                    other.powers, self.powers))
        else:
            return PhysicalUnit({str(other): 1}-self.names,
                                other/self.factor,
                                map(lambda x: -x, self.powers))

    def __pow__(self, other):
        if self.offset != 0:
            raise TypeError("cannot exponentiate units with non-zero offset")
        if isinstance(other, int):
            return PhysicalUnit(other*self.names, pow(self.factor, other),
                                map(lambda x,p=other: x*p, self.powers))
        if isinstance(other, float):
            inv_exp = 1./other
            rounded = int(N.floor(inv_exp+0.5))
            if abs(inv_exp-rounded) < 1.e-10:
                if reduce(lambda a, b: a and b,
                          map(lambda x, e=rounded: x%e == 0, self.powers)):
                    f = pow(self.factor, other)
                    p = map(lambda x,p=rounded: x/p, self.powers)
                    if reduce(lambda a, b: a and b,
                              map(lambda x, e=rounded: x%e == 0,
                                  self.names.values())):
                        names = self.names/rounded
                    else:
                        names = NumberDict()
                        if f != 1.:
                            names[str(f)] = 1
                        for i in range(len(p)):
                            names[_base_names[i]] = p[i]
                    return PhysicalUnit(names, f, p)
                else:
                    raise TypeError('Illegal exponent')
        raise TypeError('Only integer and inverse integer exponents allowed')

    def conversionFactorTo(self, other):
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: the conversion factor from this unit to another unit
        @rtype: C{float}
        @raises TypeError: if the units are not compatible
        """
        if self.powers != other.powers:
            raise TypeError('Incompatible units')
        if self.offset != other.offset and self.factor != other.factor:
            raise TypeError(('Unit conversion (%s to %s) cannot be expressed ' +
                             'as a simple multiplicative factor') % \
                             (self.name(), other.name()))
        return self.factor/other.factor

    def conversionTupleTo(self, other): # added 1998/09/29 GPW
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: the conversion factor and offset from this unit to
                  another unit
        @rtype: (C{float}, C{float})
        @raises TypeError: if the units are not compatible
        """
        if self.powers != other.powers:
            raise TypeError('Incompatible units')

        # let (s1,d1) be the conversion tuple from 'self' to base units
        #   (ie. (x+d1)*s1 converts a value x from 'self' to base units,
        #   and (x/s1)-d1 converts x from base to 'self' units)
        # and (s2,d2) be the conversion tuple from 'other' to base units
        # then we want to compute the conversion tuple (S,D) from
        #   'self' to 'other' such that (x+D)*S converts x from 'self'
        #   units to 'other' units
        # the formula to convert x from 'self' to 'other' units via the
        #   base units is (by definition of the conversion tuples):
        #     ( ((x+d1)*s1) / s2 ) - d2
        #   = ( (x+d1) * s1/s2) - d2
        #   = ( (x+d1) * s1/s2 ) - (d2*s2/s1) * s1/s2
        #   = ( (x+d1) - (d1*s2/s1) ) * s1/s2
        #   = (x + d1 - d2*s2/s1) * s1/s2
        # thus, D = d1 - d2*s2/s1 and S = s1/s2
        factor = self.factor / other.factor
        offset = self.offset - (other.offset * other.factor / self.factor)
        return (factor, offset)

    def isCompatible (self, other):     # added 1998/10/01 GPW
        """
        @param other: another unit
        @type other: L{PhysicalUnit}
        @returns: C{True} if the units are compatible, i.e. if the powers of
                  the base units are the same
        @rtype: C{bool}
        """
        return self.powers == other.powers

    def isDimensionless(self):
        return not reduce(lambda a,b: a or b, self.powers)

    def isAngle(self):
        return self.powers[7] == 1 and \
               reduce(lambda a,b: a + b, self.powers) == 1

    def setName(self, name):
        self.names = NumberDict()
        self.names[name] = 1

    def name(self):
        num = ''
        denom = ''
        for unit in self.names.keys():
            power = self.names[unit]
            if power < 0:
                denom = denom + '/' + unit
                if power < -1:
                    denom = denom + '**' + str(-power)
            elif power > 0:
                num = num + '*' + unit
                if power > 1:
                    num = num + '**' + str(power)
        if len(num) == 0:
            num = '1'
        else:
            num = num[1:]
        return num + denom