Fortran Questions


General Programming

The Compiler; Variables, Constants and Assignments; Arrays; Subprograms; Modules; Branching Statements (IF, DO, CASE, GO TO); Input/Output Statements; Pointers, Common Blocks


The Compiler

You went over Fortran 90 structures. Is there a Fortran 90 compiler on the Workstations?

Yes there is, but they have cleverly named it f77. This makes us long time programmers more comfortable, but adds to the confusion of new students of the subject.

How do you know if you are on a machine with 90 or 77?

Simplest answer is to give the compiler a small program containing a Fortran 90 statement. Other options include asking the system manager, typing the compiler name with no arguments ("f77"), or consulting "man". Always remember that the problems only work one direction. If you write a program, using only commands contained in Fortran 77, it will always compile and execute with a Fortran 90 compiler. I have tried to clearly state which statements and structures are unique to Fortran 90. The rest are Fortran 77.

When you use optimization options on the compiler is there any way to tell what part of the code the optimization has altered?

I have seen one or two compilers polite enough to provide messages on significant restructures of your program. Generally you can assume that there are small changes almost everywhere, but to tell the difference you need to know the assembly language for your machine. This compiler and most others will print assembly language (person readable machine language) for the compilation results.

I know you recommend using "f77 test.f -O" for the best level of optimization, but how does maximum optimization command, "f77 test.f -O3" hinder the way your program compiles when it maximizes the speed the most?

The -O3 option maximizes your exposure to compiler bugs. Choosing the -O options sometimes lets the compiler avoid trouble in regions where a forced maximum optimization might be a bad idea. By all means try -O3 when you you are ready to do serious number crunching, but don't trust your results until you have compared some sample results to those generated at lower optimization.

What directory is used by the compiler for compiling a Fortran Program? Where does f77 live?

For the work in this class, you should assume that everything happens in whatever directory you are in when you type the "f77". Type "pwd" if you don't know the answer to this question. The executable file called "f77" resides both in /bin and /usr/bin on these machines. This is very unsual. To locate an executable file use the "whereis" command (e.g. "whereis f77"). Unfortunately the manual pages on f77 aren't connected properly and are listed under IBM's other name for their compiler, "xlf". Try "man xlf" for more information on the compiler, but don't expect too much. IBM likes to force people to buy manuals and special CD-ROM packages.

Where can I get a Fortran Compiler for an IBM PC?

You can pick up one on the internet from the GNU project, but get a better package from MOC for about $80.00.

Is there a way to go back to the top of the error list after a failed compilation?

The Unix script command will save things for you in a file. If you are running a terminal emulator with a "scroll bar" on the side of the window. Try clicking in the scroll bar to see old stuff. Most of you probably won't have that option, so try one of the following:

f77 test.f >& testout

f77 -qsource test.f

The first dumps everything that would go to the screen (& picks up error messages which are treated as special cases by Unix) and puts the output into a file called "testout". It will complain and fail if "testout" already exists. The second will create a nice listing file called "test.lst" containing your source code and error messages.

When the main program is followed by a subprogram, why doesn't the compiler stop compiling when it hits the first END statement.

Fortran reads the entire contents of any ".f" file that you give it. The END statement just tells it that what follows is a separate program entity with limited shared information.

Does the computer compile everything between END statements separately.

Yes. Every time it hits an END statement, it compiles everything since the last END (or the beginning if no previous ENDs). The separate compilations are stuck together at the very end by a process called linking.

If we write a subroutine for someone else, how can we compile it to see if it will run before giving it to them?

This question gets to the heart of good programming practices. I'll give you an introduction here, and emphasize some of these points in later lectures. The easy part of the answer was hidden in the demonstration where I compiled 2 parts of a program separately and made a program on the 2nd partial compile. If you make a subroutine in a file called "subr1.f", you can compile to just test for Fortran errors with the command "f77 -c subr1.f". The "-c" option says just go through the compilation steps but don't make an executable code for me. This will leave a file behind called "subr1.o", which is almost machine instructions, and is ready to be linked to other stuff to make a full program. You're really not done yet. You should write a very short driver main program, that feeds some numbers to subr1 (call subr1(...)) for which you know the returned answer. You then check the known answers against the subroutine's answers before declaring victory. This testing is often easier said than done. Most useful subroutines do some fairly complicated calculations and contain IF tests that can result in different options being used depending on the input conditions. Designing test cases and checking methods can be challenging, but is essential. When approached systematically, this testing process is science at its best. You are in the business of constructing and interpreting controlled experiments. When using computers, most people simply treat them as an extension to theoretical science, another way to solve some equations. By recognizing the organized experimental aspects of creating and using computer programs you can give yourself a competitive advantage in this business.

I don't understand when you want to debug a program and you type f77 -g debug.f , to debug the program your working on, or is it a special program for debugging.

My choice of examples was poor. The file "debug.f" was just another Fortran program. I could have used the program "htcoef2.f". To prepare for debugging, I would have then typed:

f77 -g htcoef2.f

Please note that at this point, the only unusual thing I have done is to write some extra information to the executable file "a.out" for possible later use by the debugger. I can execute "a.out" just as before, and may never decide to do any debugging. The actual process of debugging begins when I type:

dbx a.out

I had trouble when I typed f77 to compile my 1st program. I was in the homework directory, do I have to be in the main directory? I was told by Unix that I had too many parameters when typing "f77 hw3.f".

The "too many parameters" message coming from Unix usually means you put a space somewhere you shouldn't like "hw3 .f". When you type the command you should be in the same directory as the file "hw3.f".

How do you use outside files that contain code in a program, and how is it brought in?

We have covered two ways. The first is appropriate when the outside files contain full subprograms. Then the file names must end with ".f", and you just list them on the compile line:

f77 main.f sub1.f sub2.f func.f

The compiler simply reads each file in turn and adds its contribution to the final executable file.

When the file just contains a segment of code, like a COMMON statement, you need to use an INCLUDE statement in your program, to tell the compiler to bring the contents of a file in for processing. What happens is that the compiler processes your statements in the original file until it hits and INCLUDE, then opens the listed file and processes everything in it. Once done with the INCLUDED file the compiler returns to processing the next line in your original file. If the file "more-code" contains the lines:

      real pi,g
      common/const/pi,g
and your program "test.f" contains:

      program test
      include 'more-code'
      print *,'Pi = ', pi,', g = ',g
      stop
      end
      blockdata constants
      include 'more-code'
      data pi,g/3.14159265,9.807/
      end
then the compiler is actually compiling the program:

      program test
         real pi,g
      common/const/pi,g
      print *,'Pi = ', pi,', g = ',g
      stop
      end
      blockdata constants
         real pi,g
      common/const/pi,g
      data pi,g/3.14159265,9.807/
      end

Variables, Constants, and Assignment

When you write a number in the form "10." does that mean "10.0"?

Yes. Fortran just needs the decimal to interpret the number as a REAL type. "10.", "10.0", "1.e1", "1.0e1", "1.0e01", "1.0e+1", and "1.0e+01" all are the same number to Fortran.

Must you use Real and Integer in homeworks?

For any homework assignment, you must use REAL and INTEGER statements to declare the appropriate type of all variables that you use. At one time or another, you will be forced to use both of these data types. You will also eventually be using LOGICAL and CHARACTER types and DOUBLE PRECISION (REAL(8)).

What is KIND? What exactly does it do?

A given computer may have several kinds of INTEGER representation (and possibly corresponding arithmetic units). It could store some integers in a single byte, some in 2 bytes, and others in 4 bytes. That machine has 3 KINDs of INTEGER variables. You force the selection of the KIND appropriate to your needs with a statement like:

      integer (kind=2) i,j,k
or as an abbreviation

      integer (2) i,j,k
Most machines (but not all) do not use sequential numbers (1,2,3) to label their kinds of integers (or reals), they tend to use numbers for kinds that match the number of bytes used in storing the variable.

How do you use a logical variable? What is stored there?

Most frequently, logical variables are used in association with IF statements. When you want to set a logical variable LVAR to true you use "LVAR=.TRUE.". For false use "LVAR=.FALSE." In practice the computer usually stores an integer 0 in memory for false and integer 1 for true. The normal logical variable occupies 1 byte of space.

