Appearance
4. A time series
A simulation writes its solution every few time steps: one file each time. A .pvd collection lists them with their time, and ParaView opens them as one time series, with a play button.
f90
error = series%initialize(filename='heat.pvd')
do cycle=0, steps
if (cycle > 0) then
call step(t)
time = time + dt
endif
if (mod(cycle, every) == 0) then
error = write_file(filename='heat_'//trim(strz(cycle, 4))//'.vtr', time=time, cycle=cycle)
! the collection is valid after each call: a run that stops here still opens in ParaView
error = series%write_dataset(filename='heat_'//trim(strz(cycle, 4))//'.vtr', timestep=time)
endif
enddo
error = series%finalize()Each output is written by the function of chapter 3, with its time and cycle:
f90
function write_file(filename, time, cycle) result(error)
!< Write the temperature at a time step, with its time and cycle.
character(*), intent(in) :: filename
real(R8P), intent(in) :: time
integer(I4P), intent(in) :: cycle
integer(I4P) :: error
type(vtk_file) :: a_vtk_file
error = a_vtk_file%initialize(format='raw', filename=filename, mesh_topology='RectilinearGrid', &
nx1=1, nx2=n, ny1=1, ny2=n, nz1=1, nz2=n, compressor='zlib')
error = a_vtk_file%xml_writer%write_fielddata(action='open')
error = a_vtk_file%xml_writer%write_fielddata(data_name='TIME', x=time)
error = a_vtk_file%xml_writer%write_fielddata(data_name='CYCLE', x=int(cycle, I8P))
error = a_vtk_file%xml_writer%write_fielddata(action='close')
error = a_vtk_file%xml_writer%write_piece(nx1=1, nx2=n, ny1=1, ny2=n, nz1=1, nz2=n)
error = a_vtk_file%xml_writer%write_geo(x=x, y=x, z=x)
error = a_vtk_file%xml_writer%write_dataarray(location='node', action='open', scalars='temperature')
error = a_vtk_file%xml_writer%write_dataarray(data_name='temperature', x=t, one_component=.true.)
error = a_vtk_file%xml_writer%write_dataarray(location='node', action='close')
error = a_vtk_file%xml_writer%write_piece()
error = a_vtk_file%finalize()
endfunction write_filepvd_file%write_datasetadds a file and its time step to the collection.- The collection is a valid file after every call: if the run crashes or is killed, everything written so far still opens in ParaView.
- The time and the cycle are written in each file too, as field data: chapter 8 restarts from them.
heat_4.f90
f90
program heat
!< Tutorial, chapter 4: a time series, one file every 2 time steps, collected in heat.pvd.
use penf, only : I4P, I8P, R8P, strz
use vtk_fortran, only : pvd_file, vtk_file
implicit none
integer(I4P), parameter :: n=24
real(R8P), parameter :: h=1._R8P/(n - 1)
real(R8P), parameter :: dt=0.15_R8P*h**2
integer(I4P), parameter :: steps=36 ! time steps of the run
integer(I4P), parameter :: every=2 ! time steps between two outputs
real(R8P) :: x(n), t(n,n,n)
real(R8P) :: time
type(pvd_file) :: series
integer(I4P) :: i, j, k, cycle, error
x = [(real(i - 1, R8P)/(n - 1), i=1, n)]
do k=1, n ; do j=1, n ; do i=1, n
t(i,j,k) = blob(x(i), x(j), x(k), [0.35_R8P, 0.4_R8P, 0.5_R8P]) + 0.6_R8P*blob(x(i), x(j), x(k), [0.7_R8P, 0.65_R8P, 0.45_R8P])
enddo ; enddo ; enddo
t(1,:,:) = 0 ; t(n,:,:) = 0 ; t(:,1,:) = 0 ; t(:,n,:) = 0 ; t(:,:,1) = 0 ; t(:,:,n) = 0
time = 0
error = series%initialize(filename='heat.pvd')
do cycle=0, steps
if (cycle > 0) then
call step(t)
time = time + dt
endif
if (mod(cycle, every) == 0) then
error = write_file(filename='heat_'//trim(strz(cycle, 4))//'.vtr', time=time, cycle=cycle)
! the collection is valid after each call: a run that stops here still opens in ParaView
error = series%write_dataset(filename='heat_'//trim(strz(cycle, 4))//'.vtr', timestep=time)
endif
enddo
error = series%finalize()
print '(A,I0,A,ES10.3,A,F6.3)', 'heat.pvd: ', steps/every + 1, ' files, until time ', time, ', maximum temperature ', &
maxval(t)
contains
function write_file(filename, time, cycle) result(error)
!< Write the temperature at a time step, with its time and cycle.
character(*), intent(in) :: filename
real(R8P), intent(in) :: time
integer(I4P), intent(in) :: cycle
integer(I4P) :: error
type(vtk_file) :: a_vtk_file
error = a_vtk_file%initialize(format='raw', filename=filename, mesh_topology='RectilinearGrid', &
nx1=1, nx2=n, ny1=1, ny2=n, nz1=1, nz2=n, compressor='zlib')
error = a_vtk_file%xml_writer%write_fielddata(action='open')
error = a_vtk_file%xml_writer%write_fielddata(data_name='TIME', x=time)
error = a_vtk_file%xml_writer%write_fielddata(data_name='CYCLE', x=int(cycle, I8P))
error = a_vtk_file%xml_writer%write_fielddata(action='close')
error = a_vtk_file%xml_writer%write_piece(nx1=1, nx2=n, ny1=1, ny2=n, nz1=1, nz2=n)
error = a_vtk_file%xml_writer%write_geo(x=x, y=x, z=x)
error = a_vtk_file%xml_writer%write_dataarray(location='node', action='open', scalars='temperature')
error = a_vtk_file%xml_writer%write_dataarray(data_name='temperature', x=t, one_component=.true.)
error = a_vtk_file%xml_writer%write_dataarray(location='node', action='close')
error = a_vtk_file%xml_writer%write_piece()
error = a_vtk_file%finalize()
endfunction write_file
subroutine step(t)
!< Advance the temperature by one explicit time step of the heat equation; the walls stay at 0.
real(R8P), intent(inout) :: t(:,:,:)
t(2:n-1,2:n-1,2:n-1) = t(2:n-1,2:n-1,2:n-1) + dt/h**2*(t(1:n-2,2:n-1,2:n-1) + t(3:n,2:n-1,2:n-1) + &
t(2:n-1,1:n-2,2:n-1) + t(2:n-1,3:n,2:n-1) + &
t(2:n-1,2:n-1,1:n-2) + t(2:n-1,2:n-1,3:n) - 6*t(2:n-1,2:n-1,2:n-1))
endsubroutine step
pure function blob(x, y, z, centre) result(t)
!< A hot blob: a Gaussian bump of temperature 1 at its centre.
real(R8P), intent(in) :: x, y, z, centre(3)
real(R8P) :: t
t = exp(-((x - centre(1))**2 + (y - centre(2))**2 + (z - centre(3))**2)/0.04_R8P)
endfunction blob
endprogram heatRunning it
$ heat
heat.pvd: 19 files, until time 1.021E-02, maximum temperature 0.368$ cat heat.pvd
<?xml version="1.0"?>
<VTKFile type="Collection" version="1.0" byte_order="LittleEndian">
<Collection>
<DataSet timestep="0.0" part="0" file="heat_0000.vtr"/>
<DataSet timestep="0.0005671077504725897" part="0" file="heat_0002.vtr"/>
<DataSet timestep="0.0011342155009451795" part="0" file="heat_0004.vtr"/>
<DataSet timestep="0.0017013232514177692" part="0" file="heat_0006.vtr"/>
<DataSet timestep="0.002268431001890359" part="0" file="heat_0008.vtr"/>
<DataSet timestep="0.0028355387523629483" part="0" file="heat_0010.vtr"/>
<DataSet timestep="0.0034026465028355376" part="0" file="heat_0012.vtr"/>
<DataSet timestep="0.003969754253308127" part="0" file="heat_0014.vtr"/>
<DataSet timestep="0.004536862003780716" part="0" file="heat_0016.vtr"/>
<DataSet timestep="0.0051039697542533055" part="0" file="heat_0018.vtr"/>
<DataSet timestep="0.005671077504725895" part="0" file="heat_0020.vtr"/>
<DataSet timestep="0.006238185255198484" part="0" file="heat_0022.vtr"/>
<DataSet timestep="0.0068052930056710734" part="0" file="heat_0024.vtr"/>
<DataSet timestep="0.007372400756143663" part="0" file="heat_0026.vtr"/>
<DataSet timestep="0.007939508506616252" part="0" file="heat_0028.vtr"/>
<DataSet timestep="0.008506616257088843" part="0" file="heat_0030.vtr"/>
<DataSet timestep="0.009073724007561434" part="0" file="heat_0032.vtr"/>
<DataSet timestep="0.009640831758034025" part="0" file="heat_0034.vtr"/>
<DataSet timestep="0.010207939508506616" part="0" file="heat_0036.vtr"/>
</Collection>
</VTKFile>heat.pvd played in ParaView: the temperature on the horizontal middle plane, raised as a surface, while the blobs merge and the cube cools down.

What you learned
One file per output, listed by a pvd_file with its time: the collection is always valid. Reference: Time series.
Next: 5. An unstructured mesh, or jump to 8. Restart.