跳转至

Pyvista库

约 505 个字 174 行代码 3 张图片 预计阅读时间 4 分钟

安装:pip install pyvista

创建Mesh对象

PolyData数据结构

这种方式绘制的是mesh face,而不是实体单元体。

import pyvista as pv
import numpy as np
points = np.array([[0, 0, 0], [1, 0, 0], [0.5, 0.5, 0]])
cells = [[3, 0, 1, 2]]
mesh = pv.PolyData(points, cells)
mesh.plot(cpos='xy',show_edges=True)

UnstructuredGrid数据结构

cells = [4, 0, 1, 2, 3] # 四面体单元,第一列为单元节点数,后面为节点编号
celltypes = [pv.CellType.TETRA] # 单元类型,与cells一一对应
points = [
    [1.0, 1.0, 1.0],
    [1.0, -1.0, -1.0],
    [-1.0, 1.0, -1.0],
    [-1.0, -1.0, 1.0],
]
grid = pv.UnstructuredGrid(cells, celltypes, points)
grid.plot(show_edges=True)

导入vtk文件

import pyvista as pv
mesh = pv.read('CRACK.vtk')
mesh.plot(cpos='xy',cmap='coolwarm')

导入inp文件

import pyvista as pv
mesh = pv.read_meshio('C3D4test2.inp')
mesh.plot(cpos='xy',cmap='coolwarm')

常见查询方法

import pyvista as pv
mesh = pv.read('CRACK.vtk')

print('node number = ',mesh.n_points)
print('element number = ',mesh.n_cells)
print('node coordinate = ',mesh.points[0])
print('point_data = ',mesh.point_data)

输出:

node number =  66429
element number =  65726
node coordinate =  [-19.362562   0.057323   0.      ]
point_data =  pyvista DataSetAttributes
Association     : POINT
Active Scalars  : S11
Active Vectors  : U
Active Texture  : None
Active Normals  : None
Contains arrays :
    U                       float32    (66429, 3)           VECTORS
    RF                      float32    (66429, 3)
    S11                     float32    (66429,)             SCALARS
    S22                     float32    (66429,)
    S12                     float32    (66429,)
    Mises                   float32    (66429,)
  • 查看有单元数:mesh.n_cells
  • 查看节点数:mesh.n_points
  • 节点坐标:mesh.points
  • 单元编码:mesh.cells(缩并为一维数组)
  • 查看 SCALARS(云图变量种类)数:mesh.n_arrays
  • 查看 point data 有哪些:mesh.point_data
  • 查看 point data 具体的值:mesh.point_data['Displacement']
  • 查看 cell data 有哪些:mesh.cell_data
  • 查看 cell data 具体的值:mesh.cell_data['AxialForce']
  • 也可以直接查看:mesh['Displacement']mesh['AxialForce']
  • 也可以自行添加 cell data 或者 point data:mesh.point_data['S11'] = s11

绘图方法

绘图方法有两种:mesh.plotpyvista.Plotter.add_mesh,个人喜欢后者,因为可以不断增添新特征,以后的教学都以后者为基础。

mesh.plot

import pyvista as pv
mesh = pv.read_meshio('C3D4test2.inp')
mesh.plot(cpos='xy',cmap='coolwarm')

pyvista.Plotter.add_mesh

import pyvista as pv
mesh = pv.read_meshio('C3D4test2.inp')
mesh.plot(cpos='xy',cmap='coolwarm')

云图绘制

import pyvista as pv
mesh = pv.read('C3D4.vtk')

dargs = dict(
scalars="U",
show_scalar_bar=False,
show_vertices=False,
show_edges=False,
# vertex_color='red',
# point_size=1,
)
pl = pv.Plotter(shape=(2, 2))

pl.add_mesh(mesh.copy(),**dargs)
pl.add_text("Magnitude Displacement", color='k')
pl.subplot(1, 0)

pl.add_mesh(mesh.copy(),component=0, line_width=2,**dargs)
pl.add_text("X Displacement", color='k')
pl.subplot(0, 1)

