add line between points in pyvista plotter - python

I have 3d points in file.I read them :
def points_to_array(pathIn):
pointArray = []
point = []
in_file = open(pathIn, 'r')
for line in in_file.readlines():
split_line = line.strip('\n').split(' ')
for i in range(0, 3):
point.append(float(split_line[i]))
pointArray.append(point)
point = []
return pointArray
And display them this way
import pyvista as pv
plotter = pv.Plotter(window_size=(1600, 1100))
points = points_to_array("C:\points.txt")
npPointArray = np.array(points)
plotter.add_points(npPointArray, color = 'r')
I want to add a line between some points (i.e from point to point as they appear in the file)
Can I do this? how?

Here's a simple example that should help. What this example does is create some lines as defined by the lines array using the vtk format of describing cells (basically, the number of points per cell, and then the cell connectivity). In this example, we're just going to create two simple lines, but you could make more, and include as many points per line as you wish.
import numpy as np
import pyvista as pv
points = np.array([[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0]])
lines = np.hstack(([2, 0, 1],
[2, 1, 2]))
pdata = pv.PolyData(points)
pdata.lines = lines
pl = pv.Plotter()
pl.add_mesh(pdata)
pl.camera_position = 'xy'
pl.add_point_labels(points, range(4), font_size=20)
pl.show()

Related

python function to count nonzero patches in array

For a given array (1 or 2-dimensional) I would like to know, how many "patches" there are of nonzero elements. For example, in the array [0, 0, 1, 1, 0, 1, 0, 0] there are two patches.
I came up with a function for the 1-dimensional case, where I first assume the maximal number of patches and then decrease that number if a neighbor of a nonzero element is nonzero, too.
def count_patches_1D(array):
patches = np.count_nonzero(array)
for i in np.nonzero(array)[0][:-1]:
if (array[i+1] != 0):
patches -= 1
return patches
I'm not sure if that method works for two dimensions as well. I haven't come up with a function for that case and I need some help for that.
Edit for clarification:
I would like to count connected patches in the 2-dimensional case, including diagonals. So an array [[1, 0], [1, 1]] would have one patch as well as [[1, 0], [0, 1]].
Also, I am wondering if there is a build-in python function for this.
The following should work:
import numpy as np
import copy
# create an array
A = np.array(
[
[0, 1, 1, 1, 0, 1],
[0, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1]
]
)
def isadjacent(pos, newpos):
"""
Check whether two coordinates are adjacent
"""
# check for adjacent columns and rows
return np.all(np.abs(np.array(newpos) - np.array(pos)) < 2):
def count_patches(A):
"""
Count the number of non-zero patches in an array.
"""
# get non-zero coordinates
coords = np.nonzero(A)
# add them to a list
inipatches = list(zip(*coords))
# list to contain all patches
allpatches = []
while len(inipatches) > 0:
patch = [inipatches.pop(0)]
i = 0
# check for all points adjacent to the points within the current patch
while True:
plen = len(patch)
curpatch = patch[i]
remaining = copy.deepcopy(inipatches)
for j in range(len(remaining)):
if isadjacent(curpatch, remaining[j]):
patch.append(remaining[j])
inipatches.remove(remaining[j])
if len(inipatches) == 0:
break
if len(inipatches) == 0 or plen == len(patch):
# nothing added to patch or no points remaining
break
i += 1
allpatches.append(patch)
return len(allpatches)
print(f"Number of patches is {count_patches(A)}")
Number of patches is 5
This should work for arrays with any number of dimensions.

How to plot band structure?

