next up previous


PHY201 - Worksheet 3, F00


Numerical Calculation of erf(x)- Part II

Developing modular code or ``OOP''

Aleksandar Donev - Dr. Phillip Duxbury

Due Friday September 22nd


Physics 201 home

Introduction

In the last worksheet you developed a numerical method to evaluate the function Erf(x). The program that you wrote was composed of the ``DO'' loop which carried out the numerical calculation, input and output statements and a convergence criterion for the ``DO'' loop. Though most of you achieved a working code for this problem, few of you had time to write the program in the nicest FORTRAN 90 style. Download our sequential code for this problem and carefully go through the statements contained there. We have already taken one part of this code into a module and ask you to restructure the remainder in this worksheet. You need to understand why the KIND statement is used and how the convergence criteria inside the loop works. Note also that we used an infinite named loop e.g. the one called ``summation''. You need to understand how that works as well. Compile and run this program to make sure it agrees with your code.

In this worksheet we want to conform to the ideas of object oriented programmming(OOP) which requires breaking code into small reusable pieces. This becomes more and more important the larger the project and the larger the number of people who are working together on a project. In fact the majority of time spent in coding is in the maintenance of code rather than in its initial construction. Commercial code and code in large research projects (e.g. high energy physics, electronic structure codes...) is useless unless it conforms to OOP principles. C++ and Fortran 90 are important steps in helping code be maintainable and reusable. JAVA takes this even further. A second feature of OOP is that code libraries and packages play an important role. Later in this course we will introduce some graphics packages that are compatable with Fortran 90 and demonstrate how to call them from within your code.

In this worksheet you will break the Fortran 90 sequential code that we gave you into smaller sub-units. The key Fortran facilities that are needed are: modules (A fortran 90 construct) and procedures (functions and subroutines). Read sections 2.1 and 2.4 carefully to better understand how modules, functions and subroutines are used in Fortran 90.

Program Organization

Procedures

One major deficiency of the code you wrote in the first worksheet was that the error function was not really a function. What we really want is to have a function called Erf which we can simply envoke like the other intrinsic Fortran functions, as in:

    WRITE(*,*) Erf(1.0_wp)

Your first task is therefore to place the calculation of the error function into a FUNCTION, which you call from the main program. First do this for the case where x is real, but note that you could have two different procedures, one for REAL and one for COMPLEX arguments and we will show you how this is done in the solution to this worksheet which we will make available with the next worksheet.

The REAL function might start as:

   FUNCTION ErfOfReal(x) RESULT(erf)
      REAL(KIND=wp), INTENT(IN) :: x
      REAL(KIND=wp) :: erf
      ...
   END FUNCTION ErfOfReal

The body of the function should be very similar to your previous code. However, there are several important differences. First, remove all I/O statements (such as WRITE). The user may call this function many times (in large calculations) and does not want to see huge printouts. All the printing should be done from the main program, while the procedure should provide enough information to the caller so that a suitable action can be taken in case of error, for example.

Modules

Now we have to introduce modules to make the above procedure complete. In Fortran 90, all procedures should be packaged in modules. So make a new MODULE in which you will place the above routine. It is also advisable that any data shared by several procedures in the module and/or the main program, such as the precision variable wp, the desired precision $\varepsilon $, or the maximum number of iterations, be placed in the module as module variables. That way all the program units that USE the module and all the procedures inside the module will be able to manipulate these variables. Remember that procedure declarations come in the module after a CONTAINS statement.

Finally, a tricky point arises with the possibility that the truncated series does not converge to within the desired accuracy. Two approaches are used to flag such an error. The first makes the above FUNCTION into a pseudo subroutine, and returns a logical variable which tells whether the summation converged or not. This is essentially an approach widely used in C:

   FUNCTION ErfOfReal(x, erf) RESULT(converged)
      REAL(KIND=wp), INTENT(IN) :: x
      REAL(KIND=wp), INTENT(OUT)  :: erf
      LOGICAL :: converged
      ...
      IF(...) THEN
         converged=.TRUE. ! If converged
      ELSE
         converged=.FALSE. ! If not converged
      END IF
       ...
   END FUNCTION ErfOfReal

