Mean Square Displacement as a Function of Time in Python - python

I have been assigned the following:
"...Plot your final MSD as a function of δt. Include errorbars σ = std(MSD)/√N,
where std(MSD) is the standard deviation among the different runs and N is the
number of runs. N.B.: This is the formula for the error of the mean for statistically
independent, normally distributed time series.
You should find that your curve resembles
MSD(δt) = Dδt,
i. e., it is linear as a function of time in spite of being the square distance the
particle goes. That means that the particle undergoes diffusive motion where it
advanced only proportional to the square root of time. Compute your diffusion
constant D from the curve, and check that D = ∆."
However, I am struggling with this as I am not very proficient at coding, as well as the fact that I am not sure I totally understand the calculation involved.
Here is the code I am working on, note that the final plot is unfinished as this is the part I am struggling with:
#%% Brownian Motion
import math
import matplotlib.pyplot as plt
import numpy as np
import random
P_dis = []
N = 10
# N = the number of iterations (i.e. the number of iterations of the loop below)
for j in range(N):
T = 10000
# T = time steps i.e. the number of jumps the particle makes
x_pos = [0]
y_pos = [0]
# initally empty lists that will be appended below
for i in range(1,T):
A = random.uniform(0 , 1)
B = A * 2 * math.pi
current_x = x_pos[i-1]
current_y = y_pos[i-1]
next_x = current_x + math.cos(B)
# takes previous xpos and applies
next_y = current_y + math.sin(B)
x_pos = x_pos + [next_x]
# appends the next x list with xpos and stores
y_pos = y_pos + [next_y]
dis = np.sqrt((0 - x_pos[T - 1])**2 + (0 - y_pos[T - 1])**2)
P_dis.append(dis)
print(j)
plt.figure()
plt.plot(x_pos , y_pos , "0.65")
plt.plot((x_pos[0] , x_pos[-1]) , (y_pos[0] , y_pos[-1]) , "r" , label = ("Particle displacement =", dis))
plt.plot(x_pos[0] , y_pos[0] , 'ob' , label = "start" )
plt.plot(x_pos[-1] , y_pos[-1] , 'oc' , label = "end")
plt.legend(loc = "upper left")
plt.xlabel("x position")
plt.ylabel("y position")
plt.title("Brownian Motion of a particle in 2 dimensions")
plt.grid(True)
#MSD
MSD = np.mean(P_dis)
print("Mean Square Distance is", MSD , "over" , N , "iterations")
plt.figure()
plt.plot(, [P_dis] , "r")
Any help on the matter would be greatly appreciated.

Let's define:
T = 1000 # Number of time steps
N = 10 # Number of particles
step_size = 1 # Length of one step
I precompute most of the data with numpy and add everything up to get the motion of the random walk:
import numpy as np
import matplotlib.pyplot as plt
# Random direction for the N particles for T time_steps
rnd_angles = np.random.random((N, T))*2*np.pi
# Initialize the positions for each particle to (0, 0)
pos = np.zeros((N, T, 2))
for t in range(1, T):
# Calculate the position at time t for all N particles
# by adding a step in a random direction to the position at time t-1
pos[:, t, 0] = pos[:, t-1, 0] + np.cos(rnd_angles[:, t]) * step_size
pos[:, t, 1] = pos[:, t-1, 1] + np.sin(rnd_angles[:, t]) * step_size
# Calculate the distance to the center (0, 0) for all particles and all times
distance = np.linalg.norm(pos, axis=-1)
# Plot the trajectory of one particle
idx_particle = 7 # Choose from range(0, N)
x_pos = pos[idx_particle, : ,0]
y_pos = pos[idx_particle, : ,1]
dis = distance[idx_particle, -1] # Get the distance at the last time step
plt.figure()
plt.plot(x_pos , y_pos , "0.65")
plt.plot((x_pos[0] , x_pos[-1]) , (y_pos[0] , y_pos[-1]) , "r" ,
label=("Particle displacement =", dis))
plt.plot(x_pos[0] , y_pos[0] , 'ob' , label = "start" )
plt.plot(x_pos[-1] , y_pos[-1] , 'oc' , label = "end")
plt.legend(loc = "upper left")
plt.xlabel("x position")
plt.ylabel("y position")
plt.title("Brownian Motion of a particle in 2 dimensions")
plt.grid(True)
You can get an idea of what is happening and how 'slow' the expansion is moving on by looking at the positions over the time:
for i in np.linspace(0, T-1, 10, dtype=int):
plt.figure()
plt.scatter(pos[:, i, 0] , pos[:, i, 1])
You are interested in the mean squared distance from the start point (0, 0) with respect to the time:
squared_distance = (distance ** 2)
msd = squared_distance.mean(axis=0)
std_msd = squared_distance.std(axis=0)
sigma = std_msd / np.sqrt(N)
plt.errorbar(x=np.arange(T), y=msd, yerr=sigma)
You can chance T, N and step_size to look at the influence on msd.

