XMLベースのvtk形式に関する覚書

  • 1
    Like
  • 0
    Comment

vtkとは

vtkはVisualization Toolkitの略称であり、データやモデルの幾何形状を保存するために使われるライブラリと、そのデータ形式を指します。
vtkは様々なアプリケーションやライブラリでのデータ出力・可視化用に広く使われていますが、その形式について日本語で詳しく解説されている記事は意外と少なく、またレガシーフォーマットについてのものがほとんどです。本稿は公式のドキュメントに記されている情報のうち、XMLをベースにした新しいvtk形式についての情報を自分向けにまとめたものです。

XMLベースのvtk形式についての他の日本語記事

vtkファイルフォーマット(バイナリ形式)
http://bigvalley.hatenablog.com/entry/2016/02/03/160414

公式のサンプルデータ

https://www.vtk.org/Wiki/VTK_Datasets で公開されています。
git clone git://vtk.org/VTKData.gitすることで大量にサンプルを手に入れることができます。(執筆現在でだいたい260MB程度)

Regacy Format と XML Format の違い

公式のドキュメントによれば、XML Formatによって
- 並列I/Oサポート
- 圧縮、バイナリのエンコーディング、ランダムアクセス、バイトオーダー等の形式のサポート
- 部分データの読み込みのサポート

等がサポートされているようです。

XML Formatのファイルの種類

XMLフォーマットの場合、ファイルの形式は以下の2つに分けられます。

Serial
1つの単一のプロセスによって読み書きされることを想定された形式. 全てのデータが1つのファイル内に格納される.
Parallel
複数のプロセスによって読み書きされることを想定された形式. 部分部分のデータはSerialなvtkファイルとして書き出し、Parallelなvtkファイルがそれらの関係性を記述・参照する形になるため、Parallelなvtkファイルは実データを含まない.

更に、格子の種類によって

Structured
構造格子。 画像データもこれに当てはまります。 種類としてはvtkImageData、vtkRectilinearGrid、 vtkStructuredGridがあります。
Unstructured
非構造格子。 種類としてはvtkPolyDataとvtkUnstructuredGridがあります。

の2つが区別されます。したがって、大きく分けて

  • Serial Structured
  • Serial Unstructured
  • Parallel Structure
  • Parallel Unstructured

の4つの記述方法があることになります。

拡張子による区別

内部的に何のデータを記述しているのかによって、以下のように拡張子を変化させる慣習があります。

  • ImageData (.vti) — Serial vtkImageData (structured).
  • PolyData (.vtp) — Serial vtkPolyData (unstructured).
  • RectilinearGrid (.vtr) — Serial vtkRectilinearGrid (structured).
  • StructuredGrid (.vts) — Serial vtkStructuredGrid (structured).
  • UnstructuredGrid (.vtu) — Serial vtkUnstructuredGrid (unstructured).
  • PImageData (.pvti) — Parallel vtkImageData (structured).
  • PPolyData (.pvtp) — Parallel vtkPolyData (unstructured).
  • PRectilinearGrid (.pvtr) — Parallel vtkRectilinearGrid (structured).
  • PStructuredGrid (.pvts) — Parallel vtkStructuredGrid (structured).
  • PUnstructuredGrid (.pvtu) — Parallel vtkUnstructuredGrid (unstructured)

各拡張子の3文字目がVTKFile Typeの頭文字に対応します。
pがついていたらParallel、そうでなかったらSerialです。
前述した通り、Parallelなvtkファイルは実際には実データを持たず、部分部分に対応するSerialなvtkデータを参照するという形になります。この他に、公式のDocumentに存在していない(なんで?)拡張子として.vthb: vtkHierarchicalBoxDataSetが存在します。

基本形式

vtkファイルの内容はVTKFile要素の中に記述されます。

basic_vtk.vti
<?xml version="1.0" ?>
<VTKFile type="ImageData">
...
</VTKFile>

VTKFileのアトリビュートとして、以下の要素を指定可能です。

