pint
is not part of Sage. Here you try to substitute some symbolic variables of a symbolic expression by elements of the pint
module. This could only work if you could coerce pint
units into the Symbolic Ring
. The way to do this is to define a coercion between pint
units and the Symbolic Ring, you can have a look at the following docs:
This should be easily doable by using the Sage units
but i guess it is not what you would like to do since you will lose the consistency checking of pint
, for example with Sage units
, you can do:
sage: units.length.meter + units.time.second
meter + second
So perhaps a substitution i not what you would like to do. Perhaps, you should instead create a .physical_consistency()
or .pint()
(or whatever suitable name) method for symbolic elements, that recurses to the tree of the symbolic expression and make the units replacement from your dictionary.
Note that for powers you will experience some problems because the .__pow__()
method is not able to deal with Sage integers:
sage: sage: ureg.meter^2
TypeError: Cannot power UnitsContainer by <type 'sage.rings.integer.Integer'>
EDIT Here is a prototype. The idea is to recursively unfold the symbolic expression and refold it with units:
def physical(expr, dico):
r"""
INPUT:
- expr is a symbolic expression.
- dico is a dictionnary that maps symbols to units (possibly with magnitude)
The units are from the pint package, please do `sage -pip install pint` to let this function work.
OUTPUT:
The resulting unit (with magnitude) or raise an error.
"""
op = expr.operator()
dimensionless = (dico.values()[0] / dico.values()[0])
if op:
if op == operator.add:
return sum([is_physical(i, dico) for i in expr.operands()])
elif op == operator.mul:
return prod([is_physical(i, dico) for i in expr.operands()])
elif op == operator.pow: # todo : improve for dimensionless powers, like for log.
return is_physical(expr.operands()[0], dico) ** float(expr.operands()[1])
elif op in [exp, log, cos, sin]: # to enlarge
if is_physical(expr.operands()[0], dico).dimensionality == dimensionless.dimensionality:
return op(is_physical(expr.operands()[0], dico).magnitude) * dimensionless
else:
raise ValueError('The {} can only eat dimensionless numbers.'.format(op))
else:
raise ValueError('I do not know how to deal with {} operator (yet).'.format(op))
else:
if expr.is_symbol():
if expr in dico:
return dico[expr]
else:
raise ValueError('The symbol {} did not get any unit.'.format(expr))
elif expr.is_numeric():
return float(expr) * dimensionless
elif expr in [e, pi]:
return float(expr) * dimensionless
else:
raise ValueError('I do not know how to deal with {} operand (yet).'.format(expr))
The reason why i did not use expr.is__constant()
, but explicitely list e
and pi
is because things like NaN
are constants, and i am not sure there will not be physical constants (that are not dimensionless). If complex numbers might be involved, it could be better to replace ...
(more)
What are b and c in the Sage session above that you copy paste?
Sorry, forgot the var('a b c') line. I just modified my question accordingly.