Automatic reduction and cancellation of units with SymPy

Question:

I am having issues with getting units to cleanly simplify. The program I am writing is involved with the calculation of spring properties, requiring 10/11 user controlled variables and about a dozen equations, all of which deal with mixed sets of units. The one missing variable can be any of the non-material properties (1/5), so I am trying to use symbolic equations that can solve for whatever my one missing variable is. I tried setting the variables with pint, which could reduce properly and did not have this problem, but they could not Sympify properly, so I had to switch back to SymPy’s unit system.

Here is some code that demonstrates the problem:

from sympy.physics.units import *
from sympy import pi, sqrt, N, Eq, symbols, solve

lbf=Quantity('lbf', abbrev='lbf')
lbf.set_global_relative_scale_factor((convert_to(pound*acceleration_due_to_gravity,newton))/newton, newton)

F, Y, L, A, m, ns, xi, d = symbols('F Y L A m ns xi d')
ssy, alpha, beta, C = symbols('ssy alpha beta C')

F= 20*lbf
Y= 2*inch
L= 3.25*inch
d= .08*inch

m= .145
A= 201.00
A*=kilo*psi*inch**m
ns= 1.20
xi= .15

eqSsy=Eq(ssy,.45*A/(d**m))
ssy=solve(eqSsy)[0]
eqAlpha=Eq(alpha,eqSsy.rhs/ns)
alpha=solve(eqAlpha)[0]
eqBeta=Eq(beta,(8*(1+xi)*F)/(N(pi)*(d**2)))
beta=solve(eqBeta)[0]
eqC=Eq(C,((2*eqAlpha.rhs-eqBeta.rhs)/(4*eqBeta.rhs))+sqrt((((2*eqAlpha.rhs-eqBeta.rhs)/(4*eqBeta.rhs))**2)-(3*eqAlpha.rhs)/(4*eqBeta.rhs)))
C=solve(eqC)[0]
print(ssy, 'n', alpha, 'n', beta, 'n', C)

This issue is not related to the lbf unit I had to create, it still happened when I had it using the raw units before I cleaned it up. This leads to C coming out as 1.62e-28*(3.66645442100299e+28*inch**2*psi - 1.54320987654321e+27*lbf + 3.666454421003e+28*sqrt(-0.252539870841386*inch**2*lbf*psi + (inch**2*psi - 0.0420899784735643*lbf)**2))/lbf instead of 10.5334875999498, because none of the units are cancelled through the calculation process.

The "fix" to this problem that I want to avoid is changing line 27, the creation of eqBeta, I have to hard convert the output units to be in psi to prevent the units from coming out as lbf/inch**2 instead of the pressure unit.

eqBeta=Eq(beta,convert_to((8*(1+xi)*F)/(N(pi)*(d**2)),psi))

Is there any way I can make beta automatically reduce to the appropriate pressure unit? The input values are given through a PyQt5 program, not like in this demo, and they can be given either imperial or metric units, so I don’t want to be manually forcing a conversion into psi (or forcibly converting C into being unitless).

I would also appreciate if someone knew of a cleaner or better way to do these calculations, as I have just been bashing my head against SymPy because I haven’t found another solution. These equations and variable names are taken from a machine design textbook, and I don’t want to have to manually create a step-by-step solving process for each possible missing variable.

Asked By: UnsweetIceTea

||

Answers:

You might try listing the base units as the quantities to be reduced to:

>>> convert_to(C, [kg, meter, second]).n(2)
11.0

Or if you don’t know which ones to use,

>>> from sympy.physics.units import UnitSystem
>>> sibase = UnitSystem.get_unit_system("SI")._base_units
>>> convert_to(C, sibase).n(2)
11.0

cf this issue

Answered By: smichr