To plot the MSE with its std with as little changes to your code as possible, you can do the following,
import math
import matplotlib.pyplot as plt
import numpy as np
import random
P_dis = []
N = 10
# N = the number of iterations i.e. the number of iterations of the loop
# below
T = 100
allx = np.array([]).reshape(0,T)
ally = np.array([]).reshape(0,T)
allD = np.array([]).reshape(0,T)
for j in range(N):
# T = time steps i.e. the number of jumps the particle makes
x_pos = [0]
y_pos = [0]
# initally empty lists that will be appended below
for i in range(1,T):
A = random.uniform(0 , 1)
B = A * 2 * math.pi
current_x = x_pos[i-1]
current_y = y_pos[i-1]
next_x = current_x + math.cos(B)
# takes previous xpos and applies
next_y = current_y + math.sin(B)
x_pos = x_pos + [next_x]
# appends the next x list with xpos and stores
y_pos = y_pos + [next_y]
dis = np.sqrt((0 - x_pos[T - 1])**2 + (0 - y_pos[T - 1])**2)
dis = np.sqrt((0 - np.array(x_pos))**2 + (0 - np.array(y_pos))**2)
allD = np.vstack([allD,dis])
allx = np.vstack([allx,np.array(x_pos)])
ally = np.vstack([ally,np.array(y_pos)])
P_dis.append(dis)
print(j)
plt.figure()
plt.plot(np.mean(allx,0) , np.mean(ally,0) , "0.65")
plt.figure()
plt.plot(np.arange(0,T),np.mean(allD,0),'b')
plt.plot(np.arange(0,T),np.mean(allD,0)+np.std(allD,0)/np.sqrt(N),'r')
plt.plot(np.arange(0,T),np.mean(allD,0)-np.std(allD,0)/np.sqrt(N),'r')

Related

2-D laplace heat equation with periodic boundary conditions

desired output**I'm trying to implement periodic condition for horizontal direction, I discretized laplace equation bottom temperature is high, at the top temperature is zero and also left and right side **
import numpy as np
import matplotlib.pyplot as plt
# Set maximum iteration
maxIter = 500
lenX = lenY = 20 #we set it rectangular
delta = 1
# Boundary condition
Ttop = 0
Tbottom = 9.75
Tright = 0
Tleft = 0
# Initial guess of interior grid
Tguess = (Ttop + Tbottom)/2
colorinterpolation = 50
colourMap = plt.cm.jet #you can try: colourMap = plt.cm.coolwarm
X, Y = np.meshgrid(np.arange(0, lenX), np.arange(0, lenY))#,indexing='ij')
T = np.empty((lenX, lenY))+ np.random.random((lenX,lenY))
T.fill(Tguess)
T[(lenY-1):, :] = Ttop
T[:1, :] = Tbottom
T[:, (lenX-1):] = Tright
T[:, :1] = Tleft
for iteration in range(0, maxIter):
for i in range(1, lenX-1, delta):
for j in range(1, lenY-1, delta):
T[i, j] = 0.125 * (T[i+1][j] + T[i-1][j] + T[i][j+1] + T[i][j-1] - 4*T[i,j]) + T[i,j]
plt.contourf(X, Y, T, colorinterpolation, cmap=colourMap)
plt.colorbar()
plt.show()
I'm getting this

Random coordinates generator

