COMMON Blocks, BLOCK DATA and EQUIVALENCE

COMMON Block

Lengthy argument lists in subroutines and user-defined functions can occur as modularised programs grow ever larger, requiring more and more information to be passed between program units. The COMMON block, a piece of shared memory in the computer, is another method for passing information between program units. Data stored in a COMMON block is not passed between program units via argument lists, but through the COMMON statement near the beginning of each program unit.

There are two types of COMMON blocks: blank and named. A program may contain only one blank COMMON block but any number of named COMMON blocks. Every COMMON block must be declared in every program unit in which the information stored therein is needed. In addition, the unique blank COMMON block must be declared in the main program.

The blank COMMON block is set up with the statement

COMMON variable-list

and the named COMMON block is declared by

COMMON /name/ variable-list

where the name between the forward slashes is the name of the named COMMON block.

Every subroutine or user-defined function that uses data stored in the COMMON block, blank or named, must have a similar statement to those above. The variable names do not need to match between program units but it is vital that their types and the order in which they appear in the list are identical.

Example

Consider the following program fragment:

      PROGRAM MAIN
      INTEGER A
      REAL    F,R,X,Y
      COMMON  R,A,F
      A = -14
      R = 99.9
      F = 0.2
      CALL SUB(X,Y)
…
      END

      SUBROUTINE SUB(P,Q)
      INTEGER I
      REAL    A,B,P,Q
      COMMON  A,I,B
…
      END

In this example, a blank COMMON block holds three values: a REAL number, an INTEGER number, and another REAL number. Memory is shared in the COMMON block in the following way:

Main Program Common Memory Storage Subroutine
R 99.9 A
A -14 I
F 0.2 B

Note that the variable names for each memory area differ between the main program and the subroutine, but that the number and type of variables are the same as is the order in which they are listed.

Named COMMON blocks are used in much the same manner. Note that a variable cannot appear in more than one named COMMON block in a program unit.

Blank Versus Named

Declaration

Blank COMMON blocks must be declared in the main program. It is not necessary to declare named COMMON blocks in the main program unless they are used there.

Length

Blank COMMON blocks need not be the same length in different program units. However, a named COMMON block must be exactly the same length wherever it appears. This means that some knowledge about how the computer stores information is necessary. That is, the programmer must know how much storage each variable or array takes in order to ensure that the named COMMON blocks are the same length.

Initialisation

Variables in blank COMMON blocks may be initialised with READ or assignment statements but not with a DATA statement. The same restrictions apply to named COMMON blocks with one important difference: named COMMON blocks may be initialised in a special nonexecutable subroutine called a BLOCK DATA subprogram.

SAVE Statement

When a subroutine or function is exited, local variables become undefined. The same thing may happen with the variables stored in named COMMON blocks. Therefore, it is possible to SAVE an entire named COMMON block (but not individual variables in the block) in a procedure with the command

SAVE /named common block1/, /named common block2/, …, /named common blockn/

Variables in a blank COMMON block never become undefined since the it is declared in the main program. Similarly, if a named COMMON block is declared in the main program, then it is unecessary to use the SAVE command in other program units. Note that you cannot use the SAVE command on a blank COMMON block.

BLOCK DATA Subprogram

A BLOCK DATA subprogram consists of the BLOCK DATA statement, any necessary type declarations, a list of the named COMMON blocks and their variables, and one or more DATA statements initialising one or more of the variables appearing in the COMMON blocks. Its sole purpose is to initialise the values in named COMMON blocks.

Example

      BLOCK DATA SETUP
      INTEGER A,B,C
      REAL    I,J,K,L
      COMMON  /AREA1/ A,B,C
      COMMON  /AREA2/ I,J,K,L
      DATA    A,B,C,I,J,K,L/0,1,2,10.0,-20.0,30.0,-40.0/
      END

EXTERNAL Statement

During compilation and linking, if one of the modules containing a procedure is omitted, the linker will almost certainly return an error but this will not happen if it's the BLOCK DATA subprogram that's missing. To avoid this possibility, the name of the BLOCK DATA subprogram unit should be included in an EXTERNAL statement in some or all of the procedures which contain the named COMMON blocks initialised by the BLOCK DATA subprogram.

The general form of the statement is

EXTERNAL ename1, ename2, , enamen

where ename is the name of an external function, subroutine, BLOCK DATA subprogram or dummy procedure in an argument list.

Use of COMMON Blocks

The use of COMMON blocks is discouraged unless there are extremely large amounts of data to be passed between program units. The reason for this is simple: it weakens modularity. Variables in COMMON blocks are global in nature and when one program unit alters a variable in this shared memory area, then it affects all of the other program units which also use this shared area, often with unexpected results. The debugging process becomes that much harder.

