Chapter 26 Exercises: Visualization Output

These exercises take you from writing a single valid VTK file to producing an animated time series and a publication figure — the full pipeline from the array in memory to the picture on the page. Work them at a real terminal with gfortran, ParaView (or VisIt), and a Python with NumPy and matplotlib installed; seeing your own field animate is the point.

Difficulty tiers. ⭐ builds a single skill in isolation; ⭐⭐ combines two or three; ⭐⭐⭐ is a small project that integrates the chapter (and often an earlier one). Solutions to problems marked and to all odd-numbered problems are in appendices/answers-to-selected.md; the rest are yours to check by compiling and by opening the result in ParaView.

A reminder that runs through every problem: never trust "it compiled" — open the file. A VTK writer can compile perfectly and still emit a header that ParaView rejects or, worse, silently misreads. Predict the file's exact contents first, then compile, then open it in a text editor and in ParaView.


Part A — Type, Compile, and Run (predict the output first)

1. † ⭐ Compile and run example-01-legacy-vtk.f90. Before you look at heat_000100.vtk, write down — on paper — exactly what its DIMENSIONS, POINT_DATA, and the six value lines will be. Then open the file and confirm every line. Which single line is the "magic string" that makes it a VTK file at all?

2. ⭐ Change the demo field in example-01 to a 2×2 plate with values u(:,1) = [10, 20], u(:,2) = [30, 40] and dx = dy = 1.0. Predict the complete file, then verify. What does POINT_DATA become, and in what order do the four values appear?

3. † ⭐⭐ Add a second scalar array to the legacy VTK writer. After the temperature block, append a second SCALARS array named error (also double 1) holding field%u - u_exact for some analytic u_exact. Predict what ParaView's coloring dropdown will offer after you open the file. (Hint: multiple SCALARS blocks may follow one POINT_DATA line.)

4. ⭐ Run example-03-simple-formats.f90 and predict both heat.dat and heat_gp.dat before opening them. In heat.dat, why is the first line 3.000 4.000 5.000 and not 0.000 1.000 2.000?

5. † ⭐⭐ Type, compile, and run a program that writes the same 3×2 field as both legacy .vtk and XML .vti (call write_vtk and write_vti). Open both in ParaView. Confirm they render identically, then diff the two files and list three structural differences between the formats.


Part B — Port It (translate to a Fortran output routine)

6. ⭐⭐ Here is a Python routine that dumps a field to legacy VTK. Port it to a Fortran subroutine with the canonical signature write_vtk(field, filename, step), matching its output byte for byte.

def write_vtk(u, dx, dy, filename):
    ny, nx = u.shape
    with open(filename, "w") as f:
        f.write("# vtk DataFile Version 3.0\n")
        f.write("ported from python\n")
        f.write("ASCII\n")
        f.write("DATASET STRUCTURED_POINTS\n")
        f.write(f"DIMENSIONS {nx} {ny} 1\n")
        f.write("ORIGIN 0 0 0\n")
        f.write(f"SPACING {dx} {dy} 1\n")
        f.write(f"POINT_DATA {nx*ny}\n")
        f.write("SCALARS temperature double 1\nLOOKUP_TABLE default\n")
        for j in range(ny):
            for i in range(nx):
                f.write(f"{u[j, i]:.6f}\n")

Watch the index order carefully: NumPy's u[j, i] is row j, column i. What must the Fortran loop be so the same value lands at the same point? (This is the row-major/column-major trap from Chapter 15 in disguise.)

7. † ⭐⭐ Port this MATLAB one-liner, which writes a field as a comma-separated matrix for a spreadsheet, to a Fortran subroutine write_csv(field, filename). Use the unlimited-repeat format from §26.4 and a comma separator.

writematrix(flipud(U), 'heat.csv')   % flipud puts the top row first

8. ⭐⭐⭐ Port a small gnuplot-driving workflow: given a Python script that saves a field and shells out to gnuplot, reproduce the Fortran half (write the 3-column blank-line-separated .dat) and the gnuplot script half (the three commands that draw the pm3d heat map). You are porting the data-writing to Fortran and leaving the plotting to gnuplot — the division of labor §26.4 argues for.


Part C — Find the Bug

9. † ⭐ This writer compiles and runs, but ParaView reports "Error reading ASCII data. Expected 12 points but could only read 6." Find and fix the bug.

write(iu, '(a, 3(1x, i0))') 'DIMENSIONS', field%nx, field%ny, 2   ! nx=3, ny=2
write(iu, '(a, 1x, i0)')    'POINT_DATA', field%nx * field%ny

10. ⭐⭐ A colleague's plate comes out transposed in ParaView — rows and columns swapped — though the values are all present. The header is correct. Here is the value loop:

do i = 1, field%nx
  do j = 1, field%ny
    write(iu, '(f0.6)') field%u(i, j)
  end do
end do

Explain why the picture is transposed and give the one-line fix. What does this cost in memory-access terms (tie your answer to the Performance Note in §26.2)?

11. † ⭐ This first line was meant to be the VTK magic string but ParaView refuses to open the file. What is wrong, and why is VTK so unforgiving here?

write(iu, '(a)') '#vtk DataFile Version 3.0'

12. ⭐⭐ A time series loads into ParaView but plays out of order: step 1000 appears before step 200. The frames are named with this helper. Diagnose the bug and fix it.

write(buf, '(a, i0, a)') 'heat_', step, '.vtk'   ! step 200 -> 'heat_200.vtk'
name = trim(buf)

