Issue with a.any() or a.all() when trying to simulate a motion using a while loop
Question:
I am simulating a motion and when using a while loop, I am getting the following error "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()". The objective is to run the while loop to the point when the accelaration is 0… Any ideas of what might’ve gone wrong?
R = 0.025
B = 0.15
L = 0.2
m = 0.05
F = 1
def a(v):
F_m = (B**2*L**2*v)/R
sum_F = F - F_m
aks = sum_F /m
return aks
v = array([0,0])
t = linspace(0,11,1000)
speed = [v[0]]
dt = 0.001
while a(v)>0:
v = v + a(v)*dt
t = t + dt
speed.append(v[0])
plot(t,speed, "r")
xlabel("t/s")
ylabel("v m/s")
grid()
show()```
Answers:
There’s a few things here.
For those playing along at home, you’ll need to include some imports at the top:
from matplotlib.pyplot import *
from numpy import *
The specific error message that you’re getting is because v
is an array of values, so a(v)
is an array, and a(v) > 0
is also an array.
The while
condition requires a single (scalar) value. As the error message suggests, you could change it to:
while (a(v) > 0).any(): # or .all()
That works but it’s not particularly scientifically rigorous. If your axes are perpendicular then you could use a Euclidean measure to combine the values:
while linalg.norm(a(v)) > 0: # sqrt( sum(a(v)**2) )
This will get you into new trouble, because although the velocity gets very small, it will never actually get to zero, so your program will run forever! More useful would be to cut it off when it gets fairly small:
while linalg.norm(a(v)) > 0.1: # for example
So, now the loop runs but we hit a new problem trying to plot:
ValueError: x and y must have same first dimension, but have shapes (1000,) and (7839,)
What’s going on? It is trying to plot t
versus speed
.
speed
is the array built up in your while loop, which has length 7839
because that’s how many iterations it took for a(v)
to drop below 0.1
.
t
is an array of length 1000 whose value isn’t actually used! You initialise it using linspace
to contain 1000 values spread equally between 0 and 11, then increment every value in it by dt
each time the loop runs, but then never use the value.
Let’s set that aside for the moment and add some new variables current_time
and times
which we’ll use in the loop:
current_time = 0
times = []
while linalg.norm(a(v)) > 0.1:
v = v + a(v)*dt
speed.append(v[0])
current_time = current_time + dt
times.append(current_time)
… and then plot speed
against our new times
array, which are constructed side by side so they have the same length.
So, that works. But we still have this t
variable from before which isn’t being used. Presumably it contains a list of times at which we’d like to sample the velocity, so let’s get rid of current_time
and times
and use the values in t
:
prev_time = 0
for time in t:
dt = time - prev_time
v = v + a(v)*dt
speed.append(v[0])
prev_time = time
Now we can plot speed
against t
because they have the same length:
Essentially the same plot as before, but now it is sampling using the times in t
rather than "for as long as it takes for a(v)
to get sufficiently small". We can verify this by reducing the number of samples, e.g.
t = linspace(0,11,11)
I am simulating a motion and when using a while loop, I am getting the following error "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()". The objective is to run the while loop to the point when the accelaration is 0… Any ideas of what might’ve gone wrong?
R = 0.025
B = 0.15
L = 0.2
m = 0.05
F = 1
def a(v):
F_m = (B**2*L**2*v)/R
sum_F = F - F_m
aks = sum_F /m
return aks
v = array([0,0])
t = linspace(0,11,1000)
speed = [v[0]]
dt = 0.001
while a(v)>0:
v = v + a(v)*dt
t = t + dt
speed.append(v[0])
plot(t,speed, "r")
xlabel("t/s")
ylabel("v m/s")
grid()
show()```
There’s a few things here.
For those playing along at home, you’ll need to include some imports at the top:
from matplotlib.pyplot import *
from numpy import *
The specific error message that you’re getting is because v
is an array of values, so a(v)
is an array, and a(v) > 0
is also an array.
The while
condition requires a single (scalar) value. As the error message suggests, you could change it to:
while (a(v) > 0).any(): # or .all()
That works but it’s not particularly scientifically rigorous. If your axes are perpendicular then you could use a Euclidean measure to combine the values:
while linalg.norm(a(v)) > 0: # sqrt( sum(a(v)**2) )
This will get you into new trouble, because although the velocity gets very small, it will never actually get to zero, so your program will run forever! More useful would be to cut it off when it gets fairly small:
while linalg.norm(a(v)) > 0.1: # for example
So, now the loop runs but we hit a new problem trying to plot:
ValueError: x and y must have same first dimension, but have shapes (1000,) and (7839,)
What’s going on? It is trying to plot t
versus speed
.
speed
is the array built up in your while loop, which has length 7839
because that’s how many iterations it took for a(v)
to drop below 0.1
.
t
is an array of length 1000 whose value isn’t actually used! You initialise it using linspace
to contain 1000 values spread equally between 0 and 11, then increment every value in it by dt
each time the loop runs, but then never use the value.
Let’s set that aside for the moment and add some new variables current_time
and times
which we’ll use in the loop:
current_time = 0
times = []
while linalg.norm(a(v)) > 0.1:
v = v + a(v)*dt
speed.append(v[0])
current_time = current_time + dt
times.append(current_time)
… and then plot speed
against our new times
array, which are constructed side by side so they have the same length.
So, that works. But we still have this t
variable from before which isn’t being used. Presumably it contains a list of times at which we’d like to sample the velocity, so let’s get rid of current_time
and times
and use the values in t
:
prev_time = 0
for time in t:
dt = time - prev_time
v = v + a(v)*dt
speed.append(v[0])
prev_time = time
Now we can plot speed
against t
because they have the same length:
Essentially the same plot as before, but now it is sampling using the times in t
rather than "for as long as it takes for a(v)
to get sufficiently small". We can verify this by reducing the number of samples, e.g.
t = linspace(0,11,11)