What is the advantage of an array over a spreadsheet format?

Both can store similar types of information in a neatly labeled and organized way. The advantage lies in where they are used. You have more control over how Fortran arrays are used than how the contents of a spreadsheet are used. In addition for any given operation on an array of numbers, once the Fortran is written, it will do the job much faster than a spreadsheet. On the other hand, when operations are not complex and computer execution time is not a problem using the spreadsheet is probably your best bet.

I'm confused about the exact use of arrays and their purpose.

The simple answer is "pay attention to the lectures and examples for the rest of the semester." Arrays are used when you have a large number of numbers on which you want to do identical or similar analysis. Let's say I have the temperature and pressure from 10,000 measuring stations in the US, and want to calculate the air density at each of these points. I load the temperatures and pressures into arrays with a dimension of 10000, and pair by pair I march through and evaluate a function to give density in another array.

dens(i)=rho(t(i),p(i))

Yes, I could have read the data in a pair at a time and printed results one line at a time without using an array. However, what if this is just the beginning? What if I'm going to take all off my temperatures, pressures, densities and several other measurements from each station and use it as part of a complicated calculation to predict the weather for the next 24 hours? I have to keep all of the data in the computer. Arrays provide a convenient way of storing and retrieving all of this information within a program. Please note that while the program is executing, information contained in arrays, like other Fortran variables, is kept in the machine memory where access times are much faster than when the information is in a disk file.

Could you go over colons again in dealing with arrays?

Within Fortran 77 there is only one use for colons associated with arrays, and that is when establishing the size and permitted range of indices in a DIMENSION or type (REAL, INTEGER, ...) statement. For example "DIMENSION A(-4:10)" assigns 15 words of memory to the array A, and makes a note that A(-4) corresponds to the first of these sequential words. For Fortran 90 using a colon within an executable statement is just shorthand for writing a DO loop involving just one line.

C(1:9)=A(1:9)+B(1:9)	is the same as:		DO 100 I=1,9
					         100	C(I)=A(I)+B(I)

C(1:9:2)=A(1:9:2)+B(1:9:2)  is the same as	DO 100 I=1,9,2
					        100  	C(I)=A(I)+B(I)
Write a few 5-10 line programs until you are comfortable with how this behaves.

Are We Actually Going to Need Character Variables? Why are they used?

You could probably get through all of the programming applications in this class without resorting to character variables. However, if you are going to create programs with legible output and/or flexible input/output you will need them. You have already seen me use them to store names of files for use in input (look at the sample program trig3.f). That is one of the most common uses for simple programs. Also, take a look at the tricks I play in plot1.f and plot2.f to generate creative output with the help of character variables. When you start driving graphics subroutines to view your data, you will find that character variables a useful and generally mandatory.

Postscript: My daughter (Senior, physics) burst out laughing when she saw this question while trying to throw me off of the family PC. Yes, she is cruel, but her experience may be closer to what you will use soon than the crazy things that I do. She is doing quite a bit of Fortran programming for Dr. Garrison in the Chem department. She says she couldn't get along without character variables to keep track of file names and manage printed and graphical output.

What is an ASCII character?

