Chapter 17 — Key Takeaways (Reading FORTRAN 77)
A one-page reference for reading fixed-form legacy code and mapping every old construct to its modern replacement. This is a reading chapter, so the "syntax" below is the syntax you must recognize, not write.
Fixed-form column rules (memorize)
| Columns | Meaning |
|---|---|
| 1–5 | statement label (a number, referenced by GO TO/DO/FORMAT) |
| 6 | continuation marker — nonblank = "continue the previous line" |
| 7–72 | the statement itself |
| 73–80 | ignored (historically the punch card's sequence number) |
C or * in column 1 |
the whole line is a comment (no ! in strict F77) |
The reflex: a baffling error in a .f file is a column nine times in ten — count columns before you
suspect anything cleverer. Build old code with gfortran -std=legacy file.f.
The legacy → modern dictionary (the core of the chapter)
| Legacy construct | One-line "what it is" | Modern replacement |
|---|---|---|
COMMON /blk/ a, b, n |
shared memory, associated by position, unchecked | a module with typed variables (Ch. 8) |
BLOCK DATA |
the only unit that may initialize named COMMON |
a module parameter / declaration initializer |
INCLUDE 'x.inc' |
raw textual paste to keep COMMON in sync |
use module, only: … |
EQUIVALENCE (a, b) |
a and b are the same bytes (aliasing) |
allocatable (reuse), reshape (alias), transfer (bits) |
GO TO 100 |
unconditional jump to a label | do/exit/cycle, if |
GO TO (l1,l2,l3), k |
computed GOTO — jump by integer index |
select case (with case default) |
IF (e) l1,l2,l3 |
arithmetic IF — jump on sign(e) |
if … else if … end if |
F(x) = expr |
statement function — inline one-liner | internal pure function (Ch. 6) |
ENTRY name |
second entry point sharing body/state | separate module procedures |
| implicit typing | first letter sets the type; I–N are INTEGER |
implicit none + explicit declarations (Ch. 2) |
DATA a, b /1.0, 2.0/ |
initialize via parallel value list | declaration initializer (real :: a = 1.0) |
nHtext (Hollerith) |
text named by length, pre-CHARACTER |
quoted character string |
The two dangers to internalize
COMMONis a global untyped memory pool. Association is by position, and each routine is compiled separately, so a reordered or retyped declaration silently corrupts data with no error. Keeping the declaration in sync across files is what spawnedINCLUDE.EQUIVALENCEis deliberate aliasing. Two names, one storage: a write through one changes the other invisibly, type overlays are non-portable, and it defeats the optimizer (the opposite of Fortran's no-aliasing advantage). Find its intent — reuse, reshape, or bit-reinterpret — before you remove it.
Implicit typing, at a glance
First letter I J K L M N -> INTEGER
First letter A-H, O-Z -> REAL
Two bites: a typo invents a new garbage variable (no "undeclared" error), and a name you meant as real
but that starts I–N triggers silent integer division (1/N = 0). implicit none removes both.
The five-step reading method (any legacy file)
- Shape first — read the comments and routine names; get the job description before any logic.
- Shared state — find the
COMMONblocks; that is the data model. - Loops and exits — locate
DO/GO TOloops and theIF (…) GO TOthat leaves them. - The kernel — find the few lines that do the math (often a statement function + one update).
- Hand-trace one step — confirm your reading against a concrete number before trusting it.
Panic comes from reading line 1 before line 2; calm comes from understanding the shape before any line.
The PLATE kernel (this chapter's legacy anchor)
- What it is: about 100 lines of FORTRAN 77 (four program units,
IMPLICIT DOUBLE PRECISION) solving steady-state heat conduction (Laplace's equation $\nabla^2 T = 0$) by Jacobi relaxation — repeatedly replacing each interior point with the average of its four neighbours until the largest change drops belowTOL. Structure:PROGRAM PLATE→SETBC(boundaries) →RELAX(the GOTO convergence loop) →OUTPT. - The tell for Jacobi: the separate
TNEWarray — each sweep computesTNEWfrom the oldT, then copiesTNEWback intoT. Updating from old neighbours is Jacobi; updating in place is Gauss–Seidel. - The four-line heart: the stencil
TNEW(I,J) = AVG(T(I-1,J), T(I+1,J), T(I,J-1), T(I,J+1))plus theDIFF/DMAXchange-tracking. (PLATEuses noEQUIVALENCEorBLOCK DATA— the recipe is a menu.) - Why its answer is checkable: on the 4×4 test grid (top edge hot 100°, three edges cold 0°) the interior
follows from symmetry —
3a - b = 100,a = 3b→b = 12.5,a = 37.5— dyadic and exact. It printsconverged in 25 iterationsand the interior37.5000 / 12.5000.
Compile flag introduced
-std=legacy — accept obsolete features and read fixed-form source; how you build an inherited .f file.
Project piece added this chapter
Not solver code — a read, built, annotated, and output-pinned PLATE kernel. You compile it with
-std=legacy, annotate each legacy construct with its modern replacement, and save its output as the
regression reference for Chapter 18, where you
modernize it step by step without changing the answer.
The ethic
Read before you rewrite; pin before you touch. Legacy scientific code is an inheritance — its validated numerics are worth more than its source. Reconstruct what it does, capture a reference output, and only then modernize — improving the engineering while proving you preserved the science.