Skip to content

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_file
  • pvd_file%write_dataset adds 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 heat

Running 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.

an animation of the temperature on the middle plane of the cube, two peaks merging and decaying

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.