mcpField
P
pyvista-visualization

pyvista-visualization

github.com/soljournerデータ・データベース9541 回インストール51 週間前に更新
リポジトリ

pyvista-visualization について

pyvista-visualizationは、PyVistaでCFDメッシュや計算結果のインタラクティブな3D表示を作成するためのガイドスキルです。場データ、形状、流線を確認したいCFD技術者向けで、表示やメッシュの出力方法も扱います。

主な機能

  1. 01構造格子・非構造格子の表示
  2. 02スカラー場の等値線とボリュームレンダリング
  3. 03速度ベクトルと流線
  4. 04メッシュのスライスとクリップ
  5. 05インタラクティブなカメラと複数ビューポート
  6. 06画像とメッシュのエクスポート

活用シーン

  1. 01圧力や温度分布の確認
  2. 02速度場と流線の追跡
  3. 03複数ビューポートでのCFD条件比較
  4. 04ParaViewでの後処理に向けた結果保存

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/soljourner/claude-engineering-skills --skill pyvista-visualization

インストール後、Claude Code で/pyvista-visualizationを入力して読み込まれたか確認します。

同梱ファイルexamples.pyreference.md

詳細

CFD 向け PyVista 3D 可視化

VTK を基盤とする高水準の 3D 可視化ライブラリ PyVista を使用して、速度場、圧力コンター、流線、ポンプ形状など、数値流体力学の結果をインタラクティブな 3D 表示で作成します。

インストールとセットアップ

pip install pyvista numpy

機能を拡張するためのオプションの依存関係:

# For mesh processing and additional features
pip install meshio vtk

# For Jupyter notebook support
pip install trame jupyter-server-proxy ipywidgets

基本的なインポートパターン:

import pyvista as pv
import numpy as np

# Set plotting theme
pv.set_plot_theme('document')  # or 'default', 'dark', 'paraview'

# Enable off-screen rendering (for scripts without display)
# pv.OFF_SCREEN = True

CFD 可視化の主要機能

3D メッシュ可視化

PyVista は、CFD に欠かせないさまざまなメッシュタイプをサポートしています。

  • 構造格子:曲線座標を持つ規則格子
  • 非構造格子:任意のセルタイプ(三角錐、六面体など)
  • PolyData:サーフェスメッシュと点群
  • 一様格子:規則的なデカルト格子(ボクセル)
import pyvista as pv
import numpy as np

# Create structured grid (e.g., pump volute domain)
x = np.linspace(0, 1, 20)
y = np.linspace(0, 1, 15)
z = np.linspace(0, 0.5, 10)
X, Y, Z = np.meshgrid(x, y, z, indexing='ij')

# Create mesh
grid = pv.StructuredGrid(X, Y, Z)

# Visualize
grid.plot(show_edges=True)
スカラー場の可視化

圧力、温度、乱流強度などのスカラー量を表示します。

# Add scalar field to mesh (e.g., pressure distribution)
pressure = 101325 + 50000 * np.exp(-((X-0.5)**2 + (Y-0.5)**2 + (Z-0.25)**2) / 0.1)
grid['pressure'] = pressure.flatten(order='F')

# Create plotter with contours
plotter = pv.Plotter()
plotter.add_mesh(grid.contour(isosurfaces=10, scalars='pressure'),
                 cmap='coolwarm',
                 show_scalar_bar=True,
                 scalar_bar_args={'title': 'Pressure [Pa]'})
plotter.show()
ベクトル場の可視化(グリフ)

矢印やその他のグリフを使用して速度ベクトルを可視化します。

# Define velocity field
U = -0.5 * (Y - 0.5)
V = 0.5 * (X - 0.5)
W = 0.2 * np.sin(2 * np.pi * Z)

vectors = np.column_stack([U.flatten(order='F'),
                           V.flatten(order='F'),
                           W.flatten(order='F')])

grid['velocity'] = vectors

# Create arrow glyphs
arrows = grid.glyph(orient='velocity', scale='velocity', factor=0.1)

