I used the following code to try and plot a 3D plane of on its x axis the height (is also the momentum arm) and on its y axis the mass (in kg) which has a linear connection with the force used. The Z axis is the resulting momentum.
Unfortunately I get the following error:
ValueError: Argument Z must be 2-dimensional.
However I do believe that Z, thus the momentum is dependent on both the mass and the height, thus is Z 2d.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
g = 9.81
m = np.linspace(0, 1, 301)
H_1 = np.arange(100, 401, 1)
for i, kg in enumerate(m):
Fg = -m[i]*g
M = np.zeros(len(H_1))
for i, mm in enumerate(H_1):
F1 = np.array([0, Fg])
F2 = np.array([Fg * np.sin(np.arctan(200 / H_1[i])), Fg * np.cos(np.arctan(200 / H_1[i]))])
Fres = np.add(F1, F2)
M_arm = np.array([0, H_1[i]])
M[i] = np.cross(M_arm, Fres)/10e3
x,y = np.meshgrid(H_1,m)
z = M
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x, y, z)
ax.set_xlabel('hoogte toren in (mm)')
ax.set_ylabel('massa')
ax.set_zlabel('momentum')
plt.show()
Check this out:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
g = 9.81
m = np.linspace(0, 1, 301)
H_1 = np.arange(100, 401, 1)
Fg = -m * g
M = np.zeros(len(H_1))
for i, mm in enumerate(H_1):
F1 = np.array([0, Fg[i]])
F2 = np.array([Fg[i] * np.sin(np.arctan(200 / H_1[i])), Fg[i] * np.cos(np.arctan(200 / H_1[i]))])
Fres = np.add(F1, F2)
M_arm = np.array([0, H_1[i]])
M[i] = np.cross(M_arm, Fres) / 10e3
x, y = np.meshgrid(H_1, m)
z = M
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x, y, z)
ax.set_xlabel('hoogte toren in (mm)')
ax.set_ylabel('massa')
ax.set_zlabel('momentum')
plt.show()
I made the following changes to your code:
Moved the calculation of Fg outside the loop.
Used the correct index i to access the elements of m and Fg in the loop.
Used the correct array index i to access the elements of H_1 in the loop.
Removed the unnecessary enumerate call in the inner loop.
Used the correct array index i to access the elements of M in the loop.
These changes should fix the errors in your code and produce the expected plot.
:)
Related
This is the real function I am looking to represent in 3D:
y = f(x) = x^2 + 1
The complex function would be as follows:
w = f(z) = z^2 + 1
Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing.
We get:
f(x + iy) = x^2 + 2xyi - y^2 + 1
So:
u = x^2 - y^2 + 1
and v = 2xy
This u is what is being used in the code below.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
X, Y = np.meshgrid(x,y)
U = (X**2) - (Y**2) + 1
fig = plt.figure(dpi = 300)
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike.
Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well.
I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes.
The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with.
I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students.
Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(z):
return z**2 + 1
def freal(x, y):
return x**2 - y**2 + 1
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
yproj = 0 # value of y for which to project xu axes
xproj = 0 # value of x to project onto yu axes
X, Y = np.meshgrid(x,y)
Z = X + 1j * Y
W = f(Z)
U = W.real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, alpha=0.7)
# xu projection
xuproj = freal(x, yproj)
ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5)
ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5)
# yu projection
yuproj = freal(xproj, y)
ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5)
ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5)
# partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s
x = np.linspace(-3, 3, 150)
y = np.linspace(0, 3, 150)
X, Y = np.meshgrid(x,y)
U = f(X + 1j*Y).real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, cmap=cm.jet)
ax.set_box_aspect( (np.diff(ax.get_xlim())[0],
np.diff(ax.get_ylim())[0],
np.diff(ax.get_zlim())[0]))
#ax.set_aspect('equal')
plt.show()
gives this result:
and
The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this.
There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.
My half circle doesn't really look like how I expected.
Am I doing this right or am I missing something pretty big here ?
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
Use np.linspace to create an array for the x values. Use many points to create the circle
Use np.sqrt to solve for an array, instead of looping through each value.
import numpy as np
import matplotlib.pyplot as plt
# function for semicircle
def semicircle(r, h, k):
x0 = h - r # determine x start
x1 = h + r # determine x finish
x = np.linspace(x0, x1, 10000) # many points to solve for y
# use numpy for array solving of the semicircle equation
y = k + np.sqrt(r**2 - (x - h)**2)
return x, y
x, y = semicircle(6, 0, 0) # function call
plt.scatter(x, y, s=3, c='turquoise') # plot
plt.gca().set_aspect('equal', adjustable='box') # set the plot aspect to be equal
Answering my own question.
Plotting the output of the code:
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
[-5, 3.3166247903554]
[-4, 4.47213595499958]
[-3, 5.196152422706632]
[-2, 5.656854249492381]
[-1, 5.916079783099616]
[0, 6.0]
[1, 5.916079783099616]
[2, 5.656854249492381]
[3, 5.196152422706632]
[4, 4.47213595499958]
Looking at the output of the coordinates, it doesn't appear to be a circle.
But if we take our equation and our coordinates and graph them on this website we see that they are indeed a circle. It's an optical illusion that they aren't. Partially because the graph is not evenly displayed and also because plotting points in a range function with steps of 1 (line 13) doesn't plot points at equal arc length distances away from each other.
I am trying to make a 3D quiver plot using matplotlib 2.2.5. I provide a simple sample code below to show my attempt. I want to make a quiver plot using fx(x,y,z), fy(x,y,z) and fz(x,y,z). These correspond to the vector field f = (fx,fy,fz). I input the data for fx,fy,fz as 3 .dat files where each .dat file is 1 column of numbers.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt #I'm using matplotlib 2.2.5
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
fx = np.genfromtxt('fx.dat')
fy = np.genfromtxt('fy.dat')
fz = np.genfromtxt('fz.dat')
N = 10
Fx = fx[::N,::N]
Fy = fy[::N,::N]
Fz = fz[::N,::N]
nrows, ncols = Fx.shape
nx = 1
ny = 1
nz = 1
x = np.linspace(-nx, nx, ncols)
y = np.linspace(-ny, ny, ncols)
z = np.linspace(-nz, nz, ncols)
xi, yi, zi = np.meshgrid(x, y, z, indexing='ij')
plt.quiver(xi, yi, zi, Fx, Fy, Fz, edgecolor='k', facecolor='black', linewidth=.5)
plt.axis('scaled')
plt.show()
The data is generated and stored as follows (using fortran):
!generate data
DO i = -nx,nx !nx = 1
DO j = -ny,ny !ny = 1
DO k = -nz,nz !nz = 1
fx(i,j,k) = i + 2.*j + 3.*k !this is a function fx that depends on x,y,z; fx(x,y,z)
fy(i,j,k) = i - 4.*j + k !this is a function fy that depends on x,y,z; fy(x,y,z)
fz(i,j,k) = i + 2*j + k !this is a function fz that depends on x,y,z; fz(x,y,z)
END DO
END DO
END DO
!store data
DO i = -nx,nx
DO j = -ny,ny
DO k = -nz,nz
WRITE(1,*) fx(i,j,k) !stores fx(x,y,z)
WRITE(2,*) fy(i,j,k) !stores fy(x,y,z)
WRITE(3,*) fz(i,j,k) !stores fz(x,y,z)
END DO
END DO
END DO
I'm assuming here you are trying to plot the quivers starting from the (origin (0,0,0)).
Using a random sample with coordinates between -1 and 1 (hoping it will match your data sample), this would go this way:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
fx = np.genfromtxt('fx.dat')
fy = np.genfromtxt('fy.dat')
fz = np.genfromtxt('fz.dat')
N = 10
Fx = fx[:N]
Fy = fy[:N]
Fz = fz[:N]
Note that I used a simple ":": based on your explanation, your .dat files have only one dimension. You use multiples colon only if you have at least 2 dimensions.
plt.quiver(0, 0, 0, Fx, Fy, Fz, edgecolor='k', facecolor='black', linewidth=.5) #To plot starting from the origin
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
ax.set_zlim(-1,1)
plt.show()
If you want to plot starting from your mesh (ie np.linspace), you can alter the code as follow (though I'm not sure this is what you are trying to achieve):
nx = 1
ny = 1
nz = 1
x = np.linspace(-nx, nx, N)
y = np.linspace(-ny, ny, N)
z = np.linspace(-nz, nz, N)
plt.quiver(x, y, z, Fx, Fy, Fz, edgecolor='k', facecolor='black', linewidth=.5)
ax.set_xlim(-2,2) #I altered the bounds to match the new coordinates
ax.set_ylim(-2,2)
ax.set_zlim(-2,2)
Note: this code has been tested on matplotlib 3.0.1 only
[TLDR]:
Essentially my question boils down to how one can extract the 2d data of a plane from a 3D numpy meshgrid
[Detailed Description]:
I am calculating the electric field of two (or more) point charges. I did this in 2D and can plot the results via matplotlib using quiver or streamplot
import numpy as np
from matplotlib import pyplot as plt
eps_0 = 8e-12
fac = (1./(4*np.pi*eps_0))
charges = [1.0,-1.0]
qx = [-2.0,2.0]
qy = [0.0,0.0]
# GRID
gridsize = 4.0
N = 11
X,Y = np.meshgrid( np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N))
# CALC E-FIELD
sumEx = np.zeros_like(X)
sumEy = np.zeros_like(Y)
for q, qxi, qyi in zip(charges,qx,qy):
dist_vec_x = X - qxi
dist_vec_y = Y - qyi
dist = np.sqrt(dist_vec_x**2 + dist_vec_y**2)
Ex = fac * q * (dist_vec_x/dist**3)
Ey = fac * q * (dist_vec_y/dist**3)
sumEx += Ex
sumEy += Ey
# PLOT
fig = plt.figure()
ax = fig.add_subplot(111)
ax.streamplot(X,Y,sumEx,sumEy)
plt.show()
This produces the correct results
I can easily extend this to 3D
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
eps_0 = 8e-12
fac = (1./(4*np.pi*eps_0))
charges = [1.0,-1.0]
qx = [-2.0,2.0]
qy = [0.0,0.0]
qz = [0.0,0.0]
# GRID
gridsize = 4.0
N = 11
X,Y,Z = np.meshgrid( np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N))
# CALC E-FIELD
sumEx = np.zeros_like(X)
sumEy = np.zeros_like(Y)
sumEz = np.zeros_like(Z)
for q, qxi, qyi, qzi in zip(charges,qx,qy,qz):
dist_vec_x = X - qxi
dist_vec_y = Y - qyi
dist_vec_z = Z - qzi
dist = np.sqrt(dist_vec_x**2 + dist_vec_y**2 + dist_vec_z**2)
Ex = fac * q * (dist_vec_x/dist**3)
Ey = fac * q * (dist_vec_y/dist**3)
Ez = fac * q * (dist_vec_z/dist**3)
sumEx += Ex
sumEy += Ey
sumEz += Ez
# PLOT
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(X,Y,Z,sumEx,sumEy,sumEz, pivot='middle', normalize=True)
plt.show()
This also yields the correct result when plotted in 3D (as far as I can tell)
But for some reason I can not figure out how to extract the data from one x-y plane from the generated 3D numpy mesh. I thought I could just do something like
zplane = round(N/2)
ax.quiver(X,Y,sumEx[:,:,zplane],sumEy[:,:,zplane])
but this does not do the trick. Does anyone know the proper way here?
Remove projection='3d' and index X and Y:
fig = plt.figure()
ax = fig.gca()
zplane = round(N / 2)
ax.quiver(X[:, :, zplane], Y[:, :, zplane], sumEx[:, :, zplane], sumEy[:, :, zplane])
plt.show()
If you select a specific zplane your plot is no longer a 3D-plot.
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