13. † ⭐⭐ This .vti writer produces a file ParaView opens but shows as empty (no data to color by). The extent and geometry are right. What is missing or wrong in the DataArray/PointData block?

write(iu,'(a)') '      <PointData>'
write(iu,'(a)') '        <DataArray type="Float64" format="ascii">'

Part D — Modernize It

14. ⭐⭐ Here is a genuine-looking FORTRAN 77-era output routine that dumps a field with a bespoke ASCII format and a hard-coded unit number. Modernize it: implicit none, free-form, newunit, intent, assumed-shape argument, kind-parameterized reals — and make it write standard legacy VTK instead of the ad-hoc format, so it opens in ParaView.

      SUBROUTINE DUMP(U, NX, NY)
      DIMENSION U(NX,NY)
      OPEN(7, FILE='dump.txt')
      WRITE(7,*) NX, NY
      DO 10 J=1,NY
      DO 10 I=1,NX
      WRITE(7,*) U(I,J)
   10 CONTINUE
      CLOSE(7)
      RETURN
      END

15. † ⭐⭐ A legacy code writes one giant text file per run with every timestep concatenated (no way to scrub to a frame). Modernize the strategy: describe (and sketch in code) how to split it into a zero-padded per-step VTK time series, and explain what capability in ParaView this unlocks that the single concatenated file cannot offer.


Part E — Design It (extend the heat solver)

16. ⭐⭐ Extend the Project Checkpoint driver to also accumulate a heat.pvd collection file (§26.3) as it saves frames, so ParaView shows the slider in physical seconds. Compute each frame's time as step * dt. Sketch the three writing moments: the header (once, before the loop), one <DataSet .../> line per saved frame, and the two closing tags (once, after the loop).

17. † ⭐⭐⭐ Design a write_output dispatcher for the solver: a single subroutine write_output(field, step, fmt) where fmt is a character(len=*) selecting 'vtk', 'vti', 'matrix', or 'gnuplot', dispatching to the right writer with a select case. Give it a sensible default and an error stop on an unknown format. Which chapter's construct is select case (Chapter 4)?

18. ⭐⭐ Add a write_centerline(field, filename) routine that writes a two-column x u file of the temperature along the horizontal centerline (j = ny/2), for a quick line plot in gnuplot or matplotlib. This is the 1-D cross-section scientists reach for constantly. Predict its length for nx = 101.

19. † ⭐⭐⭐ Design the 3-D generalization of write_vtk: a field u(:,:,:) on an nx × ny × nz grid. What changes in DIMENSIONS, POINT_DATA, the loop nest, and the SPACING line? Write the routine and state the VTK point ordering for three dimensions.


Part F — Back of the Envelope

20. ⭐ A run saves a 256 × 256 field as ASCII legacy VTK every 50 steps for 50,000 steps. About how many frames is that, and — taking each value as roughly 9 ASCII bytes (0.123456\n) — about how large is the whole time series? Would you keep it as ASCII VTK, or reach for the binary formats of Chapter 25?

21. † ⭐⭐ The same 256 × 256 field in double-precision binary is 256*256*8 bytes per frame. Compare the per-frame size of ASCII VTK (from problem 20) with binary, and state the two costs ASCII pays for its human-readability. Which cost matters more at a billion cells?

22. ⭐⭐ You want a smooth 10-second animation at 30 frames per second. Your run is 90,000 timesteps. What save_every gives you enough frames, and how many files is that? (Round to a convenient value.)

23. † ⭐ Estimate: writing one 1000 × 1000 ASCII VTK frame is a million write statements of ~9 bytes. If your disk sustains 500 MB/s, is the write more likely limited by disk bandwidth or by the per-write formatting overhead? (You need not be exact — reason about the order of magnitude and name the likely bottleneck.)


Part G — Interleaved (earlier chapters resurface)

24. ⭐⭐ (Ch. 12) The frame_name helper uses i6.6, which holds up to 999,999 steps. Your run needs 2 million steps. Rewrite the format so filenames stay sortable, and state the new fixed width. What happens if you don't widen it — what does i6.6 write for step 1,000,000?

25. † ⭐⭐ (Ch. 7) Explain, using the edit descriptors from Chapter 7, exactly what write(iu, '(a, 3(1x, i0))') 'DIMENSIONS', 100, 80, 1 places in the record, character group by character group. Why i0 and not i4?

26. ⭐⭐ (Ch. 9) The writers all take a field_t carrying nx, ny, dx, dy, u(:,:). Why is passing the single derived type better than passing five separate arguments write_vtk(u, nx, ny, dx, dy, ...)? Tie your answer to the derived-type motivation of Chapter 9.

27. † ⭐⭐⭐ (Ch. 5, Ch. 24) The value-writing loop do j; do i; write u(i,j) walks the array in column-major order. (a) Explain why that is the cache-friendly order for a Fortran array. (b) The Chapter 24 five-point stencil update u(i,j) = f(u(i±1,j), u(i,j±1)) — does it also want i innermost for the same reason? (c) State the general rule connecting VTK's required write order, Fortran's memory layout, and loop nesting.

28. ⭐⭐ (Ch. 24) Your solver visualizes beautifully for a while, then every frame goes uniformly to a huge value and ParaView shows a single flat color. The output code is fine. What numerical failure from Chapter 24 does the visualization just make visible, and what does the picture look like at the instant it goes wrong?