plotter = pv.Plotter()
plotter.add_mesh(arrows, cmap='viridis', show_scalar_bar=True,
                scalar_bar_args={'title': 'Velocity [m/s]'})
plotter.show()
流線

速度場を通る流跡線を描画します。

# Create seed points for streamlines
seed_points = pv.Disc(center=(0.5, 0.5, 0), inner=0.05, outer=0.3, normal=(0, 0, 1), r_res=6, c_res=12)

# Generate streamlines
streamlines = grid.streamlines(
    vectors='velocity',
    source_center=(0.5, 0.5, 0),
    source_radius=0.3,
    n_points=50,
    max_time=10.0
)

plotter = pv.Plotter()
plotter.add_mesh(streamlines.tube(radius=0.005), cmap='jet',
                scalar_bar_args={'title': 'Velocity Magnitude [m/s]'})
plotter.add_mesh(seed_points, color='red', point_size=10)
plotter.show()
ボリュームレンダリング

透明度を使って 3D スカラー場を表示します。

# Volume rendering for density or temperature fields
plotter = pv.Plotter()
plotter.add_volume(grid, scalars='pressure',
                  cmap='coolwarm',
                  opacity='sigmoid',  # or 'linear', custom array
                  scalar_bar_args={'title': 'Pressure [Pa]'})
plotter.show()
スライスとクリッピング

3D 領域から 2D スライスを抽出するか、領域をクリッピングします。

# Create multiple slices through domain
slices = grid.slice_orthogonal(x=0.5, y=0.5, z=0.25)

plotter = pv.Plotter()
plotter.add_mesh(slices, scalars='pressure', cmap='RdBu_r',
                show_scalar_bar=True,
                scalar_bar_args={'title': 'Pressure [Pa]'})
plotter.show()

# Clip half of domain to see internal flow
clipped = grid.clip(normal='x', value=0.5)

plotter = pv.Plotter()
plotter.add_mesh(clipped, scalars='velocity', cmap='jet',
                show_edges=True, show_scalar_bar=True)
plotter.show()

CFD の活用例

速度場の可視化
import pyvista as pv
import numpy as np

# Load or create CFD mesh
grid = pv.ImageData(dimensions=(50, 40, 30))
grid.spacing = (0.02, 0.02, 0.02)
grid.origin = (0, 0, 0)

# Simulate velocity field (e.g., pipe flow with swirl)
x, y, z = grid.points.T
r = np.sqrt((x-0.5)**2 + (y-0.4)**2)
theta = np.arctan2(y-0.4, x-0.5)

u = 2.0 * (1 - (r/0.3)**2) * (r < 0.3)  # Axial velocity
v = 0.5 * r * np.cos(theta) * (r < 0.3)  # Tangential component
w = -0.5 * r * np.sin(theta) * (r < 0.3)

grid['velocity'] = np.column_stack([u, v, w])
grid['speed'] = np.linalg.norm(grid['velocity'], axis=1)

# Visualize with streamlines and contours
plotter = pv.Plotter()

# Add velocity magnitude contours on slices
slice_y = grid.slice(normal='y', origin=(0.5, 0.4, 0.3))
plotter.add_mesh(slice_y, scalars='speed', cmap='jet',
                opacity=0.8, show_scalar_bar=True,
                scalar_bar_args={'title': 'Velocity [m/s]', 'height': 0.7})

# Add streamlines
streamlines = grid.streamlines(
    vectors='velocity',
    source_center=(0.5, 0.4, 0),
    source_radius=0.25,
    n_points=30
)
plotter.add_mesh(streamlines.tube(radius=0.003), color='white')

plotter.show()
圧力コンター
# Add pressure field (example: stagnation and wake regions)
x_norm = (x - 0.5) / 0.5
y_norm = (y - 0.4) / 0.4
z_norm = (z - 0.3) / 0.3

