Writing, compiling, and debugging Fortran computer programs on Unix

Overview

The basic idea behind getting Fortran programs to work on Unix, or any other computer system for that matter, is really quite simple. The steps are:

    1. Create a text file that has your computer program in it.
    2. Link and Compile the text file with the Fortran compiler. This will create an executable file on your Unix account that can be run. The compiler also spits out error messages when you have errors in your program. (Note: the compiler only catches language and structure errors. It does not catch any (except the simplest) logic and flow errors. You will have to learn to test your programs to make sure they work correctly.)
    3. Run the executable file.
    4. If everything works right, then stop. But I have NEVER written a computer program that did just what I wanted it to do the first time. I’m not sure I have even ever written a program that compiled correctly the first time! Otherwise you will need to
    5. debug your program, make the changes in the program file and go to step 2.

Creating the text file

You should use the text editor "pico" for creating the program files that you will ultimately run. Its not the best editor, but I know how to use it. Other ones you can use if you want to learn on your own are "vi" and "the". Actually "vi" is better than pico, but it is harder to use. "the" looks just like XEDIT in CMS, so faculty who are used to XEDIT may want to use it. I’m going to give you instructions for pico.

I want you to create a sample file called test.f

pico test.f

Now type in the following lines. Note that most of the text begins in line 7. I have typed a ruler at the top so you can see in which column things should be typed (don't type in the ruler).

    5   10   15   20   25    30   35   40

|---+----|----+----|----+----|----+----|----

      program test
      do 10 i=1,20
        write(*,*) i,‘ this is a test’
      10 continue
      stop
      end

Commands used in pico are below, and you should save this as "test.f". Just follow the directions in pico.

pico commands

^y go back a page
^v go forward a page
^d delete the character the cursor is on
^e go to the end of the line
^a go to the front of the line
^x quit
^o save (output) the file

DO NOT PRESS ^s !!! it hangs up pico.  But it you do, then type ^q and you will get control back plus anything you typed while you were frustrated that nothing seemed to be working.

^k "cut" the current line of text (or marked text—see the ^ shift 6 command). This deletes the line or marked text and copies it to a buffer so you can reinsert it.
^u "uncut" or insert the line (or lines—see next command)
^ shift 6     mark text so you can cut it and reinsert it elsewhere.

I don’t know of a "go to the end of the file" command, and you can’t get pico to show you line numbers.

Common program elements

Standard Fortran statements begin in column 7. There are ways to get around this in Fortran 77 and Fortran 90, by using what is called "free form input," but I don’t know how to do it. So you will just have to start your program statements in column 7.

 

Column
12345678901234567890
|---+----|----+----|----

      program test
      statements you put in the program
      stop
      end

Statements you will probably use are things like

      goto ..

      if(...) statement you write
         if(.....)then
         statements you write
      endif

      if(....) then
         statements you write
      else
         more statements you write
      endif

      do 10 i=1,20
         statements you write
      10 continue

      open (....)

      read (....) .....

      write(....) .....

plus algebraic operations:

and logical expressions in the if statements

Unix-specific elements of Fortran--open, read and write statements

open and read statements

When you read data from an external file, you need to use an open statement to open the file (duh!) and a read statement to read the file (duh again!).  Suppose you have some data in a file called data.ascii that looks like this:

test.dat

2.343430 2.343242 343.2343
127.3432 4.323346 3.422344
990.4432 434.3413 3454.234

The numbers are 8 columns wide and  there are three numbers on each line.

your fortran program to read this would be:

      program test
      open(10,file='test.dat',access='sequential',
     * form='formatted',status='old')
      nitems=0
   15 read(10,998,end=500) r1,r2,r3
      nitems=nitems+1
  998 format(f8.3,1x,f8.3,1x,f8.3)
      write(*,*) 'this data was read: ', r1,r2,r3
      goto 15
  500 write(*,*) 'Done with the program. ',nitems,' lines were read.'
      stop
      end    

Open statements need each of these items.  The "10" is the unit number.   It can be just about any integer, I think.  If you try big numbers and they don't work, then use ones less than 20.  Note, the unit number is the same number referred to in the read statement.

The second item is the file name.  If it isn't in the directory you are running the a.out file from, then you need to specify the complete path to the file.  The access type can be "sequential" or "direct."  You will use direct access for the COMPUSTAT dataset, but you will use sequential for most files you create.

The third item, form, can be either "formatted", "unformatted"

If the line is longer than about 260 characters, or if you are using direct access, then you also need a recl=.... where .... is the record length of the data file.

The status = 'old' just tells fortran that the file already exists.  If you are creating it, then use status = 'new'.

The read statement has a unit number from which to read, a format statement line number, and (optionally) an end=.... statement.  The end=.... option tells the program where to branch when it reaches the end of the input file.

Write statements look just like read statements--but you don't need an "end=...".

Compiling and Linking your Program

This is really easy!  When you log on to Unix, type the command

setup crsp

this accesses a script that I wrote that does lots of the linking and compiling for you.  If your program is called readindseg.f  and the program name is compusta then just enter

compile myprogram.f

If there are no syntax errors in your program then the output will be

1) 61, 61 +26c COMPUSTA <--- 1) readindseg.f
Normal E-O-J -- 61 Lines, 1 SubPgm, 1 File: (26,0) Considerata