This approach is elegant and allows better modular approach, but has some deficiences (such as an extra argument passed each time). Another simpler approach that is used often in scientific programming is to make a global (module) logical variable, called a flag, and set the flag to .FALSE. (raise the flag in case of error). Choose whichever approach you prefer, but make sure you understand both. With the second approach your module might look like:

   MODULE Erf_Series
      PUBLIC ! For now, use this statement
      INTEGER, PARAMETER :: sp=KIND(0.0E0), dp=KIND(0.0D0)
      INTEGER, PARAMETER :: wp=sp ! Or wp=dp
      INTEGER, PARAMETER :: max_iterations=100
      REAL(KIND=wp) :: epsilon
      LOGICAL :: converged=.TRUE.
      ...
   CONTAINS
      FUNCTION ErfOfReal(x) RESULT(erf)
         ...
      END FUNCTION ErfOfReal
   END MODULE Erf_Series

Main Program

The main program will now be much shorter, since all the computation is in the above routines. The main program should prompt the user to enter x, then call the function ErfOrReal, and print the result. It should also check if an error occured and print a message. For example:

   PROGRAM Erf_Numerical
      USE Erf_Series
      ...
      REAL(KIND=wp) :: x, erf ! These are actual arguments
      ...
      erf=ErfOfReal(x) ! Or complex
      IF(.NOT.converged) &
         WRITE(UNIT=*,FMT=*) ``The result below has not converged''
      WRITE(UNIT=*,FMT=*) erf
      ...
   END PROGRAM Erf_Numerical
Your tasks this week are thus

(i) To place the calculation of the erf series in a function

(ii) To place that function in a module

(iii) To call the module from the main program

Challenge question

(iv) Write an interface (see the next section) which enables the program to automatically notice whether the input x is real or complex. You will also need a function ErfComplex in the ErfSeries module which the interface would call in the event that x is complex (see the TA for help with this)

Advanced: Generic Procedure Interfaces

To make the above program fully Fortran 90 powered, one can use generic interfaces (these are not covered in the manual as they are more advanced features). If you feel like you learned enough in this worksheet already, then do not go throught his section!

In the above program we could not call ErfOfReal with a complex argument. What we want is a function like SIN, which can be called with an arbitrary precision/type of the argument. To do this, one needs to write into the module procedures for different combinations of types and precisions (kinds) of the arguments, and than make a wrapper generic routine that encompasses all different cases. In the example above, this is done by putting the following INTERFACE in the body of the module (before CONTAINS):

   INTERFACE Erf
      MODULE PROCEDURE ErfOfReal
      MODULE PROCEDURE ErfOfComplex
      ... ! Other types of arguments
   END INTERFACE Erf

Now in the main program one can write:

   WRITE(*,*) Erf(1.0_wp) ! REAL argument
   WRITE(*,*) Erf((1.0_wp,0.0_wp)) ! COMPLEX argument

Compiling with Fortran 90

We now have a Fortran 90 educational compiler that you can use on gauss. Please use this compiler only for this class and work related to it! This compiler is envoked in the same way as g77, and is called f90-vast. Suffix your files with the extension .f90 , and preferably your executables with .x. For example:

   > f90-vast YourProgram.f90 -o YourProgram.x

This compiler translates Fortran 90 programs into g77 programs. Remember that Fortran 90 has free-form syntax, so you do not need to worry about counting columns and spaces any more.

About this document ...

This document was generated using the LaTeX2HTML translator Version 98.1p1 release (March 2nd, 1998)

Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.

The command line arguments were:
latex2html -split 0 worksheet3_f00.tex.

The translation was initiated by Phil Duxbury on 2000-09-16


next up previous
Phil Duxbury
2000-09-16