[docs]classQuantity(AtomicExpr):""" Physical quantity: can be a unit of measure, a constant or a generic quantity. """is_commutative=Trueis_real=Trueis_number=Falseis_nonzero=True_diff_wrt=Truedef__new__(cls,name,dimension,scale_factor=S.One,abbrev=None,dim_sys=dimsys_default,**assumptions):ifnotisinstance(name,Symbol):name=Symbol(name)ifnotisinstance(dim_sys,DimensionSystem):raiseTypeError("%s is not a DimensionSystem"%dim_sys)ifnotisinstance(dimension,dimensions.Dimension):ifdimension==1:dimension=Dimension(1)else:raiseValueError("expected dimension or 1")else:fordim_symindimension.name.atoms(Dimension):ifdim_symnotin[i.nameforiindim_sys._dimensional_dependencies]:raiseValueError("Dimension %s is not registered in the ""dimensional dependency tree."%dim_sym)scale_factor=sympify(scale_factor)dimex=Quantity.get_dimensional_expr(scale_factor)ifdimex!=1:ifnotdim_sys.equivalent_dims(dimension,Dimension(dimex)):raiseValueError("quantity value and dimension mismatch")# replace all prefixes by their ratio to canonical units:scale_factor=scale_factor.replace(lambdax:isinstance(x,Prefix),lambdax:x.scale_factor)# replace all quantities by their ratio to canonical units:scale_factor=scale_factor.replace(lambdax:isinstance(x,Quantity),lambdax:x.scale_factor)ifabbrevisNone:abbrev=nameelifisinstance(abbrev,string_types):abbrev=Symbol(abbrev)obj=AtomicExpr.__new__(cls,name,dimension,scale_factor,abbrev)obj._name=nameobj._dimension=dimensionobj._scale_factor=scale_factorobj._dim_sys=dim_sysobj._abbrev=abbrevreturnobj@propertydefname(self):returnself._name@propertydefdimension(self):returnself._dimension@propertydefdim_sys(self):returnself._dim_sys@propertydefabbrev(self):""" Symbol representing the unit name. Prepend the abbreviation with the prefix symbol if it is defines. """returnself._abbrev@propertydefscale_factor(self):""" Overall magnitude of the quantity as compared to the canonical units. """returnself._scale_factordef_eval_is_positive(self):returnself.scale_factor.is_positivedef_eval_is_constant(self):returnself.scale_factor.is_constant()def_eval_Abs(self):# FIXME prefer usage of self.__class__ or type(self) insteadreturnself.func(self.name,self.dimension,Abs(self.scale_factor),self.abbrev,self.dim_sys)@staticmethoddefget_dimensional_expr(expr):ifisinstance(expr,Mul):returnMul(*[Quantity.get_dimensional_expr(i)foriinexpr.args])elifisinstance(expr,Pow):returnQuantity.get_dimensional_expr(expr.base)**expr.expelifisinstance(expr,Add):returnQuantity.get_dimensional_expr(expr.args[0])elifisinstance(expr,Derivative):dim=Quantity.get_dimensional_expr(expr.args[0])forindependentinexpr.args[1:]:dim/=Quantity.get_dimensional_expr(independent)returndimelifisinstance(expr,Function):args=[Quantity.get_dimensional_expr(arg)forarginexpr.args]ifall(i==1foriinargs):returnS.Onereturnexpr.func(*args)elifisinstance(expr,Quantity):returnexpr.dimension.namereturnS.One@staticmethoddef_collect_factor_and_dimension(expr):"""Return tuple with factor expression and dimension expression."""ifisinstance(expr,Quantity):returnexpr.scale_factor,expr.dimensionelifisinstance(expr,Mul):factor=1dimension=Dimension(1)forarginexpr.args:arg_factor,arg_dim=Quantity._collect_factor_and_dimension(arg)factor*=arg_factordimension*=arg_dimreturnfactor,dimensionelifisinstance(expr,Pow):factor,dim=Quantity._collect_factor_and_dimension(expr.base)exp_factor,exp_dim=Quantity._collect_factor_and_dimension(expr.exp)ifexp_dim.is_dimensionless:exp_dim=1returnfactor**exp_factor,dim**(exp_factor*exp_dim)elifisinstance(expr,Add):factor,dim=Quantity._collect_factor_and_dimension(expr.args[0])foraddendinexpr.args[1:]:addend_factor,addend_dim= \
Quantity._collect_factor_and_dimension(addend)ifdim!=addend_dim:raiseValueError('Dimension of "{0}" is {1}, ''but it should be {2}'.format(addend,addend_dim.name,dim.name))factor+=addend_factorreturnfactor,dimelifisinstance(expr,Derivative):factor,dim=Quantity._collect_factor_and_dimension(expr.args[0])forindependentinexpr.args[1:]:ifactor,idim=Quantity._collect_factor_and_dimension(independent)factor/=ifactordim/=idimreturnfactor,dimelifisinstance(expr,Function):fds=[Quantity._collect_factor_and_dimension(arg)forarginexpr.args]return(expr.func(*(f[0]forfinfds)),expr.func(*(d[1]fordinfds)))elifisinstance(expr,Dimension):return1,exprelse:returnexpr,Dimension(1)

def_Quantity_constructor_postprocessor_Add(expr):# Construction postprocessor for the addition,# checks for dimension mismatches of the addends, thus preventing# expressions like `meter + second` to be created.deset={tuple(sorted(dimsys_default.get_dimensional_dependencies(Dimension(Quantity.get_dimensional_expr(i)ifnoti.is_numberelse1)).items()))foriinexpr.argsifi.free_symbols==set()# do not raise if there are symbols# (free symbols could contain the units corrections)}# If `deset` has more than one element, then some dimensions do not# match in the sum:iflen(deset)>1:raiseValueError("summation of quantities of incompatible dimensions")returnexprBasic._constructor_postprocessor_mapping[Quantity]={"Add":[_Quantity_constructor_postprocessor_Add],}