and the compiler will create several files:   readindseg.T, readindseg.lst and a.out.  The .T file is for use in the debugger.  The .lst file has the program listing and any errors that might have been found, and the a.out file is the executable file.  To run the program once it compiles you just type:

a.out

On the other hand, if you have syntax errors you won't get the above output.  Instead, you will get:

f90: SunSoft F90 Version 1.0.1.0 (23279289) Tue Sep 22, 1998 10:40:28
f90: COMPILE TIME 0.080000 SECONDS
f90: MAXIMUM FIELD LENGTH 2500646 DECIMAL WORDS
f90: 62 SOURCE LINES
f90: 1 ERRORS, 0 WARNINGS, 0 OTHER MESSAGES, 0 ANSI
f90: CODE: 0 WORDS, DATA: 0 WORDS
1) 61, 61 COMPUSTA 1 SyntaxError <--- 1) readindseg.f

This output says that there is one error and that it is on line 1. 

Finding compile errors.  The .lst file.

If your program doesn't compile correctly, then you should open the .lst file.   Suppose you tried to compile the file test.f, and the compile came back with errors.  Then just open, in pico, the file test.lst and look for the error description.  Fix the error in test.f, recompile and see if you have more errors!  Repeat until exhausted.

Execution Errors:

Once your program compiles correctly, there are lots of places where your program can still have errors.  I find that 95% of all of my errors can be traced to some combination of the following three sources:

1.  Spelling errors.   If you misspell a variable, then you won't have the correct number in your calculation.  Suppose you have the following lines and you misspell ncomps in the second one.
       ncomps = 100
       do 10 i=1,ncomsp
         a bunch of commands here....
  10   continue

In this case, rather than processing the "bunch of commands here" 100 times, you will process it zero times, since the misspelled variable ncomsp has a default value of 0, unless it is assigned elsewhere.

These are VERY HARD TO FIND!  You can find some of them by examining the program, examining the errors, examining the program and the errors in dbx (below).  One additional technique may sometimes work, but it is a pain if you don't already program this way.  If you use the f77 compiler and use the -u option, then all variables are initially undeclared, rather than the default of having variables that start with a-h and o-z as real, and those that start with i-n as integers.  If you use this option, you have to declare each and every variable you use at the beginning of the program as real or integer, and if you use a variable that isn't declared, fortran generates a warning.   This compilation option isn't available on f90.  You have to use f77. 

I have written a command you can use if you want to compile your program so that all undeclared variables generate an error.  If your program is test.f, then compile using

compileundeclare test.f

this invokes the f77 compiler (so if you have stuff in the program that works in f90 but not f77, then it won't compile) with the -u option, and it links to all of the stuff that the compile command does.  If it doesn't work for you, let me know and I'll work on it.

2.  Floating point exceptions:  These occur when you multiply two numbers that are too big, or divide by zero, or sometimes when you access a real number with an integer variable, or vice versa.  You will get an error from your program when you try to run it, and the error message won't be very helpful.  The best way to identify where these are is to use dbx, described below, with the catch fpe command to catch and identify the floating point exceptions.

3.  Array overwriting or overreading: If you write past the boundary of an array, or read past a boundary of an array, then you will get erratic results.  Unfortunately, these won't generate errors every time this happens, so again, this is VERY HARD to detect.  Fortunately for us, the old f77 has a compile option that generates code to check all of the arrays.  It makes your program run really slow!  And it makes your program really big.  But if you need it, it might be worth using it for debugging purposes.  The compile option on our system is -C.  I have written a script so you don't need to know anything about it to use it.  If your program is test.f, then just compile using

compilecheck test.f

when you run a.out, if you overwrite or overread an array, then you will get an error telling the line number and the array the problem occured in.

Using the debugger--dbx.

I haven't done much with the debugger--mostly because I don't make mistakes, so I don't need it :)  However there are some times when it is much easier to use dbx than to write out a bunch of write statements.

Entering dbx.  If a.out is your executable file, just type

dbx a.out

dbx commands:
at the dbx prompt, enter the statement

commands

to get a list of commands and a description.  They are: full list of dbx commands:

the commands I use are:

run--to run the executable file to see where it stops
rerun--does it again
list n1 n2--to list from source line n1 to line n2
step--to step through the source code one line at a time
whereami--to display the current source line
catch fpe--traps floating point exceptions and tells you about them
print variablename--prints the value of the variable you specify
quit--quit dbx