type
vtkファイルの種類(ImageData, PolyData, RectlinearGrid, StructuredGrid, UnstructuredGrid)を指定
version
"major.minor"の形でvtkの?要素の?バージョンを指定. 仕様がしっかりと定まってない要素の読み込み時に使われるようなので、普段は気にしなくてよさそうです.
byte_order
LittleEndianかBigEndianを指定
compressor
データの圧縮形式を指定 (後述)

VTKFileの中には、vtkファイルの種類に対応した要素を記述することになります。
type="ImageData"なら<ImageData>...</ImageData>に、type="PolyData"なら<PolyData>...</PolyData>になります。

basic_vtk.vti
<?xml version="1.0" ?>
<VTKFile type="ImageData">
    <ImageData>
    ...
    </ImageData>
</VTKFile>

この要素を公式ドキュメントでは「データセット要素」(Data set element)と呼んでいます。

データセット要素

データセット要素は1つ以上のPiece要素を持ちます。このPiece要素が実際のデータを記述する単位となります。

Piece要素

Piece要素は必ず自身の幾何構造(PointsとCells)を持つ必要がありますが、typeによっては暗黙的に解決されます。描画する実データとして、PointDataかCellData要素のどちらかを必ず持つことになります。(両方持ってもOK)

ImageData

ImageData要素はWholeExtentアトリビュート、Originアトリビュート、Spacingアトリビュートを指定できます。
ImageData内のPiece要素にはExtentアトリビュートを指定できます。

imagedata.vti
<ImageData WholeExtent=”xmin xmax ymin ymax zmin zmax” Origin=”x0 y0 z0” Spacing=”dx dy dz”>
    <Piece Extent=”x1 x2 y1 y2 z1 z2”>
        <PointData>...</PointData>
        <CellData>...</CellData>
    </Piece>
</ImageData>

WholeExtentは全空間の要素数を、OriginとSpacingは起点の座標と空間グリッドの幅となります。
ImageDataでは、内部のPiece要素のPointsとCellsが起点と幅の情報から暗黙的に解決されます。
WholeExtentとOriginとSpacingが具体的にどのように解釈されるのかについては後述します。

RectlinearGrid

RectlinearGrid要素はWholeExtentアトリビュートを指定できます。
Piece要素にはExtentアトリビュートを指定できます。

rectlineargrid.vtr
<RectlinearGrid WholeExtent=”xmin xmax ymin ymax zmin zmax”>
    <Piece Extent=”x1 x2 y1 y2 z1 z2”>
        <PointData>...</PointData>
        <CellData>...</CellData>
        <Coordinates>...</Coordinates>
    </Piece>
</RectlinearGrid>

ImageDataとは違い、起点と空間幅を指定できない代わりにCoordinates要素によってPointsを指定します。Cellsは暗黙的に解決されます。

StructuredGrid

StructuredGrid要素はWholeExtentアトリビュートを指定できます。
Piece要素にはExtentアトリビュートを指定できます。

structuredgrid.vts
<StructuredGrid WholeExtent=”xmin xmax ymin ymax zmin zmax”>
    <Piece Extent=”x1 x2 y1 y2 z1 z2”>
        <PointData>...</PointData>
        <CellData>...</CellData>
        <Points>...</Points>
    </Piece>
</StructuredGrid>

StructuredGridもRectlinearGridと同様にPointを明示的に指定する必要があり、こちらではPoints要素を使用します。RectlinearGridとの違いはCoordinatesを使うかPointsを使うかどうかだと思いますが、よく分かりません。Cellsは暗黙的に解決されます。

PolyData

PolyData要素には特にアトリビュートがありません。
代わりにPiece要素のアトリビュートとして、

  • NumberOfPoints
  • NumberOfVerts
  • NumberOfLines
  • NumberOfStrips
  • NumberOfPolys

を指定可能です。PolyData要素ではPointとCell両方を明示的に指定することになります。
PointについてはPoints要素を、CellについてはVerts、Lines、Strips、Polys要素のいずれかを使用します。
Piece要素のアトリビュートで指定するのはこのPointとCellの数となります。

