Plotting particles positions over time - python

I have drawn one position(x,y,z) of N particles in an enclosed volume.
x[i] = random.uniform(a,b) ...
I also found the constant velocity(vx,vy,vz) of the N particles.
vx[i] = random.gauss(mean,sigma) ...
Now I want to find the position of the N(=100) particles over time. I used the Euler-Cromer method to this.
delta_t = linspace(0,2,n-1)
n = 1000
v[0] = vx;...
r[0] = x;...
for i in range(n-1):
v[i+1,:] = v[i,:]
r[i+1,:] = r[i,:] + delta_t*v[i+1,:]
t[i+1] = t[i] + delta_t
But I want to find the position over time for every particle. How can I do this? Also, how do I plot the particles position over time in 3D?

To find the position of the particles at a given time you can use the following code:
import numpy as np
# assign random positions in the box 0,0,0 to 1,1,1
x = np.random.random((100,3))
# assign random velocities in the range around 0
v = np.random.normal(size=(100,3))
# define function to project the position in time according to
# laws of motion. x(t) = x_0 + v_0 * t
def position(x_0, v_0, t):
return x_0 + v_0*t
# get new position at time = 3.2
position(x, v, 3.2)

Related

Brownian motion in python 2D

I want to create a Brownian motion sim
My particle will start at the (0,0), the origin then I've created NumPy random arrays for the x and y direction for example, x = [-2,1,3] and y = [0,-2,1]. Since the particle starts at the origin (0th point) it will to the next point by -2 in the direction and 0 in the y (the 1st point), and then for the 2nd point, it will 1 unit to the right (+1 in the x) and -2 units to the left(-2 in the y).
My question is how would I make it so each point acts as the new origin kind of like adding vectors. I'm thinking I would need some sort of for loop, but not sure how I would set it up.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation as am
np.random.seed(69420)
N=1000 #Number of steps
# Have the particle at the origin in 2D (x,y) plane
# Each step will have a lenght of 1 unit. Lets call it meters
# Each step is one second
# And this simulation will run for 100 seconds
## Now how to make a particle randomly walk???
t=100 # time interval
dt=t/N # delta time from each step
dx = np.random.randint(-5,5,N,dtype=int)# This would be our detla x for each step
dy = np.random.randint(-5,5,N,dtype=int)
dx_0 = np.zeros(N, dtype=int)
dy_0 = np.zeros(N,dtype=int)
X = dx_0+dx
Y = dy_0+dy
print(X)
xdis = np.cumsum(X) #Total distance travled after N steps
ydis = np.cumsum(Y)
plt.plot(dx_0,dy_0,'ko')
plt.plot(xdis,ydis,'b-')
plt.show()
This is what I have so far. Any help is appreciated.
You need to take N steps, starting from (0,0). Track your steps using a variable prev = [0,0], and make changes to it for each step. Simplified code:
prev = [0,0]
for i in range(N):
new_x = prev[0]+dx[i]
new_y = prev[1]+dy[i]
# do what you need with new_x and new_y
prev = [new_x, new_y]
Since it seems like you wish to graph the entire walk, just sum all these steps?
origin = np.array([0, 0])
x_series = np.cumsum(dx) + origin[0]
# (Similarly for y)
This is only for 1 random walker. If you have multiple random walkers - each of them will have a starting point and separate chain of dx-s.

Solve a second order ode using numpy [duplicate]

