Exemple #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
     else:
         self.names = names
     self.factor = factor
     self.offset = offset
     self.powers = powers
Exemple #2
0
 def setName(self, name):
     self.names = NumberDict()
     self.names[name] = 1
Exemple #3
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)

    __truediv__ = __div__

    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(numpy.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