pl.add_mesh(mesh.copy(),component=1,render_points_as_spheres=True,**dargs)
pl.add_text("Y Displacement", color='k')
pl.subplot(1, 1)

pl.add_mesh(mesh.copy(),component=2,**dargs)
pl.add_text("Z Displacement", color='k')

pl.link_views() # link all the views
pl.show()

以上 Python 语句,相信大家只要稍微看一下就能明白。在绘图的时候可以单独设置绘图参数,比如这样: plotter.add_mesh(mesh, scalars='Displacement', component=0,也可以使用上述代码的形式,将绘图参数集中在 dargs 的设置中。注意有以下几点:

  1. component=0 表示第一个分量,如果不设置的话,就表示合量 Magnitude
  2. 上图的节点显示看上去有很多没有显示,在实际绘图过程中,将图片逐渐放大后,每个节点的编号都会显示出来。
  3. render_points_as_spheres=True 表示将点的形状转换为 3D 圆球状,更具观赏性。
  4. show_scalar_bar=False 是为了后续定制 colorbar,如果不设置为 False,后面在你定制 colorbar 时,将会出现两个 colorbar。

自定义colorbar&cmap

Tip

若要实现模型云图颜色分段效果,需要将matplotlib内置的cmap进行“分段处理”。

import pyvista as pv
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.pyplot as plt

def create_custom_cmap(cmap_name='coolwarm', num_colors=12):
    """
    根据给定的配色方案名称,创建一个归一化的LinearSegmentedColormap。

    :param cmap_name: 配色方案的名称(如 'coolwarm', 'jet', 'viridis' 等)
    :param num_colors: 配色方案中所需的颜色数量
    :return: 归一化后的LinearSegmentedColormap
    """
    # 获取指定的配色图
    cmap = plt.get_cmap(cmap_name, num_colors)

    # 获取颜色并进行归一化到 [0, 1] 范围
    cmap_normalized = cmap(np.linspace(0, 1, num_colors))[:, :3]  # 只保留RGB通道

    # 创建并返回自定义配色图
    return LinearSegmentedColormap.from_list(f'{cmap_name}_custom', cmap_normalized, N=num_colors)


cmap_name = 'jet' # cmap名称
num_colors = 12 # 需要间隔的颜色段数
custom_cmap = create_custom_cmap(cmap_name, num_colors)

mesh = pv.read('C3D4.vtk')
dargs = dict(
scalars="U",
show_scalar_bar=False,
# show_vertices=True,
# show_edges=True,
# edge_color='#000080',
# vertex_color='red',
# point_size=1,
cmap = custom_cmap,
# render_points_as_spheres=True
)
pl = pv.Plotter()
pl.add_mesh(mesh.copy(),**dargs)
pl.add_scalar_bar(title="U",n_labels=13,n_colors=12,fmt='%.3e',vertical=True,
width=0.05,position_x=0.03, position_y=0.5,font_family='times')
pl.show()

罗盘标志

# 罗盘标志1
pl.add_axes(line_width=5,
cone_radius=0.6,
shaft_length=0.7,
tip_length=0.3,
ambient=0.5,
label_size=(0.4, 0.16))
# 罗盘标志2
pl.add_camera_orientation_widget()

缩放因子

pl.set_scale(xscale=2,yscale=4,zscale=2)

嵌入PySide6

示例代码:

import sys
import pyvista as pv
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from pyvistaqt import QtInteractor


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("PyVista in PySide6")
        self.setGeometry(100, 100, 800, 600)

        # 创建一个 QWidget 作为中央窗口
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)

        # 创建一个垂直布局
        layout = QVBoxLayout()
        self.central_widget.setLayout(layout)

        # 创建 PyVista 渲染窗口
        self.plotter = QtInteractor(self.central_widget)
        layout.addWidget(self.plotter)

        # 画一个示例 3D 对象
        self.add_example_plot()

    def add_example_plot(self):
        sphere = pv.Sphere()
        self.plotter.add_mesh(sphere, color="cyan", show_edges=True)
        self.plotter.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())