# Pressure distribution around obstacle
pressure = 101325 + 5000 * (1 - np.sqrt(x_norm**2 + y_norm**2 + z_norm**2))
grid['pressure'] = pressure

# Create pressure isosurfaces
plotter = pv.Plotter()
contours = grid.contour(isosurfaces=10, scalars='pressure')
plotter.add_mesh(contours, cmap='coolwarm', opacity=0.7,
                show_scalar_bar=True,
                scalar_bar_args={'title': 'Pressure [Pa]'})
plotter.show()
乱流の可視化
# Turbulent kinetic energy (TKE) visualization
k_turb = 0.01 * grid['speed']**2 * np.random.rand(len(grid['speed']))
grid['TKE'] = k_turb

plotter = pv.Plotter()

# Volume rendering for turbulence
plotter.add_volume(grid, scalars='TKE', cmap='hot',
                  opacity='linear',
                  scalar_bar_args={'title': 'Turbulent Kinetic Energy [m²/s²]'})

# Add outline
plotter.add_mesh(grid.outline(), color='black', line_width=2)

plotter.show()
ポンプ形状の表示
# Load pump impeller geometry (assuming STL file)
# impeller = pv.read('pump_impeller.stl')

# Or create simple impeller geometry
def create_simple_impeller():
    """Create simplified pump impeller for demonstration."""
    # Hub (cylinder)
    hub = pv.Cylinder(radius=0.02, height=0.05, center=(0, 0, 0),
                     direction=(0, 0, 1), resolution=30)

    # Blades (create 6 blades)
    blades = pv.PolyData()
    for i in range(6):
        angle = i * 60
        blade = pv.Plane(center=(0.04, 0, 0.025), direction=(0, 0, 1),
                        i_size=0.04, j_size=0.05)
        blade.rotate_z(angle, point=(0, 0, 0.025))
        blades += blade

    impeller = hub + blades
    return impeller

impeller = create_simple_impeller()

# Visualize with lighting
plotter = pv.Plotter()
plotter.add_mesh(impeller, color='lightblue', metallic=0.5,
                roughness=0.5, show_edges=True)
plotter.add_light(pv.Light(position=(1, 1, 1), light_type='scene light'))
plotter.show()
複合可視化
# Comprehensive CFD visualization combining multiple techniques
plotter = pv.Plotter()

# Background mesh with velocity magnitude
plotter.add_mesh(grid.outline(), color='black', line_width=2)

# Slice through center showing velocity contours
center_slice = grid.slice(normal='z', origin=(0.5, 0.4, 0.3))
plotter.add_mesh(center_slice, scalars='speed', cmap='jet',
                opacity=0.9, show_scalar_bar=True,
                scalar_bar_args={'title': 'Velocity [m/s]',
                               'vertical': True,
                               'height': 0.7})

# Streamlines showing flow patterns
streamlines = grid.streamlines(
    vectors='velocity',
    source_center=(0.5, 0.4, 0.1),
    source_radius=0.2,
    n_points=25,
    max_time=5.0
)
plotter.add_mesh(streamlines.tube(radius=0.002), color='white', opacity=0.8)

# Pressure isosurface highlighting high-pressure region
high_pressure = grid.threshold(value=103000, scalars='pressure')
plotter.add_mesh(high_pressure, color='red', opacity=0.3)

# Set camera and view
plotter.camera_position = 'xy'
plotter.show()

インタラクティブ機能

カメラ制御
plotter = pv.Plotter()
plotter.add_mesh(grid, scalars='pressure')

# Set camera position
plotter.camera_position = [
    (2, 2, 2),    # Camera position
    (0.5, 0.4, 0.3),  # Focal point
    (0, 0, 1)     # View up vector
]

# Or use preset views
# plotter.camera_position = 'xy'  # Top view
# plotter.camera_position = 'xz'  # Front view
# plotter.camera_position = 'yz'  # Side view
# plotter.camera_position = 'iso'  # Isometric

plotter.show()
複数のビューポート
# Create side-by-side comparison
plotter = pv.Plotter(shape=(1, 2))

