matplotlib : plot with periodic boundary - python

I have a system composed of N elliptical particles in a box with periodic boundary conditions:
import numpy as np
import matplotlib.pyplot as plt
import numpy.random as rnd
from matplotlib.patches import Ellipse
import time
infile = open ('init.txt')
N_p = 100
x, y = [], []
m = []
for i in xrange(N_p):
data = infile.readline()
raw = data.split()
x.append(float(raw[0]))
y.append(float(raw[1]))
m.append(float(raw[4]))
xnp = np.array(x)
ynp = np.array(y)
mnp = np.array(m)
fig = plt.figure(0)
ax = fig.add_subplot(111, aspect='equal')
for x, y, m in zip(xnp, ynp, mnp):
ell = Ellipse(xy=(x, y), width = 4.0, height = 2.0, angle = m)
ell.set_facecolor('blue')
ax.add_artist(ell)
ell.set_clip_box(ax.bbox)
ax.set_xlim(0, 100)
ax.set_ylim(0, 100)
fig.savefig("init.png")
plt.show()
As it is seen from the image, the ellipses are cut in the boundary, but I want to show the remainder of the ellipse in the other side of the box. How can I do this?

Related

FEA Stress plot in Python from 3 1D-arrays

I have 3 1D arrays (node x-coordinates, node y-coordinates and Von-Mises stress scalar) exported from an FEA solver.
I want to create 2D contour plots as shown below in Python:
Stress plot example
I have managed to create such plot as shown below:
Stress plot result
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
for orient in ['top', 'bot', 'side']:
x = []
y = []
z = []
stress = []
with open('data.txt') as file:
for line in file:
cur_line = line.split('\t')
cur_x_old = cur_line[0]
cur_y_old = cur_line[1]
cur_z_old = cur_line[2]
cur_s_old = cur_line[3]
if cur_x_old == 'X Location (mm)':
pass
else:
cur_x = cur_x_old.replace(",",".")
cur_y = cur_y_old.replace(",",".")
cur_z = cur_z_old.replace(",",".")
cur_s = cur_s_old.replace(",",".")
x.append(float(cur_x))
y.append(float(cur_y))
z.append(float(cur_z))
stress.append(float(cur_s))
stress = np.array(stress)
x = np.array(x)
y = np.array(y)
z = np.array(z)
levels=np.linspace(stress.min(), stress.max(), num=100)
triang = tri.Triangulation(x, y)
if orient == 'side':
plt.figure(figsize = (max(x)/50, abs(min(y))/50))
plt.tricontourf(triang, stress, cmap = 'jet', norm = mpl.colors.Normalize(0, 100), levels = levels, extend = 'max')
plt.scatter(x, y, color = 'k')
else:
plt.figure(figsize = (max(x)/50, max(z)*2/50))
plt.tricontourf(x, z, stress, cmap = 'jet', norm = mpl.colors.Normalize(0, 100), levels = levels)
My problem is that by triangulating the data, unwanted triangles are generated at the edge of the mesh (see Stress plot result). The black dots are the scatter plot from x and y coordinates. I want the colour plot to be only inside the boundaries of the grid. Is there a way to remove these unwanted triangles?

Embed subplot in cartopy map