EQUIVALENCE Statement

The EQUIVALENCE statement causes two or more items (arrays or variables) to occupy the same memory space. In the early days of FORTRAN, when computer memory was measured in kilobytes, this was a valuable technique to make the most efficient use of very limited memory. Today, computers routinely have gigabytes of memory, but EQUIVALENCE still has a use, as the case study below demonstrates.

The general form of the statement is

EQUIVALENCE (variable-list1), (variable-list2), …, (variable-listn)

where there are one or more variable-lists. The variable-list contains two or more variables or arrays or mixture of the two. The EQUIVALENCE statement occurs near the beginning of a program unit, along with type declarations and other specification statements.

Case Study

Some years ago, one of the authors wrote a FORTRAN 77 program to model the dynamics of planetary satellites by numerical integration of the equations of motion. The orbits were to be fitted to observational data, so it was necessary to integrate not only the coordinates of the satellites, but also the partial derivatives of the coordinates with respect to the initial position and velocity of each satellite, in order to calculate the variational equations.

Thus the (one-dimensional) array of quantities being integrated in the case of N satellites were:

x1, y1, z1, x2, y2, z2, …, xN, yN, zN, ∂x1/∂ξ1, ∂y1/∂ξ1, ∂z1/∂ξ1, …, ∂x1/∂η1, ∂y1/∂η1, ∂z1/∂η1, …, ∂zN/∂ζ'N

where ξ1 denotes the initial value of x1, and so forth.

In the main program, the number of coordinates and free parameters were defined using the PARAMETER statement as NCOORD and NPARAM respectively. For a two-satellite system, there are 6 coordinates and 12 components of the initial position and velocity vectors, hence:

      PARAMETER(NCOORD=6, NPARAM=12)

Then the number of partial derivatives was defined as the product of those two quantities,

      PARAMETER(NPTLS=NCOORD*NPARAM)

and the total number of equations to be integrated was the number of coordinates plus the number of partial derivatives,

      PARAMETER(NEQNS=NCOORD+NPTLS)

The one-dimensional array of quantities being integrated was declared as

      DOUBLE PRECISION P(NEQNS)

and the two-dimensional array of partial derivatives was declared as

      DOUBLE PRECISION PTLS(NCOORD,NPARAM)

Finally, EQUIVALENCE was used to make these two array share the same memory:

      EQUIVALENCE (PTLS,P(NCOORD+1))

In the case of two satellites, this results in the following memory layout:

P(1)  =  x1
P(2)  =  y1
P(3)  =  z1
P(4)  =  x2
P(5)  =  y2
P(6)  =  z2
P(7)  =  PTLS(1,1)  =  ∂x1/∂ξ1
P(8)  =  PTLS(2,1)  =  ∂y1/∂ξ1
P(9)  =  PTLS(3,1)  =  ∂x1/∂ξ1
P(10)  =  PTLS(4,1)  =  ∂x2/∂ξ1
P(11)  =  PTLS(5,1)  =  ∂y2/∂ξ1
P(12)  =  PTLS(6,1)  =  ∂z2/∂ξ1
P(13)  =  PTLS(1,2)  =  ∂x1/∂η1
P(14)  =  PTLS(2,2)  =  ∂y1/∂η1
P(15)  =  PTLS(3,2)  =  ∂z1/∂η1
P(81)  =  PTLS(6,12)  =  ∂z2/∂ζ'2

Why do this? The integration routine required a one-dimensional array of equations, but in the rest of the program, it was preferable to represent the partial derivatives as a two-dimensional array. There were three options:

  1. Don't use a two-dimensional array at all. Calculate the offset of each required partial directly in the array P. This would greatly complicate the program, as well as increasing the likelihood of bugs due to miscalculation of the correct offset.
  2. Copy elements 7 to 81 of the array P into the array PTLS after every integration step. This would be slow and wasteful, because the program performs many thousands of integration steps, and the number of partial derivatives grows quadratically with the number of satellites.
  3. Use EQUIVALENCE to map the two-dimensional array of partial derivatives directly onto the one-dimensional array of integrated quantities.

The third option was the most elegant and the most efficient. It demonstrates that EQUIVALENCE still has a place in modern FORTRAN 77 programming.

There are a few restrictions on the use of the EQUIVALENCE statement. Dummy arguments in external functions and subroutines cannot appear in the variable-list nor can variables which are external function names. It is also illegal to equivalence two or more elements of the same array or do anything else which violates storage sequence rules. It is possible to equivalence different numerical data types but you must know exactly how these values are stored internally in order to do it correctly and sensibly. The resulting code may not be portable to other machines. Use with caution.