Appearance
Parallel and composite files
Files that list other files: parallel (partitioned) headers, multi-block assemblies, time series; files written into memory. Complete programs: the cookbook and chapters 4, 6 and 7 of the tutorial.
Parallel Structured Grid (PVTS)
For MPI-parallel codes, each rank writes its own partition as a regular VTS file, then a single PVTS header file references all partitions.
fortran
use vtk_fortran, only : vtk_file, pvtk_file
use penf, only : I4P, R8P
! whole extent
integer(I4P), parameter :: nx1=0, nx2=9, ny1=0, ny2=5, nz1=0, nz2=5
! partition boundaries along x
integer(I4P), parameter :: nx1_p(2) = [0, 4]
integer(I4P), parameter :: nx2_p(2) = [4, 9]
! --- each rank writes its own partition ---
call write_partition(part=1, filename='part_01.vts')
call write_partition(part=2, filename='part_02.vts')
! --- one rank writes the parallel header ---
block
type(pvtk_file) :: a_pvtk_file
integer(I4P) :: error
error = a_pvtk_file%initialize(filename='output.pvts', &
mesh_topology='PStructuredGrid', &
mesh_kind='Float64', &
nx1=nx1, nx2=nx2, ny1=ny1, ny2=ny2, nz1=nz1, nz2=nz2)
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='open')
error = a_pvtk_file%xml_writer%write_parallel_dataarray(data_name='pressure', &
data_type='Float64', &
number_of_components=1)
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='close')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_01.vts', &
nx1=nx1, nx2=nx2_p(1), ny1=ny1, ny2=ny2, nz1=nz1, nz2=nz2)
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_02.vts', &
nx1=nx2_p(1), nx2=nx2_p(2), ny1=ny1, ny2=ny2, nz1=nz1, nz2=nz2)
error = a_pvtk_file%finalize()
end blockAdjacent partition extents
Adjacent pieces must share the boundary ordinate: nx2_p(1) of piece 1 must equal nx1_p(2) of piece 2. This is required for correct rendering in ParaView.
Parallel Rectilinear Grid (PVTR)
A .pvtr header is written as a .pvts one, with mesh_topology='PRectilinearGrid': each piece is a regular .vtr file holding the coordinates of its own extent, and the header declares the coordinates type through mesh_kind (written as PCoordinates). The test src/tests/vtk_fortran_write_pvtr.f90 is a complete example with point and cell data.
fortran
error = a_pvtk_file%initialize(filename='output.pvtr', mesh_topology='PRectilinearGrid', mesh_kind='Float64', &
nx1=0, nx2=4, ny1=0, ny2=2, nz1=0, nz2=2)
! ... PPointData / PCellData as for PVTS ...
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_01.vtr', nx1=0, nx2=2, ny1=0, ny2=2, nz1=0, nz2=2)
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_02.vtr', nx1=2, nx2=4, ny1=0, ny2=2, nz1=0, nz2=2)
error = a_pvtk_file%finalize()Parallel Unstructured Grid (PVTU)
The same approach works for unstructured grids: each rank writes its partition as a complete .vtu file, with its own points and a connectivity using local point ids (0 to np-1 of that piece), then one rank writes the .pvtu file. The .pvtu file contains no data: it declares the type of the points coordinates (mesh_kind) and of every data array of the pieces, and lists the pieces. Unstructured pieces have no extents, so write_parallel_geo takes only the piece file name.
fortran
use vtk_fortran, only : pvtk_file
use penf, only : I4P
type(pvtk_file) :: a_pvtk_file
integer(I4P) :: error
! --- after every rank has written its own part_NN.vtu ---
error = a_pvtk_file%initialize(filename='output.pvtu', mesh_topology='PUnstructuredGrid', mesh_kind='Float64')
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='open')
error = a_pvtk_file%xml_writer%write_parallel_dataarray(data_name='temperature', data_type='Float64', &
number_of_components=1)
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='close')
error = a_pvtk_file%xml_writer%write_dataarray(location='cell', action='open')
error = a_pvtk_file%xml_writer%write_parallel_dataarray(data_name='part', data_type='Int32', number_of_components=1)
error = a_pvtk_file%xml_writer%write_dataarray(location='cell', action='close')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_01.vtu')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_02.vtu')
error = a_pvtk_file%finalize()The names, types and numbers of components declared in the .pvtu file must match the data arrays written in every piece: check_pieces verifies it, see Parallel headers. See src/tests/vtk_fortran_write_pvtu.f90 for the complete program, pieces included.
Parallel Image Data (PVTI)
Each rank writes its piece as a .vti file with the same origin and spacing of the whole grid and the extents of the piece (adjacent pieces share the boundary index, as for PVTS); one rank writes the .pvti file with the whole extents, the same origin and spacing, and the extents of each piece. mesh_kind is not needed: image pieces have no points coordinates.
fortran
error = a_pvtk_file%initialize(filename='output.pvti', mesh_topology='PImageData', &
nx1=0, nx2=63, ny1=0, ny2=63, nz1=0, nz2=31, &
origin=[0._R8P, 0._R8P, 0._R8P], spacing=[0.1_R8P, 0.1_R8P, 0.2_R8P])
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='open')
error = a_pvtk_file%xml_writer%write_parallel_dataarray(data_name='phi', data_type='Float64', number_of_components=1)
error = a_pvtk_file%xml_writer%write_dataarray(location='node', action='close')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_01.vti', nx1=0, nx2=32, ny1=0, ny2=63, nz1=0, nz2=31)
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_02.vti', nx1=32, nx2=63, ny1=0, ny2=63, nz1=0, nz2=31)
error = a_pvtk_file%finalize()See src/tests/vtk_fortran_write_vti.f90 for a complete program, serial and parallel.
Parallel Polygonal Data (PVTP)
As for PVTU: each rank writes its piece as a complete .vtp file (local point ids), one rank writes the .pvtp file with the type of the points coordinates (mesh_kind, required) and of each data array, and the list of the pieces:
fortran
error = a_pvtk_file%initialize(filename='output.pvtp', mesh_topology='PPolyData', mesh_kind='Float64')
error = a_pvtk_file%xml_writer%write_dataarray(location='cell', action='open')
error = a_pvtk_file%xml_writer%write_parallel_dataarray(data_name='id', data_type='Int32', number_of_components=1)
error = a_pvtk_file%xml_writer%write_dataarray(location='cell', action='close')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_01.vtp')
error = a_pvtk_file%xml_writer%write_parallel_geo(source='part_02.vtp')
error = a_pvtk_file%finalize()See src/tests/vtk_fortran_write_vtp.f90 for a complete program with the four blocks, serial and parallel.
Multi-block Dataset (VTM)
A VTM file is a composite wrapper that references multiple individual VTK files organised into named blocks.
fortran
use vtk_fortran, only : vtm_file, vtk_file
use penf, only : I4P, R8P
type(vtk_file) :: a_vtk_file
type(vtm_file) :: a_vtm_file
character(15) :: filenames(4)
integer(I4P) :: error, f
filenames = ['block_01.vts', 'block_02.vts', 'block_03.vts', 'block_04.vts']
! write each partition as an independent VTS file
do f = 1, size(filenames, dim=1)
error = a_vtk_file%initialize(format='raw', filename=filenames(f), &
mesh_topology='StructuredGrid', &
nx1=0, nx2=9, ny1=0, ny2=5, nz1=0, nz2=5)
! ... write_piece / write_geo / write_dataarray / write_piece ...
error = a_vtk_file%finalize()
enddo
! assemble the multi-block wrapper
error = a_vtm_file%initialize(filename='output.vtm')
error = a_vtm_file%write_block(filenames=[filenames(1), filenames(2)], &
names=['1','2'], name='first block')
error = a_vtm_file%write_block(filenames=[filenames(3), filenames(4)], &
names=['3','4'], name='second block')
error = a_vtm_file%finalize()Nested blocks
Blocks can contain both datasets and other blocks, to any depth, e.g. to mirror the parts and assemblies of a CAD model. Build the hierarchy step by step with the action argument of write_block:
| Call | Effect |
|---|---|
write_block(action='open', name=...) | open a child block of the current block (or a top-level block) |
write_block(filenames=[...], names=[...], action='write') | write the files as datasets of the current block |
write_block(filenames=[...], names=[...], name=...) | write the files wrapped in a new child block |
write_block(action='close') | close the current block |
fortran
error = a_vtm_file%initialize(filename='machine.vtm')
error = a_vtm_file%write_block(action='open', name='assembly') ! Block 0
error = a_vtm_file%write_block(filenames=['a.vtu', 'b.vtu'], names=['a', 'b'], action='write') ! DataSet 0, 1
error = a_vtm_file%write_block(filenames=['b1.vtu', 'b2.vtu'], name='bolts') ! Block 2: DataSet 0, 1
error = a_vtm_file%write_block(action='open', name='sub-assembly') ! Block 3
error = a_vtm_file%write_block(filenames=['n.vtu'], name='nuts') ! Block 0: DataSet 0
error = a_vtm_file%write_block(action='close') ! (end of Block 3)
error = a_vtm_file%write_block(action='close') ! (end of Block 0)
error = a_vtm_file%finalize()- The children of a block (blocks and datasets together) get the indexes 0, 1, 2, … in the order they are written, as VTK numbers the children of a multi-block dataset. Every
action='open'needs itsaction='close'. - The string form (
filenames='a.vtu b.vtu', names separated by blanks) accepts the sameactionvalues. - The test
src/tests/vtk_fortran_write_vtm_nested.f90writes the hierarchy of issue #25, checked with VTK's reader.
Time series (PVD)
A .pvd file is a collection: it lists the files of a simulation (any VTK XML file written by vtk_file, pvtk_file or vtm_file) with their time, and ParaView loads them as a single time series. Write each time step as usual, then add it to the collection with pvd_file:
fortran
use vtk_fortran, only : pvd_file, vtk_file
use penf, only : I4P, R8P, strz
type(pvd_file) :: pvd
integer(I4P) :: error, step
real(R8P) :: time
error = pvd%initialize(filename='simulation.pvd')
do step = 0, nsteps
! ... advance the solution to `time`, write simulation_NNNN.vtu with a vtk_file ...
error = pvd%write_dataset(filename='simulation_'//trim(strz(step, 4))//'.vtu', timestep=time)
enddo
error = pvd%finalize()writes
xml
<?xml version="1.0"?>
<VTKFile type="Collection" version="1.0" byte_order="LittleEndian">
<Collection>
<DataSet timestep="0.0" part="0" file="simulation_0000.vtu"/>
<DataSet timestep="0.1" part="0" file="simulation_0001.vtu"/>
</Collection>
</VTKFile>- The file is valid at any time. Each
write_datasetwrites its entry and the closing tags, and flushes the file: a run that stops beforefinalize(crash, job killed by the scheduler) still leaves a collection that ParaView opens, with all the steps written so far. - Restart.
pvd%initialize(filename='simulation.pvd', action='append')reopens an existing collection and adds the new steps after the ones already listed (the defaultaction='new'replaces the file). The file must exist and be a collection, otherwiseinitializereturns a non-zero error. - Optional attributes.
write_datasetalso acceptspart(default0; e.g. the rank, when each process writes its own file per step),groupandname. timestepis areal(R8P), written with the shortest representation that reads back exactly.filenameis written as given: relative paths are relative to the directory of the.pvdfile.
See src/tests/vtk_fortran_write_pvd.f90 for a complete program, restart included.
Volatile XML output
In some parallel setups only one process (the master) can access the file system. The other processes can write their files into memory instead: initialize the file with is_volatile=.true., write it as usual, then get its content as a string, send it to the master, which writes it to disk with write_xml_volatile.
fortran
use vtk_fortran, only : vtk_file, write_xml_volatile
type(vtk_file) :: a_vtk_file
character(len=:), allocatable :: xml_volatile
integer(I4P) :: error
! on a process without access to the file system
error = a_vtk_file%initialize(format='binary', filename='part_01.vtr', mesh_topology='RectilinearGrid', &
is_volatile=.true., nx1=nx1, nx2=nx2, ny1=ny1, ny2=ny2, nz1=nz1, nz2=nz2)
! ... write_piece / write_geo / write_dataarray / write_piece, as usual ...
error = a_vtk_file%finalize()
call a_vtk_file%get_xml_volatile(xml_volatile) ! the whole file, as a string
call a_vtk_file%free ! free the memory of the volatile file
! ... send xml_volatile to the master ...
! on the master
error = write_xml_volatile(xml_volatile=xml_volatile, filename='part_01.vtr')- The
binaryandasciiformats support volatile files. The appended formats (raw,raw-zlib,binary-appended) write their data to the file directly: withis_volatile=.true.,initializereturns a non-zero error. - The string is the exact content of the file: written by the master, it is identical to the file written directly (the test
src/tests/vtk_fortran_write_volatile.f90checks it).