A standards group got together and decided what "characters" would be useful for computers. These include the obvious upper and lower case alphabet, characters to represent numbers (0,1,2,3,4,5,6,7,8,9), and other odds and ends that you see on the keyboard (: , . " ' + = - / \ ~ and others). They also included "control characters". This is what results when you hold the control key and hit a letter key. These characters were all written down, and each given a unique number from 0 to 127. The resulting standard relation between numbers and letters helps computers communicate character information back and forth without converting the underlying binary numbers to nonsense.

Are the characters "A" and "a" associated with different integers?

Yes. Take a look at my new full list of the ASCII definitions for how each integer from 0 to 127 is to be translated to a "character".

When you are shifting characters, won't this also shift and number's you read?

Yes and no. At the stage you see me shifting characters, the "numbers" are still really just strings of characters that have no meaning to the computer as numbers. I can move them around as much as I want as long as I don't disrupt the sequence of characters giving digits, decimal point and exponent. It is only through the process of a READ with a * format or ,"i", "f", "e" or related edit descriptor, that the characters we typed into the file are converted to internal representation of a number (integer or real). In particular, the default format '*' is smart enough to scan a character string, and based on the variable type that you request in the READ, obtain a value if your characters make sense, regardless of leading or trailing blanks.

Can you explain parsing and concatination again?

Parsing is the act of deviding a "sentence" into individual "words". Normally the separation that indicates "word" boundaries is a space or a comma. In computer applications the list of separators may be expanded, or completely different. However, the separators are always defined within the program and used for isolating "words". Once this separation is complete, individual words (or proper sequences) can be tested by the computer for "meaning" that cause the program to assign values to specific variables, or take other more complex actions. You are looking at the very beginning of language recognition.

Concatination is the inverse operation. You will frequently find that you have isolated words or phrases containing information that you want to tack together to form a coherent "sentence". At your stage, you are tempted to say "so what, I can already do that with formats". Usually true, but more awkward. When you get into passing arguments to graphics routines, the direct manipulation of characters becomes even more useful. Suppose I want the flexibilty to display results in either metric or English units. I need to give a plotting subroutine labels for the axes as character strings. I might have a CHARACTER array "varname" containing names for all variables of interest:

varname(1)='Temperature'

varname(2)='Pressure'

etc.

Actually, I probably would set these values in a DATA statement. For the units, I would actually use a doubly dimensioned array, but we aren't there yet, so I'll use two more regular arrays:

metric(1)='K'

metric(2)='Pa'

english(1)='F'

english(2)='psia'

etc.

Depending on user choice in the variable to output, and units to use, I will construct a full label with a concatination line like:

label = varname(i)//'('//metric(i)//')'

As we saw in the example this could result in contents of label with too many spaces:

Pressure (Pa )

(i=2 in this example). The solution to this is to use the "trim" intrinsic function

label = trim(varname(i))//' ('//trim(metric(i))//')'

giving a value in "label" of

Pressure (Pa)

Your plot is properly and cleanly labeled, ready for formal presentation. Why didn't I include the parentheses in the contents of the "metric" and "english" arrays? To use those same arrays for printing numbers like "200 psia".

Where are all or your CHARACTER variables coming from in your examples?

In the charvar.f and charvr90.f examples the vast majority of the CHARACTER strings are coming from the input file. The important thing that you must remember for any computer language is that the contents of a file created with an editor like "vi" or "emacs" are just characters. You can use FORMAT edit descriptors, or "*" default formats to convert the characters to numbers in many cases. However, you have the most flexibility by bringing the contents of the file into your program as characters, and then using appropriate tests within the program to sort out what the characters all mean. You are programming the computer to do the sorts of things you do when you read on this page "Temperature = 300.2".

I still don't understand PARAMETER's. What advantages do they have over integer variables?

The PARAMETER statement creates a special variable that behaves exactly like an integer or real constant. In terms of what your final machine executable file (a.out) looks like the following two programs effectively give the same results:

      	PARAMETER (NDIM=5,NR1=NDIM/2,NR2=NDIM-NR1)
     	      DIMENSION C(NDIM),A(NDIM)
      	DATA A/NR1*1.,NR2*2./
     	      DO 100 I=1,NDIM
     100	C(I)=A(I)**2+5+I
      	PRINT *, C
      	STOP
    	          END
	      DIMENSION C(5),A(5)
      	DATA A/2*1.0, 3*2.0./
     	      DO 100 I=1,5
     100	C(I)=A(I)**2+5+I
      	PRINT *, C
      	STOP
    	      END
The first advantage of using NDIM as a parameter would be more obvious in a longer program. If I want to change the second form of the program from operating on an array with 5 elements to one with 80 elements, I must search for all uses of 5 that are appropriate and change them to 80. In the first form, I only have to change the 5 in the parameter statement. Remember that only something declared a parameter can be used in the way that I have used NDIM, NR1, and NR2 in the DIMENSION and DATA statements. The other advantage of the PARAMETER is that when certain arithmetic operations only need to be done once, you can get them out of the way at compilation time with a PARAMETER statement. You won't really appreciate this advantage until you are setting up equations in FUNCTIONs or SUBROUTINEs. Certain constants in the equations may only require one initial evaluation, but the FUNCTION or SUBROUTINE may be used millions of times during a calculation. Please note that you can also use PARAMETER to set REAL constants.

Do spaces mater in equations?

No. Spaces are generally added for clarity. Some compilers get upset if you write things like " INTEGERI,J" rather than INTEGER I,J". Simple neatness will keep you out of these problems. Remember that a space is required in column 6 if you aren't continuing from the previous line. The following are all equivalent:

	x=x*y**2*sin(x)
	x=x * y**2 * sin(x)
	x = x          *       y  **  2      *     sin ( x )
I don't understand what a DATA statement does.

It simply puts a specific value into a Fortran variable. "DATA A/1.0/" is the same as "A=1.0" in terms of results. The only difference is that the DATA statement sets A to 1.0 before the program starts to execute. The biggest advantage occurs when you are trying to give a variable a value in a FUNCTION or SUBROUTINE that is used a large number of times. The program on the left is faster than the program on the right below.

	Do 100 i=1,1000000				 do 100 i=1,1000000
  100	   y(i)=addon(x(i))			   100	   y(i)=addon(x(i))
	stop						       stop
	end						       end	
	function addon(x)	                   	 function addon(x)
	data b/1.12345/					 b=1.12345
	addon=x+b					       addon=x+b
	return						 return	
	end						       end
Yes, its a stupid example, but applies to real programs.

Can more than one variable be stored in a Data Statement?

Yes. For example you can set initial values for A, B, and C with either of the following statements.

	DATA A/1.0/,B/2.0/,C/3.0/
	DATA A,B,C/1.0,2.0,3.0/
What is the difference between a DATA statement and assigning a value to a variable with an =?

It is all in what happens when the machine executable code (a.out) is generated. When I assign a variable A with the statement "DATA A/1.0/" the compiler assigns an address (location) in memory, lets say 2001, that will be used to store numbers corresponding to A. It then proceeds to preload address 2001 with the computer representation for the number 1.0. Whenever you use A on the right hand side of an = the computer uses the number in address 2001 (1.0). If you use A on the left hand side of an =, the computer puts a new number into address 2001.

If you had not used a DATA statement, but set A with the statement "A=1.0", the compiler would have begun as before assigning memory address 2001 to A, but would not preload a value there. Without telling you it would also assign an address in memory , say 2005, for the real constant 1.0, and would preload the word at this address with the computer representation for the number 1.0. At every place in your Fortran that you refer to "1.0", the computer knows to go looking in address 2005 for a number to use. In particular for your statement "A=1.0", when the program (a.out) is executed, a machine instruction exists that does the job by copying the four bytes of memory beginning at address 2005 into memory beginning at 2001.

By using the DATA statement you have eliminated a machine instruction and perhaps the need for one word of memory. Big deal? Many times yes. You will often find yourself in a situation where the instruction "A=1.0" is located at the beginning of a Function or Subroutine that may be used millions to billions of times in a calculation. Savings like that add up.

What is the difference between a PARAMETER and DATA statement?

The difference is if I set a parameter "x1" with:

      parameter (x1=1.1534e-07)
x1 will always have that value, within the program unit containing the parameter statement. I can't change the value associated with x1 later. However, if I set x1 with:

      data x1/1.1534e-07/ 
I can later change the value if necessary:

      x1=2.1433e-08
Choice between the two depends on your needs in the program.

I'm still unclear about the different ways you can set initial values to an array

For now just worry about the data statement. Think about the way you list the array elements to receive values, and the actual values to be assigned separately. For an array "a" with a dimension of four. You can establish the same initial values with any of the following:

data a/2.,3.,4.,5./

data a(1),a(2),a(3),a(4) / 2.,3.,4.,5./

data (a(j),j=1,4)/2.,3.,4.,5./

The last two can be applied to initialize just part of the array:

      data a(2),a(3) / 3.,4./
      data (a(j),j=2,3)/3.,4./
The implied DO loop functions the same as it does in WRITE and READ statements. You'll get more practice on implied DOs as the semester rolls on.

Now on to the values. You only have two options here. Either list every value separating each by commas, or when you would have repeated the same number in the full value list, you can take a shortcut with the repeat operator. The following are the same.

      data a/3.,3.,3.,5./
      data a/3*3.,5./
What does the following line do?

real, dimension(10) :: a=(/(j,j=1,10)/), b, c

It reserves memory space for 3 real arrays each containing 10 elements. For the array "a", it also preloads values into those 10 elements (a(1)=1, a(2)=2, ... a(10)=10). However, for "b" and "c" no initial values are set. Usually the elements of "b" and "c" are all set to zero, but don't bet on it.

For the index notation of multidimensional arrays, does the first index number correspond to the number of columns and the second to the number of rows?

Other way around. The first index gives the row number and the second the column number.

Why bother with arrays?

The big reason for arrays is the relative speed of computer memory and computer disk. Yes, I could constantly cycle through the file temp.data as I need to use temperatures for various calculations. However, it is much faster (factors greater than 1000) to read the information into memory once, and use the data from memory whenever needed. The homework problem is deceptively simple. I can come up with useful calculations that would use that temperature (and probably other information like pressure) thousands to millions of times.

The array language structure is nice because one name plus an index: temp(i) covers all of the information that I might need. Without arrays I would be stuck creating variable names temp1, temp2, temp3, ... temp288 to keep things in memory, and would have to go back and add new variable names if I wanted to go to two days worth of data.

I'm sort of confused about how you use a specific value out of an array. What do you call up?

The source of your confusion is probably the similarity in syntax between a function reference and an array reference. To use a value in an array, you just include the postition index of that value. If I want to multiply the 3rd element of array "A" by the 4th element of array "B", and put the result in the 2nd element of array "C", I write:

      C(2)=A(3)*B(4)   
The number between the parentheses is just a position indicator. What's more it doesn't have to be a number as above, it can be an INTEGER variable containing a value. For example I would get the same result as above with:

      k=2
      j=3
      i=4
      C(k)=A(j)*B(i)
What is pipelining and what does it do for us?

Pipelining is feeding a steady (once per clock cycle) stream of numbers to an arithmetic unit. We don't do it directly, but make it easier for the compiler to set up, by using as many Fortran 90 array constructs and array intrinsic functions as possible ( a(1:n)= b(1:n)*c(1:n) or xsum=sum(x(1:n)) )

What does the colon mean when you define an array (e.g. A(:))?

It says that I don't know the actual range of "A". If I knew that the first element in "A" is numbered "1" and the last is numbered "10",then I might write "real A(1:10)", but I don't know the size yet, so I throw out the 1 and the 10, leaving "real A(:)".

What is the difference between DIM=1, and DIM=2?

If I define 3 arrays with "REAL A(3,5), SROW(5), SCOL(3) then the line

      SROW = SUM(A,DIM=1)
is the same as the following double loop, in which I sum the contributions of all rows within each column.

      DO 200 J=1,5
         SROW(J)=0
         DO 100 I=1,3
            SROW(J) = SROW(J) + A(I,J)
 100     CONTINUE
 200    CONTINUE
and for the other dimension

      SCOL = SUM(A,DIM=2)
is the same as summing the connects of all columns for each row, as done in the following double loop

      DO 200 I =1,3
         SCOL(I)=0
         DO 100 J=1,5
            SCOL(I) = SROW(I) + A(I,J)
 100      CONTINUE
 200    CONTINUE
When using Arrays, is it possible for the user to input the parameter for the dimension something like this:

real x,dim print *, 'Please input the number of dimension for the array' read *, x parameter (dim=x) real a(dim), b(dim), No. This violates Fortran's separation of non-executable and executable statements. The PARAMETER and REAL are non-executable and must be above the PRINT and READ. This restriction is what resulted in the creation of the ALLOCATE and associated statements.

If you're eventually going to use X(1:N) with a chosen N what's the point in using X(*)?

Given what you know now, no point at all. If N is available to me in the argument list, I would generally use "REAL X(N)" rather than "REAL X(*)". Two exceptions to this. If I pass N as an argument to give the range of a DO loop associated with X, but also make a special reference to X(N+1) later in the subroutine, I would use "X(*)" to note that the array is really longer than N. Some smart compilers will force me to do this. The second exception is related to Fortran 77. As mentioned below, I can pass N to my subroutine through a COMMON block and not include it in the argument list. If that is how I do it, pure Fortran 77 won't accept "REAL X(N)", and I'm stuck with "REAL X(*)". Fortran 90 and most late model improved Fortran 77 compilers will permit "REAL X(N)" when N is in a COMMON block.

Will we need to know all of the intrinsic functions (UBOUND, LBOUND, SIZE, ...)? What kind of practical uses do they have?

Know that the capabilities exist, but don't bother memorizing the names. This will let you search your favorite Fortran manual (or class notes) for details on the Function when you need it. Know how to apply the DIM argument. Know that Fortran 90 functions can do Matrix multiplies, and Dot Products, and although we didn't get to it in class, know that they can do the transpose of a matrix.

See above for practical uses of LBOUND and UBOUND. I use MATMUL, DOT_PRODUCT, and TRANSPOSE operations as a part of more complicated procedures for the efficient solution of very large systems of linear equations, particularly ones that have special "Sparse" (lots of zeros) structure.

What is the rank of an array, again?

The rank of a Fortran array is just the number of dimensions (subscripts) that it has. For the type statement "REAL A(10), B(5,3), C(3,3,4), D(6,6,6,6)", A has rank 1, B has a rank of 2, C has a rank of 3, and D a rank of 4.

What is the maximum number of places in memory for one array.

Fortran places no bounds on this. You are only limited by the space on the computer, or the maximum size of the INTEGER variable that you are using as an array index.

How are we supposed to know how much memory we need to allocate?

That information can only come from the input of data to the problem. Your problem as a programmer is to figure the best way to either force the user to directly give you the space requirements, or to deduce the space requirements by counting how much data is provided to the program. Look at the array3a.f and array3b.f examples.

How do you enter length for memory allocation?

It is a second level effect. You specify the length between the parentheses of the array given to allocate. You never have a statement "allocate (a,b,c)". It contains no size information. You must have something like "allocate (a(n),b(1:n),c(m:n))", where m and n are variables that have already been assigned values appropriate to the bounds of the arrays.

How exactly does a program allocate space for arrays when you wish for the arrays to be ALLOCATABLE? What usually determines the sizes?

Allocation of space begins with the ALLOCATABLE declaration.

real, allocatable :: b(:)

This tells the compiler that whenever it sees the variable "b" in statements, it shouldn't insert a specific address in memory, but coding that will go to another specific location in memory to get the address of "b" (this is related to pointers in some language). When you actually ask for a specific amount of space in "b" with

allocate ( b(nspace))

the details of what happens next are probably machine dependent, but the net effect is as follows: if each element of "b" is 4 bytes long, and your original program took up "proglen" bytes, then the new amount of space that your program uses in machine memory is:

new_proglen=proglen + 4*nspace

That location in memory that was reserved to contain the address in memory for "b" now contains the address at the beginning of the new 4*nspace bytes of memory.

The only reason that you bother with allocatable memory is to respond to varying user needs for memory. This means that there is always some clue from the input to the program that will specify the size of your arrays. If you are lucky (as in the homework), one element of "b" is contained in each line of the input file. You count the number of lines in the input to get "nspace". Generally, you are not that lucky, but it's the same general idea. You make one pass through the input file, simply counting the number of elements set or requested in each array to be allocated. You allocate the arrays, then REWIND the input file, and READ again, picking up actual values for the array elements.

What are mask statements and what are they used for?

First "mask" is not a formal Fortran statement. In our applications "mask" is an optional argument to a set of intrinsic functions. Think of it as a way of activating IF tests within the function. For example a simple minded Fortran representation for what goes on ina reference to "minval(a(1:n))" is:

      aminval=1.e38
      do i =1,n
         aminval=min(aminval,a(i))
      enddo
      minval=aminval
If instead I include the "mask" argument, for example as "minval(a(1:n),mask=a.gt.0)", then the action in minval could be represented as:

      aminval=1.e38
      do i =1,n
         if(a(i).gt.0) aminval=min(aminval,a(i))
      enddo
      minval=aminval
Could you go over masking one more time?

The idea of a mask is to determine which elements of an array are elgible for an array operation. For example suppose that I have two arrays:

a = (/ 1.0, 2.0, 3.0, 3.5, 4.0, 5.0/)

b = (/ -1., 2.0, -3., 3.0, 6.0, 7.0/)

and I use the following Fortran to sum some of the elements of "a"

asum = sum ( a, mask=b.gt.0 )

The first thing that effectively happens is that the mask argument is used to create a logical array containing the results of the test for every element in b.

logical-test = (.false., .true., .false., .true., .true., .true.)

This had better have the same number of elements as "a" or we are in trouble. This array of "true" and "false" values tells us that elements 1 and 3 of "a" will not be included in the sum, but the rest will.

Masks also occur in the WHERE structure:

where (b.gt.0)

c = a/b

else where

c = 0.

end where

For the values of "a" and "b" given above, elements 1 and 3 are false and produce c(1)=0 and c(2)=0. For the rest of the elements, the division is performed (e.g. c(2)=a(2)/b(2), c(4)=a(4)/b(4), etc).

When you are defining your reals (e.g. real a(100), b(100), ...), do the "a" and "b" have to be letters or can they be numbers or even something like a variable name?

The "a" and "b" can be any legal Fortran variable name. In Fortran 90 the only practical limitations are that the first character must be a letter of the alphabet (a-z, A-Z), and you can't expect Fortran to recognize any blanks in the name ("my var" is just "myvar" to Fortran, try "my_var" if you want separation).

Subprograms

Why does Fortran require both a "stop" and "end" or a "return" and an "end" back to back? That seems redundant.

Actually, the use of STOP and RETURN in this case is an old and admittedly redundant programming practice. As we get into the use of IF and related statements you will see the potential for using STOP and RETURN on their own. The Fortran 77 and 90 standards state that if an END statement is reached in a main program it will terminate execution (same as STOP), and if an END is reached in a subprogram, it has the same effect as a RETURN.

What is the difference between a Subprogram and a Subroutine.

A subroutine is one type of a subprogram. A Function is another, and a BLOCKDATA routine is a third.

Subprograms. What do they do and how do the help program.

They do anything that the main program can do. They help you organize your total program (main program + subprograms) by grouping specific tasks in a well defined location. They can save you repeating similar program structures at several places in your code. Also, for many tasks someone has already written a subprogram to do the job and you save a lot of work by picking up their subprogram and plugging it into your work. You will see an example of this when we start solving systems of linear equations (say 10 equations and 10 unknowns). You will define the equations, then let somebody else's subprogram solve them.

Please explain Function Subprograms.

What you have so far is just meant as an introduction. I'll be talking about them several more times during the semester. In the mean time, play with my example in newton3.f . It will get you safely through the homework. As an exercise try adding a second function at the end of the file called FUNCTION G(x), that sets G(x)=x4-2x2+1, and add lines in the main program to evaluate G(0), G(1), G(2) and print these values.

I am still unsure exactly how to put these functions and subroutine to good use. What exactly do they do and why are the better than a normal straight-out program?

First functions. The intrinsic "sin" function is a good example of why to use a function. There are many times that I want to use the sine of some angle within a larger expression. For example the height at time "t" of a shell fired with initial speed, v0, and elevation angle, angrad, can be computed with the assignment statement:

      height = v0*sin(angrad) - 0.5*g*t**2
The use of the function in this line is a natural match with what I would write as an equation of motion, and saves me preceding the evaluation of height with the many Fortran lines needed to generate a good approximation to the sine of angrad. Somebody else worried about the programming to get sin(angrad). This function is special only in that it used more often than most others. If I'm working with a heat conduction problem, I might have a statement requiring the use of a zero order Bessel function.

      T= 300.+10.*exp(5.*time)*Bess0(4.*r)
Fortran doesn't provide Bessel functions in its set of intrinsic functions, but it lets me write my own.

      function bess0(x)
.
.    (a few hundred boring lines here that you don't want to see)
.
      return
      end
The nicest part of this is that I'm going to use this Bessel function many times in a given program, and use it in a lot of programs over the years. One set of Fortran Statements generates the values I need anywhere in the program. In new programs, all I have to do is to include a reference to the function in any appropriate Fortran statement, and include a copy of the Bessel function subprogram taken from one of my older programs (or my personal library of useful functions and subroutines). New work is minimal.

Look at htcoef1.f for a simple example of a function. More will follow during the semester.

The subroutine is appropriate when I'm doing things that don't naturally produce a number to be used in the middle of some formula. The best reason for the subroutine is when this group of statements will be used many times by your program (see htcoef2.f). However, it is also a good idea to group lines that perform a well defined task into a subroutine, even if they are used just once. It adds to the clarity of your program, helping to create an outline structure. You will gain further appreciation for the value of subprograms as the semester goes on.

In my main program, the argument list for a function is "(x,dfdx), and in the function it is "(x,deriv)". What happens if I use the variable "deriv" from the function in the main program? Will it have the same value as dfdx or will it give me an error?

Here's a sample of what I think you're asking

      program test
      implicit none
      real x,y,dfdx,deriv,f
      x=10.
      y=f(x,dfdx)
      print *, ' x = ',x,'  y = ',y, '  dfdx = ', dfdx,
     1  '  deriv = ',deriv
      stop
      end
      function f ( x, deriv)
      implicit none
      real x,dfdx,deriv,f
      deriv = 2*x
      f = x**2
      return
      end
If you compile and execute this program you will usually get no compiler error messages a result of:

x = 10.0000 y = 100.000 dfdx = 20.0000 deriv = 0.00000E+00 What has happened is that the variable named "deriv" in the main program is totally unrelated to "deriv" in the function "f". Because no value has been assigned to "derive" in the main program. It simply contains the value of 0.0 that was loaded into it's location in memory when the program was created. By the way, you should not rely on zeros being loaded into variables if you don't tell the computer anything else. The variables "dfdx" in the main program and "deriv" in "f". are related. Because they occupy the same position in the argument list of the function and the reference to the function.

Could you go over exactly what DMIN means?

Actually you are dealing with DMIN1 and DMIN0 functions that are analogs to AMIN1 and AMIN0 functions. The "D" says that you are returning a DOUBLE PRECISION value, and for real arguments (DMIN1), they are DOUBLE PRECISION. The best bet continues to be the generic MIN function, which will sort out the needed types.

If you attempt something like MATMUL(A(2,1),B(2,1)) will it give you an error message, or do you need to build in diagnostics?

Yes, that is a Fortran syntax error. "matmul" wants arguments that are simply names of whole arrays. You have given it 2 elements of arrays as arguments.

What is the need for functions, when subroutines can do everything?

Convenience in arithmetic expressions. " y = 2*sin(x)+x**2" is easier and closer to our experience in mathematics than

      call sin (x,sinval)
      y=2*sinval+x**2
Are subroutines compiled as separate programs?

In effect yes. The compiler processes each program unit (main program, subroutine, function) alone, without thinking about the other units. Without telling you, it passes along its results to another program called a loader or linker that takes care of the final details of adjusting address references so the main program and subprograms can all talk to each other. Later in the semester, I will show you an example where I compile some subroutines by themselves, without creating an executable program. In a separate operation I will compile the main program and link it to the results of the earlier subroutine compilation.

External means you want to use a variable in the call statement for a subroutine as a function, right?

Basically yes, but the "variable" in the call statement must be the name of a function that you have created somewhere else in your program. In the Subroutine statement, the corresponding argument can have any name, but must be also declared in an EXTERNAL statement so it is recognized as a function.

I missed what an INTRINSIC statement is. What does it do?

An INTRINSIC statement makes a list of intrinsic functions (e.g. sin, cos, exp) that you want to pass as arguments to subroutines, or functions.

Why do you pass an intrinsic function as an argument to a subroutine? Why can't you just call it within your subroutine?

Normally you do just call the intrinsic function within your subroutine. The only time you pass a function name through an argument is when you are trying to improve the flexibility of your subroutine. Look at plot2.f for a crude example of this. You may need a little more math to understand my next example. I have at times needed to generate "analytic" series solutions to heat conduction problems. In Cartesian coordinates this involves a Fourier series sum of trigonometric functions. In Cylindrical coordinates the series is in multiples of Bessel functions. For common boundary conditions, the mechanics of generating the solution is the basically the same. I can create a single subroutine to produce solutions for both Cartesian and Cylindrical coordinates, and let the argument list pass the appropriate functions for the desired coordinate system.

Why doesn't Fortran have intrinsic functions for something as simple as factorial?

Two reasons. Factorial isn't all that common in heavy duty scientific and engineering applications. When it does occur, it almost always in a context where it is more computationally efficient to generate it as you go. You need 2! first then 3!, then 4!, etc. You are basically stuck doing a factorial within the context of a do loop unless you get really good and learn to write "recursive functions", but then you are just fooling yourself and writing another form of do loop. When you are taking the factorial of a large number and don't need an exact answer you can resort to Stirling's Approximation. A Fortran statement that will load the value of this approximation into the variable nfact is:

	nfact = sqrt(2*3.1415963*n)*(n/2.71828)**n 
by 20! this has a 0.4% error. Try larger numbers on the machine for a better feeling of the approximation.

What does the function REAL(x) do?

Not much if x is already a real variable. If x is and integer, the output of REAL is a real (floating point number with the same value. REAL(3) is 3.00000E+00. If x is a complex number REAL returns the real part (as opposed to the imaginary part) of x.

I am still unclear with Subprograms. Could you give a summary.

The purpose of Subprograms (Functions and Subroutines) is to isolate certain well defined calculational (or I/O) tasks in a special location. This permits easy use of these lines of programming at any point in a large program. This has the side-effect of minimizing errors in your program. Rather than entering the same 20 lines of programming at 10 different places in a large program, and introducing typos in some of these locations, I put it all in one location limiting the area I must search if an error exists. If I later decide to do this specific task in a different way, replacement of the old coding is much simpler. In addition the total program becomes much easier to understand if there is just one point where viscosity is calculated, one point where enthalpy is calculated, etc.

Functions and Subroutines are very isolated from each other and from the main program. You start by assuming that a given subprogram knows nothing about what is going on elsewhere, that variable names do not necessarily have the same meaning as they do elsewhere. You pass information into and out from subprograms via the argument list. Say that in the main program you have the line:

	z=funxy(x,y)
and later we define the function as

	function funxy(a,b)
	real a,b,funxy
	funxy=(a+b)**2
	return
	end 
When the line defining z is reached, the program makes a note of were the values that we have labeled "x" and "y" are located, and where in memory it wants the value called "funxy" tucked away. It usually puts this note in a memory location near the start of the instructions to evaluate funxy then branches to these instructions. Although funxy was written in terms of variables called "a" and "b", it checks the note from the calling program (calling program could be the main program or another subprogram) and associates "a" with the contents of the memory location named "x" in the calling program and "b" with the contents of the memory location named "y". The numbers in these locations are summed, the sum is squared, and the result is tucked away in the memory location reserved to contain the results from "funxy".

What are the relative advantages of Function Subprograms, and Statement Functions?

The Function Subprogram should be your choice when evaluation of the function takes more than one line of Fortran or the function is referenced from many places in your program, and size of your executable program is important. I am including liberal use of the line continuation option (character in column 6) in my definition of "one line of Fortran". It is not difficult to generate functions that need some IF tests to cover special cases or error processing (see viscl.f). These must be Function Subprograms. As for the space issue, you need to understand how each of these function types are implemented in machine code. The machine instructions for a Function Subprogram exist at only one location in the final executable code. When the Function Subprogram is referenced somewhere, some notes are made about the location of information and the location where program execution will continue after return from the function, then the flow of instruction execution branches to the Function Subprogram's code. All of this takes some extra computer time beyond what would be needed if the Function's programming was just imbedded in the rest of the code, but generally takes less space.

When a statement function is referenced an interesting thing happens at compilation time. The statement function basically disappears from the final executable machine code. If I write the following Fortran, with the first line as a statement function:

	g(x,a,b)=a*x+b
	x=1.0
	y=2.0
	z=g(x,2.,3.)+g(y,4.,1.)
then what is effectively generated after the compiler is through is the machine code equivalent to:

	x=1.0
	y=1.0
	z=2.*x+3.+4.*y+1.
No branches are generated to statement function coding, the statement function is substituted into the locations where it is referenced. This results in code that takes less time to execute than code using an equivalent Function Subprogram, but when used too frequently with long functions can produce longer executable files than you want.

What is the difference between a Function Subprogram and a Subroutine.

Some of the comments in the textbook are misleading on this subject. There are really only two differences. The biggest difference is that a subroutine never returns a value that is associated with its name. This means that you never need to declare a subroutine name in a type statement (REAL, INTEGER, ...). All information coming back from a subroutine passes through the argument list, or something called a COMMON block (later). However, there is nothing in these communications channels that can't be used by a Function Subprogram. A secondary difference is that a Subroutine need not have an argument list. This won't make sense to you until you learn about COMMON blocks, so I've ducked the issue thus far.

I'm confused about CALL statements. Should they be within subroutines that they are calling for?

Think of a CALL as a GO TO statement. If you say "CALL INPUT1", Fortran effectively looks for the statement called "SUBROUTINE INPUT1" and goes there to execute more instructions. Just before taking the jump, the program makes some notes so that the code in the SUBROUTINE knows about location of arguments, and knows that when it hits a RETURN statement, it should GO TO the statement in your code following "CALL INPUT1". You can see when thinking of this as a GO TO operation, there are too many opportunities for an infinite loop if SUBROUTINE INPUT1 contains a line that says "CALL INPUT1". There are also some subtle data management nightmares. To avoid all of this, older Fortran standards refused to let you include "CALL INPUT1" in SUBROUTINE INPUT1. This continues to be a good idea. However, for those who like to live on the edge Fortran 90 will let you include "CALL INPUT1" in SUBROUTINE INPUT1. This process is called recursion. I never use it, and don't recommend it. You will get another story from a certified Computer Scientist. Those folks really love recursion, and to be fair can do some fairly slick things with it.

Can you call an intrinsic function in a parameter statement?

No, except for functions specifying a variable's kind attribute. You can only use simple operations +, -, *, and /, combined with constants (1.0, 1, 1.e1, etc) and previously defined parameters. You can use **, but only if the exponent is of type integer so that the compiler can generate the answer without using the intrinsic functions "exp" and "log".

Modules

If I change a value of a variable defined in a MODULE in one Subroutine, is it changed when I use it in another.

Yes. Look at and execute module.f .

What is a MODULE?

A module is a place where you reserve memory for variables with specific names. When the compiler processes the module statement it sets aside memory for the variables listed within the module. When you include a USE statement for a given module in a subprogram, the compiler looks at the list of variable names defined in the MODULE, and any time it sees a variable with one of these names in the subprogram (or main program if appropriate), it uses the memory location reserved by the MODULE rather than associating a new one for the variable. This lets several (or many) subprograms agree that a variable name really refers to the same memory location, and all pick up changes made by any one subprogram without information going through the argument lists.

Can MODULEs be put at the end of the main routine. Where do they go?

Think of them as another program unit like a SUBROUTINE, FUNCTION, or BLOCK DATA. Put them after the main program if you like, or put them after any subroutine or function. Doesn't matter.

When using COMMON blocks or MODULES where is the best place to put them?

I like to locate MODULES before the "PROGRAM" card of the main program (see module.f ). However, you can place the MODULE program unit after the END statement of the main program or any subprogram. Note that there is no flexibility in the position of the USE statement. USE statements must immediately follow the PROGRAM, SUBROUTINE, or FUNCTION statements. COMMON blocks are always contained within the program units where they are needed, grouped near the top with other non-executable statements.

Branching Statements

What kind of Loops does Fortran have (For loops, While Loops, Repeat Loops)?

Older Fortrans had a fairly simple DO loop statement. Fortran 90 has enhanced these capabilities with DO WHILE constructs, the WHERE, CYCLE and EXIT statements. WHERE is a standalone loop type structure for use with arrays, and CYCLE and EXIT are statements that are used in conjunction with DO and DO WHILE loops. We'll get to this stuff later.

What is the difference between IF, THEN and DO WHILE statements.

IF THEN combined with GO TO statements will let you do anything you want. The DO WHILE and other DO constructs allow you to loop through certain portions of code many times without ever writing GO TO statements. This makes coding slightly simpler, definitely clearer, and computer science types much happier.

What is the difference between DO, DO WHILE, and IF ( ) GOTO Loops.

In terms of what the computer actually does, there is generally no difference. You can structure all three so that they do the same thing. When properly indented, the DO structures tend to be easier to follow. The DO WHILE structure can produce slightly more compact coding, combining a straight DO with the option for some extra comparison logic. In a vector and/or parallel computer, DO's send strong hints to the compiler that it should be looking for ways to feed a pipeline or spread calculations over multiple processors.

How do I change the DO loop increment to something other than 1?

"DO 100 I=1,10,2" will do the loop for I=1,3,5,7,9.

What happens if a DO WHILE statement doesn't stop running?

Very good question! Sometimes you aren't careful enough with your logical expression in the DO WHILE line, or with other options to exit the loop, and this does happen. On most Unix computers, you will be sitting around half of forever waiting for the program to be killed. If you believe that it has been running too long, hold the Control key and hit the "c". If you don't see an obvious error, turn on dbx, set a breakpoint on the next statement after the DO WHILE, and watch the evolution of variables that contribute to you logical test that keeps the loop running. When you see what is wrong, type "quit" at the dbx prompt, to end the session.

When is it better to use a DO WHILE instead of an IF statement.

Avoid the use of IF statements combined with GOTO to repeatedly loop over the same block of code. This is a job for DO WHILE or simple DO statements. IF statements are for simple decisions to do one thing or another (or another...) then to move on to other code.

I don't understand what the 3 arguments in DO loops are or say. For instance "do 100 i=1,10,2"

Best way to do that is to look at what happens to a sample loop. Try the following:

      do 100 i=1,10,2
         print *, ' In loop i = ',i
 100 continue
      print *, 'After loop i=', i
The printed results will be:
In loop i =         2
In loop i =         4
In loop i =         6
In loop i =         8
In loop i =        10
After loop i =        12
Your DO statement said to do the loop the first time with i=2, do the next time with i=2+2=4, do the 3rd time with i=4+2=6, and keep doing the statements in the loop with i incremented by 2 until i >10. When i>10, don't do anything in the loop again and move on to the next statement after the end of the loop.

Is it possible to use the values generated in the middle of a do loop?

Yes, look at the example above. The variables y and dfdx are generated in the middle of the loop, but also "used" in the print statement below the loop.

Is it possible to lay out a two dimensional array with the double DO loops:

	DO 10 J=1,4						
		DO 20 K=1,4
			A(J,K)=SUM(T(1:N)**(J+K-2))
    20			CONTINUE
    10 		CONTINUE	
yes, if you have a Fortran 90 compiler.

Does it matter if you use DO WHILE or loops like DO 100? Does it make any difference?

You can do any looping task with either form. The difference is in the level of convenience. If you are going to be stepping some INTEGER as the loop proceeds (counting), use the "do 100 i=imin,imax,istep" form. If it is easier to do your conditional tests somewhere in the middle of the loop, go with the do 100 type loops again.

What is a SELECT CASE statement?

It is really just another (often more convenient form) of an IF, THEN, ELSE. For the lecture notes example:

      select case (i)
         case default
            cond = 0
         case (1:5)
            cond=condUO2(temp)
         case (7)
            cond=condZr(temp)
      end select
You could also write:

      if (i.ge.1.and.i.le.5) then
            cond=condUO2(temp)
      else if (i.eq.7) then
            cond=condZr(temp)
          else
            cond = 0
      endif

Can you use <, >, = in your CASE statements?

Yes as part of logical expressions, but the CASE becomes more complex than using IF, THEN, ELSE. For example I can do the following:

         select case (i>1)
         case (.true.)
               print *, 'i>1'
         case (.false.)
              print *, 'i<= 1'
         end select
Is the CASE DEFAULT widely used in programming? What type of programs?

Fortran 90 is too new for any of its new constructs to be "widely used". However, CASE DEFAULT is a variation of the simple ELSE statement. ELSE is very widely used. IF, THEN, ELSE IF, ELSE Anytime, you have a simple logical decision to do one thing (IF, THEN), or another(ELSE IF), and a totally different thing to do if your simple tests fail (ELSE). One fairly universal use of this logic, is in subroutines for processing input. A given user response is compared to a series of accepted responses and appropriate action is taken in each case. An ELSE (or CASE DEFAULT) takes care of sending an error message when the user provides unrecognized input. You will discover many more ways to use this in Fortran or another language as you continue to program.

How does the compiler know how to use the "i" in "go to (100,200,400,600 ) i". In general the GOTO statement is confusing.

Whenever a "go to" is followed by a set of integers in parentheses, the compiler only knows of one thing to do with the "i". The compiler actually converts this structure to a bunch of machine code IF branch statements. The effect of this is that if "i" has a value 2, then the computer branches to execute the statement with label 200. If "i" has a value 3, then the computer branches to execute the statement with label 400. If "i" has a value greater than the number of integers (labels references) within the parentheses, then the next statement after the "go to (100,200,400,600) i".

All a GOTO does is to change the flow of the program. When a GOTO is reached, it generally tells the CPU to execute an instruction somewhere in memory other than the next location after the GOTO. Look at an example with GOTOs or write one of your own, and watch it execute with dbx, using the "step" command to see each statement as it is executed.

Why are GO TO statements bad, and what can we do to avoid them?

GO TO is bad only to the extent that it makes coding difficult to read or significantly increases the possibility for undetected programming errors. I think they are an important part of the language, but should be used sparingly. Use of DO loops and IF, THEN, ELSE constructs (along with good indentation habits) whenever practical will take care of most of the problem. In extreme cases where you have many nested IF statements, with many lines contained in each THEN/ELSE option set, even good indentation habits will not improve code clarity. Consider breaking code off into subroutines, or go back to GOTO constructs where you can at least trace label numbers with an editor.

Input/Output Statements

What is the difference between a PRINT and a WRITE?

A print always outputs to the "default device" generally the terminal screen. A write sends things to a numbered unit that may be the screen, a disk file, or sometimes a printer (see OPEN statement for connecting unit number to a file).

How do we make sure that all new data is being added to the end of the file "temp.log"?

open (12,file='temp.log', status='old', position='APPEND')

Why does a program with an OPEN statement but no CLOSE statement still compile?

Two reasons. First a CLOSE is not mandatory. The STOP statement quietly looks around for all open units and closes them properly. Secondly, OPEN is a peculiar statement that has effects beyond subprogram boundaries. I can OPEN unit 11 connecting it to a file with the name "input.data" in any subroutine I want. Any other subroutine (function, or portion of the main program) executed after that OPEN will know to direct the results of any WRITE(11,... statements, to the file "input.data".

Can you use an INQUIRE statement to find out if "temp.data" is being read?

Yes, the command "inquire (file='temp.data', number=iunum)" returns a value of -1 if the file is not currently connected to a unit number, or the value of the unit number, if the file has been opened. Its up to you to include logic to decide if that unit number is used for READ or WRITE.

How do you use FORMAT statements and the format number in a write statement?

Always think of the FORMAT and WRITE as a tightly coupled pair. First construct the write statement to put out the values of whatever variables that you need to see (say "j", "x", and "y"). Next think about how you want the numbers to appear in the file or on the screen. If an existing FORMAT gives that result use its number. If not pick any number not currently used as a label and include it in the second position within the WRITE's parentheses (first number is the unit number or a "*" for screen). The statement:

write(11, 2222) j, x, y

writes to the file opened on unit 11 using a FORMAT labeled 2222. Now I construct the format to give the desired appearance of the line. Perhaps I wanted to handle up to 4 digits in the INTEGER "j", standard decimal notation for "x" with 3 digits after the decimal, allowing for values of x between 0 and 9999.999 , and exponential notation for "y" with 4 digits after the decimal, a significant digit before the decimal point, and room for a negative sign in front. I'll also allow at least 5 blank spaces between each number.

2222 format (i4, 5x, f8.3, 5x, 1p, e11.4)

I can insert quoted strings above, if labels are needed for the numbers. The best thing that you can do is to try a few of these with a short test problem.

If you have a number like 11.5967 and you print it with format f4.1 will it output 11.5 or 11.6?

It rounds up to 11.6

What makes the difference when you use different descriptors like i, f, e, p? Could you explain some of these examples like 1p, e9.3, 3x,?

"i" only works if the variable to be output in that position is an INTEGER. "i5" says output an integer in the line allowing 5 spaces to do the job. If the integer is only 2 digits, the first 3 of the 5 spaces are blanks, and the last 2 contain numbers.

"f" only works if the variable is REAL, and puts out a simple decimal number like 1.1, 2.123, etc.

"e" only works if the variable is REAL, and puts out a number in exponential notation like 0.1234e+01, 0.999e-01, etc.

"p" is for use only if you get tired of the leading zero in "e" format. If the variable x contains 1.23400000, then the write:

write(*,1000) x 1000 format(' x = ',1p,e9.3) give a resulting line

x = 1.234e+01 One key item is to get used to synchronizing the use of "i", "f", and "e" with the variables in the WRITE statement. For

write(*,1001) icount, x, y 1001 format ( ' At icount = ', i5, ' x = ', f5.2,5x, 'y = ',1p,e12.3) The "i5" is the first variable descriptor in the FORMAT and tells you how to output the first variable listed after "write(*,1001)" (icount). The "f5.2" is the second variable descriptor in the FORMAT and tells you how to output the second variable after the WRITE (x). The "e12.3" is the third variable descriptor and tells you how to output the third variable being written (y), as modified by the preceding "1P". The "5x" inserts 5 blank spaces between the end of the printed value of "x", and the beginning of the printed string "y ="

Spend some time running format.f and looking at how each combination of WRITE and FORMAT statement produces results on the screen.

How do you get a FORMAT statement to write the titles in the file you write into?

Take a look at the "1000" FORMAT in format.f . Titles involve, making a decision on exactly where you want them in the line, then constructing a FORMAT with the appropriate mix of spacing (X edit descriptors), and quoted strings. I could put this title anywhere, just by changing the unit number on the WRITE statement.

open (11,file='test.out') write(11,1000) 1000 format(14x,' Pressure',13x,'Temperature',17x,'Density') would put the same title string into file "test.out", that format.f wrote to the screen.

How can I format my output to look nicer (clear screen, double space, etc.)

The best tools within Fortran are the in the FORMAT statement. "X" to add spaces in a line, and "/" to add blank lines on the output. Fortran doesn't know directly about your terminal type, so can't issue a specific screen clear command. You can brute force with "////////////////////////" (24 "/"'s) or call the Unix system as in the following example (note the extra call to get a listing of files after the screen is cleared). When clearing screens it is also useful to learn the Fortran "PAUSE" command to pause execution until you hit the RETURN (ENTER) key. Note that 'call system' is specific to the RS6000's. Other systems often have similar Unix connections.

		program test
		call system('clear')
		call system('ls -alF')
		pause
		call system('whoami')
		end
What does // do? (in a Format)

It sticks 2 character strings together into a single string. If char1='file1.in' and char2='file2.out' then when char3=char1(1:5)//char2(6:9) the contents of char3 are 'file1.out'

How about going over Format statements or Write statements that serve as formats.

You're asking for a Chapter or 2 in a text book, but let me cover the basics. A Format is in a sense a language within the Fortran language. It tells the computer how to convert its data into a nice set of characters for display, printout, or further character processing. As such the contents of a format statement are not converted to mysterious machine instructions until they are actually used (for example in a WRITE statement). This opens the possibility of using a WRITE statement to write things into a character variable to form a format variable. The WRITE does not directly serve as a format. It just builds a character string that works as a format somewhere else. For example in

	 CHARACTER*40 MYFORM
	 ...
	 WRITE(MYFORM,2000 ) N
  2000 FORMAT('(''I='',',I1,'I5)')
	 WRITE(6,MYFORM)(II(J),J=1,N)
the first WRITE creates a format MYFORM that is used in the second WRITE. You also learned in examples like this that if you want a single quote to appear as part of a character string, you must enter it within a quoted string as 2 sequential single quotes (''). The only other major new thing that you learned about formats since the last exam is that you can write more numbers than you have specifications for numbers within the format statement. When the last ) of a format is reached, the WRITE wraps around back to the last ( to find out what to do with the remaining numbers. When this wrap-around occurs, a new line is started on the output (like a hidden / in the format). You should still remember what I, E, F, X, and / can do and identify the result of writing things like the numbers 5, 3, 1.1, .03, 1001.1 with a format "(1X,2I5,1P,3E10.3)"

Is there any way to use variables in a format statement? Yes, but you have to use one format statement to build a second using a write to a character string. For example if you want to include the value of "n" as the number of real numbers per line you would do the following:

	 PARAMETER (N=4)
      CHARACTER FORM1*16
      REAL A(N,N)
      DO 10 I=1,N
      DO 10 J=1,N
      A(I,J)=J*100+I
 10   CONTINUE
      WRITE(FORM1,2000)N
 2000 FORMAT('(1X,',I3,'F6.1)')
      WRITE(6,FORM1) A
      STOP
      END

Pointers

Originally you said you can only use a pointer in an integer variable statement, but later you wrote a pointer in a real statement. Can you do both?

Sorry for the confusion. I told you that a pointer is an integer, which is true internally. At its root, a pointer contains an integer giving the address in memory of some variable. However, the pointer variable carries with it a type definition telling what type variables it can point to. In Fortran you never see the underlying memory address stored with the pointer. The pointer can have any data type, including real, integer, logical, character, complex, and derived types. It can only point to targets with the same data type.

Target is the original variable and pointer is the variable that is set equivalent to the target (e.g. px=>x) right?

Yes.

Why can't you assign a pointer to an individual space in an array?

You can if the pointer is not an array itself. For example

      real, target :: a(100)
      real, pointer :: aptr
      aptr => a(50)
will work just fine. The pointer variable "aptr" is equivalent to element 50 of array "a".

Can pointers be placed anywhere in a program?

Pointer assignments ( pa => a) are "executable" statements and can be placed anywhere that other executable statements may be placed, providing proper target and pointer declarations have been made in the preceding non-executable statement block. Once assigned, the pointer variable can be used anywhere that a normal variable would be used in executable statements. Three places that pointer variables can never be used are DATA, NAMELIST (we skipped that one), and EQUIVALENCE statements.

What are some more functions of pointers? I'm a little unclear as to what they can do and what they are used for.

I'm at a loss to give you a clean example in science and engineering, without giving you a couple semesters worth of applications involving solution of partial differential equations. It boils down to a question of how you have to arrange data for modeling complex physical systems. I've talked frequently about simulating the flow of air in our classroom, but what if I wanted a program that could model the flow of air in the whole building, in fact in any building. The grid of points that I would lay down to evaluate air properties at points in the building would be very complex, and in a good program, generated at the time input specified the geometry of the building. In practice, solving the problem for the entire building is best accomplished by looking at the solution in each room and then accounting for air flows between the rooms. You have a full set of the grid points for the building, with subsets representing individual rooms. Pointers do a nice job of picking up subsets of points for each room to do the local solution steps.

If you want a computer science example, take a look at section 20.7 p. 537 in your textbook, where the author works with a linked list. Linked lists are very important in the computer science world, but have limited application when creating models of physical systems.

Are pointers used in C++? If so, do they function the same as in C or Fortran?

Standard C is a contained in C++ just as Fortran 77 is contained in Fortran 90, so you automatically have C pointers. C++ adds a data type called the "Reference Type", that is like a Fortran pointer.

Common Blocks

Is a MODULE better than a COMMON block.

Almost always yes. The only reasons to use COMMON blocks are if you expect to use your program on a computer with only a FORTRAN 77 compiler (they still exist), or if it is very important that you control the order in which your data is stored in memory.

What does "COMMON/CONTROL/ A, B, C" do to the values of A, B, C?

It doesn't do anything to the values. It just establishes a place in memory for the values to be stored and retrieved. Every subroutine and function in your program that contains the line "COMMON/CONTROL/ A, B, C" will agree that operations using variables A, B, or C will get numbers from or put numbers in the same appropriate location in memory. That is, they agree that a reference to "A" means the same in all routines with common CONTROL, etc.. To give A, B, or C a value, you have two options. You can assign values at compilation time with a BLOCK DATA routine:

	BLOCK DATA  ABCVAL
	COMMON/CONTROL/ A, B, C
	DATA  A,B,C / 1.0, 2.0, 3.0/
	END
You can also assign and use values with executable statements. Say subroutines SUB1 and SUB2 contain this common block. If SUB1 contains the lines "A=1.0", and "B=2.0", and SUB2 has the line "C=A+B", then the value of C after this line in SUB2 is executed is 3.0.

Do the declarations of REAL and INTEGER have to come after the COMMON statement?

No.

What's the use of a blank common as opposed to a common with a name?

The major use is in cutting the size of the of the executable file produced by "f77" (usually a.out). It won't seem like a big deal to you now, but in applications containing many arrays with thousands to millions of elements each, this can make a huge difference in the amount of disk space you soak up. A secondary use is that, when the program starts, the space finally allocated to blank common in memory is at the very end of the program. If you are tricky enough, you can take advantage of this to dynamically extend the size of a single array in blank common as your space requirements grow during execution.

When is the common command not used correctly in a Fortran Statement?

You're asking for quite a bit here. There are lots of ways to introduce errors. The most obvious is to try inserting a common in the midst of executable statements. It belongs up with DIMENSIONs, PARAMETER's, SAVE's, and the rest of the non-executables. A more subtle problem is the use of multiple COMMON statements for blank common or the same named common. Fortran will accept the following program:

      program testcom
      common a,b
      common c,d
      a=1.0
      b=2.0
      c=3.0
      d=4.0
      call sub1
      stop
      end
      subroutine sub1
      common c,d
      print *, c, d
      return
      end

However, the values printed out are 1.0 and 2.0. The second common statement in the main program just tacks the variables "c" and "d" onto blank common after "b". The two commons together are equivalent to "common a,b,c,d". In the subroutine you are saying that "c" is the first element in blank common, so Fortran associates "c" in "sub1" with the same address in memory as assigned to "a" in the main program. Also recall that mixing reals and integers (common/blk1/a,b,i,c) when the reals are double precision may cause errors on some machines, and is not a good idea on any 32 bit machine.

How do you use a BLOCKDATA (where you put it, etc)?

Use it like a subroutine, but instead of starting with a "SUBROUTINE" statement, start with a "BLOCKDATA" statement. End it with an "END", but don't bother with the "RETURN". You can put it anywhere in the program after the "END" of another subprogram. Take a look at the use of BLOCKDATA in fall.f.

If you use a BLOCK DATA statement to initialize a common block, do you lose the advantage of a smaller executable file?

No, and Yes. You don't lose anything because the disk space advantage was already gone when you decided to use the named common blocks rather than blank common. BLOCK DATA will only assign values to contents of named common blocks. The disk space decision takes place when you choose to place variables in named common or blank common. Please remember that you can have lots of different named common blocks, but there is only one blank common block.

Why would you use BLOCKDATA?

When doing scientific and engineering calculations, there are lots of constants that you may need. The values of Pi, g, Plank's constant are simple ones. Often there are hundreds to thousands of constants that feed into Equations of State and calculations of physical properties like fluid viscosities, conductivities, and so on. These really are constants. Numbers that are needed for calculations, but that you do not want to calculate yourself. Generally, once imbedded in a code you won't change them for many years (or the rest of your life). To save computer time you want to preload the values of the constants with a DATA statement.

You will need many of these constants in several subroutines, so you may choose to transmit them with a COMMON. At this point Fortran backs you into a corner and forces you to create a BLOCKDATA subroutine for DATA statements defining values for any variables in COMMON blocks. For example, if I am going to give access to the values for Pi and g in:

common/const/ pi,g

and I want to define the values with a DATA statement, then I have to include:

blockdata constval real pi,g common/const/ pi,g data pi,g/3.14159265,9.807/ end

Up to other Questions and Answers / Home


Maintained by John Mahaffy : jhm@psu.edu