Ed Akin - Object-Oriented Programming Via Fortran 90-95 (2003, Cambridge University Press)
Ed Akin - Object-Oriented Programming Via Fortran 90-95 (2003, Cambridge University Press)
Ed Akin
Rice University
Mechanical Engineering and Materials Science Department
Houston, Texas
Draft # 4.2, Copyright c 2001, All rights reserved.
ii
Contents
Preface vii
1 Program Design 1
1.1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Problem Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Modular Program Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4. Program Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4.1. Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4.2. Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4.3. Flow Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.4.4. Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4.5. Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.4.6. Dynamic Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.5. Program evaluation and testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6. Program documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.7. Object Oriented Formulations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.8. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2 Data Types 23
2.1. Intrinsic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2. User Defined Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.3. Abstract Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.4. Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5. Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
A Bibliography 187
F Subject Index 0
G Program Index 1
There has been an explosion of interest in, and books on object-oriented programming (OOP). Why have
yet another book on the subject? In the past a basic education was said to master the three r’s: reading,
’riting, and ’rithmetic. Today a sound education in engineering programming leads to producing code that
satisfy the four r’s: readability, reusability, reliability, and really-efficient. While some object-oriented
programming languages have some of these abilities Fortran 90/95 offers all of them for engineering
applications. Thus this book is intended to take a different tack by using the Fortran 90/95 language as its
main OOP tool. With more than one hundred pure and hybrid object-oriented languages available, one
must be selective in deciding which ones merit the effort of learning to utilize them. There are millions
of Fortran programmers, so it is logical to present the hybrid object-oriented features of Fortran 90/95 to
them to update and expand their programming skills. This work provides an introduction to Fortran 90
as well as to object-oriented programming concepts. Even with the current release (Fortran 95) we will
demonstrate that Fortran offers essentially all of the tools recommended for object-oriented programming
techniques. It is expected that Fortran 200X will offer additional object-oriented capabilities, such as
declaring ”extensible” (or virtual) functions. Thus, it is expected that the tools learned here will be of
value far into the future.
It is commonly agreed that the two decades old F77 standard for the language was missing several
useful and important concepts of computer science that evolved and were made popular after its release,
but it also had a large number of powerful and useful features. The following F90 standard included
a large number of improvements that have often been overlooked by many programmers. It is fully
compatible with all old F77 standard code, but it declared several features of that standard as obsolete.
That was done to encourage programmers to learn better methods, even though the standard still supports
those now obsolete language constructs. The F90 standards committee brought into the language most of
the best features of other more recent languages like Ada, C, C++, Eiffel, etc. Those additions included in
part: structures, dynamic memory management, recursion, pointers (references), and abstract data types
along with their supporting tools of encapsulation, inheritance, and the overloading of operators and
routines. Equally important for those involved in numerical analysis the F90 standard added several new
features for efficient array operations that are very similar to those of the popular M ATLAB environment.
Most of those features include additional options to employ logical filters on arrays. All of the new array
features were intended for use on vector or parallel computers and allow programmers to avoid the bad
habit of writing numerous serial loops. The current standard, F95, went on to add more specific parallel
array tools, provided “pure” routines for general parallel operations, simplified the use of pointers, and
made a few user friendly refinements of some F90 features. Indeed, at this time one can view F90/95 as
the only cross-platform international standard language for parallel computing. Thus Fortran continues
to be an important programming language that richly rewards the effort of learning to take advantage of
its power, clarity, and user friendlyness.
We begin that learning process in Chapter 1 with an overview of general programming techniques.
Primarily the older “procedural” approach is discussed there, but the chapter is closed with an outline of
the newer “object” approach to programming. An experienced programmer may want to skip directly to
the last section of Chapter 1 where we outline some object-oriented methods. In Chapter 2, we introduce
the concept of the abstract data types and their extension to classes. Chapter 3 provides a fairly detailed
introduction to the concepts and terminology of object-oriented programming. A much larger supporting
glossary is provided as an appendix.
For the sake of completeness Chapter 4 introduces language specific details of the topics discussed in
4
bottom-up, 4 Drill, 106
bounds, 159 Global Position, 114
bubble sort, 93, 95 Great Arc, 114
ordered, 96 Position Angle, 114
bug, 9 clipping function, 14, 71
CLOSE statement, 75
C, 1, 33, 52 Coad/Yourdon method, 18
C++, 1, 10, 14, 24, 33, 52, 58, 60, 77, 82, 103, colon operator, 56, 61, 62, 78, 160, 163, 167,
123 170
CALL statement, 42 column major order, 181
CASE DEFAULT statement, 64 column matrix, 174
CASE statement, 64 column order, 162
cases, 62 comma, 99
central processor unit, 73 comment, 1, 2, 7, 9, 12, 52
character, 82 commutative, 101, 176, 177
case change, 81 compiler, 10, 15, 91
control, 77 complex, 10, 82, 165
from number, 81 COMPLEX type, 23, 53
functions, 78 COMPLEX type , 24
non-print, 103 composition, 34, 36
non-printable, 77 conditional, 7–9, 11, 51, 58
strings, 77 conformable, 176
to number, 81 connectivity, 170
character set, 23 constant array, 160
CHARACTER type, 23, 26, 53 constructor, 18, 29, 34, 125, 134, 135
chemical element, 25 default, 18
circuits, 170 intrinsic, 18, 26, 34, 39
circular shift, 172 manual, 36
class, 15, 19, 33 public, 37
base, 18 structure, 26
Date, 120, 123 CONTAINS statement, 29, 33, 34, 73, 76, 86
derived, 18 continuation marker, 10
Drill, 105 control key, 79
Employee, 125 conversion factors, 29
Geometric, 120 count-controlled DO, 12, 13
Global Position, 114 CPU, see central processor unit
Great Arc, 114 curve fit, 91
hierarchy, 33 CYCLE statement, 66
instance, 33
Manager, 125, 135 data abstraction, 19
Person, 120, 123 data hiding, 36
polymorphic, 133 data types, 10
Position Angle, 109, 114 intrinsic, 23
Professor, 123 user defined, 23
Student, 120, 123 date, 101
class code DEALLOCATE, 15
class Angle, 114 deallocate, 18, 42
class Circle, 34 DEALLOCATE statement, 75
class Date, 37 debugger, 17
class Fibonacci Number, 29 debugging, 16
class Person, 37 default case, 64
class Rational, 42 default value, 29
class Rectangle, 34 dereference, 58
class Student, 37 derived class, 121
validation, 29
variable, 8, 10, 23, 51
global, 14
name, 10
Program Design
1.1 Introduction
The programming process is similar in approach and creativity to writing a paper. In composition, you
are writing to express ideas; in programming you are expressing a computation. Both the programmer
and the writer must adhere to the syntactic rules (grammar) of a particular language. In prose, the funda-
mental idea-expressing unit is the sentence; in programming, two units statements and comments are
available.
Standing back, composition from technical prose to fiction should be organized broadly, usually
through an outline. The outline should be expanded as the detail is elaborated, and the whole re-examined
and re-organized when structural or creative flaws arise. Once the outline settles, you begin the actual
composition process, using sentences to weave the fabric your outline expresses. Clarity in writing
occurs when your sentences, both internally and globally, communicate the outline succinctly and clearly.
We stress this approach here, with the aim of developing a programming style that produces efficient
programs that humans can easily understand.
To a great degree, no matter which language you choose for your composition, the idea can be ex-
pressed with the same degree of clarity. Some subtleties can be better expressed in one language than
another, but the fundamental reason for choosing your language is your audience: People do not know
many languages, and if you want to address the American population, you had better choose English
over Swahili. Similar situations happen in programming languages, but they are not nearly so complex
or diverse. The number of languages is far fewer, and their differences minor. Fortran is the oldest lan-
guage among those in use today. C and C++ differ from it somewhat, but there are more similarities
than not. M ATLAB’s language, written in C and Fortran, was created much later than these two, and its
structure is so similar to the others that it can be easily mastered. The C++ language is an extension of
the C language that places its emphasis on object oriented programming (OOP) methods. Fortran added
object oriented capabilities with its F90 standard, and additional enhancements for parallel machines
were issued with F95. The Fortran 2000 standard is planned to contain more user-friendly constructs for
polymorphism and will, thus, enhance its object-oriented capabilities. This creation of a new language
and its similarity to more established ones are this book’s main points: More computer programming lan-
guages will be created during your career, but these new languages will probably not be much different
than ones you already know. Why should new languages evolve? In MATLAB’s case, it was the desire to
express matrix-like expressions easily that motivated its creation. The difference between M ATLAB and
Fortran 90 is infinitesimally small compare to the gap between English and Swahili.
An important difference between programming and composition is that in programming you are writ-
ing for two audiences: people and computers. As for the computer audience, what you write is “read” by
interpreters and compilers specific to the language you used. They are very rigid about syntactic rules,
and perform exactly the calculations you say. It is like a document you write being read by the most de-
tailed, picky person you know; every pronoun is questioned, and if the antecedent is not perfectly clear,
then they throw up their hands, rigidly declaring that the entire document cannot be understood. Your
picky friend might interpret the sentence “Pick you up at eight” to mean that you will literally lift him or
her off the ground at precisely 8 o’clock, and then demand to know whether the time is in the morning or
c 2001 J.E. Akin 1
afternoon and what the date is.
Humans demand even more from programs. This audience consists of two main groups, whose goals
can conflict. The larger of the two groups consists of users. Users care about how the program presents
itself, its user interface, and how quickly the program runs, how efficient it is. To satisfy this audience,
programmers may use statements that are overly terse because they know how to make the program more
readable by the computer’s compiler, enabling the compiler to produce faster, but less human-intelligible
program. This approach causes the other portion of the audience programmers to boo and hiss. The
smaller audience, of which you are also a member, must be able to read the program so that they can
enhance and/or change it. A characteristic of programs, which further distinguishes it from prose, is
that you and others will seek to modify your program in the future. For example, in the 1960s when
the first version of Fortran was created, useful programs by today’s standards (such as matrix inversion)
were written. Back then, the user interface possibilities were quite limited, and the use of visual displays
was limited. Thirty years later, you would (conceivably) want to take an old program, and provide a
modern user interface. If the program is structurally sound (a good outline and organized well) and is
well-written, re-using the “good” portions is easy accomplished.
The three-audience situation has prompted most languages to support both computer-oriented and
human-oriented “prose”. The program’s meaning is conveyed by statements, and is what the computer
interprets. Humans read this part, which in virtually all languages bears a strong relationship to mathe-
matical equations, and also read comments. Comments are not read by the computer at all, but are there
to help explain what might be expressed in a complicated way by programming language syntax. The
document or program you write today should be understandable tomorrow, not only by you, but also by
others. Sentences and paragraphs should make sense after a day or so of gestation. Paragraphs and larger
conceptual units should not make assumptions or leaps that confuse the reader. Otherwise, the document
you write for yourself or others served no purpose. The same is true with programming; the program’s
organization should be easy to follow and the way you write the program, using both statements and com-
ments, should help you and others understand how the computation proceeds. The existence of comments
permits the writer to directly express the program’s outline in the program to help the reader comprehend
the computation.
These similarities highlight the parallels between composition and programming. Differences become
evident because programming is, in many ways, more demanding than prose writing. On one hand, the
components and structure of programming languages are far simpler than the grammar and syntax of any
verbal or written language. When reading a document, you can figure out the misspelled words, and not
be bothered about every little imprecision in interpreting what is written. On the other, simple errors, akin
to misspelled words or unclear antecedents, can completely obviate a program, rendering it senseless or
causing it to go wildly wrong during execution. For example, there is no real dictionary when it comes
to programming. You can define variable names containing virtually any combination of letters (upper
and lower case), underscores, and numbers. A typographical error in a variable’s name can therefore
lead to unpredictable program behavior. Furthermore, computer execution speeds are becoming faster
and faster, meaning that increasingly complex programs can run very quickly. For example, the program
(actually groups of programs) that run NASA’s space shuttle might be comparable in size to Hugo’s Les
Misérables, but its complexity and immediate importance to the “user” far exceeds that of the novel.
As a consequence, program design must be extremely structured, having the ultimate intentions of
performing a specific calculation efficiently with attractive, understandable, efficient programs. Achiev-
ing these general goals means breaking the program into components, writing and testing them separately,
then merging them according to the outline. Toward this end, we stress modular programming. Modules
can be on the scale of chapters or paragraphs, and share many of the same features. They consist of a se-
quence of statements that by themselves express a meaningful computation. They can be merged to form
larger programs by specifying what they do and how they interface to other packages of software. The
analogy in prose is agreeing on the character’s names and what events are to happen in each paragraph
so that events happen to the right people in the right sequence once the whole is formed. Modules can be
re-used in two ways. As with our program from the 1960s, we would “lift” the matrix inversion routine
and put a different user interface around it. We can also re-use a routine within a program several times.
For example, solving the equations of space flight involves the inversion of many matrices. We would
c 2001 J.E. Akin 2
want our program to use the matrix inversion routine over and over, presenting it with a different matrix
each time.
The fundamental components of good program design are
4. Module/program evaluation and testing, during which you refine the program and find errors
The result of following these steps is an efficient, easy-to-use program that has a user’s guide (how does
someone else run your program) and internal documentation so that other programmers can decipher the
algorithm.
Today it is common in a university education to be required to learn at least one foreign language.
Global interactions in business, engineering, and government make such a skill valuable to one’s career.
So it is in programming. One often needs to be able to read two or three programming languages even
if you compose programs in only one language. It is common for different program modules, in different
languages, to be compiled separately and then brought together by a “linker” to form a single executable.
When something goes wrong in such a process it is usually helpful to have a reading knowledge of the
programming languages being used.
When composing to express ideas there are, at least, two different approaches to consider: poetry and
prose. Likewise, in employing programming languages to create software there are distinctly different
approaches available. The two most common ones are “procedural programming” and “object-oriented
programming.” The two approaches are conceptually sketched in Fig. 1.1. They differ in the way that the
software development and maintenance are planned and implemented. Procedures may use objects, and
objects usually use procedures, called methods. Usually the object-oriented code takes more planning
and is significantly larger, but it is generally accepted to be easier to maintain. Today when one can have
literally millions of users active for years or decades, maintenance considerations are very important.
What is the execution environment and what should be in the user interface?
Is the program a stand-alone program, calculating the quadratic formula for example, or do the
results need to be plotted? In the former case, simple user input is probably all that is needed, but
the programmer might want to write the program so that its key components could be used in other
programs. In the latter, the program probably needs to be written so that it meshes well with some
pre-written graphics environment.
c 2001 J.E. Akin 3
AA
Generation n Generation n+1
AAA A
AAA
•••
A AAA
A AA
AAA
A
•••
Figure 1.1: Here, the game is played on an 8 8 square array, and the filled squares indicate the presence
of life. The arrows emanating from one cells radiate to its eight neighbors. The rules are applied to the
nth generation to yield the next. The row of three filled cells became a column of three, for example.
What is going to happen to this configuration the next generation?
What are the required and optional outputs, and what are their formats (printed, magnetic, graph-
ical, audio)?
In many cases, output takes two forms: interactive and archival. Interactive output means that the
programs results must be provided to the user or to other programs. Data format must be defined
so that the user can quickly see or hear the programs results. Archival results need to be stored on
long-term media, such as disk, so that later interpretation of the file’s contents is easy (recall the
notion of being able to read tomorrow what is written today) and that the reading process is easy.
The answers to these questions help programmers organize their thoughts, and can lead to decisions
about programming language and operating environment. At this point in the programming process, the
programmer should know what the program is to do and for whom the program is written. We don’t yet
have a clear notion of how the program will accomplish these tasks; that comes down the road. This
approach to program organization and design is known as top-down design. Here, broad program goals
and context is defined first, with additional detail filled in as needed. This approach contrasts with bottom-
up design, where the detail is decided first, then merged into a functioning whole. For programming, top-
design makes more sense, but you as well as professional programmers are frequently lured into writing
code immediately, usually motivated by the desire to “get something running and figure out later how to
organize it all.” That approach is motivated by expediency, but usually winds up being more inefficient
than a more considered, top-down approach that takes longer to get off the ground, but with increased
likelihood of working more quickly. The result of defining the programming problem is a specification:
how is the program structured, what computations does it perform, and how should it interact with the
user.
An Extended Example: The Game of Life
To illustrate how to organize and write a simple program, let’s structure a program that plays The Game
of Life. Conway’s “Game of Life” was popularized in Martin Gardner’s Mathematical Games column in
the October 1970 and February 1971 issues of Scientific American. This game is an example of what is
known in computer science as cellular automata. An extensive description of the game can be found in
The Recursive Universe by William Poundstone (Oxford University Press, 1987).
The rules of the game are quite simple. Imagine a rectangular array of square cells that are either
empty (no living being present) or filled (a being lives there). As shown in Fig. 1.1, each cell has eight
neighboring cells. At each tick of the clock, a new generation of beings is produced according to how
many neighbors surround a given cell.
If a cell is empty, fill it if three of its neighboring cells are filled; otherwise, leave it empty.
If a cell is filled, it
dies of loneliness if it has zero or one neighbors,
continues to live if it has two or three neighbors,
dies of overcrowding if it has more than three neighbors.
c 2001 J.E. Akin 4
The programming task is to allow the user to “play the game” by letting him or her define initial
configurations, start the program, which applies the rules and displays each generation, and stop the
game at any time the user wants, returning to the initialization stage so that a new configuration can be
tried. To understand the program task, we as programmers need to pose several questions, some of which
might be
What computer(s) are preferred, and what kind of display facilities do they have?
No matter how these questions are answered, we start by forming the program’s basic outline. Here is
one way we might outline the program in a procedural fashion.
1. Allow the user to initialize the rectangular array or quit the program.
Note how the idea of reusing the portion of the program that applies game rules arises naturally. This
idea is peculiar to programming languages, having no counterpart in prose (It’s like being told at the end
of a chapter to reread it!). This kind of looping behavior also occurs when we go back and allow the user
to restart the program.
c 2001 J.E. Akin 5
Program
Main Control
Subprogram #1
Subprogram #2
Figure 1.2: Modular program organization relies on self-contained routines where the passage of data (or
messages) from one to the other is very well defined, and each routine’s (or objects) role in the program
becomes evident.
c 2001 J.E. Akin 6
[1] ! This is a comment line in Fortran 90
[2]
[3] program main ! a program called main
[4] ! begin the main program
[5] print *,"Hello, world" ! * means default format
[6] end program main ! end the main program
[1] // This is a comment line in C++
[2] #include <iostream.h> // standard input output library
[3]
[4] main () // a program called main
[5] // begin the main program
[6] cout << "Hello, world" << endl ; // endl means new line
[7] return 0; // needed by some compilers
[8] // end the main program
[1] % This is a comment line in MATLAB
[2]
[3] function main () % a program called main
[4] % begin the main program
[5] disp (’Hello, world’); % display the string
[6] % end the main program
comments that we allow to include the original outline and to describe computational details;
functions that express each routine, whether it be computational or concerned with the user inter-
face;
Comments. A comment begins with a comment character, which in our pseudocode we take to be the
exclamation point !, and ends when the line ends. Comments can consume an entire line or the right
portion of some line.
! This is a comment: you can read it, but the computer won’t
statements
statement ! From the comment character to end of this line is a comment
statements
The statements cited in the above lines share the status of the sentence that characterizes most written
languages. It is made up of components specific to the syntax of the programming language in use. For
example, most programming books begin with a program that does nothing but print “Hello world” on
the screen (or other output device). The pseudocode for this might have the following form:
! if necessary, include the device library
initiate my program, say main
send the character string ‘‘Hello world’’ to the output device library
terminate my program
Figure 1.3 illustrates this in three common languages, beginning with F90. At this point one can now
say that they are multi-lingual in computer languages. Here, too, we may note that, unlike the other two
languages shown, in Fortran when we begin a specific type of software construct, we almost always ex-
plicitly declare where we are ending its scope. Here the construct pair was program and end program,
but the same style holds true for if and end if pairs, for example. All languages have rules and syntax
to terminate the scope of some construct, but when several types of different constructs occur in the same
program segment, it may be unclear in which order they are terminating.
Functions. To express a program’s organization through its component routines and routines, we use
the notation of mathematical functions. Each program routine accepts inputs, expressed as arguments of
a function, performs its calculations, and returns the computational results as functional values.
output 1 = routine (input 1,...,input m)
or
c 2001 J.E. Akin 7
call routine (input 1,..., input m, output 1,..., output n)
In Fortran, a routine evaluating a single output object, as in the first style, is called a function and, oth-
erwise, it is called a subroutine. Other languages usually use the term function in both cases. Each
routines’s various inputs and results are represented by variables, which, in sharp contrast to mathemat-
ical variables, have text-like names that indicate what they contain. These names contain no spaces, but
may contain several words. There are two conventions for variable names containing two or more words:
either words are joined by the underbar character “ ” (like next generation) or each word begins
with an uppercase letter (like NextGeneration). The results of a routines’s computation are always
indicated by a sequence of variables on the left side of the equals sign =. The use of an equals sign does
not mean mathematical equality; it is a symbol in our pseudocode that means “assign a routines’s results
to the variables (in order) listed on the left.”
Conditionals. To create something other than a sequential execution of routines, conditionals form a
test on the values of one or more variables, and continue execution at one point or another depending
on whether the test was true or false. That is usually done with the if statement. It either performs the
instruction(s) that immediately follow (after the then keyword) if some condition is valid (like x > 0) or
those that follow the else statement if the condition is not true.
if test then
statement group A ! executed if true
else
statement group B ! executed if false
end if
The test here can be very complicated, but is always based on values of variables. Parentheses should be
used to clarify exactly what the test is. For example,
((x > 0) and (y = 2))
One special statement frequently found in if statements is stop: This command means to stop or abort
the program, usually with a fatal error message.
Conditionals allow the program to execute non-sequentially (the only mode allowed by statements).
Furthermore, program execution order can be data-dependent. In this way, how the program be-
haves what output it produces and how it computes the output depends on what data, or messages, it
is given. This means that exact statement execution order is determined by the data, and/or messages, and
the programmer not just the programmer. It is this aspect of programming languages that distinguishes
them from written or spoken languages. An analogy might be that chapters in a novel are read in the
order specified by the reader’s birthday; what that order might be is determined by the novelist through
logical constructs. The tricky part is that in programming languages, each execution order must make
sense and not lead to inconsistencies or, at worst, errors: The novel must make sense in all the ways the
novelist allows. This data- and message-dependent execution order can be applied at all programming
levels, from routine execution to statements. Returning to our analogy to the novel, chapter (routine)
order and sentence (statement) order depend on the reader’s birthday. Such complexity in prose has little
utility, but does in programming. How else can a program be written that informs the user on what day
of the week and under what phase of the moon she was born given the birth date?
Loops. Looping constructs in our formal pseudocode take the form of do loops, where the keyword
do is paired with the key phrase end do to mean that the expressions and routine invocations contained
therein are calculated in order (from top to bottom), then calculated again starting with the first, then
again, then again, . . . , forever. The loop ceases only when we explicitly exit it with the exit command.
The pseudocode loop shown below on the left has the execution history shown on the right.
do
y = routine 1(x) y = routine 1(x)
z = routine 2(y) z = routine 2(y)
x = routine 3(z) x = routine 3(z) [let’s say x=-1]
if x > 0 then y = routine 1(x)
exit z = routine 2(y)
end if x = routine 3(z) [let’s say x=1]
end do [program ends]
c 2001 J.E. Akin 8
Loop Pseudocode
Indexed loop do index=b,i,e
statements
end do
Post-test loop do
statements
if test exit
end do
Infinite loops occur when the Boolean expression always evaluates to true; these are usually not what
the programmer intended and represent one type of program error a “bug.”y The constructs enclosed
by the loop can be anything: statements, logical constructs, and other loops! Because of this variety,
programs can exhibit extremely complex behaviors. How a program behaves depends entirely on the
programmer and how their definition of the program flows based on user-supplied data and messages.
The pseudocode loops are defined in Table 1.1.
1.4.1 Comments
Comments need no further elaboration for pseudocode. However, programmers are encouraged to make
heavy use of comments.
1.4.2 Statements
Calculation is expressed by statements, which share the structure (and the status) of the sentence that
characterizes virtually all written language. Statements that are always executed one after the other as
written. A statement in most languages has a simple, well-defined structure common to them all.
variable = expression
y This term was originated by Grace Hopper, one of the first programmers. In the early days of computers, they were partially
built with mechanical devices known as relays. A relay is a mechanical switch that controls which way electric current flows:
The realization of the logical construct in programming languages. One day, a previously working program stopped being so.
Investigation revealed that an insect had crawled into the computer and had become lodged in a relay’s contacts. She then coined
the term “bug” to refer not only to such hardware failures, but to software ones as well since the user becomes upset no matter
which occurs.
c 2001 J.E. Akin 9
Statements are intended to bear a great resemblance to mathematical equations. This analogy with math-
ematics can appear confusing to the first-time programmer. For example, the statement a = a+1, which
means “increment the variable a by one” makes perfect sense as a programming statement, but no sense
as an algebraic equality since it seems to say that 0 = 1. Once you become more fluent in programming
languages, what is mathematics and what is programming become easily apparent. Statements are said to
be terminated when a certain character is encountered by the interpreter or the compiler. In Fortran, the
termination character is a carriage return or a semicolon (;). In C++, all statements must be terminated
with a semicolon or a comma; carriage returns do not terminate statements. M ATLAB statements may
end with a semicolon ‘;’ to suppress display of the calculated expression’s value. Most statements in
M ATLAB programs end thusly.
Sometimes, statements become quite long, becoming unreadable. Two solutions to improve clarity
can be used: decompose the expression into simpler expressions or use continuation markers to allow
the statement to span more than one line of text. The first solution requires you to use intermediate vari-
ables, which only results in program clutter. Multiline statements can be broken at convenient arithmetic
operators, and this approach is generally preferred. C++ has no continuation character; statements can
span multiple text lines, and end only when the semicolon is encountered. In M ATLAB, the continuation
character sequence comprise three periods ‘...’ placed at the end of each text line (before the carriage
return or comment character). In Fortran, a statement is continued to the next line when an ampersand &
is the last character on the line.
Variables. A variable is a named sequence of memory locations to which values can be assigned. As
such, every variable has an address in memory, which most languages conceal from the programmer so as
to present the programmer with a storage model independent of the architecture of the computer running
the program. Program variables correspond roughly to mathematical variables that can be integer, real,
or, complex-valued. Program variables can be more general than this, being able in some languages
to have values equal to a user-defined data type or object which, in turn, contains sequences of other
variables. Variables in all languages have names: a sequence of alphanumeric characters that cannot
begin with a number. Thus, a, A, a2, and a9b are feasible variable names (i.e., the interpreter or compiler
will not complain about these) while 3d is not. Since programs are meant to be read by humans as well as
interpreters and compilers, such names may not lead to program clarity even if they are carefully defined
and documented. The compiler/interpreter does not care whether humans can read a program easily or
not, but you should: Use variable names that express what the variables represent. For example, use
force as a name rather than f; use i, j, and k for indices rather than ii or i1.
In most languages, variables have type: the kind of quantity stored in them. Frequently occurring
data types are integer and floating point, for example. Integer variables would be chosen if the variable
were only used as an array index; floating point if the variable might have a fractional part.
In addition to having a name, type, and address, each variable has a value of the proper type. The
value should be assigned before the variable is used elsewhere. Compilers should indicate an error if a
variable is used before it has been assigned a value. Some languages allow variables to have aliases which
are usually referred to as “pointers” or “references”. Most higher level languages also allow programmers
to create “user defined” data types.
Assignment Operator. The symbol = in a statement means assignment of the expression into the vari-
able provided on the left. This symbol does not mean algebraic equality; it means that once expression
is computed, its value is stored in the variable. Thus, statements that make programming sense, like
a=a+1, make no mathematical sense because ‘=’ means different things in the two contexts. Fortran
90, and other languages, allow the user to extend the meaning of the assignment symbol (=) to other
operations. Such advanced features are referred to as “operator overloading”.
Expressions. Just as in mathematics, expressions in programming languages can have a complicated
structure. Most encountered in engineering programs amount to a mathematical expression involving
variables, numbers, and functions of variables and/or numbers. For example the following are all valid
statements.
A = B
x = sin(2*z)
force = G*mass1*mass2/(r*r)
c 2001 J.E. Akin 10
Thus, mathematical expressions obey the usual mathematical conventions, but with one added complex-
ity: Vertical position cannot be used help express what the calculation is; program expressions have only
one dimension. For example, the notation ab c clearly expresses to you how to perform the calculation.
However, the one-dimensional equivalent, obtained by smashing this expression onto one line, becomes
ambiguous: does a=bc mean divide a by b then multiply by c, or divide a by the product of b and c?
This ambiguity is relieved in program expressions in two ways. The first, the human-oriented way, de-
mands the use of parentheses grouping constructs to clarify what is being meant, as in (a=b)c . The
language-oriented way makes use of precedence rules: What an expression means is inferred from a set
of rules that specify what operations take effect first. In our example, because division is stronger than
multiplication, a=bc means (a=b)c. Most people find that frequent reliance on precedence rules leads to
programs that take a long time to decipher; the compiler/interpreter is “happy” either way.
Expressions make use of the common arithmetic and relational operators. They may also involve
function evaluations; the sin function was called in the second expression given in the previous example.
Programming expressions can be as complicated as the arithmetic or Boolean-algebra ones they emulate.
1.4.3 Flow Control
If a program consisted of a series of statements, statements would be executed one after the other, in the
order they were written. Such is the structure of all prose, where the equivalent of a statement is the
sentence. Programming languages differ markedly from prose in that statements can be meaningfully
executed over and over, with details of each execution differing each time (the value of some variable
might be changed), or some statements skipped, with statement ordering dependent on which statements
were executed previously or upon external events (the user clicked the mouse). With this extra variability,
programming languages can be more difficult for the human to trace program execution than the effort
it takes to read a novel. In written languages, sentences can be incredibly complex, much more so
than program statements; in programming, the sequencing of statements program flow can be more
complex.
The basic flow control constructs present in virtually all programming languages are loops
repetitive execution of a series of statements and conditionals diversions around statements.
Loops. Historically, the loop has been a major tool in designing the flow control of a procedure and one
would often code a loop segment without giving it a second thought. Today massively parallel computers
are being widely used and one must learn to avoid coding explicit loops in order to take advantage of
the power of such machines. Later we will review which intrinsic tools are included in F90 for use on
parallel (and serial) computers to offer improved efficiency over explicit loops.
The loop allows the programmer to repeat a series of statements, with a parameter the loop vari-
able taking on a different value for each repetition. The loop variable can be an integer or a floating-
point number. Loops can be used to control iterative algorithms, such as the Newton-Raphson algorithm
for finding solutions to nonlinear equations, to accumulate results for a sequential calculation, or to
merely repeat a program phrase, such as awaiting for the next typed input. Loops are controlled by a
logical expression, which when evaluated to true allows the loop another iteration and when false
terminates the loop and commences program execution with the statement immediately following those
statements enclosed within the loop.
There are three basic kinds of looping constructs, the choice of which is determined by the kind
of iterative behavior most appropriate to the computation. The indexed loop occurs most frequently in
programs. Here, one loop variable varies across a range of values. In pseudocode, the index’s value
begins at b, increments each time through the loop by i, and the loop ends when the index exceeds e.
For example:
do j = b, e, i
As an example of an indexed loop, let’s explore summing the series of numbers stored in the array A.
If we knew the number of elements in the array when we write the program, the sum can be calculated
c 2001 J.E. Akin 11
explicitly without using a loop.
sum = A1 + A2 + A3 + A4
However, we have already said that our statements must be on a single line, so we need a way to repre-
sent the subscript attached to each number. We develop the convention that a subscript is placed inside
parentheses like
sum = A(1) + A(2) + A(3) + A(4)
Such programs are very inflexible, and this hard-wired programming style is discouraged. For example,
suppose in another problem the array contains 1,000 elements. With an indexed loop, a more flexible,
easier to read program can be obtained. Here, the index assumes a succession of values, its value tested
against the termination value before the enclosed statements are executed, with the loop terminating once
this test fails to be true. The following generic indexed loop also sums array elements, but in a much
more flexible, concise way.
sum = 0
for i = 1,n
sum = sum + A(i)
end for
Here, the variable n does not need to be known when the program is written; this value can wait until the
program executes, and can be established by the user or after data is read.
In F90 the extensive
PN support for matrix expressions allows implicit loops. For example, consider
the calculation of i=1 xi yi . The language provides at least three ways of performing this calculation.
Assuming the vectors x and y are column vectors,
1. sum xy = 0
N = size(x)
do i = 1,N
sum xy = sum xy + x(i)*y(i)
end do
2. sum xy = sum(x*y)
3. sum xy = dot product(x,y)
The first method is based on the basic loop construct, and yields the slowest running program of the
three versions. In fact, avoiding the do statement by using implicit loops will almost always lead to faster
running programs. The second, and third statements employ intrinsic functions and/or operators designed
for arrays. In many circumstances, calculation efficiency and clarity of expression must be balanced. In
practice, it is usually necessary to set aside memory to hold subscripted arrays, such as x and y above,
before they can be referenced or used.
Conditionals. Conditionals test the veracity of logical expressions, and execute blocks of statements
accordingly (see Table 1.2). The most basic operation occurs when we want to execute a series of state-
ments when a logical expression, say test, evaluates to true. We call that a simple if conditional; the
beginning and end of the statements to be executed when test evaluates to true are enclosed by special
delimiters, which differ according to language. When only one statement is needed, C++ and Fortran
allow that one statement to end the line that begins with the if conditional. When you want one group
of statements to be executed when test is true and another set to be executed when false, you use
the if-else construct. When you want to test a series of logical expressions that are not necessarily com-
plementary, the nested-if construct allows for essentially arbitrarily complex structure to be defined. In
such cases, the logical tests can interlock, thereby creating programs that are quite difficult to read. Here
is where program comments become essential. For example, suppose you want to sum only the positive
numbers less than or equal to 10 in a given sequence. Let’s assume the entire sequence is stored in array
A. In informal pseudocode, we might write
loop across A
if A(i) > 0 and A(i) < = 10 add to sum
end of loop
c 2001 J.E. Akin 12
Conditional Pseudocode
if if (test) statement
if if test then
statements
end if
do i=1,n
if (A(i) > 0) & (A(i) <= 10)
sum = sum + A(i)
end if
end do
Several points are illustrated by this pseudocode example. First of all, the statements that can be included
with a loop can be arbitrary, comprised of simple statements, loops, and conditionals in any order. This
same generality applies to statements within a conditional as well. Secondly, logical expressions can
themselves be quite complicated. Finally, note how each level of statements in the program is indented,
visually indicated the subordination of statements within higher level loops or conditionals. This stylistic
practice lies at the heart of structured programming: explicit indication of each statement within the sur-
rounding hierarchy. In modern programming, the structured approach has become the standard because
it leads to greater clarity of expression, allowing others to understand the program more quickly and the
programmer to find bugs more readily. Employing this style only requires the programmer to use the
space key liberally when typing the program. Since sums are computed so often you might expect that a
language would provide an intrinsic function to compute it. For F90 and M ATLAB you would be correct.
1.4.4 Functions
Functions, which define sub-programs having a well-defined interface to other parts of the program, are
an essential part of programming languages. For, if properly developed, these functions can be included
in future programs, and they allow several programmers to work on complex programs. The function
takes an ordered sequence of messages, objects, or variables as its arguments, and returns to the calling
program a value (or set of values) that can be assigned to an object or variable. Familiar examples of
a function are the mathematical ones: the sin function takes a real-valued argument, uses this value to
calculate the trigonometric sine, and returns that value, which can be assigned to a variable.
y = sin(x)
Note that the argument need not be a variable: a number can be explicitly provided or an expression
can be used. Thus, sin(2.3) and sin(2*cos(x)) are all valid. Functions may require more than one
argument. For example, the atan2 function, which computes the arctangent function in such a way that
the quadrant of the calculated angle is unambiguous, needs the x and y components of the triangle.
z = atan2(x, y)
Note that the order of the arguments the x component must be the first and the number of argu-
ments both x and y are needed matter for all functions: The calling program’s argument ordering
c 2001 J.E. Akin 13
A
AAA
A
L
clip ( x, L)
AAAAAA
–L x
L
–L
Figure 1.4: Input-output relationship for the function clip(x). So long as jxj < L, this function equals
its argument; for larger values, the output equals the clipping constant L no matter how large the input
might be.
and number must agree with those imposed by the function’s definition. Said another way, the inter-
face between the two must agree. Analogous to plugs and electric sockets in the home, a three prong
plug won’t fit into a two-hole socket, and, if you have a two-prong plug, you must plug it in the right
way. A function is usually defined separately, outside the body of any program or other function. We
call a program’s extent its scope. In M ATLAB, a program’s scope is equivalent to what is in a file; in
C and C++, scope is defined by brace pairs; and in Fortran, scope equals what occurs between function
declaration and its corresponding end statement. Variables are also defined within a program’s and a
function’s scope. What this means is that a variable named x defined within a function is available to all
statements occurring within that function, and different functions can use the same variable name without
any conflict occurring. What this means is that two functions f1 and f2 can each make use of a variable
named x, and the value of x depends on which function is being referred to. In technical terms, the scope
of every variable is limited to its defining function. At first, this situation may seem terribly confusing
(“There are two variables both of which are named x?”); further thought brings the realization that this
convention is what you want. Because each function is to be a routine a program having a well-defined
interface, execution of the function’s internal statements must not depend on the program that uses it.
This convention becomes especially important when different people write the programs or functions.
Thus, such local variables those defined locally within a function do not conflict, and they are stored
in different memory locations by the compiler or interpreter.
This limited scope convention can be countermanded when you explicitly declare variables to be
global. Such variables are now potentially available to all functions, and each function cannot define a
variable having the same name. For example, you may well want a variable pointedly named pi to be
available to all functions; you can do so by declaring it to be a global variable. To demonstrate scope,
consider the following simple example. Here, we want to clip the values stored in the array x and store
the results in the array y.
The clipping function has the generic form show in Figure 1.4. Thus, values of the argument that are
less than L in magnitude are not changed, while those exceeding this limit are set equal to the limiting
value. In the program example, note that the name of the array in the calling program x is the same
as the argument’s name used in the definition of the function. Within the scope of a program or function,
an array and a scalar variable cannot have the same name. In our case, because each variable’s scope is
limited to the function or program definition, no conflict occurs: Each is well defined and the meaning
should be unambiguous. Also note that the second argument has a different name in the program that in
the function. No matter how the arguments are defined, we say that they are passed to the function, with
c 2001 J.E. Akin 14
the function’s variables set equal to values specified in the calling program. These interface rules allows
the function to be used in other programs, which means that we can reuse functions whenever we like!
1.4.5 Modules
Another important programming concept is that of packaging a group of related routines and/or selective
variables into a larger programming entity. In the Ada language they are called packages, while C++
and M ATLAB call them classes. F90 has a generalization of this concept that it calls a module. As we
will see later the F90 module includes the functionality of the C++ classes, as well as other uses such as
defining global constants. Therefore, we will find the use of F90 modules critical to our object-oriented
programming goals. In that context modules provide us with the means to take several routines related
to a specific data type and to encapsulate them into a larger programming unit that has the potential to be
reused for more than one application.
1.4.6 Dynamic Memory Management
From the very beginning, several decades ago, there was a clear need to be able to dynamically allocate
and deallocate segments of memory for use by a program. The initial standards for Fortran did not allow
for this. It was necessary to invoke machine language programs to accomplish that task or to write tools
to directly manage arrays by defining “pseudo-pointers” to manually move things around in memory or to
overwrite space that was no longer needed. It was very disappointing that the F77 standard failed to offer
that ability, although several “non-standard” compilers offered such an option. Beginning with the F90
standard a full set of dynamic memory management abilities is now available within Fortran. Dynamic
memory management is mainly needed for arrays and pointers. Both of these will be considered late,
with a whole chapter devoted to arrays. Both of these entities can be declared as ALLOCATABLE and
later one will ALLOCATE and then DEALLOCATE them. There are also new “automatic arrays” that
have the necessary memory space supplied and then freed as needed.
Pointers are often used in “data structures”, abstract data types, and objects. To check on the status
of such features one can invoke the ALLOCATED intrinsic and use ASSOCIATED to check on the
status of pointers and apply NULLIFY to pointers that need to be freed or initialized. Within F90
allocatable arrays cannot be used in the definitions of derived types, abstract data types, or objects.
However, allocatable pointers to arrays can be used in such definitions. To assist in creating efficient
executable codes, entities that might be pointed at by a pointer must have the TARGET attribute.
Numerous examples of dynamic memory management will be seen later. Persons that write compilers
suggest that, in any language, it is wise to deallocate dynamic memory in the reverse order of its creation.
The F90 language standard does not require that procedure but you see that advice followed in most of
the examples.
c 2001 J.E. Akin 15
an interpreter.y It can accept typed commands created as the user thinks of them (plot a graph, see that
a parameter must have been typed incorrectly, change it, and replot, for example) or entire programs.
Because interpreters examine programs locally (line-by-line), program execution tends to be slower than
when a compiler is used.
Compilers are programs that take your program in its entirety and produce an executable version of
it. Compiler output is known as an executable file that, in UNIX for example, can become a command
essentially indistinguishable from others that are available. C++ is an example of a language that is
frequently compiled rather than interpreted. Compilers will produce much more efficient (faster running)
programs than interpreters, but if you find an error, you must edit and re-compile before you can attempt
execution again. Because compilation takes time, this cycle can be time-consuming if the program is
large.
Interpreters are themselves executable files written in compiled languages: MATLAB is written in
C. Executable programs produced by compilers are stand-alone programs: Everything user input and
output, file reading, etc. must be handled by the user’s program. In an interpreter, you can supplement a
program’s execution by typed instructions. For example, in an interpreter you can type a simple command
to make the variable a equal to 1; in a compiled program, you must include a program that asks for the
value of a. Consequently, users frequently write programs in the context of an interpreter, understand
how to make the program better by interacting with it, and then re-express it in a compiled language.
Both interpreters and compilers make extensive use of what are known as library commands or func-
tions. A natural example of a library function is the sin function: Users typically do not want to program
explicitly the computation of the trigonometric sine function. Instead, they want to be able to pull it “off
the shelf” and use as need be. Library modules are just programs written in a computer language like
you would write. Consequently, both interpreters and compilers allow user programs to become part
of the library, which is usually written by many programmers over a long period of time. It is through
modules available in a library that programming teams cooperate. Library modules tend to be more ex-
tensive and do more things in an interpreter. For example, M ATLAB provides a program that produces
pseudo-three-dimensional plots of functions. Such routines usually do not come with a compiler, but
may be purchased separately from graphics programming specialists. For compiled languages, we refer
to linking the library routines to the user’s program (in interpreters, this happens as a matter of course). A
linker is a program that takes modules produced by the compiler, be they yours or others, associates the
modules, and produces the executable file we mentioned earlier. Most C++ compilers “hide” the linking
step from you; you may think you are typing just the command to compile the program, but it is actually
performing that step for you. When you are compiling a module not intended for stand-alone execution,
a compiler option that you type can prevent the compiler from performing the linking step.
Debugging is the process of discovering and removing program errors. Two main types of errors
occur in writing programs: what we would generally term “typos” and what are design errors. The first
kind may be readily found (where is the function sni?) or more subtle (you type aa instead of a for a
variable’s name and aa also exists!). The second kind of error can be hard or subtle to find. The main
components of this process are
1. Search the program module by eye as you do a “mental run through” of its task. This kind of error
searching begins when you first think about program organization, and continues as you refine the
program. Why write a program that is logically flawed?
2. If written in a compiled language, compile the program to find syntax errors or warnings about
unused or undefined variables. If in an interpreted language, attempt preliminary execution to
obtain similar error messages. Linking also can locate modules or libraries that are improperly
referenced.
3. Running the executable file with typical data sets often causes the program to abort a harsh
word that expresses the situation where the program goes crazy and ceases to behave and the
system to supply an error message, such as division by zero. Error messages may help locate the
programming error.
y This statement is only partially true. M ATLAB does have some features of a compiler, like looking ahead to determine if
interface errors exist with respect to functions called by the main program.
c 2001 J.E. Akin 16
Easy errors to find are syntactic errors: You have violated the language’s rules of what a well-formed
program must be. Usually, the interpreter or compiler complain bitterly on encountering a syntax error.
Compilers find these at compile time (when the program is compiled), interpreters at run time. Design
errors are only revealed when we supply the program with data. The programmer should design test data
that exercises each of the program’s valid operations. Invalid user input (asking for the logarithm of a
negative number, for example) should be caught by the program and warning messages sent to the user.
The previous description of generic programming languages indicates why finding bugs can be quite
complicated. Programs can exhibit quite complex behaviors, and tracing incorrect behaviors can be
correspondingly difficult. One often hears the (true) statement “Computers do what we say, not what
we want.” Users frequently want computers to be smart, fixing obvious design (mental) errors because
they obviously conflict with what we want. However, this situation is much like what the novelist faces.
Inexact meaning can confuse the reader; he or she does not have a direct pathway to the novelist’s mind.
As opposed to the novelist, extensive testing of your program can detect such errors and make your
program approach perfection. Many operating systems supply interactive debugger programs that can
trace the execution of a program in complete detail. They can display the values of any variable, stop
at selected positions for evaluation, execute parts of the code in a statement-by-statement fashion, etc.
These can be very helpful in finding difficult-to-locate bugs, but they still cannot read your mind.
Be that as it may, what can the programmer do when the program compiles (no syntactic errors),
doesn’t cause system error messages (no dividing by zero), but the results are not correct? The simplest
approach is to include extra statements in your program, referred to as debugging statements, that display
(somewhat verbosely) values of internal variables. For example, in a loop you would print the value of the
loop index and all variables that the loop might be affecting. Because this output can be voluminous, the
most fruitful approach is to debug smaller problems. With this debugging information, you can usually
figure out the error, correct it, and change the comments accordingly. Without the latter, your program
and your internal documentation are out-of-sync.
Once debugged, you could delete the debugging statements you added. A better approach is to just
hide them. You can do this two ways: Comment them out or encase them in a conditional that is true when
the program is in “debugging mode.” The commenting approach completely removes the debugging
statements from the program execution stream, and allows you to easily put them back if further program
elaborations result in errors. The use of conditionals does put an overhead on computational efficiency,
but usually a small one.
c 2001 J.E. Akin 17
1.7 Object Oriented Formulations
The above discussion of subprograms follows the older programming style where the emphasis is placed
on the procedures that a subprogram is to apply to the supplied data. Thus, it is referred to as procedural
programming. The alternate approach focuses on the data and its supporting functions, and is known as
an object oriented approach and is the main emphasis of this work. It also generalizes the concept of
data types and is usually heavily dependent on user defined data types and their extension to abstract data
types. These concepts are sketched in Fig. 1.5.
b) Object-Oriented Programming
The process or creating an “object-oriented” (OO) formulation involves at least three stages: Object-
Oriented Analysis (OOA), Object-Oriented Design (OOD), and Object-Oriented Programming (OOP).
Many books have been written on each of these three subjects. Formal graphical standards for represent-
ing the results of OOA and OOD have been established and are widely used in the literature. Here the
main emphasis will be placed on OOP on the assumption that the two earlier stages have been completed.
In an effort to give some level of completeness, summaries of OOA and OOD procedures are given in
Tables 1–1 and 1–2, respectively. Having completed OOA and OOD studies one must select a language
to actually implement the design. More than 100 objected-oriented languages are in existence and use
today. They include “pure” OO languages like Crisp, Eiffel, Rexx, Simula, Smalltalk, etc. and “hybrid”
OO languages like C++ , F90 , Object Pascal, etc. In which of them should you invest your time? To get
some insight into answers to this question we should study the advice of some of the recognized leaders
in the field. In his 1988 book on OO software construction B. Myers listed seven steps necessary to
achieve object-orientedness in an implementation language. They are summarized in Table 1-3 and are
all found to exist in F90 and F95 . Thus we proceed with F90 as our language of choice. The basic F90
procedures for OOP will be illustrated in some short examples in Chapter 3 after covering some prelim-
inary material on abstract data types in Chapter 2. Additional OOP applications will also be covered in
later chapters.
c 2001 J.E. Akin 18
Table 1–1. OO Analysis Summary
c 2001 J.E. Akin 19
Table 1–2. OO Design Summary
5. Inheritance :
A class may be defined as an extension or restriction of another (in F90).
6. Polymorphism and dynamic binding :
Entities are permitted to refer to objects of more than one class and operations can have different
realizations in different classes (partially in F90/F95, expected in Fortran 2000).
7. Multiple and repeated inheritance :
Can declare a class as heir to more than one class, and more than once to the same class (in F90).
c 2001 J.E. Akin 20
1.8 Exercises
1 Checking trigonometric identities
We know that the sine and cosine functions obey the trigonometric identity sin2 + cos2 = 1
no matter what value of is used. Write a pseudocode, or M ATLAB , or F90 program that checks
this identity. Let it consist of a loop that increments across N equally spaced angles between 0 and
, and calculates the quantity in question, printing the angle and the result. Test your program for
several values of N . (Later we will write a second version of this program that does not contain
any analysis loops, using instead M ATLAB’s, or F90’s, ability to calculate functions of arrays.)
2 Newton-Raphson algorithm
A commonly used numerical method of solving the equation f (x) = 0 has its origins with the
beginnings of calculus. Newton noted that the slope of a function tended to cross the x-axis near a
function’s position of zero value (called a root).
f(x)
A
A
f(xi )
AA xi+1 xi x
Because the function’s slope at some point xi equals its derivative f (xi ), the equation of the line
0
passing through f (xi ) is f 0 (xi )x + f (xi ) f 0 (xi )xi . Solving for the case when this expression
equals the next trial root xi+1 .
f (xi )
xi+1 = xi
f 0 (xi )
The algorithm proceeds by continually applying this iterative equation until the error is “small.”
The definition of “small” is usually taken to mean that the absolute relative difference between
successive iterates is less than some tolerance value . (Raphson extended these concepts to an
array of functions.)
(a) In pseudocode, write a program that performs the Newton-Raphson algorithm. Assume that
functions that evaluate the function and its derivative are available. What is the most conve-
nient form of loop to use in your program?
(b) Translate your pseudocode into F90, or M ATLAB, and apply your program to the simple
function f (x) = e2x 5x 1. Use the functional expressions directly in your program or
make use of functions.
3 Game of Life pseudocode
Develop a pseudocode outline for the main parts of the “Game of Life” which was discussed earlier
and shown in Fig. 1.3. Include pseudocode for a function to compute the next generation.
c 2001 J.E. Akin 21
Chapter 2
Data Types
Any computer program is going to have to operate on the available data. The valid data types that are
available will vary from one language to another. Here we will examine the intrinsic or built-in data types,
user-defined data types or structures and, finally, introduce the concept of the abstract data type which
is the basic foundation of object-oriented methods. We will also consider the precision associated with
numerical data types. The Fortran data types are listed in Table 2–1. Such data can be used as constants,
variables, pointers and targets.
j j
j
Floating Point Integer
(Default Precision)
j Selected-Int-Kind
j
j j j
Complex Real Double Precision
(Default Precision) (Default Precision) [Obsolete]
Selected-Real-Kind’s Selected-Real-Kind
c 2001 J.E. Akin 23
For numerical computations, numbers are represented as integers or decimal values known as floating
point numbers or floats. The former is called an INTEGER type. The decimal values supported in Fortran
are the REAL and COMPLEX types. The range and precision of these three types depends on the hardware
being employed. At the present, 1999, most computers have 32 bit processors, but some offer 64 bit
processors. This means that the precision of a calculated result from a single program could vary from
one brand of computer to another. One would like to have a portable precision control so as to get
the same answer from different hardware; whereas some languages, like C++, specify three ranges of
precision (with specific bit widths). Fortran provides default precision types as well as two functions to
allow the user to define the “kind” of precision desired.
Significant
Type Bit Width Digits Common Range
integer 16 10 –32,768 to 32,767
real 32 6 1037 to 1037
double precisiony 64 15 10307 to 10307
complex 2@32 2@6 two reals
yobsolete in F90, see selected real kind
Still, it is good programming practice to employ a precision that is of the default, double, or quad pre-
cision level. Table 2–2 lists the default precisions for 32 bit processors. The first three entries correspond
to types int, float, and double, respectively, of C++. Examples of F90 integer constants are
–32 0 4675123 24 short 24 long
In both cases, we note that it is possible to impose a user-defined precision kind by appending an under-
score ( ) followed by the name of the integer variable that gives the precision kind number. For example,
one could define
long = selected int kind(9)
defines a real with 15 significant digits with an exponent range of 307. Likewise, a higher precision
real might be defined by the integer kind
quad = selected real kind(18,4932)
to denote 18 significant digits over the exponent range of 4932. If these kinds of precision are available
on your processors, then the F90 types of “integer (long),” “real (double),” and “real (quad)” would
correspond to the C++ precision types of “long int,” “double,” and “long double,” respectively. If the
processor cannot produce the requested precision, then it returns a negative number as the integer kind
number. Thus, one should always check that the kind (i.e., the above integer values of long, double, or
quad) is not negative, and report an exception if it is negative.
The old F77 intrinsic type of DOUBLE PRECISION has been declared obsolete, since it is now easy
to set any level of precision available on a processor. Another way to always define a double precision
real on any processor is to use the “kind” function such as
double = kind(1.0d0)
where the symbol ‘d’ is used to denote the I/O of a double precision real. For completeness it should be
noted that it is possible on some processors to define different kinds of character types, such as “greek”
or “ascii”, but in that case, the kind value comes before the underscore and the character string such as:
ascii “a string”.
c 2001 J.E. Akin 24
[ 1] Module Math Constants ! Define double precision math constants
[ 2] implicit none
[ 3] ! INTEGER, PARAMETER :: DP = SELECTED REAL KIND (15,307)
[ 4] INTEGER, PARAMETER :: DP = KIND (1.d0) ! Alternate form
[ 5] real(DP), parameter:: Deg Per Rad = 57.295779513082320876798155 DP
[ 6] real(DP), parameter:: Rad Per Deg = 0.017453292519943295769237 DP
[ 7]
[ 8] real(DP), parameter:: e Value = 2.71828182845904523560287 DP
[ 9] real(DP), parameter:: e Recip = 0.3678794411714423215955238 DP
[10] real(DP), parameter:: e Squared = 7.389056098930650227230427 DP
[11] real(DP), parameter:: Log10 of e = 0.4342944819032518276511289 DP
[12]
[13] real(DP), parameter:: Euler = 0.5772156649015328606 DP
[14] real(DP), parameter:: Euler Log = -0.5495393129816448223 DP
[15] real(DP), parameter:: Gamma = 0.577215664901532860606512 DP
[16] real(DP), parameter:: Gamma Log = -0.549539312981644822337662 DP
[17] real(DP), parameter:: Golden Ratio = 1.618033988749894848 DP
[18]
[19] real(DP), parameter:: Ln 2 = 0.6931471805599453094172321 DP
[20] real(DP), parameter:: Ln 10 = 2.3025850929940456840179915 DP
[21] real(DP), parameter:: Log10 of 2 = 0.3010299956639811952137389 DP
[22]
[23] real(DP), parameter:: pi Value = 3.141592653589793238462643 DP
[24] real(DP), parameter:: pi Ln = 1.144729885849400174143427 DP
[25] real(DP), parameter:: pi Log10 = 0.4971498726941338543512683 DP
[26] real(DP), parameter:: pi Over 2 = 1.570796326794896619231322 DP
[27] real(DP), parameter:: pi Over 3 = 1.047197551196597746154214 DP
[28] real(DP), parameter:: pi Over 4 = 0.7853981633974483096156608 DP
[29] real(DP), parameter:: pi Recip = 0.3183098861837906715377675 DP
[30] real(DP), parameter:: pi Squared = 9.869604401089358618834491 DP
[31] real(DP), parameter:: pi Sq Root = 1.772453850905516027298167 DP
[32]
[33] real(DP), parameter:: Sq Root of 2 = 1.4142135623730950488 DP
[34] real(DP), parameter:: Sq Root of 3 = 1.7320508075688772935 DP
[35]
[36] End Module Math Constants
[37]
[38] Program Test
[39] use Math Constants ! Access all constants
[40] real :: pi ! Define local data type
[41] print *, ’pi Value: ’, pi Value ! Display a constant
[42] pi = pi Value; print *, ’pi = ’, pi ! Convert to lower precision
[43] End Program Test ! Running gives:
[44] ! pi Value: 3.1415926535897931 ! pi = 3.14159274
To illustrate the concept of a defined precision intrinsic data type, consider a program segment to
make available useful constants such as pi (3.1415: : :) or Avogadro’s number (6:02 : : : 1023 ). These
are real constants that should not be changed during the use of the program. In F90, an item of that nature
is known as a PARAMETER. In Fig. 2.1, a selected group of such constants have been declared to be of
double precision and stored in a MODULE named Math Constants. The parameters in that module can
be made available to any program one writes by including the statement “ use math constants” at the
beginning of the program segment. The figure actually ends with a short sample program that converts
the tabulated value of pi (line 23) to a default precision real (line 42) and prints both.
c 2001 J.E. Akin 25
[ 7] end type
Having created the new data type, we would need ways to define its values and/or ways to refer to any of
its components. The latter is accomplished by using the component selection symbol “%”. Continuing
the above program segment we could write:
[ 8] type (chemical element) :: argon, carbon, neon ! elements
[ 9] type (chemical element) :: Periodic Table(109) ! an array
[10] real :: mass ! a scalar
[11]
[12] carbon%atomic mass = 12.010 ! set a component value
[13] carbon%atomic number = 6 ! set a component value
[14] carbon%symbol = "C" ! set a component value
[15]
[16] argon = chemical element ("Ar", 18, 26.98) ! construct element
[17]
[18] read *, neon ! get "Ne" 10 20.183
[19]
[20] Periodic Table( 5) = argon ! insert element into array
[21] Periodic Table(17) = carbon ! insert element into array
[22] Periodic Table(55) = neon ! insert element into array
[23]
[24] mass = Periodic Table(5) % atomic mass ! extract component
[25]
[26] print *, mass ! gives 26.9799995
[27] print *, neon ! gives Ne 10 20.1830006
[28] print *, Periodic Table(17) ! gives C 6 12.0100002
[29] end program create a type
read in all the neon components, in order (line 17). [The ‘*’ means that the system is expected
to automatically find the next character, integer and real, respectively, and to insert them into the
components of neon.]
inserted argon, carbon and neon into their specific locations in the periodic table array (lines 19–
21).
extracted the atomic mass of argon from the corresponding element in the periodic element
array (line 23).
print the real variable, mass (line 25). [The ‘*’ means to use a default number of digits.]
printed all components of neon (line 26). [Using a default number of digits.]
printed all the components of carbon by citing its reference in the periodic table array (line 27).
[Note that the printed real value differs from the value assigned in line 12. This is due to the way
reals are represented in a computer, and will be considered elsewhere in the text.]
A defined type can also be used to define other data structures. This is but one small example of the
concept of code re-use. If we were developing a code that involved the history of chemistry, we might
use the above type to create a type called history as shown below.
type (chemical element) :: oxygen
type history ! a second type using the first
character (len=31) :: element name
integer :: year found
type (chemical element) :: chemistry
c 2001 J.E. Akin 26
end type history
type (history) :: Joseph Priestley ! Discoverer
oxygen = chemical element ("O", 76, 190.2) ! construct element
Joseph Priestley = history ("Oxygen", 1774, oxygen) ! construct
print *, Joseph Priestley ! gives Oxygen 1774 O 76 1.9020000E+02
Shortly we will learn about other important aspects of user-defined types, such as how to define operators
that use them as operands.
c 2001 J.E. Akin 27
ADT name
Public attributes
Private attributes
Public members
Private members
Send Send
Member Name
Message Type
Receive Receive
Member Name
Message Type
Receive, Modified
Member Name
Send Type
above discussions of the methods (routines) that are coupled to a data type and describe what you can
and can not do with the data type should give the programmer good insight into what must be done to
plan and implement the functions needed to yield a relatively complete ADT.
chemical_element ADT
character symbol
integer atomic_number
real atomic_mass
chemical_element chemical_element
It is common to have a graphical representation of the ADTs and there are several different graphical
formats suggested in the literature. We will use the form shown in Fig. 2.4 where a rectangular box begins
with the ADT name and is followed by two partitions of that box that represent the lists of attribute data
and associated member routines. Items that are available to the outside world are in sub-boxes that cross
over the right border of the ADT box. They are the parts of the public interface to the ADT. Likewise
those items that are strictly internal, or private, are contained fully within their respective partitions of
the ADT box. There is a common special case where the name of the data type itself is available for
external use, but its individual attribute components are not. In that case the right edge of the private
attributes lists lie on the right edge of the ADT box. In addition, we will often segment the smallest box
for an item to give its type (or the most important type for members) and the name of the item. Public
c 2001 J.E. Akin 28
member boxes are also supplemented with an arrow to indicate which take in information (<--), or send
out information (-->). Such a graphical representation of the previous chemical element ADT, with
all its items public, is shown in Fig. 2.4.
The sequence of numbers known as Fibonacci numbers is the set that begins with one and two and
where the next number in the set is the sum of the two previous numbers (1, 2, 3, 5, 8, 13, ...). A primarily
private ADT to print a list of Fibonacci numbers up to some limit is represented graphically in Fig. 2.5.
2.4 Classes
A class is basically the extension of an ADT by providing additional member routines to serve as con-
structors. Usually those additional members should include a default constructor which has no argu-
ments. Its purpose is to assure that the class is created with acceptable default values assigned to all
its data attributes. If the data attributes involve the storage of large amounts of data (memory) then one
usually also provides a destructor member to free up the associated memory when it is no longer needed.
F95 has an automatic deallocation feature which is not present in F90 and thus we will often formally
deallocate memory associated with data attributes of classes.
As a short example we will consider an extension of the above Fibonacci Number ADT. The ADT
for Fibonacci numbers simply keeps up with three numbers (low, high, and limit). Its intrinsic ini-
tializer has the (default) name Fibonacci. We generalize that ADT to a class by adding a constructor
named new Fibonacci number. The constructor accepts a single number that indicates how many
values in the infinite list we wish to see. It is also a default constructor because if we omit the one
optional argument it will list a minimum number of terms set in the constructor. The graphical repre-
sentation of the Fibonacci Number class extends Fig. 2.4 for its ADT by at least adding one public
constructor, called new Fibonacci number, as shown in Fig. 2.5. Technically, it is generally accepted
that a constructor should only be able to construct a specific object once. This differs from the intrin-
sic initializer which could be invoked multiple times to assign different values to a single user-defined
type. Thus, an additional logical attribute has been added to the previous ADT to allow the constructor,
new Fibonacci number, to verify that it is being invoked only once for each instance of the class. The
coding for this simple class is illustrated in Fig. 2.6. There the access restrictions are given on lines 4, 5,
and 7 while the attributes are declared on line 8 and the member functions are given in lines 13-33. The
validation program is in lines 36–42, with the results shown as comments at the end (lines 44–48).
c 2001 J.E. Akin 29
Figure 2.5: Representation of a Fibonacci Number Class
c 2001 J.E. Akin 30
2.5 Exercises
1. Create a module of global constants of common a) physical constants, b) common units conversion
factors.
2. Teams in a Sports League compete in matches that result in a tie or a winning and loosing team.
When the result is not a tie the status of the teams is updated. The winner is declared better that the looser
and better than any team that was previously bettered by the loser. Specify this process by ADTs for
the League, Team, and Match. Include a logical member function is better than which expresses
whether a team is better than another.
c 2001 J.E. Akin 31
Chapter 3
3.1 Introduction
The use of Object Oriented (OO) design and Object Oriented Programming (OOP) are becoming in-
creasingly popular. Thus, it is useful to have an introductory understanding of OOP and some of the
programming features of OO languages. You can develop OO software in any high level language, like
C or Pascal. However, newer languages such as Ada, C++, and F90 have enhanced features that make
OOP much more natural, practical, and maintainable. C++ appeared before F90 and currently, is prob-
ably the most popular OOP language, yet F90 was clearly designed to have almost all of the abilities of
C++ . However, rather than study the new standards many authors simply refer to the two decades old
F77 standard and declare that Fortran can not be used for OOP. Here we will overcome that misinformed
point of view.
Modern OO languages provide the programmer with three capabilities that improve and simplify
the design of such programs: encapsulation, inheritance, and polymorphism (or generic functionality).
Related topics involve objects, classes, and data hiding. An object combines various classical data types
into a set that defines a new variable type, or structure. A class unifies the new entity types and supporting
data that represents its state with routines (functions and subroutines) that access and/or modify those
data. Every object created from a class, by providing the necessary data, is called an instance of the
class. In older languages like C and F77, the data and functions are separate entities. An OO language
provides a way to couple or encapsulate the data and its functions into a unified entity. This is a more
natural way to model real-world entities which have both data and functionality. The encapsulation is
done with a “module” block in F90, and with a “class” block in C++. This encapsulation also includes
a mechanism whereby some or all of the data and supporting routines can be hidden from the user. The
accessibility of the specifications and routines of a class is usually controlled by optional “public” and
“private” qualifiers. Data hiding allows one the means to protect information in one part of a program
from access, and especially from being changed in other parts of the program. In C++ the default is
that data and functions are “private” unless declared “public,” while F90 makes the opposite choice for
its default protection mode. In a F90 “module” it is the “contains” statement that, among other things,
couples the data, specifications, and operators before it to the functions and subroutines that follow it.
Class hierarchies can be visualized when we realize that we can employ one or more previously
defined classes (of data and functionality) to organize additional classes. Functionality programmed into
the earlier classes may not need to be re-coded to be usable in the later classes. This mechanism is called
inheritance. For example, if we have defined an Employee class, then a Manager class would
inherit all of the data and functionality of an employee. We would then only be required to add only
the totally new data and functions needed for a manager. We may also need a mechanism to re-define
specific Employee class functions that differ for a Manager class. By using the concept of a class
hierarchy, less programming effort is required to create the final enhanced program. In F90 the earlier
class is brought into the later class hierarchy by the “use” statement followed by the name of the “module”
statement block that defined the class.
Polymorphism allows different classes of objects that share some common functionality to be used in
code that requires only that common functionality. In other words, routines having the same generic name
c 2001 J.E. Akin 33
are interpreted differently depending on the class of the objects presented as arguments to the routines.
This is useful in class hierarchies where a small number of meaningful function names can be used to
manipulate different, but related object classes. The above concepts are those essential to object oriented
design and OOP. In the later sections we will demonstrate by example additional F90 implementations
of these concepts.
Circle Class
real radius
real pi
Circle make_Circle
real Circle_Area
Circle Circle
Another terminology used in OOP is that of constructors and destructors for objects. An intrinsic
constructor is a system function that is automatically invoked when an object is declared with all of its
possible components in the defined order (see lines 37 and 43). In C++, and F90 the intrinsic constructor
has the same name as the “type” of the object. One is illustrated in the statement
four sides = Rectangle (2.1,4.3)
which, in turn, was coupled to the class Rectangle which had two components, base and height,
defined in that order, respectively. The intrinsic constructor in the example statement sets component
c 2001 J.E. Akin 34
Figure 3.2: Representation of a Rectangle Class
base = 2.1 and component height = 4.3 for that instance, four sides, of the type Rectangle.
This intrinsic construction is possible because all the expected components of the type were supplied. If
all the components are not supplied, then the object cannot be constructed unless the functionality of the
c 2001 J.E. Akin 35
[ 1] function make Rectangle (bottom, side) result (name)
[ 2] ! Constructor for a Rectangle type
[ 3] implicit none
[ 4] real, optional, intent(in) :: bottom, side
[ 5] type (Rectangle) :: name
[ 6] name = Rectangle (1.,1.) ! default to unit square
[ 7] if ( present(bottom) ) then ! default to square
[ 8] name = Rectangle (bottom, bottom) ; end if
[ 9] if ( present(side) ) name = Rectangle (bottom, side) ! intrinsic
[10] end function make Rectangle
[11] . . .
[12] type ( Rectangle ) :: four sides, square, unit sq
[13] ! Test manual constructors
[14] four sides = make Rectangle (2.1,4.3) ! manual constructor, 1
[15] area = compute area ( four sides) ! generic function
[16] write ( 6,100 ) four sides, area
[17] ! Make a square
[18] square = make Rectangle (2.1) ! manual constructor, 2
[19] area = compute area ( square) ! generic function
[20] write ( 6,100 ) square, area
[21] ! "Default constructor", here a unit square
[22] unit sq = make Rectangle () ! manual constructor, 3
[23] area = compute area (unit sq) ! generic function
[24] write ( 6,100 ) unit sq, area
[25] . . .
[26] ! Running gives:
[27] ! Area of 2.1 by 4.3 rectangle is 9.03
[28] ! Area of 2.1 by 2.1 rectangle is 4.41
[29] ! Area of 1.0 by 1.0 rectangle is 1.00
c 2001 J.E. Akin 36
Date Class
integer month
integer day
integer year
Date Date_
Date Print_Date
Date Read_Date
Date Set_Date
Date Date
The intrinsic constructor, Date (lines 14 and 34), requires all the components be supplied, but it does
no error or consistency checks. My practice is to also define a “public constructor” whose name is the
same as the intrinsic constructor except for an appended underscore, that is, Date . Its sole purpose is to
do data checking and invoke the intrinsic constructor, Date. If the function Date (line 10) is declared
“public” it can be used outside the module class Date to invoke the intrinsic constructor, even if the
components of the data type being constructed are all “private.” In this example we have provided another
manual constructor to set a date, set Date (line 31), with a variable number of optional arguments. Also
supplied are two subroutines to read and print dates, read Date (line 27) and print Date (line 16),
respectively.
A sample main program that employs this class is given in Fig. 3.7, which contains sample outputs
as comments. This program uses the default constructor as well as all three programs in the public class
functionality. Note that the definition of the class was copied in via an “include” (line 1) statement and
activated with the “use” statement (line 4).
Now we will employ the class Date within a class Person which will use it to set the date of
birth (DOB) and date of death (DOD) in addition to the other Person components of name, national-
ity, and sex. As shown in Fig. 3.8, we have made all the type components “private,” but make all the
supporting functionality public, as represented graphically in Fig. 3.8. The functionality shown provides
a manual constructor, make Person, routines to set the DOB or DOD, and those for the printing of
most components. The source code for the new Person class is given in Fig. 3.9. Note that the manual
constructor (line 12) utilizes “optional” arguments and initializes all components in case they are not
supplied to the constructor. The Date public function from the class Date is “inherited” to initial-
ize the DOB and DOD (lines 18, 57, and 62). That function member from the previous module was
activated with the combination of the “include” and “use” statements. Of course, the include could have
been omitted if the compile statement included the path name to that source. A sample main program
for testing the class Person is in Fig. 3.10 along with comments containing its output. It utilizes the
constructors Date (line 7), Person (line10), and make Person (line 24).
Next, we want to use the previous two classes to define a class Student which adds something
else special to the general class Person. The student person will have additional “private” compo-
nents for an identification number, the expected date of matriculation (DOM), the total course credit hours
earned (credits), and the overall grade point average (GPA), as represented in Fig. 3.11. The source lines
for the type definition and selected public functionality are given in Fig. 3.12. There the constructors
are make Student (line 19) and Student (line 47). A testing main program with sample output is
illustrated in Fig. 3.13. Since there are various ways to utilize the various constructors three alternate
methods have been included as comments to indicate some of the programmers options. The first two
include statements (lines 1, 2) are actually redundant because the third include automatically brings
in those first two classes.
c 2001 J.E. Akin 37
[ 1] module class Date ! filename: class Date.f90
[ 2] implicit none
[ 3] public :: Date ! and everything not "private"
[ 4]
[ 5] type Date
[ 6] private
[ 7] integer :: month, day, year ; end type Date
[ 8]
[ 9] contains ! encapsulated functionality
[10]
[11] function Date (m, d, y) result (x) ! public constructor
[12] integer, intent(in) :: m, d, y ! month, day, year
[13] type (Date) :: x ! from intrinsic constructor
[14] if ( m < 1 .or. d < 1 ) stop ’Invalid components, Date ’
[15] x = Date (m, d, y) ; end function Date
[16]
[17] subroutine print Date (x) ! check and pretty print a date
[18] type (Date), intent(in) :: x
[19] character (len=*),parameter :: month Name(12) = &
[20] (/ "January ", "February ", "March ", "April ",&
[21] "May ", "June ", "July ", "August ",&
[22] "September", "October ", "November ", "December "/)
[23] if ( x%month < 1 .or. x%month > 12 ) print *, "Invalid month"
[24] if ( x%day < 1 .or. x%day > 31 ) print *, "Invalid day "
[25] print *, trim(month Name(x%month)),’ ’, x%day, ", ", x%year;
[26] end subroutine print Date
[27]
[28] subroutine read Date (x) ! read month, day, and year
[29] type (Date), intent(out) :: x ! into intrinsic constructor
[30] read *, x ; end subroutine read Date
[31]
[32] function set Date (m, d, y) result (x) ! manual constructor
[33] integer, optional, intent(in) :: m, d, y ! month, day, year
[34] type (Date) :: x
[35] x = Date (1,1,1997) ! default, (or use current date)
[36] if ( present(m) ) x%month = m ; if ( present(d) ) x%day = d
[37] if ( present(y) ) x%year = y ; end function set Date
[38]
[39] end module class Date
c 2001 J.E. Akin 38
Person Class
character name
character nationality
integer sex
Date Date_Of_Birth
Date Date_Of_Death
Person Person_
Person make_Person
Person print_DOB
Person print_DOD
Person print_Name
Person print_Nationality
Person print_Sex
Person set_DOB
Person set_DOD
Person Person
the important topic of operator overloading. Additional numerical applications of OOP will be illustrated
in later chapters.
3.3.1 A Rational Number Class and Operator Overloading
To illustrate an OOP approach to simple numerical operations we will introduce a fairly complete rational
number class, called class Rational which is represented graphically in Fig. 3.14. The defining F90
module is given in Fig. 3.15. The type components have been made private (line 5), but not the type
itself, so we can illustrate the intrinsic constructor (lines 38 and 102), but extra functionality has been
provided to allow users to get either of the two components (lines 52 and 57). The provided routines
shown in that figure are:
add Rational convert copy Rational delete Rational
equal integer gcd get Denominator get Numerator
invert is equal to list make Rational
mult Rational Rational reduce
Procedures with only one return argument are usually implemented as functions instead of subroutines.
Note that we would form a new rational number, z , as the product of two other rational numbers, x
and y , by invoking the mult Rational function (line 90),
z = mult Rational (x, y)
which returns z as its result. A natural tendency at this point would be to simply write this as z =
x y. However, before we could do that we would have to have to tell the operator, “*”, how to act
when provided with this new data type. This is known as overloading an intrinsic operator. We had the
foresight to do this when we set up the module by declaring which of the “module procedures” were
equivalent to this operator symbol. Thus, from the “interface operator (*)” statement block (line 14)
the system now knows that the left and right operands of the “*” symbol correspond to the first and
second arguments in the function mult Rational. Here it is not necessary to overload the assignment
operator, “=”, when both of its operands are of the same intrinsic or defined type. However, to convert
c 2001 J.E. Akin 39
[ 1] module class Person ! filename: class Person.f90
[ 2] use class Date
[ 3] implicit none
[ 4] public :: Person
[ 5] type Person
[ 6] private
[ 7] character (len=20) :: name
[ 8] character (len=20) :: nationality
[ 9] integer :: sex
[10] type (Date) :: dob, dod ! birth, death
[11] end type Person
[12] contains
[13] function make Person (nam, nation, s, b, d) result (who)
[14] ! Optional Constructor for a Person type
[15] character (len=*), optional, intent(in) :: nam, nation
[16] integer, optional, intent(in) :: s ! sex
[17] type (Date), optional, intent(in) :: b, d ! birth, death
[18] type (Person) :: who
[19] who = Person (" ","USA",1,Date (1,1,0),Date (1,1,0)) ! defaults
[20] if ( present(nam) ) who % name = nam
[21] if ( present(nation) ) who % nationality = nation
[22] if ( present(s) ) who % sex = s
[23] if ( present(b) ) who % dob = b
[24] if ( present(d) ) who % dod = d ; end function
[25]
[26] function Person (nam, nation, s, b, d) result (who)
[27] ! Public Constructor for a Person type
[28] character (len=*), intent(in) :: nam, nation
[29] integer, intent(in) :: s ! sex
[30] type (Date), intent(in) :: b, d ! birth, death
[31] type (Person) :: who
[32] who = Person (nam, nation, s, b, d) ; end function Person
[33]
[34] subroutine print DOB (who)
[35] type (Person), intent(in) :: who
[36] call print Date (who % dob) ; end subroutine print DOB
[37]
[38] subroutine print DOD (who)
[39] type (Person), intent(in) :: who
[40] call print Date (who % dod) ; end subroutine print DOD
[41]
[42] subroutine print Name (who)
[43] type (Person), intent(in) :: who
[44] print *, who % name ; end subroutine print Name
[45]
[46] subroutine print Nationality (who)
[47] type (Person), intent(in) :: who
[48] print *, who % nationality ; end subroutine print Nationality
[49]
[50] subroutine print Sex (who)
[51] type (Person), intent(in) :: who
[52] if ( who % sex == 1 ) then ; print *, "male"
[53] else ; print *, "female" ; end if ; end subroutine print Sex
[54]
[55] subroutine set DOB (who, m, d, y)
[56] type (Person), intent(inout) :: who
[57] integer, intent(in) :: m, d, y ! month, day, year
[58] who % dob = Date (m, d, y) ; end subroutine set DOB
[59]
[60] subroutine set DOD(who, m, d, y)
[61] type (Person), intent(inout) :: who
[62] integer, intent(in) :: m, d, y ! month, day, year
[63] who % dod = Date (m, d, y) ; end subroutine set DOD
[64] end module class Person
an integer to a rational we could, and have, defined an overloaded assignment operator procedure (line
10). Here we have provided the procedure, equal Integer, which is automatically invoked when
we write : type(Rational)y; y = 4. That would be simpler than invoking the constructor called
make rational. Before moving on note that the system does not yet know how to multiply an integer
times a rational number, or visa versa. To do that one would have to add more functionality, such as a
function, say int mult rn, and add it to the “module procedure” list associated with the “*” operator.
A typical main program which exercises most of the rational number functionality is given in Fig. 3.16,
along with typical numerical output. It tests the constructors Rational (line 8), make Rational
c 2001 J.E. Akin 40
[ 1] include ’class Date.f90’
[ 2] include ’class Person.f90’ ! see previous figure
[ 3] program main
[ 4] use class Date ; use class Person ! inherit class members
[ 5] implicit none
[ 6] type (Person) :: author, creator
[ 7] type (Date) :: b, d ! birth, death
[ 8] b = Date (4,13,1743) ; d = Date (7, 4,1826) ! OPTIONAL
[ 9] ! Method 1
[10] ! author = Person ("Thomas Jefferson", "USA", 1, b, d) ! NOT if private
[11] author = Person ("Thomas Jefferson", "USA", 1, b, d) ! constructor
[12] print *, "The author of the Declaration of Independence was ";
[13] call print Name (author);
[14] print *, ". He was born on "; call print DOB (author);
[15] print *, " and died on "; call print DOD (author); print *, ".";
[16] ! Method 2
[17] author = make Person ("Thomas Jefferson", "USA") ! alternate
[18] call set DOB (author, 4, 13, 1743) ! add DOB
[19] call set DOD (author, 7, 4, 1826) ! add DOD
[20] print *, "The author of the Declaration of Independence was ";
[21] call print Name (author)
[22] print *, ". He was born on "; call print DOB (author);
[23] print *, " and died on "; call print DOD (author); print *, ".";
[24] ! Another Person
[25] creator = make Person ("John Backus", "USA") ! alternate
[26] print *, "The creator of Fortran was "; call print Name (creator);
[27] print *, " who was born in "; call print Nationality (creator);
[28] print *, ".";
[29] end program main ! Running gives:
[30] ! The author of the Declaration of Independence was Thomas Jefferson.
[31] ! He was born on April 13, 1743 and died on July 4, 1826.
[32] ! The author of the Declaration of Independence was Thomas Jefferson.
[33] ! He was born on April 13, 1743 and died on July 4, 1826.
[34] ! The creator of Fortran was John Backus who was born in the USA.
Student Class
Person who
character id [SSN]
Date matriculation
integer credits
real gpa
Student Student_
Student make_Student
Student get_Person
Student print_DOM
Student print_GPA
Student set_DOM
Student Student
(lines 14, 18, 25), and a simple destructor delete Rational (line 38). The intrinsic constructor (line
6) could have been used only if all the attributes were public, and that is considered an undesirable
practice in OOP. The simple destructor actually just sets the “deleted” number to have a set of default
components. Later we will see that constructors and destructors often must dynamically allocate and
deallocate, respectively, memory associated with a specific instance of some object.
c 2001 J.E. Akin 41
[ 1] module class Student ! filename class Student.f90
[ 2] use class Person ! inherits class Date
[ 3] implicit none
[ 4] public :: Student, set DOM, print DOM
[ 5] type Student
[ 6] private
[ 7] type (Person) :: who ! name and sex
[ 8] character (len=9) :: id ! ssn digits
[ 9] type (Date) :: dom ! matriculation
[10] integer :: credits
[11] real :: gpa ! grade point average
[12] end type Student
[13] contains ! coupled functionality
[14]
[15] function get person (s) result (p)
[16] type (Student), intent(in) :: s
[17] type (Person) :: p ! name and sex
[18] p = s % who ; end function get person
[19]
[20] function make Student (w, n, d, c, g) result (x) ! constructor
[21] ! Optional Constructor for a Student type
[22] type (Person), intent(in) :: w ! who
[23] character (len=*), optional, intent(in) :: n ! ssn
[24] type (Date), optional, intent(in) :: d ! matriculation
[25] integer, optional, intent(in) :: c ! credits
[26] real, optional, intent(in) :: g ! grade point ave
[27] type (Student) :: x ! new student
[28] x = Student (w, " ", Date (1,1,1), 0, 0.) ! defaults
[29] if ( present(n) ) x % id = n ! optional values
[30] if ( present(d) ) x % dom = d
[31] if ( present(c) ) x % credits = c
[32] if ( present(g) ) x % gpa = g ; end function make Student
[33]
[34] subroutine print DOM (who)
[35] type (Student), intent(in) :: who
[36] call print Date(who%dom) ; end subroutine print DOM
[37]
[38] subroutine print GPA (x)
[39] type (Student), intent(in) :: x
[40] print *, "My name is "; call print Name (x % who)
[41] print *, ", and my G.P.A. is ", x % gpa, "." ; end subroutine
[42]
[43] subroutine set DOM (who, m, d, y)
[44] type (Student), intent(inout) :: who
[45] integer, intent(in) :: m, d, y
[46] who % dom = Date ( m, d, y) ; end subroutine set DOM
[47]
[48] function Student (w, n, d, c, g) result (x)
[49] ! Public Constructor for a Student type
[50] type (Person), intent(in) :: w ! who
[51] character (len=*), intent(in) :: n ! ssn
[52] type (Date), intent(in) :: d ! matriculation
[53] integer, intent(in) :: c ! credits
[54] real, intent(in) :: g ! grade point ave
[55] type (Student) :: x ! new student
[56] x = Student (w, n, d, c, g) ; end function Student
[57] end module class Student
When considering which operators to overload for a newly defined object one should consider those
that are used in sorting operations, such as the greater-than, >, and less-than, <, operators. They are
often useful because of the need to sort various types of objects. If those symbols have been correctly
overloaded then a generic object sorting routine might be used, or require few changes.
3.4 Discussion
The previous sections have only briefly touched on some important OOP concepts. More details will be
covered later after a general overview of the features of the Fortran language. There are more than one
hundred OOP languages. Persons involved in software development need to be aware that F90 can meet
almost all of their needs for a OOP language. At the same time it includes the F77 standard as a subset
and thus allows efficient use of the many millions of Fortran functions and subroutines developed in the
past. The newer F95 standard is designed to make efficient use of super computers and massively parallel
c 2001 J.E. Akin 42
[ 1] include ’class Date.f90’
[ 2] include ’class Person.f90’
[ 3] include ’class Student.f90’ ! see previous figure
[ 4] program main ! create or correct a student
[ 5] use class Student ! inherits class Person, class Date also
[ 6] implicit none
[ 7] type (Person) :: p ; type (Student) :: x
[ 8] ! Method 1
[ 9] p = make Person ("Ann Jones","",0) ! optional person constructor
[10] call set DOB (p, 5, 13, 1977) ! add birth to person data
[11] x = Student (p, "219360061", Date (8,29,1955), 9, 3.1) ! public
[12] call print Name (p) ! list name
[13] print *, "Born :"; call print DOB (p) ! list dob
[14] print *, "Sex :"; call print Sex (p) ! list sex
[15] print *, "Matriculated:"; call print DOM (x) ! list dom
[16] call print GPA (x) ! list gpa
[17] ! Method 2
[18] x = make Student (p, "219360061") ! optional student constructor
[19] call set DOM (x, 8, 29, 1995) ! correct matriculation
[20] call print Name (p) ! list name
[21] print *, "was born on :"; call print DOB (p) ! list dob
[22] print *, "Matriculated:"; call print DOM (x) ! list dom
[23] ! Method 3
[24] x = make Student (make Person("Ann Jones"), "219360061") ! optional
[25] p = get Person (x) ! get defaulted person data
[26] call set DOM (x, 8, 29, 1995) ! add matriculation
[27] call set DOB (p, 5, 13, 1977) ! add birth
[28] call print Name (p) ! list name
[29] print *, "Matriculated:"; call print DOM (x) ! list dom
[30] print *, "was born on :"; call print DOB (p) ! list dob
[31] end program main ! Running gives:
[32] ! Ann Jones
[33] ! Born : May 13, 1977
[34] ! Sex : female
[35] ! Matriculated: August 29, 1955
[36] ! My name is Ann Jones, and my G.P.A. is 3.0999999.
[37] ! Ann Jones was born on: May 13, 1977 , Matriculated: August 29, 1995
[38] ! Ann Jones Matriculated: August 29, 1995 , was born on: May 13, 1977
machines. It includes most of the High Performance Fortran features that are in wide use. Thus, efficient
use of OOP on parallel machines is available through F90 and F95.
None of the OOP languages have all the features one might desire. For example, the useful concept
of a “template” which is standard in C++ is not in the F90 standard. Yet the author has found that a
few dozen lines of F90 code will define a preprocessor that allows templates to be defined in F90 and
expanded in line at compile time. The real challenge in OOP is the actual OOA and OOD that must be
completed before programming can begin, regardless of the language employed. For example, several
authors have described widely different approaches for defining classes to be used in constructing OO
finite element systems. Additional example applications of OOP in F90 will be given in the following
chapters.
c 2001 J.E. Akin 43
Rational Class
integer numerator
integer denominator
Rational Rational_
Rational make_Rational
Rational add_Rational
Rational convert
Rational copy_Rational
Rational delete_Rational
Rational equal_Rational
Rational get_Denominator
Rational get_Numerator
Rational invert
Rational is_equal_to
Rational list
Rational mult_Rational
Rational Rational
integer gcd
Rational reduce
c 2001 J.E. Akin 44
[ 1] module class Rational ! filename: class Rational.f90
[ 2] implicit none
[ 3] ! public, everything but following private routines
[ 4] private :: gcd, reduce
[ 5] type Rational
[ 6] private ! numerator and denominator
[ 7] integer :: num, den ; end type Rational
[ 8]
[ 9] ! overloaded operators interfaces
[ 10] interface assignment (=)
[ 11] module procedure equal Integer ; end interface
[ 12] interface operator (+) ! add unary versions & (-) later
[ 13] module procedure add Rational ; end interface
[ 14] interface operator (*) ! add integer mult Rational, etc
[ 15] module procedure mult Rational ; end interface
[ 16] interface operator (==)
[ 17] module procedure is equal to ; end interface
[ 18] contains ! inherited operational functionality
[ 19] function add Rational (a, b) result (c) ! to overload +
[ 20] type (Rational), intent(in) :: a, b ! left + right
[ 21] type (Rational) :: c
[ 22] c % num = a % num*b % den + a % den*b % num
[ 23] c % den = a % den*b % den
[ 24] call reduce (c) ; end function add Rational
[ 25]
[ 26] function convert (name) result (value) ! rational to real
[ 27] type (Rational), intent(in) :: name
[ 28] real :: value ! decimal form
[ 29] value = float(name % num)/name % den ; end function convert
[ 30]
[ 31] function copy Rational (name) result (new)
[ 32] type (Rational), intent(in) :: name
[ 33] type (Rational) :: new
[ 34] new % num = name % num
[ 35] new % den = name % den ; end function copy Rational
[ 36]
[ 37] subroutine delete Rational (name) ! deallocate allocated items
[ 38] type (Rational), intent(inout) :: name ! simply zero it here
[ 39] name = Rational (0, 1) ; end subroutine delete Rational
[ 40]
[ 41] subroutine equal Integer (new, I) ! overload =, with integer
[ 42] type (Rational), intent(out) :: new ! left side of operator
[ 43] integer, intent(in) :: I ! right side of operator
[ 44] new % num = I ; new % den = 1 ; end subroutine equal Integer
[ 45]
[ 46] recursive function gcd (j, k) result (g) ! Greatest Common Divisor
[ 47] integer, intent(in) :: j, k ! numerator, denominator
[ 48] integer :: g
[ 49] if ( k == 0 ) then ; g = j
[ 50] else ; g = gcd ( k, modulo(j,k) ) ! recursive call
[ 51] end if ; end function gcd
[ 52]
[ 53] function get Denominator (name) result (n) ! an access function
[ 54] type (Rational), intent(in) :: name
[ 55] integer :: n ! denominator
[ 56] n = name % den ; end function get Denominator
c 2001 J.E. Akin 45
[ 57] function get Numerator (name) result (n) ! an access function
[ 58] type (Rational), intent(in) :: name
[ 59] integer :: n ! numerator
[ 60] n = name % num ; end function get Numerator
[ 61]
[ 62] subroutine invert (name) ! rational to rational inversion
[ 63] type (Rational), intent(inout) :: name
[ 64] integer :: temp
[ 65] temp = name % num
[ 66] name % num = name % den
[ 67] name % den = temp ; end subroutine invert
[ 68]
[ 69] function is equal to (a given, b given) result (t f)
[ 70] type (Rational), intent(in) :: a given, b given ! left == right
[ 71] type (Rational) :: a, b ! reduced copies
[ 72] logical :: t f
[ 73] a = copy Rational (a given) ; b = copy Rational (b given)
[ 74] call reduce(a) ; call reduce(b) ! reduced to lowest terms
[ 75] t f = (a%num == b%num) .and. (a%den == b%den) ; end function
[ 76]
[ 77] subroutine list(name) ! as a pretty print fraction
[ 78] type (Rational), intent(in) :: name
[ 79] print *, name % num, "/", name % den ; end subroutine list
[ 80]
[ 81] function make Rational (numerator, denominator) result (name)
[ 82] ! Optional Constructor for a rational type
[ 83] integer, optional, intent(in) :: numerator, denominator
[ 84] type (Rational) :: name
[ 85] name = Rational(0, 1) ! set defaults
[ 86] if ( present(numerator) ) name % num = numerator
[ 87] if ( present(denominator)) name % den = denominator
[ 88] if ( name % den == 0 ) name % den = 1 ! now simplify
[ 89] call reduce (name) ; end function make Rational
[ 90]
[ 91] function mult Rational (a, b) result (c) ! to overload *
[ 92] type (Rational), intent(in) :: a, b
[ 93] type (Rational) :: c
[ 94] c % num = a % num * b % num
[ 95] c % den = a % den * b % den
[ 96] call reduce (c) ; end function mult Rational
[ 97]
[ 98] function Rational (numerator, denominator) result (name)
[ 99] ! Public Constructor for a rational type
[100] integer, optional, intent(in) :: numerator, denominator
[101] type (Rational) :: name
[102] if ( denominator == 0 ) then ; name = Rational (numerator, 1)
[103] else ; name = Rational (numerator, denominator) ; end if
[104] end function Rational
[105]
[106] subroutine reduce (name) ! to simplest rational form
[107] type (Rational), intent(inout) :: name
[108] integer :: g ! greatest common divisor
[109] g = gcd (name % num, name % den)
[110] name % num = name % num/g
[111] name % den = name % den/g ; end subroutine reduce
[112] end module class Rational
c 2001 J.E. Akin 46
[ 1] include ’class Rational.f90’
[ 2] program main
[ 3] use class Rational
[ 4] implicit none
[ 5] type (Rational) :: x, y, z
[ 6] ! ------- only if Rational is NOT private ----------
[ 7] ! x = Rational(22,7) ! intrinsic constructor if public components
[ 8]
[ 9] x = Rational (22,7) ! public constructor if private components
[10] write (*,’("public x = ")’,advance=’no’); call list(x)
[11] write (*,’("converted x = ", g9.4)’) convert(x)
[12] call invert(x)
[13] write (*,’("inverted 1/x = ")’,advance=’no’); call list(x)
[14]
[15] x = make Rational () ! default constructor
[16] write (*,’("made null x = ")’,advance=’no’); call list(x)
[17] y = 4 ! rational = integer overload
[18] write (*,’("integer y = ")’,advance=’no’); call list(y)
[19] z = make Rational (22,7) ! manual constructor
[20] write (*,’("made full z = ")’,advance=’no’); call list(z)
[21] ! Test Accessors
[22] write (*,’("top of z = ", g4.0)’) get numerator(z)
[23] write (*,’("bottom of z = ", g4.0)’) get denominator(z)
[24] ! Misc. Function Tests
[25] write (*,’("making x = 100/360, ")’,advance=’no’)
[26] x = make Rational (100,360)
[27] write (*,’("reduced x = ")’,advance=’no’); call list(x)
[28] write (*,’("copying x to y gives ")’,advance=’no’)
[29] y = copy Rational (x)
[30] write (*,’("a new y = ")’,advance=’no’); call list(y)
[31] ! Test Overloaded Operators
[32] write (*,’("z * x gives ")’,advance=’no’); call list(z*x) ! times
[33] write (*,’("z + x gives ")’,advance=’no’); call list(z+x) ! add
[34] y = z ! overloaded assignment
[35] write (*,’("y = z gives y as ")’,advance=’no’); call list(y)
[36] write (*,’("logic y == x gives ")’,advance=’no’); print *, y==x
[37] write (*,’("logic y == z gives ")’,advance=’no’); print *, y==z
[38] ! Destruct
[39] call delete Rational (y) ! actually only null it here
[40] write (*,’("deleting y gives y = ")’,advance=’no’); call list(y)
[41] end program main ! Running gives:
[42] ! public x = 22 / 7 ! converted x = 3.143
[43] ! inverted 1/x = 7 / 22 ! made null x = 0 / 1
[44] ! integer y = 4 / 1 ! made full z = 22 / 7
[45] ! top of z = 22 ! bottom of z = 7
[46] ! making x = 100/360, reduced x = 5 / 18
[47] ! copying x to y gives a new y = 5 / 18
[48] ! z * x gives 55 / 63 ! z + x gives 431 / 126
[49] ! y = z gives y as 22 / 7 ! logic y == x gives F
[50] ! logic y == z gives T ! deleting y gives y = 0 / 1
c 2001 J.E. Akin 47
3.5 Exercises
1. Use the class Circle to create a class Sphere that computes the volume of a sphere. Have
a method that accepts an argument of a Circle. Use the radius of the Circle via a new member
get Circle radius to be added to the class Circle.
2. Use the class Circle and class Rectangle to create a class Cylinder that computes
the volume of a right circular cylinder. Have a method that accepts arguments of a Circle and a height,
and a second method that accepts arguments of a Rectangle and a radius. In the latter member
use the height of the Rectangle via a new member get Rectangle height to be added to the
class Rectangle.
3. Create a vector class to treat vectors with an arbitrary number of real coefficients. Assume that the
class Vector is defined as follows:
Vector Class
integer size
real, pointer data (:)
Vector assign
Vector make_Vector
Vector add_Real_to_Vector
Vector add_Vector
Vector copy_Vector
Vector delete_Vector
real dot_Vector
Vector equal_Real
logical is_equal_to
real length
Vector list
Vector normalize_Vector
Vector read_Vector
Vector real_mult_Vector
integer size_Vector
Vector subtract_Real
Vector subtract_Vector
real values
Vector Vector_
real Vector_max_value
real Vector_min_value
Vector Vector_mult_real
Vector Vector
Overload the common operators of (+) with add Vector and add Real to Vector, (–) with
subtract Vector and subtract Real, (*) with dot Vector, real mult Vector and Vec-
tor mult real, (=) with equal Real to set all coefficients to a single real number, and (==) with
routine is equal to.
Include two constructors assign and make Vector. Let assign convert a real array into an instance
of a Vector. Provide a destructor, means to read and write a Vector, normalize a Vector, and determine its
extreme values.
c 2001 J.E. Akin 48
4. Modify the above Vector class to extend it to a Sparse Vector Class where the vast majority
of the coefficients are zero. Store and operate only on the non-zero entries.
Sparse_Vector Class
integer non_zeros
integer, pointer rows (:)
real, pointer values (:)
Sparse_Vector make_Sparse_Vector
Sparse_Vector add_Real_to_Sparse_Vector
Sparse_Vector add_Sparse_Vector
Sparse_Vector delete_Sparse_Vector
real dot_Vector
Sparse_Vector el_by_el_Mult
Sparse_Vector equal_Vector
real get_element
logical is_equal_to
integer largest_index
real normalize_Vector
length
real norm
Sparse_Vector normalize_Vector
Sparse_Vector pretty
Sparse_Vector read_Vector
Sparse_Vector real_mult_Sparse
integer rows_of
Sparse_Vector set_element
Sparse_Vector show
Sparse_Vector show_r_v
integer size_of
Sparse_Vector Sparse_mult_real
Sparse_vector sub_Sparse_Vector
Sparse_Vector sum_Sparse_Vector
real Vector_max_value
real Vector_min_value
Sparse_Vector Vector_to_Sparse
Sparse_Vector zero_Sparse
Sparse_Vector Sparse_Vector
c 2001 J.E. Akin 49
Chapter 4
The preceding chapter described the programming process as starting with a clearly specified task, ex-
pressing it mathematically as a set of algorithms, translating the algorithms in pseudocode, and finally,
translating the pseudocode into a “real” programming language. The final stages of this prescription work
because most (if not all) computational languages have remarkable similarities: They have statements,
the sequencing of which are controlled by various loop and conditional constructs, and functions that
foster program modularization. We indicated how similar MATLAB, C++, and Fortran are at this level,
but these languages differ the more they are detailed. It is the purpose of this chapter to describe those
details, and bring you from a superficial acquaintance with a computational language to fluency. Today,
the practicing engineer needs more than one programming language or environment. Once achieving
familiarity with one, you will find that learning other languages is easy.
When selecting a programming tool for engineering calculations, one is often faced with two different
levels of need. One level is where you need to quickly solve a small problem once, such as a homework
assignment, and computational efficiency is not important. You may not care if your code takes ten
seconds or one hundred seconds to execute; you want convenience. At that level it may make sense
to use an engineering environment like M ATLAB, or Mathematica. At the other extreme you may be
involved in doing a wide area weather prediction where a one-day run time, instead of a ten-day run time,
defines a useful versus a non-useful product. You might be developing a hospital laboratory system for
reporting test results to an emergency room physician where an answer in ten seconds versus an answer in
ten minutes can literally mean the difference between life or death for a patient. For programming at this
level one wants an efficient language. Since such projects can involve programming teams in different
countries, you want your language to be based on an international standard. Then you would choose to
program a language such as C++ or F90. Since most students have experienced only the first need level,
they tend to overvalue the first approach and devalue the second. This chapter will illustrate that the skills
needed for either approach are similar.
The structure of this chapter follows our usual progression to learning a language: What are variables,
how can variables be combined into expressions, what constructs are available to control program flow,
and how are functions defined so that we can employ modularity. The basics are described in Chapter 1;
we assume you are familiar with the language basics described there. Initially, this chapter will parallel
the program composition section of Chapter 1 as applied in the C++, F90, and M ATLAB languages, and
then it will bring in more advanced topics.
The features of F90 that are to be discussed here have been combined in a series of tables and placed
in Appendix B. It is expected that we will want to refer to those tables as we read this section as well
as later when we program. At times, references to C++ and M ATLAB have been given to show the
similarities between most languages and to provide an aid for when having to interface in reading codes
in those languages.
4.1 Comments
In M ATLAB and Fortran, a single character ‘%’ in M ATLAB, ‘!’ in F90 located anywhere in a line of
text means that the remainder of the text on that line comprises the comment. In C, an entirely different
c 2001 J.E. Akin 51
Language Syntax Location
M ATLAB % comment (to end of line) anywhere
C++ // comment (to end of line) anywhere
F90 ! comment (to end of line) anywhere
F77 * comment (to end of line) column 1
structure for comments occurs. Comments begin with the two-character sequence ‘/*’ and end with the
next occurrence of the two-character sequence ‘*/’. In C, comments can occur anywhere in a program;
they can consume a portion of a line, temporarily interrupting a statement, or they can span multiple
lines of text. C++ allows the use of the C comment syntax, but has added a more popular two-character
sequence ‘//’ to proceed a comment to the end of a line. Table 4.1 gives a summary of these comments
syntax. It is also in the “Fortran 90 Overview” for quick reference. Samples of comment statements are
shown in Fig. 1.3, which gives the corresponding versions of the classic “hello world” program included
in most introductory programming texts.
The built-in, or intrinsic, data types allowed for variables are summarized in Table 4.2. Additional user
defined types will be considered later. The expressions usually involves the use of arithmetic operators
and/or relational operators which are given in Tables 4.3 and 4.4, respectively. The order in which the
language applies these operators is called their precedence, and they are shown in Table 4.5. They are
also in the “Fortran 90 Overview” for quick reference.
In moving from M ATLAB to high level languages one finds that it is necessary to define the type of
each variable. Fortran has a default naming convention for its variables and it allows an easy overriding
of that built in “implicit” convention. Since most engineering and mathematical publications used the
letters from “i” through “n” as subscripts, summation ranges, loop counters, etc. Fortran first was released
with implicit variable typing such that all variables whose name begin with the letters “i” through “n”,
inclusive, defaulted to integers, unless declared otherwise. All other variables default to be real, unless
declared otherwise. In other words, you can think of the default code as if it contained the statements:
The effect is automatic even if the statements are omitted. Explicit type declarations override any given
IMPLICIT types. For example, if the code had the above implicit defaults one could also explicitly
identify the exceptions to those default rules, such as the statements:
c 2001 J.E. Akin 52
Storage M ATLABa C++ F90 F77
byte char character:: character
integer int integer:: integer
single precision float real:: real
double precision double real*8:: double precision
b
complex complex:: complex
Boolean bool logical:: logical
argument parameter:: parameter
pointer * pointer::
structure struct type::
a M ATLAB 4 requires no variable type declaration; the only two distinct types in M ATLAB are strings and reals (which include
complex). Booleans are just 0s and 1s treated as reals. M ATLAB5 allows the user to select more types.
b There is no specific data type for a complex variable in C++; they must be created by the programmer.
is desired. Otherwise, M ATLAB assumes matrix operations; figure out the difference between ‘*’ and ‘.*’. Note that since matrix
and scalar addition coincide, no ‘.+’ operator exists (same holds for subtraction).
b Fortran 90 allows the user to change operators and to define new operator symbols.
c In all languages the minus sign is used for negation (i.e., changing sign).
d In C++ the exponentiation is calculated by function pow (x; y ).
We will also see that the programmer can define new data types and explicitly declare their type as well.
The F90 standard discourages the use of any IMPLICIT variables such as
which forces the programmer to specifically declare the type of each and every variable used, and is
referred to as strong typing. However, you need to know that such default variable types exist because
they are used in many millions of lines of older Fortran code and at some point you will need to use or
change such an existing program.
c 2001 J.E. Akin 53
Description M ATLAB C++ F90 F77
Equal to == == == .EQ.
Not equal to ˜= != /= .NE.
Less than < < < .LT.
Less or equal <= <= <= .LE.
Greater than > > > .GT.
Greater or equal >= >= >= .GE.
Logical NOT ˜ ! .NOT. .NOT.
Logical AND & && .AND. .AND.
Logical inclusive OR ! || .OR. .OR.
Logical exclusive OR xor .XOR. .XOR.
Logical equivalent == == .EQV. .EQV.
Logical not equivalent ˜= != .NEQV. .NEQV.
M ATLAB
C++ Operators F90 Operatorsa F77 Operators
Operators
() () [] -> . () ()
+ - ! ++ -- + ** **
- * & (type)
sizeof
* / * / % * / * /
+ -b + -b + -b + -b
< <= > >= << >> // //
== ˜= < <= > => == /= < <= > .EQ. .NE.
>= .LT. .LE.
.GT. .GE.
˜ == != .NOT. .NOT.
& && .AND. .AND.
| || .OR. .OR.
= | .EQV. .NEQV. .EQV. .NEQV.
?:
= += -= *= /=
%= &= ˆ= |=
<<= >>=
,
a User-defined unary (binary) operators have the highest (lowest) precedence in F90.
b These are binary operators representing addition and subtraction. Unary operators + and - have higher precedence.
c 2001 J.E. Akin 54
[ 1] program main
[ 2] ! Examples of simple arithmetic in F90
[ 3] implicit none
[ 4] integer :: Integer Var 1, Integer Var 2 ! user inputs
[ 5] integer :: Mult Result, Div Result, Add Result
[ 6] integer :: Sub Result, Mod Result
[ 7] real :: Pow Result, Sqrt Result
[ 8]
[ 9] print *, ’Enter two integers:’
[10] read *, Integer Var 1, Integer Var 2
[11]
[12] Add Result = Integer Var 1 + Integer Var 2
[13] print *, Integer Var 1,’ + ’, Integer Var 2,’ = ’, Add
[14]
[15] Sub Result = Integer Var 1 - Integer Var 2
[16] print *, Integer Var 1,’ - ’, Integer Var 2,’ = ’, Sub
[17]
[18] Mult Result = Integer Var 1 * Integer Var 2
[19] print *, Integer Var 1,’ * ’, Integer Var 2,’ = ’, Mult
[20]
[21] Div Result = Integer Var 1 / Integer Var 2
[22] print *, Integer Var 1,’ / ’, Integer Var 2,’ = ’, Div
[23]
[24] Mod Result = mod (Integer Var 1, Integer Var 2) ! remai
[25] print *, Integer Var 1,’ mod ’, Integer Var 2,’ = ’, Mod
[26]
[27] Pow Result = Integer Var 1 ** Integer Var 2 ! raise t
[28] print *, Integer Var 1,’ ˆ ’, Integer Var 2,’ = ’, Pow
[29]
[30] Sqrt Result = sqrt( real(Integer Var 1))
[31] print *,’Square root of ’, Integer Var 1,’ = ’, Sqrt Result
[32]
[33] end program main ! Running produces:
[34] ! Enter two integers:
[35] ! 25 + 4 = 29
[36] ! 25 - 4 = 21
[37] ! 25 * 4 = 100
[38] ! 25 / 4 = 6, note integer
[39] ! 25 mod 4 = 1
[40] ! 25 ˆ 4 = 3.9062500E+05
[41] ! Square root of 25 = 5.0000000
An example program that employs the typical math operators in F90 is shown in Fig. 4.1. It presents
examples of addition (line 11), subtraction (line 14), multiplication (line 17), division (line 20), as well as
the use of the remainder or modulo function (line 23), exponentiation (line 26), and square root operators
(line 29). In addition it shows a way of inputing data from the default input device (line 9). The results
are appended as comments (lines 33-40). Observe that a program must include one and only one segment
that begins with the word program (line 1) and ends with the line end program (line 32). If a name
is assigned to the program then it must be appended to both of these lines. Often the name of main is
used, as here, but it is not required as it is in C++ . A C++ formulation of this example is included for
comparison in the appendix as are several other examples from this chapter.
A special expression available in M ATLAB and F90 uses the colon operator (:) to indicate forming
a vector (row matrix) of numbers according to an arithmetic progression. In MATLAB, the expression
b:i:e means the vector [b (b + i) (b + 2i) (b + N i)], where (b + N i) is the largest number less than
or equal to (greater than or equal to if i is negative) the value of the variable e. Thus, b means “beginning
value”, i means the increment, and e the end value. The expression b:e means that the increment equals
one. You can use this construct to excise a portion of a vector or matrix. For example, x(2:5) equals
the vector comprised by the second through fifth elements of x, and A(3:5,i:j) creates a matrix from
the third, fourth, and fifth rows, ith through j th columns of the matrix A. F90 uses the convention of
b:e:i and has the same defaults when :i is omitted. This operator, also known as the subscript triplet,
is described in Table 4.6.
Of course, expressions often involve the use of functions. A tabulation of the built-in functions in
our languages is given in Table 4.7 and the F90 overview, as are all the remaining tables of this chapter.
The arguments of functions and subprograms have some important properties that vary with the language
used. Primarily, we are interested in how actual arguments are passed to the dummy arguments in the
subprogram. This data passing happens by either of two fundamentally different ways: by reference, or
c 2001 J.E. Akin 55
B = Beginning, E = Ending, I = Increment
by value. One should understand the difference between these two mechanisms.
“Passing by reference” means that the address in memory of the actual argument is passed to the
subprogram instead of the value stored at that address. The corresponding dummy argument in the
subprogram has the same address. That is, both arguments refer to the same memory location so any
change to that argument within the subprogram is passed back to the calling code. A variable is passed
by reference to a subroutine whenever it is expected that it should be changed by the subprogram. A
related term is “dereferencing”. When you dereference a memory address, you are telling the computer
to get the information located at the address. Typically, one indirectly gives the address by citing the
c 2001 J.E. Akin 56
Description C++ F90 F77 M ATLAB
Conditionally execute statements if if if if
f g end if end if end
Loop a specific number of times for k=1:n do k=1,n do # k=1,n for k=1:n
f g end do # continue end
c 2001 J.E. Akin 57
4.3.1 Explicit Loops
The following discussion will introduce the important concept of loops. These are required in most
programs. However, the reader is warned that today the writing of explicit loops are generally not the
most efficient way to execute a loop operation in Fortran90 and M ATLAB. Of course, older languages
like F77 and C do require them, so that the time spent here not only covers the explicit loop concepts but
aids one in reading older languages. Our pseudocode for the common loops is :
c 2001 J.E. Akin 58
Loop M ATLAB C++ Fortran
Indexed loop
f
for index=matrix for (init;test;inc) do index=b,e,i
statements statements
statements
g
end end do
Post-test loop do f do
statements statements
g while (test) if (test) exit
end do
Loop Pseudocode
Indexed loop for index=b,i,e
statements
end for
Post-test loop do
statements
if test exit
end do
In engineering programming one often needs to repeatedly perform a group of operations. Most
computer languages have a statement to execute this powerful and widely-used feature. In Fortran this
is the DO statement, while in C++ and M ATLAB it is the FOR statement. This one statement provides
for the initialization, incrementing and testing of the loop variable, plus repeated execution of a group of
statements contained within the loop. In Fortran77, the loop always cites a label number that indicates
the extent of the statements enclosed in the loop. This is allowed in F90, but not recommended, and is
considered obsolete. Instead, the END DO indicates the extent of the loop, and the number label is omitted
in both places. F90 does allow one to give a name to a loop. Then the structure is denoted as NAME:DO
followed by END DO NAME. Examples of the syntax for these statements for the languages of interest are
given in Table 4.9.
A simple example of combining loops and array indexing is illustrated in Figs. 4.2 and 4.3. Note in
Fig. 4.2 that the final value of a loop counter (called Integer Var here) upon exiting the loop (line 10)
can be language or compiler dependent despite the fact that they are same here. In Fig. 4.3, we introduce
for the first time a variable with a single subscript (line 5) and containing five numbers (integers) to be
manually initialized (lines 8-10) and then to be listed in a loop (lines 12-15) over all their values. Note
that C++ stores the first entry in an array at position zero (see appendix listing), M ATLAB uses position
one, and F90 defaults to position one.
C++ and Fortran 90 allow a special option to create loops that run “forever.” These could be used, for
example, to read an unknown amount of data until terminated, in a non-fatal way, by the input statement.
In C++, one omits the three loop controls, such as
f
for (;;) // forever loop
loop block
g // end forever loop
while in F90, one simply omits the loop control and gives only the DO command:
do ! forever
c 2001 J.E. Akin 59
[ 1] program main
[ 2] ! Examples of a simple loop in F90
[ 3] implicit none
[ 4] integer Integer Var
[ 5]
[ 6] do Integer Var = 0,4,1
[ 7] print *, ’The loop variable is:’, Integer Var
[ 8] end do ! over Integer Var
[ 9]
[10] print *, ’The final loop variable is:’, Integer Var
[11]
[12] end program main ! Running produces:
[13] ! The loop variable is: 0
[14] ! The loop variable is: 1
[15] ! The loop variable is: 2
[16] ! The loop variable is: 3
[17] ! The loop variable is: 4
[18] ! The final loop variable is: 5 <- NOTE
loop block
end do ! forever
Most of the time, an infinite loop is used as a loop while true or a loop until true construct. These
will be considered shortly.
4.3.2 Implied Loops
Fortran and M ATLAB have shorthand methods for constructing “implied loops.” Both languages offer
the colon operator to imply an incremental range of integer values. Its syntax and types of applications
are given in Table 4.6 (page 56). The allowed usages of the operator differ slightly between the two
languages. Note that this means that the loop controls are slightly different in that the do control employs
commas instead of colons. For example, two equivalent loops are
Fortran M ATLAB
do k=B,E,I for k=B:I:E
A(k) = k**2 A(k) = k 2 ^
end do
end
Fortran offers an additional formal implied do loop that replaces the do and end do with a closed
pair of parentheses in the syntax:
(object, k = B,E,I)
c 2001 J.E. Akin 60
where again the increment, I, defaults to unity if not supplied. The above implied do is equivalent to the
formal loop
do k=B,E,I
define object
end do
However, the object defined in the implied loop can only be utilized for four specific Fortran operations:
1) read actions, 2) print and write actions, 3) data variables (not value) definitions, and 4) defining
array elements. For example,
print *, (4*k-1, k=1,10,3) ! 3, 15, 27, 39
read *, (A(j,:), j=1,rows) ! read A by rows, sequentially
The implied do loops can be nested to any level like the standard do statement. One simply makes the
inner loop the object of the outer loop, so that
((object j k, j=min, max), k=k1,k2,inc)
For example,
print *, (((A(k)*B(j)+3), j=1,5), k=1,max)
! read array by rows in each plane
read *, (((A(i,j,k), j=1,cols), i=1,rows), k=1,max)
Actually, there is even a simpler default form of implied dos for reading and writing arrays. That default
is to access arrays by columns. That is, process the leftmost subscript first. Thus, for an array with three
subscripts,
read *, A () read *, (((A(i,j,k), i=1,rows), j=1,cols), k=1,planes)
Both languages allow the implied loops to be employed to create an array vector simply by placing
the implied loop inside the standard array delimit symbols. For example, we may want an array to equally
distribute N + 1 points over the distance from zero to D.
F90: X = (/(k,k=0,N)/)* D/(N+1)
M ATLAB: X = [0:N] * D / (N+1),
which illustrates that M ATLAB allows the use of the colon operator to define arrays, but F90 does not.
In addition to locating elements in an array by the regular incrementing of loop variables, both
Fortran90 and M ATLAB support even more specific selections of elements: by random location via vector
subscripts, or by value via logical masks such as where and if in F90 and M ATLAB, respectively.
4.3.3 Conditionals
Logic tests are frequently needed to control the execution of a block of statements. The most basic
operation occurs when we want to do something when a logic test gives a true answer. We call that a
simple IF statement. When the test is true, the program executes the block of statements following the
IF. Often only one statement is needed, so C++ and Fortran allow that one statement to end the line that
begins with the IF logic. Frequently we will nest another IF within the statements from a higher level
IF. The common language syntax forms for the simple IF are given below in Table 4.10, along with the
examples of where a second true group is nested inside the first as shown in Table 4.11.
The next simplest case is where we need to do one thing when the answer is true, and a different
thing when the logic test is false. Then the syntax changes simply to an IF ftrue groupg ELSE
ffalse groupg mode of execution. The typical IF-ELSE syntaxes of the various languages are given
in Table 4.12. Of course, the above statement groups can contain other IF or IF-ELSE statements nested
within them. They can also contain any valid statements, including DO or FOR loops.
The most complicated logic tests occur when the number of cases for the answer go beyond the two
(true-false) of the IF-ELSE control structure. These multiple case decisions can be handled with the IF-
ELSEIF-ELSE control structures whose syntax is given in Table 4.13. They involve a sequence of logic
c 2001 J.E. Akin 61
M ATLAB Fortran C++
f
if l expression IF (l expression) THEN if (l expression)
true group true group
end END IF true group;
g
IF (l expression) true statement if (l expression)
true statement;
Table 4.10: IF Constructs. The quantity l expression means a logical expression having a value that
is either TRUE of FALSE. The term true statement or true group means that the statement or group
of statements, respectively, are executed if the conditional in the if statement evaluates to TRUE.
f
if l expression IF (l expression) THEN if (l expression)
true group A true group A
else ELSE true group A
false group B false group B g
f
end END IF else
false group B
g
tests, each of which is followed by a group of statements that are to be executed if, and only if, the test
answer is true. There can be any number of such tests. They are terminated with an ELSE group of default
statements to be executed if none of the logic tests are true. Actually, the ELSE action is optional. For
program clarity or debugging, it should be included even if it only prints a warning message or contains
a comment statement. Typical “if” and “if-else” coding is given in Figs. 4.4, 4.5, and 4.6. Figure 4.4
simply uses the three logical comparisons of “greater than” (line 9), “less than” (line 12), or “equal to”
(line 15), respectively. Figure 4.5 goes a step further by combining two tests with a logical “and” test
(line 9), and includes a second else branch (line 11) to handle the case where the if is false. While the
input to these programs were numbers (line 7), the third example program in Fig. 4.6 accepts logical
input (lines 6,8) that represents either true or false values and carries out Boolean operations to negate
an input (via NOT in line 9), or to compare two inputs (with an AND in line 11, or OR in line 17, etc.) to
produce a third logical value.
Since following the logic of many IF-ELSEIF-ELSE statements can be very confusing both the C++
and Fortran languages allow a CASE selection or “switching” operation based on the value (numerical or
character) of some expression. For any allowed specified CASE value, a group of statements is executed.
If the value does not match any of the specified allowed CASE values, then a default group of statements
are executed. These are illustrated in Table 4.14.
c 2001 J.E. Akin 62
M ATLAB Fortran C++
f
if l expression1 IF (l expression1) THEN if (l expression1)
true group A true group A
elseif l expression2 ELSE IF (l expression2) THEN true group A
true group B true group B g
f
elseif l expression3 ELSE IF (l expression3) THEN else if (l expression2)
true group C true group C
else ELSE true group B
default group D default group D g
f
end END IF else if (l expression3)
true group C
g
f
else
default group D
g
[ 1] program main
[ 2] ! Examples of relational "if" operator in F90
[ 3] implicit none
[ 4] integer :: Integer Var 1, Integer Var 2 ! user inputs
[ 5]
[ 6] print *, ’Enter two integers:’
[ 7] read *, Integer Var 1, Integer Var 2
[ 8]
[ 9] if ( Integer Var 1 > Integer Var 2 ) &
[10] print *, Integer Var 1,’ is greater than ’, Integer Var 2
[11]
[12] if ( Integer Var 1 < Integer Var 2 ) &
[13] print *, Integer Var 1,’ is less than ’, Integer Var 2
[14]
[15] if ( Integer Var 1 == Integer Var 2 ) &
[16] print *, Integer Var 1,’ is equal to ’, Integer Var 2
[17]
[18] end program main
[19]
[20] ! Running with 25 and 4 produces:
[21] ! Enter two integers:
[22] ! 25 is greater than 4
[ 1] program main
[ 2] ! Illustrate a simple if-else logic in F90
[ 3] implicit none
[ 4] integer Integer Var
[ 5]
[ 6] print *,’Enter an integer: ’
[ 7] read *, Integer Var
[ 8]
[ 9] if ( Integer Var > 5 .and. Integer Var < 10 ) then
[10] print *, Integer Var, ’ is greater than 5 and less than 10’
[11] else
[12] print *, Integer Var, ’ is not greater than 5 and less than 10’
[13] end if ! range of input
[14]
[15] end program main
[16] !
[17] ! Running with 3 gives: 3 is not greater than 5 and less than 10
[18] ! Running with 8 gives: 8 is greater than 5 and less than 10
Fortran90 offers an additional optional feature called construct names that can be employed with the
above IF and SELECT CASE constructs to improve the readability of the program. The optional name,
followed by a colon, precedes the key words IF and SELECT CASE. To be consistent, the name should
also follow the key words END IF or END SELECT which always close the constructs. The construct
name option also is available for loops where it offers an additional pair of control actions that will be
explained later. Examples of these optional F90 features are given in Table 4.15.
While C++ and M ATLAB do not formally offer this option, the same enhancement of readability can
c 2001 J.E. Akin 63
[ 1] program main
[ 2] ! Examples of Logical operators in F90
[ 3] implicit none
[ 4] logical :: Logic Var 1, Logic Var 2
[ 5] print *,’Print logical value of A (T or F):’
[ 6] read *, Logic Var 1
[ 7] print *,’Print logical value of B (T or F):’
[ 8] read *, Logic Var 2
[ 9] print *,’NOT A is ’, (.NOT. Logic Var 1)
[10]
[11] if ( Logic Var 1 .AND. Logic Var 2 ) then
[12] print *, ’A ANDed with B is true’
[13] else
[14] print *, ’A ANDed with B is false’
[15] end if ! for AND
[16]
[17] if ( Logic Var 1 .OR. Logic Var 2 ) then
[18] print *, ’A ORed with B is true’
[19] else
[20] print *, ’A ORed with B is false’
[21] end if ! for OR
[22]
[23] if ( Logic Var 1 .EQV. Logic Var 2 ) then
[24] print *, ’A EQiValent with B is true’
[25] else
[26] print *, ’A EQiValent with B is false’
[27] end if ! for EQV
[28]
[29] if ( Logic Var 1 .NEQV. Logic Var 2 ) then
[30] print *, ’A Not EQiValent with B is true’
[31] else
[32] print *, ’A Not EQiValent with B is false’
[33] end if ! for NEQV
[34]
[35] end program main
[36] ! Running with T and F produces:
[37] ! Print logical value of A (T or F): T
[38] ! Print logical value of B (T or F): F
[39] ! NOT A is F
[40] ! A ANDed with B is false
[41] ! A ORed with B is true
[42] ! A EQiValent with B is false
[43] ! A Not EQiValent with B is true
F90 C++
SELECT CASE (expression) switch (expression)
CASE (value 1) f
group 1 case value 1 :
CASE (value 2) group 1
group 2 break;
. case value 2 :
.
. group 2
CASE (value n) break;
group n .
.
.
CASE DEFAULT
default group case value n :
END SELECT group n
break;
default:
default group
break;
g
be achieved by using the trailing comment feature to append a name or description at the beginning and
end of these logic construct blocks.
Both C++ and Fortran allow statement labels and provide controls to branch to specific labels. Today
you are generally advised not to use a GO TO and its associated label! However, they are common in
many F77 codes. There are a few cases where a GO TO is still considered acceptable. For example, the
pseudo-WHILE construct of F77 requires a GO TO.
c 2001 J.E. Akin 64
F90 Named IF F90Named SELECT
name: IF (logical 1) THEN name: SELECT CASE (expression)
true group A CASE (value 1)
ELSE IF (logical 2) THEN group 1
true group B CASE (value 2)
ELSE group 2
default group C CASE DEFAULT
ENDIF name default group
END SELECT name
Fortran C++
DO 1 ... for (...) f
DO 2 ...
...
for (...) f
...
IF (disaster) THEN if (disaster)
GO TO 3 go to error
END IF ...
... g
2 END DO
1 END DO
g
error:
3 next statement
Table 4.16: GO TO Break-out of Nested Loops. This situation can be an exception to the general recom-
mendation to avoid GO TO statements.
initialize test
IF (l expression) THEN
true statement group
modify logical value
GO TO #
END IF
The GO TO can also be effectively utilized in both Fortran and C++ to break out of several nested loops.
This is illustrated in Table 4.16. The “break-out” construct can be used in the situation when, as a part of
a subroutine, you wanted the program exit the loop and also exit the subroutine, returning control to the
calling program. To do that, one would simply replace the GO TO statement with the RETURN statement.
In F90, one should also append the comment “! to calling program” to assist in making the subroutine
more readable.
You may find it necessary to want to skip a cycle in loop execution and/or exit from a single loop.
Both Fortran and C++ provide these control options without requiring the use of a GO TO. To skip a loop
cycle, Fortran90 and C++ use the statements CYCLE and continue, respectively, and EXIT and break
to abort a loop. These constructs are shown in Tables 4.17 and 4.18. Other forms of the GO TO in F77
were declared obsolete in F90, and should not be used. The Fortran abort examples could also use the
RETURN option described above in the rare cases when it proves to be more desirable or efficient.
As mentioned earlier, F90 allows the programmer to use “named” DO constructs. In addition to im-
c 2001 J.E. Akin 65
F77 F90 C++
DO 1 I = 1,N
f
DO I = 1,N for (i=1; i<n; i++)
IF (exit condition) THEN IF (exit condition) THEN
GO TO 2 EXIT ! this do if (exit condition)
ELSE ELSE break;// out of loop
false group false group else if
END IF END IF false group
g
1 CONTINUE END DO end
2 next statement next statement
next statement
main: DO ! forever
test: DO k=1,k max
third: DO m=m max,m min,-1
IF (test condition) THEN
CYCLE test ! loop on k
END IF
END DO third ! loop on m
fourth: DO n=n min,n max,2
IF (main condition) THEN
EXIT main ! forever loop
END DO fourth ! on n
END DO test ! over k
END DO main
next statement
proving readability, this feature also offers additional control over nested loops because we can associate
the CYCLE and EXIT commands with a specific loop (Table 4.19). Without the optional name, the CYCLE
and EXIT commands act only on the inner-most loop in which they lie. We will see later that Fortran90
allows another type of loop called WHERE that is designed to operate on arrays.
It is very common to need to perform a loop so long as a condition is true, or to run the loop until
a condition becomes true. The two are very similar and both represent loops that would run forever
unless specifically terminated. We will refer to these two approaches as WHILE loops and UNTIL loops.
The WHILE logic test is made first in order to determine if the loop will be entered. Clearly, this means that
if the logic test is false the first time it is tested, then the statement blocks controlled by the WHILE are
never executed. If the WHILE loop is entered, something in the loop must eventually change the value of
a variable in the logic test or the loop would run forever. Once a change causes the WHILE logic test to be
false control is transferred to the first statement following the WHILE structure. By way of comparison,
an UNTIL loop is always entered at least once. Upon entering the loop, a beginning statement group is
executed. Then the logic test is evaluated. If the test result is true, the loop is exited and control is
passed to the next statement after the group. If the test is false, then an optional second statement group
is executed before the loop returns to the beginning statement group. The pseudo-code for these two
similar structures are given as follows :
c 2001 J.E. Akin 66
Since these constructs are commonly needed, several programming languages offer some support for
them. For example, Pascal has a REPEAT UNTIL command and C++ has the DO-WHILE pair for the until-
true construct. For the more common while-true loops, C++ and M ATLAB offer a WHILE command, and
Fortran 90 includes the DO WHILE. F77, however, only has the obsolete IF-GO TO pairs as illustrated in
a previous example. Many current programmers consider the WHILE construct obsolete because it is less
clear than a DO-EXIT pair or a “for-break” pair. Indeed, the F90 standard has declared the DO WHILE
as obsolete and eligible for future deletion from the language. We can see how the loop-abort feature of
C++ and F90 includes both the WHILE and UNTIL concepts. For example, the F90 construct
initialize logical variable
DO WHILE (logical variable) ! is true
true group
re-evaluate logical variable
END DO ! while true
.
.
.
If one omits all three for expressions, then it becomes an “infinite loop” or a “do forever” which can
represent a WHILE or UNTIL construct by proper placement of the break command. Furthermore, C has
the do-while construct that is equivalent to Pascal’s REPEAT-UNTIL.
do // forever until true
statements
evaluate logical variable
while (logical variable) // is true
The syntax for the classical WHILE statements in C++, Fortran and M ATLAB are given in Table 4.20.
Fortran90 has declared the DO WHILE as obsolete, and recommends the DO-EXIT pair instead! Using
infinite loops with clearly aborted stages is a less error-prone approach to programming.
c 2001 J.E. Akin 67
M ATLAB C++
initialize test initialize test
f
while l expression while (l expression)
true group
change test true group
change test
g
end
F77 F90
initialize test initialize test
# continue do while (l expression)
IF (l expression) THEN true group
true group change test
change test end do
go to #
END IF
Function
M ATLABa C++ Fortran
Type
program statements
f
main(argc,char **argv) program main
[y1...yn]=f(a1,...,am) type y
[end of file] statements type a1,...,type am
statements
g
y = f(a1,I,am);
y = f(a1,...,am)
call s(a1,...,am)
end program
Table 4.21: Function definitions. In each case, the function being defined is named f and is called with
m arguments a1,...,am.
4.4 Subprograms
The concept of modular programming requires the use of numerous subprograms or procedures to execute
independent segments of the calculations or operations. Typically, these procedures fall into classes such
as functions, subroutines, and modules. We will consider examples of the procedures for each of our
target languages. These are shown in Table 4.21.
Recall that Table 8.6 compared several intrinsic functions that are common to both F90 and M ATLAB.
For completeness, all of the Fortran90 functions are listed both alphabetically and by subject in Ap-
pendix B. Similar listings for M ATLAB can be found in the M ATLAB Primer.
4.4.1 Functions and Subroutines
Historically, a function was a subprogram that employed one or more input arguments and returned a
single result value. For example, a square root or logarithm function would accept a single input value
and return a single result. All of the languages of interest allow the user to define such a function, and they
c 2001 J.E. Akin 68
One-Input, One-Result Procedures
M ATLAB function out = name (in)
F90 function name (in) ! name = out
function name (in) result (out)
C++ name (in, out)
all provide numerous intrinsic or built-in functions of this type. As you might expect, such a procedure
is called a function in C++, Fortran and M ATLAB. As an example of such a procedure, consider the
calculation of the mean value of a sequence of numbers defined as
1 X
n
mean = xk :
n
k=1
In Fortran90, a subprogram to return the mean (average) could be
function mean(x)
! mean = sum of vector x, divided by its size
real :: mean, x(:)
mean = sum(x)/size(x)
end function mean
Note that our function has employed two other intrinsic functions: size to determine the number of
elements in the array x, and sum to carry out the summation of all elements in x. Originally in Fortran,
the result value was required to be assigned to the name of the function. That is still a valid option in F90,
but today it is considered better practice to specify a result value name to be returned by the function.
The mean function is a M ATLAB intrinsic and can be used directly.
To illustrate the use of a result value, consider the related “median” value in F90.
function mid value(x) result(median)
! return the middle value of vector x
real :: median, x(:)
median = x(size(x)/2) ! what if size = 1 ??
end function mid value
To apply these two functions to an array, say y, we would simply write y ave = mean(y), and y mid
= mid value(y), respectively. While Fortran allows a “function” to return only a single object, both
C++ and M ATLAB use that subprogram name to return any number of result objects. Fortran employs
the name “subroutine” for such a procedure. Such procedures are allowed to have multiple inputs and
multiple outputs (including none). The syntax of the first line of these two subprogram classes are shown
in Table 4.22. Note that a typical subprogram may have no arguments, multiple input arguments (in1,
in2, inout), multiple result arguments (inout, out2), and arguments that are used for both input
and result usage (inout). These example names have been selected to reflect the fact that a programmer
usually intends for arguments to be used for input only, or for result values only, or for input, modification,
and output. It is considered good programming practice to declare such intentions to aid the compiler in
detecting unintended uses. F90 provides the INTENT statement for this purpose, but does not require its
use.
Having outlined the concepts of subprograms, we will review some presented earlier and then give
some new examples. Figure 1.3 presented a clipping function which was earlier expressed in pseudocode.
A corresponding Fortran implementation of such a clipping function is given in Fig. 4.7. Note that it is
very similar to the pseudocode version.
c 2001 J.E. Akin 69
[ 1] program main
[ 2] ! clip the elements of an array
[ 3] implicit none
[ 4] real, parameter :: limit = 3
[ 5] integer, parameter :: n = 5
[ 6] real :: y(n), x(n)
[ 7] ! Define x values that will be clipped
[ 8] x = (/ (-8. + 3.*k, k = 1,n) /) ! an implied loop
[ 9] do i = 1, n
[10] y(i) = clip (x(i), limit)
[11] end do
[12] print *, x
[13] print *, y
[14]
[15] contains ! methods
[16]
[17] function clip (x, L) result (c)
[18] ! c = clip(x, L) - clip the variable x, output
[19] ! x = scalar variable, input
[20] ! L = limit of the clipper, input
[21] !
[22] real, intent(in) :: x, L ! variable types
[23] real :: c ! variable types
[24] intent (in) x, L ! argument uses
[25] if ( abs(x) <= L ) then ! abs of x less than or equal L
[26] c = x; ! then use x
[27] else ! absolute of x greater than L ?
[28] c = sign(L,x) ! sign of x times L
[29] end if ! of value of x
[30] end function ! clip
[31] end program main
[32] !
[33] ! produces:
[34] ! -5.0000000 -2.0000000 1.0000000 4.0000000 7.0000000
[35] ! -3.0000000 -2.0000000 1.0000000 3.0000000 3.0000000
For the purpose of illustration an alternate F90 version of the Game of Life, shown earlier in Chapter 1
as pseudocode, is given in the assignment solutions section. Clearly we have not introduced all the
features utilized in these example codes so the reader should continue to refer back to them as your
programming understanding grows.
A simple program that illustrates program composition is maximum.f90, which asks the user to
specify several integers from which the program finds the largest. It is given in Fig. 4.8. Note how
the main program accepts the user input (lines 15,20), with the maxint function (line 22) finding the
maximum (lines 25-34). Perhaps modularity would have been better served by expressing the input
portion by a separate function. Of course, this routine is not really needed since F90 provides intrinsic
functions to find maximum and minimum values (maxval, minval) and their locations in any array
(maxloc, minloc). A similar C++ program composition is shown for comparison in the appendix.
c 2001 J.E. Akin 70
[ 1] program maximum ! of a set of integers (see intrinsic maxval)
[ 2] implicit none
[ 3] interface ! declare function interface protype
[ 4] function maxint (input, input length) result(max)
[ 5] integer, intent(in) :: input length, input(:)
[ 6] integer :: max
[ 7] end function ! maxint
[ 8] end interface
[ 9]
[10] integer, parameter :: ARRAYLENGTH=100
[11] integer :: integers(ARRAYLENGTH);
[12] integer :: i, n;
[13]
[14] ! Read in the number of integers
[15] print *,’Find maximum; type n: ’; read *, n
[16] if ( n > ARRAYLENGTH .or. n < 0 ) &
[17] stop ’Value you typed is too large or negative.’
[18]
[19] do i = 1, n ! Read in the user’s integers
[20] print *, ’Integer ’, i, ’?’; read *, integers(i)
[21] end do ! over n values
[22] print *, ’Maximum: ’, maxint (integers, n)
[23] end program maximum
[24]
[25] function maxint (input, input length) result(max)
[26] ! Find the maximum of an array of integers
[27] integer, intent(in) :: input length, input(:)
[28] integer :: i, max
[29]
[30] max = input(1); ! initialize
[31] do i = 1, input length ! note could be only 1
[32] if ( input(i) > max ) max = input(i);
[33] end do ! over values
[34] end function maxint ! produces this result:
[35] ! Find maximum; type n: 4
[36] ! Integer 1? 9
[37] ! Integer 2? 6
[38] ! Integer 3? 4
[39] ! Integer 4? -99
[40] ! Maximum: 9
c 2001 J.E. Akin 71
Global Variable Declaration
M ATLAB global list of variables
F77 common /set name/ list of variables
F90 module set name
save
type (type tag) :: list of variables
end module set name
C++ extern list of variables
c 2001 J.E. Akin 72
module or program name inherit
Optional territorial variable, type specification, and calls
contains
subroutine Internal 1
territorial specifications and calls
contains
subroutine Internal 2
local computations
end subroutine Internal 2
subroutine Internal 3
local computations
end subroutine Internal 3
c 2001 J.E. Akin 73
Action C++ F90
Bitwise AND & iand
Bitwise exclusive OR ^ ieor
Bitwise exclusive OR j ior
Circular bit shift ishftc
Clear bit ibclr
Combination of bits mvbits
Extract bit ibits
Logical complement not
Number of bits in integer sizeof bit size
Set bit ibset
Shift bit left ishft
Shift bit right ishft
Test on or off btest
Transfer bits to integer transfer
c 2001 J.E. Akin 74
[ 1] module exceptions
[ 2] implicit none
[ 3] integer, parameter :: INFO = 1, WARN = 2, FATAL = 3
[ 4] integer :: error count = 0
[ 5] integer :: max level = 0
[ 6] contains
[ 7]
[ 8] subroutine exception (program, message, flag)
[ 9] character(len=*) :: program
[10] character(len=*) :: message
[11] integer, optional :: flag
[12]
[13] error count = error count + 1
[14]
[15] print *, ’Exception Status Thrown’
[16] print *, ’ Program :’, program
[17] print *, ’ Message :’, message
[18] if ( present(flag) ) then
[19] print *, ’ Level :’, flag
[20] if ( flag > max level ) max level = flag
[21] end if ! flag given
[22] end subroutine exception
[23]
[24] subroutine exception status ()
[25] print *
[26] print *, "Exception Summary:"
[27] print *, " Exception count = ", error count
[28] print *, " Highest level = ", max level
[29] end subroutine exception status
[30] end module exceptions
integer input). They also allow the ERR = error label branching described above for the file open/close
operations.
In addition, the READ statement also retains the old standard keyword END = to identify a label number
to which control transfers when an end-of-file (EOF) is detected.
Status Inquiry: Whether in UNIT mode or FILE mode, the INQUIRE statement for file operations allows
the IOSTAT = and ERR = keywords like the OPEN statement. In addition, either mode supports two logical
keywords : EXISTS = to determine if the UNIT (or FILE) exists, and OPENED = to determine if a (the)
file is connected to this (an) unit.
Optional Arguments: The PRESENT function returns a logical value to indicate whether or not an
optional argument was provided in the invocation of the procedure in which the function appears.
Pointers and Targets: The ASSOCIATED function returns a logical value to indicate whether a pointer
is associated with a specific target, or with any target.
c 2001 J.E. Akin 75
a copy of the program and deleting from the copy all information except that which describes the argu-
ments and subprogram type. If the program does not exist, you write the interface first to define what
will be expected of the subprogram regardless of who writes it. It is considered good programming style
to include explicit interfaces, or prototype code, even if they are not required.
If in doubt about the need for an explicit interface see if the compiler gives an error because it is not
present. In F90 the common reasons for needing an explicit interface are: 1) Passing an array that has
only its rank declared. For example, A(:,:), B(:). These are known as “assumed-shape” arrays; 2)
Using a function to return a result that is: a) an array of unknown size, or b) a pointer, or c) a character
string with a dynamically determined length. Advanced features like optional argument lists, user defined
operators, generic subprogram names (to allow differing argument types) also require explicit operators.
In C++ before calling an external function, it must be declared with a prototype of its parameters.
The general form for a function is
function type function name ( argument type list);
where the argument type list is the comma separated list of pairs of type and name for each
argument of the function. These names are effectively treated as comments, and may be different from
the names in the calling program, or even omitted. The use of a prototype was shown in Fig. 4.8 and is
used again in Fig. 4.12 which also illustrates passing arguments by reference or by value.
An interface block for external subprograms was not required by F77 (thereby leading to hard to find
errors), but is strongly recommended if F90 and is explicitly required in several situations. The general
form for a F90 interface is
where the argument name list is the comma separated list of names. Of course, the function type
refers to the result argument name. These names may be different from the names in the calling program.
A typical subroutine interface body would be
where the argument name list is the comma separated list of names. The topic of a module procedure is
covered elsewhere. The use of a interface block was shown in Fig. 4.8 and used in two new codes, shown
in Fig. 4.12, and the corresponding C++ code in the appendix, which also illustrate passing arguments by
reference (line 23) and by value (line 19) in both F90 and C++. The important, and often confusing, topic
of passing by reference or value was discussed in Sec. 4.2 and is related to other topics to be considered
later, such as the use of “pointers” in C++ and F90, and the “intent” attribute of F90 arguments. Passing
by reference is default in F90 while passing by value is default in C++ .
c 2001 J.E. Akin 76
[ 1] program main
[ 2] implicit none
[ 3] ! declare the interface prototypes
[ 4] interface
[ 5] subroutine Change (Refer)
[ 6] integer :: Refer; end subroutine Change
[ 7] subroutine No Change (Value)
[ 8] integer :: Value; end subroutine No Change
[ 9] end interface
[10]
[11] ! illustrate passing by reference and by value in F90
[12]
[13] integer :: Input Val, Dummy Val
[14]
[15] print *, "Enter an integer: "
[16] read *, Input Val; print *, "Input value was ", Input Val
[17]
[18] ! pass by value
[19] call No Change ( (Input Val) ) ! Use but do not change
[20] print *, "After No Change it is ", Input Val
[21]
[22] ! pass by reference
[23] call Change ( Input Val ) ! Use and change
[24] print *, "After Change it is ", Input Val
[25] end program
[26]
[27] subroutine Change (Refer)
[28] ! changes Refer in calling code IF passed by reference
[29] integer :: Refer
[30] Refer = 100;
[31] print *, "Inside Change it is set to ", Refer
[32] end subroutine Change
[33]
[34] subroutine No Change (Value)
[35] ! does not change Value in calling code IF passed by value
[36] integer :: Value
[37] Value = 100;
[38] print *, "Inside No Change it is set to ", Value
[39] end subroutine No Change
[40]
[41] ! Running gives:
[42] ! Enter an integer: 12
[43] ! Input value was 12
[44] ! Inside No Change it is set to 100
[45] ! After No Change it is 12
[46] ! Inside Change it is set to 100
[47] ! After Change it is 100
32 positions higher (character 97 is ‘a’). These printable characters begin at character 32, as shown in
Table 4.25 for the ASCII standard. The first 33 characters are “non-printing” special control characters.
For example, NUL = null, EOT = end of transmission, BEL = bell, BS = backspace, and HT = horizontal
tab. To enter a control character, one must simultaneously hold down the CONTROL key and hit the letter
that is 64 positions higher in the list. That is, an end of transmission EOT is typed as CONTROL-D. The
code SP denotes the space character, and we will use the underscore “ ” to represent a blank in strings.
We can employ the standard relational operators (e.g., less than) to compare strings and would find
that ’bad’ < ’dog’ < ’same’ == ’same ’, that ’word’ > ’WORD’, and that ’four’ < ’one’
< ’two’ while ’1’ < ’2’ < ’4’. Note that the above equality occurred because trailing blanks are not
considered in relational operations, but leading blanks are considered: ’same’ 6= ’ same’. The F90
function adjustL removes leading blanks and appends them to the right end. Thus, it adjusts the string
to the left, so that ’same’ == adjustL(’ same’). This and other F90 intrinsic character functions
are summarized in Table 4.26.
All blanks are considered when determining the length of a character string. In F90 the intrinsic
function LEN provides these data so that LEN(’same’) = 4, LEN(’ same’) = 6, and LEN(’same ’)
= 7. There is another intrinsic function, LEN TRIM, that provides the string length ignoring trail-
ing blanks. By way of comparison: LEN TRIM(’same’) = 4, LEN TRIM(’ same’) = 6, and
LEN TRIM(’same ’) = 4. Each character in a string or any internal substrings may be referenced
by the colon operator. Given a character variable we can define a substring, say sub as
sub = variable(K:L) for 0 < K,L <= LEN(variable)
= null for K > L
c 2001 J.E. Akin 77
0 NUL 1 SOH 2 STX 3 ETX 4 EOT 5 ENQ 6 ACK 7 BEL
8 BS 9 HT 10 NL 11 VT 12 NP 13 CR 14 SO 15 SI
16 DLE 17 DC1 18 DC2 19 DC3 20 DC4 21 NAK 22 SYN 23 ETB
24 CAN 25 EM 26 SUB 27 ESC 28 FS 29 GS 30 RS 31 US
32 SP 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ’
40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 /
48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7
56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ?
64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G
72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O
80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W
88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ˆ 95 _
96 ‘ 97 a 98 b 99 c 100 d 101 e 102 f 103 g
104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o
112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w
120 x 121 y 122 z 123 { 124 | 125 } 126 ˜ 127 DEL
For example, given the string ’howl’, then we can define bird = string(2:4) = ’owl’, and prep
= string(1:3) = ’how’.
The F90 and F77 operator used to concatenate strings into larger strings is “//”. Continuing the last
example, we see that the concatenation string(1:3)//’ ’//string(2:4)//’?’ is ’how owl?’,
while the concatenation ’same ’//’word’ becomes ’same word’ and ’bad’//’ ’//’dog’
becomes ’bad dog’. Programs illustrating the reading and concatenating two strings are given in
Fig. 4.13, and in the companion C++ code in the appendix.
Sometimes one needs to type in a non-printing character, such as a tab or a newline. To allow this,
special transmissions have been allowed for, as summarized in Table 4.27.
Remember the ASCII character features: the uppercase letters correspond to numbers 65 through 90
in the list, while the lowercase letters are numbers 97 through 122, so that if we wanted to convert “G” to
c 2001 J.E. Akin 78
[ 1] program main
[ 2] ! Compare two strings
[ 3] ! Concatenate two character strings together
[ 4] ! Get the combined length
[ 5] implicit none
[ 6] character(len=20) :: String1, String2
[ 7] character(len=40) :: String3
[ 8] integer :: length
[ 9]
[10] print *,’Enter first string (20 char max):’
[11] read ’(a)’, String1 ! formatted
[12]
[13] print *,’Enter second string (20 char max):’
[14] read ’(a)’, String2 ! formatted
[15]
[16] ! compare
[17] if ( String1 == String2 ) then
[18] print *, "They are the same."
[19] else
[20] print *, "They are different."
[21] end if
[22]
[23] ! concatenate
[24] String3 = trim (String1) // trim (String2)
[25]
[26] print *,’The combined string is:’, String3
[27] length = len trim (String3)
[28] print *,’The combined length is:’, length
[29]
[30] end program main
[31] ! Running with "red" and "bird" produces:
[32] ! Enter first string (20 char max): red
[33] ! Enter second string (20 char max): bird
[34] ! They are different.
[35] ! The combined string is: redbird
[36] ! The combined length is: 7
[37] ! Also "the red" and "bird" works
or visa versa:
UPPER_G = achar(iachar(’g’) - 32)
since they differ by 32 locations. Likewise, since the zero character “0” occurs in position 48 of the
ASCII set we could convert a single digit to the same numerical value with:
integer :: number_5
number_5 = iachar(’5’) - 48
and so forth for all ten digits. To convert a string of digits, such as ’5623’, to the corresponding number
5623, we could use a looping operation.
c 2001 J.E. Akin 79
[ 1] program main
[ 2] ! Convert a character string to an integer in F90
[ 3] implicit none
[ 4] character(len=5) :: Age Char
[ 5] integer :: age
[ 6]
[ 7] print *, "Enter your age: "
[ 8] read *, Age Char ! a character string
[ 9]
[10] ! convert using an internal file read
[11] read (Age Char, fmt = ’(i5)’) age ! convert to integer
[12]
[13] print *, "Your integer age is ", age
[14] print ’(" Your binary age is ", b8)’, age
[15] print ’(" Your hexadecimal age is ", z8)’, age
[16] print ’(" Your octal age is ", o8)’, age
[17]
[18] end program main
[19] !
[20] ! Running gives:
[21] ! Enter your age: 45
[22] ! Your integer age is 45
[23] ! Your binary age is 101101
[24] ! Your hexadecimal age is 2D
[25] ! Your octal age is 55
However, since loops can be inefficient, it is better to learn that, in F90, an “internal file” can be (and
should be) employed to convert one data type to another. Here we could simply code:
! internal file called convert
write(convert, ‘‘(A)’’) digit
read(convert, ‘‘(I4)’’) number
to convert a character to an integer (or real) number. Converting strings to integers is shown in the codes
given in Fig. 4.14 (line 11) and the corresponding C++ appendix routine. Similar procedures would be
used to convert strings to reals. The C++ version (see appendix) uses the intrinsic function “atoi” while
the F90 version uses an internal file for the conversion.
One often finds it useful to change the case of a string of characters. Some languages provide intrinsic
functions for that purpose. In C++ and M ATLAB the function to convert a string to all lower case letters
are called tolower and lower, respectively. Here we define a similar F90 function called to lower
which is shown in Fig. 4.15 along with a testing program in Fig. 4.16. Note that the testing program
uses an interface to tolower (lines 4-13) assuming that routine was compiled and stored external to the
testing program. The tolower function employs the intrinsic function index (line 16) to see if the k-th
character of the input string is an upper case letter. The intrinsic function len is also used (line 8) to
force the new string to be the same length as the original string.
c 2001 J.E. Akin 80
[ 1] function to lower (string) result (new string) ! like C
[ 2] ! -------------------------------------------------------------
[ 3] ! Convert a string or character to lower case
[ 4] ! (valid for ASCII or EBCDIC processors)
[ 5] ! -------------------------------------------------------------
[ 6] implicit none
[ 7] character (len = *), intent(in) :: string ! unknown length
[ 8] character (len = len(string)) :: new string ! same length
[ 9] character (len = 26), parameter :: &
[10] UPPER = ’ABCDEFGHIJKLMNOPQRSTUVWXYZ’, &
[11] lower = ’abcdefghijklmnopqrstuvwxyz’
[12] integer :: k ! loop counter
[13] integer :: loc ! position in alphabet
[14] new string = string ! copy everything
[15] do k = 1, len(string) ! to change letters
[16] loc = index ( UPPER, string(k:k)) ! first upper
[17] if (loc /= 0 ) new string(k:k) = lower(loc:loc) ! convert it
[18] end do ! over string characters
[19] end function to lower
languages impose of defining explicitly each variable’s type to be tedious, unnecessary, and a source of
bugs. It’s tedious because the programmer must think not only about what the variable represents, but
also how the computations calculate its value, unnecessary because mathematics doesn’t work that way
(the variable x represents a quantity regardless whether it turns out to be an integer or a complex value),
and bug-creating because computations involving different types and assigned to a typed variable can
yield nonmathematical results (for example, dividing the integers 1 with 3 and assigning the results to an
integer yields a zero value).
M ATLAB is one language in which variables are not explicitly typed. (Beginning programmers
cheer!) Internally, M ATLAB represents numbers in double precision floating point. If a variable’s value
corresponds to an integer, M ATLAB will gleefully print it that way, effectively hiding its floating point
representation. A surprise occurs when a calculation accidentality becomes complex: M ATLAB will
(silently) change what the variable represents from being real to being complex. For example, M ATLAB
will, without complaint, calculate x=log(-1) and assign the value 3:14159i to x. In many applications,
the expression that yielded the value of 1 because of an error, and M ATLAB will let the error propagate.
(Beginning programmers sigh!) Most, if not all typed languages will immediately announce the evalua-
tion of the logarithm of a negative number, and halt execution. By explicitly defining the kinds of values
a variable will assume helps programming clarity and run-time debugging to some degree.
C++ has four intrinsic (i.e., built-in) types of data integer, single and double precision reals, and
character and F90 has the similar set: integer, real, complex, logical, and character. F90 also allows
the user to create a specific precision level for integer and real data. C++ has specified byte sizes for
three character, six integer, one single precision real, and two double precision real data types for a total
of twelve intrinsic data types.
c 2001 J.E. Akin 81
C, C++ Variable.component.sub component
F90 Variable%component%sub component
In addition to intrinsic types, C, C++ and F90 allow the formation of new types of
data structures that are collections of values of not necessarily the same type. These procedures
are named struct or type in C and F90, respectively.
To go along with this freedom, F90 allows you to define new operations to act on the derived types.
While C++ retains the struct keyword, it is viewed as a class with only public data members and no
functions. In other words, in C++ class is a generalization of struct and, thus, class is the preferred
keyword to use. As an example of a task made easier by derived data, consider creating parts of a data
structure to be used in an address book. We will need a variable that can have components and sub-
components. They are referenced by a special syntax and defined as illustrated in Tables 4.28 and 4.29.
This procedure for defining a new type of data structure can be “nested” by referring to other derived
type entities defined earlier in the program. These concepts are shown in Table 4.30. One should declare
the data type of all variables used in a program module. This is also true for user defined data structures.
Table 4.31 outlines the forms of these statements, how structures are initialized, and how component
values are assigned.
There are times when either the derived type variable or its components, or both, could be subscripted
objects (i.e., arrays). Then care must be taken in the interpretation of which variable or component is
being addressed. Table 4.32 illustrates the typical combinations with the F90 syntax.
As a concrete example, consider a phone type and address type definition.
c 2001 J.E. Akin 82
C, C++ struct data tag variable list; /* Definition */
f g
struct data tag variable = component values ; /* Initialization */
variable.component.sub component = value; /* Assignment */
F90 C++
type phone type struct phone type f
g
integer :: area code, number, extension int area code, number, extension;
end type phone type ;
type address type
integer :: number
struct address type f
int number;
character (len = 35) :: street, city char street[35], city[35];
character (len = 2) :: state char state[2];
integer :: zip code
g
int zip code;
end type address type ;
F90 C++
type person type struct person type f
character (len = 50) :: name char name[50];
type (phone type) :: phone struct phone type phone;
type (address type) :: address struct address type address;
g
integer :: born year int born year;
end type person type ;
F90 C++
type (person type) :: sammy, barney struct person type sammy, barney;
or build an address book array filled with the above data structures by defining
c 2001 J.E. Akin 83
F90
integer, parameter :: number = 99
type (person type), dimension (number) :: address book
C++
#define NUMBER 99
struct person type address book[NUMBER];
F90 C++
sammy%phone = phone type (713, 5278100, 0) f
sammy.phone = 713, 5278100, 0 ; g
sammy%zip code = 770051892 sammy.zip code = 770051892;
F90 C++
,
,
print sammy%phone printf("(%d)%d, extension %d",
print sammy%address%zip code sammy.area code,
sammy.number,
sammy.extension);
printf("%d", sammy.zip code);
and then define specific members for barney with the “constructor”
F90 C++
barney = person type("Barn Owl", & f
barney = "Barn Owl", 0,0,0 , f g
phone type(0,0,0), & sammy.address, 1892,
sammy%address, 1892, "Sammy’s cousin") "Sammy’s cousin" ; g
Note the difference in the defined type constructors. Two are actually used here because the second com-
ponent must be defined as a phone type. C++ just uses brackets to enclose the supplied components
of each user defined type. F90 has an intrinsic functon that is created automatically by the type definition
and it accepts all of the components required by the type. That is why the function name “phone type”
appears in the intrinsic constructor routine “person type”. Finally, put them in the book.
F90 C++
address book(1) = sammy address book[1] = sammy;
address book(2) = barney address book[2] = barney;
Fig. 4.17 presents a sample code for utilizing user defined structure types using F90 (there is a C++
version in the appendix). First a “person” structure is created (lines 4-7) by using only the intrinsic
types of integers and characters. It then is used in turn within an additional data structure (line 10). The
components of the structures are read (lines 18, 21, 24) and output (lines 26,27). For more general data,
suggested in the comments, formatted input/output controls would be necessary.
c 2001 J.E. Akin 84
[ 1] program main ()
[ 2] ! Define structures and components, via F90
[ 3] implicit none
[ 4] type Person ! define a person structure type
[ 5] character (len=20) :: Name
[ 6] integer :: Age
[ 7] end type Person
[ 8]
[ 9] type Who Where ! use person type in a new structure
[10] type (Person) :: Guest
[11] character (len=40) :: Address
[12] end type Who Where
[13]
[14] ! Fill a record of the Who Where type components
[15] type (Who Where) Record;
[16]
[17] print *,"Enter your name: "
[18] read *, Record % Guest % Name
[19]
[20] print *,"Enter your city: "
[21] read *, Record % Address
[22]
[23] print *,"enter your age: "
[24] read *, Record % Guest % Age
[25]
[26] print *,"Hello ", Record % Guest % Age, " year old ", &
[27] Record % Guest % Name, " in ", Record % Address
[28]
[29] end program main
[30]
[31] ! Running with input: Sammy, Houston, 104 gives
[32] ! Hello 104 year old Sammy in Houston
[33] !
[34] ! But try: Sammy Owl, Houston, 104 for a bug
will simply consist of two integer types, named num and denom, respectively. New data types can be
defined in any program unit. For maximum usefulness we will place the definition in a module named
Fractions. To use this new data type we will want to have subprograms to define a fraction, list its
components, and multiply two fractions together, and to equate one fraction to another. In addition to
the intrinsic constructor function fraction we will create a manual constructor function called assign
and it will have two arguments, the numerator value, and denominator value, and will use them to return
a fraction type. The listing subroutine, called list Fraction, simply needs the name of the fraction
to be printed. The function, mult Fraction, accepts two fraction names, and returns the third fraction
as their product. Finally, we provide a function that equates the components of one fraction to those in a
new fraction.
This data structure is presented in Fig. 4.18. There we note that the module starts with the definition
of the new data type (lines 2-4), and is followed with the “contains” statement (line 12). The subpro-
grams that provide the functionality of the fraction data type follow the “contains” statement and are thus
coupled to the definition of the new type. When we have completed defining the functionality to go with
the new data type we end the module.
In this example the program to invoke the fraction type follows in Fig. 4.19. To access the module,
which defines the new data type and its supporting functions, we simply employ a “use” statement at the
beginning of the program (line 2). The program declares three Fraction type variables (line 3): x, y ,
and z . The variable x is defined to be 22/7 with the intrinsic type constructor (line 5), while y is assigned
a value of 1/3 by using the function assign (line 7). Both values are listed for confirmation. Then we
form the new fraction, z = 22=21, by invoking the mult Fraction function (line 9),
z = mult Fraction (x, y)
which returns z as its result. A natural tendency at this point would be to simply write this as z = x y .
However, before we could do that we would have to tell the operators, “*” and ”=”, how to act when
provided with this new data type. This is known as overloading an intrinsic operator. We had the foresight
to do this when we set up the module by declaring which of the “module procedure”s were equivalent
to each operator symbol. Thus from the “interface operator (*)” statement block the system now knows
that the left and right operands of the “*” symbol correspond to the first and second arguments in the
c 2001 J.E. Akin 85
[ 1] module Fractions ! F90 "Fraction" data structure and functionality
[ 2] implicit none
[ 3] type Fraction ! define a data structure
[ 4] integer :: num, den ! with two "components"
[ 5] end type Fraction
[ 6]
[ 7] interface operator (*) ! extend meaning to fraction
[ 8] module procedure mult Fraction ; end interface
[ 9]
[10] interface assignment (=) ! extend meaning to fraction
[11] module procedure equal Fraction ; end interface
[12]
[13] contains ! functionality
[14] subroutine assign (name, numerator, denominator)
[15] type (Fraction), intent(inout) :: name
[16] integer, intent(in) :: numerator, denominator
[17]
[18] name % num = numerator ! % denotes which "component"
[19] if ( denominator == 0 ) then
[20] print *, "0 denominator not allowed, set to 1"
[21] name % den = 1
[22] else; name % den = denominator
[23] end if ; end subroutine assign
[24]
[25] subroutine list(name)
[26] type (Fraction), intent(in) :: name
[27]
[28] print *, name % num, "/", name % den ; end subroutine list
[29]
[30] function mult Fraction (a, b) result (c)
[31] type (Fraction), intent(in) :: a, b
[32] type (Fraction) :: c
[33]
[34] c%num = a%num * b%num ! standard = and * here
[35] c%den = a%den * b%den ; end function mult Fraction
[36]
[37] subroutine equal Fraction (new, name)
[38] type (Fraction), intent(out) :: new
[39] type (Fraction), intent(in) :: name
[40]
[41] new % num = name % num ! standard = here
[42] new % den = name % den ; end subroutine equal Fraction
[43] end module Fractions
function mult Fraction. Likewise, the left and right operands of “=” are coupled to the first and
second arguments, respectively, of subroutine equal Fraction. The testing main and verification
results are in Fig. 4.19 Before moving on note that the system does not yet know how to multiply a
integer times a fraction, or visa versa. To do that one would have to add more functionality, such as
a function, say int mult frac, and add it to the ”module procedure” list associated with the “*”
operator.
When considering which operators to overload for a newly defined data type one should consider
those that are used in sorting operations, such as the greater-than, >, and less-than, <, operators. They
are often useful because of the need to sort various types of data. If those symbols have been correctly
overloaded then a generic sorting routine might be used, or require few changes.
4.7.2 User Defined Operators
In addition to the many intrinsic operators and functions we have seen so far, the F90 user can also define
new operators or extend existing ones. User defined operators can employ intrinsic data types and/or user
defined data types. The user defined operators, or extensions, can be unary or binary (i.e., have one or
two arguments). The operator symbol must be included between two periods, such as ‘.op.’. Specific
examples will be given in the next chapter.
c 2001 J.E. Akin 86
[ 1] program main
[ 2] use Fractions
[ 3] implicit none
[ 4] type (Fraction) :: x, y, z
[ 5]
[ 6] x = Fraction (22,7) ! default constructor
[ 7] write (*,’("default x = ")’, advance=’no’) ; call list(x)
[ 8] call assign(y,1,3) ! manual constructor
[ 9] write (*,’("assigned y = ")’, advance=’no’) ; call list(y)
[10] z = mult Fraction (x,y) ! function use
[11] write (*,’("x mult y = ")’, advance=’no’) ; call list(z);
[12] print *, "Trying overloaded * and = for fractions:"
[13] write (*,’("y * x gives ")’, advance=’no’) ; call list(y*x) ! multi
[14] z = x*y ! new operator uses
[15] write (*,’("z = x*y gives ")’, advance=’no’) ; call list(z) ! add
[16] end program main ! Running gives:
[17] ! default x = 22/7 ! assigned y = 1/3 ! x mult y = 22/21
[18] ! Trying overloaded * and = for fractions:
[19] ! y * x gives 22/21 ! z = x*y gives 22/21
C++ F90
Declaration type tag *pointer name; type (type tag), pointer ::
pointer name
Target &target name type (type tag), target :: target name
Examples char *cp, c; character, pointer :: cp
int *ip, i; integer, pointer :: ip
float *fp, f; real, pointer :: fp
cp = & c; cp => c
ip = & i; ip => i
fp = & f; fp => f
they allow dynamic data structures, such as “linked lists” and “tree structures,” and they allow recursive
algorithms. Note that rather than containing data themselves, pointer variables simply exist to point
to where some data are stored. Unlike C and M ATLAB the F90 pointers are more like the “reference
variables” of the C++ language in that they are mainly an alias or synonym for another variable, or part
of another variable. They do not allow one to easily get the literal address in memory as does C. This is
why programmers that write computer operating systems usually prefer C over F90. But F90 pointers
allow easy access to array partitions for computational efficiency, which C++ does not. Pointers are often
used to pass arguments by reference.
The item to which a pointer points is known as a target variable. Thus, every pointer has a logical
status associated with it which indicates whether or not it is currently pointing to a target. The initial
value of the association is .false., or undefined.
4.8.1 Pointer Type Declaration
For every type of data object that can be declared in the language, including derived types, a correspond-
ing type of pointer and target can be declared (Table 4.33).
While the use of pointers gives programmers more options for constructing algorithms, they also have
a potential severely detrimental effect on the program execution efficiency. To ensure that compilers can
produce code that execute efficiently, F90 restricts the variables, to which a pointer can point, to those
specifically declared to have the attribute target. This, in part, makes the use of pointers in F90 and
C++ somewhat different. Another major difference is that C++ allows arithmetic to be performed on the
pointer address, but F90 does not.
So far, we have seen that F90 requires specific declarations of a pointer and an potential target.
However, C++ employs two unary operators, & and *, to deal with pointers and targets, respectively.
Thus, in C++ the operator &variable name means “the address of” variable name, and the C++
operator *pointer name means “the value at the address of” pointer name.
c 2001 J.E. Akin 87
C, C++ pointer name = NULL
F90 nullify (list of pointer names)
F95 pointer name = NULL()
[ 1] program pt expression
[ 2] !
[ 3] ! F90 example of using pointers in expressions
[ 4] implicit none
[ 5] integer, POINTER :: p, q, r
[ 6] integer, TARGET :: i = 1, j = 2, k = 3
[ 7]
[ 8] q => j ! q points to integer j
[ 9] p => i ! p points to integer i
[10] !
[11] ! An expression that "looks like" pointer arithmetic
[12] ! automatically substitutes the target value:
[13] !
[14] q = p + 2 ! means: j = i + 2 = 1 + 2 = 3
[15] print *, i, j, k ! print target values
[16] p => k ! p now points to k
[17] print *, (q-p) ! means print j - k = 3 - 3 = 0
[18] !
[19] ! Check associations of pointers
[20] print *, associated (r) ! false
[21] r => k ! now r points to k, also
[22] print *, associated (p,i) ! false
[23] print *, associated (p,k) ! true
[24] print *, associated (r,k) ! true
[25] end program pt expression
c 2001 J.E. Akin 88
angle. If the perimeter has N sides, the surveyor measures the length of each side and the interior angle
each side forms with the next. Often the perimeter has visual obstructions and offsets around them must
be made, recorded, and corrected for later. Regardless of how careful the surveyor is, errors are invariably
introduced during the measurement process. However, the error in angle measurements can be bounded.
The program for implementing the recording and correcting of the angles in a survey could be written
using a singly linked list. A linked list is chosen because the programmer has no idea how many sides
the perimeter has, and linked lists can grow arbitrarily. Because of the linked list’s ability to absorb a
short or long data stream, the user does not have to be asked to count the number of legs in the traverse.
The program begins by declaring a derived type that contains one angle measurement and a pointer to
the next measurement. A count is kept of the number of legs in this loop and the forward pointer for the
last angle read is cleared (set to null) to signal the end of list. After all the data are read, the entire list of
angles is reviewed to get the total of the measurements. This starts by revisiting the head of the list and
adding together all the angle measurements until a null pointer is encountered, signaling the end of list.
Then the error can be computed and distributed equally among the legs of the traverse.
if the files, class Person.h or class Person.inc, were in the same directory as your program. Otherwise,
it is necessary to give the complete system path to the file, such as,
include ‘/home/caam211/Include/inv.f90’
include ‘/home/caam211/Include/SolveVector.f90’
which give source links to the caam211 course files for the function inv(A) for returning the inverse
of a matrix A, and the function SolveVector(A,B) which returns the solution vector X for the matrix
system A*X = B.
In F90 one can also provide a “module” that defines constants, user defined types, supporting sub-
programs, operators, etc. Any of those features can be accessed by first including such a F90 module
before the main program and later invoking it with a “use” statement which cites the “module” name. For
example, the F90 program segments:
include ‘/home/caam211/Include/caam211 operators.f90’
Program Lab2 A 2
. . .
call test matrix ( A, B, X ) ! form and invert test matrix
. . .
subroutine test matrix ( A, B, X )
use caam211 operators ! included above
implicit none
real :: A(:,:), B(:), X(:)
real :: A inv(size(A,1),size(A,1)) ! automatic array allocation
A inv = inv(A)
X = A .solve. B ! like X = A B in Matlab n
. . .
c 2001 J.E. Akin 89
gives a source link to the caam211 course “module” source file named caam211 operators.f90
which contains subprograms, such as the function inv(), and operator definitions like .solve. which
is equivalent to the “\” operator in M ATLAB.
In the last example the omission of the “include” statement would require a compiler dependent state-
ment to allow the system to locate the module cited in the “use” statement. For the National Algorithms
Group (NAG) F90 compiler that link would be given as
f90 -o go /home/caam211/Include/caam211 operators.f90 my.f90
if the above segment was stored in the file named my.f90, while for the Cray F90 compiler a path flag,
-p, to the compiled version is required, such as:
f90 -o go -p /home/caam211/Include/caam211 op CRAY.o my.f90
1 X
N 2
2 = yi f (xi )
N
i=1
Least squares fitting of functions to data amounts to minimizing the dataset’s mean squared error with
respect to the parameters.
c 2001 J.E. Akin 90
To illustrate the least-squares approach, let’s fit a linear function to a dataset. Substituting the assumed
functional form f (x) = mx + b into the expression for the mean-squared error, we have
1 X
N 2
2
= yi (mx + b) i
N
i=1
We can find a set of equations for the parameters m and b that minimize this quantity by evaluating the
derivative of 2 with respect to each parameter and setting each to zero.
d2 1 X
N
dm
=
N
2x i yi (mx + b) = 0i
i=1
d2
N
1 X
db
=
N
2 y i i
(mx + b) = 0
i=1
After some simplification, we find that we have two linear equations to solve for the fitting parameters.
N ! N ! N
1 X 2 1 X 1 X
m N
xi +b N
xi =
N
xi yi
i=1 i=1! i=1
1 X
N XN
m N
xi +b =
1
N
yi
i=1 i=1
Thus, finding the least-squares fit of a straight line to a set of data amounts to solving a set of two linear
equations, the coefficients of which are computed from the data. Note that the four summations in the
last equation have the same range count (N) and could be evaluated in a single explicit loop.
An Aside
Because fitting data with a linear equation yields a set of two easily solved equations for the parameters,
one approach to fitting nonlinear curves to data is to convert the nonlinear problem into a linear one. For
example, suppose we want to fit a power law to the data: f (x) = axb . Instead of minimizing the mean
squared error directly, we transform the data so that we are fitting it with a linear curve. In the power
law case, the logarithm of the fitting curve is linear in the parameters: log f (x) = log a + b log x. This
equation is not linear in the parameter a. For purposes of least-squares fits, we instead treat a0 = log a
as the linear fit parameter, solve the resulting set of linear equations for a0 , and calculate a = exp a0 to
determine the power law fitting parameter. By evaluating the logarithm of xi and yi and applying the least
squares equations governing the fitting of a linear curve to data, we can fit a power-law function to data.
Thus, calculating a linear least squares fit to data underlies general approximation of measurements by
smooth curves. For an insight to the types of relationships that can be determined, see the following
summary.
x-axis y -axis Relationship
Linear Linear y = mx + b linear
Linear Logarithmic log y = mx + b exponential: y = eb emx
Logarithmic Linear y = m log x + b logarithmic
Logarithmic Logarithmic log y = m log x + b power-law: y = eb xm
We can now specify the computations required by the least squares fitting algorithm mathematically.
Algorithm: Least-Squares Fitting of Straight Lines to Data
1. Given N pairs of data points (xi ; yi )
PN 1 P N x, 1 P N x,
2. Calculatey a11 = N1 2
i=1 xi , a12 = N i=1 i a21 = N i=1 i a22 = 1, c1 =
1 PN 1 PN y .
N i=1 xi yi , and c2 = N i=1 i
y Note that these calculations can be performed in one loop rather than four.
c 2001 J.E. Akin 91
3. Solve the set of linear equations
a11 a12 m c1
=
a21 a22 b c2
m = (a12 c2
N c1 )=(a12 a21 N a11 )
b = (c2
m a12 )=N
Clearly, the second method produces a somewhat simpler expression than the first, and is vastly superior
to the first. In the sample code that follows in Fig. 4.21 we use the intrinsic array functions but encourage
the reader to check the results with a single loop that computes all six terms need to find m and b.
There are a few new features demonstrated in this example code. In line 6 we have specified a fixed
unit number to associate with the data file to be specified by the user. But we did not do an INQUIRE to
see if that unit was already in use. We will accept a user input filename (lines 8, 25 and 28) that contains
the data to be fitted. An interface (lines 12-21) is provided to external routines that will determine the
number of lines of data in the file and the read those data into the two arrays. Those two routines are given
elsewhere. Of course, the memory for the data arrays must be dynamically allocated (line 35) before they
can be read (line 37). After the least squares fit is computed (line 40) and printed the memory space for
the data is freed (line 44).
In the lsq fit subroutine (line 47) the three items of interest are passed in the array fit. (Routine
lsq fit could have been written as a function, try it.) Observe that y must be the same length as array
x so the size intrinsic was used to ensure that (line 56). The data summations are evaluated with the sum
intrinsic (lines 62-64) and it is used again to evaluate the mean squared error mse (line 72) as described
in step 4 of the algorithm. The test data (lines 78-89) and results (lines 92-96) are given as comments as
usual. Since no explicit loops have been used this form would be more efficient on vector computers and
some parallel computers.
4.10.2 Sorting
One of the most useful computational routines is sorting: Ordering a sequence of data according to some
rule. For example, the alphabetized list of filenames producted by a system directory command is far
easier to read than an unsorted list would be. Furthermore, data can be fruitfully sorted in more than one
way. As an example, you can sort system files by their creation date.
Sorting algorithms have been well studied by computer scientists in a quest to find the most efficient.
We use here the bubble sort algorithm, perhaps the oldest, but not most efficient. This algorithm makes
multiple passes over a list, going down the list interchanging adjacent elements in the list if needed to
put them in order. For example, consider the list [b, e, a, d, f, c], shown in Fig. 4.22, that we
c 2001 J.E. Akin 92
[ 1] program linear fit
[ 2] ! ------------------------------------------------------
[ 3] ! F90 linear least-squares fit on data in file
[ 4] ! specified by the user.
[ 5] ! ------------------------------------------------------
[ 6] implicit none
[ 7] integer, parameter :: filenumber = 1 ! RISKY
[ 8] real, allocatable :: x(:), y(:) ! data arrays
[ 9] character (len = 64) :: filename ! name of file to read
[10] integer :: lines ! number of input lines
[11] real :: fit(3) ! final results
[12]
[13] interface
[14] function inputCount(unit) result(linesOfInput)
[15] integer, intent(in) :: unit ! file unit number
[16] integer :: linesOfInput ! result
[17] end function inputCount
[18] subroutine readData (inFile, lines, x, y)
[19] integer, intent(in) :: inFile, lines ! file unit, size
[20] real, intent(out) :: x(lines), y(lines) ! data read
[21] end subroutine readData
[22] end interface
[23]
[24] ! Get the name of the file containing the data.
[25] write (*,*) ’Enter the filename to read data from:’
[26] read (*,’(A64)’) filename
[27]
[28] ! Open that file for reading.
[29] open (unit = filenumber, file = filename)
[30]
[31] ! Find the number of lines in the file
[32] lines = inputCount (filenumber)
[33] write (*,*) ’There were ’,lines,’ records read.’
[34]
[35] ! Allocate that many entries in the x and y array
[36] allocate (x(lines), y(lines))
[37]
[38] call readData (filenumber, lines, x, y) ! Read data
[39] close (filenumber)
[40]
[41] call lsq fit (x, y, fit) ! least-squares fit
[42] print *, "the slope is ", fit(1) ! display the results
[43] print *, "the intercept is ", fit(2)
[44] print *, "the error is ", fit(3)
[45] deallocate (y, x)
[46] contains
[47]
wish to sort to alphabetical order. In the first pass, the algorithm begins by examining the first two list
elements (b, e). Since they are in order, these two are left alone. The next two elements (e, a)
are not in order; these two elements of the list are interchanged. In this way, we “bubble” the element a
toward the top and e toward the bottom. The algorithm proceeds through the list, interchanging elements
if need be until the last element is reached. Note that the bottom of the list at the end of the first pass
contains the correct entry. This effect occurs because of the algorithm’s structure: The “greatest” element
will always propagate to the list’s end. Once through the pass, we see that the list is in better, but not
perfect, order. We must perform another pass just like the first to improve the ordering. Thus, the second
pass need consider only the first n 1 elements, the third n 2, etc. The second pass does make the
list better formed. After more passes, the list eventually becomes sorted. To produce a completely sorted
list, the bubble-sort algorithm requires no more passes than the number of elements in the list minus one.
The following F90 routines illustrate some of the initial features of a simple procedural approach
to a simple process like the bubble-sort algorithm. We begin by considering the sorting of a list of real
numbers as shown in subroutine Sort Reals in Fig. 4.22.
In line 1 we have passed in the size of the array, and the actual array (called database). Note that the
database has intent (inout) because we plan to overwrite the original database with the newly sorted order,
which is done in lines 18–20. For efficiency sake we have included an integer counter, swaps Made,
so that we can determine if the sort has terminated early. If we wished to apply the same bubble-sort
algorithm to an integer array all we would have to do is change the procedure name and lines 6 and 10
that describe the type of data being sorted (try it).
c 2001 J.E. Akin 93
[48] subroutine lsq fit (x, y, fit)
[49] ! ------------------------------------------------------
[50] ! Linear least-squares fit, A u = c
[51] ! ------------------------------------------------------
[52] ! fit = slope, intercept, and mean squared error of fit.
[53] ! lines = the length of the arrays x and y.
[54] ! x = array containing the independent variable.
[55] ! y = array containing the dependent variable data.
[56] implicit none
[57] real, intent(in) :: x(:), y(size(x))
[58] real, intent(out) :: fit(3)
[59] integer :: lines
[60] real :: m, b, mse
[61] real :: sumx, sumx2, sumy, sumxy
[62]
[63] ! Summations
[64] sumx = sum ( x ) ; sumx2 = sum ( x**2 )
[65] sumy = sum ( y ) ; sumxy = sum ( x*y )
[66]
[67] ! Calculate slope intercept
[68] lines = size(x)
[69] m = (sumx*sumy - lines*sumxy)/(sumx**2 - lines*sumx2)
[70] b = (sumy - m*sumx)/lines
[71]
[72] ! Predicted y points and the sum of squared errors.
[73] mse = sum ( (y - m*x - b)**2 )/lines
[74] fit(1) = m ; fit(2) = b ; fit(3) = mse ! returned
[75] end subroutine lsq fit
[76]
[77] end program linear fit
[78]
[79] ! Given test set 1 in file lsq 1.dat:
[80] ! -5.000000 -2.004481
[81] ! -4.000000 -1.817331
[82] ! -3.000000 -1.376481
[83] ! -2.000000 -0.508725
[84] ! -1.000000 -0.138670
[85] ! 0.000000 0.376678
[86] ! 1.000000 0.825759
[87] ! 2.000000 1.036343
[88] ! 3.000000 1.815817
[89] ! 4.000000 2.442354
[90] ! 5.000000 2.636355
[91] ! Running the program yields:
[92] !
[93] ! Enter the filename to read data from: lsq 1.dat
[94] ! There were 11 records read.
[95] ! the slope is 0.4897670746
[96] ! the intercept is 0.2988743484
[97] ! the error is 0.2139159478E-01
That is true because the compiler knows how to apply the > operator to all the standard numerical
types in the language. But what if we want to sort character strings, or other types of objects? Fortran has
lexical operators (like LGE) to deal with strings, but user defined objects would require that we overload
the > operator, if the expected users would not find the overloading to be confusing. In other words, you
could develop a fairly general sort routine if we changed lines 6 and 10 to be
[ 6] type (Object), intent(inout) :: database (lines)
[10] type (Object) :: temp
and provided an overloading of > so that line 17 makes sense for the defined Object (or for selected
component of it).
To illustrate the sort of change that is necessary to sort character strings consider subroutine
Sort String Fig. 4.23:
To keep the same style as the previous algorithm and overload the > operator we would have to have a
procedure that utilizes the lexical operators in lines 24 and 25, along with the interface definition on lines
12 through 17, do define the meaning of > in the context of a string. While the concept of a “template”
for a code to carry out a bubble-sort on any list of objects it may not always be obvious what > means
when it is overloaded by you or some other programmer.
Note that in the two above sorting examples we have assumed that we had the authority to change the
original database, and that it was efficient to do so. Often that is not the case. Imagine the case where
the database represents millions of credit card users, each with a large number components of numbers,
c 2001 J.E. Akin 94
Pass 1
b b b b b b
? e a a a a
e
a a? e? d d d
d d d e e e
f f f f? f? c
c c c c c f
Pass 2
b a a a a
?
a b? b b b
d d d d d
?
e e e e c
c c c c? e
f f f f f
character strings, or general objects. If many workers are accessing those data for various sorting needs
you probably would not allow the original dataset to be changed for reasons of safety or security. Then
we consider an alternative to moving around the actual database components. That is, we should consider
using moving pointers to large data components, or pseudo-pointers such as an ordering array. The use
of an ordering array is shown in Fig. 4.24 where subroutine Integer Sort now includes an additional
argument.
The third argument has intent (out), as shown in line 7, and is an integer array of the same length
as the original database which has now been changed to intent (in) so the compiler will not allow us to
change the original data. If the data are properly sorted as supplied then it should not be changed and the
new order should be the same as the original sequential input. That is why line 13 initializes the return
order to a sequential list. Then we slightly change the previous sort logic so that lines 19 through 23 now
check whats in an ordered location, and change the order number when necessary, but never change the
original data. After exiting this routine you could list the information, in sorted order, without changing
the original data simply by using vector subscripts in a print statement like:
print *, database (order).
c 2001 J.E. Akin 95
[ 1] subroutine Sort String (lines, database)
[ 2] ! Bubble Sort of (Changed) String Database
[ 3] implicit none
[ 4]
[ 5] integer, intent(in) :: lines ! input size
[ 6] character(len=*), intent(inout) :: database (lines) ! records
[ 7]
[ 8] character (len = len(database (1))) :: temp ! swap holder
[ 9] integer :: swaps Made ! number of swaps in a pass
[10] integer :: count ! loop variable
[11]
[12] interface ! to lower
[13] function to lower (string) result (new String)
[14] character (len = *), intent(in) :: string
[15] character (len = len(string)) :: new String
[16] end function to lower
[17] end interface ! to lower
[18]
[19] do ! Repeat this loop forever... (until we break out of it)
[20] swaps Made = 0 ! Initially, we’ve made no swaps
[21] ! Make one pass of the bubble sort algorithm
[22] do count = 1, (lines - 1)
[23] ! If the element is greater than the one after it, swap them
[24] if ( LGT (to lower (database (count )),
[25] to lower (database (count + 1))) ) then
[26] temp = database (count )
[27] database (count ) = database (count + 1)
[28] database (count + 1) = temp
[29] swaps Made = swaps Made + 1
[30] end if
[31] end do
[32] ! If we made no swaps, berak out of the loop.
[33] if ( swaps Made == 0) exit ! do count swaps
[34] end do
[35] end subroutine Sort String
Clearly you could write a very similar program using a true “pointer” array since they are now standard
in Fortran.
Next we will start to generalize the idea of sorting to include the sorting of objects that may have
numerous components. Assume the each record object to be read is defined as in Fig. 4.25.
There may be thousands, or millions, of such records to be read from a file, sorted by name and/or
number, and then displayed in sorted order. Program test bubble, in Fig. 4.26 illustrates one approach to
such a problem. Here since the database of records are to read from a file we do not yet know how many
c 2001 J.E. Akin 96
[ 1] module record Module
[ 2] !-------------------------------------------------------------
[ 3] ! record Module holds the "record" type
[ 4] !-------------------------------------------------------------
[ 5] ! record is a data structure with two names and an id number.
[ 6] type record
[ 7] character (len=24) :: last Name ! last name
[ 8] character (len=24) :: first Name ! first name
[ 9] integer :: id ! id number
[10] end type record
[11] end module record Module
there are to be stored. Therefore, it is declared allocatable in line 13, and allocated later in line 34 after
we have evaluated the file size of a file named by the user. Although not generally necessary we have
selected to have an order array for names and a different one for numbers. The are sort by Name, and
sort by Number, respectively and are treated in a similar fashion to the database memory allocation as
noted in lines 13–14, and line 35.
In line 21 we have arbitrarily set a unit number to be used for the file. That is okay for a very small
code, but an unnecessary and unwise practice in general. The Fortran intrinsic inquire allows one to
c 2001 J.E. Akin 97
determine which units are inactive and we could create a function, say Get Next Unit, to select a safe
unit number for our input operation. After accepting a file name we open the unit, and count the number
of lines present in the file (see line 30). Had the database been on the standard input device, and not
contained any non-printing control characters, we could have easily read it with the statement
read *, database
However, it does contain tabs (ASCII character number 9), and is in a user defined file instead of the
standard input device so line 38 invokes subroutine read Data to get the data base. Of course, once the
tabs and commas have been accounted for and the names and id number extracted it uses an intrinsic
constructor on each line to form its database entry like:
database (line Count) = Record (last, first, id)
After all the records have been red into the database note that line 42 extracts all the last names with the
syntax
database (:) last Name
so they are copied into subroutine String Sort, as its second argument, and the ordered list
sort by Name) is returned to allow operations that need a last name sort. Likewise, subroutine In-
teger Sort, shown above, is used in line 50 to sort the id numbers and save the data in order list
sort by Number. The ordered lists are used in show Data, in lines 46 and 53, to display the sorted
information, without changing the original data.
If the supplied file, say namelist, contained data in the format of (String comma String tab Number)
with the following entries:
[ 1] Indurain, Miguel 5623
[ 2] van der Aarden, Eric 1245
[ 3] Rominger, Tony 3411
[ 4] Sorensen, Rolf 341
[ 5] Yates, Sean 8998
[ 6] Vandiver, Frank 45
[ 7] Smith, Sally 3821
[ 8] Johnston, David 3421
[ 9] Gillis, Malcolm 3785
[10] Johns, William 7234
[11] Johnston, Jonathan 7234
[12] Johnson, Alexa 5190
[13] Kruger, Charlotte 2345
[14] Butera, Robert 7253
[15] Armstrong, Lance 2374
[16] Hegg, Steve 9231
[17] LeBlanc, Lucien 23
[18] Peiper, Alan 5674
[19] Smith-Jones, Nancy 9082
and
c 2001 J.E. Akin 98
[26] ! Data sorted by number:
[27] !
[28] ! LeBlanc Lucien 23
[29] ! Vandiver Frank 45
[30] ! Sorensen Rolf 341
[31] ! van der Aarden Eric 1245
[32] ! Kruger Charlotte 2345
[33] ! Armstrong Lance 2374
[34] ! Rominger Tony 3411
[35] ! Johnston David 3421
[36] ! Gillis Malcolm 3785
[37] ! Smith Sally 3821
[38] ! Johnson Alexa 5190
[39] ! Indurain Miguel 5623
[40] ! Peiper Alan 5674
[41] ! Johns William 7234
[42] ! Johnston Jonathan 7234
[43] ! Butera Robert 7253
[44] ! Yates Sean 8998
[45] ! Smith-Jones Nancy 9082
[46] ! Hegg Steve 9231
4.11 Exercises
1 Frequently we need to know how many lines exist in an external file that is to be used by our
program. Usually we need that information to dynamically allocate memory for the arrays
that will be constructed from the file data to be read. Write a F90 program or routine that will
accept a unit number as input, open that unit, loop over the lines of data in the file connected to the
unit, and return the number of lines found in the file. (A external file ends when the iostat from
a read is less than zero.)
2 A related problem is to read a table of data from an external file. In addition to knowing the number
of lines in the file it is necessary to know the number of entities (columns) per line and to verify
that all lines of the file have the same number of columns. Develop a F90 program for that purpose.
(This is the sort of checking that the M ATLAB load function must do before loading an array of
data.)
c 2001 J.E. Akin 99
3 Write a program that displays the current date and time and uses the module tic toc, in Fig. 4.10,
to display the CPU time required for a calculation.
4 Develop a companion function called to upper that converts a string to all upper case letters.
Test it with the above program.
5 Develop a function that will take an external file unit number and count the number of lines in the
file connected to that unit. This assumes that the file has been “opened” on that unit. The interface
to the function is to be:
interface
function inputCount(unit) result(linesOfInput)
integer, intent(in) :: unit ! file unit number
integer :: linesOfInput ! result
end function inputCount
end interface
6 Assume the file in the previous problem contains two real values per line. Develop a subroutine
that will read the file and return two vectors holding the first and second values, respectively. The
interface to the subroutine is to be:
interface
subroutine readData (inFile, lines, x, y)
integer, intent(in) :: inFile, lines ! file unit, size
real, intent(out) :: x(lines), y(lines) ! data read
end subroutine readData
end interface
7 Written replies to the questions given below will be required. All of the named files are provided
in source form as well as being listed in the text. The cited Figure number indicates where some or
all of the code is discussed in the text.
c 2001 J.E. Akin 100
8 The vertical motion of a projectile at any time, t, has a position given by y = y0 + V0 t 1=2 g t2,
and a velocity of V = V0 g t when upward is taken as positive, and where the initial conditions
on the starting position and velocity, at t = 0, are y0 and V0 , respectively. Here the gravitational
acceleration term, g , has been taken downward. Recall that the numerical value of g depends on
the units employed. Use metric units with g = 9:81m=s2 for distances measured in meters and
time in seconds.
Write a C++ or F90 program that will accept initial values of y0 and V0 , and then compute and
print y and V for each single input value of time, t. Print the results for y0 = 1:5 meters and
V0 = 5:0m=s for times t = 0:5, 2.0, and 4.0 seconds.
9 Modify the projectile program written in Problem 2 to have it print the time, position, and velocity
for times ranging from 0.0 to 2.0 seconds, in increments of 0.05 seconds. If you use a direct loop
do not use real loop variables. Conclude the program by having it list the approximate maximum
(positive) height reached and the time when that occurred. The initial data will be the same, but
should be printed for completeness. The three columns of numbers should be neat and right jus-
tified. In that case the default print format (print * in F90) will usually not be neat and one must
employ a “formatted” print or write statement.
10 The Greatest Common Divisor of two positive integers can be computed by at least two differ-
ent approaches. There is a looping approach known as the Euclidean Algorithm which has the
following pseudocode:
Implement this approach and test with max = 532 = 28 19 and min = 112 = 28 8. The
names of the remainder functions are given in Table 4.7.
Another approach to some algorithms is to use a “recursive” method which employs a subprogram
which calls itself. This may have an advantage in clarifying the algorithm, and/or in reducing
the round off error associated with the computations. For example, in computer graphics Bernstein
Polynomials are often used to display curves and surfaces efficiently by using a recursive definition
in calculating their value at a point.
The Greatest Common Divisor evaluation can also be stated in terms of a recursive function, say
gcd, having max and min as its initial two arguments. The following pseudocode defines the
function:
gcd(max, min) is
a) max if min = 0, otherwise
b) gcd(min, remainder of max divided by min) if min > 0
Also implement this version and verify that it gives the same result as the Eulerian Algorithm. Note
that F90 requires the use of the word ”recursive” when defining the subprogram statement block.
For example,
c 2001 J.E. Akin 101
11 It is not uncommon for data files to be prepared with embedded tabs. Since it is a non-printing
control character you can not see it in a listing. However, if you read the file expecting an integer,
real, or complex variable the tab will cause a fatal read error. So one needs a tool to clean up such
a file.
Write a program to read a file and output a duplicate copy, except that all tabs are replaced with a
single space. One could read a complete line and check its characters, or read the file character by
character. Remember that C++ and F90 have opposite defaults when advancing to a new line. That
is, F90 advances to the next line, after any read or write, unless you include the format control,
advance = ’no’, while C++ does not advance unless you include the new line control, “<<
endl”, and C does not advance unless you include the new line control, “\n”.
12 Engineering data files consisting of discrete groups of variable types often begin with a control line
that lists the number of rows and columns of data, of the first variable type, that follow beginning
with the next line. At the end of the data block, the format repeats: control line, variable type
data block, etc. until all the variable types are read (or an error occurs where the end of file is
encountered). Write a program that reads such a file which contains an integer set, a real set, and a
second real set.
13 Neither C++ or F90 provides an inverse hyperbolic tangent function. Write such a function, called
arctanh. Test it with three different arguments against the values given by M ATLAB.
14 Often if one is utilizing a large number of input/output file units it may be difficult to keep up with
which one you need. One approach to dealing with that problem may be to define a unit Class
or to create an units Module to provide functionality and global access to file information. In
the latter case assume that we want to provide a function to simply find a unit number that is not
currently in use and utilize it for our input/output action:
interface
function get next io unit () result (next)
integer :: next ! the next available unit number
end function get next io unit
end interface
Use the Fortran INQUIRE statement to build such a utility. If you are familiar with Matlab you
will see this is similar to its fopen feature.
c 2001 J.E. Akin 102
Chapter 5
5.1 Introduction
In Section 1.7 we outlined procedures that should be considered while conducting the object-oriented
analysis and object-oriented design phases that are necessary before the OOP can begin. Here we will
expand on those concepts, but the reader is encouraged to read some of the books on those subjects.
Many of the references on OOA and OOD rely heavily on detailed graphical diagrams to describe the
classes, their attributes ans states, and how they interact with other classes. Often those OO methods do
not go into any programming language specific approaches. Our interest is on OOP so we usually will
assume that the OOA and OOD have been completed and supplied to us as a set of tables that describe the
application, and possibly a software interface contract. Sometimes we will use a subset of the common
OO methods diagrams to graphically represent the attributes and members of our classes. Since they
being used for OOP the graphical representations will contain, in part, the intrinsic data type descriptions
of the language being employed, as well as the derived types created with them.
Behavior
What questions should it be able to answer?
3
What is the volumetric material removal rate? (mm /s)
Interfaces
What entities need to be input or output?
Material data
Torque produced
Power
Area : A = d2 =4 (mm2 )
Angular velocity : ! ; 1 rev=min = 260 rad/s (rad/s)
Material removal rate : M = A feed ! (mm3 /s)
Power : P =mu=T ! (W)
Torque : T = P=! ; 1 m = 1000 mm (Nmm)
Diameter : d (mm)
Feed rate : feed (mm/rev)
Material dissipation : u (Ws/mm3 )
The full implementation of the drill class is given in Fig. 5.3, and a main program to test the drill
class is given in Fig. 5.4. When we wrote the manual constructor, Drill , in this example we chose to
utilize the intrinsic constructor Drill (in lines 18 and 21) rather than including lines to assign values to
each of the components of our data type. If at some later time we add or delete a component in the type
declaration then the number of required arguments for the intrinsic constructor would also change. That
would require the revision of all members that used the intrinsic constructor. An advantage of the object-
oriented approach to programming is that we know that all such routines (that can access the intrinsic
constructor) are encapsulated within this class declaration module, and we can be sure that no other code
segments must be changed to remain consistent with the new version. That is, OOP helps with code
maintance.
to uniquely define the location, which we will refer to as the Global Position. Here we see that the
two Position Angle object values are a ”Part-Of” the Global Position class, or we can say that
a Global Position ”Has-A” Position Angle.
The sort of relationships between classes that we have noted above are quite common and relate to the
concept of inheritance as a means to reuse code. In an ”Is-A” relationship, the derived class is a variation
of the base class. Here the derived class Position Angle forms an ”Is-A” relation to the base class,
Angle. In a ”Has-A” relationship, the derived class has an attribute or property of the base class. Here
the derived class of Global Position forms a Has-A relation to its base class of Position Angle.
Also, the Great Arc class forms a ”Has-A” relation to the Global Position class.
Looking back at previous classes, in Chapter 3, we observe that the class Student ”Is-A” variation
of the class Person and the class Person forms at least one ”Has-A” relationship with the class Date.
In general we know that a graduate student is a ”Kind-Of” student, but not every student is a graduate
student. This subtyping, or ”Is-A” relationship is also called interface inheritance. Likewise, complicated
classes can be designed from simpler or composition inheritance.
The OO Analysis Tables for the classes of Great Arc, Global Position, Position Angle,
and Angle are given in Tables 5.3 through 5.6, respectively. Historically people have specified lati-
tude and longitude mainly in terms of whole (integer) degrees, minutes, and seconds. Sometimes you
find navigation charts that give positions in whole degrees and decimal minutes. Today GPS data are
being used for various high accuracy positioning such as land surveys, or the control of robots as they
move over distances of a few meters. The latter will clearly need decimal seconds values in their con-
structor. Thus, we will create a number of constructors for the position angles. In the next chapter we
will review how to access any of them, based on the signature of their arguments, through the use of
a single polymorphic routine name. These considerations and the OOA tables lead to the construction
of the corresponding set of OO Design Tables given in Tables 5.7 through 5.10. Those OOD tables
could lead to software interface contracts to be distributed to the programming groups. When combined
and tested they yield the corresponding class modules which are shown for the classes Angle, Posi-
tion Angle, Global Position, and Great Arc in Figs. 5.6 to 5.12, respectively. They in turn
are verified by the main program given in Fig. 5.13 along with its output.
Behavior
What questions should it be able to answer?
What is the (smallest) great arc between the points
Relationships
What are its related classes?
Has-A pair of Global Positions
Interfaces
What entities need to be input or output?
The distance between two positions.
Attributes
What knowledge does it possess or require?
Latitude (degrees, minutes, seconds, and direction)
Behavior
What questions should it be able to answer?
What is the latitude of the location
Relationships
What are its related classes?
Part-Of GreatArc
Interfaces
What entities need to be input or output?
The latitude and longitude, and a position name.
Direction (N or S or E or W)
Behavior
What questions should it be able to answer?
What is its magnitude and direction
Relationships
What are its related classes?
Part-Of Global Positions
Is-A Angle
Interfaces
What entities need to be input or output?
None
Attributes
What knowledge does it possess or require?
Signed value (radians)
Behavior
What questions should it be able to answer?
What is the current value
Relationships
What are its related classes?
Base Class for Position Angle
Interfaces
What entities need to be input or output?
None
Attributes
Name Type Private Description
latitude Position Angle Y Latitude
longitude Position Angle Y Longtitude
name characters Y Point name
Behavior
Name Private Description
Global Position N Constructor for d-m-s pairs and point name
set Lat and Long at N Constructor for lat-long-name set
get Latitude N Return latitude of a point
get Longitude N Return longitude of a point
set Latitude N Insert latitude of a point
set Longitude N Insert longitude of a point
Data
Name Description
None
Interfaces
Name Description
List Position Print name and latitude, longitude of a position
Attributes
Name Type Private Description
rad real Y Radian measure of the angle (rad)
Behavior
Name Private Description
Angle N A generic constructor
List Angle N List angle value in radians and degrees
Data
Name Description
Deg per Rad Unit conversion parameter
real rad
real Deg_Per_Rad
Angle Angle_
Angle List_Angle
Angle Angle
integer deg
integer min
real sec
character dir
Position_Angle Default_Angle
Position_Angle Decimal_min
Position_Angle Decimal_sec
Position_Angle Int_deg
Position_Angle Int_deg_min
Position_Angle Int_deg_min_sec
Position_Angle List_Position_Angle
Position_Angle Read_Position_Angle
real to_Decimal_Degrees
real to_Radians
Position_Angle Position_Angle
Global_Position Class
Position_Angle latitude
Position_Angle longitude
character name
Global_Position Global_Position_
Global_Position set_Lat_and_Long_at
Position_Angle get_Latitude
Position_Angle get_Longitude
Position_Angle set_Latitude
Position_Angle set_Longitude
Global_Position List_Position
Global_Position Global_Position
Global_Position point_1
Global_Position point_2
real arc
real Earth_Radius_Mean
real m_Per_Mile
Great_Arc Great_Arc_
real get_Arc
Great_Arc List_Great_Arc
real List_PT_to_Pt
Great_Arc Great_Arc
6.1 Introduction
As we have seen earlier in our introduction to OOP inheritance is a mechanism for deriving a new class
from an older base class. That is, the base class, sometimes called the super class, is supplemented or
selectively altered to create the new derived class. Inheritance provides a powerful code reuse mechanism
since a hierarchy of related classes can be created that share the same code. A class can be derived from
an existing base class using the module construct illustrated in Fig. 6.1.
We note that the inheritance is invoked by the USE statement. Sometimes an inherited entity (attribute
or member) needs to be slightly amended for the purposes of the new classes. Thus, at times one may
want to selectively bring into the new class only certain entities from the base class. The modifier ONLY
in a USE statement allows one to select the desired entities from the base class as illustrated below in
Fig. 6.2. It is also common to develop name conflicts when combining entities from one or more related
classes. Thus a rename modifier, =>, is also provided for a USE statement to allow the programmer to
pick a new local name for an entity onherited from the base class. The form for that modifier is given in
Fig. 6.3.
It is logical to extend any or all of the above inheritance mechanisms to produce multiple inheritance.
Multiple Inheritance allows a derived class to be created by using inheritance from more than a single
base class. While multiple inheritance may at first seem like a panacea for efficient code reuse, experi-
ence has shown that a heavy use of multiple inheritance can result in entity conflicts and be otherwise
counterproductive. Nevertheless it is a useful tool in OOP. In F90 the module form for selective multiple
inheritance would combine the above USE options in a single module as illustrated in Fig. 6.4.
Note that both empl1 and empl2 are each an instance of a class, and therefore they are objects and thus
distinctly different from a class.
Next we deal with a manager which Is-A “kind of” employee. One difference is that some managers
may be paid a salary rather than an hourly rate. Thus we have the Manager class inherit the attributes
of the Employee class and add a new logical attribute isSalaried which is true when the manager is
salary based. To support such a case we must add a new member setSalaried which can turn the new
attribute on or off, and a corresponding member payM that uses the isSalaried flag when computing
the pay. The class Manager module is shown in Fig. 6.9 Note that the constructor Manager defaults
to an hourly worker (line 33) and it uses the inherited employee constructor (line 31). Figure 6.10 shows
a test program to validate the manager class (and indirectly the employee class). It defines a salaried
manager, mgr1, an hourly manager mgr2, and prints the name and weekly pay for both. (Verify these
weekly pay amounts.)
With these two classes we have mainly used different program names for members that do similar
things in each class (the author’s preference). However, many programmers prefer to use a single member
name for a typical operation, regardless of the class of the operand. We also restricted all the attributes
to private and allowed all the members to be public. We could use several alternate approaches to
building our Employee and Manager classes. For example, assume we want a single member name
called pay to be invoked for an employee, or manager (or executive). Furthermore we will allow the
attributes to be public instead of private. Lowering the access restrictions to the attributes makes
it easier to write an alternate program, but it is not a recommended procedure since it breaks the data
hiding concept that has been shown to be important to OO software maintenance and reliability. The
alternate Employee and Manager classes are shown in Figs. 6.11 and 6.12, respectively. Note that
they both have a pay member but their arguments are of different classes and their internal calculations
are different. Now we want a validation program that will create both classes of individuals, and use
a single member name, PrintPay, to print the proper pay amount from the single member name pay.
This can be done in different ways. One problem that arises in our plan to reuse the code in the two
alternate class modules is that neither contained a pay printing member. We will need two new routines,
PrintPayEmployee and PrintPayManager, and a generic or polymorphic interface to them. We have
at least three ways to do this. One way is to place the two routines in an external file (or external to main
if in the same file), leave the two class modules unchanged, and have the main program begin with (or
INCLUDE) an external interface prototype. This first approach to main is shown in Fig. 6.13. Note that
the two new external routines must each use their respective class module.
A second approach would be to have the two new routines become internal to the main, after line
30, and occur before end program. Another change would be that each routine would have to omit its
use statement (such as lines 34 and 41). Why? Because they are now internal to main and it has already
made use of the two classes (in line 2). That approach is shown in Figs. 6.13
A third approach would be the most logical and consistent with OOP principles. It is to make all the
class attributes private, place the print members in each respective class, insert a single generic name
interface in each class, and modify the main program to use the polymorphic name regardless of the class
of the argument it acts upon. The improved version of the classes are given below in Figs. 6.14, 6.15, and
6.16. Observe that generic interfaces for PrintPay and getName have been added, but that we could
not do that for a corresponding setData; do you know why? A final improvement will be given as an
assignment.
6.3 Polymorphism
Fortran 90 and 95 do not include the full range of polymorphism abilities that one would like to have in
an object-oriented language. It is expected that the Fortran 2000 standard will add those abilities.
Some of the code “re-use” features can be constructed through the concept of subprogram “tem-
plates,” which will be discussed below. The lack of a standard “Is A” polymorphism can be overcome in
F90/95 by the use of the SELECT CASE feature to define “sub-types” of objects. This approach of sub-
typing programming provides the desired additional functionality, but it is clearly not as easy to change
or extend as an inheritance feature built into the language standard. A short example will be provided.
6.3.1 Templates
One of our goals has been to develop software that can be reused for other applications. There are some
algorithms that are effectively independent of the object type on which they operate. For example, in a
sorting algorithm one often needs to interchange, or swap, two objects. A short routine for that purpose
follows:
subroutine swap integers (x, y)
implicit none
integer, intent(inout) :: x, y
integer :: temp
temp = x
x = y
y = temp
end subroutine swap integers
Observe that in this form it appears necessary to have one version for integer arguments and another for
real arguments. Indeed we might need a different version of the routine for each type of argument that
you may need to swap. A slightly different approach would be to write our swap algorithm as:
subroutine swap objects (x, y)
implicit none
type (Object), intent(inout) :: x, y
type (Object) :: temp
temp = x
x = y
y = temp
end subroutine swap objects
which would be a single routine that would work for any Object, but it has the disadvantage that one
find a way to redefine the Object type for each application of the routine. That would not be an easy
task. (While we will continue with this example with the algorithm in the above forms it should be noted
that the above approaches would not be efficient if x and y were very large arrays or derived type objects.
In that case we would modify the algorithm slightly to employ pointers to the large data items and simply
swap the pointers for a significant increase in efficiency.)
Consider ways that we might be able to generalize the above routines so that they could accept and
swap any specific type of arguments. For example, the first two versions could be re-written in a so called
template form as:
subroutine swap Template$ (x, y)
implicit none
Template$, intent(inout) :: x, y
Template$ :: temp
temp = x
x = y
y = temp
end subroutine swap Template$
In the above template the dollar sign ($) was includes in the “wild card” because while it is a valid member
of the F90 character set it is not a valid character for inclusion in the name of a variable, derived type,
function, module, or subroutine. In other words, a template in the illustrated form would not compile, but
such a name could serve as a reminder that its purpose is to produce a code that can be compiled after the
“wild card” substitutions have been made.
With this type of template it would be very easy to use a modern text editor to do a global substitution
of any one of the intrinsic types character, complex, double precision, integer, logi-
cal, or real for the “wild card” keyword Template$ to produce a source code to swap any or all of
the intrinsic data types. There would be no need to keep up with all the different routine names if we
placed all of them in a single module and also created a generic interface to them such as:
module swap library
implicit none
interface swap ! the generic name
module procedure swap character, swap complex
module procedure swap double precision, swap integer
module procedure swap logical, swap real
end interface
contains
subroutine swap characters (x, y)
. . .
end subroutine swap characters
subroutine swap . . .
. . .
end module swap library
The use of a text editor to make such substitutions is not very elegant and we expect that there may
be a better way to pursue the concept of developing a reuseable software template. The concept of a text
editor substitution also fails when we go to the next logical step and try to use a derived type argument
instead of any of the intrinsic data types. For example, if we were to replace the “wild card” with our
previous type (chemical element) that would create:
subroutine swap type (chemical element) (x,y)
implicit none
This would fail to compile because it violates the syntax for a valid function or subroutine name, as well
as the end function or end subroutine syntax. Except for the first and last line syntax errors this would be
a valid code. To correct the problem we simply need to add a little logic and omit the characters type
( ) when we create a function, module, or subroutine name that is based on a derived type data entity.
Then we obtain
subroutine swap chemical element (x,y)
implicit none
type (chemical element), intent (inout)::x,y
type (chemical element) ::temp
temp = x
x = y
y = temp
end subroutine swap chemical element
Some programmers criticize F90/95 for not offering this ability as part of the standard. A few C++ pro-
grammers criticize templates and advise against their use. Regardless of the merits of including template
pre-processors in a language standard it should be clear that it is desirable to plan software for its efficient
reuse.
With F90 if one wants to take advantage of the concepts of templates then the only choices are to carry
out a little text editing or develop a pre-processor with the outlined capabilities. The former is clearly
the simplest and for many projects may take less time than developing such a template pre-processor.
However, if one makes the time investment to produce a template pre-processor one would have a tool
that could be applied to basically any coding project.
6.3.2 Subtyping Objects (Dynamic Dispatching)
One polymorphic feature missing from the Fortran 90 standard (but expected in Fortran 2000) that is
common to most object oriented languages is called run-time polymorphism or dynamic dispatching. In
the C++ language this ability is introduced in the so-called “virtual function.” To emulate this ability
is quite straightforward in F90 but is not elegant since it usually requires a group of if-elseif statements
or other selection processes. It is only tedious if the inheritance hierarchy contains many unmodified
subroutines and functions. The importance of the lack of a standardized dynamic dispatching depends on
the problem domain to which it must be applied. For several applications demonstrated in the literature
the alternate use of subtyping has worked quite well and resulted in programs that have been shown to
run several times faster than equivalent C++ versions.
We implement dynamic dispatching in F90 by a process often called subtyping. Two features must be
constructed to do this. First, a pointer object, which can point to any subtype member in an inheritance
hierarchy, must be developed. Second, an if-elseif or other selection method is developed to serve as a
dispatch mechanism to select the unique appropriate procedure to be executed based on the actual class
referenced in the controlling pointer object. This subtyping process is also referred to as implementing
a polymorphic class. Of course, the details of the actual dispatching process can be hidden from the
procedures that utilize the polymorphic class.
This process will be illustrated buy creating a specific polymorphic class, called
Is A Member Class, which has polymorphic procedures named new, assign, and display. They will
construct a new instance of the object, assign it a value, and list its components. The minimum example
of such a process requires two members and is easily extended to any number of member classes. We
begin by defining each of the subtype classes of interest.
The first is a class, Member 1 Class, which has two real components and the encapsulated func-
tionality to construct a new instance and another to accept a pointer to such a subtype and display related
information. It is shown in Fig. 6.17. The next class, Member 2 Class, has three components: two
reals and one of type Member 1. It has the same sort of functionality, but clearly must act on more
components. It has also inherited the functionally from the Member 1 Class; as displayed in Fig. 6.18.
The polymorphic class is called the Is A Member Class and is shown in Fig. 6.19. It includes all
of the encapsulated data and function’s of the above two subtypes by including their use statements. The
necessary pointer object is defined as an Is A Member type that has a unique pointer for each subtype
member (two in this case). It also defines a polymorphic interface to each of the common procedures to
be applied to the various subtype objects. In the polymorphic function assign the dispatching is done very
simply. First, all pointers to the family of subtypes are nullified, and then the unique pointer component
to the subtype of interest is set to point to the desired member. The dispatching process for the display
procedure is different. It requires an if-elseif construct that contains calls to all of the possible subtype
members (two here) and a failsafe default state to abort the process or undertake the necessary exception
handling. Since all but one of the subtype pointer objects have been nullified it employs the associated
intrinsic function to select the one, and only, procedure to call and passes the pointer object on to that
procedure. The validation of this dynamic dispatching through a polymorphic class is shown in Fig. 6.20.
There a target is declared for reach possible subtype and then each of them is constructed and sent on
to the other polymorphic functions. The results clearly show that different display procedures were used
depending on the class of object supplied as an argument. It is expected that the new Fortran 2000
standard will allow such dynamic dispatching in a much simpler fashion.
6.4 Exercises
1. Write a main program that will use the Class X and Class Y, given below, to invoke each of the f(v)
routines and assign a value of 66 to the integer component in X, and 44 to the integer component in Y.
(Solution given.)
module class X
public :: f
type X ; integer a; end type X
contains ! functionality
subroutine f(v); type (X ), intent(in) :: v
print *,"X f() executing"; end subroutine
end module class X
module class Y
use class X, X f => f ! renamed
public :: f
type Y ; integer a; end type Y ! dominates X a
contains ! functionality, overrides X f()
subroutine f(v); type (Y ), intent(in) :: v
print *,"Y f() executing"; end subroutine
end module class Y
2. Create the generic interface that would allow a single constructor name, Position Angle , to be
used for all the constructors given in the previous chapter for the class Position Angle. Note that this
is possible because they all had unique argument signatures. Also provide a new main program to test
this polymorphic version.
3. Modify the last Manager class by deleting the member setDataM and replace its appearance in the
last main with an existing constructor (but not used) in that class. Also provide a generic setData
interface in the class Employee as a nicer name and to allow for other employees, like executives, that
may have different kinds of attributes that may need to be set in the future. Explain why we could not use
setDataM in the generic setData.
4. The final member setDataE in Employee is actually a constructor and the name is misleading since
it does not just set data values, it also builds the name. Rename setDataE to the constructor notation
Employee and provide a new member in Employee called setRateE that only sets the employee pay
rate.
OO Data Structures
7.2 Stacks
A stack is a data structure where access is restricted to the last inserted object. It is referred to as a last-in
first-out (LIFO) container. In other words, a stack is a container to which elements may only be inserted
or removed at one end of the container, called the top of the stack. It behaves much like a pile of dinner
plates. You can place a new element on the pile (widely known as a push), remove the top element from
the pile (widely known as a pop), and identify the element on the top of the pile. You can also have the
general concept of an empty pile, and possibly a full pile if it is associated with some type of restrictive
container. Since at this point we only know about using arrays as containers we will construct a stack
container by using an array.
Assume that we have defined the attributes of the “Object” that is to use our container by building a
module called object type. Then we could declare the array implementation of a stack type to be:
type stack
private
integer :: size ! size of array
integer :: top ! top of stack
type (Object) :: a(limit) ! stack items array
end type stack
end module stack type
In the interface we see that some of the member services ( is Stack Empty and is Stack Full)
are independent of the contained objects. Others (pop from Stack and push on Stack) explicitly
depend on the Object utilizing the container. Of course, the constructor (here make Stack) always
indirectly relates to the Object being contained in the array. The full details of a Stack class are given in
Fig. 7.1.
For a specific implementation test we will simply utilize objects that have a single integer attribute.
That is, we define the object of interest by a code segment like:
module object type
type Object
integer :: data ; end type ! one integer attribute
end module object type
Obviously, there are many other types of objects that one may want to create and place in a container like
a stack. At the present one would have to edit the above segment to define all the attributes of the object.
(Begin to think about how you might seek to automate such a process.) The new Stack class is tested
in Fig. 7.2, while a history of the example stack is sketched in Fig. 7.3. The only part of that code that
depends on a specific object is in line 7 where the (public) intrinsic constructor, Object, was utilized
rather that some more general constructor, say Object .
In Fig. 7.1 note that we have used an alternate syntax and specified the type of function result (logical,
Object, or stack) as a prefix to the function name (lines 16, 28, 36, 40). The author thinks that the form
used in the interface contract is easier to read and understand since it requires an extra line of code,
however some programmers prefer the condensed style of Fig. 7.1. Later we will examine an alternate
implementation of a stack by using a linked list.
The stack implementation shown here is not complete. For example, some programmers like to in-
clude a member, say show Stack top, to display the top element on the container without removing it
from the stack. Also we need to be concerned about pre-conditions that need to be satisfied for a member
and may require that we throw an exception message. You can not pop an item off of an empty stack, nor
can you push an item onto the top of a full stack. Only the member pop from Stack does such pre-
condition checking in the sample code. Note that members is Stack Empty and is Stack Full
Full ? F F F T F F F F
Empty ? T F F F F F T T
Error ? N N N N N N N Y
Stack: | | |4| |5| |6| |5| |4| | | | |
| | | | |4| |5| |4| | | | | | |
| | | | | | |4| | | | | | | | |
--- --- --- --- --- --- --- ---
(Line) 9 12 13 14 17 20 23 26
<
E ----- > Top
|D|
|C| ---------
|B| Front <---- A B C D E <---- Rear
|A| ---------
---
a) Stack b) Queue
because now there is no way to release memory for X ptr. To avoid this we need to free the memory
before the pointer is nullified, so the segment becomes:
real, pointer :: X ptr (:)
allocate ( X ptr(Big number) )
. . . ! use X ptr
deallocate ( X ptr ) ! memory released
nullify ( X ptr )
Remember that in F95 the memory is automatically deallocated at the end of the scope of the variable,
unless one retains the variable with a SAVE statement (and formally deallocates it elsewhere).
7.4.1 Singly Linked Lists
We begin the study of the singly linked list by showing the notations employed in Fig. 7.7. From experi-
enced we have chosen to have a dummy first node, called first, to simplify our algorithms so that a list
is never truly empty. Also as we scan through a list we will use one pointer, called current, to point to
the current object in the list and a companion, called previous, to point to the directly preceding object
(if any). If no objects have been placed in the list then both of these simply point to the first node.
The end of the list is denoted by the next pointer attribute taking on the null value. To insert or delete
objects one must be able to rank two objects. This means that in order to have a generic linked list one
must overload the relational operators, (< and ==) when the object to be placed in the container is de-
fined. Since most objects have different types of attributes the overloading process is clearly application
<
-----------
| First | --- Singly Linked List % First (An empty object instance)
----------- -----------
| Next --+---- > <
| Obj 1 | --- Previous pointer
> <
----------- ----------- -----------
| Next --+---- | Obj m | --- Current pointer
----------- ----------- -----------
| Next --+---- | Obj n > |
>
----------- -----------
| Next --+---- Null
----------- -----
---
-
b) List of singly linked nodes
<
-----------
| | --- Singly Linked List % First
| First <
| --- Previous pointer
| <
| --- Current pointer
>
-----------
| Next --+----- Null
----------- -----
---
-
c) An ‘‘empty’’ (one node) singly linked list
dependent. The process for inserting an object is sketched in Fig. 7.8 while that for deleting an object is
in Fig. 7.9.
The Singly Linked List class is given in Fig. 7.10. It starts with the definition of a singly linked
node (lines 4-8) that has an object attribute and a pointer attribute to locate the next node. Then a list
is begun (lines 10–13) by creating the dummy first node that is consider to represent an empty list. The
object deletion member must employ an overloaded operator (line 28), as must the insertion member
(line 52). Observe that a list never gets “full”, unless the system runs out of memory. The empty list test
member (line 62) depends on the pointer status, but is independent of the objects stored. The constructor
for a list (line 68) simply creates the first node and nullifies it. The printing member (line 74) is called
an iterator since it runs through all objects in the list. The testing program for this container type and
its output results are given in Fig. 7.11. In order to test such a container it is necessary to have an object
type defined. Here an object with a single integer value was selected, and thus it was easy to overload the
relational operators with a clear meaning as shown in Fig. 7.12.
DELETE
with it. The printing member (line 90) is called an iterator since it runs through all objects in the list.
The testing program for this container type and its output results are given in Fig. 7.15. Here an object
with a single integer value was selected, and thus it was easy to overload the relational operators with a
clear meaning as shown in Fig. 7.12.
defined in line 4. The hardware transportability of this code is assured by establishing the required con-
stant record with the intrinsic given in line 10. It is then used in opening the file, which is designated
as a direct file in line 12. Lines 16–24 create the object record numbers in a sequential fashion. They
also define the new object to be stored with each record. In lines 27–32 the records are accessed in a
backwards order, but could have been accessed in any random or partial order. In line 35 a random object
is given a new value. Finally, the changes are output in a sequential order in lines 37–42. Sample input
data and program outputs are included as comments at the end of the program.
c 2001 J.E. Akin 155
Action C++a F90 F77 M ATLAB
Pre-allocate
integer A[100] INTEGER A(100) INTEGER A(100) A(100)=0
Initialize for j=0,99 A=12 do 5 J=1,100 for j=1:100
A[j]=12 A(J)=12 A(j)=12
end 5 continue end
Both Fortran and C++ require you to specify the maximum range of each subscript of an array before
the array or its elements are used. MATLAB does not have this as a requirement, but pre-allocating the
array space can drastically improve the speed of M ATLAB, as well as making much more efficient use of
the available memory. If you do not pre-allocate M ATLAB arrays, then the interpreter must check at each
step to see if a position larger than the current maximum has been reached. If so, the maximum value is
increased and memory is found to store the new element. Thus, failure to pre-allocate M ATLAB arrays is
permissible but inefficient.
For example, assume we want to set a vector A having 100 elements, to an initial value of 12. The
procedures are compared in Table 8.1. This example could have also been done efficiently in F90 and
M ATLAB by using the colon operator: A(1:100) = 12. The programmer should be alert for the chance
to replace loops with the colon operator: it’s more concise while retaining readability and executes more
quickly. The joys of the colon operator are described more fully in x8.1.3 (page 159).
Array operations often use special characters and operators. Fortran has “implied” DO loops associ-
ated with its array operations (see x4.3.2, page 60). Similar features in M ATLAB and F90 are listed in
Table 8.2.
Fortran has always had efficient array handling features, but until the release of F90 it was not easy
to dynamically create and release the memory space needed to store arrays. That is a useful feature for
arrays that require large amounts of space but are not needed for the entire life of the program. F90 has
several types of arrays, with the most recent types being added to allow the use of array operations, and
intrinsic functions similar to those in M ATLAB. Without getting into the details of the F90 standards and
terminology we will introduce the most common array usages in a historical order:
F77: Constant Arrays, Dummy Dimension Arrays, Variable Rank Arrays
F90: Automatic Arrays, Allocatable Arrays.
These different approaches all have the common feature that memory space needed for an array must be
set aside (allocated) before any element in the array is utilized.
The new F90 array features include the so-called automatic arrays. An automatic array is one that
appears in a subroutine, or function and has its size, but not its name, provided in the argument list of the
subprogram. For example,
subroutine auto A B (M, N, Other arguments)
implicit none
integer :: M, N
c 2001 J.E. Akin 156
real :: A(M, N), B(M) ! Automatic arrays
! Create arrays A & B and use them for some purpose
...
end subroutine auto A B
would automatically allocate space for the M rows and N columns of the array A and for the M rows of
array B. When the purpose of the subroutine is finished and it “returns” to the calling program the array
space is automatically released, and the arrays A and B cease to exist. This is a useful feature, especially
in Object Oriented programs. If the system does not have enough space available to allocate for the array
the program stops and gives an error message to that effect. With today’s large memory computers that
is unlikely to occur except for the common user error where the dimension argument is undefined.
An extension of this concept that allows more flexibility and control is the allocatable array. An
allocatable array is one that has a known rank (number of subscripts), but an initially unknown extent
(range over each subscript). It can appear in any program, function, or subroutine. For example,
program make A B ! Allocatable arrays
implicit none
real, allocatable :: A(:,:), B(:) ! Declares rank of each
integer :: M, N ! Row and column sizes
integer :: A B Status ! Optional status check
print *,"Enter the number of rows and columns: "
read *, M, N ! Now know the (default) extent of each subscript
allocate ( A(M, N), B(M), stat = A B Status ) ! dynamic storage
! Verify that the dynamic memory was available
if ( A B Status /= 0 ) stop "Memory not available in make A B"
! Create arrays A & B and use them for some purpose
...
deallocate (A, B) ! free the memory space
! Do other things
...
end program make A B
would specifically allocate space for the M rows and N columns of the array A and for the M rows of
array B, and optionally verify that the space was available. When the purpose of the arrays are finished
the space is specifically released, and the arrays A and B cease to exist. The optional status checking
feature is useful in the unlikely event that the array is so large that the system does not have that much
dynamic space available. Then the user has the option of closing down the program in some desirable
way, or simply stopping on the spot.
The old F77 standard often encouraged the use of dummy dimension arrays. The dummy dimension
array is one that appears in a subroutine, or function and has its size and its name provided in the argument
list of the subprogram. For example,
subroutine dummy A B (M, N, A, B, Other things)
implicit none
integer :: M, N
real :: A(M, N), B(M) ! dummy arrays
! Create arrays A & B and use them for some purpose
...
end subroutine dummy A B
would imply that existing space for the M rows and N columns of the array A and for the M rows of array
B (or more) was declared or allocated in the calling program. When the purpose of the subroutine is
finished and it “returns” to the calling program the space in the calling program for the arrays A and B
continues to exist until the declaring program unit terminates.
Of course the use of constant dimensioned arrays is always allowed. The constant dimension array is
one that appears in any program unit and has integer constants, or integer parameter variables (preferred)
as given extents for each subscript of an array. For example,
program main
implicit none
integer, parameter :: M max=20, N max=40 ! Maximum expected
integer :: Days per Month(12) ! Constant array
integer :: M, N ! User sizes
real :: A(M max, N max), B(M max) ! Constant arrays
print *,"Enter the number of rows and columns: "
read *, M, N ! The user extent of each subscript
! Verify that the constant memory is available
if ( M > M max ) stop "Row size exceeded in main"
if ( N > N max ) stop "Column size exceeded in main"
! Create arrays A & B and use them for some purpose
call dummy A B (M, N, A, B, Other things) ! dummy arrays
c 2001 J.E. Akin 157
Action F90 M ATLAB
Define sizea
integer :: A (2, 3) A(2,3)=0;
Enter rows A(1,:)=(/1,7,-2/) A=[1,7,-2;
A(2,:)=(/3,4,6/) 3,4,6];
...
end program main
subroutine dummy A B (M, N, A, B, Other things) ! dummy arrays
implicit none
integer :: M, N
real :: A(M, N), B(M)
! Create arrays A & B and use them for some purpose
...
end subroutine main
In general it is considered very bad style to use integer constants, like 12, in a dimension, or in a DO
loop control, except for the unusual case where its meaning is obvious, and where you never expect to
have to change the number. In the example declaration:
integer :: Days per Month(12) ! Constant array
It is obvious that we are thinking about 12 months per year and that we do not expect the number of
months per year to ever change in other potential applications of this program.
8.1.1 Initializing Array Elements
Explicit lists of the initial elements in an array are allowed by C++, Fortran, and M ATLAB. M ATLAB is
oriented to enter element values in the way that we read, that is, row by row. Fortran and C also allow
array input by rows, but the default procedure is to accept values by ranging over its subscripts from left
to right. That is, both F90 and C++ read by columns as their default mode. For example, consider the
2 3
array " #
A = 13 74 2
6 :
This array could be typed as explicit input with the commands shown in Table 8.3. An alternative for F90
and M ATLAB is to define the full array by column order as a vector that is then reshaped into a matrix
with a specified number of rows and columns. The use of the RESHAPE operator is shown in Table 8.4.
Returning to the previous example, we see that the matrix A could have also been defined as
c 2001 J.E. Akin 158
To initialize the elements of an array to zero or unity, F90 and M ATLAB have special constructs or
functions that fill the bill. For example, for A to be zero and B to have unity elements, we could use the
following commands.
If we want to create a new array B with the first three even numbers, we would use implied loops.
Arrays can also be initialized by reading their element values from a stored data file. The two most
common types of files are ASCII (standard characters) and binary (machine language) files. ASCII files
are easy to read and edit, but binary files make more efficient use of storage, and are read or written much
faster than ASCII files. ASCII files are often denoted by the name extension of “dat”. Binary files are
denoted by the name extension “mat” in M ATLAB, while in Fortran the extension “bin” is commonly
employed.
For example, assume that the above A(2,3) array is to be initialized by reading its values from an
ASCII file created by a text editor and given the name of A.dat. Further, assume that we wish to
multiply all elements by 3 and store it as a new ASCII file. Then we could use read procedures like
those in Table 8.5 where the last M ATLAB command associated a file name and a file type with the desired
input/output (I/O) action. Fortran requires an OPEN statement to do this if the default I/O files (unit 5 to
read and unit 6 to write) are not used in the read or write.
8.1.2 Intrinsic Array Functions
Note that M ATLAB has intrinsic functions ones and zeros to carry out a task that F90 does with an op-
erator. Often the reverse is true. M ATLAB has several operators that in Fortran correspond to an intrinsic
function or a CALLed function. A comparison of the similar F90 and M ATLAB array mathematical oper-
ators are given in Table 8.5. They generally only differ slightly in syntax. For example, to transpose the
matrix A, the F90 construct is transpose(A) while in M ATLAB 1 3 5 it’s simply
1A’ .y In F90, the * operator
means, for matrices, term by term multiplication: when A= 2 4 6 and B= 3 5 6 , A*B yields 16 20
2 4 6 20 .
36
In M ATLAB, the same operation is expressed as A.*B. To multiply the matrices A and B, Fortran requires
the use of the intrinsic function matmul (i.e., matmul(A,B)) while M ATLAB uses the * operator (A*B).
Another group of commonly used functions that operate on arrays in Fortran90 and M ATLAB are
briefly described in Table 8.6. Both languages have several other functions of a more specialized nature,
but those in Table 8.6 are probably the most commonly used in programs.
Often one needs to truncate a real number in some special fashion. Table 8.7 illustrates how to do
that using some of the functions common to the languages of interest. That table also implies how one
can convert reals to integers and vice versa.
c 2001 J.E. Akin 159
c 2001
J.E. Akin
Description Equation Fortran90 Operator Matlab Operator Original Sizes Result Size
Scalar plus scalar c = a b c = a b c = a b; 1; 1 1; 1
Element plus scalar cjk =
ajk b c = a b c = a b; m; n and 1; 1 m; n
0
; m; n n; m
c
=
=
P k
k
Aik Bkj
Ak Bk
C
c
= matmul(A; B )
= sum(A B) c
C
= sum(A.
= A B;
B );
m; r and r; n
m; 1 and m; 1
m; n
1; 1
c = dot product(A; B ) c = A B
0
; m; 1 and m; 1 1; 1
Table 8.5: Array Operations in Programming Constructs. Lower case letters denote scalars or scalar elements of arrays. Matlab arrays are allowed a maximum of
two subscripts while Fortran allows seven. Upper case letters denote matrices or scalar elements of matrices.
You can also use the colon operator to extract smaller
1 7 arrays from larger ones. If we wanted to extract
= 2 , to get, respectively,
the second row and third column of the array, A 3 4 6
( )
= [3 4 6] = 2
G ; C
6 ;
c 2001 J.E. Akin 162
WHERE (logical array expression)
true array assignments
ELSEWHERE
false array assignments
END WHERE
WHERE (logical array expression)
true array assignment
One can often use colon operators to avoid loops acting on arrays to define new arrays. For example,
consider a square matrix
2
1 2 33
A = 64 4 5 6 75 :
7 8 9
We can flip it left to right to create a new matrix (in F90 syntax)
2
3 2 13
n:1:-1) = 4 6 5 4 5
6 7
B=A(:,
9 8 7
or flip it up to down
2
7 8 93
C=A(n:1:-1, :) = 4
6
4 5 6 75
1 2 3
or flip it up to down, then left to right
2
9 8 73
D = A (n:1:-1, n:1:-1) = 4
6
6 5 4 75 ,
3 2 1
where n =3 is the number of rows in the matrix A. In the M ATLAB syntax, the second and third
numbers would be interchanged in the colon operator. Actually, MATLAB has intrinsic operators to flip
the matrices so that one could simply write
B = fliplr(A); C = flipud(A); D = rot90(A);
Table 8.9: F90 Array Operators with Logic Mask Control. T and F denote true and false, respectively.
Optional arguments: b -- DIM & MASK, d -- DIM, m -- MASK, v -- VECTOR and DIM = 1 implies
for any rows, DIM = 2 for any columns, and DIM = 3 for any plane.
then, WHERE (A > B) B = A gives a new B = 17 34 58 . By default, M ATLAB always acts
on matrices and considers scalars a special case. Thus, it would employ the standard syntax, if A >
B,B=A, to do the same task.
A more sophisticated way to selectively pick subscripts of an array is to use a mask array. A mask
array is the same size and shape as the array on which it will act. It is a Boolean array: All its elements
have either true or false values. When associated with an operator, the operator will only act on those
elements in the original array whose corresponding mask location is true (i.e., .true. in Fortran, true in
C++ and 1 in M ATLAB and C). Fortran90 has several operations that allow or require masks (Table 8.9).
M ATLAB functions with the same name exist in some cases, as seen in Table 8.6. Usually, they correspond
to the F90 operator where the mask is true everywhere.
c 2001 J.E. Akin 164
ALL ANY COUNT
CSHIFT DOT PRODUCT EOSHIFT
MATMUL MAXLOC MAXVAL
MINLOC MINVAL PACK
PRODUCT REPEAT RESHAPE
SPREAD SUM TRANSFER
TRANSPOSE TRIM UNPACK
A general Fortran principle underlies the fact that the array mentioned in the WHERE mask may be
changed within the WHERE construct. When an array appears in the WHERE statement mask, the logical
test is executed first and the host system retains the result independent of whatever happens later inside
the WHERE construct. Thus, in the program fragment
integer, parameter :: n = 5
real :: x (n) = (/ (k, k = 1, n) /)
where (x > 0.0)
x = -x
end where
the sign is reversed for all elements of x because they all pass the initial logical mask. It is as if a classic
DO sequence had been programmed
do i = 1, n, 1
if (x(i) > 0.0) x(i) = -x(i)
end do
Thus, the new values for x are f 2, 4, 6, 8/30, 10/30 g rather than f2, 4, 6, 8/18, 10/18g. This standard-
conforming, but otherwise “unexpected”, result should raise a caution for the programmer. If one did not
want the above illustrated result, then it would be necessary to use the same mask of the WHERE as an
optional argument to SUM: sum(x, mask = x > 6.0). A lot of care needs to be taken to assure that
transformational intrinsics that appear in a WHERE construct use exactly the same mask.
8.1.5 User Defined Operators
In addition to the many intrinsic operators and functions we have seen so far, the F90 user can also define
new operators or extend existing ones. User defined operators can employ intrinsic data types and/or user
defined data types. The user defined operators, or extensions, can be unary or binary (i.e., have one or
two arguments). The operator symbol must be included between two periods, such as ‘.op.’. As an
example, consider a program to be used to create a shorthand notation to replace the standard F90 matrix
transpose and matrix multiplication functions so that we could write
B = .t. A
C = B .x. D
or C = (.t.A) .x. D
instead of B = TRANSPOSE(A)
C = MATMUL (B, D)
or C = MATMUL(TRANSPOSE (A), D)
c 2001 J.E. Akin 165
Operator Action Use Algebra
T
.t. transpose .t.A A
To do this, one must have a MODULE PROCEDURE to define the operator actions for all envisioned (and
incorrect) inputs and an INTERFACE OPERATOR that informs F90 what your operation symbol is.
Fig. 8.1 illustrates the code that would partially define the operator ‘.t.’. Note that while TRANSPOSE
accepts any type of matrix of any rank, our operator works only for real or integer rectangular arrays (of
rank 2). It would not transpose LOGICAL arrays or vectors. That oversight can be extended by adding
more functions to the interface.
If one works with matrices often, then one may want to define your own library of matrix operators.
Such operators are not standard in F90 as they are in M ATLAB, but can be easily added. To provide a
foundation for such a library, we provide a Matrix Operators module with the operators defined in
Table 8.11. The reader is encouraged to expand the initial support provided in that module.
c 2001 J.E. Akin 166
[ 1] MODULE Ops Example ! User defined matrix transpose example
[ 2]
[ 3] IMPLICIT NONE
[ 4] INTERFACE OPERATOR (.t.) ! transpose operator
[ 5] MODULE PROCEDURE Trans R, Trans I ! for real or integer matrix
[ 6] ! Remember to add logicals and vectors later
[ 7] END INTERFACE ! defining .t.
[ 8]
[ 9] CONTAINS ! the actual operator actions for argument types
[10]
[11] FUNCTION Trans R ( A ) ! defines .t. for real rank 2 matrix
[12] REAL, DIMENSION(:,:), INTENT(IN) :: A
[13] REAL, DIMENSION(SIZE(A,2), SIZE(A,1)) :: Trans R
[14] Trans R = TRANSPOSE (A)
[15] END FUNCTION Trans R ! for real rank 2 transpose via .t.
[16]
[17] FUNCTION Trans I ( A ) ! defines .t. for integer rank 2 matrix
[18] INTEGER, DIMENSION(:,:), INTENT(IN) :: A
[19] INTEGER, DIMENSION(SIZE(A,2), SIZE(A,1)) :: Trans I
[20] Trans I = TRANSPOSE (A)
[21] END FUNCTION Trans I ! for integer rank 2 transpose via .t.
[22]
[23] END MODULE Ops Example ! User defined matrix transpose example
[24]
[25] PROGRAM Demo Trans ! illustrate the .t. operator
[26] USE Ops Example ! module with user definitions
[27] IMPLICIT NONE
[28] INTEGER, PARAMETER :: M = 3, N = 2 ! rows, columns
[29] REAL, DIMENSION(M,N) :: A ; REAL, DIMENSION(N,M) :: B
[30]
[31] ! define A, test operator, print results
[32] A = RESHAPE ( (/ ((I*J , I=1,M), J=1,N) /), SHAPE(A) )
[33] B = .t. A
[34] PRINT *, ’MATRIX A’ ; CALL M print (A, M, N)
[35] PRINT *, ’MATRIX B’ ; CALL M print (B, N, M)
[36] ! Produces the result:
[37] ! MATRIX A
[38] ! RC 1 2
[39] ! 1 1.000 2.000
[40] ! 2 2.000 4.000
[41] ! 3 3.000 6.000
[42] !
[43] ! MATRIX B
[44] ! RC 1 2 3
[45] ! 1 1.000 2.000 3.000
[46] ! 2 2.000 4.000 6.000
[47] END PROGRAM Demo Trans
2
1 2 33
A = 64 4 5 6 75 Reverse = [321]
;
7 8 9
Flip left to right: 2
3 2 13
B=A(: , Reverse) = 4 6 5 4 5
6 7
9 8 7
Flip up to down: 2
7 8 93
C = A(Reverse, :) = 4 4 5 6 5
6 7
1 2 3
Flip up to down, left to right: 2
9 8 73
D = A (Reverse,Reverse) = 4 6 5 4 5
6 7
3 2 1
Figure 8.2: F90 and M ATLAB Vector Subscripts and Array Shifts.
c 2001 J.E. Akin 167
five = (/ 1 2 3 4 5 /)
! without a pad
three = eoshift(five,2) ! = (/ 3 4 5 0 0 /)
three = eoshift(five,-2) ! = (/ 0 0 1 2 3 /)
! with a pad
pad = eoshift(five,2,9) ! = (/ 3 4 5 9 9 /)
pad = eoshift(five,-2,9) ! = (/ 9 9 1 2 3 /)
2
3 1 23
E = A (:, Random) = 4
6
6 4 5 75 :
9 7 8
While the reshape option of F90 and M ATLAB allows the array elements to change from one rect-
angular storage mode to another, one can also move elements around in the fixed shape array by utilizing
the colon operators, or by the use of “shift operators.” The latter accept an integer to specify how many
locations to move or shift an element. A positive number moves an element up a column, a negative value
moves it down the column, and a zero leaves it unchanged. The elements that are moved out of the array
either move from the head of the queue to the tail of the queue (called a “circular shift”) or are replaced
by a user specified “pad” value (called an “end off shift”). If no pad is given, its value defaults to zero.
These concepts are illustrated for F90 in Figures 8.3 and 8.4.
8.1.7 Component Gather and Scatter
Often the equations governing a system balance principle are assembled from the relative contributions
of each component. When the answers for a complete system have been obtained, it is then possible to
recover the response of each component. The automation of these processes has six basic requirements:
1. a component balance principle written in matrix form,
2. a joint connectivity data list that defines where a given component type connects into the system,
3. a definition of a scatter operator that scatters the coefficients of the component matrices into
corresponding locations in the governing system equations,
4. an efficient system equation solver,
5. a gather operator to gather the answers from the system for those joints connected to a compo-
nent, and
6. a recovery of the internal results in the component.
The first of these is discipline-dependent. We are primarily interested in the gather-scatter operations.
These are opposites that both depend on the component connectivity list, which is often utilized as a
vector subscript. The number of rows in the component equations is less than the number of rows in the
assembled system, except for the special case where the system has only a single component. Thus, it
is the purpose of the gather-scatter operators to define the relation between a system row number and a
particular component row number. That is, they define the relation that defines the subset of component
:
unknowns, say Ve for component e, in terms of all the system unknowns, say V Ve e V. Here the
containment is defined by the component’s connection list and the number of unknowns per joint. If
there is only one unknown per joint, then the subset involves only the connection list. The above process
gathers the subset of component unknowns from the full set of system unknowns.
Let the list of joints or nodes connected to the component be called Le . The k th member in this list
contains the corresponding system node number, K: i.e. K = L e(k). Thus, for a single unknown per
c 2001 J.E. Akin 168
(2)
while in F90 or M ATLAB vector subscript form, it is simply V e = V(L e), for a single unknown per
joint. When there is more than one unknown per joint, the relation can be written in two ways.
We pick the one that counts (assigns equation numbers to) all unknowns at a joint before going on to
the next joint. Let the number of unknowns per joint be N. Then by deduction, one finds that the equation
number for the j -th unknown at the K th system node is
(
E K; j ) = (
N K 1) + j; 1
j N:
But to find which equation numbers go with a particular component, we must use the connection list
L e. For the k th local node, K = L e (k) and
( )= (
E k; j N L ( ) 1) +
e k j ; 1 j N :
If we loop over all nodes on a component, we can build an index list, say I e, that tells which equations
relate to the component.
INTEGER, ALLOCATABLE :: I e(:), V e(:)
ALLOCATE(I e(N * SIZE (L e)), V e (N*SIZE(L e)))
DO k = 1, SIZE(L e) ! component nodes
DO j = 1, N ! unknowns per node
LOCAL = N *(k-1) + j
SYSTEM = N *(L e (k) - 1) + j
I e (LOCAL) = SYSTEM
END DO ! on unknowns
END DO ! on local nodes.
or in vector subscript form V e = V(I e) for an arbitrary number of unknowns per joint.
To illustrate the scatter concept, consider a system shown in Figure 8.5, which has six components
and five nodes. If there is only one unknown at each joint (like voltage or axial displacement), then
the system equations will have five rows. Since each component is connected to two nodes, each will
contribute to (scatter to) two of the system equation rows. Which two rows? That is determined by the
connection list shown in the figure. For example, component (4) is joined to nodes 4 and 3. Thus, the
coefficients in the first row of the local component balance low would scatter into (be added to) the fourth
row of the system, while the second row of the component would scatter to the third system equation row.
If the component balance law is symmetric, then the columns locations scatter in the same fashion.
c 2001 J.E. Akin 169
8.2 Matrices
Matrices are very commonly used in many areas of applied mathematics and engineering. While they
can be considered a special case of the subscripted arrays given above they have their on special algebra
and calculus notations that are useful to know. In the following sections we will describe matrices and
the intrinsic operations on them that are included in F90 and M ATLAB. Neither C nor C++ have such
useful intrinsics, but require the programmer to develop them or extract them from a special library.
A matrix is defined as a rectangular array of quantities arranged in rows and columns. The array is
enclosed in brackets, and thus if there are m rows and n columns, the matrix can be represented by
2 3
a11 a12 a13 1 a j 1 a n
6
6
a21 a22 a23 2 a j 2 a n 7
7
6 .. .. 7
6 7
A= = [A]
6 . . 7
6
6 ai 1 ai 2 ai 3 aij ain 7
7
(8.1)
6 .. .. 7
4 . . 5
am 1 am 2 am 3 amj amn
where the typical element aij has two subscripts, of which the first denotes the row (ith ) and where the
second denotes the column (j th ) which the element occupies in the matrix. A matrix with m rows and n
columns is defined as a matrix of order m n, or simply an m n matrix. The number of rows is always
specified first. In Equation 8.1, the symbol A stands for the matrix of m rows and n columns, and it is
usually printed in boldface type. If m n = =1
, then the matrix is equivalent to a scalar. If m , the =1
matrix A reduces to the single row
A = [ a11 a 12 a 13 a j1 1 ] = (A)a n
which is called a row matrix. Similarly, if n = 1, the matrix A reduces to the single column
2 3
a11
6 a21 7
A=6
6 .
7
7 = col[ a 11 a 21 am 1 ] = fAg
4 . . 5
am 1
which is called a column matrix, or vector. When all the elements of matrix are equal to zero, the matrix
is called null or zero and is indicated by 0. A null matrix serves the same function as zero does in
ordinary algebra. To set all the elements of A to zero, one writes A in F90, and A m; n =0 = zeros[ ]
in M ATLAB.
=
If m n, the matrix is said to be square.
2 3
a11 a 12 a n 1
A=6 .
4 ..
..
.
7
5
an 1 an 2 ann
Before considering some of the matrix algebra implied by the above equation, a few other matrix
types need definition. A diagonal matrix is a square matrix which has zero elements outside the principal
diagonal. It follows, therefore, that for a diagonal matrix aij =0
when i 6 j , and not all aii are zero. A =
typical diagonal matrix may be represented by
2
a 11 0 0 3
6 0 22 0 7
A=6
a
7
6 . .. 7 ;
4 . . . 5
0 0 ann
0 0 1
A Toeplitz matrix has constant-valued diagonals. An identity matrix is Toeplitz as is the following
matrix. 2 3
1 2 3 5
A=
6
6 4 1 2 3 77
4 1 4 1 25
10 1 4 1
Note how the values of a Toeplitz matrix’s elements are determined by the first row and the first column.
M ATLABuses the Toeplitz function to create this unusual matrix.
=
A symmetric matrix is a square matrix whose elements aij aji for all i; j . For example,
2
12 2 1 3
A = 4 2 33 0 5
1 0 15
is symmetric: The first row equals the first column, the second row the second column, etc.
An antisymmetric or skew symmetric matrix is a square matrix whose elements aij aji for all=
i; j . Note that this condition means that the diagonal values of an antisymmetric matrix must equal zero.
In M ATLAB an appended prime is used to denote the transpose of any matrix, such as B A0 , =
whereas in F90 we employ the intrinsic function B = transpose( )
A , or a user defined operator like
=
B .t.A which we defined earlier.
If all the elements on one side of the diagonal of a square matrix are zero, the matrix is called a
triangular matrix. There are two types of triangular matrices: (1) an upper triangular U, whose elements
below the diagonal are all zero, and (2) a lower triangular L, whose elements above the diagonal are all
zero. An example of a lower triangular matrix is
2
10 0 0 3
L=4 1 3 0 5 :
5 1 2
A matrix may be divided into smaller arrays by horizontal and vertical lines. Such a matrix is then
referred to as a partitioned matrix, and the smaller arrays are called submatrices. For example, we can
3 3
partition a matrix into four submatrices as shown:
2 3 2 3
a11 a12 j a13 2 1 j 3
A=6
6 a21 a22 j a23 7
7 = A11 A12 6
= 64 10 5 j 0 7
7
4 j 5 A21 A22 j 5
a31 a 32 j a33 4 6 j 10
c 2001 J.E. Akin 171
where, in the F90 and M ATLABcolon notation;
A11 = 11
a21
a
= 102 15 = A(1 : 2 1 : 2)
a12
a22
;
A12 = = 30 = A(1 : 2 3)
a13
a23
;
A21 = a3132 = 4 6 = A(3 1 : 2)
a ;
It should be noted that the elements of a partitioned matrix must be so ordered that they are compatible
with the whole matrix A and with each other. That is, A11 and A12 must have an equal number of rows.
Likewise, A21 and A22 must have an equal number of rows. Matrices A11 and A21 must have an equal
number of columns. Likewise, for A12 and A22 . Note that A22 is a matrix even though it consists of
only one element. Provided the general rules for matrix algebra are observed, the submatrices can be
treated as if they were ordinary matrix elements.
8.2.1 Matrix Algebra
To define what addition and multiplication means for matrices, we need to define an algebra for arrays
of numbers so that they become useful to us. Without an algebra, all we have is a sequence of definitions
without the ability to manipulate what they mean!
Addition of two matrices of the same order is accomplished by adding corresponding elements of
= +
each matrix. The matrix addition C A B (as we write it in F90 and M ATLAB), where A, B and C
are matrices of the same order m n can be indicated by the equation
where cij , aij , and bij are typical elements of the C, A, and B matrices, respectively. An example of
matrix addition is
2
3 0 13 2 1 1 13 2 2 1 03
4 2 1 2 5+4 2 5 6 5=4 4 4 85 :
1 1 1 3 4 9 2 5 10
Matrix subtraction, C = A B, is performed in a similar manner.
Matrix addition and subtraction are associative and commutative. That is, with the previous defini-
tions for matrix addition and subtraction, grouping and ordering with respect to these operations does not
affect the result.
(
A BC )=( )
A B C and C B A
Multiplication of the matrix A by a scalar c is defined as the multiplication of every element of the matrix
by the scalar c. Thus, the elements of the product B =
cA are given by bij =
caij , and is written as
=
B C A in both F90 and M ATLAB. Clearly, scalar multiplication distributes over matrix addition.
We could define special multiplication in the somewhat boring way as the term by term product of
two identical sized matrices: C =
AB ) cij = =
aij bij . This feature is allowed in both F90 and
= =
M ATLAB where it is written as C A*B, and C A.*B, respectively. Although this definition might
be useful in some applications, this choice for what multiplication means in our algebra does not give us
much power. Instead, we define the matrix product C AB to mean =
X
p
k =1
A and B can be multiplied together as only when the number of columns in A; p, equals the number
of rows in B. When this condition is fulfilled, the matrices A and B are said to be conformable for
multiplication. Otherwise, matrix multiplication of two matrices cannot be defined. The product of two
c 2001 J.E. Akin 172
conformable matrices A and B having orders m p and p n, respectively, yields an m n matrix C.
=
In M ATLAB this is simply written as C A*B, where as in F90 one would use the intrinsic function
C = matmul ( ) =
A; B , or a user defined operator such as C A.x.B which we defined earlier.
The reason why this definition for matrix multiplication was chosen so that we can concisely represent
a system of linear equations. The verbose form explicitly lists the equations.
a11 x1 +a12 x2 + a13 x3 + + a1 n xn = c1
a21 x1 +a22 x2 + a23 x3 + + a2 n xn = c2
a31 x1 + a32 x2 + a33 x3 + + a3 n xn = c3
.. ..
. .
an x 1 1 +a 2 x2 +a 3 x3 + +a
n n nn xn = cn
where the aij ’s and ci ’s usually represent known coefficients and the xi ’s unknowns. To express these
equations more precisely, we define matrices for each of these arrays of numbers and lay them out as a
matrix-vector product equaling a vector.
2 32 3 2 3
a11 a12 a13 1
a n x1 c1
6
6 a21 a22 a23 a2 n
76
76 x2 7
7
6
6 c2 7
7
6
6 a31 a32 a33 a3 n
76
76 x3 7
7 = 6
6 c3 7
7
6 .. 76 .. 7 6 .. 7
4 . 54 . 5 4 . 5
an 1 an 2 an 3 ann xn cn
=
We thus obtain the more compact matrix form AX C. A represents the square matrix of coefficients,
X the vector (column matrix) of unknowns, and C the vector of known quantities.
Matrix multiplication is associative and distributive. For example,
(AB)C = A(BC)
A(B + C) = AB + AC
However, matrix multiplication is not commutative. In general, AB 6 BA. Consequently, the order =
in which matrix multiplication is specified is by no means arbitrary. Clearly, if the two matrices are
not conformable, attempting to commute the product makes no sense (the matrix multiplication BA is
not defined). In addition, when the matrices are conformable so that either product makes sense (the
matrices are both square and have the same dimensions, for example), the product cannot be guaranteed
to commute. You should try finding a simple example that illustrates this point. When two matrices A and
B are multiplied, the product AB is referred to either as B premultiplied by A, or as A postmultiplied
=
by B. When AB BA, the matrices A and B are then said to be commutable. For example, the unit
matrix I commutes with any square matrix of the same order: AI IA A.y = =
The process of matrix multiplication can also be extended to partitioned matrices, provided the indi-
vidual products of submatrices are conformable for multiplication. For example, the multiplication
A11 A12 B11 B12 A11 B11 + A12 B21 A11 B12 + A12 B22
AB = =
A21 A22 B21 B22 A21 B11 + A22 B21 A21 B12 + A22 B22
is possible provided the products A11 B11 , A12 B21 , etc. are conformable. For this condition to be ful-
filled, it is only necessary for the vertical partitions in A to include a number of columns equal to the
number of rows in the corresponding horizontal partitions in B.
T ZT YT BT AT . As an example
( ) =
3 equals AB YZ
The transpose of a product of2matrices
3
of matrix multiplication, let B =4 1 5 and A = 21 10 01 ; then
2
2
2 1 0
33
7
AB =
1 0 1 4 12 5 = 6
y This result is why I is called the identity matrix: It is the identity element with respect to matrix multiplication.
c 2001 J.E. Akin 173
2
2 13
BT AT =
3 1 2 4 1 05 = 7 6
0 1
8.2.2 Inversion
Every (non-singular) square matrix A has an inverse, indicated by A 1 , such that by definition the
product AA 1 is a unit matrix I. The reverse is also true: A 1 A I. Inverse matrices are very useful
=
=
in the solution of simultaneous equations AX C such as above where A and C are known and X is
unknown. If the inverse of A is known, the unknowns of the X matrix can be (symbolically) found by
premultiplying both sides of the equation by the inverse A 1 AX A 1 C so that =
X = A 1C :
In this way, in theory we have “solved” our system of linear equations. To employ this approach, we
must find the inverse of the matrix A, which is not any easy task. Despite this computational difficulty,
using matrix algebra to concisely express complicated linear combinations of quantities often provides
much insight into a problem and its solution techniques.
Various methods can be used to determine the inverse of a given matrix. For very large systems of
equations it is probably more practical to avoid the calculation of the inverse and solve the equations by
a procedure called factorization. Various procedures for computing an inverse matrix can be found in
2 2 3 3
texts on numerical analysis. The inverse of or matrices can easily be written in closed form
2 2
by using Cramer’s rule. For a matrix, we have the classic formula, which no engineering student
should forget.
d b
1
a
c
b
d
= ad
c
bc
a
However, finding the inverse of larger arrays using Cramer’s rule is very inefficient computationally. In
M ATLAB an inverse matrix of A is computed as inv(A), but this is only practical for matrices of a small
size, say <100. F90 does not have an intrinsic matrix inversion function but we provide such a function,
named inv, in our operator library.
8.2.3 Factorizations
=
We have indicated that we will frequently employ matrices to solve linear equation systems like A x b,
where A is a known square matrix, B is a known vector, and X is an unknown vector. While in theory
the solution is simply the inverse of A times the vector B, x A( 1) b, that is computationally the
=
least efficient way to find the vector X. In practice, one usually uses some form of factorization of the
matrix A. A very common method is to define A to be the product of two triangular matrices, defined
above, say L U = A, where L is a square lower triangular matrix and U is a square upper triangular
matrix. Skipping the details of this “LU-factorization” we could rewrite the original matrix system as
LU x = b, which can be viewed as two matrix identities:
L = h b
U = x h;
where h is a new temporary vector, and where both L and U are much cheaper to compute than the
inverse of A. We do not need the inverse of L or U since, as triangular matrices, their first or last row
contains only one non-zero term. That allows us to find one term in the unknown vector from one scalar
equation. The processes of recovering the vectors from these two identities is called substitution.
We illustrate this process with a example set of four equations with A and b given as:
2
1800 600 360 900 3
6
A=6
0 4500 2700 2250 777
6
4 0 2700 2700 1890 5
6300 5250 1890 3795
c 2001 J.E. Akin 174
bT = [ 6300 2250 1890 21405 ] :
The LU-factorization process mentioned above gives the first of two lower triangular systems; Lh =
: 2 38 9 8 9
60 0 0 0 6300
b
>
> h1 >
> >
> >
>
7> > > >
6
6
6
0 150 0 0 7<
7
>
h2
>
=
=>
>
< 2250 >
=
6
4 0 90 36 0 7>
5>>
>
h3 >
>
>
>
>
>
>
1890 >
>
>
>
:
210 105 42 10 :
h4
; :
21405 ;
Observe that the significant difference from A x = is that the first row of this identity has one
b
h
T
= [ 105 15 15 30 ] :
Now that h is known we can write the upper triangular identity, U = x h , as:
2 38 9 8 9
30 10 6 15 >
> x1 >
> >
> 105 >
>
7> > > >
6
6
6
0 30 18 15 7<
7
>
x2
>
=
=>
>
< 15 >
=
6
4 0 0 30 15 7>
5>>
>
x3 >
>
>
>
>
>
>
15 >
>
>
>
:
0 0 0 30 :
x4
; :
30 ;
This time the bottom row has only one unknown, x4 30 = 30
, so the last unknown is x 4 = 1.
Working backward up to the next row again there is only one unknown:
30 3 + 15 ( 1) = 15
x
so that x3 =0
. Proceeding back up through the remaining rows to get all the unknowns is called “back
substitution.” It yields
x
T
= [4 0 0 1] :
By inspection you can verify that this satisfies the original system of linear equations, A x b. With a =
little more work one can employ matrix multiplication to verify that L U A. While we have not given =
the simple algorithm for computing L and U from A, it is widely known as the “LU Factorization,”
and is in many texts on numerical analysis. Other common factorizations are the “QR Factorization,” the
“Cholesky Factorization” for a symmetric positive definite A, and the “SVD Factorization” for the case
where A is rectangular, or ill-conditioned and one is seeking a best approximation to X.
The factorization process is relatively expensive to compute but is much less expensive that an inver-
sion. The forward and backward substitutions are very fast and cheap. In problems where you have many
different b vectors (and corresponding x vectors, such as time dependent problems), one carries out the
expensive factorization process only once and the executes the cheap forward and back substitution for
each b vector supplied.
8.2.4 Determinant of a Matrix
Every square matrix, say A, has a single scalar quantity associated with it. That scalar is called the
determinant, jAj, of the matrix. The determinant is important in solving equations and inverting matrices.
A very important result is that the inverse A 1 exists if and only if jAj 6= 0. If the determinant is zero,
the matrix A (and the equivalent set of equations) is said to be singular. Simple conditions on a matrix’s
structure can be used to infer the determinant or its properties.
If two rows or columns are equal, the determinant is zero.
c 2001 J.E. Akin 175
Interchanging two rows, or two columns, changes the sign of the determinant.
The determinant is unchanged if any row, or column, is modified by adding to it a linear combina-
tion of any of the other rows, or columns.
A singular square matrix may have nonsingular square partitions.
The last two items will become significant when we consider how to apply boundary conditions and how
to solve a system of equations.
8.2.5 Matrix Calculus
At times you might find it necessary to differentiate or integrate matrices. These operations are simply
carried out on each and every element of the matrix. Let the elements aij of A be a function of a
parameter t. Then, the derivative and integral of a matrix simply equals term-by-term differentiation and
integration, respectively.
A
B = d
dt
! bij = 1 1
daij
dt
; i m; j n
Z Z
C = A dt ! cij = 1 1
aij dt; i m; j n
When dealing with functional relations the concept of rate of change is often very important. If we
()
have a function f of a single independent variable, say x, then we call the rate of change the derivative
with respect to x, which is written as df =dx. Generalizing this notion to functions of more than two
variables, say z = ( )
f x; y , we may define two distinct rates of change. One is the function’s rate of
change with respect to one variable with the other held constant. We thus define partial derivatives.
When x is allowed to vary, the derivative is called the partial derivative with respect to x, and is denoted
by @ f =@ x. By analogy with the usual definition of derivative, this partial derivative is mathemtically
= lim!0 ( + ) ( )
defined as
fx = @f
@x x
f x x; y
x
f x; y
:
A similar definition describes the partial derivative with respect to y , denoted by @ f =@ y . The second
notion of rate-of-change is the total derivative, which is expressed as df .
df = @f
@x
dx + @f
@y
dy
These definitions can be extended to include a function of any number of independent variables.
Often one encounters a scalar u defined by a symmetric square n n matrix, A, a column vector B,
and a column vector X of n parameters. The combination we have in mind has the form
u = 12 XT AX + XTB + C (8.2)
If we calculate the derivative of the scalar u with respect to each xi , the result is the column vector
@u
@ X
= AX + B ;
a result that can be verified by expanding Equation 8.2, differentiating with respect to every xi in X, and
rewriting the result as a matrix product.
8.2.6 Computation with Matrices
Clearly, matrices are useful in representing systems of linear equations and expressing the solution. As
said earlier, we need to be able to express linear equations in terms of matrix notation so that analytic
manipulations become easy. Furthermore, calculations with linear equations become easy if we can di-
rectly express our matrix formulas in terms of programs. This section describes programming constructs
for the simple matrix expressions and manipulations covered in this chapter.
c 2001 J.E. Akin 176
M ATLAB C++ F90
a
Pre-allocate A(100)=0 int A[100]; integer A(100)
linear array
Initialize to a for j=1:100 % slow for (j=0; j<100; j++) A=12
A(j)=12
constant value of end A[j]=12;
12 % better way
A=12*ones(1,100)
Pre-allocate A=ones(10,10) int A[10][10]; integer A(10,10)
two-dimensional
array
a C++ has a starting subscript of 0, but the argument in the allocation statement is the array’s size.
g ;
In most languages, we must express the fact that a variable is an ordered array of numbers—a
matrix—rather than a scalar (or some other kind of variable). Such declaration statements usually occur
at the beginning of the program or function. Table 8.12 shows the declaration of an integer array for
our suite of programming languages. Both Fortran and C++ require you to specify the maximum range
of each subscript of an array before the array or its elements are used. Such range specification is not
required by M ATLAB, but pre-allocating the array space can drastically improve the speed of M ATLAB,
as well as making much more efficient use of the available memory. If you do not pre-allocate M ATLAB
arrays, the interpreter must check at each step if a position in a row or column is larger than the current
maximum. If so, the maximum value is increased and the memory found to store the new element. Thus,
failure to pre-allocate M ATLAB arrays is permissible but inefficient.
Array initialization is concisely expressed in both Fortran and M ATLAB; in C++, you must write
a small program to initialize an array to a nonzero value.y If an array contains a variety of different
numbers, we can concisely express the initialization; again, in C++, we must explicitly write statements
for each array element.
An Aside: Matrix Storage
Most computer languages do not make evident how matrices are stored. More frequently than you might
think, it becomes necessary to know how an array is actually stored in the computer’s memory and
retrieved. The procedure both Fortran and M ATLAB use to store the elements of an array is known as
column major order: all the elements of the first column are stored sequentially, then all of the second,
etc. Another way of saying this is that the first (left most) subscript ranges over all its values before the
second is incremented. After the second subscript has been incremented then the first again ranges over
all its values. In C++, row major order is used: The first row of an array is stored sequentially, then the
second, etc. Clearly, translating programs from Fortran to C++ or vice versa must be done with care.
However, the above knowledge can be used to execute some operations more efficiently. For example,
the matrix addition procedure could be written as ck ak bk , = + 1
k m n. One circumstance
y Global arrays those declared outside of any function definition are initialized to zero in many versions of C++. Array
declared within the scope of a function have no predefined values.
c 2001 J.E. Akin 177
M ATLAB C++ F90
Addition
C=A+B
C=A+B for (i=0; i<n; i++)f C=A+B
for (j=0; j<n; j++)f
C[i][j]=A[i][j]+B[i][j];
g
Multiplication
C = AB
C=A*B for (i=0; i<n; i++)f C=matmul(A,B)
for (j=0; j<n; j++)f
C[i][j] = 0;
for (k=0; k<n; k++)f
C[i][j] += A[i][k]*B[k][j];
g
g
Scalar
multiplication C=a*B for (i=0; i<n; i++)f C=a*B
C = aB for (j=0; j<n; j++)f
C[i][j] = a*B[i][j];
g
Matrix
inverse
B=A 1
B=inv(A) a B=inv(A)a
a Neither C++ nor F90 have matrix inverse functions as part of their language definitions nor as part of standard collections
of mathematical functions (like those listed in Table 4.7). Instead, a special function, usually drawn from a library of numerical
functions, or a user defined operation, must be used.
where knowing the storage format becomes crucial is extracting submatrices in partitioned arrays. Such
a Fortran subroutine would have to dimension the arrays with a single subscript.
Expressing the addition, subtraction, or multiplication of arrays in Fortran or M ATLAB is concise and
natural. Explicit programs must be written in C++ to accomplish these calculations. Table 8.14 displays
what these constructs are for the special case of square matrices with n rows.
8.3 Exercises
1. Often it is necessary to check computer programs that invert matrices. One approach is use
test matrices for which the inverse is known analytically. Few such matrices are known, but one is the
following n n matrix.
2 +2 1 0 0 0 1 3 1 2
1 2 2 1 3
2 +2 2 2 +2
n
n n n
1 1
n n
6
6 2 1 2 0 0 0 7
7
6
6 n 1 n n 1 3 2 7
7
0 1 1 1
2 0 0
6 7 6 7
6
2 7 6 2 1 4 3 7
=
n n n
6 7 6 7
6 .. .. .. .. .. .. .. 7 6 .. .. 7
6 7 6 7
6 . . . . . . . 7 6 . . 7
6
4 0 0 21 1 1
2
7
5
6
4 2 3 4 n n 1 7
5
1 0 0 12 +2 1 2 3 1
2 +2 2 +2
n
n n
n n
Develop two routines that will create each of these two matrices for a given n value, and test them with
a main program that uses matmul to compute their matrix product. The result should be the identity
matrix.
c 2001 J.E. Akin 178
2. The numerical accuracy in calculating an inverse is always an issue: To what extent can you
believe the accuracy of the numbers that computer programs calculate. Because of the finite precision
used to represent floating point numbers, floating point calculations can only rarely yield exact answers.
We want to empirically compute the difference between the inverse of the first matrix in the previous
exercise by using a library inversion routine and compare its result with the exact answer. Because the
error varies throughout the matrix, we need to summarize the error with a single quantity. Two measures
are
q
routinely used: the peak absolute error max
i;j jaij bij j and the root-mean-squared (rms) error
n
12 P
i;j
( aij bij )2.y The first captures the biggest difference between the elements of two matrices,
and the second summarizes the error throughout the entire difference. Clearly, the peak absolute error
is always larger than the rms error. Comparing these two error measures provides some insight into the
distribution of error: If the two are comparable, the errors have about the same size; if not, the errors
deviate greatly throughout the matrix.
3. Combine the intrinsic array features of F90 with the concepts of OO classes to create a Vector
Class that is built around a type that has attributes consisting of the integer length of a vector and an array
of its real components. Provide members to construct vectors, delete the arrays, real vectors, list vectors,
and carry out basic mathematics operations. Overload the operators +, -, *, =, and ==. Avoid writing any
serial loops.
4. Extend the above Vector Class concepts to a Sparse Vector Class where it is assumed that most
of the values in the vector are zero and for efficiency only the non-zero entries are to be stored. This
clearly exceeds the intrinsic array features of F90 and begins to show the usefulness of OOP. The defined
type must be extended to include an integer array that contains the location (row number) of the non-zero
values. In addition to changing the input and output routines to utilize the extra integer position list, all
the mathematical member functions such as addition will have to be changed so that the resulting vector
has non-zero terms in locations that are a union of the two given location sets (unless the operation creates
new zero values). Use the concept of logical array masks in computing the dot product. Avoid writing
any serial loops.
y The 1=n2 term occurs in this expression because that equals the number of terms in the sum. The rms error is used frequently
in the practice to measure error; you average the squared error across the dataset and evaluate the square-root of the result.
c 2001 J.E. Akin 179
180
Chapter 9
Advanced Topics
9.1 Templates
One of our goals has been to develop software that can be reused for other applications. There are some
algorithms that are effectively independent of the object type on which they operate. For example, in a
sorting algorithm one often needs to interchange, or swap, two objects. A short routine for that purpose
follows:
subroutine swap integers (x, y)
implicit none
integer, intent(inout) :: x, y
integer :: temp
temp = x
x = y
y = temp
end subroutine swap integers
Observe that in this form it appears necessary to have one version for integer arguments and another for
real arguments. Indeed we might need a different version of the routine for each type of argument that
you may need to swap. A slightly different approach would be to write our swap algorithm as:
subroutine swap objects (x, y)
implicit none
type (Object), intent(inout) :: x, y
type (Object) :: temp
temp = x
x = y
y = temp
end subroutine swap objects
which would be a single routine that would work for any Object, but it has the disadvantage that one
find a way to redefine the Object type for each application of the routine. That would not be an easy
task. (While we will continue with this example with the algorithm in the above forms it should be noted
that the above approaches would not be efficient if x and y were very large arrays or derived type objects.
In that case we would modify the algorithm slightly to employ pointers to the large data items and simply
swap the pointers for a significant increase in efficiency.)
Consider ways that we might be able to generalize the above routines so that they could accept and
swap any specific type of arguments. For example, the first two versions could be re-written in a so called
template form as:
subroutine swap Template$ (x, y)
implicit none
Template$, intent(inout) :: x, y
Template$ :: temp
temp = x
x = y
y = temp
end subroutine swap Template$
In the above template the dollar sign ($) was includes in the “wild card” because while it is a valid member
of the F90 character set it is not a valid character for inclusion in the name of a variable, derived type,
function, module, or subroutine. In other words, a template in the illustrated form would not compile, but
The use of a text editor to make such substitutions is not very elegant and we expect that there may
be a better way to pursue the concept of developing a re-useable software template. The concept of a text
editor substitution also fails when we go to the next logical step and try to use a derived type argument
instead of any of the intrinsic data types. For example, if we were to replace the “wild card” with our
previous type (chemical element) that would create:
subroutine swap type (chemical element) (x,y)
implicit none
type (chemical element), intent (inout)::x,y
type (chemical element) ::temp
temp = x
x = y
y = temp
end subroutine swap type (chemical element)
This would fail to compile because it violates the syntax for a valid function or subroutine name, as well
as the end function or end subroutine syntax. Except for the first and last line syntax errors this would be
a valid code. To correct the problem we simply need to add a little logic and omit the characters type
( ) when we create a function, module, or subroutine name that is based on a derived type data entity.
Then we obtain
subroutine swap chemical element (x,y)
implicit none
type (chemical element), intent (inout)::x,y
type (chemical element) ::temp
temp = x
x = y
y = temp
end subroutine swap chemical element
to the subtype of interest is set to point to the desired member. The dispatching process for the display
procedure is different. It requires an if-elseif construct that contains calls to all of the possible subtype
members (two here) and a failsafe default state to abort the process or undertake the necessary exception
handling. Since all but one of the subtype pointer objects have been nullified it employs the ASSOCI-
ATED intrinsic function to select the one, and only, procedure to call and passes the pointer object on to
that procedure. In F90 a pointer can be nullified by using the NULLIFY statement, while F95 allows the
alternative of pointing at the intrinsic NULL function with returns a disassociated pointer. The NULL
function can also be used to define the initial association status of a pointer at the point it is declared.
That is a better programming style.
The are other approaches for implementing the dynamic dispatching concepts. Several examples are
give in the publications by the group Decyk, Norton, and Szymanski (1995, 1997, 1999) and on Prof.
Szymanski’s Web site.
specific, and for the most useful of those features to appear in the next standard release. Compiler releases
by Cray c Digitial c and Silicon Graphics c computers are examples of versions with extensive en-
hancements. Some compilers, like the Digitial c Visual Fortran c are designed to develop applications
for the Microsoft c Windows c system and contain library modules for ”standard” graphical displays
via QuickWin c for dialog routines to the Graphical User Interface (GUI), for interfacing with multiple
programming languages or the operation system, and for multiple ”thread” operations. Threads are not
currently in the F90 standard. They allow for response to the user interaction with any of a set of multiple
buttons or dials in an active GUI.
Fortran 90 is a subset of the High Performance Fortran (HPF) standard that has been developed for
use on massively parallel computers. We have not discussed those enhancements.
Even without these special enhancements the OOP abilities of F90 provide an important tool in en-
gineering and scientific programming. In support of that position we close with a quote from computer
scientist Professor Boleslaw K. Szymanski’s Web page on High Performance Object-Oriented Program-
ming in Fortran 90 where his group concludes: ”All of our Fortran 90 programs execute more quickly
than the equivalent C++ versions, yet the abstraction modeling capabilities that we needed were compa-
rably powerful.”
Bibliography
1. Adams, J.C., Brainerd, W.S., Martin, J.T., Smith, B.T. and Wagener, J.L., Fortran 90 Handbook:
Complete ANSI / ISO Reference, Intertext Publications, McGraw-Hill Book Company, New York,
1992.
2. Akin, J.E. “Object-oriented Programming via Fortran 90,” Engineering Computations, 16(1) 26-
48, 1999.
3. Angell, I.O. and Griffith, G., High Resolution Computer Graphics Using Fortran 77, Macmillan,
London, 1987.
5. Barton, J.J. and L.R. Nackman, Scientific and Engineering C++, Addison Wesley, 1994.
6. Cary, J.R., S.G. Shasharina, J.C. Cummings, J.V.W. Reynders, and P.J. Hinker, “A Comparison of
C++ and Fortran 90 for Object-Oriented Scientific Programming”, Computer Phys. Comm., 105,
20, 1997.
9. Decyk, V.K., Norton,C.D. and B.K. Szymanski, “How to Express C++ Concepts in Fortran90,”
Scientific Programming, 6, 363–390, 1997.
11. Dubois-Pèlerin, Y. and P. Pegon, “Improving Modularity in Object-Oriented Finite Element Pro-
gramming,” Communications in Numerical Methods in Engineering, 13, 193–198, 1997.
12. Filho, J.S.R.A. and P.R.B. Devloo, “Object Oriented Programming in Scientific Computations,”
Engineering Computations, 8(1), 81–87, 1991.
13. Gray, M.G., and R.M. Roberts, “Object-Based Programming in Fortran 90”, Computers in Physics,
11, 355, 1997.
15. George, A. and J. Liu “An Object-Oriented Approach to the Design of a User Interface for a Sparse
Matrix Package”, em SIAM J. Matrix Anal. Appl., 20(4), 953–969, 1999.
Fortran 90 Overview
This overview of Fortran 90 (F90) features is presented as a series of tables that illustrate the syntax and
abilities of F90. Frequently comparisons are made to similar features in the C++ and F77 languages and
to the Matlab environment.
These tables show that F90 has significant improvements over F77 and matches or exceeds newer
software capabilities found in C++ and Matlab for dynamic memory management, user defined data
structures, matrix operations, operator definition and overloading, intrinsics for vector and parallel pro-
cessors and the basic requirements for object-oriented programming.
They are intended to serve as a condensed quick reference guide for programming in F90 and for
understanding programs developed by others.
4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
4.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.17 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.18 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.21 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.22 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.23 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
c 2001 J.E. Akin 191
4.25 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.26 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.27 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.28 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.29 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.30 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.31 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.33 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
4.34 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
5.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
8.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
8.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
8.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
8.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
8.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
8.7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
8.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
8.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
8.10 Intrinsic Functions Allowing Logical Mask Control . . . . . . . . . . . . . . . . . . . 165
8.11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
8.12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
8.13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
8.14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
c 2001 J.E. Akin 192
B.19 F90 DOs Named for Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
B.20 Looping While a Condition is True . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
B.21 Function definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
B.22 Arguments and return values of subprograms . . . . . . . . . . . . . . . . . . . . . . 10
B.23 Defining and referring to global variables . . . . . . . . . . . . . . . . . . . . . . . . 11
B.24 Bit Function Intrinsics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
B.25 The ACSII Character Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
B.26 F90 Character Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
B.27 How to type non-printing characters . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
B.28 Referencing Structure Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
B.29 Defining New Types of Data Structure . . . . . . . . . . . . . . . . . . . . . . . . . . 13
B.30 Nested Data Structure Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
B.31 Declaring, initializing, and assigning components of user-defined datatypes . . . . . . 13
B.32 F90 Derived Type Component Interpretation . . . . . . . . . . . . . . . . . . . . . . 14
B.33 Definition of pointers and accessing their targets . . . . . . . . . . . . . . . . . . . . . 14
B.34 Nullifing a Pointer to Break Association with Target . . . . . . . . . . . . . . . . . . . 14
B.35 Special Array Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
B.36 Array Operations in Programming Constructs . . . . . . . . . . . . . . . . . . . . . . 15
B.37 Equivalent Fortran 90 and M ATLAB Intrinsic Functions . . . . . . . . . . . . . . . . . 16
B.38 Truncating Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
B.39 F90 WHERE Constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
B.40 F90 Array Operators with Logic Mask Control . . . . . . . . . . . . . . . . . . . . . 18
B.41 Array initialization constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
B.42 Array initialization constructs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
B.43 Elementary matrix computational routines . . . . . . . . . . . . . . . . . . . . . . . . 34
B.44 Dynamic allocation of arrays and pointers . . . . . . . . . . . . . . . . . . . . . . . . 34
B.45 Automatic memory management of local scope arrays . . . . . . . . . . . . . . . . . . 35
B.46 F90 Single Inheritance Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
B.47 F90 Selective Single Inheritance Form . . . . . . . . . . . . . . . . . . . . . . . . . . 35
B.48 F90 Single Inheritance Form, with Local Renaming . . . . . . . . . . . . . . . . . . . 35
B.49 F90 Multiple Selective Inheritance with Renaming . . . . . . . . . . . . . . . . . . . 36
c 2001 J.E. Akin 2
Language Syntax Location
M ATLAB % comment (to end of line) anywhere
C /*comment*/ anywhere
F90 ! comment (to end of line) anywhere
F77 * comment (to end of line) column 1
complex). Booleans are just 0s and 1s treated as reals. M ATLAB5 allows the user to select more types.
b There is no specific data type for a complex variable in C++; they must be created by the programmer.
is desired. Otherwise, M ATLAB assumes matrix operations; figure out the difference between ‘*’ and ‘.*’. Note that since matrix
and scalar addition coincide, no ‘.+’ operator exists (same holds for subtraction).
b Fortran 90 allows the user to change operators and to define new operator symbols.
c In all languages the minus sign is used for negation (i.e., changing sign).
d In C++ the exponentiation xy is calculated by function pow (x; y ).
c 2001 J.E. Akin 3
Description M ATLAB C++ F90 F77
Equal to == == == .EQ.
Not equal to ˜= != /= .NE.
Less than < < < .LT.
Less or equal <= <= <= .LE.
Greater than > > > .GT.
Greater or equal >= >= >= .GE.
Logical NOT ˜ ! .NOT. .NOT.
Logical AND & && .AND. .AND.
Logical inclusive OR ! || .OR. .OR.
Logical exclusive OR xor .XOR. .XOR.
Logical equivalent == == .EQV. .EQV.
Logical not equivalent ˜= != .NEQV. .NEQV.
M ATLAB
C++ Operators F90 Operatorsa F77 Operators
Operators
() () [] -> . () ()
+ - ! ++ -- + ** **
- * & (type)
sizeof
* / * / % * / * /
+ -b + -b + -b + -b
< <= > >= << >> // //
== ˜= < <= > => == /= < <= > .EQ. .NE.
>= .LT. .LE.
.GT. .GE.
˜ == != .NOT. .NOT.
& && .AND. .AND.
| || .OR. .OR.
= | .EQV. .NEQV. .EQV. .NEQV.
?:
= += -= *= /=
%= &= ˆ= |=
<<= >>=
,
a User-defined unary (binary) operators have the highest (lowest) precedence in F90.
b These are binary operators representing addition and subtraction. Unary operators + and - have higher precedence.
c 2001 J.E. Akin 4
Description M ATLAB C++ F90 F77
exponential exp(x) exp(x) exp(x) exp(x)
natural log log(x) log(x) log(x) log(x)
base 10 log log10(x) log10(x) log10(x) log10(x)
square root sqrt(x) sqrt(x) sqrt(x) sqrt(x)
raise to power (xr ) x.ˆr pow(x,r) x**r x**r
absolute value abs(x) fabs(x) abs(x) abs(x)
smallest integer>x ceil(x) ceil(x) ceiling(x)
largest integer<x floor(x) floor(x) floor(x)
division remainder rem(x,y) fmod(x,y) mod(x,y)a mod(x,y)
modulo modulo(x,y)a
complex conjugate conj(z) conjg(z) conjg(z)
imaginary part imag(z) imag(z) aimag(z)
drop fraction fix(x) aint(x) aint(x)
round number round(x) nint(x) nint(x)
cosine cos(x) cos(x) cos(x) cos(x)
sine sin(x) sin(x) sin(x) sin(x)
tangent tan(x) tan(x) tan(x) tan(x)
arc cosine acos(x) acos(x) acos(x) acos(x)
arc sine asin(x) asin(x) asin(x) asin(x)
arc tangent atan(x) atan(x) atan(x) atan(x)
arc tangentb atan2(x,y) atan2(x,y) atan2(x,y) atan2(x,y)
hyperbolic cosine cosh(x) cosh(x) cosh(x) cosh(x)
hyperbolic sine sinh(x) sinh(x) sinh(x) sinh(x)
hyperbolic tangent tanh(x) tanh(x) tanh(x) tanh(x)
hyperbolic arc cosine acosh(x)
hyperbolic arc sine asinh(x)
hyperbolic arctan atanh(x)
a Differ for x < 0.
b atan2(x,y) is used to calculate the arc tangent of x=y in the range [ ; +]. The one-argument function atan(x)
computes the arc tangent of x in the range [ =2; +=2].
c 2001 J.E. Akin 5
Description C++ F90 F77 M ATLAB
Conditionally execute statements if if if if
f g end if end if end
Loop a specific number of times for k=1:n do k=1,n do # k=1,n for k=1:n
f g end do # continue end
Post-test loop do f do
statements statements
g while (test) if (test) exit
end do
c 2001 J.E. Akin 6
M ATLAB Fortran C++
f
if l expression IF (l expression) THEN if (l expression)
true group true group
end END IF true group;
g
IF (l expression) true statement if (l expression)
true statement;
Table B.10: IF Constructs. The quantity l expression means a logical expression having a value that
is either TRUE of FALSE. The term true statement or true group means that the statement or group
of statements, respectively, are executed if the conditional in the if statement evaluates to TRUE.
f
if l expression1 IF (l expression1) THEN if (l expression1)
true group A true group A
if l expression2 IF (l expression2) THEN true group A
true group B true group B
f
if (l expression2)
end END IF
true group C true group C true group B
end END IF g
statement group D statement group D true group C
g
statement group D
f
if l expression IF (l expression) THEN if (l expression)
true group A true group A
else ELSE true group A
false group B false group B g
f
end END IF else
false group B
g
f
if l expression1 IF (l expression1) THEN if (l expression1)
true group A true group A
true group A
g
elseif l expression2 ELSE IF (l expression2) THEN
true group B true group B
f
elseif l expression3 ELSE IF (l expression3) THEN else if (l expression2)
true group C true group C
true group B
g
else ELSE
default group D default group D
end END IF else if (l expression3)
f
true group C
g
else
f
default group D
g
c 2001 J.E. Akin 7
F90 C++
f
SELECT CASE (expression) switch (expression)
CASE (value 1)
group 1 case value 1 :
CASE (value 2) group 1
group 2 break;
. case value 2 :
. group 2
.
CASE (value n) break;
group n .
.
CASE DEFAULT .
default group case value n :
END SELECT group n
break;
default:
default group
g
break;
Fortran C++
DO 1 ... for (...) f
DO 2 ...
...
for (...) f
...
IF (disaster) THEN if (disaster)
GO TO 3 go to error
END IF
g
...
...
2 END DO
1 END DO
g
error:
3 next statement
Table B.16: GO TO Break-out of Nested Loops. This situation can be an exception to the general recom-
mendation to avoid GO TO statements.
c 2001 J.E. Akin 8
F77 F90 C++
DO 1 I = 1,N
f
DO I = 1,N for (i=1; i<n; i++)
IF (exit condition) THEN IF (exit condition) THEN
GO TO 2 EXIT ! this do if (exit condition)
ELSE ELSE break;// out of loop
false group false group else if
END IF END IF false group
1 CONTINUE END DO end
2 next statement next statement g
next statement
main: DO ! forever
test: DO k=1,k max
third: DO m=m max,m min,-1
IF (test condition) THEN
CYCLE test ! loop on k
END IF
END DO third ! loop on m
fourth: DO n=n min,n max,2
IF (main condition) THEN
EXIT main ! forever loop
END DO fourth ! on n
END DO test ! over k
END DO main
next statement
M ATLAB C++
initialize test initialize test
f
while l expression while (l expression)
true group
change test true group
change test
g
end
F77 F90
initialize test initialize test
# continue do while (l expression)
IF (l expression) THEN true group
true group change test
change test end do
go to #
END IF
c 2001 J.E. Akin 9
Function
M ATLABa C++ Fortran
Type
program statements
f
main(argc,char **argv) program main
[y1...yn]=f(a1,...,am) type y
[end of file] statements type a1,...,type am
statements
g
y = f(a1,I,am);
y = f(a1,...,am)
call s(a1,...,am)
end program
Table B.21: Function definitions. In each case, the function being defined is named f and is called with
m arguments a1,...,am.
c 2001 J.E. Akin 10
Global Variable Declaration
M ATLAB global list of variables
F77 common /set name/ list of variables
F90 module set name
save
type (type tag) :: list of variables
end module set name
C++ extern list of variables
c 2001 J.E. Akin 11
0 NUL 1 SOH 2 STX 3 ETX 4 EOT 5 ENQ 6 ACK 7 BEL
8 BS 9 HT 10 NL 11 VT 12 NP 13 CR 14 SO 15 SI
16 DLE 17 DC1 18 DC2 19 DC3 20 DC4 21 NAK 22 SYN 23 ETB
24 CAN 25 EM 26 SUB 27 ESC 28 FS 29 GS 30 RS 31 US
32 SP 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ’
40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 /
48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7
56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ?
64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G
72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O
80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W
88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ˆ 95 _
96 ‘ 97 a 98 b 99 c 100 d 101 e 102 f 103 g
104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o
112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w
120 x 121 y 122 z 123 { 124 | 125 } 126 ˜ 127 DEL
Table B.25: The ACSII Character Set.
c 2001 J.E. Akin 12
C, C++ Variable.component.sub component
F90 Variable%component%sub component
C, C++
f g
struct data tag variable list; /* Definition */
struct data tag variable = component values ; /* Initialization */
variable.component.sub component = value; /* Assignment */
c 2001 J.E. Akin 13
INTEGER, PARAMETER :: j max = 6
TYPE meaning demo
INTEGER, PARAMETER :: k max = 9, word = 15
CHARACTER (LEN = word) :: name(k max)
END TYPE meaning demo
TYPE (meaning demo) derived(j max)
Construct Interpretation
derived All components of all derived’s elements
derived(j) All components of jth element of derived
derived(j)%name All k max components of name within jth element of derived
derived%name(k) Component k of the name array for all elements of derived
derived(j)%name(k) Component k of the name array of jth element of derived
C++ F90
Declaration type tag *pointer name; type (type tag), pointer ::
pointer name
Target &target name type (type tag), target :: target name
Examples char *cp, c; character, pointer :: cp
int *ip, i; integer, pointer :: ip
float *fp, f; real, pointer :: fp
cp = & c; cp => c
ip = & i; ip => i
fp = & f; fp => f
c 2001 J.E. Akin 14
c 2001
J.E. Akin
Description Equation Fortran90 Operator Matlab Operator Original Sizes Result Size
Scalar plus scalar c = a b c = a b c = a b; 1; 1 1; 1
Element plus scalar cjk
= ajk b c = a b c = a b; m; n and 1; 1 m; n
C C A m; n n; m
P
Matrix times matrix Cij = k Aik Bkj C = matmul(A; B ) C = A B; m; r and r; n m; n
P
Vector dot vector c = k Ak Bk c = sum(A B) c = sum(A: B ); m; 1 and m; 1 1; 1
c = dot product(A; B ) c = A B
0; m; 1 and m; 1 1; 1
Table B.36: Array Operations in Programming Constructs. Lower case letters denote scalars or scalar elements of arrays. Matlab arrays are allowed a maximum
of two subscripts while Fortran allows seven. Upper case letters denote matrices or scalar elements of matrices.
Table B.37: Equivalent Fortran90 and M ATLAB Intrinsic Functions.
The following KEY symbols are utilized to denote the TYPE of the in-
trinsic function, or subroutine, and its arguments: A-complex, integer,
or real; I-integer; L-logical; M-mask (logical); R-real; X-real; Y-real;
V-vector (rank 1 array); and Z-complex. Optional arguments are not
shown. Fortran 90 and MATLAB also have very similar array operations
and colon operators.
c 2001 J.E. Akin 16
Type Fortran90 M ATLAB Brief Description
R TAN(X) tan(x) Tangent function of real X.
R TANH(X) tanh(x) Hyperbolic tangent function of real X.
R TINY(X) realmin Smallest positive number like X.
R TRANSPOSE(X) x’ Matrix transpose of any type matrix.
R X=1 x=ones(length(x)) Set all elements to 1.
R X=0 x=zero(length(x)) Set all elements to 0.
For more detailed descriptions and example uses of these intrinsic functions see Adams, J.C., et al.,
Fortran 90 Handbook, McGraw-Hill, New York, 1992, ISBN 0–07–000406–4.
c 2001 J.E. Akin 17
Function Description Opt Example
all Find if all values are true, for a fixed di- d all(B = A, DIM = 1)
mension. (true, false, false)
any Find if any value is true, for a fixed di- d any (B > 2, DIM = 1)
mension. (false, true, true)
count Count number of true elements for a d count(A = B, DIM = 2)
fixed dimension. (1, 2)
maxloc Locate first element with maximum m maxloc(A, A < 9)
value given by mask. (2, 3)
maxval Max element, for fixed dimension, given b maxval (B, DIM=1, B > 0)
by mask. (2, 4, 6)
merge Pick true array, A, or false array, B, ac- – merge(A, B,
L)
cording to mask, L. 0 3 5
2 4 8
minloc Locate first element with minimum value m minloc(A, A > 3)
given by mask. (2, 2)
minval Min element, for fixed dimension, given b minval(B, DIM = 2)
by mask. (1, 2)
pack Pack array, A, into a vector under control v pack(A, B < 4)
of mask. (0, 7, 3)
product Product of all elements, for fixed dimen- b product(B) ; (720)
sion, controlled by mask. product(B, DIM = 1, T)
(2, 12, 30)
sum Sum all elements, for fixed dimension, b sum(B) ;(21)
controlled by mask. sum(B, DIM = 2, T)
(9, 12)
unpack Replace the true locations in array B con- –
unpack(U, L, B)
trolled by mask L with elements from the 7 3 8
vector U. 2 4 9
0 3 5 1 3 5 T F T
A = ; B = ; L = ; U = (7; 8; 9)
7 4 8 2 4 6 F F T
Table B.40: F90 Array Operators with Logic Mask Control. T and F denote true and false, respectively.
Optional arguments: b -- DIM & MASK, d -- DIM, m -- MASK, v -- VECTOR and DIM = 1 implies
for any rows, DIM = 2 for any columns, and DIM = 3 for any plane.
c 2001 J.E. Akin 18
Alphabetic Table of Fortran90 Intrinsic Functions (continued)
Type Intrinsic Description
R ANINT (X [,K]) Real whole number nearest to X, of the given kind.
L ANY (M [,D]) True if any mask, M, element is true, in dimension D.
R ASIN (X) Arcsine (inverse sine) function of real X.
L ASSOCIATED (P [,T]) True if pointer, P, is associated with any target, or T.
R ATAN (X) Arctangent (inverse tangent) function of real X.
R ATAN2 (Y,X) Arctangent for argument of complex number (X, Y).
I BIT SIZE (I) Maximum number of bits integer I can hold, e.g. 32.
L BTEST (I,I POS) True if bit location I POS of integer I has value 1.
I CEILING (X) Least integer real X, of the given kind.
C CHAR (I [,K]) Character in position I of processor collating sequence.
Z CMPLX (X [,Y][,K]) Convert real(s) to complex type, of given kind.
Z CONJG (Z) Conjugate of complex number Z.
R COS (R Z) Cosine function of real or complex argument.
R COSH (X) Hyperbolic cosine function of real X.
I COUNT (M [,D]) Number of true mask, M, elements, in dimension D.
? CSHIFT (? ARAY,I SHIF [,D]) Circular shift out and in for I SHIF elements.
call DATE AND TIME ([S DATE] Real-time clock date, time, zone, and vector
[,S TIME] [,S ZONE] with year, month, day, UTC, hour, minutes, seconds,
[,I V VALUES]) and milliseconds.
R DBLE (A) Convert A to double precision real.
N DIGITS (N) Number of significant digits for N, e.g. 31.
R DIM (X,Y) The difference, MAX (X – Y, 0.0).
N,L DOT PRODUCT (V,V 2) Dot product of vectors V and V 2.
R DPROD (X,Y) Double precision real product of two real scalars.
? EOSHIFT (? ARRAY, Perform vector end-off shift by I shift terms,
I SHIFT [,? FILL][,D]) and fill, in dimension D.
R EPSILON (X) Number 1, for numbers like X, e.g. 2??–23.
R,Z EXP (R Z) Exponential function of real or complex argument.
I EXPONENT (X) Exponent part of the model for real X.
I FLOOR (X) Greatest integer less than or equal to X.
R FRACTION (X) Fractional part of the model for real X.
N HUGE (N) Largest number for numbers like N, e.g. 2??128.
I IACHAR (C) Position of character C in ASCII collation.
B IAND (I,I 2) Logical AND on the bits of I and I 2.
B IBCLR (I,I POS) Clear bit I POS to zero in integer I.
B IBITS (I,I POS,I LEN) Extract an I LEN sequence of bits at I POS in I.
B IBSET (I,I POS) Set bit I POS to one in integer I.
I ICHAR (C) Position of character C in processor collation.
B IEOR (I,I 2) Exclusive OR on the bits of I and I 2.
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (right).
I INT (A [,K]) Convert A to integer type, of given kind.
B IOR (I,I 2) Inclusive OR on the bits of I and I 2.
B ISHFT (I,I SHIFT) Logical shift of bits of I by I SHIFT, pad with 0.
B ISHFTC (I,I SHIFT [,I SIZE]) Logical circular shift of I SIZE rightmost bits of I.
I KIND (ANY) Kind type integer parameter value for any argument.
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along dimension D.
I LEN (S) Total character string length.
I LEN TRIM (S) Length of S without trailing blanks.
L LGE (S,S 2) True if S > or equal to S 2 in ASCII sequence.
L LGT (S,S 2) True if S follows S 2 in ASCII collating sequence.
(continued)
c 2001 J.E. Akin 19
Alphabetic Table of Fortran90 Intrinsic Functions (continued)
Type Intrinsic Description
L LLE (S,S 2) True if S < or equal to S 2 in ASCII sequence.
L LLT (S,S 2 True if S precedes S 2 in ASCII collating sequence.
R LOG (R Z) Natural (base e) logarithm of real or complex number.
L LOGICAL (L [,K]) Convert L to logical of kind K.
R LOG10 (X) Common (base 10) logarithm function of real X.
N,L MATMUL (MATRIX,MATRIX 2) Conformable matrix multiplication.
N MAX (N,N 2 [,N 3,...]) Maximum value of two or more numbers same type.
I MAXEXPONENT (X) Maximum exponent for real numbers like X, e.g. 128.
I,V MAXLOC (N ARRAY [,M]) Location(s) of maximum ARRAY element, passing M.
N MAXVAL (N ARRAY [,D] [,M]) Maximum ARRAY term, in dimension D, passing M.
? MERGE (? TRUE,? FALSE,M) Use ? TRUE when M is true; ? FALSE otherwise.
N MIN (N,N 2 [,N 3,...]) Minimum value of two or more same type numbers.
I MINEXPONENT (X) Minimum exponent for real numbers like X, e.g. –125.
I,V MINLOC (N ARRAY [,M]) Location(s) of minimum ARRAY term, passing M.
N MINVAL (N ARRAY [,D] [,M]) Minimum ARRAY term, in dimension D, passing M.
N MOD (N,N 2) Remainder for N 2. That is, N–INT(N/N 2)?N 2.
N MODULO (N,N 2) Modulo, that is, N–FLOOR(N/N 2)?N 2.
call MVBITS (I FROM,I LOC, Copy I LEN bits at I LOC in I FROM to I TO
I LEN,I TO,I POS) at I POS.
R NEAREST (X,Y) Nearest number at X in the direction of sign Y.
I NINT (X [,K]) Integer nearest to real X, of the stated kind.
I NOT (I) Logical complement of the bits of integer I.
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY at true M into vector, using V PAD.
I PRECISION (R Z) Decimal precision for a real or complex R Z, e.g. 6.
L PRESENT (OPTIONAL) True if optional argument is present in call.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY elements, along D, for mask M.
I RADIX (N) Base of the model for numbers like N, e.g. 2.
call RANDOM NUMBER (X) Pseudo-random numbers in range 0 < X < 1.
call RANDOM SEED ([I SIZE] Initialize random number generator, defaults to
[,I V PUT][,I V GET]) processor initialization.
I RANGE (A) Decimal exponent range in the model for A, e.g. 37.
R REAL (A [,K]) Convert A to real type, of type K.
S REPEAT (S,I COPIES) Concatenates I COPIES of string S.
? RESHAPE (? ARAY,I V SHAP Reshape ARAY, using vector SHAP, pad from
[,? PAD] [,V ORDER]) an array, and re-order.
R RRSPACING (X) Relative spacing reciprocal of numbers near X.
R SCALE (X,I) Return X times b??I, for base of b = RADIX (X).
I SCAN (S,S SET [,L BACK]) Leftmost character index in S found in S SET; (right-
most).
I SELECTED INT KIND (I r) Integer kind with range, –(10??I r) to (10??I r).
I SELECTED REAL KIND Kind for real of decimal precision, I, and exponent
I ([I] [,I r]) range, I r.
R SET EXPONENT (X,I) Number with mantissa of X and exponent of I.
I,V SHAPE (? ARRAY) ARRAY (or scalar) shape vector.
N SIGN (N,N 2) Absolute value of N times sign of same type N 2.
R,Z SIN (R Z) Sine function of real or complex number.
R SINH (X) Hyperbolic sine function of real X.
I SIZE (? ARRAY [,D]) ARRAY size, along dimension D.
R SPACING (X) Absolute spacing of numbers near real X, e.g. 2??–17.
? SPREAD (? ARAY,D,I COPIES) I COPIES along dimension D of ARAY into an array
of rank 1 greater.
(continued)
c 2001 J.E. Akin 20
Alphabetic Table of Fortran90 Intrinsic Functions (continued)
Type Intrinsic Description
R,Z SQRT (R Z) Square root function, of real or complex number.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY elements, along D, passing mask M.
call SYSTEM CLOCK ([I NOW] Integer data from real-time clock. CPU time is
[,I RATE] [,I MAX]) (finish now - start now) / rate.
R TAN (X) Tangent function of real X.
R TANH (X) Hyperbolic tangent function of real X.
R TINY (N) Smallest positive number, like N, e.g. 2??–126.
? TRANSFER (? ARAY, V MOLD Same representation as ARAY, but type of MOLD, in
[,I SIZE]) vector of length SIZE.
? TRANSPOSE (MATRIX) Matrix transpose of any type matrix.
S TRIM (S) Remove trailing blanks from a single string.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
? UNPACK (V,M,? USE) Unpack vector V at true elements of M, into USE.
I VERIFY (S,S SET [,L BACK]) First position in S not found in S SET (or last).
c 2001 J.E. Akin 21
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
ARRAY: LOCATION
I,V MAXLOC (N ARRAY [,M]) Location(s) of maximum ARRAY term, passing M.
I,V MINLOC (N ARRAY [,M]) Location(s) of minimum ARRAY term, passing M.
ARRAY: MANIPULATION
? CSHIFT (? ARRAY,I SHIFT [,D]) Circular shift out and in for I SHIFT elements.
? EOSHIFT (? ARRAY,I SHIFT End-off shift ARRAY, and fill, in dimension D.
[,? FIL][,D])
? TRANSPOSE (MATRIX) Matrix transpose of any type matrix.
ARRAY: MATHEMATICS
N,L DOT PRODUCT (V,V 2) Dot product of vectors V and V 2.
N,L MATMUL (MATRIX,MATRIX 2) Conformable matrix multiplication.
N MAXVAL (N ARRAY [,D] [,M]) Value of max ARRAY term, along D, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of min ARRAY term, along D, passing M.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY terms, along D, for mask M.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY terms, along D, passing mask M.
ARRAY: PACKING
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY for true M into vector, pad from
V PAD.
? UNPACK (V,M,? USE) Unpack V at true elements of M, into USE.
ARRAY: REDUCTION
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ANY (M [,D]) True if any mask, M, term is true, along D.
I COUNT (M [,D]) Number of true mask, M, terms, along dimension D.
N MAXVAL (N ARRAY [,D] [,M]) Value of max ARRAY term, along D, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of min ARRAY term, along D, passing M.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY terms, along D, for mask M.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY terms, along D, passing mask M.
BACK SCAN
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (or right).
I SCAN (S,S SET [,L BACK]) Left character index in S also in S SET (or right).
I VERIFY (S,S SET [,L BACK]) First position in S not belonging to S SET (or last).
BIT: INQUIRY
I BIT SIZE (I) Max number of bits possible in integer I, e.g. 32.
BIT: MANIPULATION
L BTEST (I,I POS) True if bit location I POS of integer I has value one.
B IAND (I,I 2) Logical AND on the bits of I and I 2.
B IBCLR (I,I POS) Clear bit I POS to zero in integer I.
B IBITS (I,I POS,I LEN) Extract I LEN bits at I POS in integer I.
B IBSET (I,I POS) Set bit I POS to one in integer I.
B IEOR (I,I 2) Exclusive OR on the bits of I and I 2.
B IOR (I,I 2) Inclusive OR on the bits of I and I 2.
B ISHFT (I,I SHIFT) Logical shift of bits of I by I SHIFT, pad with 0.
B ISHFTC (I,I SHIFT [,I SIZE]) Logical circular shift of I SIZE rightmost bits of I.
call MVBITS (I GET, I LOC, I,
I TO,I POS) Copy I bits at I LOC in I GET to I TO at I POS.
I NOT (I) Logical complement of the bits of integer I.
? TRANSFER (? ARRAY,
(continued)
c 2001 J.E. Akin 22
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
V MOLD [,I SIZE]) Same representation as ARRAY, but type of MOLD.
BOUNDS
I CEILING (X) Least integer greater than or equal to real X.
I FLOOR (X) Greatest integer less than or equal to X.
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along dimension D.
N MAX (N,N 2 [,N 3,...]) Maximum value of two or more numbers same type.
N MAXVAL (N ARRAY [,D] [,M]) Value of max ARRAY term, along D, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of min ARRAY term, along D, passing M.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
CALLS
call MVBITS (I GET,I LOC,I, Copy I bits at I LOC in I GET to I TO at I POS.
I TO,I POS)
call DATE AND TIME ([S DATE] Real-time clock data.
[,S TIME] [,S ZONE]
[,I V VALUES])
call RANDOM NUMBER (X) Pseudo-random numbers in range 0 < X < 1.
call RANDOM SEED ([I SIZE] Initialize random number generator.
[,I V P] [,I V G])
call SYSTEM CLOCK ([I NOW] Integer data from real-time clock.
[,I RAT] [,I MX])
CHARACTERS
C ACHAR (I) Character in position I of ASCII collating sequence.
C CHAR (I [,K]) Character in position I of processor collation.
I IACHAR (C) Position of character C in ASCII collating sequence.
I ICHAR (C) Position of character C in processor collation.
CLOCK
call SYSTEM CLOCK ([I NOW] Integer data from real-time clock.
[,I RAT] [,I MX])
COMBINING
? MERGE (? TRUE,? FALSE,M) Use ? TRUE term if M is true or ? FALSE
otherwise.
COMPLEX
R AIMAG (Z) Imaginary part of complex number.
Z CMPLX (X [,Y][,K]) Convert real(s) to complex type, of given kind.
Z CONJG (Z) Conjugate of complex number Z.
R COS (R Z) Cosine function of real or complex argument.
R,Z EXP (R Z) Exponential function of real or complex argument.
R LOG (R Z) Natural (base e) logarithm of real or complex num-
ber.
I PRECISION (R Z) Decimal precision of real or complex value, e.g. 6.
R,Z SIN (R Z) Sine function of real or complex number.
R,Z SQRT (R Z) Square root function, of real or complex number.
CONVERSIONS
R AIMAG (Z) Imaginary part of complex number.
R AINT (X [,K]) Truncate X to a real whole number.
Z CMPLX (X [,Y][,K]) Convert real (s) to complex type, of given kind.
R DBLE (A) Convert A to double precision real.
R DPROD (X,Y) Double precision product of two default real scalars.
(continued)
c 2001 J.E. Akin 23
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
I INT (A [,K]) Convert A to integer type, of given kind.
L LOGICAL (L [,K]) Convert L to logical of kind K.
I NINT (X [,K]) Integer nearest to real X, of the stated kind.
R REAL (A [,K]) Convert A to real type, of type K.
N SIGN (N,N 2) Absolute value of N times sign of same type N 2.
? TRANSFER (? ARRAY, Same representation as ARRAY, but type of MOLD.
V MOLD [,I SIZ])
COPIES
? MERGE (? TRUE,? FALSE,M) Use ? TRUE if M is true or ? FALSE otherwise.
call MVBITS (I FROM,I LOC, I, Copy I bits at I LOC in I FROM to I TO at
I TO,I POS) I POS.
S REPEAT (S,I COPIES) Concatenates I COPIES of string S.
? SPREAD (? ARRAY,D,I COPIES) I COPIES along D of ARRAY to rank 1 greater
array.
COUNTING
I COUNT (M [,D]) Number of true mask, M, terms, along dimension D.
DATE
call DATE AND TIME ([S DATE] Real-time clock data.
[,S TIME] [,S ZONE]
[,I V VALUES])
DIMENSION OPTIONAL ARGUMENT
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ANY (M [,D]) True if any mask, M, term is true, along D.
I COUNT (M [,D]) Number of true mask, M, terms, along dimension D.
? CSHIFT (? ARRAY,I SHIFT [,D]) Perform circular shift out and in for I SHIFT terms.
? EOSHIFT (? ARRAY, Perform end-off shift, and fill, in dimension D.
I SHIFT [,? FIL][,D])
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along dimension D.
N MAXVAL (N ARRAY [,D] [,M]) Value of max ARRAY term, along D, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of min ARRAY term, along D, passing M.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY terms, along D, for mask M.
I SIZE (? ARRAY [,D]) ARRAY size, along dimension D.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY terms, along D, passing mask M.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
DIMENSIONS
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along dimension D.
I,V SHAPE (? ARRAY) ARRAY (or scalar) shape vector.
I SIZE (? ARRAY [,D]) ARRAY size, along dimension D.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
DOUBLE PRECISION (see SELECTED REAL KIND)
R DBLE (A) Convert A to double precision real.
R DPROD (X,Y) Double precision product of two default real scalars.
EXISTENCE
L ALLOCATED (? ARRAY) True if the array is allocated.
L ASSOCIATED (P [,T]) True if pointer, P, is associated with any target, or T.
L PRESENT (OPTIONAL) True if optional argument is present in call.
FILE
(continued)
c 2001 J.E. Akin 24
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
FILL IN
? EOSHIFT (? ARRAY,I SHIFT
[,? FIL][,D]) End-off shift ARRAY, and fill, in dimension D.
INQUIRY: ARRAY
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ALLOCATED (? ARRAY) True if the array is allocated.
L ANY (M [,D]) True if any mask, M, term is true, along D.
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along dimension D.
I,V SHAPE (? ARRAY) ARRAY (or scalar) shape vector.
I SIZE (? ARRAY [,D]) ARRAY size, along dimension D.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
INQUIRY: BIT
I BIT SIZE (I) Max number of bits possible in integer I, e.g. 32.
INQUIRY: CHARACTER
I LEN (S) Total character string length.
I LEN TRIM (S) Length of S without trailing blanks.
INQUIRY: NUMBER MODEL
N DIGITS (N) Number of significant digits in number N, e.g. 31.
R EPSILON (X) Number 1, for numbers like X, e.g. 2??–23.
N HUGE (N) Largest number for numbers like N, e.g. 2??128.
I MAXEXPONENT (X) Max exponent for real numbers like X, e.g. 128.
I MINEXPONENT (X) Min exponent for real numbers like X, e.g. –125.
I PRECISION (R Z) Decimal precision for real or complex value, e.g. 6.
I RADIX (N) Base of the model for numbers like N, e.g. 2.
I RANGE (A) Decimal exponent range for A, e.g. 37.
I,V SHAPE (? ARRAY) ARRAY (or scalar) shape vector.
I SIZE (? ARRAY [,D]) ARRAY size, along dimension D.
R TINY (N) Smallest positive number, like N, e.g. 2??–126.
INQUIRY: MISCELLANEOUS
I COUNT (M [,D]) Number of true mask, M, elements, along D.
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (or right).
I SCAN (S,S SET [,L BACK]) Left character index in S also in S SET; (or right).
I VERIFY (S,S SET [,L BACK]) First position in S not belonging to S SET, (or last).
INTEGERS
I CEILING (X) Least integer greater than or equal to real X.
I FLOOR (X) Greatest integer less than or equal to X.
I MAX1 (X,X2 [,X3]) Maximum integer from list of reals
I MIN1 (X,X2 [,X3]) Minimum integer from list of reals
N MODULO (N,N 2) Modulo, N-FLOOR(N/N 2)?N 2.
I SELECTED INT KIND (I r) Integer with exponent, –(10??I r) to (10??I r).
KIND: INQUIRY
I KIND (ANY) Kind type integer parameter value for any argument.
KIND: DEFINITION
I SELECTED INT KIND (I r) Integer with exponent, –(10??I r) to (10??I r).
I SELECTED REAL KIND ([I] Real with precision, I, and exponent range, I r.
[,I r])
KIND: USE OPTION
(continued)
c 2001 J.E. Akin 25
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
R AINT (X [,K]) Truncate X to a real whole number.
R ANINT (X [,K]) Real whole number nearest to X.
C CHAR (I [,K]) Character in position I of processor collation.
Z CMPLX (X [,Y][,K]) Convert real(s) to complex type, of given kind.
I INT (A [,K]) Convert A to integer type, of given kind.
L LOGICAL (L [,K]) Convert L to logical of kind K.
I NINT (X [,K]) Integer nearest to real X, of the stated kind.
R REAL (A [,K]) Convert A to real type, of type K.
LOCATION
I IACHAR (C) Position of character C in ASCII collating sequence.
I ICHAR (C) Position of character C in processor collation.
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (or right).
I,V MAXLOC (N ARRAY [,M]) Vector location(s) of ARRAY maximum, passing M.
I,V MINLOC (N ARRAY [,M]) Vector location(s) of ARRAY minimum, passing M.
I SCAN (S,S SET [,L BACK]) Left character index in S found in S SET; (or right).
LOGICAL
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ALLOCATED (? ARRAY) True if the array is allocated.
L ANY (M [,D]) True if any mask, M, term is true, along D.
L ASSOCIATED (P [,T]) True if pointer, P, is associated with any target, or T.
L BTEST (I,I POS) True if bit location I POS of integer I has value one.
N,L DOT PRODUCT (V,V 2) Dot product of vectors V and V 2.
B IAND (I,I 2) Logical AND on the bits of I and I 2.
B IEOR (I,I 2) Exclusive OR on the bits of I and I 2.
B IOR (I,I 2) Inclusive OR on the bits of I and I 2.
B ISHFT (I,I SHIFT) Logical shift of bits of I by I SHIFT, pad with 0.
L LGE (S,S 2) True if S is S 2 in ASCII collating sequence.
L LGT (S,S 2) True if S follows S 2 in ASCII collating sequence.
L LLE (S,S 2) True if S is to S 2 in ASCII collating sequence.
L LLT (S,S 2) True if S precedes S 2 in ASCII collating sequence.
N,L MATMUL (MATRIX,MATRIX 2) Conformable matrix multiplication.
L LOGICAL (L [,K]) Convert L to logical of kind K.
I NOT (I) Logical complement of the bits of integer I.
L PRESENT (OPTIONAL) True if optional argument is present in call.
MASK, or MASK OPTIONAL ARGUMENT
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ANY (M [,D]) True if any mask, M, term is true, along D.
I COUNT (M [,D]) Number of true mask, M, terms, along dimension D.
I,V MAXLOC (N ARRAY [,M]) Vector of location(s) of ARRAY max’s, passing M.
N MAXVAL (N ARRAY [,D] [,M]) Value of ARRAY maximum, along D, passing M.
? MERGE (? TRUE,? FALSE,M) Use ? TRUE if M is true or ? FALSE otherwise.
I,V MINLOC (N ARRAY [,M]) Vector location(s) of ARRAY minimum, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of ARRAY minimum, along D, passing M.
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY for true M into vector, pad from
V PAD.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY terms, along D, for mask M.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY terms, along D, passing mask M.
MATHEMATICAL FUNCTIONS
R ACOS (X) Arc cosine (inverse cosine) function of real X.
(continued)
c 2001 J.E. Akin 26
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
R ASIN (X) Arcsine (inverse sine) function of real X.
R ATAN (X) Arctangent (inverse tangent) function of real X.
R ATAN2 (Y,X) Arctangent for argument of complex number (X, Y).
R COS (R Z) Cosine function of real or complex argument.
R COSH (X) Hyperbolic cosine function of real X.
R,Z EXP (R Z) Exponential function of real or complex argument.
R LOG (R Z) Natural logarithm of real or complex number.
R LOG10 (X) Common (base 10) logarithm function of real X.
R,Z SIN (R Z) Sine function of real or complex number.
R SINH (X) Hyperbolic sine function of real X.
R TAN (X) Tangent function of real X.
R TANH (X) Hyperbolic tangent function of real X.
MATRICES (See ARRAYS)
N,L DOT PRODUCT (V,V 2) Dot product of vectors V and V 2.
N,L MATMUL (MATRIX,MATRIX 2) Conformable matrix multiplication.
? TRANSPOSE (MATRIX) Matrix transpose of any type matrix.
NUMBER MODEL
N DIGITS (N) Number of significant digits for N, e.g. 31.
R EPSILON (X) Number 1, for numbers like X, e.g. 2??–23.
I EXPONENT (X) Exponent part of the model for real X.
R FRACTION (X) Fractional part of the model for real X.
N HUGE (N) Largest number for numbers like N, e.g. 2??128.
R NEAREST (X,Y) Nearest number at X in the direction of sign Y.
I RADIX (N) Base of the model for numbers like N, e.g. 2.
I RANGE (A) Decimal exponent range for A, e.g. 37.
R RRSPACING (X) Reciprocal of relative spacing of numbers near X.
R SCALE (X,I) Return X times b??I, where base b = RADIX (X).
R SET EXPONENT (X,I) Real with mantissa part of X and exponent part of I.
R SPACING (X) Absolute spacing of numbers near X, e.g. 2??-17.
R TINY (N) Smallest positive number, like N, e.g. 2??–126.
NUMERIC FUNCTIONS
A ABS (A) Absolute value of A.
R AIMAG (Z) Imaginary part of complex number.
R ANINT (X [,K]) Real whole number nearest to X.
I CEILING (X) Least integer greater than or equal to real X.
Z CMPLX (X [,Y][,K]) Convert real(s) to complex type, of given kind.
Z CONJG (Z) Conjugate of complex number Z.
R DBLE (A) Convert A to double precision real.
R DPROD (X,Y) Double precision real product of two real scalars.
I FLOOR (X) Greatest integer less than or equal to X.
I INT (A [,K]) Convert A to integer type, of given kind.
N MAX (N,N 2 [,N 3,...]) Maximum value of two or more numbers same type.
N MIN (N,N 2 [,N 3,...]) Minimum value of two or more same type numbers.
N MOD (N,N 2) Remainder for N 2, i.e., N-INT(N/N 2)?N 2.
N MODULO (N,N 2) Modulo, N-FLOOR(N/N 2)?N 2.
R REAL (A [,K]) Convert A to real type, of type K.
N SIGN (N,N 2) Absolute value of N times sign of same type N 2.
PADDING
B ISHFT (I,I SHIFT) Logical shift of bits of I by I SHIFT, pad with 0.
(continued)
c 2001 J.E. Akin 27
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY for true M into vector, pad from V PAD.
? RESHAPE (? ARRAY,I V SHAPE
[,? PAD] [,V ORDER]) Reshape ARRAY to vector SHAPE, pad, re-order.
POINTER
L ASSOCIATED (P [,T]) True if pointer, P, is associated with any target, or T.
PRESENCE
L PRESENT (OPTIONAL) True if optional argument is present in call.
RANDOM NUMBER
call RANDOM NUMBER (X) Pseudo-random numbers in range 0 < X < 1.
call RANDOM SEED ([I SIZE]
[,I V P][,I V G]) Initialize random number generator.
REALS
R AINT (X [,K]) Truncate X to a real whole number.
R ANINT (X [,K]) Real whole number nearest to X.
R AMAX0 (I,I2 [,I3]) Maximum real from list of integers.
R AMIN0 (I,I2 [,I3]) Minimum real from list of integers.
R REAL (A [,K]) Convert A to real type, of type K.
I SELECTED REAL KIND ([I] Real with precision, I, and exponent range, I r.
[,I r])
REDUCTION
L ALL (M [,D]) True if all mask, M, terms are true, along D.
L ANY (M [,D]) True if any mask, M, term is true, along D.
I COUNT (M [,D]) Number of true mask, M, terms, along dimension D.
N MAXVAL (N ARRAY [,D] [,M]) Value of max ARRAY term, along D, passing M.
N MINVAL (N ARRAY [,D] [,M]) Value of min ARRAY term, along D, passing M.
A PRODUCT (A ARRAY [,D] [,M]) Product of ARRAY terms, along D, for mask M.
A SUM (A ARRAY [,D] [,M]) Sum of ARRAY terms, along D, passing mask M.
RESHAPING ARRAYS
? CSHIFT (? ARRAY,I SHIFT [,D]) Perform circular shift out and in for I SHIFT terms.
? EOSHIFT (? ARRAY,I SHFT
[,? FIL] [,D]) End-off shift ARRAY, and fill, in dimension D.
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY for true M into vector, pad from
V PAD.
? RESHAPE (? ARRAY,I V SHAPE
[,? PAD] [,V ORDER]) Reshape ARRAY to vector SHAPE, pad, re-order.
? UNPACK (V,M,? USE) Unpack V for true elements of M, into USE.
REVERSE ORDER
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (right-
most).
I SCAN (S,S SET [,L BACK]) Left character index in S found in S SET; (right-
most).
I VERIFY (S,S SET [,L BACK]) First position in S not found in S SET, (or last).
SHIFTS
? CSHIFT (? ARRAY,I SHIFT [,D]) Perform circular shift out and in for I SHIFT terms.
? EOSHIFT (? ARRAY,I SHIFT
[,? FILL][,D]) Perform end-off shift, and fill, in dimension D.
B ISHFT (I,I SHIFT) Logical shift of bits of I by I SHIFT, pad with 0.
B ISHFTC (I,I SHIFT [,I SIZE]) Logical circular shift of I SIZE rightmost bits of I.
(continued)
c 2001 J.E. Akin 28
Subject Table of Fortran 90 Intrinsic Functions (continued)
Type Intrinsic Description
STRING
C ADJUSTL (S) Adjust S left, move leading blanks to trailing blanks.
C ADJUSTR (S) Adjust S right, move trailing to leading blanks.
I INDEX (S,S SUB [,L BACK]) Left starting position of S SUB within S (or right).
I LEN (S) Total character string length.
I LEN TRIM (S) Length of S without trailing blanks.
L LGE (S,S 2) True if S is to S 2 in ASCII collating sequence.
L LGT (S,S 2) True if S follows S 2 in ASCII collating sequence.
L LLE (S,S 2) True if S is to S 2 in ASCII collating sequence.
L LLT (S,S 2) True if S precedes S 2 in ASCII collating sequence.
S REPEAT (S,I COPIES) Concatenates I COPIES of string S.
I SCAN (S,S SET [,L BACK]) Left character index in S found in S SET; (or right).
S TRIM (S) Remove trailing blanks from a single string.
I VERIFY (S,S SET [,L BACK]) First position in S not found in S SET, (or last).
TARGET
L ASSOCIATED (P [,T]) True if pointer, P, is associated with any target, or T.
TIME
call DATE AND TIME ([S DATE] Real-time clock data.
[,S TIME] [,S ZONE]
[,I V VALUES])
call SYSTEM CLOCK ([I NOW] Integer data from real-time clock.
[,I RAT] [,I MX])
VECTOR (See ARRAYS)
N,L DOT PRODUCT (V,V 2) Dot product of vectors V and V 2.
I,V LBOUND (? ARRAY [,D]) ARRAY lower bound(s) vector, along D.
I,V MAXLOC (N ARRAY [,M]) Location(s) of maximum ARRAY term, passing M.
I,V MINLOC (N ARRAY [,M]) Location(s) of minimum ARRAY term, passing M.
?,V PACK (? ARRAY,M [,V PAD]) Pack ARRAY for true M into vector, pad from
V PAD.
? RESHAPE (? ARRAY,I V SHAPE
[,? PAD] [,V ORDER]) Reshape ARRAY to vector SHAPE, pad, re-order.
I,V SHAPE (? ARRAY) ARRAY (or scalar) shape vector.
? TRANSFER (? ARRAY, V MOLD
[,I SIZE]) Same representation as ARRAY, but type of MOLD.
I,V UBOUND (? ARRAY [,D]) ARRAY upper bound(s) vector, along dimension D.
c 2001 J.E. Akin 29
M ATLAB C++ F90
a
Pre-allocate A(100)=0 int A[100]; integer A(100)
linear array
Initialize to a for j=1:100 % slow for (j=0; j<100; j++) A=12
A(j)=12
constant value of end A[j]=12;
12 % better way
A=12*ones(1,100)
Pre-allocate A=ones(10,10) int A[10][10]; integer A(10,10)
two-dimensional
array
a C++ has a starting subscript of 0, but the argument in the allocation statement is the array’s size.
g;
standard F77 statements are a sub-set of F90. Attribute options, and their specifiers, for each statement
are given in the companion table ”Fortran 90 Attributes and Specifiers”. The numerous options for the
INQUIRE statement are given in the table entitled “Options for F90 INQUIRE.”
In addition to the statements given below F90 offers intrinsic array operations, implied do loops,
vector subscripts, and about 160 intrinsic functions. Those functions, with their arguments, are given
in tables “Alphabetical Table of Fortran 90 Intrinsic Functions and Subroutines,” and “Subject Table of
Fortran 90 Intrinsic Functions and Subroutines.”
F90 Syntax
! preceeds a comment in F90
in column one denotes a comment line in F77
& continues a line in F90 (must be in column 6 for F77)
; terminates a statement in F90 (allows multiple statements per line)
variable = expression or statement ! is an assignment (column 7 in F77)
ALLOCATABLE [::] array name[(extents)] [, array name[(extents)]]
ALLOCATE (array name)
ALLOCATE (array name [, STAT=status] [,array name [, STAT=status]])
BACKSPACE i exp ! file unit number
BACKSPACE ([UNIT=]i value [, IOSTAT=i variable] [, ERR=i label])
C in column one denotes a comment line in F77
CALL subroutine name [([args])]
CASE (range list) [select name] ! purpose
CASE DEFAULT [select name] ! purpose
CHARACTER LEN=i value [::] s list
CHARACTER [(LEN=i value j * [, KIND=]i kind)] [[, attr list] ::] s list
CHARACTER [(i value j *, [KIND=]i kind)] [[, attr list] ::] s list
(continued)
c 2001 J.E. Akin 30
F90 Syntax (continued)
CHARACTER [([KIND=i kind] [, LEN=i value j *])] [[, attr list] ::] s list
CLOSE (i value) ! unit number
CLOSE ([UNIT=]i value [, ERR=i label] [, IOSTAT=i variable] [, STATUS=exp])
COMPLEX [::] variable list
COMPLEX [([KIND=]i kind)] [[, attr list] ::] variable list
CONTAINS ! internal definitions follow
CYCLE ! current do only for a purpose
CYCLE [nested do name] ! and terminate its sub do’s for a purpose
DEALLOCATE (array name)
DEALLOCATE (array name [, STAT=status] [, array name [, STAT=status]])
DIMENSION array name(extents) [, array name(extents)]
DO ! forever
DO i variable = i start, i stop ! loop name or purpose
DO [i variable = i start, i stop [, i inc]] ! loop name or purpose
DO [i label,] [i variable = i start, i stop [, i inc]] ! loop name
[loop name:] DO [i variable = i start, i stop [, i inc]] ! purpose
[loop name:] DO [i label,] [i variable = i start, i stop [, i inc]]
DO WHILE (logical expression) ! obsolete, use DO-EXIT pair
DO [i label,] WHILE (logical expression) ! obsolete-obsolete
[ name:] DO [i label,] WHILE (logical expression) ! obsolete
ELSE [if name]
ELSE IF (logical expression) THEN [if name]
ELSE WHERE (logical expression)
END [name] ! purpose
END DO [do name] ! purpose
END FUNCTION [function name] ! purpose
END IF [if name] ! purpose
END INTERFACE ! purpose
END MODULE [module name] ! purpose
END PROGRAM [program name] ! purpose
END SELECT [select name] ! purpose
END SUBROUTINE [name] ! purpose
END TYPE [type name] ! purpose
END WHERE ! purpose
ENDFILE i exp ! for file unit number
ENDFILE ([UNIT=]i value [, IOSTAT=i variable] [, ERR=i label])
ENTRY entry name [([args])] [RESULT(variable name)]
EXIT ! current do only for a purpose
EXIT [nested do name] ! and its sub do’s for a purpose
EXTERNAL program list
i label FORMAT (specification and edit list)
FUNCTION name ([args]) ! purpose
FUNCTION name ([args]) [RESULT(variable name)] ! purpose
[type] [RECURSIVE] FUNCTION name ([args]) [RESULT(variable name)]
[RECURSIVE] [type] FUNCTION name ([args]) [RESULT(variable name)]
GO TO i label ! for a reason
IF (logical expression) executable statement
[name:] IF (logical expression) THEN ! state purpose
IMPLICIT type (letter list) ! F77 (a-h,o-z) real, (i-n) integer
IMPLICIT NONE ! F90 recommended default
INCLUDE source file path name ! purpose
INQUIRE ([FILE=]’name string’ [, see INQUIRE table]) ! re file
(continued)
c 2001 J.E. Akin 31
F90 Syntax (continued)
INQUIRE ([NAME=]s variable [, see INQUIRE table]) ! re file
INQUIRE (IOLENGTH=i variable [, see INQUIRE table]) ! re output
INQUIRE ([UNIT=]i value [, see INQUIRE table]) ! re unit
INTEGER [::] variable list
INTEGER [([KIND=]i kind)] [[, attr list] ::] variable list
INTENT ([IN j INOUT j OUT]) argument list
INTERFACE ASSIGNMENT (+ j - j * j / j = j **) ! user extension
INTERFACE OPERATOR (.operator.) ! user defined
INTERFACE [interface name]
INTRINSIC function list
LOGICAL [::] variable list
LOGICAL [([KIND=]i kind)] [[, attr list] ::] variable list
MODULE PROCEDURE program list
MODULE module name ! purpose
NULLIFY (pointer list)
OPEN (i value) ! unit number
OPEN ([UNIT=]i value [, ERR=i label] [, IOSTAT=i variable] [, other spec])
OPTIONAL [::] argument list
PARAMETER (variable=value [, variable=value])
POINTER [::] name[(extent)] [, name[(extent)]] ! purpose
PRINT * , output list ! default free format
PRINT * , (io implied do) ! default free format
PRINT ’(formats)’ , output list ! formatted
PRINT ’(formats)’ , (io implied do) ! formatted
PRIVATE [[::] module variable list] ! limit access
PROGRAM [program name] ! purpose
PUBLIC [[::] module variable list] ! default access
READ * , input list ! default free format
READ * , (io implied do) ! default free format
READ ’(formats)’, input list ! formatted
READ ’(formats)’, (io implied do) ! formatted
READ ([UNIT=]i value, [FMT=]i label [, io spec list]), input list ! formatted
READ ([UNIT=]i value, s variable [, io spec list]), input list ! formatted
READ ([UNIT=]i value, ’(formats)’ [, io spec list]), input list ! formatted
READ (i value), input list ! binary read
READ ([UNIT=]i value, [, io spec list]), input list ! binary read
READ (s variable, [FMT=]i label), input list ! internal file type change
READ ([UNIT=]s variable, [FMT=]i label [, io spec list]), input list ! internal file change
REAL [::] variable list
REAL [([KIND=]i kind)] [[, attr list] ::] variable list
RECURSIVE FUNCTION name ([args]) [RESULT(variable name)] ! purpose
[ type] RECURSIVE FUNCTION name ([args]) [RESULT(variable name)] ! purpose
RECURSIVE SUBROUTINE name [([args])] ! purpose
RETURN ! from subroutine name
REWIND i exp ! file unit number
REWIND ([UNIT=]i value [, IOSTAT=i variable] [, ERR=i label])
SAVE [[::] variable list]
[name:] SELECT CASE (value)
SEQUENCE
STOP [’stop message string’]
SUBROUTINE name [([args])] ! purpose
SUBROUTINE name [([args])] [args, optional args] ! purpose
(continued)
c 2001 J.E. Akin 32
F90 Syntax (continued)
[RECURSIVE] SUBROUTINE name [([args])] ! purpose
TARGET [::] name[(extent)] [, name[(extent)]]
TYPE (type name) [[, attr list] ::] variable list
TYPE [, PRIVATE j PUBLIC] name
USE module name [, ONLY: list in module name] ! purpose
USE module name [, new var or sub=>old name] ! purpose
WHERE (logical array expression) ! then
WHERE (logical array expression) array variable = array expression
WRITE * , output list ! default free format
WRITE * , (io implied do) ! default free format
WRITE ’(formats)’, output list ! formatted write
WRITE ’(formats)’, (io implied do) ! formatted write
WRITE ([UNIT=]i value, [FMT=]i label [, io spec list]), output list ! formatted write
WRITE ([UNIT=]i value, s variable [, io spec list]), output list ! formatted write
WRITE ([UNIT=]i value, ’(formats)’ [, io spec list]), output list ! formatted write
WRITE (i value), output list ! binary write
WRITE (i value), (io implied do) ! binary write
WRITE ([UNIT=]i value, [, io spec list]), output list ! binary write
WRITE (s variable, [FMT=]i label), output list ! internal file type change
WRITE ([UNIT=]s variable, [FMT=]i label [, io spec list]), output list ! internal file change
Obsolescent statements are those from Fortran77 that are redundant and for which better methods are
available in both Fortran77 and Fortran90.
Obsolete Syntax
ASSIGN i label TO i variable
BLOCK DATA [block data name]
COMMON [/common block name/] r variable list, i variable list
[i label] CONTINUE ! from do [do name]
DATA variable list / value list /
DATA (array implied do) / value list /
DOUBLE PRECISION [[, attr list] :: ] variable list
DO [i label,] [r variable = r start, r stop [, r inc]] ! real control
DO CONTINUE pair
[name:] DO [i label,] WHILE (logical expression) ! obsolete
END BLOCK DATA [block data name]
EQUIVALENCE (variable 1, variable 2) [ , (variable 3, variable 4)]
GO TO (i label 1,i label 2,...,i label n)[ , ] i variable
IF (arithmetic exp) i label neg, i label zero, i label pos
NAMELIST /group name/ variable list
PAUSE ! for human action
RETURN alternates
statement function (args) = expression
The attributes lists for the type declarations, e.g. REAL, are ALLOCATABLE, DIMENSION, INTENT,
OPTIONAL, KIND, POINTER, PARAMETER, PRIVATE, PUBLIC, SAVE, and TARGET; those for OPEN and
CLOSE are ACCESS, ACTION, BLANK, and DELIM; while those for READ and WRITE are ADVANCE, END,
EOR, ERR, and FMT.
c 2001 J.E. Akin 33
M ATLAB C++ F90
Addition
C=A+B C=A+B for (i=0; i<10; i++) f C=A+B
for (j=0; j<10; j++) f
g
C[i][j]=A[i][j]+B[i][j];
g
Multiplication
f
C = AB C=A*B for (i=0; i<10; i++)
for (j=0; j<10; j++) f
C=matmul(A,B)
f
C[i][j] = 0;
for (k=0; k<10; k++)
g
C[i][j] += A[i][k]*B[k][j];
g
g
Scalar
multiplication C=a*B for (i=0; i<10; i++) f C=a*B
C= B a
for (j=0; j < 10; j++) f
g
C[i][j] = a*B[i][j];
g
Matrix
inverse
B=A B=inv(A)a
B=inv(A) a
1
a Neither C++ nor F90 have matrix inverse functions as part of their language definitions nor as part of standard collections
of mathematical functions (like those listed in Table 4.7). Instead, a special function, usually drawn from a library of numerical
functions, or a user defined operation, must be used.
f g
vector = new type tag [space 1]
if (vector == 0) error process
matrix = new type tag [space 1 * space 2]
...
delete matrix
...
delete vector
delete point
c 2001 J.E. Akin 34
SUBROUTINE AUTO ARRAYS (M,N, OTHER)
USE GLOBAL CONSTANTS ! FOR INTEGER K
IMPLICIT NONE
INTEGER, INTENT (IN) :: M,N
type tag, INTENT (OUT) :: OTHER (M,N) ! dummy array
! Automatic array allocations
type tag :: FROM USE (K)
type tag :: FROM ARG (M)
type tag :: FROM MIX (K,N)
...
! Automatic deallocation at end of scope
END SUBROUTINE AUTO ARRAYS
c 2001 J.E. Akin 35
module derived class name
use base1 class name
use base2 class name
use base3 class name, only: list of entities
use base4 class name, local name => base entity name
! new attribute declarations, if any
...
contains
c 2001 J.E. Akin 36
Examples of F90 Statements
The following is a list of examples of the recommended Fortran90 statements. Some have been
declared obsolete, and are expected to be deleted in future standards. Thus, they should not be utilized
in new programs. They are noted in the comments. In some cases the most common simple form
of a statement is shown along with it’s more general options. Note that the new attribute terminator
symbol :: is always optional, but its use is recommended. While Fortran is not case-sensitive, this
table employs upper case letters to denote standard features, and lower case letters for user supplied
information. The following abbreviations are employed: arg=argument, attr=attribute, exp=expression,
i =integer, l =logical, r =real, s =string, spec=specifier, z =complex.
Recall that F90 allows variable names to be 31 characters long and they may include an underscore
(but F77 allows only six characters and no underscore). F90 lines may contain up to 132 characters (but
just 72 in F77). All standard F77 statements are a sub-set of F90.
The attributes lists for the type declariations, e.g. REAL, are ALLOCATABLE, DIMENSION, INTENT,
OPTIONAL, KIND, POINTER, PARAMETER, PRIVATE, PUBLIC, SAVE, and TARGET. Those op-
tional attributes for OPEN are ACCESS = [DIRECT, SEQUENTIAL], ACTION = [READ, READWRITE,
WRITE], BLANK = [NULL, ZERO], DELIM = [APOSTROPHE, NONE, QUOTE], ERR = i label, FILE =
s name, FORM = [FORMATTED, UNFORMATTED], IOSTAT = i var, PAD = [NO, YES], POSITION =
[APPEND, ASIS, REWIND], RECL = i len, STATUS = [NEW, OLD, REPLACE, SEARCH, UNKNOWN],
and UNIT = i unit; while CLOSE utilizes only ERR, IOSTAT, STATUS, and UNIT.
The io spec list options for READ and WRITE are ADVANCE = [NO, YES], END = i label, EOR =
i label, ERR = i label, FMT = [*, i label, s var], IOSTAT = i var, NML = var list, REC = i exp, SIZE
= i size, and UNIT = i unit.
c 2001 J.E. Akin 38
Fortran Statement Examples (continued)
Name Examples Comments
CLOSE (9, ERR=99, IOSTAT=io, STATUS=’DELETE’) File status
CLOSE (UNIT=8, ERR=95, IOSTAT=io ok)
Common COMMON / name / h, p, t ! Obsolete Named common
COMMON p, d, q(m,n) ! Obsolete Blank common
Complex COMPLEX u, v, w(3, 6) :: recommended
COMPLEX :: u = (1.0,1.0), v = (1.0,10.0) Initialize u and v
COMPLEX :: variable list
COMPLEX attr list :: variable list
COMPLEX (KIND=i2 kind), attr list :: variable list Kind
Contains CONTAINS Internal definitions
CONTAINS
FUNCTION mine (b) Or subroutines
:::
END DO
DO ! forever Unlabeled do
:::
END DO ! forever
DO WHILE (diff <= delta) Unlabeled while
:::
END DO
DO 100 WHILE (diff < = delta) ! Obsolete Labeled while
(continued)
c 2001 J.E. Akin 39
Fortran Statement Examples (continued)
Name Examples Comments
:::
c 2001 J.E. Akin 40
Fortran Statement Examples (continued)
Name Examples Comments
Entry ENTRY sec1 (x, y) Arguments
ENTRY sec2 (a1, a2, *4) ! Obsolete, use CASE Alternate return to 4
ENTRY section No arguments
ENTRY entry name RESULT(variable name) Result
Equivalence EQUIVALENCE (v (1), a (1,1)) Obsolete
EQUIVALENCE (v, a)
EQUIVALENCE (x, v(10)), (p, q, d)
Exit EXIT Current do only
EXIT nested do name Current & sub-dos
External EXTERNAL my program
Format 10 FORMAT (2X, 2I3, 3F6.1, 4E12.2, 2A6, 3L2 ) XIFEAL
10 FORMAT (// 2D6.1, 3G12.2) D, G
10 FORMAT (2I3.3, 3G6.1E3, 4E12.2E3) Exponent w
10 FORMAT (’a quoted string’, ”another”, I2) Strings
10 FORMAT (1X, T10, A1, T20, A1) Tabs
10 FORMAT (5X, TR10, A1, TR10, A1, TL5, A1) Tab right, left
10 FORMAT (”Init=”, I2, :, 3X, ”Last=”, I2) : stop if empty
10 FORMAT (’Octal ’, o6, ’, Hex ’ z6) Octal, hex
10 FORMAT (specification and edit list)
Function FUNCTION z (a, b) Arguments
FUNCTION w (e, d) RESULT (a) Result
FUNCTION name (args)
FUNCTION name No argument
FUNCTION name (args) RESULT(variable name)
INTEGER FUNCTION n (j, k) Type
INTEGER FUNCTION name (args)
COMPLEX RECURSIVE FUNCTION dat (args)
RECURSIVE REAL FUNCTION name (args)
Go To GO TO 99 Unconditional
GO TO (10,20,35,95), i variable ! Obsolete Computed
If IF (arithmetic exp) 95, 10, 20 ! Obsolete Arithmetic
IF (logic) RETURN Logical if
IF (logic) n = n + 2
IF (logic) THEN if block
n=n+1
k=k+1
END IF
leap year: IF (logical expression) THEN Named
IF (logic) THEN if else block
n=n+1
ELSE
k=k+1
END IF
IF (c == ’a’) THEN if else-if block
na = na + 1
CALL sub a
ELSE IF (c == ’b’) THEN (Use CASE)
nb = nb + 1
ELSE IF (c == ’c’) THEN
nc = nc + 1
(continued)
c 2001 J.E. Akin 41
Fortran Statement Examples (continued)
Name Examples Comments
CALL sub c
END IF
Implicit IMPLICIT INTEGER (i-n) F77 default
Type IMPLICIT REAL (a-h,o-z) F77 default
IMPLICIT NONE Recommended F90
IMPLICIT CHARACTER *10 (f,l) Character
IMPLICIT COMPLEX (a-c,z) Complex
IMPLICIT TYPE (color) (b,g,r) Derived type
IMPLICIT LOGICAL (KIND=bit) (m) Logical
Include INCLUDE ’path/source.f’
Inquire INQUIRE (UNIT=3, OPENED=t or f) Opened
INQUIRE (FILE=’mydata’, EXIST=t or f) Exists
INQUIRE (UNIT=3, OPENED=ok, IOSTAT=k) I/O status
INQUIRE (FILE=’name string’, see INQUIRE table) Re file
INQUIRE (NAME=s variable, see INQUIRE table) Re file
INQUIRE (IOLENGTH=i var, see INQUIRE table) Re output
INQUIRE (7, see INQUIRE table) Re unit
INQUIRE (UNIT=8, see INQUIRE table) Re unit
Integer INTEGER c, d(4) :: Recommended
INTEGER (long), attr list :: variable list
INTEGER, DIMENSION (4) :: a, d, e
INTEGER, ALLOCATABLE, DIMENSION(:,:) :: a, b Allocatable
INTEGER :: a = 100, b, c = 9 Initialize a & c
INTEGER :: i, j, k, l, m, n, month, year = 1996
INTEGER, attr list :: variable list
INTEGER (KIND=i2 kind), attr list :: variable list Kind
Intent INTENT (IN) :: credit card owners
INTENT (INOUT) :: amount due
INTENT (OUT) income rank
Interface INTERFACE ASSIGNMENT (=) User extension
INTERFACE OPERATOR (+) User extension
INTERFACE OPERATOR (–) User extension
INTERFACE OPERATOR (/) User extension
INTERFACE OPERATOR (*) User extension
INTERFACE OPERATOR (**) User extension
INTERFACE OPERATOR (.operator.) User defined
INTERFACE
INTERFACE interface name
Intrinsic INTRINSIC SQRT, EXP Functions
Logical LOGICAL c :: recommended
LOGICAL, ALLOCATABLE :: mask(:), mask 2(:,:) Allocatable
LOGICAL (KIND = byte) :: flag, status Kind
LOGICAL :: b = .FALSE., c Initialize b
Module MODULE PROCEDURE mat x mat, mat x vec Generics
MODULE my matrix operators
Namelist NAMELIST /data/ s, n, d Obsolete
Nullify NULLIFY (pointer list)
Open OPEN (7) Unit number
OPEN (UNIT=3, FILE="data.test") Name
OPEN (UNIT=2, FILE="data", STATUS = "old") File status
(continued)
c 2001 J.E. Akin 42
Fortran Statement Examples (continued)
Name Examples Comments
OPEN (UNIT=3, IOSTAT=k) I/O status
OPEN (9, ERR = 12, ACCESS ="direct") Access type
OPEN (8, ERR=99, IOSTAT=io ok) Error go to
OPEN (UNIT=8, ERR=99, IOSTAT=io ok)
Optional OPTIONAL slow, fast Argument list
OPTIONAL :: argument list
Parameter PARAMETER (a="xyz"), (pi=3.14159) Character
PARAMETER (a=”z”, pi=3.14159) Real
PARAMETER (x=11, y = x/3) Computed
PARAMETER, REAL :: weight = 245.6 Type
Pause PAUSE ! for human action Obsolete
Pointer POINTER current, last :: recommended
POINTER :: name(4,5) Rank
REAL, POINTER :: y(:), x(:,:,:) Type
Print PRINT *, a, j List-directed
PRINT *, output list Default unformatted
PRINT *, (io implied do) Implied do
PRINT *, “The squre root of”, n, ’is’, SQRT(n) Function
PRINT *, (4*k-1, k=1,10,3)
PRINT 10, a, j Formatted
PRINT 10, m array Array
PRINT 10, (m(i), i = j,k) Implied do
PRINT 10, s(j:k) Substring
PRINT ’(A6, I3)’, a, j Character, integer
PRINT FMT=’(A6, I3)’, a, j Included format
PRINT data namelist ! Obsolete Namelist
PRINT ’(formats)’, output list Formatted
PRINT ’(formats)’, (io implied do) Implied do
PRINT ’(I4)’, (2*k, k=1,5)
Private PRIVATE
PRIVATE :: module variable list Specific items
Program PROGRAM my job
PROGRAM
Public PUBLIC
PUBLIC :: module variable list Specific items
Read READ *, a, j List-directed
READ 1, a , j Formatted
READ 10, m array Formatted array
READ 10, (m(i), i=j, k) Implied do
READ 10, s(i:k) Substring
READ ’(A6, I3)’, a, i Character, integer
READ (1, 2) x, y Formatted file
READ (UNIT=1, FMT=2) x, y
READ (1, 2, ERR=8, END=9) x, y End of file go to
READ (UNIT=1, FMT=2, ERR=8, END=9) x, y Error go to
READ (*, 2) x, y Formatted, std out
READ (*, 10) m array Unformatted array
READ (*, 10) (m(i), i=j, k) Implied do
READ (*, 10) s(i:k) Substring
READ (1, *) x, y Unformatted file
(continued)
c 2001 J.E. Akin 43
Fortran Statement Examples (continued)
Name Examples Comments
READ (*, *) x, y Unformatted, std out
READ (1, ’(A6, I3)’) x, y Character, integer
READ (1, FMT=’(A6, I3)’) x, y Included format
READ (1, s fmt) x, y Format in a string
READ (1, FMT=s fmt) x, y
READ (*, NML=data) ! Obsolete Namelist read
READ (1, NML=data) ! Obsolete Namelist from a file
READ (1, END=8, ERR=9) x, y Unformatted
READ (s2, 1, ERR=9) x Internal, formatted
READ (s2, *, ERR=9) x Unformatted
READ (s2, REC=4, END=8) x Internal, direct
READ (1, REC=3) v Unformatted direct
READ (1, 2, REC=3) v Formatted direct
READ *, input list Default unformatted
READ *, (io implied do) Implied do
READ *, (a(j,:), j=1, rows)
READ ’(formats)’, input list Formatted read
READ ’(formats)’, (io implied do) Formatted read
READ ’(5I5, (5I5))’, (num(k), k=1, n)
READ (8, FMT=20), input list Formatted
READ (8, FMT=20, ADVANCE=’NO’), input Advance
READ (9, FMT=20, io spec list), input list I/O Specification
READ (UNIT=7, 20, io spec list), input list
READ (UNIT=8, FMT=10, io spec list), input
READ (7, s fmt, io spec list), input list Stored format
READ (UNIT=7, s fmt, io spec list), input
READ (9, ’(formats)’, io spec list), input list Inline format
READ (UNIT=9, ’(formats)’, io spec list), input
READ (8), input list Binary read
READ (UNIT=7), input list
READ (8, io spec list), input list I/O Specification
READ (UNIT=9, io spec list), input list
READ (s variable, FMT=20), input list Internal file,
READ (UNIT=s variable, 10, io spec list), input type change
Real REAL*4 :: recommended
REAL :: r, m(9)
REAL*16 a, b, c Quad Precision
REAL*8, DIMENSION(n) :: a, b, c Double Precision
REAL :: a = 3.14, b, c = 100.0 Initialize a & c
REAL :: variable list
REAL, attr list :: variable list
REAL, POINTER :: a(:,:)
REAL (KIND=i2 kind), attr list :: variable list Kind
REAL (double), attr list :: variable list
Recursive RECURSIVE FUNCTION name Function
RECURSIVE FUNCTION a(n) RESULT(fac) Result
INTEGER RECURSIVE FUNCTION name (args)
RECURSIVE SUBROUTINE name (args) Subroutine
RECURSIVE SUBROUTINE name
Return RETURN Standard return
(continued)
c 2001 J.E. Akin 44
Fortran Statement Examples (continued)
Name Examples Comments
Rewind REWIND i exp Compute unit
REWIND 2 Unit number
REWIND k
REWIND (UNIT=8, IOSTAT=k, ERR=9) Error go to
REWIND (UNIT=8, ERR=95)
REWIND (8, IOSTAT=io ok, ERR=99) I/O status
Save SAVE a, /name/, c Scalars, common
SAVE Everything
SAVE :: variable list
Select Case SELECT CASE (value)
name: SELECT CASE (value) Named
u or l SELECT CASE (letter) Block
CASE ("a":"z") ! lower case
lower = .TRUE.
CASE ("A":"Z") ! upper case
lower = .FALSE.
CASE DEFAULT ! not a letter
PRINT *, "Symbol is not a letter", letter
lower = .FALSE.
END SELECT u or l
Sequence SEQUENCE Forced storage
Stop STOP
STOP "invalid data" With message
Subroutine SUBROUTINE sub1 (a, b)
SUBROUTINE sub1 No arguments
SUBROUTINE name (args, optional args) Optional arguments
SUBROUTINE sub3 (a, b, *9) ! Obsolete, use CASE Return to 9
RECURSIVE SUBROUTINE sub2 (a, b) Recursive
Target TARGET :: name, name 2 See Pointer
TARGET :: name(4,5), name 2(3)
Type TYPE (person) car pool(5) User defined type
Declaration TYPE (color), DIMENSION(256) :: hues
TYPE (type name), attr list :: variable list
TYPE (person), DIMENSION (n) :: address book
TYPE (type name) :: variable list
TYPE (student record) Definition block
CHARACTER (name len) :: last, first
INTEGER :: rank
END TYPE student record
Type TYPE, PRIVATE name Access
Statement TYPE, PUBLIC :: name
Use USE module name
USE module name, ONLY: list in module name Only
USE module name, var subr fun name => old name Rename
Where WHERE (logical array mask) Then
WHERE ( a array > 0.0 ) Where block
sqrt a = SQRT(a array)
END WHERE
WHERE ( mask > 0.0 ) Elsewhere block
a array = mask
(continued)
c 2001 J.E. Akin 45
Fortran Statement Examples (continued)
Name Examples Comments
ELSEWHERE
a array = 0.0
END WHERE
WHERE (a array>0) b array = SQRT(a array) Statement
Write WRITE (*, 10) s(j:k) Substring
WRITE (1, *) x, y Unformatted file
WRITE (*, *) x, y Unformatted
WRITE (1, ’(A6, I3)’) x, y Character, integer
WRITE (1, FMT=’(A6, I3)’) x, y Included format
WRITE (1, s fmt) x, y Stored format string
WRITE (1, FMT=s fmt) x, y
WRITE (*, NML=data) ! Obsolete Namelist to stdout
WRITE (1, NML=data) ! Obsolete Namelist to a file
WRITE (1, END=8, ERR=9) x, y Unformatted
WRITE (1, REC=3) v Unformatted direct
WRITE (1, 2, REC=3) v Formatted direct
WRITE (s2, 1, ERR=9) x Internal, format
WRITE (s2, *, ERR=9) x Unformatted
WRITE (s2, REC=4, END=8) x Internal, direct
WRITE *, output list Unformatted
WRITE *, (io implied do) Implied do
WRITE *, ((a(i, j), j=1, cols), i=1, rows)
WRITE ’(formats)’, output list Formatted write
WRITE ’(formats)’, (io implied do) Implied do
WRITE (7, 10, ADVANCE=’NO’), output list Advance
WRITE (8, 10, io spec list), output list I/O specification
WRITE (9, FMT=20, io spec list), output list
WRITE (UNIT=7, 10, io spec list), output list
WRITE (9, s fmt, io spec list), output list Stored format
WRITE (UNIT=8, s fmt, io spec list), output
WRITE (9, ’(formats)’, io spec list), output list Inline format
WRITE (UNIT=7, ’(formats)’, io spec list), output
WRITE (8), output list Binary write
WRITE (7), (io implied do) Implied do
WRITE (8, ADVANCE=’NO’), output list Advance
WRITE (9, io spec list), output list I/O specification
WRITE (UNIT=9, io spec list), output list
WRITE (s variable, FMT=20), output list Internal file
WRITE (UNIT=s variable, FMT=20), output list
WRITE (s variable, 20, io spec list), output list I/O specification
WRITE (UNIT=s var, FMT=20, io spec), output
c 2001 J.E. Akin 46
Appendix C
The alternate logic constructs employ tests at the end of the loop and transfer out the end of the
loop when necessary. M ATLAB and C++ transfer using the “break” command while F90 uses the “exit”
command.
This code illustrates the type of common physical constants that can be made available as global
variables that you can define for your field of study. They can be accessed by any program that includes
a use Physical Constants line and cites a parameter name, as shown on line 60 below.
[ 1] Module Physical Constants ! Define Physical Constants
[ 2] ! Define selected precision
[ 3] INTEGER, PARAMETER :: DP = KIND (1.d0) ! Alternate form
[ 4]
[ 5] ! ========== Physics Constants and units ==========
[ 6] real(DP), parameter:: AMU Value = 1.6605402E-27 DP ! kg
[ 7] real(DP), parameter:: Atmosphere Pres = 9.80665E+04 DP ! Pa
[ 8] real(DP), parameter:: Avogadro = 6.0221367E+23 DP ! 1/mol
[ 9] real(DP), parameter:: Bohr Magneton = 9.2740154E-24 DP ! J/T
[10] real(DP), parameter:: Bohr Radius = 5.29177249E-11 DP ! m
[11] real(DP), parameter:: Boltzmann = 1.380657E-23 DP ! J/K
[12] real(DP), parameter:: c Light = 2.997924580E+8 DP ! m/s
[13] real(DP), parameter:: Electron Compton = 2.42631058E-12 DP ! m
[14] real(DP), parameter:: Electron Angular = 5.2729E-35 DP ! J*s
[15] real(DP), parameter:: Electron Charge =-1.60217738E-19 DP ! coul
[16] real(DP), parameter:: Electron Mass Rest = 9.1093897E-31 DP ! kg
[17] real(DP), parameter:: Electron Moment = 9.2847700E-24 DP ! J/T
[18] real(DP), parameter:: Electron Radius = 2.81794092E-15 DP ! m
[19] real(DP), parameter:: Faraday = 9.6485309E+04 DP ! C/mo
[20] real(DP), parameter:: G Universal = 6.67260E-11 DP ! mˆ3/(sˆ2*kg)
[21] real(DP), parameter:: Light Year = 9.46073E+15 DP ! m
[22] real(DP), parameter:: Mech equiv Heat = 4.185E+3 DP ! J/kcal
[23] real(DP), parameter:: Molar Volume = 0.02241410 DP ! mˆ3/mol
[24] real(DP), parameter:: Neutron Mass = 1.6749286E-27 DP ! kg
[25] real(DP), parameter:: Permeability = 1.25663706143E-06 DP ! H/m
[26] real(DP), parameter:: Permittivity = 8.85418781762E-12 DP ! F/m
[27] real(DP), parameter:: Planck Const = 6.6260754E-34 DP ! J*s
[28] real(DP), parameter:: Proton Mass = 1.6726230E-27 DP ! kg
[29] real(DP), parameter:: Proton Moment = 1.41060761E-26 DP ! J/T
[30] real(DP), parameter:: Quantum charge r = 4.13556E+12 DP ! J*s/C
[31] real(DP), parameter:: Rydberg inf = 1.0973731534E+07 DP! 1/m
[32] real(DP), parameter:: Rydberg Hydrogen = 1.09678E+07 DP ! 1/m
[33] real(DP), parameter:: Std Atmosphere = 1.01325E+05 DP ! Pa
[34] real(DP), parameter:: Stefan Boltzmann = 5.67050E-08 DP ! W/(mˆ2*Kˆ4)
[35] real(DP), parameter:: Thomson cross sect = 6.6516E-29 DP ! mˆ2
[36] real(DP), parameter:: Universal Gas C = 8.314510 DP ! J/mol*K
[37]
[38] ! ========== Astronomy Constants and units ==========
[39] real(DP), parameter:: AU Earth Sun = 1.4959787E+11 DP ! m
[40] real(DP), parameter:: Anomal Month = 27.5546 DP ! days
[41] real(DP), parameter:: Anomal Year = 365.2596 DP ! days
[42] real(DP), parameter:: Dracon Month = 27.2122 DP ! days
[43] real(DP), parameter:: Earth G = 9.80665 DP ! m/sˆ2
[44] real(DP), parameter:: Earth Mass = 5.974E+24 DP ! kg
[45] real(DP), parameter:: Earth Radius Eq = 6.37814E+6 DP ! m
[46] real(DP), parameter:: Earth Radius Mean = 6.371E+6 DP ! m
[47] real(DP), parameter:: Earth Radius Polar = 6.356755E+6 DP ! m
[48] real(DP), parameter:: Julian Year = 365.25 DP ! days
[49] real(DP), parameter:: Rotation Day = 23.93447222 DP ! hours
[50] real(DP), parameter:: Sidereal Day = 23.93446944 DP ! hours
[51] real(DP), parameter:: Sidereal Month = 27.3217 DP ! days
[52] real(DP), parameter:: Sidereal Ratio = 1.0027379092558 DP
[53] real(DP), parameter:: Sidereal Year = 365.2564 DP ! days
[54] real(DP), parameter:: Solar Day = 24.06571111 DP ! hours
[55] real(DP), parameter:: Synodic Month = 29.5306 DP ! days
[56] real(DP), parameter:: Tropical Year = 365.2422 DP ! days
[57] end Module Physical Constants ! Define Physical Constants
[58] Program Test
[59] use Physical Constants
[60] print *, ’Avogadro = ’, Avogadro ; End Program Test
[61] ! Running gives: Avogadro = 0.602213669999999967E+24
For persons familiar with vectors the use of overloaded operators makes sense (but it often does not
make sense). Thus we overload the addition, subtraction, multiplication, assignment, and logical equal
to operators by defining the correct class members to be used for different argument types:
[ 9] ! Overload common operators
[ 10] interface operator (+) ! add others later
[ 11] module procedure add Vector, add Real to Vector; end interface
[ 12] interface operator (-) ! add unary versions later
[ 13] module procedure subtract Vector, subtract Real; end interface
[ 14] interface operator (*) ! overload *
[ 15] module procedure dot Vector, real mult Vector, Vector mult real
[ 16] end interface
[ 17] interface assignment (=) ! overload =
[ 18] module procedure equal Real; end interface
[ 19] interface operator (==) ! overload ==
[ 20] module procedure is equal to; end interface
[ 21]
Then we encapsulate the supporting member functions, beginning with two constructors, assign and
make Vector:
[ 22] contains ! functions & operators
[ 23]
[ 24] function assign (values) result (name) ! array to vector constructor
[ 25] real, intent(in) :: values(:) ! given rank 1 array
[ 26] integer :: length ! array size
[ 27] type (Vector) :: name ! Vector to create
[ 28] length = size(values); allocate ( name%data(length) )
[ 29] name % size = length; name % data = values; end function assign
[ 30]
[ 31] function make Vector (len, values) result(v) ! Optional Constructor
[ 32] integer, optional, intent(in) :: len ! number of values
[ 33] real, optional, intent(in) :: values(:) ! given values
[ 34] type (Vector) :: v
[ 35] if ( present (len) ) then ! create vector data
[ 36] v%size = len ; allocate ( v%data(len) )
[ 37] if ( present (values)) then ; v%data = values ! vector
[ 38] else ; v%data = 0.d0 ! null vector
[ 39] end if ! values present
[ 40] else ! scalar constant
[ 41] v%size = 1 ; allocate ( v%data(1) ) ! default
[ 42] if ( present (values)) then ; v%data(1) = values(1) ! scalar
[ 43] else ; v%data(1) = 0.d0 ! null
[ 44] end if ! value present
[ 45] end if ! len present
[ 46] end function make Vector
[ 47]
Note that lines 55 and 62 above are similar ways to avoid writing serial loops that would have to be used
in most languages. This keeps the code cleaner and shorter, and more importantly it lets the compiler
carry out those operations in parallel on some machines.
While copy members are very important to C++ programmers the following copy Vector should
probably be omitted since you would not usually pass big arrays as copies and F90 defaults to passing by
reference unless forced to pass by value.
[ 63]
[ 64] function copy Vector (name) result (new)
[ 65] type (Vector), intent(in) :: name
The routine delete Vector is the destructor for this class. In some sense it is incomplete because it
does not delete the size attribute. It was decided that while the actual array of data may take a huge
amount of storage, the single integer was not important. To be more complete one would have to have to
make size an integer pointer and allocate and deallocate it at numerous locations within this module.
[ 69]
[ 70] subroutine delete Vector (name) ! deallocate allocated items
[ 71] type (Vector), intent(inout) :: name
[ 72] integer :: ok ! check deallocate status
[ 73] deallocate (name%data, stat = ok )
[ 74] if ( ok /= 0 ) stop "Vector not allocated in delete Vector"
[ 75] name%size = 0 ; end subroutine delete Vector
[ 76]
[ 77] function dot Vector (a, b) result (c) ! overload *
[ 78] type (Vector), intent(in) :: a, b
[ 79] real :: c
[ 80] if ( a%size /= b%size ) stop "Sizes differ in dot Vector"
[ 81] c = dot product(a%data, b%data); end function dot Vector
[ 82]
[ 83] subroutine equal Real (new, R) ! overload =, real to vector
[ 84] type (Vector), intent(inout) :: new
[ 85] real, intent(in) :: R
[ 86] if ( associated (new%data) ) deallocate (new%data)
[ 87] allocate ( new%data(1) ); new%size = 1
[ 88] new%data = R ; end subroutine equal Real
[ 89]
[ 90] logical function is equal to (a, b) result (t f) ! overload ==
[ 91] type (Vector), intent(in) :: a, b ! left & right of ==
[ 92] t f = .false. ! initialize
[ 93] if ( a%size /= b%size ) return ! same size ?
[ 94] t f = all ( a%data == b%data ) ! and all values match
[ 95] end function is equal to
[ 96]
[ 97] function length (name) result (n) ! accessor member
[ 98] type (Vector), intent(in) :: name
[ 99] integer :: n
[100] n = name % size ; end function length
[101]
[102] subroutine list (name) ! accessor member, for prettier printing
[103] type (Vector), intent(in) :: name
[104] print *,"[", name % data(1:name%size), "]"; end subroutine list
[105]
[106] function normalize Vector (name) result (new)
[107] type (Vector), intent(in) :: name
[108] type (Vector) :: new
[109] real :: total, nil = epsilon(1.0) ! tolerance
[110] allocate ( new%data(name%size) ) ; new%size = name%size
[111] total = sqrt ( sum ( name%data**2 ) ) ! intrinsic functions
[112] if ( total < nil ) then ; new%data = 0.d0 ! avoid division by 0
[113] else ; new%data = name%data/total
[114] end if ; end function normalize Vector
[115]
[116] subroutine read Vector (name) ! read array, assign
[117] type (Vector), intent(inout) :: name
[118] integer, parameter :: max = 999
[119] integer :: length
[120] read (*,’(i1)’, advance = ’no’) length
[121] if ( length <= 0 ) stop "Invalid length in read Vector"
[122] if ( length >= max ) stop "Maximum length in read Vector"
[123] allocate ( name % data(length) ) ; name % size = length
[124] read *, name % data(1:length) ; end subroutine read Vector
[125]
[126] function real mult Vector (r, v) result (new) ! overload *
[127] real, intent(in) :: r
[128] type (Vector), intent(in) :: v
[129] type (Vector) :: new ! new = r * v
[130] if ( v%size < 1 ) stop "Zero size in real mult Vector"
[131] allocate ( new%data(v%size) ) ; new%size = v%size
[132] new%data = r * v%data ; end function real mult Vector
[133]
[134] function size Vector (name) result (n) ! accessor member
[135] type (Vector), intent(in) :: name
[136] integer :: n
[137] n = name % size ; end function size Vector
[138]
[139] function subtract Real(v, r) result(new) ! vector-real, overload -
[140] type (Vector), intent(in) :: v
[141] real, intent(in) :: r
[142] type (Vector) :: new ! new = v + r
The routine delete Vector is the manual constructor for this class. It has no optional arguments so
both arguments must be supplied, and it duplicates the constructor on line 31, but it uses the naming
convention preferred by the author.
[158]
[159] function Vector (length, values) result(name) ! constructor
[160] integer, intent(in) :: length ! array size
[161] real, target, intent(in) :: values(length) ! given array
[162] real, pointer :: pt to val(:) ! pointer to array
[163] type (Vector) :: name ! Vector to create
[164] integer :: get m ! allocate flag
[165] allocate ( pt to val (length), stat = get m ) ! allocate
[166] if ( get m /= 0 ) stop ’allocate error’ ! check
[167] pt to val = values ! dereference values
[168] name = Vector(length, pt to val) ! intrinsic constructor
[169] end function Vector
[170]
[171] function Vector max value (a) result (v) ! accessor member
[172] type (Vector), intent(in) :: a
[173] real :: v
[174] v = maxval ( a%data(1:a%size) ) ; end function Vector max value
[175]
[176] function Vector min value (a) result (v) ! accessor member
[177] type (Vector), intent(in) :: a
[178] real :: v
[179] v = minval ( a%data(1:a%size) ) ; end function Vector min value
[180]
[181] function Vector mult real(v, r) result(new) ! vec*real, overload *
[182] type (Vector), intent(in) :: v
[183] real, intent(in) :: r
[184] type (Vector) :: new ! new = v * r
[185] if ( v%size < 1 ) stop "Zero size in Vector mult real"
[186] new = Real mult Vector(r, v); end function Vector mult real
[187]
[188] end module class Vector
A first test of this class is given below along with comments that give the verifications of the members.
[ 1] ! Testing Vector Class Constructors & Operators
[ 2] include ’class Vector.f90’ ! see previous figure
[ 3] program check vector class
[ 4] use class Vector
[ 5] implicit none
[ 6]
[ 7] type (Vector) :: x, y, z
[ 8]
[ 9] ! test optional constructors: assign, and copy
[10] x = make Vector () ! single scalar zero
[11] write (*,’("made scalar x = ")’,advance=’no’); call list(x)
[12]
[13] call delete Vector (x) ; y = make Vector (4) ! 4 zeros
[14] write (*,’("made null y = ")’,advance=’no’); call list(y)
[15]
[16] z = make Vector (4, (/11., 12., 13., 14./) ) ! 4 non-zeros
[17] write (*,’("made full z = ")’,advance=’no’); call list(z)
[18] write (*,’("assign [ 31., 32., 33., 34. ] to x")’)
[19]
[20] x = assign( (/31., 32., 33., 34./) ) ! (4) non-zeros
[21] write (*,’("assigned x = ")’,advance=’no’); call list(x)
[22]
[23] x = Vector (4, (/31., 32., 33., 34./) ) ! 4 non-zeros
[24] write (*,’("public x = ")’,advance=’no’); call list(x)
[25] write (*,’("copy x to y =")’,advance=’no’)
[26] y = copy Vector (x) ; call list(y) ! copy
[27]
[28] ! test overloaded operators
[29] write (*,’("z * x gives ")’,advance=’no’); print *, z*x ! dot
[30] write (*,’("z + x gives ")’,advance=’no’); call list(z+x) ! add
Having tested the vector class we will now use it in some typical vector operations. We want a program
that will work with arrays of vectors to read in the number of vectors. The array of vectors will use an
automatic storage mode. That could be risky because if the system runs out of memory we get a fatal
error message and the run aborts. If we made the alternate choice of allocatable arrays then we could
check the allocation status and have a chance (but not a good chance) of closing down the code is some
”friendly” manner. Once the code reads the number of vectors then for each one it reads the number of
components and the the component values. After testing some simple vector math we compute a more
complicated result know as the orthonormal basis for the given set of vectors:
[ 1] ! Test Vector Class Constructors, Operators and Basis
[ 2] include ’class Vector.f’
[ 3]
[ 4] program check basis ! demonstrate a typical Vector class
[ 5] use class Vector
[ 6] implicit none
[ 7]
[ 8] interface
[ 9] subroutine testing basis (N V)
[ 10] integer, intent(in) :: N V
[ 11] end subroutine testing basis
[ 12] end interface
[ 13]
[ 14] print *, "Test automatic allocate, deallocate"
[ 15] print *, " " ; read *, N V
[ 16] print *, "The number of vectors to be read is: ", N V
[ 17] call testing basis ( N V) ! to use automatic arrays
[ 18] end program check basis
[ 19]
[ 20] subroutine testing basis (N V)
[ 21] ! test vectors AND demo automatic allocation/deallocation
[ 22] use class Vector
[ 23]
[ 24] integer, intent(in) :: N V
[ 25] type (Vector) :: Input(N V) ! automatic array
[ 26] type (Vector) :: Ortho(N V) ! automatic array
[ 27] integer :: j
[ 28] real :: norm
[ 29]
[ 30] interface
[ 31] subroutine orthonormal basis (Input, Ortho, N given)
[ 32] use class Vector
[ 33] type (Vector), intent(in) :: Input(N given)
[ 34] type (Vector), intent(out) :: Ortho(N given)
[ 35] integer, intent(in) :: N given
[ 36] end subroutine orthonormal basis
[ 37] end interface
[ 38]
[ 39] print *, " " ; print *, "The given ", N V, " vectors:"
[ 40] do j = 1, N V
[ 41] call read Vector ( Input(j) )
[ 42] call list ( Input(j) )
[ 43] end do ! for j
[ 44]
[ 45] print *, " "
The overloading process is similar, but now we will see that much more logic is required to deal with the
zero entries and new zeros created by addition or multiplication.
[ 8] interface assignment (=)
[ 9] module procedure equal Vector ; end interface
[ 10] interface operator (.dot.) ! define dot product operator
[ 11] module procedure dot Vector ; end interface
[ 12] interface operator (==) ! Boolean equal to
[ 13] module procedure is equal to ; end interface
[ 14] interface operator (*) ! term by term product
[ 15] module procedure el by el Mult, real mult Sparse
[ 16] module procedure Sparse mult real
[ 17] end interface
[ 18] interface operator (-) ! for sparse vectors
[ 19] module procedure Sub Sparse Vectors ; end interface
[ 20] interface operator (+) ! for sparse vectors
[ 21] module procedure Sum Sparse Vectors ; end interface
[ 22]
[ 23] contains ! operators and functionality
In the following constructor for the class note that both the pointer array attributes are allocated (line 32)
the same amount of storage in memory. One should also include the allocation status flag here and checks
its value to raise a possible exception (as seen in lines 41-46).
[ 24] subroutine make Sparse Vector (s,n,r,v)
[ 25] ! allows zero length vectors
[ 26] type (sv) :: s ! name
[ 27] integer, intent(in) :: n ! size
[ 28] integer, intent(in) :: r(n) ! rows
[ 29] real, intent(in) :: v(n) ! values
[ 30] if ( n < 0 ) stop &
[ 31] "Error, negative rows in make Sparse Vector"
[ 32] allocate (s%rows(n), s%values(n))
[ 33] s%non zeros = n ! copy size
[ 34] s%rows = r ! row array assignment
[ 35] s%values = v ! value array assignment
[ 36] end subroutine make Sparse Vector
[ 37]
This is really a destructor. Again, it is incomplete because the integer array size was not made allocatable
for simplicity.
[ 38] subroutine delete Sparse Vector (s)
[ 39] type (sv) :: s ! name of sparse vector
[ 40] integer :: error ! deallocate status flag, 0 no error
[ 41] deallocate (s%rows, s%values, stat = error) ! memory released
[ 42] if ( error == 0 ) then
[ 43] s%non zeros = 0 ! reset size
[ 44] else ! never created
[ 45] stop "Sparse vector to delete does not exist"
[ 46] end if ; end subroutine delete Sparse Vector
[ 47]
This creates a user defined operator call .dot. to be applied to sparse vectors.
[ 48] function dot Vector (u, v) result (d) ! defines .dot.
[ 49] ! dot product of sparse vectors
[ 50] type (sv), intent(in) :: u, v ! sparse vectors
[ 51] type (sv) :: w ! sparse vector, temporary
[ 52] real :: d ! dot product value
[ 53] d = 0.0 ! default
[ 54] if ( u%non zeros < 1 .or. v%non zeros < 1 ) return ! null
[ 55] w = el by el Mult (u, v) ! element by element sparse product
[ 56] if ( w%non zeros > 0 ) &
[ 57] d = sum( w%values(1:w%non zeros) ) ! summed
[ 58] call delete Sparse Vector (w) ! delete temp
The above dot Vector is more complicated in this format because it is likely that stored non-zero
values will be multiplied by (unstored) zeros. Thus, the real work is done in the following member
function that employs Boolean logic. The terms for the summation that creates the scalar dot product are
first computed in a full vector equal in length to the minimum row number given. Observe that its size
is established through the use of the min intrinsic, acting on the two given sizes, within the dimension
attribute for the full array (lines 67,68). Three logical arrays (line 68) are used as “masks” which are
true when a non-zero exists in the corresponding row of their associated sparse vector (down to the
minimum row cited above). The three logical vectors are initialized in lines 77 to 92. That process ends
with the third vector being created as a Boolean product (line 91) and the maximum possible number of
non-zero products is found from the count intrinsic (line 92).
It is also important to note that the working space vector full is an automatic array and memory
for it is automatically allocated for it each time the function is called. It could be an extremely long
vector and thus it is possible (but not likely) that there would not be enough memory available. Then the
system would abort with an error message. To avoid that possibility one could have declared full to be
an allocatable vector and then allocate its memory by using a similar min construct. That allocation
request should (always) include the STAT flag so that if the memory allocation fails it would be possible
to issue an exception to try to avoid a fatal crash of the system (not likely).
[ 61] function el by el Mult (u, v) result (w) ! defines * operator
[ 62] ! element by element product of sparse vectors: 0 * real ?
[ 63] type (sv), intent(in) :: u, v ! given vectors
[ 64] type (sv) :: w ! new vector
[ 65] real :: full( min( u%rows(u%non zeros), & ! automatic
[ 66] & v%rows(v%non zeros) ) ) ! workspace
[ 67] logical, dimension( min( u%rows(u%non zeros), &
[ 68] v%rows(v%non zeros))) :: u m, v m, w m ! logical product masks
[ 69] integer :: j, k, last, n, row
[ 70] ! is either u or v null ?
[ 71] if ( u%non zeros < 1 .or. v%non zeros < 1 ) then ! w is null
[ 72] allocate ( w%rows(0), w%values(0) )
[ 73] w%non zeros = 0
[ 74] return ! a null sparse vector
[ 75] end if ! no calculation necessary
[ 76]
[ 77] ! Initialize logic masks
[ 78] last = min( u%rows(u%non zeros), v%rows(v%non zeros) ) ! max size
[ 79] u m = .false. ! assume no contributions
[ 80] do j = 1, size(u%rows)
[ 81] row = u%rows(j) ! get row number to flag
[ 82] if ( row > last ) exit ! j loop
[ 83] u m(row) = .true. ! possible contribution
[ 84] end do ! to initalize u mask
[ 85] v m = .false. ! assume no contributions
[ 86] do j = 1, size(v%rows)
[ 87] row = v%rows(j) ! get row number to flag
[ 88] if ( row > last ) exit ! j loop
[ 89] v m(row) = .true. ! possible contribution
[ 90] end do ! to initalize v mask
[ 91] w m = (u m .and. v m ) ! Boolean product logic
[ 92] n = count ( w m ) ! count possible products
[ 93] ! if ( n == 0 ) print *,"Warning: zero length sparse" ! debug
[ 94]
The vector full is set to zero (line 96) and comparison DO loops (lines 97,101) over the two given
vectors are minimized (lines 100,103) by testing where the mask vector w m is true (thereby indicating
a non-zero product). When all the products are stored in the full vector it is converted to the sparse
vector storage mode (line 109) for release as the return result. Because full is an automatic array
its memory is automatically released when the function is exited.
[ 95] ! Fill the product workspace, full
[ 96] full = 0.0 ! initialize
[ 97] do j = 1, size(u%rows) ! loop over u
[ 98] row = u%rows(j) ! row in u
[ 99] if ( row > last ) exit ! this loop in u ! past end of w
[100] if ( .not. w m(row) ) cycle ! to next j ! not in product
[101] do k = 1, size(v%rows) ! loop over v
[102] if ( v%rows(k) > last ) exit ! this loop ! past end of w
[103] if ( .not. w m(v%rows(k)) ) cycle ! to k+1 ! not in product
[104] if ( row == v%rows(k) ) then ! same row, u & v
[105] full(row) = u%values(j)*v%values(k) ! get product
The operator overloading members are given with the next function (line 112) as well as in lines 140,
231, and 320.
[112] subroutine equal Vector (new, s) ! overload =
[113] type (sv), intent(inout) :: new
[114] type (sv), intent(in) :: s
[115] allocate ( new%rows(s%non zeros) )
[116] allocate ( new%values(s%non zeros) )
[117] new%non zeros = s%non zeros
[118] if ( s%non zeros > 0 ) then
[119] new%rows (1:s%non zeros) = s%rows (1:s%non zeros) ! array copy
[120] new%values(1:s%non zeros) = s%values(1:s%non zeros) ! copy
[121] end if ; end subroutine equal Vector
[122]
[123] function get element (name, row) result (v)
[124] type (sv), intent(in) :: name ! sparse vector
[125] integer, intent(in) :: row ! row in sparse vector
[126] integer :: j ! loops
[127] real :: v ! value at row
[128] v = 0.0 ! default
[129] if ( row < 1 ) stop "Invalid row number, get element"
[130] if ( name%non zeros < 1 ) return ! not here
[131] if ( row > name%rows(name%non zeros) ) return ! not here
[132] do j = 1, name%non zeros
[133] if ( row == name%rows(j) ) then
[134] v = name%values(j) ! found the value
[135] return ! search done
[136] end if ! in the vector
[137] end do ! over possible values
[138] end function get element
[139]
[140] function is equal to (a, b) result (t or f) ! define ==
[141] type (sv), intent(in) :: a, b ! two sparse vectors
[142] logical :: t or f
[143] integer :: i ! loops
[144] t or f = .true. ! default
[145] if ( a%non zeros == b%non zeros ) then ! also check values
[146] do i = 1, a%non zeros ! or use count function for simplicity
[147] if (a%rows(i) /= b%rows(i) .or. &
[148] a%values(i) /= b%values(i)) then
[149] t or f = .false. ! because rows and/or values differ
[150] return ! no additional checks needed
[151] end if ! same values
[152] end do ! over sparse rows
[153] else ! sizes differ so vectors must be different
[154] t or f = .false.
[155] end if ! sizes match
[156] end function is equal to
[157]
[158] function largest index (s) result(row)
[159] type (sv), intent(in) :: s ! sparse vector
[160] integer :: row ! last non-zero in full vector
[161] integer :: j ! loops
[162] row = 0 ! initalize
[163] if ( s%non zeros < 1 ) return ! null vector
[164] do j = s%non zeros, 1, -1 ! loop backward
[165] if ( s%values(j) /= 0.0 ) then ! last non-zero term
[166] row = s%rows(j) ! actual row number
[167] return ! search done
[168] end if
[169] end do
[170] end function largest index
[171]
[172] function length (name) result (n)
[173] type (sv), intent(in) :: name
[174] integer :: n
[175] n = name % non zeros ! read access to size, if private
[176] end function length
[177]
Once again we observe that the next two functions employ the colon operator (lines 185,196,199,201)
to avoid explicit serial loops which would make them faster on certain vector and parallel computers.
[178] function norm (name) result (total)
[179] type (sv), intent(in) :: name
[180] real :: total
[181] if ( name%non zeros < 1 ) then
In the following subtraction and addition functions we again note that sparse terms with the same
values but opposite signs can result in new zero terms in the resulting vector. A temporary automatic
workspace vector, full, is used to hold the preliminary results. In this case it must have a size that is the
maximum of the two given vectors. Thus, the max intrinsic is employed in its dimension attribute (lines
331,344) which is opposite the earlier multiplication example (line 65).
[328] function Sub Sparse Vectors (u, v) result (w) ! defines -
[329] type (sv), intent(in) :: u, v
[330] type (sv) :: w
[331] real :: full( max( u%rows(u%non zeros), & ! automatic
[332] & v%rows(v%non zeros) ) ) ! workspace
[333] if ( u%non zeros <= 0 ) stop "First vector doesn’t exist"
[334] if ( v%non zeros <= 0 ) stop "Second vector doesn’t exist"
[335] full = 0.0 ! set to zero
[336] full(u%rows) = u%values ! copy first values
[337] full(v%rows) = full(v%rows) - v%values ! less second values
[338] w = Vector To Sparse (full) ! delete any zeros
[339] end function Sub Sparse Vectors ! automatically deletes full
[340]
[341] function Sum Sparse Vectors (u, v) result (w) ! defines +
[342] type (sv), intent(in) :: u, v
This function is invoked several times in other member functions. It simply accepts a standard (dense)
vector and converts it to the sparse storage mode in the return result.
[379] function Vector To Sparse (full) result (sparse)
[380] real, intent(in) :: full(:) ! standard array
[381] type (sv) :: sparse ! sparse vector copy
[382] integer :: j, n, number ! loops and counters
[383] n = count ( full /= 0.0 ) ! count non zeros
[384] ! if ( n == 0 ) print *, "Warning: null full vector "
[385] allocate ( sparse%rows(n), sparse%values(n) )
[386] sparse%non zeros = n ! sparse size
[387] number = 0 ! non zeros inserted
[388] do j = 1, size(full)
[389] if ( full(j) == 0.0 ) cycle ! to next j value
[390] number = number + 1 ! non zeros inserted
[391] sparse%rows(number) = j ! row number in full
[392] sparse%values(number) = full(j) ! value
[393] if ( number == n ) exit ! all non zeros found
[394] end do ; end function Vector To Sparse
[395]
[396] function zero sparse () result (s)
[397] type (sv) :: s ! create sparse null vector
[398] s%non zeros = 0
[399] allocate (s%rows(0), s%values(0)); end function zero sparse
[400] end module class sparse Vector
After that we want to try all the reasonable choices for breading the data set into two adjacent regions
that are each to be fit with a different straight line. Trial variables were defined in lines 10 and 12, while
the best results found are in variables declared in lines 11, 13, and 14. Note that on line 48 we have
required that at least three points be used to define an approximate straight line. If we allowed two points
to be employed we would get a false (or misleading) indication of zero error for such a choice. Thus, in
In splitting up the two data regions not that it was not necessary to copy segments of the independent
and dependent data. Instead the colon operator, or implied do loops, were used in lines 50 and 51 to pass
vectors with j and (lines – j) entries, respectively to the two calls to lsq fit. After combining the two
errors, in line 52, we update the current best choice for the data set division point in lines 55 through 58.
[ 54] ! does this division gives you a smaller error ?
[ 55] if ( error < error min ) then
[ 56] error min = error ; split = j
[ 57] left = fit1 ; right = fit2
[ 58] end if ! current best choice
[ 59] end do ! of split choices
After we exit the loop, at line 59, we simply list the best results obtained. In line 73 we have also
deallocated the data arrays even though it is just a formality at this point since all memory is released at the
program terminates immediately afterwards. Had this been a subroutine or function then we would need
to be sure that allocated variables are released when their access scope has terminated. Later versions of
Fortran will do that for you, but good programmers should keep up with memory allocations.
[ 60] ! Display the results
[ 61] print *, "Two line best fit; combined error is ", error min
[ 62] print *, "Best division of the data is:"
[ 63] print *, "data(:j), data(j+1:), where j = ", split
[ 64] print *, "Left line fit:"
[ 65] print *, "the slope is ", left(1)
[ 66] print *, "the intercept is ", left(2)
[ 67] print *, "the error is ", left(3)
[ 68] print *, "Right line fit:"
[ 69] print *, "the slope is ", right(1)
[ 70] print *, "the intercept is ", right(2)
[ 71] print *, "the error is ", right(3)
[ 72]
[ 73] deallocate (y, x)
[ 74] end program two line lsq fit
[ 75]
For completeness an input routine, read xy file, is illustrated. It is elementary since it does not
check for any read errors, and thus does not allow for any exception control if the read somehow fails.
[ 76] subroutine read xy file (infile, lines, x, y)
[ 77] !------------------------------------------------------
[ 78] ! Take a file number, the number of lines to be read,
[ 79] ! and put the data into the arrays x and y
[ 80] !------------------------------------------------------
[ 81] implicit none
[ 82] integer, intent(in) :: inFile ! unit to read
[ 83] integer, intent(in) :: lines ! length of the file
[ 84] real, intent(out) :: x(lines) ! independent data
[ 85] real, intent(out) :: y(lines) ! dependent data
[ 86] integer j
[ 87] rewind (inFile) ! go to front of the file
[ 88] do j = 1, lines ! for the entire file
[ 89] read (infile, *) x(j), y(j) ! get the x and y values
[ 90] end do ! over all lines
[ 91] end subroutine read xy file
[ 92]
If the supplied data file was huge, say argument lines has a value of ten million, the such data would
probably have been stored in a binary rather that a formatted file. In that case we would simply invoke a
binary read by re-writing line 89 as
[ 89] read (infile) x(j), y(j) ! binary read of x and y
Here we will not go into the details about how we would have to replace subroutine inputCount an
equivalent one for binary files. To do that you will have to study the Fortran INQUIRE statement for
files, and its IOLENGTH option to get a hardware independent record lenght of a real variable.
[ 93] ! Given test data in file two line.dat:
[ 94] ! 0.0000000e+00 1.7348276e+01
[ 95] ! 1.0000000e+00 6.5017349e+01
[ 96] ! 2.0000000e+00 8.7237749e+01
[ 97] ! 3.0000000e+00 1.2433478e+02
[ 98] ! 4.0000000e+00 1.5456681e+02
[ 99] ! 5.0000000e+00 1.8956219e+02
[100] ! 6.0000000e+00 2.1740486e+02
[101] ! 7.0000000e+00 2.3138619e+02
[102] ! 8.0000000e+00 2.7995041e+02
[103] ! 9.0000000e+00 3.1885162e+02
[104] ! 1.0000000e+01 3.4628642e+02
[105] ! 1.1000000e+01 3.3522546e+02
[106] ! 1.2000000e+01 3.7626218e+02
[107] ! 1.3000000e+01 3.9577060e+02
[108] ! 1.4000000e+01 4.2217988e+02
[109] ! 1.5000000e+01 4.3388828e+02
[110] ! 1.6000000e+01 4.5897959e+02
[111] ! 1.7000000e+01 4.9506511e+02
[112] ! 1.8000000e+01 5.0747649e+02
[113] ! 1.9000000e+01 5.2168101e+02
[114] ! 2.0000000e+01 5.2976511e+02
Assuming the formatted data are stored in file two line.dat, as shown above we obtain the best two
straight ine fit.
[115] ! Running the program gives:
[116] !
[117] ! Enter the data input filename: two line.dat
[118] ! There were 21 records read.
[119] ! Single line fit
[120] ! the slope is 25.6630135
[121] ! the intercept is 53.2859993
[122] ! the error is 343.854675
[123] ! Two line best fit; combined error is 126.096634
[124] ! Best division of the data is:
[125] ! data(:j), data(j+1:), where j = 11
[126] ! Left line fit:
[127] ! the slope is 31.9555302
[128] ! the intercept is 24.9447269
[129] ! the error is 46.060421
[130] ! Right line fit:
[131] ! the slope is 21.6427555
[132] ! the intercept is 112.166664
[133] ! the error is 80.0362091
[134]
Check this out by plotting the data points and the three straight line segments. Just remember that the
first line covers the whole domain, while the second goes only up to halfway between points 11 and 12
while the third line runs from there to the end of the independent data.
Otherwise, if the unit after last unit is open we must loop over all the higher unit numbers in search of
one that is closed. If we succeed then we update last unit and return by exiting the forever loop, as seen
in lines 24 and 25.
[19] else ! loop through allowed units
[20] do ! forever
[21] next = next + 1
[22] inquire (unit=next, opened=open)
[23] if ( .not. open ) then
[24] last unit = next ! found it
[25] exit ! the unit loop
[26] end if
At this point it may be impossible to find a unit. However, with 999 units available it is likely that
one that was previously in use has now been closed and is available again. Before aborting we reset the
search and allow three cycles to find a unit that is now free. That is done in lines 27–31.
[27] if ( next == max unit ) then ! attempt reset 3 times
[28] last unit = 0
[29] count = count + 1
[30] if ( count <= 3 ) next = min unit - 1
[31] end if ! reset try
In the unlikely event that all allowed units are still in use we abort the function after giving some
insight to why.
[32] if ( next > max unit ) then ! abort
[33] print *,’ERROR: max unit exceeded in get next io unit’
[34] stop ’ERROR: max unit exceeded in get next io unit’
[35] end if ! abort
[36] end do ! over unit numbers
[37] end if ! last unit
[38] end function get next io unit
C.13 Problem 5.4.4 : Polymorphic interface for the class ‘Position Angle’
[ 1] module class Position Angle ! file: class Position Angle.f90
[ 2] use class Angle
[ 3] implicit none
[ 4] type Position Angle ! angle in deg, min, sec
[ 5] private
[ 6] integer :: deg, min ! degrees, minutes
[ 7] real :: sec ! seconds
[ 8] character :: dir ! N | S, E | W
[ 9] end type
The above type definitions are unchanged. The only new part of the module for this class is the INTER-
FACE given in the following four lines.
[10] interface Position Angle ! generic constructor
[11] module procedure Decimal sec, Decimal min
[12] module procedure Int deg, Int deg min, Int deg min sec
[13] end interface
[14] contains . . .
We simply replace all the previous constructor calls with the generic function Position Angle as
shown on lines 8 through 17 below.
[ 9] a1 = Position Angle (10, 30, 0., "N") ! note decimal point
[10] call List Position Angle (a1)
[11] a1 = Position Angle (10, 30, 0, "N")
[12] call List Position Angle (a1)
[13] a1 = Position Angle (10, 30, "N")
[14] call List Position Angle (a1)
[15] a1 = Position Angle (20, "N")
[16] call List Position Angle (a1)
[17] a2 = Position Angle (30, 48, 0., "N")
[18] call List Position Angle (a2)
C.14 Problem 6.4.1 : Using a function with the same name in two classes
[ 1] include ’class X.f90’
[ 2] include ’class Y.f90’
[ 3] program main ! modified from Fig. 4.6.2-3F
[ 4] use class Y, Y f => f ! renamed in main
[ 5] implicit none
[ 6] type (X ) :: x, z ; type (Y ) :: y
[ 7] x%a = 22 ! assigns 22 to the a defined in X
[ 8] call X f(x) ! invokes the f() defined in X
[ 9] print *,"x%a = ", x%a ! lists the a defined in X
[10] y%a = 44 ! assigns 44 to the a defined in Y
[11] x%a = 66 ! assigns 66 to the a defined in X
[12] call Y f(y) ! invokes the f() defined in Y
[13] call X f(x) ! invokes the f() defined in X
[14] print *,"y%a = ", y%a ! lists the a defined in X
[15] print *,"x%a = ", x%a ! lists the a defined in X
[16] z%a = y%a ! assign Y a to z in X
[17] print *,"z%a = ", z%a ! lists the a defined in X
[18] end program main ! Running gives:
[19] ! X f() executing ! x%a = 22
[20] ! Y f() executing ! X f() executing
[21] ! y%a = 44 ! x%a = 66
[22] ! z%a = 44
The generic setData could not also contain setDataM because it has the same argument signature as
setDataE and the compiler would not be able to tell which dynamic binding to select.
D.1 Introduction
It is necessary to be multilingual in computer languages today. Since C++ is often used in the OOP
literature it should be useful to have C++ versions of the same code given earlier in F90. In most cases
these examples have the same variable names and the line numbers are usually very close to each other.
This appendix will allow you to flip from F90 examples in Chapter 4 of the main body of the text to see
similar operations in C++.
[ 1] #include <iostream.h>
[ 2] #include <stdlib.h>
[ 3] #include <math.h> // system math files
[ 4]
[ 5] main()
[ 6] // Convert a character string to an integer in C++
[ 7] f
[ 8] char Age Char[5];
[ 9] int age;
[10]
[11] cout << "Enter your age: ";
[12] cin >> Age Char;
[13]
[14] // convert with intrinsic function
[15] age = atoi(Age Char);
[16]
[17] cout << "Your integer age is " << age << endl;
[18] cout << "Your hexadecimal age is " << hex << age << endl;
[19] cout << "Your octal age is " << oct << age << endl;
[20]
[21] g // end of main
[22]
[23] // Running gives:
[24] // Enter your age: 45
[25] // Your integer age is 45.
[26] // Your hexadecimal age is 2d.
[27] // Your octal age is 55.
abstract class: A class primarily intended to define an instance, but can not be instantiated without
additional methods.
abstract data type: An abstraction that describes a set of items in terms of a hidden data structure and
operations on that structure.
abstraction: A mental facility that permits one to view problems with varying degrees of detail depend-
ing on the current context of the problem.
accessor: A public member subprogram that provides query access to a private data member.
actor: An object that initiates behavior in other objects, but cannot be acted upon itself.
agent: An object that can both initiate behavior in other objects, as well as be operated upon by other
objects.
ADT: Abstract data type.
AKO: A Kind Of. The inheritance relationship between classes and their superclasses.
allocatable array: A named array having the ability to dynamically obtain memory. Only when space
has been allocated for it does it have a shape and may it be referenced or defined.
argument: A value, variable, or expression that provides input to a subprogram.
array: An ordered collection that is indexed.
array constructor: A means of creating a part of an array by a single statement.
array overflow: An attempt to access an array element with a subscript outside the array size bounds.
array pointer: A pointer whose target is an array, or an array section.
array section: A subobject that is an array and is not a defined type component.
assertion: A programming means to cope with errors and exceptions.
assignment operator: The equal symbol, “=”, which may be overloaded by a user.
assignment statement: A statement of the form “variable = expression”.
association: Host association, name association, pointer association, or storage association.
attribute: A property of a variable that may be specified in a type declaration statement.
automatic array: An explicit-shape array in a procedure, which is not a dummy argument, some or all
of whose bounds are provided when the procedure is invoked.
data type: A named category of data that is characterized by a set of values. together with a way to
denote these values and a collection of operations that interpret and manipulate the values. For an
intrinsic type, the set of data values depends on the values of the type parameters.
deallocation statement: A statement which releases dynamic memory that has been previously allo-
cated to an allocatable array or a pointer.
debugger software: A program that allows one to execute a program in segments up to selected break-
points, and to observe the program variables.
declaration statement: A statement which specifies the type and, optionally, attributes of one or more
variables or constants.
default constructor: A class member function with no arguments that assigns default initial values to
all data members in a newly created instance of a class.
defined operator: An operator that is not an intrinsic operator and is defined by a subprogram that is
associated with a generic identifier.
deque: A container that supports inserts or removals from either end of a queue.
derived class: A class whose declaration indicates that it is to inherit the public members of a previously
defined base class.
derived type: A user defined data type with components, each of which is either of intrinsic type or of
another derived type.
destructor: An operation that cleans up an existing instance of a class that is no longer needed.
destructor operations: Methods which destroy objects and reclaim their dynamic memory.
dummy argument: An argument in a procedure definition which will be associated with the actual
(reference or value) argument when the procedure is invoked.
dynamic binding: The allocation of storage at run time rather than compile time, or the run time asso-
ciation of an object and one of its generic operations..
edit descriptor: An item in an input/output format which specifies the conversion between internal and
external forms.
encapsulation: A modeling and implementation technique (information hiding) that separates the exter-
nal aspects of an object from the internal, implementation details of the object.
exception: An unexpected error condition causing an interruption to the normal flow of program control.
external file: A sequence of records that exists in a medium external to the program.
function body: A block of statements that manipulate parameters to accomplish the subprogram’s pur-
pose.
function definition: Program unit that associates with a subprogram name a return type, a list of argu-
ments, and a sequence of statements that manipulate the arguments to accomplish the subprogram’s
purpose
function header: A line of code at the beginning of a function definition; includes the argument list, and
the function return variable name.
generic function: A function which can be called with different types of arguments.
generic identifier: A lexical token that appears in an INTERFACE statement and is associated with all
the procedures in the interface block.
generic interface block: A form of interface block which is used to define a generic name for a set of
procedures.
generic name: A name used to identify two or more procedures, the required one being determined by
the types of the non-optional arguments in the procedure invocation.
generic operator: An operator which can be invoked with different types of operands.
Has-A: A relationship in which the derived class has a property of the base class.
hashing technique: A technique used to create a hash table, in which the array element where an item
is to be stored is determined by converting some item feature into an integer in the range of the size
of the table.
heap: A region of memory used for data structures dynamically allocated and deallocated by a program.
host association: Data, and variables automatically available to an internal procedure from its host.
information hiding: The principle that the state and implementation of an object should be private to
that object and only accessible via its public interface.
inheritance: The relationship between classes whereby one class inherits part or all of the public de-
scription of another base class, and instances inherit all the properties and methods of the classes
which they contain.
instance diagram: A drawing showing the instance connection between two objects along with the num-
ber or range of mapping that may occur.
intent: An attribute of a dummy argument that which indicates whether it may be used to transfer data
into the procedure, out of the procedure, or both.
interaction diagram: A diagram that shows the flow of requests, or messages between objects.
interface: The set of all signatures (public methods) defined for an object.
internal file: A character string that is used to transfer and/or convert data from one internal storage
mode to a different internal storage mode.
internal procedure: A procedure contained within another program unit, or class, and which can only
be invoked from within that program unit, or class.
intrinsic constructor: A class member function with the same name as the class which receives initial
values of all the data members as arguments.
Is-A: A relationship in which the derived class is a variation of the base class.
keyword: A programming language word already defined and reserved for a single special purpose.
link: The process of combining compiled program units to form an executable program.
linked list: A data structure in which each element identifies its predecessor and/or successor by some
form of pointer.
linker: Software that combines object files to create an executable machine language program.
member data: Variables declared as components of a defined type and encapsulated in a class.
method: A class member function encapsulated with its class data members.
method resolution: The process of matching a generic operation on an object to the unique method
appropriate to the object’s class.
message: A request, from another object, for an object to carry out one of its operations.
message passing: The philosophy that objects only interact by sending messages to each other that re-
quest some operations to be performed.
module: A program unit which allows other program units to access variables, derived type definitions,
classes and procedures declared within it by USE association.
module procedure: A procedure which is contained within a module, and usually used to define generic
interfaces, and/or to overload or define operators.
object diagram: A graphical representation of an object model showing relationships, attributes, and
operations.
object-oriented (OO): A software development strategy that organizes software as a collection of ob-
jects that contain both data structure and behavior. (Abbreviated OO.)
OO (acronym): Object-oriented.
operation: Manipulation of an object’s data by its member function when it receives a request.
operator overloading: A special case of polymorphism; attaching more than one meaning to the same
operator symbol. ‘Overloading’ is also sometimes used to indicate using the same name for differ-
ent objects.
overflow: An error condition arising from an attempt to store a number which is too large for the storage
location specified; typically caused by an attempt to divide by zero.
overloading: Using the same name for multiple functions or operators in a single scope.
overriding: The ability to change the definition of an inherited method or attribute in a subclass.
parameterized classes: A template for creating real classes that may differ in well-defined ways as
specified by parameters at the time of creation. The parameters are often data types or classes, but
may include other attributes, such as the size of a collection. (Also called generic classes.)
pass-by-reference: Method of passing an argument that permits the function to refer to the memory
holding the original copy of the argument
pass-by-value: Method of passing an argument that evaluates the argument and stores this value in the
corresponding formal argument, so the function has its own copy of the argument value
pointer: A single data object which stands for another (a “target”), which may be a compound object
such as an array, or defined type.
pointer array: An array which is declared with the pointer attribute. Its shape and size may not be
determined until they are created for the array by means of a memory allocation statement.
pre-condition: Specifies the condition(s) that must be true before an operation can be executed.
private: That part of an class, methods or attributes, which may not be accessed by other classes, only
by instances of that class.
prototype: A statement declaring a function’s return type, name, and list of argument types.
public: That part of an object, methods or attributes, which may be accessed by other objects, and thus
constitutes its interface.
quadtree: A tree structure where each tree node has four child nodes.
query operation: An operation that returns a value without modifying any objects.
rank: Number of subscripted variables an array has. A scalar has rank zero, a vector has rank one, a
matrix has rank two.
scope: That part of an executable program within which a lexical token (name) has a single interpreta-
tion.
sequential: A kind of file in which each record is written (read) after the previously written (read) record.
service: A class member function encapsulated with its class data members.
shape: The rank of an array and the extent of each of its subscripts. Often stored in a rank-one array.
signature: The combination of a subprogram’s (operator’s) name and its argument (operand) types.
Does not include function result types.
stack: Region of memory used for allocation of function data areas; allocation of variables on the stack
occurs automatically when a block is entered, and deallocation occurs when the block is exited
strong typing: The property of a programming language such that the type of each variable must be
declared.
structure component: The part of a data object of derived type corresponding to a component of its
type.
sub-object: A portion of a data object that may be referenced or defined independently of other portions.
It may be an array element, an array section, a structure component, or a substring.
subprogram header: A block of code at the beginning of a subprogram definition; includes the name,
and the argument list, if any.
subscript triplet: A method of specifying an array section by means of the initial and final subscript
integer values and an optional stride (or increment).
super class: A class from which another class inherits. (See base class.)
Subject Index
In the index the F90/95 intrinsic attributes, functions, subroutines, statements, etc. are shown in upper-
case letters even though Fortran is not case sensitive. The page numbers are cited with the chapter (or
appendix) number followed by a period, followed by the pages in that chapter separated by commas.
Topics that occur frequently are only cited at their first few uses.
Program Index