I am solving an ODE for an harmonic oscillator numerically with Python. When I add a driving force it makes no difference, so I'm guessing something is wrong with the code. Can anyone see the problem? The (h/m)*f0*np.cos(wd*i) part is the driving force.
import numpy as np
import matplotlib.pyplot as plt
# This code solves the ODE mx'' + bx' + kx = F0*cos(Wd*t)
# m is the mass of the object in kg, b is the damping constant in Ns/m
# k is the spring constant in N/m, F0 is the driving force in N,
# Wd is the frequency of the driving force and x is the position
# Setting up
timeFinal= 16.0 # This is how far the graph will go in seconds
steps = 10000 # Number of steps
dT = timeFinal/steps # Step length
time = np.linspace(0, timeFinal, steps+1)
# Creates an array with steps+1 values from 0 to timeFinal
# Allocating arrays for velocity and position
vel = np.zeros(steps+1)
pos = np.zeros(steps+1)
# Setting constants and initial values for vel. and pos.
k = 0.1
m = 0.01
vel0 = 0.05
pos0 = 0.01
freqNatural = 10.0**0.5
b = 0.0
F0 = 0.01
Wd = 7.0
vel[0] = vel0 #Sets the initial velocity
pos[0] = pos0 #Sets the initial position
# Numerical solution using Euler's
# Splitting the ODE into two first order ones
# v'(t) = -(k/m)*x(t) - (b/m)*v(t) + (F0/m)*cos(Wd*t)
# x'(t) = v(t)
# Using the definition of the derivative we get
# (v(t+dT) - v(t))/dT on the left side of the first equation
# (x(t+dT) - x(t))/dT on the left side of the second
# In the for loop t and dT will be replaced by i and 1
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-dT*b/m) + (dT/m)*F0*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
# Ploting
#----------------
# With no damping
plt.plot(time, pos, 'g-', label='Undampened')
# Damping set to 10% of critical damping
b = (freqNatural/50)*0.1
# Using Euler's again to compute new values for new damping
for i in range(0, steps):
vel[i+1] = (-k/m)*dT*pos[i] + vel[i]*(1-(dT*(b/m))) + (F0*dT/m)*np.cos(Wd*i)
pos[i+1] = dT*vel[i] + pos[i]
plt.plot(time, pos, 'b-', label = '10% of crit. damping')
plt.plot(time, 0*time, 'k-') # This plots the x-axis
plt.legend(loc = 'upper right')
#---------------
plt.show()
The problem here is with the term np.cos(Wd*i). It should be np.cos(Wd*i*dT), that is note that dT has been added into the correct equation, since t = i*dT.
If this correction is made, the simulation looks reasonable. Here's a version with F0=0.001. Note that the driving force is clear in the continued oscillations in the damped condition.
The problem with the original equation is that np.cos(Wd*i) just jumps randomly around the circle, rather than smoothly moving around the circle, causing no net effect in the end. This can be best seen by plotting it directly, but the easiest thing to do is run the original form with F0 very large. Below is F0 = 10 (ie, 10000x the value used in the correct equation), but using the incorrect form of the equation, and it's clear that the driving force here just adds noise as it randomly moves around the circle.
Note that your ODE is well behaved and has an analytical solution. So you could utilize sympy for an alternate approach:
import sympy as sy
sy.init_printing() # Pretty printer for IPython
t,k,m,b,F0,Wd = sy.symbols('t,k,m,b,F0,Wd', real=True) # constants
consts = {k: 0.1, # values
m: 0.01,
b: 0.0,
F0: 0.01,
Wd: 7.0}
x = sy.Function('x')(t) # declare variables
dx = sy.Derivative(x, t)
d2x = sy.Derivative(x, t, 2)
# the ODE:
ode1 = sy.Eq(m*d2x + b*dx + k*x, F0*sy.cos(Wd*t))
sl1 = sy.dsolve(ode1, x) # solve ODE
xs1 = sy.simplify(sl1.subs(consts)).rhs # substitute constants
# Examining the solution, we note C3 and C4 are superfluous
xs2 = xs1.subs({'C3':0, 'C4':0})
dxs2 = xs2.diff(t)
print("Solution x(t) = ")
print(xs2)
print("Solution x'(t) = ")
print(dxs2)
gives
Solution x(t) =
C1*sin(3.16227766016838*t) + C2*cos(3.16227766016838*t) - 0.0256410256410256*cos(7.0*t)
Solution x'(t) =
3.16227766016838*C1*cos(3.16227766016838*t) - 3.16227766016838*C2*sin(3.16227766016838*t) + 0.179487179487179*sin(7.0*t)
The constants C1,C2 can be determined by evaluating x(0),x'(0) for the initial conditions.

