Ryo Furue (Email: my family name AT hawaii PERIOD edu)
There are already many tutorials exploring various features of the Fortran 90 (F90) language in a systematic way. Instead of adding yet another tutorial of such kind, I here focus on how to improve your Fortran 77 (F77) programs by using small subsets of the F90 language. To do so, I'll show small code in F77, point out problems in it, and propose a solution or improvement using some features of F90.
I think this approach is useful to convince scientists that F90 is useful and easy to adopt. I've found persistent resistance among fellow scientists against F90, reasons for the resistance including
A: It's hard to climb up to this floor everyday.If you find this conversation funny, you understand how I feel.
B: But, why don't you use the elevator?
A: Um, I heard it's for expert building-climbers. I just have to climb it once a day, so it's not worth it to learn the new technology.
subroutine sub1
common /pars/a, b, d
! . . . . .
end
subroutine sub2
common /pars/a, b, c, d
! . . . . .
end
You sometimes modify a COMMON block in one subroutine
and forget to update other subroutines using the COMMON block.
This error often leads to a long debug session.
What's the problem? The fundamental source of the problem is that you have identical information at many places. (This is a recurring theme as we'll see.)
Put the COMMON block in a MODULE and USE it elsewhere:
module pars_mod
common /pars/a, b, d
save
end module pars_mod
subroutine sub1
use pars_mod
! . . . . .
end
subroutine sub2
use pars_mod
! . . . . .
end
Here, the original problem does not exist, because
its source (repetition of the same information)
is gone.
In fact, it's better to avoid COMMON blocks altogether. The solution above can be rewritten as
module pars_mod
real a, b, d
save
end module pars_mod
subroutine sub1
use pars_mod
! . . . . .
end
subroutine sub2
use pars_mod
! . . . . .
end
You may have noticed the SAVE statements in the MODULEs above. You might think this is an extra complication in F90. It's not. In fact you usually have to save COMMON variables in F77, too. Did you know that? Indeed, situations where you need to save COMMON variables are the same as those where you need to save MODULE variables. When you have to save and when you don't is a complicated subject, which I don't want to discuss here. Don't worry. Since you want COMMON and MODULE variables to be saved 99% of the time, it's safe always to save them.
"Wait," I hear you say, "I've never used SAVE but my COMMON blocks have always worked for me." Right. For the same reason, your MODULE variables usually work without SAVE-ing them. I just recommend using SAVE, to be on the safe side.
How do you give initial values to COMMON variables? You might assign values to them in a subroutine:
subroutine sub1
common /pars/a, b
a = 3.14
b = -999.0
! . . . . .
end
subroutine sub2
common /pars/a, b
common /foos/kk, mm
kk = -1
mm = 10
!. . . use a, b, kk, and mm . . .
end
But, what if you forget to call
sub1 before calling sub2 ?
That's bad, because in sub2, those variables won't have
proper values.
Also, it's hard to keep track of who initializes which COMMON
variables. In the example above,
sub1 initializes
a and b and
sub2 initializes
kk and mm.
If you have many COMMON blocks, this would be pretty messy.
Or you might use DATA statements:
subroutine sub1
common /pars/a, b
data a/3.14/, b/-999.0/
! . . . . .
end
subroutine sub2
common /pars/a, b
common /foos/kk, mm
data kk/-1/, mm/10/
! . . . . .
end
But, still you aren't sure whether or not a
and b are initialized in sub2.
The problem of who initializes what isn't solved, either.
You can guarantee the initialization of COMMON variables by putting all COMMON blocks and their initializations to the main program:
program main
common /pars/a, b
common /foos/kk, mm
data a/3.14/, b/-999.0/
data kk/-1/, mm/10/
. . . . .
end
subroutine sub1
common /pars/a, b
. . . use a and b . . .
end
subroutine sub2
common /pars/a, b
common /foos/kk, mm
!. . . use a, b, kk, and mm . . .
end
As a result, you end up with having all COMMON blocks in the main
program, whether you use them in the main program or not. Your
program becomes the messier for that.
The best solution within F77 to this problem is to use a BLOCK DATA.
block data
common /pars/a, b
common /foos/kk, mm
data a/3.14/, b/-999.0/
data kk/-1/, mm/10/
end
subroutine sub1
common /pars/a, b
!. . . use a and b . . .
end
subroutine sub2
common /pars/a, b
common /foos/kk, mm
!. . . use a, b, kk, and mm . . .
end
You initialize all COMMON blocks in a single BLOCK DATA.
At least, we have solved the original
problems: The COMMON variables are guaranteed to be initialized before
any subroutines are called, and it is now clear that initial values
are all given in the BLOCK DATA.
This solution, however, still has a problem of the repetition of the same information. For example, if you introduce another common block, or another variable in an existing COMMON block, you have to edit the BLOCK DATA, too, which you sometimes forget.
When the two COMMON blocks in the problem above aren't much related, it's natural to put them in different MODULEs:
module pars_mod
real:: a = 3.14, b = -999.0
save
end module pars_mod
module foos_mod
integer:: kk = -1, mm = 10
save
end module foos_mod
subroutine sub1
use pars_mod
!. . . use a and b . . .
end
subroutine sub2
use pars_mod
use foos_mod
!. . . use a, b, kk, and mm . . .
end
This solution is much cleaner than the F77 counterpart
for a few reasons. First, the definition of variables
and their initial values are close together:
real:: a = 3.14, b = -999.0
Second, the repetition of information is reduced.
Finally, you can keep related variables in a single place
and unrelated variables separated; in the example above,
a and b are put in one MODULE
and kk and mm are in another.
The last point is another important, recurring theme: Related information should be put in one place and unrelated information, separated. That will lead to a better organized (and therefore easier to understand) program.
In the example above, the initialization of COMMON variables have been just assignments of constants. What if you need more than just assignments to initialize those variables? For example, you sometimes need to conduct some computation to determine initial values for COMMON variables. BLOCK DATA cannot be used because it cannot contain any executable statements. In that case, your best solution within F77 would be
subroutine init_pars
common /pars/a, b
! . . . . . .
read(. . .) dd, ee
call compute_a_b(a, b, dd, ee)
end
subroutine compute_a_b(a, b, dd, ee)
! . . . . . .
end
program main
call init_pars
! . . . . . .
end
subroutine sub1
common /pars/a, b
! . . . use a and b
end
This isn't bad. But, it's better to put the information concerning
the variables a and b together (better
organization).
MODULEs can contain not only variables but also subroutines:
module pars_mod
real a, b
save
contains
subroutine init_pars
. . . . . .
read(. . .) dd, ee
call compute_a_b(a, b, dd, ee)
end subroutine init_pars
subroutine compute_a_b(a, b, dd, ee)
! . . . . . .
end subroutine compute_a_b
end module pars_mod
program main
use pars_mod
call init_pars
. . . . . .
end
subroutine sub1
use pars_mod
. . . use a and b . . .
end
Now, MODULE pars_mod contains everything related to
variables a and b.
You often need to use constants at various places in your program. How should you define and use them? The worst strategy is
subroutine sub1
. . . . . .
a = b * 1000
end
subroutine sub2
. . . . . .
d = e / 1000
. . . . . .
k = i + 1000
end
What's this magic number 1000 ?
What if you later need to change its value?
Should you change all occurrences of the value 1000 ?
or do some of the occurrences have different meanings?
The source of this problem is again the repetition of information.
A better approach is to give the value a name and use the latter throughout your program:
subroutine sub1
parameter (nx = 1000)
. . . . . .
a = b * nx
end
subroutine sub2
parameter (nx = 1000)
parameter (mode = 1000)
. . . . . .
d = e / nx
. . . . . .
k = i + mode
end
Now, it's clear that the two occurrences of 1000 in
sub2 have different meanings, so that even if you need to
change the value of nx, you won't make the mistake of
changing the value of mode.
The program above still has a problem: When you change the value
of nx, you may forget to do so in
some of the subroutines. Repetition of information hurts, again.
You put the definition of constants into a MODULE:
module pars_mod
parameter (nx = 1000)
end pars_mod
subroutine sub1
use pars_mod
. . . . . .
a = b * nx
end
subroutine sub2
use pars_mod
parameter (mode = 1000)
. . . . . .
d = e / nx
. . . . . .
k = i + mode
end
Now, you have only to change the value of nx in MODULE
pars_mod.
Notice that I didn't put the other constant (mode)
in a MODULE.
That's okey if the constant is used only in sub2.
Subroutines sometimes need to share a lot of values. Should we pass them as arguments to the subroutines?
subroutine sub1(a, b, p, q, r, s, t, u)
! . . . use p, q, r, s, t, and u . . .
end
subroutine sub2(kkk, p, q, r, s, t, u)
! . . . use p, q, r, s, t, and u . . .
end
subroutine othersub(x, y, z, p, q, r, s, t, u)
! . . . Doesn't use p, q, r, s, t, u . . .
call sub2(kkk, p, q, r, s, t, u)
end
program main
p = 3.14
q = -999.0
r = p + 3*q
read(*,*) s
t = s * p
u = t * t + s
call sub1(a, b, p, q, r, s, t, u)
call othersub(x, y, z, p, q, r, s, t, u)
end
This style will quickly become cumbersome. You have to pass
all the shared variables down to all subroutines requiring them.
In particular, look at othersub, which needs to receive
those variables and pass them onto sub2, even though
othersub itself doesn't use them.
You are therefore tempted to use COMMON blocks to avoid this problem:
subroutine sub1(a, b)
common /pars/p, q, r, s, t, u
! . . . use p, q, r, s, t, and u . . .
end
subroutine sub2(kkk)
common /pars/p, q, r, s, t, u
! . . . use p, q, r, s, t, and u . . .
end
subroutine othersub(x, y, z)
! . . . . .
call sub2(kkk)
end
program main
common /pars/p, q, r, s, t, u
p = 3.14
q = -999.0
r = p + 3*q
read(*,*) s
t = s * p
u = t * t + s
call sub1(a, b)
call othersub(x, y, z)
end
This is an improvement. It is now clear that othersub has
nothing to do with p, q, r,
s, t, or u.
Still, this isn't good enough: The fact that sub1
and sub2 share the variables isn't clear; the fact
that othersub doesn't care about them isn't clear;
nor is it clear
who is responsible for the values of the shared variables.
You put the shared variables and subroutines that use them into a MODULE:
module mysubs
real, private:: p, q, r, s, t, u
save
contains
subroutine init_mysubs
p = 3.14
q = -999.0
r = p + 3*q
read(*,*) s
t = s * p
u = t * t + s
end subroutine init_mysubs
subroutine sub1(a, b)
! . . . use p, q, r, s, t, and u . . .
end subroutine sub1
subroutine sub2(kkk)
! . . . use p, q, r, s, t, and u . . .
end subroutine sub2
end module mysubs
subroutine othersub(x, y, z)
use mysubs
! . . . . .
call sub2(kkk)
end
program main
use mysubs
call init_mysubs
call sub1(a, b)
call othersub(x, y, z)
end
Now, all the problems stated above are solved.
1) It's clear that p, q, r,
s, t, and u are shared
only by sub1 and sub2;
2) It's clear who is responsible for determining their values;
3) It's clear that othersub has nothing to do with
them.