polydata.vtp
<VTKFile type=”PolyData”>
    <PolyData>
        <Piece NumberOfPoints=”#” NumberOfVerts=”#” NumberOfLines=”#” NumberOfStrips=”#” NumberOfPolys=”#”>
            <PointData>...</PointData>
            <CellData>...</CellData>
            <Points>...</Points>
            <Verts>...</Verts>
            <Lines>...</Lines>
            <Strips>...</Strips>
            <Polys>...</Polys>
        </Piece>
    </PolyData>
</VTKFile>

Unstructured Grid

UnstructuredGrid要素でもPolyDataと同様にアトリビュートがありません。
Piece要素のNumberOfPointsアトリビュート、NumberOfCellsアトリビュートでPointとCellの数を指定します。

unstructuredgrid.vtu
<VTKFile type=”UnstructuredGrid”>
    <UnstructuredGrid>
        <Piece NumberOfPoints=”#” NumberOfCells=”#”>
            <PointData>...</PointData>
            <CellData>...</CellData>
            <Points>...</Points>
            <Cells>...</Cells>
        </Piece>
    </UnstructuredGrid>
</VTKFile>

DataArray要素

PointDataやCellData、Points、Cellsなどの中身は全てDataArray要素によって記述します。
DataArrayはNameアトリビュートを持ち、後述するように親の要素からNameを参照することでデータ割り当てを行います。

その他、以下のようなアトリビュートがあります。

Name
名前
type
データの種類。Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64が指定できます.
format
データの形式。"ascii"、"binary"、"appended"のどれかになります。
NumberOfComponents
1まとまり中の要素数. 例えば3次元のベクトルなら`NumberOfComponents="3"`
offset
formatがappendedの時にのみ有効です。offsetの値として指定された位置をデータの開始位置として読み取り、そのDataArrayのデータとして付け足します.

DataArrayのformatについて

format="ascii"

テキストとして直に数値を持ちます。空白がSeparatorとして扱われます。

ascii.vtk
<DataArray type=”Int32” Name=”data1" format=”ascii”>10 20 30 ... </DataArray>

format="binary"

Base64エンコードされたバイナリデータとして要素を格納します。VTKFileのアトリビュートで指定したbyte_orderに従って解釈されます。エンコード前に圧縮していた場合、compressorで対応する圧縮形式を指定する必要があります。

ascii.vtk
<DataArray type=”Float32” Name=”data2” format=”binary”>bAAAAAAAAAAAAIA/AAAAQAAAQEAAAIBA... </DataArray>

format="appended"

これはDataArray要素内に直接値を書き込むのではなく、別の部分に書き込まれたAppendedData要素を参照するために使われます。
AppendedData要素はVTKFile中の末尾に、データセット要素の後に書き込む形になります。

appended.vtk
<VTKFile ...>
    ...
    <AppendedData encoding=”base64”>
        _QMwEAAAAAAAAA...
    </AppendedData>
</VTKFile>

AppendedDataの中身は _ (underscore)から始まる必要があります。
AppendedDataに名前などはつけられないみたいなので、DataArray要素のoffsetアトリビュートによって使いたいデータがどこから始まるのかを指定することになります。不便ですね!
ちなみに、offsetの位置については最初の _ 分は数えなくていいようです。

PointDataとCellData

PointDataとCellDataにはScalars, Vectors, Normals, Tensors, TCoords(テクスチャコーディネート)アトリビュートを指定できます。これはそれぞれ、内部のDataArrayに持つ変数の"名前"を指定します。
よく分からないので例を書くと

pointdata.vtk
<PointData Scalars="Potential" Vectors="Efield">
    <DataArray Name="Potential" .../>
    <DataArray Name="Efield" .../>
</PointData>

という感じで、実際のデータを格納するのは内部のDataArray要素で、それらがスカラなのかベクトルなのかをPointData(またはCellData)のアトリビュートで指定できるということです。複数のスカラを持っている時にどうするのかはよく分かりません。
PointDataかCellDataのうち、不要な方は<PointData />のように書くか、単に省略してしまってもOKのようです。

