So far I have code for Brownian Motion in 1D and 2D. My 3D graph is obviously incorrect, since my x,y,and z data variables are all the same. I just don't know what to set them too. I'm trying to follow https://www.mathworks.com/matlabcentral/fileexchange/32067-brownian-motion?focused=5191300&tab=function Here's my code:
import numpy as np
from pylab import show
from math import sqrt
from scipy.stats import norm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def brownian(x0, n, dt, delta, out=None):
# n : The number of steps to take.
# dt : time step
# delta : "speed" of motion
# out :If `out` is NOT None, it specifies the array in which to put the
# result. If `out` is None, a new numpy array is created and returned.
x0 = np.asarray(x0) #I.C
r = norm.rvs(size=x0.shape + (n,), scale=delta*sqrt(dt)) #generate n numbers for sample
if out is None: #create out array
out = np.empty(r.shape)
np.cumsum(r, axis=-1, out=out) #cumulative sum for random variables
out += np.expand_dims(x0, axis=-1)#initial condition.
return out
def main():
fig = plt.figure(1) #prepare plot
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax = fig.add_subplot(2, 3, 3, projection='3d')
delta = 2 # The Wiener process parameter.
T = 10.0
N = 500# Number of steps.
dt = T/N
m = 5 # Number of "lines"
x = np.empty((m,N+1))# Create an empty array to store the realizations.
x[:, 0] = 0# Initial values of x.
brownian(x[:,0], N, dt, delta, out=x[:,1:])
t = np.linspace(0.0, T, N+1)
for i in range(m):
ax1.plot(t, x[i])
ax1.set_title('1D Brownian Motion')
ax1.set_xlabel('t')
ax1.set_ylabel('x')
ax1.grid(True)
ax2.plot(x[0],x[1])
ax2.plot(x[0,0],x[1,0], 'go') #begin
ax2.plot(x[0,-1], x[1,-1], 'ro') #end
ax2.set_title('2D Brownian Motion')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.axis('equal')
ax2.grid(True)
#Data for a three-dimensional line
zline = np.linspace(0, 15, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
ax.plot3D(xline, yline, zline, 'gray')
# Data for three-dimensional scattered points
zdata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
xdata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
ydata = brownian(x[:,0], N, dt, delta, out=x[:,1:])
ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='hot');
ax.set_title('3D Brownian Motion')
show()
return
main()
The first call to brownian makes 5 lines, since x[:, 0] has shape (5,):
brownian(x[:,0], N, dt, delta, out=x[:,1:])
So you could use any 3 of them to generate a 3D Brownian motion:
xdata, ydata, zdata = x[:3,:]
import numpy as np
from pylab import show
from math import sqrt
from scipy.stats import norm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
def brownian(x0, n, dt, delta, out=None):
# n : The number of steps to take.
# dt : time step
# delta : "speed" of motion
# out :If `out` is NOT None, it specifies the array in which to put the
# result. If `out` is None, a new numpy array is created and returned.
x0 = np.asarray(x0) #I.C
r = norm.rvs(size=x0.shape + (n,), scale=delta*sqrt(dt)) #generate n numbers for sample
if out is None: #create out array
out = np.empty(r.shape)
np.cumsum(r, axis=-1, out=out) #cumulative sum for random variables
out += np.expand_dims(x0, axis=-1)#initial condition.
return out
def main():
fig = plt.figure(1) #prepare plot
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax = fig.add_subplot(2, 3, 3, projection='3d')
delta = 2 # The Wiener process parameter.
T = 10.0
N = 500# Number of steps.
dt = T/N
m = 5 # Number of "lines"
x = np.empty((m,N+1))# Create an empty array to store the realizations.
x[:, 0] = 0# Initial values of x.
brownian(x[:,0], N, dt, delta, out=x[:,1:])
t = np.linspace(0.0, T, N+1)
for i in range(m):
ax1.plot(t, x[i])
ax1.set_title('1D Brownian Motion')
ax1.set_xlabel('t')
ax1.set_ylabel('x')
ax1.grid(True)
ax2.plot(x[0],x[1])
ax2.plot(x[0,0],x[1,0], 'go') #begin
ax2.plot(x[0,-1], x[1,-1], 'ro') #end
ax2.set_title('2D Brownian Motion')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.axis('equal')
ax2.grid(True)
xdata, ydata, zdata = x[:3,:]
ax.plot3D(xdata, ydata, zdata)
ax.set_title('3D Brownian Motion')
show()
return
main()
Related
Is there a function to calculate rapidly the normal vectors of each of the meshes of my grid? I'm looking to have vectors of the form n = [nx, ny, nz], for the example below it is easy to calculate it but the Mz will evolve and I have to update the vectors at each step.
import numpy as np
import matplotlib.pyplot as plt
m, n = 21, 21 #n nodes
dx, dy = 21.e-6, 21.e-6 #size x y
x = np.linspace(0, dx, n)
y = np.linspace(0, dy, m)
Mx, My = np.meshgrid(x, y)
Mz = np.zeros((m, n))
#PLOT
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(Mx*1e3, My*1e3, Mz*1e3)
ax.set_xlabel('$X~(mm)$')
ax.set_ylabel('$Y~(mm)$')
ax.set_zlabel('$Z~(mm)$')
plt.show()
I try to sample two paths with the same parameter which are sigma=1, mu=-1 and the initial position is 2. But I do not know how to sample two paths at the same time. Here is my code for single path of Brownian motion by Python:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(5)
fig = plt.figure()
T = 1
N = 501 # Number of points, number of subintervals = N-1
dt = T/(N-1) # Time step
t = np.linspace(0,T,N)
# Preallocate arrays for efficiency:
dX = [0]*N
X = [0]*N
# Initialization:
dX[0] = np.sqrt(dt)*(1*np.random.randn(0,1)-1) # Eq. (3)
X[0] = 2
for i in range(1,N):
dX[i] = np.sqrt(dt)*(1*np.random.randn()) # Eq. (3)
X[i] = X[i-1] + dX[i] # Eq. (4)
plt.plot(t, X)
plt.xlabel('Time $t$', fontsize=14)
plt.ylabel('Random Variable $X(t)$', fontsize=14)
plt.title('1D Brownian Path', fontsize=14)
axes = plt.gca()
axes.set_xlim([0,T])
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.tight_layout()
plt.show()
# Uncomment to save the image
#fig.savefig('brownian_1d.png', dpi=600)
Here's a concise way to do it:
mu = -1
sigma = 1
X0 = 2
K = 2 # number of paths
dX = np.sqrt(T/(N-1)) * (sigma*np.random.randn(N, K)+mu)
X = np.cumsum(dX, axis=0) + X0
I am trying to find the best way to use KDTree.query_ball_point function to find the concentration of particles within 'r' distance, for every particle at each step of the animation, and have the number value from this count used in the conc equation. Essentially I am trying to make the settling (stokes) velocity a function of the concentration of surrounding particles.
So far my attempts have not worked, below is the code I am working with (excluding my failed KDTree attempts)
UPDATE: I have added in the def "concentration" to evaluate the concentration of nearest neighboring particles at every step of the animation. Additionally, I have changed the "r" (radius) to be constant so there is no variation in the size of particles so that the effects of the concentration of neighboring particles on the particle settling velocity can be observed more clearly (currently not working at all).
The problem: I can't figure out what is wrong with def concentration. there seems to be a bug, as the animation will not run.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
import pdb
from scipy import spatial
from scipy.spatial import KDTree
n=5
sigma=0.01
#m = np.random.uniform(n)
pp = 2.56 # pp = particle density (Sphene=3.53) (Feldspar=2.56)
#(g/cm^3)
pf = 2.7 # pf = fluid density(g/cm^3)
g = 9.8 # g = gravity (m/s^2)
r = 0.003 # r = radius of sphere (meter)
mu = 0.53 # mu = dynamic viscosity of fluid (log10Poise)
rp = 0.0005 #radius around particle to check for nearest neighbor
fig, ax = plt.subplots()
az = plt.axes(xlim=(-1, 1), ylim=(-100, 0))
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
#pdb.set_trace()
def v_stokes(pp,pf,g,r,mu):
top=2*(pp-pf)*g*(r**2)
bottom=9*mu
ws=top/bottom
return ws
def init():
ax.set_xlim( -2, 2)
ax.set_ylim(-10, 0)
return ln,
def concentration(xdata, ydata, rp):
pdb.set_trace()
coords = list(zip(xdata, ydata))
tree = spatial.KDTree(coords)
test = np.column_stack([xdata, ydata])
nnl = tree.query_ball_point(test, rp) #nearest neighbors as a list
#(had tree in here before test but shape was wrong)
nnt = np.zeros(len(nnl)) #nearest neighbors total
for i in range(len(nnt)):
nnt[i] = len(nnl[i])
return nnt,
def update(frame):
global xdata
global ydata
global rp
global concentration
xdata = xdata + np.random.normal(0, sigma, n)
#ydata = ydata + np.random.normal(-0.1, sigma, n)
wss = v_stokes(pp,pf,g,r,mu)
if frame == 0.0:
ydata = np.zeros(len(xdata)) #makes the ydata length = xdata at
#time 0
cp = concentration(xdata, ydata, rp)
ydata = ydata + (wss*(1-cp))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True, interval=50)
plt.show()
Below is the code that I started with, basic animation of particles.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.animation import FuncAnimation
import random
n=100
sigma=0.1
nt=2000
fig, ax = plt.subplots()
xdata, ydata = [0.0], [0.0]
ln, = plt.plot([], [], 'ro', animated=True)
def init():
ax.set_xlim( -5, 5)
ax.set_ylim(-10, 0)
return ln,
def update(frame):
global xdata
global ydata
xdata = xdata + np.random.normal(0, sigma, n)
ydata = ydata + np.random.normal(-0.1, sigma, n)
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True, in)
plt.show()
I have the following code which produces a cylinder-like object using matplotlib:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi,nz=7,20
r=1 # radius of cylinder
phi = np.linspace(0,360, nphi)/180.0*np.pi
z= np.linspace(0,1.0,nz)
print z
cols=[]
verts2 = []
for i in range(len(phi)-1):
cp0= r*np.cos(phi[i])
cp1= r*np.cos(phi[i+1])
sp0= r*np.sin(phi[i])
sp1= r*np.sin(phi[i+1])
for j in range(len(z)-1):
z0=z[j]
z1=z[j+1]
verts=[]
verts.append((cp0, sp0, z0))
verts.append((cp1, sp1, z0))
verts.append((cp1, sp1, z1))
verts.append((cp0, sp0, z1))
verts2.append(verts)
value=np.random.rand()
#print value
col=plt.cm.rainbow(0.9)
#print col
cols.append(col)
poly3= Poly3DCollection(verts2, facecolor=cols,edgecolor = "none" )
poly3.set_alpha(0.8)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 1)
plt.show()
This code produces the following image:
However as you can see the are sharp corners in the figure. Is there anyway to make these edges rounder so that the figure looks like a proper cylinder with a circular cross-section as opposed to a hexagonal cross-section?
The third argument to
np.linspace
controls how many values you want it to generate. Thus, nphi controls the
number of values in phi, and nz controls the number of values in z:
phi = np.linspace(0,360, nphi)/180.0*np.pi
z = np.linspace(0,1.0,nz)
So if you increase nphi, then you'll get more points along the circle:
cp0 = r*np.cos(phi[i])
sp0 = r*np.sin(phi[i])
For example, try changing nphi, nz = 7,20 to nphi, nz = 70, 2.
Note that there is no need for nz to be greater than 2 since the sides of the
cylinder are flat in the z direction.
By the way, the double for-loop can be replaced by:
PHI, Z = np.meshgrid(phi, z)
CP = r * np.cos(PHI)
SP = r * np.sin(PHI)
XYZ = np.dstack([CP, SP, Z])
verts = np.stack(
[XYZ[:-1, :-1], XYZ[:-1, 1:], XYZ[1:, 1:], XYZ[1:, :-1]], axis=-2).reshape(-1, 4, 3)
So, for example,
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi, nz = 70, 2
r = 1 # radius of cylinder
phi = np.linspace(0, 360, nphi) / 180.0 * np.pi
z = np.linspace(0, 1.0, nz)
PHI, Z = np.meshgrid(phi, z)
CP = r * np.cos(PHI)
SP = r * np.sin(PHI)
XYZ = np.dstack([CP, SP, Z])
verts = np.stack(
[XYZ[:-1, :-1], XYZ[:-1, 1:], XYZ[1:, 1:], XYZ[1:, :-1]], axis=-2).reshape(-1, 4, 3)
cmap = plt.cm.rainbow
cols = cmap(np.random.random())
poly3 = Poly3DCollection(verts, facecolor=cols, edgecolor="none")
poly3.set_alpha(0.8)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 1)
plt.show()
yields
I have the following problem:
a have N points on a sphere specified by a array x, with x.shape=(N,3). This array contains their cartesian coordinates. Furthermore, at each point, I have a specified temperature. This quantity is saved in an array T, with T.shape=(N,).
Is there any straight forward way to map this temperature distribution into the plane using different colors?
If it simplifies the task, the position can also be given in polar coordinates (\theta,\phi).
To plot your data, you can use Basemap. The only problem is, that both contour and contourf routines needs gridded data. Here is example with naive (and slow) IDW-like interpolation on sphere. Any comments are welcome.
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
def cart2sph(x, y, z):
dxy = np.sqrt(x**2 + y**2)
r = np.sqrt(dxy**2 + z**2)
theta = np.arctan2(y, x)
phi = np.arctan2(z, dxy)
theta, phi = np.rad2deg([theta, phi])
return theta % 360, phi, r
def sph2cart(theta, phi, r=1):
theta, phi = np.deg2rad([theta, phi])
z = r * np.sin(phi)
rcosphi = r * np.cos(phi)
x = rcosphi * np.cos(theta)
y = rcosphi * np.sin(theta)
return x, y, z
# random data
pts = 1 - 2 * np.random.rand(500, 3)
l = np.sqrt(np.sum(pts**2, axis=1))
pts = pts / l[:, np.newaxis]
T = 150 * np.random.rand(500)
# naive IDW-like interpolation on regular grid
theta, phi, r = cart2sph(*pts.T)
nrows, ncols = (90,180)
lon, lat = np.meshgrid(np.linspace(0,360,ncols), np.linspace(-90,90,nrows))
xg,yg,zg = sph2cart(lon,lat)
Ti = np.zeros_like(lon)
for r in range(nrows):
for c in range(ncols):
v = np.array([xg[r,c], yg[r,c], zg[r,c]])
angs = np.arccos(np.dot(pts, v))
idx = np.where(angs == 0)[0]
if idx.any():
Ti[r,c] = T[idx[0]]
else:
idw = 1 / angs**2 / sum(1 / angs**2)
Ti[r,c] = np.sum(T * idw)
# set up map projection
map = Basemap(projection='ortho', lat_0=45, lon_0=15)
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
# compute native map projection coordinates of lat/lon grid.
x, y = map(lon, lat)
# contour data over the map.
cs = map.contourf(x, y, Ti, 15)
plt.title('Contours of T')
plt.show()
One way to do this is to set facecolors by mapping your heat data through the colormap.
Here's an example:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 80)
v = np.linspace(0, np.pi, 80)
# create the sphere surface
x=10 * np.outer(np.cos(u), np.sin(v))
y=10 * np.outer(np.sin(u), np.sin(v))
z=10 * np.outer(np.ones(np.size(u)), np.cos(v))
# simulate heat pattern (striped)
myheatmap = np.abs(np.sin(y))
ax.plot_surface(x, y, z, cstride=1, rstride=1, facecolors=cm.hot(myheatmap))
plt.show()
Here, my "heatmap" is just stripes along the y-axis, which I made using the function np.abs(np.sin(y)), but anything that goes form 0 to 1 will work (and, of course, it needs to match the shapes on x, etc.