# Unit conversion

I'm not certain if the behaviour is correct in this example, or I am possibly misunderstanding something. Sorry about the lengthy example, but I had difficulties simplifying it into a simpler one.

# Setup units
lbf  = units.force.pound_force;
inch = units.length.inch;
sec  = units.time.second;
psi  = lbf / inch^2;
gal  = 231.0*inch^3;
min  = 60.0*sec;
gpm  = gal/min;
hole = gpm/sqrt(psi);

# Define the given constraints
FLext = 20000.0*lbf;
vext  = 15.0*inch/sec;
FLret = -10000.0*lbf;
vret  = -15.0*inch/sec;

# Assumed values
Ps = 3000.0*psi;
pv = 1.0;
pc = 1.4;

# System of 3 equations, 3 unknowns
ABE = var('A_BE');
ARE = var('A_RE');
kv = var('k_v');
eq1 = vext^2 == (Ps*ABE - FLext) * kv^2 / ABE^3 / (1 + pv^2 * pc^(-3));
eq2 = vret^2 == (FLret + Ps*ARE) * kv^2 / ARE^3 / (1 + pv^2 * pc^3);
eq3 = pc == ABE / ARE;
sol = solve([eq1, eq2, eq3], [ABE, ARE, kv], solution_dict=True);
sol = sol[1];
show(sol[ABE])
show(sol[ARE])
show(sol[kv])

# Showing the units of various variables
show(sol[kv].convert())
show(hole.convert())
show( (sol[kv]/hole).convert() ) # this should be dimensionless

# Showing the potential bug with unit conversion
show(sol[kv].convert(hole)) # ValueError: Incompatible units


Running the code, the section labelled # Showing the units of various variables, you can see the various units of the relevant variables. The code for show( (sol[kv]/hole).convert() ) should return a dimensionless value, however it still shows units in the result. Looking at the non-numeric portions of it, everything appears to cancel. Running show(sol[kv].convert(hole)) results in ValueError: Incompatible units. I expected a conversion into gpm/sqrt(psi) (i.e. holes).

What concerns me is my calculator (TI-92+) has conversion difficulties with this unit as well, being unable to convert. Is this the expected/normal/ideal behaviour for sage? If so, for what reason(s)? If not, what should I do?

Thank you,

menturi

edit retag close merge delete

Sort by » oldest newest most voted

It seems that the units end up in sqrt() expressions. Even if you have something like sqrt(second^2), Sage does not simplify this automatically. This is a bug caused by the fact that second is a symbolic variable and unless you explicitly state the domain for a variable it is assumed to be complex. For a complex variable x, we can't say that sqrt(x^2) = x. But if x > 0, we can:

sage: var('y', domain='positive')
y
sage: sqrt(y^2)
y
sage: sqrt(x^2)
sqrt(x^2)


There is also the fact that sqrt() only performs trivial simplifications automatically. This is not simplified for instance:

sage: sqrt(y^2/(x+1))
sqrt(y^2/(x + 1))


Because of another bug, even though we already declared y > 0, we need to state this to Maxima before we can simplify further:

sage: t = sqrt(y^2/(x+1))
abs(y)/sqrt(x + 1)
sage: assume(y > 0)
y/sqrt(x + 1)


You might be able to work around your problems by combining some of the tricks above. It would be great if someone files a ticket to declare units as positive.

more

For now, I find it more useful to define my own units instead of using the build-in Sage units. For example, I would rewrite this code as follows:

# Setup units
#    Define base units (kilogram, meter, second) and secondary units (lbf, inch, sec)
kilogram  = var('kilogram', domain='positive'); assume(kilogram,'real');
meter = var('meter', domain='positive'); assume(meter,'real');
second  = var('second', domain='positive'); assume(second,'real');
lbf  = 4.44822162*kilogram*meter/second^2
inch = 0.0254*meter
sec  = 1*second;
psi  = lbf / inch^2;
gal  = 231.0*inch^3;
min  = 60.0*sec;
gpm  = gal/min;
hole = gpm/sqrt(psi);

# Define the given constraints
FLext = 20000.0*lbf;
vext  = 15.0*inch/sec;
FLret = -10000.0*lbf;
vret  = -15.0*inch/sec;

# Assumed values
Ps = 3000.0*psi;
pv = 1.0;
pc = 1.4;

# System of 3 equations, 3 unknowns
ABE = var('A_BE');
ARE = var('A_RE');
kv = var('k_v');
eq1 = vext^2 == (Ps*ABE - FLext) * kv^2 / ABE^3 / (1 + pv^2 * pc^(-3));
eq2 = vret^2 == (FLret + Ps*ARE) * kv^2 / ARE^3 / (1 + pv^2 * pc^3);
eq3 = pc == ABE / ARE;
soln = solve([eq1, eq2, eq3], [ABE, ARE, kv], solution_dict=True);
sol = soln[1];
show(sol[ABE])
show(sol[ARE])
show(sol[kv])

# Showing the units of various variables
#     Use .full_simplify()._convert(RR) to provide simplified results
show(sol[kv].full_simplify()._convert(RR))
show(hole.full_simplify()._convert(RR))
show((sol[kv]/hole).full_simplify()._convert(RR) ) # this should be dimensionless

# This final result is now dimensionless, as desired.


I am still playing around, but the main idea is define one's own units (as shown at the beginning of the above example) using "domain" and "assume" to ensure they are positive real. Methods such as ".full_simplify()" and "._convert(RR)" can then be used to produce simple numerical results with all the units correctly cancelled out.

more