I'm running a Vasp calculation with pyiron. I can easily plot the total density of states by accessing the ElectronicStructure and Dos objects, e.g.
from pyiron.project import Project
pr = Project('tmp')
pr.remove_jobs(recursive=True)
vasp = pr.create_job(pr.job_type.Vasp, 'vasp')
vasp.structure = pr.create_ase_bulk('Al')
vasp.run()
dos = vasp.get_electronic_structure().get_dos()
dos.plot_total_dos()
Is there a similarly convenient way of plotting the band structure hiding somewhere?
Although there isn't a direct plot function, the band structure can be plot using the eigenvalue matrix
import matplotlib.pylab as plt
plt.plot(vasp.get_electronic_structure().eigenvalue_matrix);
Or to plot it manually you could use:
# The trace is system dependent, in this example we use:
trace = np.array([[0, 0, 0], # Gamma
[1, 0, 0], # X
[1, 1, 0], # M
[0, 0, 0], # Gamma
[0, 0, 1], # Z
[1, 0, 1], # R
[1, 1, 1], # A
[0, 0, 1]]) # Z
label_ticks = ['$\Gamma$', 'X', 'M', '$\Gamma$', 'Z', 'R', 'A', 'Z']
energy = ham['output/electronic_structure/eig_matrix']
E_f = ham_chg['output/electronic_structure/efermi']
energy -= E_f
n_kpoints = len(energy)
n_trace = int(n_kpoints / (len(trace)-1))
normal_ticks = [i*n_trace for i in range(len(trace))]
plt.axhline(y=0, ls='--', color='k')
plt.plot(energy, 'r-')
plt.xlim(normal_ticks[0], normal_ticks[-1])
plt.xticks(normal_ticks, label_ticks)
plt.grid(axis='x')
plt.ylabel("Energy - $E_F$ [eV]")
plt.ylim(-1, 1);
This requires two VASP calculation, first you calculate the charge density:
ham_1.write_charge_density = True
And after this job is executed you use the charge density to calculate the band structure by restarting from the previous job:
ham_2 = ham.restart_from_charge_density(job_name="job_band", icharg=11)
But to my knowledge we currently have no automated functionality for this.
If you are using VASP to calculate the band structure, a friendly tool named VASPKIT will help you plot it easily! Here's a link!
After your installation is complete, open vaspkit in your folder, then type: 21
211) Band-Structure
212) Projected Band-Structure of Only-One-Selected Atom
213) Projected Band-Structure of Each Element
214) Projected Band-Structure of Selected Atoms
215) Projected Band-Structure by Element-Weights
216) The Sum of Projected Band for Selected Atoms and Orbitals
Then you can follow the prompts to automatically draw the energy band diagram.

How to set absolute position of GLMeshItem in 3D pyqtgraph implementation

I am building a visualizer for some data and want to use 3D spheres plotted in pyqtgraphs 3D OpenGL components to represent targets identified within the data provided.
I am able to generate the spheres and move them using GLMeshItem.translate() commands, however I am not able to find a convenient way of setting coordinates of the sphere without first getting the current position of said spheres through a call to .transform() and then generating a translate command from it's current position to the new absolute coordinates I would like it to be moved to. It may be that that is the only way to accomplish this, I just suspect there is a more direct set the mesh items absolute coordinates that I just can't seem to identify.
The following code shows a basic framework for what I am doing, and also the current method I am using to move the sphere.
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.showMaximized()
w.setWindowTitle('pyqtgraph example: GLMeshItem')
w.setCameraPosition(distance=40)
g = gl.GLGridItem()
g.scale(2,2,1)
w.addItem(g)
verts = np.array([
[0, 0, 0],
[2, 0, 0],
[1, 2, 0],
[1, 1, 1],
])
faces = np.array([
[0, 1, 2],
[0, 1, 3],
[0, 2, 3],
[1, 2, 3]
])
colors = np.array([
[1, 0, 0, 0.3],
[0, 1, 0, 0.3],
[0, 0, 1, 0.3],
[1, 1, 0, 0.3]
])
md = gl.MeshData.sphere(rows=4, cols=4)
colors = np.ones((md.faceCount(), 4), dtype=float)
colors[::2,0] = 0
colors[:,1] = np.linspace(0, 1, colors.shape[0])
md.setFaceColors(colors)
m3 = gl.GLMeshItem(meshdata=md, smooth=False)#, shader='balloon')
w.addItem(m3)
target = gl.MeshData.sphere(4,4,10)
targetMI = gl.GLMeshItem(meshdata = target, drawFaces = True,smooth = False)
w.addItem(targetMI)
while(1):
targetMI.translate(0.1,0,0)
app.processEvents()
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
As can be seen in this example. translate works fine for moving relative to the current position. I am just curious as to whether there is a method for doing an absolute position move on the GLMeshItem (in this case targetMI) such that I could make it move to a coordinate without having to first get the transform and then calculating the translate required to move to the desired coordinates.
An option is to reset the item’s transform to an identity transformation by resetTransform(), before you set the absolute position by translate(). e.g:
targetMI.resetTransform()
targetMI.translate(10, 0, 0)

Adding an animation to a random walk plot [Python]

