PyVista 可視化 (plot3d)

PyVista backend は、2D スライスを 3D 空間に置く、3D スカラー場を volume / slice / contour として描く、3D ベクトル場を streamlines / quiver として描くための 3D 可視化 API です。通常の 1D/2D 解析は プロットplot() / cmap() / contour() を使い、3D の視点操作や重ね描きが必要なときに PyVista を使います。

入口の選び方

対象

推奨 API

戻り値

2D scalar slice

data.phisp[-1, 100, :, :].plot3d(...)

pyvista.Plotter

3D scalar volume

data.phisp[-1].plot3d(mode=...)

pyvista.Plotter

3D vector field

data.j1xyz[-1].plot3d(mode=..., backend="pyvista")

pyvista.Plotter

Boundary meshes

data.boundaries.plot3d(plotter=...)

pyvista.Plotter

Backtrace / trace paths

trace.plot3d(plotter=...)

pyvista.Plotter

Mesh construction only

emout.plot.pyvista_plot.create_*_mesh(...)

PyVista mesh object

Data2d.plot3d()Data3d.plot3d() は PyVista で描画します。VectorData.plot3d() も既定で PyVista backend を使います。Matplotlib 3D backend を使う場合は backend="mpl" を指定します。

インストール

PyVista は emout の通常依存としてインストールされます。3D 表示を使う環境でも追加の extra 指定は不要です。

pip install emout

古い環境や editable install で ModuleNotFoundError が出る場合は、emout を再インストールして依存関係を更新してください。

スカラー場

2D スライスを 3D 空間に置く

2D slice は pyvista.StructuredGrid の平面として描画されます。plot3d() は plotter を返すので、あとからカメラや screenshot を操作できます。

import emout

data = emout.Emout("output_dir")

plotter = data.phisp[-1, 100, :, :].plot3d(
    cmap="viridis",
    clim=(-20, 20),
    show_edges=False,
    show=False,
)
plotter.show()

3D ボリュームを描く

3D scalar は mode で描き方を切り替えます。

mode

描画内容

主な追加オプション

"box"

外側 surface

opacity, show_edges

"volume"

volume rendering

opacity

"slice"

直交 slice

cmap, clim

"contour"

等値面

contour_levels, opacity

data.phisp[-1].plot3d(mode="box", opacity=0.4, show=True)
data.phisp[-1].plot3d(mode="volume", opacity="sigmoid", show=True)
data.phisp[-1].plot3d(mode="slice", cmap="coolwarm", clim=(-50, 50), show=True)
data.phisp[-1].plot3d(mode="contour", contour_levels=12, show=True)

画像として保存したい場合は、show=False で plotter を受け取り、PyVista の screenshot() を使います。

plotter = data.phisp[-1].plot3d(mode="contour", contour_levels=10, show=False)
plotter.screenshot("phisp_contour.png")
plotter.close()

filename= を渡すと、plot3d() 側で保存まで実行できます。.png などの画像拡張子は screenshot として保存します。.html は PyVista の Jupyter/trame 追加依存が入っている環境では interactive HTML として保存できます。既存の 2D plot と同じく savefilename= も互換エイリアスとして使えます。

data.phisp[-1].plot3d(mode="contour", levels=[0.0], filename="phisp_iso.png")
data.j1xyz[-1].plot3d(mode="stream", filename="j1_stream.png")

ベクトル場

3 成分を持つ VectorData は、mode="stream" / "streamline" で streamlines、mode="quiver" / "vec" で glyph arrows を描けます。

data.j1xyz[-1].plot3d(
    mode="stream",
    backend="pyvista",
    n_points=300,
    tube_radius=0.02,
    show=True,
)

data.j1xyz[-1].plot3d(
    mode="quiver",
    backend="pyvista",
    skip=(3, 3, 2),
    factor=0.4,
    show=True,
)

streamline の seed は seed_mode で選べます。既定の sphere は中心付近から開始します。plane は 2D streamline に近い見え方になりやすく、volume は領域全体、surface は境界メッシュ上から開始します。任意の開始点を固定したい場合は seed_points を渡します。quiver は skip で間引き、factor で矢印の長さを調整します。

data.j1xyz[-1].plot3d(seed_mode="plane", seed_plane="xz", seed_position="center")
data.j1xyz[-1].plot3d(seed_mode="volume", n_points=1000)
data.j1xyz[-1].plot3d(seed_mode="surface", seed_surface=data.boundaries)
data.j1xyz[-1].plot3d(seed_points=[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]])

