Numerical Differentiation


Assignment :

Start Hw14


New Fortran:

COMMON, INCLUDE, BLOCKDATA


Numerical Evaluation of Derivatives

The use of a quadratic to approximate integration also gives us the opportunity to obtain approximations to derivatives. For our previous set of coefficients:

the first derivative can be written as:

and the second derivative can be approximated as:

We will also find that it is useful to know the first derivative halfway between two points (say between xi and xi+1.

If you think about it, that is the same approximation to the first derivative that you would get if you had fit a straight line to the function between xi and xi+1. Such expressions for derivatives have limited value alone, but are tremendously useful in converting Differential equations to algebraic equations, that can be solved on a computer.

COMMON, INCLUDE, and BLOCKDATA

I'm bringing up the subject here of COMMON, INCLUDE, and BLOCKDATA, to wrap up a loose end in your education about older Fortran statements. What you should remember about COMMON is that it is an old way of handling the data communication now provided by modules. The only reason that you would use a COMMON in a new program is if you must have control of the order in which your variables are located in memory, but programming that relies on this knowledge of ordering (variable aa, after variable a1, ...) is not a good idea in most cases. Once you've committed to COMMON blocks, INCLUDE is a good idea to get the power now found in USE, and BLOCKDATA is the only way you can initialize (DATA statements) variables in named COMMON blocks. File this general information away in a corner of your brain in case you have to perform surgery on an old Fortran program.

COMMON

A COMMON block (see trapezoid.f ) is an old but useful feature in Fortran for letting subprograms communicate information. In its simplest form, blank COMMON, the statement name is followed immediately by a list of variables. If the same statement is repeated in different program units, all will agree on the memory location for any listed variables in the order that they are listed. A change in value of one of the listed variables, will be seen by all program units containing that COMMON statement. For example the following program:

program test

common a,b,c

a=1.

b=2.

call sub1

call sub2

stop

end

subroutine sub1

common a,b,c

c = a + b

return

end

subroutine sub2

common a,b,c

print *, 'c = ', c

return

end

will correctly print out a value of three. The main program and two subroutines all agree on the locations for "a", "b", and "c."

One thing should be noticed in this example for those of you who like shortcuts. You might be tempted to replace the common statement in "sub2" with "common c". The result of this replacement would be for the program to print a value of one. As with argument lists, its really order that counts in the variable list for a COMMON block. For this example, the main program, and two subroutines all agree that the first variable name (whatever it may be) listed after the first occurrence of "COMMON" always refers to the same address in memory. The second variable listed after "COMMON" refers to the next address in memory after the space occupied by the first variable, and so on through the last listed variable. Although you can use this order dependency of COMMON to let different subroutines use different names for the same quantity (location), this leads to very unreadable code, and should be avoided. Use identical copies of blank COMMON (or named COMMON), when communicating information.

You may have noticed that I was careful in the last paragraph to refer to the "first occurrence" of COMMON. You are permitted to put multiple blank COMMON statements in a program unit. However, you are not really creating separate COMMON blocks. There can be only one blank COMMON in a program, and multiple COMMON statements just act to extend the length of the single blank COMMON. For example, writing the following two statements:

common a,b,c

common x,y,z

is exactly equivalent to writing

common a,b,c,x,y,z

Having just one COMMON can be fairly constraining, so Fortran also permits named COMMON blocks. The name of the common block immediately follows "COMMON" and is always delimited by "/". Examples of named COMMON are:

common/realvar/x,y,z

common/intvar/ i,j,k

common/miscvar/ x1,i1,y1,j1,z1,k1

The name of the common block is arbitrary and you do not declare its type in a REAL or INTEGER statement. A COMMON may contain variables of any type, and mixtures of different types. However, if you mix data types, you must be a little cautious. Most computers want double precision real variables to begin at even multiples of the double precision word length. If "x1" and "y1" are 64 bit real numbers, and "i1" is a 32 bit integer in the above example, the even 64 bit spacing for the real numbers is broken, and trouble can result. Whenever possible segregate variables into different named COMMONs by type. When not possible (for example blank COMMON), list all of your REAL variables before listing any INTEGER or CHARACTER variables.

So why do we bother to have both blank and named COMMON? The reason is memory assignment. You should recall that when a program is compiled, space is generally reserved for all variables within the resulting executable file. One exception to this rule that we have already seen is the use of ALLOCATABLE arrays. Space for such arrays is not assigned until the program is running and requests a specific size for the array. This is very important for major scientific and engineering applications where arrays can occupy tens to hundreds of megabytes. However, ALLOCATABLE is a recent (Fortran 90) invention. Originally blank COMMON filled this need. Space for any variable listed in blank COMMON is not assigned until the program starts to execute. This last minute allocation of space, has one important side effect. You can not initialize values for any variable in blank COMMON with a DATA statement. There is no place in the executable file to store the initial value.

In addition to providing more convenient grouping of variables, named COMMON fills the need for COMMON variables that should be initialized with a DATA statement. Unless a special (nonstandard) compiler option is used, memory for named COMMONs is reserved in the executable file. However, a potential for significant confusion exists. With the named COMMON appearing in many subprograms (and perhaps not in the main program at all), where are you going to put the DATA statement? The Fortran standard saves you the decision by not letting you put it in any standard program unit. You must create a special BLOCKDATA subprogram to hold the DATA statements. Here is an example of one such routine, that I've chosen to name DATA1 (name is up to you as are any names chosen for named common blocks).

blockdata data1

common/dtdata/ dt,dt1,dt2

common/dxdata/ dx,dx1,dx2

real dt,dt1,dt2

real dx, dx1,dx2

data dt,dt1,dt2/.01.001,.0001/,dx,dx1,dx2/.1,.2,.3/

end

You can create more than one such subprogram, located anywhere in your program. Usually, BLOCKDATAs are placed immediately after the main program, if the full program is contained in a single file. A BLOCKDATA routine is by its nature non-executable. It generally contains only the BLOCKDATA, END, COMMON, DATA, and type statements, but can also contain EQUIVALENCE, PARAMETER, IMPLICIT, and USE (see below).

I mentioned the value of blank COMMON for storing arrays, but haven't said anything about the interaction of COMMON (both blank and named) with the process of array declaration. Based on your current experience you might guess one approach.

real a(10000), b(10000), c(10), d, e

common a, b

common/other/c,d,e

However, you can also set the dimension in the COMMON statements.

real a, b, c, d, e

common a(10000), b(10000)

common/other/c(10),d,e

Either form has the same effect. Given that COMMON establishes a specific memory layout, I prefer the second form, giving the full details of the storage in one location.

One final note on mechanical details of named COMMON. Occasions may arise where the contents of a COMMON are lost. Suppose the main program calls SUBROUTINE A, and SUBROUTINE A calls SUBROUTINE B (but MAIN doesn't call B), and a COMMON named VAL is in A and B but not main. When A returns to MAIN, the Fortran 90 standard doesn't guarantee that the contents of VAL will be preserved for the next call to A. The best way to guarantee the contents of VAL won't disappear is to include a "COMMON/VAL/..." in the main program, even if none of the variables in VAL are used in MAIN. Another way is to use the SAVE statement. Both A and B must include the statement:

save /val/

Important features of COMMON

  1. The order of variables in a common block determines the order of their locations in memory
  2. Mixing integers and character variables with reals in common can cause compiler complaints, and possible inefficiencies. 32 bit machines want to align double precision real numbers on even word boundaries, and character variables can end on any byte.
  3. Blank COMMON does not claim space until execution begins, so there is nowhere to initialize variable values with a DATA statement.
  4. You can use named COMMON to give same information passing features and control of memory order, plus the ability to initialize with DATA statements. However, DATA statements must reside in one special place called a BLOCK DATA routine.

While we are on the subject of COMMON blocks, I want to give another example of the value of EQUIVALENCE statements. Often in large programming applications you need to write the contents of all significant system variables including entire common blocks to a disk file for later continuation of your calculation. This is called a restart dump. Why bother? If you get 4 days into a 5 day long computer calculation and the machine crashes, you will suddenly appreciate the value of these restart dumps. Instead of starting the whole thing from scratch, you can pick up the last restart dump to continue with a relatively small loss of time. These dumps are also useful when running a large number of parametric calculations with a common starting point at the end of a long initialization calculation.

The task is simplified with the coding like this:

COMMON/CONTROL/DELT,DXB,DXA,XGB,YCOM,ZCOM,RDTA,XXA

REAL CONTA(8)

EQUIVALENCE (DELT,CONTA(1))

WRITE(11) CONTA

So what? The example is greatly simplified. In real life it is not unusual to have large numbers of variables (>50) in a given named common. This structure makes the write statements cleaner, and easier to maintain if variables are added to the named common. What happened to the format number in the WRITE? This is an example of an unformatted write. The contents of the listed variables are dumped to the disk file attached to unit 11, in exactly the form they have in memory. Here you would send 8 strings of 32 bits to the disk, containing the bit representation of real numbers. They could be read back from disk with "READ(11) CONTA"

Using COMMON and argument lists is a balancing act. Each argument in an argument list generally has an associated cost in computer time to pass the argument address to the subprogram, and long argument lists also carry the burden of making the program more difficult to read. COMMON blocks simply establish a common agreement on variable locations, that is used during creation of the executable code. I can use COMMON variables in subroutines with no additional cost beyond use of local variables (same holds for MODULE variables). The results of the test program funspeed.f give a little feel for the potential advantage of COMMON. The unoptimized code shows a significant difference between using an argument list and using a COMMON to pass information. This difference widens as more variables need to be passed. However, what is going on with the optimized results? I am passing the same variables in every pass through the DO loop. The compiler is smart enough to know that it only needs to do a single load at the beginning of the DO loop to set the addresses used by the subprogram to locate arguments. Use of argument lists within a DO loop is not always a significant disadvantage. When in doubt on the proper approach run some timing studies. If no major speed advantage is obvious, make your decision based on clarity of your program (could dictate either arguments or COMMON), and on the potential for use of your subprogram in other applications (usually encourages argument lists).

INCLUDE

If you get tired of changing copies of COMMON blocks in many different subroutines, you can put a single copy of a COMMON block in its own file. The Unix © convention is that the file end with ".h". The contents of the file can be accessed by the compiler by including an INCLUDE statement at the appropriate location (see trapz1.f, trapcom.h and trapcom1.h ). Use of INCLUDE is not restricted to COMMON blocks. It can be used anytime you have a block of code that appears unaltered in more that one location in the program. However, such applications to blocks of executable statements lead to highly unreadable programs, and should be avoided.

MODULEs

Discussion of COMMON blocks and the INCLUDE statement has been postponed because you obtained these basic capabilities early in the class from the combination of the MODULE and USE statements. The only capability of COMMON missing in a MODULE is the ability to force data to reside in machine memory in a specific order.

MODULEs have several features not available in COMMON blocks. Unlike COMMON, you can include ALLOCATABLE arrays in MODULES. In doing so you have the ability to allocate an array in one subroutine for use in others (see module.f.) MODULES also make it more convenient to provide internal subprograms to program units than is possible with an INCLUDE statement. A single MODULE permits typing of variables and definition of internal subprograms. The same job would require two INCLUDEs.. When you get to the point of building internal functions, you may want to learn about the PUBLIC and PRIVATE statements to control the flow of information from you module subprograms.

My primary purpose in introducing COMMON blocks has been to prepare you to work with older FORTRAN programs. Unless you have a strong need to control the memory layout of your variables, you should stick with MODULE and USE statements in your own programs.


Back to the Table of Contents / Home


Written and Maintained by John Mahaffy : jhm@psu.edu