I want to embed subplots canvas inside a cartopy projected map. I wrote this code to show the expected result by using rectangles:
#%%
import numpy as np
import cartopy as cr
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from cartopy.io import shapereader
import geopandas
resolution = '10m'
category = 'cultural'
name = 'admin_0_countries'
shpfilename = shapereader.natural_earth(resolution, category, name)
# read the shapefile using geopandas
df = geopandas.read_file(shpfilename)
# read the country borders
usa = df.loc[df['ADMIN'] == 'United States of America']['geometry'].values[0]
can = df.loc[df['ADMIN'] == 'Canada']['geometry'].values[0]
central_lon, central_lat = -80, 60
extent = [-85, -55, 40, 62]
# ax = plt.axes(projection=ccrs.Orthographic(central_lon, central_lat))
#Golden ratio
phi = 1.618033987
h = 7
w = phi*h
fig = plt.figure(figsize=(w,h))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
#Set map extent
ax.set_extent(extent)
ax.set_xticks(np.linspace(extent[0],extent[1],11))
ax.set_yticks(np.linspace(extent[2],extent[3],6))
ax.add_geometries(usa, crs=ccrs.PlateCarree(), facecolor='none',
edgecolor='k')
# ax.gridlines()
ax.coastlines(resolution='50m')
nx, ny = 7,6
#Begin firts rectangle
xi = extent[0] + 0.5
yi = extent[2] + 0.5
x, y = xi, yi
#Loop for create the plots grid
for i in range(nx):
for j in range(ny):
#Inner rect height
in_h = 2.8
#Draw the rect
rect = ax.add_patch(mpatches.Rectangle(xy=[x, y], width=phi*in_h, height=in_h,
facecolor='blue',
alpha=0.2,
transform=ccrs.PlateCarree()))
#Get vertex of the drawn rectangle
verts = rect.get_path().vertices
trans = rect.get_patch_transform()
points = trans.transform(verts)
#Refresh rectangle coordinates
x += (points[1,0]-points[0,0]) + 0.2
if j == ny-1:
x = xi
y += (points[2,1]-points[1,1]) + 0.2
# print(points)
fig.tight_layout()
fig.savefig('Figure.pdf',format='pdf',dpi=90)
plt.show()
This routine prints this figure
What I am looking for is a way to embed plots that match every single rectangle in the figure. I tried with fig.add_axes, but I couldn't get that mini-canvas match with the actual rectangles.
Since you want to embed the axes inside the parent axes is recommend using inset_axes, see the documentation here.
I wrote simple code to demonstrate how it works. Clearly there will be some tweaking of the inset_axes positions and sizes necessary for your desired output, but I think my trivial implementation already does decent.
All created axes instances are stored in a list so that they can be accessed later.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
axis = []
x = np.linspace(-85, -55)
y = np.linspace(40, 62)
ax.plot(x, y)
offset_l = 0.05
offset_h = 0.12
num_x = 6
num_y = 7
xs = np.linspace(offset_l, 1-offset_h, num_x)
ys = np.linspace(offset_l, 1-offset_h, num_y)
for k in range(num_x):
for j in range(num_y):
ax_ins = ax.inset_axes([xs[k], ys[j], 0.1, 0.1])
ax_ins.axhspan(0, 1, color='tab:blue', alpha=0.2)
axis.append(ax_ins)
Alternatively, you can also specify the inset_axes positions using data coordinates, for this you have to set the kwarg transform in the method to transform=ax.transData, see also my code below.
import matplotlib.pyplot as plt
import numpy as np
#Golden ratio
phi = 1.618033987
h = 7
w = phi*h
fig, ax = plt.subplots(figsize=(w, h))
axis = []
x = np.linspace(-85, -55)
y = np.linspace(40, 62)
ax.plot(x, y)
offset_l = 0.05
offset_h = 0.12
num_x = 6
num_y = 7
fig.tight_layout()
extent = [-85, -55, 40, 62]
xi = extent[0] + 0.5
yi = extent[2] + 0.5
in_h = 2.8
in_w = phi * 2.8
spacing = 0.4
for k in range(num_x):
for j in range(num_y):
ax_ins = ax.inset_axes([xi+k*(in_w + phi*spacing), yi+j*(in_h + spacing),
in_w, in_h], transform=ax.transData)
ax_ins.axhspan(0, 1, color='tab:blue', alpha=0.2)
axis.append(ax_ins)

How to rescale a plot in a subplot with matplotlib

I have 4 subplots with a different 3D plot with a colorbar.
I want to plot a XY view of my 3D plot, remove the x,y,z axis and resize my plot to use all the space available in the subplot such that the XY view has the same height as the colorbar. I can remove the axis but I do not know how to resize the image. I attached a working code to illustrate this.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')
Any idea how can I do this?
I have added
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
to your code which hides the axes and sets the view to 60% of its previous value. The result looks like this:
Full code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')

How to 4D plot with contour over cube, using matplotlib?