I have written a code that plots random walks. There are traj different random walks generated and each consists of n steps. I would like to animate their moves. How can I do that?
My code below:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def random_walk_2D(n, traj = 1):
for i in range(traj):
skoki = np.array([[0, 1], [1, 0], [-1, 0], [0, -1]])
losy = np.random.randint(4, size = n)
temp = skoki[losy, :]
x = np.array([[0, 0]])
temp1 = np.concatenate((x, temp), axis = 0)
traj = np.cumsum(temp1, axis = 0)
plt.plot(traj[:, 0], traj[:, 1])
plt.plot(traj[-1][0], traj[-1][1], 'ro') #the last point
plt.show()
As it stands now, you generate traj in one shot. I mean that traj in traj = np.cumsum(temp1, axis = 0) already contains all the "story" from the beginning to the end. If you want to create an animation that is in "real time", you should not generate traj in one shot, but iteratively, plotting new steps as they come. What about doing:
import numpy as np
import matplotlib.pyplot as plt
def real_time_random_walk_2D_NT(
nb_steps, nb_trajs, with_dots=False, save_trajs=False, tpause=.01
):
"""
Parameters
----------
nb_steps : integer
number of steps
nb_trajs : integer
number of trajectories
save_trajs : boolean (optional)
If True, entire trajectories are saved rather than
saving only the last steps needed for plotting.
False by default.
with_dots : boolean (optional)
If True, dots representative of random-walking entities
are displayed. Has precedence over `save_trajs`.
False by default.
tpause : float (optional)
Pausing time between 2 steps. .01 secondes by default.
"""
skoki = np.array([[0, 1], [1, 0], [-1, 0], [0, -1]])
trajs = np.zeros((nb_trajs, 1, 2))
for i in range(nb_steps):
_steps = []
for j in range(nb_trajs):
traj = trajs[j,:,:]
losy = np.random.randint(4, size = 1)
temp = skoki[losy, :]
traj = np.concatenate((traj, temp), axis = 0)
traj[-1,:] += traj[-2,:]
_steps.append(traj)
if save_trajs or with_dots:
trajs = np.array(_steps)
if with_dots:
plt.cla()
plt.plot(trajs[:,i, 0].T, trajs[:,i, 1].T, 'ro') ## There are leeway in avoiding these costly transpositions
plt.plot(trajs[:,:i+1, 0].T, trajs[:,:i+1, 1].T)
else:
plt.plot(trajs[:,-1+i:i+1, 0].T, trajs[:,-1+i:i+1, 1].T)
else:
trajs = np.array(_steps)[:,-2:,:]
plt.plot(trajs[:,:, 0].T, trajs[:,:, 1].T)
plt.pause(tpause)
real_time_random_walk_2D_NT(50, 6, with_dots=True)
real_time_random_walk_2D_NT(50, 6)

Make a dot plot in Python using a data file that consists of list of arrays

I have two arrays (of equal length) that define the length of x- and y-axis.
for e.g:
x_array = np.arange(xmin, xmax, xbins)
y_array = np.arange(ymin, ymax, ybins)
With this I can plot an xy-grid. On every bin (x_{i} - x_{i-1},y_{i} - y_{i-1}) of this grid I want a dot plot of the frequency of occurrence of some quantity z. I already know the frequency of occurrence of z but it is in this format:
(array([9460, 3, 172, 76, 79, 121, 201, 0, 115, 0, 0,
0, 0, 0]) array([9460, 3, 172, 76, 79, 121, 201, 0, 115, 0, 0,
0, 0, 0]))
To plot this frequency on top of the grid I need to do something like this:
for x in x_array:
for y in y_array:
plot(x,y,data[x][y])
but I can read the data only as lines not as arrays. How do I get around this?
The snippet above - (array([455127, 36160, etc - is this the actual text content of the file? You might be able to load the data like
import ast
with open('test_hist.txt') as inf:
# grab the file contents as string
text = inf.read()
# make it look like Python instead of PHP
text = "[" + text.replace('(', '').replace('array', '').replace(')', '') + "]"
# parse string to data structure
data = ast.literal_eval(text)
Edit: ok, the data is pre-binned; after a quick look at the matplotlib gallery I found a close match to your desired output using the pyplot.hist2d() function. We can modify it to use your data like so:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from numpy import arange
height, width = len(data), len(data[0])
xs, ys, weights = [], [], []
for yval, row in zip(arange(0., 30., 0.3), data):
for xval, weight in zip(range(0, 10000, 100), row):
xs.append(xval)
ys.append(yval)
weights.append(weight)
plt.hist2d(xs, ys, bins=[width, height], weights=weights, norm=LogNorm())
plt.colorbar()
plt.show()
which (on the given sample data) produces
Edit 2: fixed axis ranges!

Categories

Resources