Writing, compiling, and debugging Fortran computer programs on Unix
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:
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.
^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 textsee 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 linessee next command)
^ shift 6 mark text so you can cut it and reinsert it elsewhere.
I dont know of a "go to the end of the file" command, and you cant get pico to show you line numbers.
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 dont 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
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=...".
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.
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.
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.
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