Chapter 18 — Key Takeaways (Modernizing Legacy Fortran)

A one-page reference for taking FORTRAN 77 to modern Fortran without changing the answer.

The eight-step recipe (in order)

# Step Legacy → Modern Home chapter
1 Add implicit none implicit typing → declare everything Ch. 2
2 Free-form source fixed columns → free-form (findent/fprettify) Ch. 2
3 COMMON → modules memory overlay → typed, checked namespace Ch. 8
4 Add intent silent inoutintent(in/out/inout) Ch. 6
5 Assumed-size → assumed-shape a(*)/a(n)a(:); size travels with the array Ch. 6
6 GOTO → structured control GOTO/arith. IFdo/exit/cycle/if/select case Ch. 4
7 EQUIVALENCE → types/transfer memory aliasing → maxval/reshape, transfer, derived types Ch. 9
8 Add error handling silent failure → error stop, stat=, iostat=, validation Ch. 13

Rule of thumb: safest/most-mechanical first (1, 2, 6), structural (interface-changing) next (3, 4, 5), EQUIVALENCE case-by-case (7), error handling last (8, easiest once the code is clean).

The three core terms

Term One-line meaning
Incremental modernization Small, individually verified changes; the code compiles and passes its tests at every step.
Regression test Compare output to a golden reference from the trusted original; fail if it moves beyond tolerance.
Numerical equivalence Same results — bit-for-bit (arithmetic preserved) or close enough (a stated tolerance).
Characterization test A test that pins current behavior (not intended behavior) so you can refactor underneath it.

Bit-for-bit vs "close enough"

Demand bit-for-bit when… Fall back to tolerance when…
implicit none, free-form, COMMON→module precision changed (realreal(dp))
intent, assumed-shape added a reduction/summation reordered
GOTOdo (arithmetic untouched) -ffast-math/FMA contraction enabled, or a different compiler

Bit-for-bit is a property of source plus compiler plus flags — pin them if you promise it. Compare numbers within tolerance, never diff text (format/whitespace ≠ numerical difference).

Modern replacements at a glance

! COMMON  ->  module
module grid_data
  implicit none
  integer, parameter :: dp = selected_real_kind(15, 307)
  real(dp) :: t(21,21)
  integer  :: n
end module grid_data

! GOTO convergence loop  ->  do / exit  (exit = negation of the legacy continue test)
do
  iters = iters + 1
  ! ... one sweep, compute dmax ...
  if (dmax <= tol .or. iters >= maxit) exit
end do

! assumed-size + no intent  ->  assumed-shape + intent
subroutine relax(t, tol, maxit, iters)
  real(dp), intent(inout) :: t(:,:)
  real(dp), intent(in)    :: tol
  integer,  intent(in)    :: maxit
  integer,  intent(out)   :: iters

! EQUIVALENCE (2-D aliased as 1-D, for a max)  ->  array intrinsic
tmax = maxval(t(1:n, 1:n))

! EQUIVALENCE (reinterpret bits)  ->  transfer, explicitly
bits = transfer(1.0_real32, 0_int32)     ! = 1065353216

! error handling
if (n < 3) error stop 'grid must be at least 3x3'

The PLATE result (memorize)

  • 4×4 grid, top edge 100°, three edges 0° → interior settles to 37.5° (upper), 12.5° (lower), from $3a-b=100$ and $a=3b$ ⇒ $8b=100$.
  • Jacobi from a zero interior: max change is 25 after sweep 1, then halves each sweep, first below $10^{-6}$ on sweep 25.
  • All iterates are exact dyadic doubles → the modern version reproduces the legacy one exactly (a bit-for-bit, indeed rounding-free, migration).

Compile flags introduced

Flag Use
gfortran -std=legacy file.f build the fixed-form FORTRAN 77 "before"
gfortran -std=f2018 -Wall file.f90 build the modern "after"
-fcheck=all run-time bounds checking (enabled by assumed-shape)

The ethic

Never rewrite what you can refactor. A rewrite discards the one irreplaceable asset — validated behavior — and re-earns it only at the very end, if ever. Refactoring keeps it the whole time. Improve the engineering; preserve the science; prove it with a regression test.

Project piece advanced this chapter

The Part IV side quest: apply steps 1 (implicit none) and 3 (COMMON → module) to the F77 PLATE kernel, producing project-checkpoint.f90 — which still prints the identical 37.5 / 12.5 field in 25 sweeps. Chapter 19 finishes the remaining steps with the translation dictionary.