Points

Points要素は点の座標をDataArray要素の羅列として格納します。
座標系のまとまりを表すためNumberOfComponents="3"としたDataArray要素を用いて、以下のように表します。

points.vtk
<Points>
    <DataArray NumberOfComponents=”3” ...>
        0 0 0
        1 0 0
        0 1 0
        1 1 0
    </DataArray>
</Points>

上例なら4つのPoint (0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0)を表しています。

Coordinates

Coordinates要素は、3軸の座標系をそれぞれ別のDataArray要素として持つ座標記述用の要素です。
Points要素よりも簡易に書けますが、3次元の直交座標を暗黙に仮定するため、Unstructuredな場合には使えません。

coordinates.vtk
<Coordinates>
   <DataArray ...>x0 x1 x2 x3 ...</DataArray>
   <DataArray ...>y0 y1 y2 y3 ...</DataArray>
   <DataArray ...>z0 z1 z2 z3 ...</DataArray>
</Coordinates>

3つのDataArrayがそれぞれの軸の座標に対応し、(x0, y0, z0), (x1, y0, z0), (x2, y0, z0), ...という形で座標点の組み合わせを生成してくれます。

Verts, Lines, Strips, Polys

これらの4要素はCellを明示的に指定するために使われます。
内部の要素として、connectivityとoffsetsを指定する2つのDataArray要素を持つ必要があります。
最初のDataArrayがconnectivityとして、2つめのDataArrayがoffsetsとして扱われます。

verts_etc.vtk
<Verts>
    <DataArray type=”Int32” Name=”connectivity” .../>
    <DataArray type=”Int32” Name=”offsets” .../>
</Verts>

Connectivityについては、Pointsなどで定義した順に0→1→2→…とインデックスが割り振られているため、
それをベースに、どのインデックスとどのインデックスをくっつけるかを指定します。
Offsetsは各Connectivityの区切りを指定するDataArrayで、各要素の終わりの要素番号を指定します。
2点区切りのLinesなら、2→4→6→...と単純に2増加の数列になります。

たとえば、3点間を結ぶような時の指定方法は以下のようになります。

lines.vtp
<Points>
  <DataArray NumberOfComponents="3" type="Float32" Name="points" format="ascii">
    0.0 0.0 0.0
    0.0 1.0 0.0
    1.0 0.0 0.0
    1.0 1.0 0.0
  </DataArray>
</Points>
<Lines>
  <DataArray type="Int32" Name="connectivity" format="ascii">
    0 1
    1 2
    2 3
  </DataArray>
  <DataArray type="Int32" Name="offsets" format="ascii">
    2 4 6
  </DataArray>
</Lines>

Cells

Cells要素ではconnectivity, offsets, typesの3要素が必要になります。

cells.vtk
<Cells>
    <DataArray type=”Int32” Name=”connectivity” .../>
    <DataArray type=”Int32” Name=”offsets” .../>
    <DataArray type=”UInt8” Name=”types” .../>
</Cells>

ConnectivityとOffsetsについてはVerts等の場合と同様ですが、
Cells要素の場合はTypesも必要になります。

これは公式のドキュメントのFigure 2(下図)に示されています。
スクリーンショット 2017-10-25 16.38.03.png

例えば、Voxel要素を構成するなら

<DataArray type="UInt8" Name="types" format="ascii">
  11 11 11 11 ... # 要素数だけ続ける
</DataArray>

という形になります。

実際に色々書いてみる

座学はここまでにして、実際に色々なデータを書いてみます。
単一の3次元直交構造等幅格子の場合 = ImageDataの場合で色々試してみましょう。

まずPiece一つで全てのデータを割り当てる場合。

imagedatatest1.vti
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 1" Origin="0 0 0" Spacing="1.0 1.0 1.0">
        <Piece Extent="0 2 0 2 0 1">
            <PointData Scalars="test1">
                <DataArray Name="test1" type="Int32" format="ascii">
                    1 2 3 4 5 6
                    7 8 9 10 11 12
                    13 14 15 16 17 18
                </DataArray>
            </PointData>
        </Piece>
    </ImageData>