I want to generate random coordinates for spheres in a box geometry. I'm using while loop and i have 2 condition. First one is the distance of coordinates. General distance formula was used so that the coordinates do not overlap. Second one is the porosity. When porosity is less than 0.45 generating should stop. My code is working correctly but when i reduce porosity condition less than 0.80 the algorithm stucks. It cannot reach that porosity even after hours. How can I improve it to generate coordinates faster? Any suggestions are appreciated.
#dist = math.sqrt(((x2-x1)**2) + ((y2-y1)**2) + ((z2-z1)**2))
import math
import random
import numpy as np
import matplotlib.pyplot as plt
A = 0.04 # x border.
B = 0.04 # y border.
C = 0.125 # z border.
V_total = A*B*C # volume
r = 0.006 # min distance of spheres.
radius = 0.003 # radius of spheres.
wall_distance = 0.003
Porosity = 1.0
coordinates = np.array([])
while Porosity >= 0.90:
# coordinates
x = random.uniform(wall_distance, A-wall_distance)
y = random.uniform(wall_distance, B-wall_distance)
z = random.uniform(wall_distance, C-wall_distance)
coord1 = (x,y,z)
if coordinates.shape[0] == 0: # add first one without condition
coordinates = np.array([coord1])
else:
coordinates = np.vstack((coordinates, coord1))
# seperate x,y,z and convert list for control
d_x = coordinates[:,0]
x = d_x.tolist()
d_y = coordinates[:,1]
y = d_y.tolist()
d_z = coordinates[:,2]
z = d_z.tolist()
for j in range(len(y)):
for k in range(j+1, len(z)):
dist = math.sqrt(((x[j]-x[k])**2) + ((y[j]-y[k])**2) + ((z[j]-z[k])**2))
if dist <= r:
coordinates = coordinates[:-1, :] # if distance is less than r, remove last coordinate
# check porosity
V_spheres = (4/3) * (np.pi) * (radius**3) * len(coordinates)
V_void = V_total - V_spheres
Porosity = V_void / V_total
print("Porosity: {}".format(Porosity))
print("Number of spheres: {}".format(len(coordinates)))
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim([0, A])
ax.set_ylim([0, B])
ax.set_zlim([0, C])
ax.set_title('Coordinates for spheres')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p = ax.scatter(coordinates[:,0], coordinates[:,1], coordinates[:,2])
fig.colorbar(p)
plt.show()
There are a number of things you can do to improve your performance here. See my modified code below, with explanations
import math
import random
import numpy as np
import matplotlib.pyplot as plt
A = 0.04 # x border.
B = 0.04 # y border.
C = 0.125 # z border.
V_total = A*B*C # volume
r = 0.006 # min distance of spheres.
radius = 0.003 # radius of spheres.
wall_distance = 0.003
Porosity = 1.0
coordinates = np.empty((0,3)) # initialize array with correct shape
while Porosity >= 0.70:
# coordinates
x = random.uniform(wall_distance, A-wall_distance)
y = random.uniform(wall_distance, B-wall_distance)
z = random.uniform(wall_distance, C-wall_distance)
is_invalid = (True in [
math.sqrt(((x - coordinates[i_coor,0])**2) +
((y - coordinates[i_coor,1])**2) +
((z - coordinates[i_coor,2])**2)) <= r
for i_coor in range(coordinates.shape[0]) ])
if not is_invalid:
coordinates = np.append(coordinates,[[x,y,z]], axis = 0)
else:
continue
V_spheres = (4/3) * (np.pi) * (radius**3) * len(coordinates)
V_void = V_total - V_spheres
Porosity = V_void / V_total
print(f"placed coord {len(coordinates)}, por = {Porosity}")
print("Porosity: {}".format(Porosity))
print("Number of spheres: {}".format(len(coordinates)))
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim([0, A])
ax.set_ylim([0, B])
ax.set_zlim([0, C])
ax.set_title('Coordinates for spheres')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
p = ax.scatter(coordinates[:,0], coordinates[:,1], coordinates[:,2])
np.savetxt('out.csv', coordinates)
fig.colorbar(p)
plt.show()
the main thing I changed is this double for loop
for j in range(len(y)):
for k in range(j+1, len(z)):
dist = math.sqrt(((x[j]-x[k])**2) + ((y[j]-y[k])**2) + ((z[j]-z[k])**2))
This was checking every pair of points for overlap EACH TIME YOU ADD A SINGLE POINT. That took unnecessarily long. By only checking if the new point intersects with the old points, you reduce your runtime from O(n^3) to O(n^2). I was able to pretty quickly run this with 0.5 perosity.

