A kinematics (SUVAT) equation solver for micropython

I made a Python program for my calculator's micro-python that lets you enter a value for any 3 of these variables:

s - displacement/distance
u - initial velocity
v - final velocity
a - acceleration
t - time


and returns the other two. I know there are many repeated sections, but I want to know how to make it more compact and efficient, and coding tricks.

The stopper() function responds to the string "x" because the x button is very easily accessible on the calculator.

# Original SUVAT equations
# v = u + at
# s = 0.5(u+v)*t
# v^2 = u^2 + 2a*s
# s = ut + 0.5at^2
from math import sqrt, pi

def input_pi_replacer(prompt):
return input(prompt).replace("pi", str(pi))

def stopper():
stop_or_continue = input("Stop?: ")
if stop_or_continue == "x":
raise SystemExit

print("Suvat Calculator:")
print("if variable not\ngiven then type\n'x'\n")

def suvatsolver():

while True:
# this will keep track of how many variables are known, since three are required.
variable_count = 0
[s_known, u_known, v_known, a_known, t_known] = [False, False, False, False, False]

distance = input_pi_replacer("Distance in metres\n:  ")
# this assumes that the input is actually acceleration number.
# then it says the variable is known and adds 1 to the variable count.
if distance != "x":
s_known = True
distance = float(eval(distance))
variable_count += 1

initial_velocity = input_pi_replacer("Initial velocity\nin metres/second\n: ")
if initial_velocity != "x":
u_known = True
initial_velocity = float(eval(initial_velocity))
variable_count += 1

final_velocity = input_pi_replacer("Final velocity\n in metres/second\n: ")
if final_velocity != "x":
v_known = True
final_velocity = float(eval(final_velocity))
variable_count += 1
else:
pass

acceleration = input_pi_replacer("Acceleration\n in metres/second squared\n: ")
if acceleration != "x":
a_known = True
acceleration = float(eval(acceleration))
variable_count += 1
else:
pass

time = input_pi_replacer("Time\nin seconds\n: ")
if time != "x":
t_known = True
time = float(eval(time))
variable_count += 1
else:
pass

if variable_count < 3:
print("not enough variables")
break

# this next bunch of statements pretty much goes through
# all possible combinations of three variables
# and calculates the remaining unknowns (I solved the equations myself)
if s_known and u_known and v_known:
acceleration = (final_velocity ** 2 - initial_velocity ** 2) / (2 * distance)
time = 2 * distance / (initial_velocity + final_velocity)
print("")
print("Acceleration =\n", acceleration)
print("Time =\n", time)

elif s_known and u_known and a_known:
final_velocity = sqrt(initial_velocity ** 2 + 2 * acceleration * distance)
t1 = -initial_velocity / acceleration + sqrt(2 * acceleration * distance + initial_velocity ** 2) / acceleration
t2 = -initial_velocity / acceleration - sqrt(2 * acceleration * distance + initial_velocity ** 2) / acceleration
print("")
print("Final velocity =\n", final_velocity, "\nor ", -final_velocity)
print("Time =\n", t1, "\nor ", t2)

elif s_known and u_known and t_known:
final_velocity = 2 * distance / time - initial_velocity
acceleration = 2 * (distance - initial_velocity * time) / time ** 2
print("")
print("Final velocity =\n", final_velocity)
print("Acceleration =\n", acceleration)

elif s_known and v_known and a_known:
initial_velocity = sqrt(final_velocity ** 2 - 2 * acceleration * distance)
t1 = 2 * distance / (final_velocity + sqrt(final_velocity ** 2 - 2 * acceleration * distance))
t2 = 2 * distance / (final_velocity - sqrt(final_velocity ** 2 - 2 * acceleration * distance))
print("")
print("Initial velocity =\n", initial_velocity, "\nor", -initial_velocity)
print("Time =\n", t1, "\nor", t2)

elif s_known and v_known and t_known:
initial_velocity = 2 * distance / time - final_velocity
acceleration = (final_velocity - initial_velocity) / time
print("")
print("Initial velocity =\n", initial_velocity)
print("Acceleration =\n", acceleration)

elif s_known and a_known and t_known:
initial_velocity = (distance - 0.5 * acceleration * time ** 2) / time
final_velocity = initial_velocity + acceleration * time
print("")
print("Initial velocity =\n", initial_velocity)
print("Final velocity =\n", final_velocity)

elif u_known and v_known and a_known:
distance = (final_velocity ** 2 - initial_velocity ** 2) / (2 * acceleration)
time = (final_velocity - initial_velocity) / acceleration
print("")
print("Distance =\n", distance)
print("Time =\n", time)

elif u_known and v_known and t_known:
distance = 0.5 * (initial_velocity + final_velocity) * time
acceleration = (final_velocity - initial_velocity) / time
print("")
print("Distance =\n", distance)
print("Acceleration =\n", acceleration)

elif u_known and a_known and t_known:
final_velocity = initial_velocity + acceleration * time
distance = initial_velocity * time + 0.5 * acceleration * time ** 2
print("")
print("Final velocity =\n", final_velocity)
print("Distance =\n", distance)

elif v_known and a_known and t_known:
initial_velocity = final_velocity - acceleration * time
distance = 0.5 * (2 * final_velocity - acceleration * time) * time
print("")
print("Initial velocity =\n", initial_velocity)
print("Distance =\n", distance)

print("")
break

while True:
suvatsolver()
stopper()



Add docstrings for the functions.

Add instructions in the input prompt to let the user know that x means stop. Change:

stop_or_continue = input("Stop?: ")


to:

stop_or_continue = input("Stop?: (type x to stop)")


Change the name of the function from suvatsolver to suvat_solver.

The pass code is not needed and can be removed to simplify the code:

    else:
pass


Remove all the repeated:

print("")


from inside the big if/elif, and place one before the if.

Having 5 very similar variables named like known_ is odd. Consider replacing them with a dictionary:

known = {"s":False, "u":False, "v":False, "a":False, "t":False}


Then set each key as before:

    if distance != "x":
known["s"] = True


Lines like this are hard to read when there are so many of them:

    if s_known and u_known and v_known:

elif s_known and u_known and a_known:


You can use a helper function to check for 3 known variables:

    def all_3_known(var_list: str) -> bool:
for var in var_list:
if known[var] == False:
return False
return True

if all_3_known('suv'):

elif all_3_known('sua'):


You can also factor all variable_count settings out of the indivdual variable checks:

    variable_count = 0
for x in known.values():
if x:
variable_count += 1

if variable_count < 3:
print("not enough variables")
break

• all_3_known can easily be all(known[var] for var in var_list) Commented Apr 16 at 18:38
• @301_Moved_Permanently: Agreed. Thanks. Commented Apr 16 at 19:18

optional values

The five flags like “a_known” are overkill. It would be enough to test “if a is None:”

Break out a helper that is responsible for obtaining 5 optional values, π substituted.

enough values

Create another helper that worries about whether we have enough values, and if we’re over-specified.

decouple

Having verified that we have obtained a good 5-tuple from the user, pass this in to a compute helper that fills in the remaining two. Note that it is amenable to easy unit testing.

matrix form

You might prefer to re-phrase the system of equations in matrix form. Then you could use the Euler tearing algorithm, and could better cope with colinearity when user redundantly specifies 4 values.

outer loop

Consider replacing “while True” with a predicate that answers whether the user wants to quit.