</VTKFile>

WholeExtentを0-2として、Spacingを1.0としているので、
x: [0.0, 1.0, 2.0]
y: [0.0, 1.0, 2.0]
z: [0.0, 1.0]
の3x3x2で合計18点になります。そのため、PointData内部のDataArrayの要素数を18点としています。

可視化するとこんな感じ。
スクリーンショット 2017-09-12 1.59.39.png

色合いから分かる通り、値の割り当て順は(0,0,0)→(1,0,0)→(2,0,0)→(0,1,0)→(1,1,0)→……という順に、IJK-orderで割り当てられていきます。

次に複数のPieceでExtentを分割する場合。

imagedatatest2.vti
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 1" Origin="0 0 0" Spacing="1.0 1.0 1.0">
        <Piece Extent="0 1 0 2 0 1">
            <PointData Scalars="test1">
                <DataArray Name="test1" type="Int32" format="ascii">
                    1 2 3 4 5 6
                    7 8 9 10 11 12
                </DataArray>
            </PointData>
        </Piece>
        <Piece Extent="2 2 0 2 0 1">
            <PointData Scalars="test1">
                <DataArray Name="test1" type="Int32" format="ascii">
                    13 14 15 16 17 18
                </DataArray>
            </PointData>
        </Piece>
    </ImageData>
</VTKFile>
  • x: [0.0, 1.0]
  • y: [0.0, 1.0, 2.0]
  • z: [0.0, 1.0]

を1つ目のPieceに、

  • x: [2.0]
  • y: [0.0, 1.0, 2.0]
  • z: [0.0, 1.0]

を2つ目のPieceに分けています。
この場合、割り当て順が(0,0,0)→(1,0,0)→(0,1,0)→(1,1,0)→…と変わるので、可視化するとこうなります。

スクリーンショット 2017-09-12 2.03.56.png

Piece要素でExtentを分けても特に分割線などは表示されず、幾何形状としてはPiece1つの場合と何ら変わりません。領域分割などでプロセス分割したデータを記述する場合、分割線を表示したいなら複数のPieceではなくて後述するParallelを使うとよいでしょう。

Cellデータを割り当ててみます。各方向のセル数はノード数-1になるので、(3-1)x(3-1)x(2-1)=4個必要です。

imagedatacell1.vti
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 1" Origin="0 0 0" Spacing="1.0 1.0 1.0">
        <Piece Extent="0 2 0 2 0 1">
            <CellData Scalars="test_cell_values">
                <DataArray Name="test_cell_values" type="Int32" format="ascii">
                    1 2 3 4
                </DataArray>
            </CellData>
        </Piece>
    </ImageData>
</VTKFile>

はい。
スクリーンショット 2017-09-12 2.14.26.png

Cell中心のベクトルを割り当ててみましょう。
VectorのDataArrayにはNumberOfComponents="3"が必要になります。

imagedatacell2.vti
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 1" Origin="0 0 0" Spacing="1.0 1.0 1.0">
        <Piece Extent="0 2 0 2 0 1">
            <CellData Scalars="test_cell_values" Vectors="test_cell_vectors">
                <DataArray Name="test_cell_values" type="Int32" format="ascii">
                    1 2 3 4
                </DataArray>
                <DataArray Name="test_cell_vectors" NumberOfComponents="3" type="Int32" format="ascii">
                    1 0 0
                    0 1 0
                    0 0 1
                    1 1 1
                </DataArray>
            </CellData>
        </Piece>
    </ImageData>
</VTKFile>

いい感じですね。
スクリーンショット 2017-09-12 2.20.51.png

CellDataを用いて、Faceにデータを割り当てることも可能です。
z軸のextentを0→0にして2次元にしてみましょう。

imagedata2d1.vdi
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 0" Origin="0 0 0" Spacing="1.0 1.0 0.0">
        <Piece Extent="0 2 0 2 0 0">
            <CellData Scalars="test_cell_values">
                <DataArray Name="test_cell_values" type="Int32" format="ascii">
                    1 2 3 4
                </DataArray>
            </CellData>
        </Piece>
    </ImageData>
