Notes and Suggestions These programs have been written over a long span of time, over twenty-five years, but each one has been converted to Fortran 95 during the span of little more than one year. As a result, some features of my programming style should appear uniform across the programs, while vestiges of old styles remain. These programs were developed using the Lahey F95 Version 5.5 Compiler on a Pentium PC under MS Windows 95. With minimal changes, discussed below, these programs also ran successfully using NAG F95 compiler in a UNIX environment on a SUN Ultra 5. The only changes that were necessary dealt with different settings for double precision calculations: Lahey -- KIND=8 NAG -- KIND=2 When using another compiler, the user should immediately determine how double precision calculations are invoked. The code distributed with this book follows the Lahey (KIND=8) setting. If your compiler uses another setting, such as KIND=2, then the following changes should suffice: 1. Change all 'KIND=8' strings to 'KIND=2' 2. Other changes, such as a conversion of an integer variable j to double precision with the intrisic function real(j,kind), will require a change in the second argument from 8 to 2. Each instance has been flagged in the code with comments which can be found by searching for the string '! KIND' and making the appropriate change in that line of code. In a similar vein, all of the seeds for the linear congruential random number generators (see Chapter 10) have been set to the same value, so a search for the string '5151917' should locate the setting of the seed so that it can be changed appropriately. Most compilers permit some flexibility in its operation. The most important options are 1. style of code (F77, F90, F95) 2. code optimization 3. error trapping 4. array/type checking 5. debugging options In my view, the best compiler does exactly as the instructions dictate, and complains about any errors. If there are syntax errors, the program should fail compilation and not produce an executable; if there are run-time errors, such as invoking a square root for a negative argument, the program should abort. To this end, these are my comments on compiler options: 1. write your programs in F95 coding style Development of a uniform programming style is essential for producing reliable code. Primarily, a common style permits easy reading and understanding; also, a ritualistic approach to coding can help avoid syntax errors and omissions. 2. use the lowest level or second-lowest level of optimization Compilers use some optimization techniques to speed up the operation. Some optimization is sound and good, but others can cause some annoying behavior or very different results on different compilers. The worst situation are some weird things that can occur with random number generation (see Chapter 10). In general, optimization can make subtle changes in the behavior and performance of some code. 3. permit underflow to flush to zero without error; abort overflows You should handle some level of error trapping yourself. Make sure all variables are initialized; do not assume they are initialized to zero. Moreover, while some compilers have an option to set the initialization (usually to zero), if your code is to be used on another machine with a compiler without such an option, considerable effort will be needed to insure that all of the initializations are done properly. A better option would be to set all variables to underfined. Code in a careful way so that any overflows are truly errors and should cause an abort. For example, for large values of x, don't code p = x/exp(x) because when x is too large exp(x) will overflow while a proper value of zero is available. Coding as p = x * exp(-x) and flushing underflows to zero will provide the proper result. Read carefully Chapter 2. 4. always use type checking; use array bound checking as a debug tool The most common errors in Fortran that are difficult to debug are those where variables or arrays are passed as arguments to subprograms. Type checking produces an error if the arguments in the calling program do not match the type in the subprogram. Array bound checking produces an error if the index to an array exceeds the array bounds. Usually array bounds are not checked; an address may be computed that is the storage location of a variable of a different type. Passing arrays as arguments is still clumsy in Fortran, and often code is written for expediency that would always fail using bound checking, although the program would always run correctly. Avoiding array bound checking permits some cleaner code with an increased risk of programming error. Array bound checking usually slows programs substantially. 5. if you have the time, learn your compiler's debugging tools Most compilers have some debugging tools available. They differ across compilers and change over time. If you intend to do a large project in Fortran with the same compiler, then it would be worth your time and effort to learn the debugging tools that are available. This will pay off when you've made some subtle error that hours of staring at the code will not reveal.