Programming of 4th order Runge-Kutta in advection equation in python

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from math import pi
# wave speed
c = 1
# spatial domain
xmin = 0
xmax = 1
#time domain
m=500; # num of time steps
tmin=0
T = tmin + np.arange(m+1);
tmax=500
n = 50 # num of grid points
# x grid of n points
X, dx = np.linspace(xmin, xmax, n+1, retstep=True);
X = X[:-1] # remove last point, as u(x=1,t)=u(x=0,t)
# for CFL of 0.1
CFL = 0.3
dt = CFL*dx/c
# initial conditions
def initial_u(x):
return np.sin(2*pi*x)
# each value of the U array contains the solution for all x values at each timestep
U = np.zeros((m+1,n),dtype=float)
U[0] = u = initial_u(X);
def derivatives(t,u,c,dx):
uvals = [] # u values for this time step
for j in range(len(X)):
if j == 0: # left boundary
uvals.append((-c/(2*dx))*(u[j+1]-u[n-1]))
elif j == n-1: # right boundary
uvals.append((-c/(2*dx))*(u[0]-u[j-1]))
else:
uvals.append((-c/(2*dx))*(u[j+1]-u[j-1]))
return np.asarray(uvals)
# solve for 500 time steps
for k in range(m):
t = T[k];
k1 = derivatives(t,u,c,dx)*dt;
k2 = derivatives(t+0.5*dt,u+0.5*k1,c,dx)*dt;
k3 = derivatives(t+0.5*dt,u+0.5*k2,c,dx)*dt;
k4 = derivatives(t+dt,u+k3,c,dx)*dt;
U[k+1] = u = u + (k1+2*k2+2*k3+k4)/6;
# plot solution
plt.style.use('dark_background')
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
line, = ax1.plot(X,U[0],color='cyan')
ax1.grid(True)
ax1.set_ylim([-2,2])
ax1.set_xlim([0,1])
def animate(i):
line.set_ydata(U[i])
return line,
I want to program in Python an advection equation which is (∂u/∂t) +c (∂u/∂x) = 0. Time should be discretized with Runge-kutta 4th order. Spatial discretiziation is 2nd order finite difference. When I run my code, I get straight line which transforms into sine wave. But I gave as initial condition sine wave. Why does it start as straight line? And I want to have sine wave moving forward. Do you have any idea on how to get sine wave moving forward? I appreciate your help. Thanks in advance!
While superficially your computation steps are related to the RK4 method, they deviate from the RK4 method and the correct space discretization too much to mention it all.
The traditional way to apply ODE integration methods is to have a function derivatives(t, state, params) and then apply that to compute the Euler step or the RK4 step. In your case it would be
def derivatives(t,u,c,dx):
du = np.zeros(len(u));
p = c/(2*dx);
du[0] = p*(u[1]-u[-1]);
du[1:-1] = p*(u[2:]-u[:-2]);
du[-1] = p*(u[0]-u[-2]);
return du;
Then you can do
X, dx = np.linspace(xmin, xmax, n+1, retstep=True);
X = X[:-1] # remove last point, as u(x=1,t)=u(x=0,t)
m=500; # number of time steps
T = tmin + np.arange(m+1);
U = np.zeros((m+1,n),dtype=float)
U[0] = u = initial_u(X);
for k in range(m):
t = T[k];
k1 = derivatives(t,u,c,dx)*dt;
k2 = derivatives(t+0.5*dt,u+0.5*k1,c,dx)*dt;
k3 = derivatives(t+0.5*dt,u+0.5*k2,c,dx)*dt;
k4 = derivatives(t+dt,u+k3,c,dx)*dt;
U[k+1] = u = u + (k1+2*k2+2*k3+k4)/6;
This uses dt as computed as the primary variable in the time stepping, then constructs the arithmetic sequence from tmin with step dt. Other ways are possible, but one has to make tmax and the number of time steps compatible.
The computation up to this point should now be successful and can be used in the animation. In my understanding, you do not produce a new plot in each frame, you only draw the graph once and after that just change the line data
# animate the time data
line, = ax1.plot(X,U[0],color='cyan')
ax1.grid(True)
ax1.set_ylim([-2,2])
ax1.set_xlim([0,1])
def animate(i):
line.set_ydata(U[i])
return line,
etc.

Wave on a string analysis