</VTKFile>

この場合、CellData要素の内容はFaceへの割り当てとして扱われます.
スクリーンショット 2017-09-12 2.26.20.png

PolyDataもやっていきましょう。
例えばEdge要素の分布をプロットする場合はこんな感じになります。

edge_lines.vtp
<?xml version="1.0" ?>
<VTKFile type="PolyData" version="0.1" byte_order="LittleEndian">
    <PolyData>
        <Piece NumberOfPoints="4" NumberOfLines="3" NumberOfVerts="0" NumberOfStrips="0" NumberOfPolys="0">
            <PointData Scalars="test">
                <DataArray type="Float32" Name="test" format="ascii">
                    0.0
                    1.0
                    2.0
                    3.0
                </DataArray>
            </PointData>
            <Points>
                <DataArray NumberOfComponents="3" type="Float32" Name="points" format="ascii">
                    0.0 0.0 0.0
                    0.0 1.0 0.0
                    1.0 0.0 0.0
                    1.0 1.0 0.0
                </DataArray>
            </Points>
            <Lines>
                <DataArray type="Int32" Name="connectivity" format="ascii">
                    0 1
                    1 2
                    2 3
                </DataArray>
                <DataArray type="Int32" Name="offsets" format="ascii">
                    2 4 6
                </DataArray>
            </Lines>
        </Piece>
    </PolyData>
</VTKFile>

0番の点と1番の点、1番の点と2番の点、2番の点と3番の点の間に3本の線を引いています。

スクリーンショット 2017-10-25 16.33.38.png

WholeExtentとOriginとSpacingについて

OriginとSpacingがあるんだからWholeExtentじゃなくて各方向への要素数じゃないのかという点が疑問だったのですが、次のように解釈されるようです。
各方向のOriginの座標をx0、Spacingをdx、WholeExtentを[xmin, xmax]とする時、

  • 割り当ての開始点は x0 + dx * xmin
  • 割り当ての終了点は x0 + dx * xmax

となります。つまり、WholeExtentは起点から前後方向への要素数と解釈できます。

extenttest.vdi
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="-1 1 0 2 0 0" Origin="-10 -10 0" Spacing="1.0 1.0 0.0">
        <Piece Extent="-1 1 0 2 0 0">
            <CellData Scalars="test_cell_values" Vectors="test_cell_vectors">
                <DataArray Name="test_cell_values" type="Int32" format="ascii">
                    5 6 7 8
                </DataArray>
            </CellData>
        </Piece>
    </ImageData>
</VTKFile>

上記のようにOriginとWholeExtentをずらすと、下図のようになります。
スクリーンショット 2017-09-12 2.42.33.png

また、Spacingを1から0.5へ変更すると

extenttest.vdi
<?xml version="1.0"?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="-1 1 0 2 0 0" Origin="-10 -10 0" Spacing="0.5 0.5 0.0">
        <Piece Extent="-1 1 0 2 0 0">
            <CellData Scalars="test_cell_values" Vectors="test_cell_vectors">
                <DataArray Name="test_cell_values" type="Int32" format="ascii">
                    5 6 7 8
                </DataArray>
            </CellData>
        </Piece>
    </ImageData>
</VTKFile>

下図のようになります。
スクリーンショット 2017-09-12 2.46.37.png

数値計算に使うような場合、基本的にはOrigin="0.0 0.0 0.0"としてWholeExtent="0 nx 0 ny 0 nz"としていいと思いますが、位置を調整する必要がある時も柔軟に調整可能のようです。

Parallelな場合

PImageData, PRectilinearGrid, PStructuredGrid, PPolyData, PUnstructuredGridというように、各データセット要素の種類の頭にPをつけたものがParallelバージョンになります。
Parallelデータセット要素は実際にはデータを持たず、Serialデータセット要素をsourceとしたPiece要素の集合を持ちます。
下記に記すように子の各要素にも頭にPがつきます。