I would like to 4D plot over the cube (x,y,z) vs. q, using the colormap on the 3 surfaces of the cubes, where the color and contour are determined depending on the q variable. Basically, I am looking for a similar image like this:
Any help is appreciated.
See my example of 3D ABC feild
import pyvista as pv
import numpy as np
from numpy import mgrid
import matplotlib.pyplot as plt
print('initializing domain')
xmin = -800.
xmax = 800.
Lx = xmax-xmin
B0 = 1
k = 1
alpha = 2.0*np.pi*k/Lx
x, y, z = Lx*mgrid[0:1:51j, 0:1:51j, 0:1:51j]
print('initializing 3D B field')
Bx = B0*(np.sin(alpha*z) + np.cos(alpha*y))
By = B0*(np.sin(alpha*x) + np.cos(alpha*z))
Bz = B0*(np.sin(alpha*y) + np.cos(alpha*x))
B = np.column_stack((Bx.ravel(), By.ravel(), Bz.ravel()))
grid = pv.StructuredGrid(x, y, z)
grid["ABC field magnitude"] = np.linalg.norm(B, axis=1)
grid["ABC field vectors"] = B
grid.set_active_vectors("ABC field vectors")
#contours = grid.contour(8, scalars="ABC field magnitude")
#arrows = contours.glyph(orient="ABC field vectors", factor=50.0)
print('plotting')
pv.set_plot_theme('document')
p = pv.Plotter(notebook=0, shape=(1,1))
#p.background_color='white'
#p.window_size
cmap = plt.cm.get_cmap("viridis", 4)
p.add_mesh(grid, cmap=cmap)
p.show_grid()
#p.add_mesh(arrows)
#p.subplot(0,1)
#slices = grid.slice_orthogonal(x=20, y=20, z=30)
#slices = grid.slice_orthogonal()
#p.add_mesh(slices, cmap=cmap)
##p.subplot(1,0)
#p.add_mesh(contours, opacity=1)
#p.subplot(1,1)
#p.add_mesh(arrows)
#single_slice = arrows.slice(normal=[1, 1, 0])
#slices = arrows.slice_orthogonal(x=20, y=20, z=30)
#slices = grid.slice_orthogonal()
#p.add_mesh(single_slice, cmap=cmap)
p.show_grid()
p.link_views()
p.view_isometric()
p.show(screenshot='abc3d_slicing.png')
A simple answer is
import numpy as np
import matplotlib.pyplot as plt
length = 10
data = length*np.mgrid[0:1:51j, 0:1:51j, 0:1:51j].reshape(3,-1).T
contour = np.random.rand(data.shape[0])
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
data_plot = ax.scatter(data[:,0], data[:,1], data[:,2], c=contour)
fig.colorbar(data_plot)
To optimize to only boundary points
length = 10
vol_data = length*np.mgrid[0:1:51j, 0:1:51j, 0:1:51j].reshape(3,-1).T
bound_data = np.array([data_i for data_i in vol_data
if any([coord in [0, length] for coord in data_i])])
contour = np.random.rand(bound_data.shape[0])
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
data_plot = ax.scatter(bound_data[:,0], bound_data[:,1], bound_data[:,2], c=contour)
fig.colorbar(data_plot)

How to draw circle by data with matplotlib + python?

I can draw a circle by scatter, which has been shown in the image. But I want to draw them buy a line, because there are many circles in total, I need to link nodes together for a certain circle. Thanks.
I the order of the points is random, you can change X-Y to polar, and sort the data by angle:
create some random order points first:
import pylab as pl
import numpy as np
angle = np.arange(0, np.pi*2, 0.05)
r = 50 + np.random.normal(0, 2, angle.shape)
x = r * np.cos(angle)
y = r * np.sin(angle)
idx = np.random.permutation(angle.shape[0])
x = x[idx]
y = y[idx]
Then use arctan2() to calculate the angle, and sort the data by it:
angle = np.arctan2(x, y)
order = np.argsort(angle)
x = x[order]
y = y[order]
fig, ax = pl.subplots()
ax.set_aspect(1.0)
x2 = np.r_[x, x[0]]
y2 = np.r_[y, y[0]]
ax.plot(x, y, "o")
ax.plot(x2, y2, "r", lw=2)
here is the output:
Here is one way to do it. This answer uses different methods than the linked possible duplicate, so may be worth keeping.
import matplotlib.pyplot as plt
from matplotlib import patches
fig = plt.figure(figsize=plt.figaspect(1.0))
ax = fig.add_subplot(111)
cen = (2.0,1.0); r = 3.0
circle = patches.Circle(cen, r, facecolor='none')
ax.add_patch(circle)
ax.set_xlim(-6.0, 6.0)
ax.set_ylim(-6.0, 6.0)
If all you have are the x and y points, you can use PathPatch. Here's a tutorial
If your data points are already in order, the plot command should work fine. If you're looking to generate a circle from scratch, you can use a parametric equation.
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> t = np.linspace(0,2*np.pi, 100)
>>> x = np.cos(t)
>>> y = np.sin(t)
>>> plt.plot(x,y)

Categories