I am trying to analyse a wave on a string by solving the wave equation with Python. Here are my requirements for the solution.
1) I model reflective ends by using much larger masses on first and last point on the string -> Large inertia
2)No spring on edges. Then k[0] and k[-1] will be ZERO.
I have problem with indices. In my 2nd loop I get y[i,j-1], k[i-1], y[i-1,j]. In the first iteration the loop will then use y[0,-1], k[-1], y[-1,0]. These are my last points and not my first points. How can I avoid this problem?
What you need is initiating your mass array with one additional element. I mean
...
m = [mi]*N # mass array [!!!] instead of (N-1) [!!!]
...
Idem for your springs
...
k = [ki]*N
...
Consequently, you can keep k[0] equal to 10. since you model reflective ends. You may thus want to comment or drop this line
...
##k[0] = 0
...
For aesthetic considerations you may want to fill the gap at the end of the x-axis. In this case, simply do
N = 201 # Number of mass points
Your code thus becomes
from numpy import *
from matplotlib.pyplot import *
# Variables
N = 201 # Number of mass points
nT = 1200 # Number of time points
mi = 0.02 # mass in kg
m = [mi]*N # mass array
m[-1] = 100 # Large last mass reflective edges
m[0] = 100 # Large first mass reflective edges
ki = 10.#spring
k = [ki]*N
k[-1] = 0
dx = 0.2
kappa = ki*dx
my = mi/dx
c = sqrt(kappa/my) # velocity
dt = 0.04
# 3 vectors
x = arange( N )*dx # x points
t = arange( N )*dt # t points
y = zeros( [N, nT ] )# 2D array
# Loop over initial condition
for i in range(N-1):
y[i,0] = sin((7.*pi*i)/(N-1)) # Initial condition dependent on mass point
# Iterating over time and position to find next position of wave
for j in range(nT-1):
for i in range(N-1):
y[i,j+1] = 2*y[i,j] - y[i,j-1] + (dt**2/m[i])*(k[i-1]*y[i+1,j] -2*k[i-1]*y[i,j] + k[i]*y[i-1,j] )
#check values of edges
print y[:2,j+1],y[-2:,j+1]
# Creates an animation
cla()
ylabel("Amplitude")
xlabel("x")
ylim(-10,10)
plot(x,y[:,j-2])
pause(0.001)
close()
which produces
Following your comment, I think that if you want to see the wave traveling along the string before reflection, you should not initiate conditions everywhere (in space). I mean, e.g., doing
...
# Loop over initial condition
for i in range(N-1):
ci_i = sin(7.*pi*i/(N-1)) # Initial condition dependent on mass point
if np.sign(ci_i*y[i-1,0])<0:
break
else:
y[i,0] = ci_i
...
produces
New attempt after answers:
from numpy import *
from matplotlib.pyplot import *
N = 201
nT = 1200
mi = 0.02
m = [mi]*(N)
m[-1] = 1000
m[0] = 1000
ki = 10.
k = [ki]*N
dx = 0.2
kappa = ki*dx
my = mi/dx
c = sqrt(kappa/my)
dt = 0.04
x = arange( N )*dx
t = arange( N )*dt
y = zeros( [N, nT ] )
for i in range(N-1):
y[i,0] = sin((7.*pi*i)/(N-1))
for j in range(nT-1):
for i in range(N-1):
if j == 0: # if j = 0 then ... y[i,j-1]=y[i,j]
y[i,j+1] = 2*y[i,j] - y[i,j] + (dt**2/m[i])*(k[i-1]*y[i+1,j] -2*k[i-1]*y[i,j] + k[i]*y[i-1,j] )
else:
y[i,j+1] = 2*y[i,j] - y[i,j-1] + (dt**2/m[i])*( k[i-1]*y[i+1,j] -2*k[i-1]*y[i,j] + k[i]*y[i-1,j] )
cla()
ylim(-1,1)
plot(x,y[:,j-2])
pause(0.0001)
ylabel("Amplitude")
xlabel("x")
print len(x), len(t), N,nT
Here is a plot of the new attempt at solution with |amplitude| of anti node equal 1.0. Will this do anything with further solving the issue with indices?

Using code to write a function that calculates values for Projectile motion

I have an assignment that asks me to use some given code to write a function which calculates the angle needed to hit a target 10 metres away.
here is the given code:
from visual import *
from visual.graph import * # For the graphing functions
#Create a graph display window (gdisplay)
win = gdisplay(xtitle="Distance [m]", ytitle="Height [m]")
#And a curve on this display
poscurve = gcurve(gdisplay=win, color=color.cyan)
#Target position (10 meters away)
target_pos = vector(10,0,0)
#Set the starting angle (in degrees)
angle = 45
#Set the magnitude of the starting velocity (in m/s)
v0 = 12.0
#Gravity vector (m/s**2)
g = vector(0, -9.8, 0)
#Create a vector for the projectile's velocity
velocity = v0 * vector(cos(anglepi/180), sin(anglepi/180), 0)
#and the position
position = vector(0,0,0)
dt = 0.01 # Time step
#Start loop. Each time taking a small step in time
while (position.y > 0) or (velocity.y > 0): # Change in position # dx = (dx/dt) * dt dx = velocity * dt
# Change in velocity
# dv = (dv/dt) * dt
dv = g * dt
# Update the position and velocity
position = position + dx
velocity = velocity + dv
# Plot the current position
poscurve.plot(pos=position)
#When loop finishes, velocity.y must be < 0, and position.y < 0
print "Landed at X position: ", position.x print "X distance to target: ", position.x - target_pos.x
How would I now write a function to calculate the required value? I have no idea where to start, any help would be greatly appreciated!
Thanks
You could use maths to work out an equation for the result.
This works out as:
range = 2v^2/g *cos(a)sin(a)
where v=initial velocity
a=angle
g=gravitational acceleration
You can use this python script to find the answer:
from math import cos
from math import sin
from math import radians
from math import fabs
a=0 # angle in degrees
target=10 # How far you want it to go in m
v=12 # initial velocity in m/s
g=9.81 #gravitational acceleration m/s/s
best_angle=None
nearest_answer=None
while a<45: # we only need to check up to 45 degrees
r = 2*v*v/g*cos(radians(a))*sin(radians(a))
if not nearest_answer or fabs(r-target)<fabs(nearest_answer-target):
nearest_answer = r
best_angle = a
print("{0} -> {1}".format(a,r))
a+=.1 # try increasing the angle a bit. The lower this is the more accurate the answer will be
print("Best angle={}".format(best_angle))

Categories