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 ! . . . . . endYou 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 ! . . . . . endHere, 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 . . . endBut, 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/ ! . . . . . endBut, 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 . . . endAs 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 . . . endYou 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 . . . endThis 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.0Second, 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 endThis 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 . . . endNow, 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 endWhat'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 endNow, 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 endNow, 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) endThis 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) endThis 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) endNow, 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.