# Left: velocity magnitude
plotter.subplot(0, 0)
plotter.add_mesh(grid, scalars='speed', cmap='jet')
plotter.add_text('Velocity Magnitude', font_size=12)

# Right: pressure
plotter.subplot(0, 1)
plotter.add_mesh(grid, scalars='pressure', cmap='coolwarm')
plotter.add_text('Pressure', font_size=12)

plotter.link_views()  # Synchronize camera movements
plotter.show()
アニメーション
# Animate rotating view
plotter = pv.Plotter()
plotter.add_mesh(grid, scalars='speed', cmap='jet')

# Save as GIF or MP4
# plotter.open_gif('rotation.gif')
# plotter.open_movie('rotation.mp4')

# Create rotation animation
path = plotter.generate_orbital_path(n_points=36, shift=0)
plotter.orbit_on_path(path, write_frames=False)

エクスポートと保存

静止画像の保存
plotter = pv.Plotter(off_screen=True)
plotter.add_mesh(grid, scalars='pressure', cmap='coolwarm')
plotter.camera_position = 'iso'

# Save high-resolution image
plotter.screenshot('cfd_pressure.png', transparent_background=False,
                  window_size=[1920, 1080])
メッシュのエクスポート
# Save mesh with data for later use
grid.save('cfd_results.vtk')

# Export to other formats
grid.save('cfd_results.vtu')  # VTK Unstructured Grid
grid.save('cfd_results.vtp')  # VTK PolyData
ParaView へのエクスポート

PyVista のメッシュは VTK 互換であり、高度な後処理を行うため ParaView で直接開くことができます。

CFD 可視化のベストプラクティス

  1. 適切なカラーマップを選択します:

    • 速度:'jet'、'viridis'、'plasma'
    • 圧力/温度:'coolwarm'、'RdBu_r'
    • 乱流:'hot'、'inferno'
  2. スケールを統一します:複数のケースを比較する際は、clim(色の範囲)を設定します。

  3. コンテキストを追加します:形状の輪郭、座標軸、スケールバーを含めます。

  4. 大規模なデータセット向けに最適化します:

    • 間引きを使用します:mesh.decimate(0.5) で点を減らします。
    • LOD(詳細度)を有効にします:plotter.enable_eye_dome_lighting()
  5. 効果的にラベルを付けます:タイトルや、単位付きのスカラー凡例ラベルを追加します。

  6. ライティングを検討します:3D の立体感を高めるため、カスタムライトを追加します。

  7. 透明度を適切に使用します:不透明なサーフェスと透明なボリュームを組み合わせます。

クイックリファレンス

import pyvista as pv
import numpy as np

# Create mesh
grid = pv.ImageData(dimensions=(50, 40, 30))
grid.spacing = (0.02, 0.02, 0.02)

# Add scalar field
grid['pressure'] = np.random.rand(grid.n_points)

# Add vector field
grid['velocity'] = np.random.rand(grid.n_points, 3)

# Create plotter
plotter = pv.Plotter()

# Add mesh with options
plotter.add_mesh(grid,
                scalars='pressure',    # Scalar field name
                cmap='coolwarm',       # Colormap
                opacity=0.8,           # Transparency
                show_edges=True,       # Show cell edges
                show_scalar_bar=True,  # Show colorbar
                clim=[0, 1])          # Color limits

# Add streamlines
streamlines = grid.streamlines(vectors='velocity', n_points=20)
plotter.add_mesh(streamlines.tube(radius=0.001), color='white')

# Slice
slice_z = grid.slice(normal='z')
plotter.add_mesh(slice_z, scalars='pressure')

# Contour isosurfaces
contours = grid.contour(isosurfaces=10, scalars='pressure')
plotter.add_mesh(contours, opacity=0.5)

# Camera and view
plotter.camera_position = 'iso'
plotter.add_axes()
plotter.show_grid()

# Display
plotter.show()

# Save
plotter.screenshot('output.png')

関連項目

コメント