重ね描き

同じ plotter に追加する

各 PyVista API は plotter= を受け取ります。同じ plotter に複数の layer を追加すると、scalar slice、volume outline、vector streamlines を同じ 3D scene に重ねられます。

plotter = data.phisp[-1].plot3d(
    mode="slice",
    cmap="coolwarm",
    clim=(-50, 50),
    show=False,
)

data.j1xyz[-1].plot3d(
    mode="stream",
    backend="pyvista",
    plotter=plotter,
    tube_radius=0.02,
    color="white",
    show=True,
)

境界メッシュも同じ plotter に追加できます。data.boundaries.plot3d() は collection 全体、data.boundaries[0].plot3d() は単独境界、data.boundaries.mesh().plot3d() は構築済み MeshSurface3D を描画します。

plotter = data.phisp[-1].plot3d(mode="slice", show=False)
data.boundaries.plot3d(
    plotter=plotter,
    color="0.7",
    opacity=0.35,
    show_edges=True,
    show=True,
)

境界メッシュ生成引数は mesh_kwargs、境界 index ごとの上書きは per で渡します。Matplotlib / plot_surfaces() 経路の詳細は 境界メッシュ を参照してください。

Backtrace / trace 軌跡を重ねる

data.trace.*(..., get_trace=True) の戻り値は plot3d() を持ち、既存 plotter に軌跡を追加できます。確率を計算している場合は、alpha="auto" で軌道ごとの確率を透明度に使います。

trace = data.trace.forward(
    x=20.0, y=32.0, z=40.0,
    vx=(-5.0, 5.0, 16),
    vy=0.0,
    vz=(-5.0, 5.0, 16),
    get_trace=True,
)

plotter = data.phisp[-1].plot3d(mode="slice", show=False)
trace.plot3d(
    plotter=plotter,
    direction="forward",
    tube_radius=0.05,
    color="black",
    show=True,
)

get_probabilities=False で trace だけ作った場合、確率由来の alpha はありません。必要なら alpha=0.3 のように明示してください。

単位と軸順序

グリッドデータの軸順序は (t, z, y, x)、3D volume は (z, y, x) です。PyVista helper はこの順序を読み取り、PyVista 側の (x, y, z) 座標へ並べ替えます。

use_si=True が既定で、単位 metadata がある場合は座標と値を SI 単位へ変換します。単位 metadata がないデータでは use_si=True を渡しても内部的に grid / raw 値のまま描画します。

offsets=(x_offset, y_offset, z_offset) には数値または "left" / "center" / "right" を指定できます。複数 layer を重ねるときは、すべての layer で同じ use_sioffsets を使ってください。

リモート実行と HPC

PyVista 描画は plotter をローカル Python プロセス上に作る API です。data.remote() / remote_figure() は Matplotlib 画像レンダリング向けなので、Data3d.plot3d() の PyVista scene を worker に保持して再描画する用途ではありません。

KUDPC などの login node では、重い PyVista 描画や大きな 3D データ読み込みを直接実行せず、計算ノードや対応する可視化環境へ回してください。スクリプト内では show=False で plotter を作り、screenshot() で画像保存する形にすると batch 実行しやすくなります。

plotter = data.phisp[-1].plot3d(mode="slice", show=False)
plotter.screenshot("phisp_slice.png")
plotter.close()

低水準 helper

通常は plot3d() を使います。PyVista mesh を自分で加工したい場合だけ、emout.plot.pyvista_plot の helper を直接使います。

from emout.plot.pyvista_plot import (
    create_plane_mesh,
    create_surface_mesh,
    create_volume_mesh,
    create_vector_mesh3d,
)

mesh, scalar_name, axis_labels, scalar_label = create_volume_mesh(data.phisp[-1])

低水準 helper は PyVista オブジェクトを返すだけで、emout の remote rendering や article recording の高水準 API ではありません。

よくあるエラー

症状

原因

対処

ModuleNotFoundError: pyvista

古い環境または依存関係が未更新

pip install -U emout

Data2d with time axis is not supported

t 軸を含む 2D slice を渡した

時刻を 1 つに固定して空間 2D slice にする

requires spatial axes x,y,z

3D 空間軸が揃っていない

data.phisp[-1] のように time だけ固定する

streamlines が出ない

seed 数や seed 半径が小さい

n_points / source_radius を増やす

layer がずれる

use_sioffsets が layer 間で違う

すべての layer で同じ指定にする