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 __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 __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
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 setName(self, name): self.names = NumberDict() self.names[name] = 1
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
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