Finite Difference Solution to Heat Equation

Practicing finite difference implementation and I cannot figure out why my solution looks so strange. Code taken from: http://people.bu.edu/andasari/courses/numericalpython/Week9Lecture15/PythonFiles/FTCS_DirichletBCs.py.
Note: I'm using this lecture example for the heat equation not the reaction-diffusion equation!
I haven't learned the relevant mathematics so this could be why!
My code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import math as mth
from mpl_toolkits.mplot3d import Axes3D
import pylab as plb
import scipy as sp
import scipy.sparse as sparse
import scipy.sparse.linalg
# First start with diffusion equation with initial condition u(x, 0) = 4x - 4x^2 and u(0, t) = u(L, t) = 0
# First discretise the domain [0, L] X [0, T]
# Then discretise the derivatives
# Generate algorithm:
# 1. Compute initial condition for all i
# 2. For all n:
# 2i. Compute u_i^{n + 1} for internal space points
# 2ii. Set boundary values for i = 0 and i = N_x
M = 40 # number of grid points for space interval
N = 70 # '' '' '' '' '' time ''
x0 = 0
xL = 1 # unit grid differences
dx = (xL - x0) / (M - 1) # space step
t0 = 0
tF = 0.2
dt = (tF - t0) / (N - 1)
D = 0.3 # thermal diffusivity
a = D * dt / dx**2
# Create grid
tspan = np.linspace(t0, tF, N)
xspan = np.linspace(x0, xL, M)
# Initial matrix solution
U = np.zeros((M, N))
# Initial condition
U[:, 0] = 4*xspan - 4*xspan**2
# Boundary conditions
U[0, :] = 0
U[-1, 0] = 0
# Discretised derivative formula
for k in range(0, N-1):
for i in range(1, M-1):
U[i, k+1] = a * U[i-1, k] + (1 - 2 * a) * U[i, k] + a * U[i + 1, k]
X, T = np.meshgrid(tspan, xspan)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, T, U, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_xticks([0, 0.05, 0.1, 0.15, 0.2])
ax.set_xlabel('Space')
ax.set_ylabel('Time')
ax.set_zlabel('U')
plt.tight_layout()
plt.show()
edit: Changed therm diff value to correct one.
The main problem is the time step length. If you look at the differential equation, the numerics become unstable for a>0.5. Translated this means for you that roughly N > 190. I get a nice picture if I increase your N to such value.
However, I thing somewhere the time and space axes are swapped (if you try to interpret the graph then, i.e. boundary conditions and expected dampening of profile over time). I cannot figure out right now why.
Edit: Actually, you swap T and X when you do meshgrid. This should work:
N = 200
...
...
T, X = np.meshgrid(tspan, xspan)
...
surf = ax.plot_surface(T, X, U, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
...
ax.set_xlabel('Time')
ax.set_ylabel('Space')

How to Fix Index Error in Differential Equation?

I am trying to create a program that solves the mass-spring-damper system using backward differentiating, the only problem is that I am running into an index error that I am not sure how to solve:
import numpy as np
import matplotlib.pyplot as plt
def MSD_Solver(m,b,K):
#input: m = mass, b = damping ratio, K = spring constant
#output: (t,x) time vs position
tinitial = 0
tfinal = 15
step = .005
t = np.linspace(tinitial,tfinal,step)
x = np.zeros_like(t)
x[0]=0
x[1]=0
for k in range (len(t)-1): # extra element so subtract by 1
x[k] = (t**2)/((m+b)*t+(t**2)*k) + (x[k-2](-m))/((m+b)*t+(t**2)*k) + (x[k-1]((2*m)+(b*t)))/((m+b)*t+(t**2)*k)
return plt.plot(t,x)
print(MSD_Solver(1,.5,5)),MSD_Solver(1,1,5),MSD_Solver(1,2,5)
plt.show()
The linspace doc shows that the third argument is the number of items, not the step. Your step value got truncated to 0, so the returned array for t was empty. As a result, x has no elements, and x[0] is out of range.
Try this:
tinitial = 0
tfinal = 15
step = .005
num = (tfinal - tinitial) / step + 1
t = np.linspace(tinitial,tfinal,num)
This will get you to the semantic errors in your complex computation.
You want, probably(?), use first and second order difference quotients to discretize
m*x''(t) + b*x'(t) + K*x(t) = 1
to
m*(x[j+1]-2*x[j]+x[j-1]) + 0.5*dt*b*(x[j+1]-x[j-1]) + dt^2*K*x[j] = dt**2
so that
x[j+1] = ( dt**2 + (2*m-K*dt**2)*x[j] - (m-0.5*dt*b)*x[j-1] ) / (m+0.5*dt*b)
In code
def MSD_Solver(m,b,K):
#input: m = mass, b = damping ratio, K = spring constant
#output: (t,x) time vs position
tinitial = 0
tfinal = 15
step = .005
t = np.arange(tinitial,tfinal,step)
x = np.zeros_like(t)
dt = t[1]-t[0] # use the actual time step
x[0:2] = [ 0, 0]
for j in range(1,len(t)-1):
x[j+1] = ( dt**2 + (2*m-K*dt**2)*x[j] - (m-0.5*dt*b)*x[j-1] ) / (m+0.5*dt*b)
return t,x
t,x = MSD_Solver(1,.5,5)
plt.plot(t,x); plt.show();

Barabasi-Albert model, wrong degree exponent

I'm trying to generate a scale-free network using the Barabasi-Albert model. The model predicts a degree distribution that follows p(k) ~ k^-3 but mine shows k^-2.
The algorithm was taken from Barabasi's book at this URL: http://barabasi.com/networksciencebook,
here is the relevant paragraph:
Barabasi's algorithm
Here is my code, could someone please help me figure out what is wrong?
import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
plt.rcParams["figure.figsize"] = (15,6)
#initialize values
N = 10000
k = 2
m = int(k / 2)
#initialize matrices
adjacency = np.zeros((N,N))
degrees = np.zeros(N)
#add links
for i in range(N):
degrees[i] = m
for c in range(m):
# choose a node with probability proportional to it's degree
j = np.random.choice(N, p = degrees / (2 * m * i + m + c))
degrees[j] += 1
adjacency[i][j] += 1
adjacency[j][i] += 1
def get_binned_data(labels, values, num):
min_label, max_label = min(labels), max(labels)
base = (max_label / min_label) ** (1 / num)
bins = [base**i for i in range(int(np.log(max_label) / np.log(base)) + 1)]
binned_values, binned_labels = [], []
counter = 0
for b in bins:
bin_size = 0
bin_sum = 0
while counter < len(labels) and labels[counter] <= b:
bin_size += values[counter]
bin_sum += values[counter] * labels[counter]
counter += 1
if(bin_size):
binned_values.append(bin_size)
binned_labels.append(bin_sum / bin_size)
return binned_labels, binned_values
labels, values = zip(*sorted(Counter(degrees).items(), key = lambda pair:
pair[0]))
binned_labels, binned_values = get_binned_data(labels, values, 15)
fig, (ax1, ax2) = plt.subplots(ncols = 2, nrows = 1)
fig.suptitle('Barabasi-Albert Model',fontsize = 25)
ax1.loglog(binned_labels, binned_values, basex = 10, basey = 10, linestyle =
'None', marker = 'o', color = 'red')
ax1.set(xlabel = 'degree', ylabel = '# of nodes')
ax1.set_title('log-log scale (log-binned)',{'fontsize':'15'})
ax2.plot(labels, values, 'ro')
ax2.set(xlabel = 'degree', ylabel = '# of nodes')
ax2.set_title('linear scale',{'fontsize':'15'})
plt.show()
Your code does not run (probabilities in np.random.choice do not sum to 1). Why not p = degrees/np.sum(degrees)?
According to Wikipedia, you need to start with some already connected nodes, whereas you start from nothing. Also, you should probably put degrees[i] = m after the inner loop to avoid forming links from node i to itself.
This might help, but it's not clear to me how you generate your degree plot, so I can't verify it.

Categories