PImageData, PRectlinearGrid, PStructuredGrid, PPolyData, PUnstructuredGrid

それぞれの要素に対応するParallel版です。
領域分割時のGhostセル/ノードを表現できるように、GhostLevelアトリビュートでいくつオーバーラップするかを指定できるようです。それ以外はSerial版と同様です。
各Parallelデータセット要素はPointDataとCellDataの代わりに、PPointData要素とPCellData要素を持ちます。

例えばPImageDataの場合は以下のように、各要素の頭にPが付きます。

pimagedata.pvtp
<VTKFile type=”PImageData” ...>
    <PImageData WholeExtent=”x1 x2 y1 y2 z1 z2” GhostLevel=”#” Origin=”x0 y0 z0” Spacing=”dx dy dz”>
        <PPointData>...</PPointData>
        <PCellData>...</PCellData>
        <Piece Extent=”x1 x2 y1 y2 z1 z2” Source=”imageData0.vti”/>
    </PImageData>
</VTKFile>

これ以外にも、PointsPPointsに、CoordinatesPCoordinatesになったりします。

PPointDataとPCellDataとPDataArray

それぞれPointData、CellData、DataArray要素のParallel版です。

Parallelな場合の具体的な記述方法

Parallelな場合の記述方法がよく分からないので具体的な例を見てみます。
実際にはデータを格納せず、Serial版と同じ構造のみを(Pを頭につけた要素を用いて)記述することで、ParallelなI/Oに対応します。

公式の例ですが、例えば以下のようなSerialなvtpファイルがあったとします。

test1.vtp
<?xml version="1.0"?>
    <VTKFile type="PolyData" version="0.1" byte_order="LittleEndian">
        <PolyData>
            <Piece NumberOfPoints="8" NumberOfVerts="0" NumberOfLines="0" NumberOfStrips="0" NumberOfPolys="6">
                <Points>
                    <DataArray type="Float32" NumberOfComponents="3" format="ascii">
                        0 0 0
                        1 0 0
                        1 1 0
                        0 1 0
                        0 0 1
                        1 0 1
                        1 1 1
                        0 1 1
                    </DataArray>
                </Points>
                <PointData Scalars="my_scalars">
                    <DataArray type="Float32" Name="my_scalars" format="ascii">
                        0 1 2 3 4 5 6 7
                    </DataArray>
                </PointData>
                <CellData Scalars="cell_scalars" Normals="cell_normals">
                    <DataArray type="Int32" Name="cell_scalars" format="ascii">
                        0 1 2 3 4 5
                    </DataArray>
                    <DataArray type="Float32" Name="cell_normals" NumberOfComponents="3" format="ascii">
                        0 0 -1 0 0 1 0 -1 0 0 1 0 -1 0 0 1 0 0
                    </DataArray>
                </CellData>
                <Polys>
                    <DataArray type="Int32" Name="connectivity" format="ascii">
                        0 1 2 3
                        4 5 6 7
                        0 1 5 4
                        2 3 7 6
                        0 4 7 3
                        1 2 6 5
                    </DataArray>
                    <DataArray type="Int32" Name="offsets" format="ascii">
                        4 8 12 16 20 24
                    </DataArray>
                </Polys>
            </Piece>
        </PolyData>
    </VTKFile>

paraviewで見るとこんな感じです。
スクリーンショット 2017-09-11 23.32.43.png

test1.vtpの座標だけズラしてもう1つ用意します.

test2.vtp
<?xml version="1.0"?>
    <VTKFile type="PolyData" version="0.1" byte_order="LittleEndian">
        <PolyData>
            <Piece NumberOfPoints="8" NumberOfVerts="0" NumberOfLines="0" NumberOfStrips="0" NumberOfPolys="6">
                <Points>
                    <DataArray type="Float32" NumberOfComponents="3" format="ascii">
                        1 1 1
                        2 1 1
                        2 2 1
                        1 2 1
                        1 1 2
                        2 1 2
                        2 2 2
                        1 2 2
                    </DataArray>
                </Points>
                ... // 以下は同じ
            </Piece>
        </PolyData>
    </VTKFile>

これをsourceにParallel版を書くと、以下のようになります。

<?xml version="1.0"?>
   <VTKFile type="PPolyData" version="0.1" byte_order="LittleEndian">
      <PPolyData GhostLevel="0">
         <PPointData Scalars="my_scalars">
         <PDataArray type="Float32" Name="my_scalars"/>
      </PPointData>
      <PCellData Scalars="cell_scalars" Normals="cell_normals">
         <PDataArray type="Int32" Name="cell_scalars"/>
         <PDataArray type="Float32" Name="cell_normals" NumberOfComponents="3"/>
      </PCellData>
      <PPoints>
         <PDataArray type="Float32" NumberOfComponents="3"/>
      </PPoints>
      <Piece Source="test1.vtp"/>
      <Piece Source="test2.vtp"/>
   </PPolyData>
</VTKFile>

ParaViewで見るとこうなりました。
スクリーンショット 2017-09-11 23.41.59.png

やりました!

階層構造を持つ場合

実はAdaptive Mesh Refinementで計算したデータを可視化したい!というのが本稿執筆というかvtk形式について調べ始めた動機だったのですが、vtk形式自体には明示的な階層構造表現の能力はなくて、公式のライブラリ Github.com - Kitware/VTK が頑張ってMultiBlockなMeshを出力するぜ!ということのようです。
無念……。

公式のドキュメントには載っていないのですが、階層構造を持つ場合はvtkHierarchicalBoxDataSet要素を使うことで記述可能です。これを用いたMesh Refinementなデータの記述と可視化については(長くなったので)別記事で解説します。

拙作のVTK出力用ライブラリの宣伝: SimpleVTK

公式のライブラリ( https://github.com/Kitware/VTK )は様々なユーティリティ関数がありますが、描画用(読み取り用)の関数群やGUIサポートなども含んでいるため非常に巨大です。

そこで、VTK形式のことが分かっている人がVTKを出力するために使うためのシンプルなC++ヘッダーライブラリを書きました。
hsimyu/SimpleVTK - https://github.com/hsimyu/SimpleVTK

こんな感じで書けます。

#include <simple_vtk.hpp>

int main() {

    // automatically insert extent information
    SimpleVTK gen;
    gen.enableExtentManagement();
    gen.changeBaseExtent(0, 2, 0, 2, 0, 2);
    gen.changeBaseOrigin(0.0, 0.0, 0.0);
    gen.changeBaseSpacing(1.0, 1.0, 0.5);

    std::vector<float> values{
        0, 1, 2, 3, 4, 5, 6, 7, 8,
        9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24,
        25, 26
    };

    gen.beginVTK("ImageData");
        gen.beginContent();
            gen.beginPiece();
                gen.addPointScalars("potential", "Float32", "ascii", values);
            gen.endPiece();
        gen.endContent();
    gen.endVTK();

    gen.generate("test1");
    return 0;
}

こんなvtiファイルが出てきます。

test1.vti
<?xml version="1.0" ?>
<VTKFile type="ImageData">
    <ImageData WholeExtent="0 2 0 2 0 2" Origin="0 0 0" Spacing="1 1 0.5">
        <Piece Extent="0 2 0 2 0 2">
            <PointData Scalars="potential">
                <DataArray Name="potential" type="Float32" format="ascii">
                    0 1 2 3 4 5 6 7 8 9 
                    10 11 12 13 14 15 16 17 18 19 
                    20 21 22 23 24 25 26
                </DataArray>
            </PointData>
        </Piece>
    </ImageData>
</VTKFile>

元々VTKを調べ始めた動機がAMRデータの可視化ということで、特にvtkHierarchicalBoxDataSetの出力を行う際に便利に使えるものになっています。詳しくは下記で。
vtk形式によるブロック構造格子AMR(Adaptive Mesh Refinement)データの記述方法とParaViewによる可視化方法

※Binaryエンコーディングや圧縮のサポートについては今のところ実装されていません。