To save content items to your account,
please confirm that you agree to abide by our usage policies.
If this is the first time you use this feature, you will be asked to authorise Cambridge Core to connect with your account.
Find out more about saving content to .
To save content items to your Kindle, first ensure no-reply@cambridge.org
is added to your Approved Personal Document E-mail List under your Personal Document Settings
on the Manage Your Content and Devices page of your Amazon account. Then enter the ‘name’ part
of your Kindle email address below.
Find out more about saving to your Kindle.
Note you can select to save to either the @free.kindle.com or @kindle.com variations.
‘@free.kindle.com’ emails are free but can only be saved to your device when it is connected to wi-fi.
‘@kindle.com’ emails can be delivered even when you are not connected to wi-fi, but note that service fees apply.
In this chapter we apply the approximation theory we presented in Chapter 3 to find solutions of linear and nonlinear equations and to perform integration of general functions. Both subjects are classical, but they serve as basic tools in scientific computing operations and in solving systems of ordinary and partial differential equations. With regard to root finding, we consider both scalar as well as systems of nonlinear equations. We present different versions of the Newton-Raphson method, the steepest descent method, and the conjugate gradient method (CGM); we will revisit the latter in Chapter 9. With regard to numerical integration we present some basic quadrature approaches, but we also consider advanced quadrature rules with singular integrands or in unbounded domains.
On the programming side, we first introduce the concept of passing a function to a function; in the previous chapter we were passing variables. This allows an easy implementation of recursion, which is so often encountered in scientific computing. We offer several C++ examples from root finding and numerical integration applications that make use of recursion, and we show an effective use of classes and overloaded operators. We also address parallel programming with emphasis on domain decomposition, specifically the concept of reduction operations. We introduce the MPI commands MPI_Reduce and MPI_Allreduce for accomplishing reduction operations among a collection of processes.
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 with similar features in the C++ and F77 languages and 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 processors 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.
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 fundamental idea-expressing unit is the sentence; in programming, two units – statements and comments – are available.
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 reexamined and reorganized 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 humans can easily understand.
To a great degree, no matter which language you choose for your composition, the idea can be expressed 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.
Fortran 90 includes several features to give the programmer the tools necessary to manage dynamic memory usage. However, one tends to think of these tools as completely free-standing statements or functions. In practice, a large code often has several related arrays or pointers that need to be created and released from memory at the same time. Basically, that means we should supply subprograms that generalize the operations provided by the intrinsic functions allocated and associated.
Here we illustrate this concept with a segment of a class that came from a classic finite element analysis system. The attributes are various types of allocatable arrays – local integers that will establish the array sizes. Additional items required to manage the memory are accessed through the use association of the module, called system_constants. The encapsulated members include initialization, debugging, and printing subprograms as well as the actual memory management members.
Figure 9.1 shows segments of the code Elem_Type_Data_Class. Here the word “type” is not used in the language sense but to identify one of about 18 possible finite elements from an existing library (such as a line, triangle, tetrahedron, etc.). Among the class attributes note that the local integer array item, line 5, serves the purpose of checking for local fatal error checks. It is sized to receive the number of subgroup allocations. Recall that the allocate function has an optional status return code, stat=, lines 36, 37, and so forth.
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 and 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, and 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 represent the attributes and members of our classes graphically. Since they are 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.
The Drill Class
Our first illustration of typical OO methods will be to apply them to a common electric drill. It feeds a rotating cutting bit through a workpiece, thereby removing a volume of material. The effort (power or torque) required to make the hole clearly depends on the material of the workpiece as well as the attributes of the drill.
The preceding chapter described the programming process as starting with a clearly specified task, expressing it mathematically as a set of algorithms, translating the algorithms into 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 is 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 occurs when you need to solve a small problem quickly once, such as a homework assignment, and computational efficiency is not important. You may not care if your code takes 10 seconds or 100 seconds to execute; you want convenience. At that level it may make sense to use an engineering environment like Matlab or Mathematica.
The use of object-oriented (OO) design and object-oriented programming (OOP) methods is becoming increasingly 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. Appearing before F90, C++ currently is probably 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-decade-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 represent its state with routines (functions and subroutines) that access or modify those data, or both. 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.
We have seen that F90 has a very strong intrinsic base for supporting the use of subscripted arrays. Fortran arrays can contain intrinsic data types as well as user-defined types (i.e., ADTs). One cannot directly have an array of pointers, but an array containing defined types that are pointers or that have components that are pointers is allowable. Arrays offer an efficient way to contain information and to insert and extract information. However, there are many times when creating an efficient algorithm dictates that we use some specialized storage method, or container, and a set of operations to act with that storage mode. The storage representation and the set of operations that are allowed for it are known as a data structure. How you store and retrieve an item from a container is often independent of the nature of the item itself. Thus, different instances of a data structure may produce containers for different types of objects. Data structures have the potential for a large amount of code reuse, which is a basic goal of OOP methods. In the following sections we will consider some of the more commonly used containers. We will begin with stacks and queues, which are illustrated in Figure 7.1.
Stacks
A stack is a data structure in which 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.
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 intended to result in mastery of the three r's: reading, 'riting, and 'rithmetic. Today a sound education in engineering programming leads to producing code that satisfies the four r's: readability, reusability, reliability, and real efficiency. Although 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 100 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, and 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 OOP concepts. Even with the current release (Fortran 95) we will demonstrate that Fortran offers essentially all of the tools recommended for OOP 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.
As we saw 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 Figure 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 bring into the new class selectively 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 in Figure 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 inherited from the base class. The form for that modifier is given in Figure 6.3.
It is logical to extend any or all of the aforementioned 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.
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 and 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.
Intrinsic Types
The simplest data type is the LOGICAL type, which has the Boolean values of either .true. or .false. and is used for relational operations. The other nonnumeric data type is the CHARACTER. The sets of valid character values will be defined by the hardware system on which the compiler is installed. Character sets may be available in multiple languages such as English and Japanese. There are international standards for computer character sets. The two most common ones are the English character sets defined in the ASCII and EBCDIC standards that have been adapted by the International Standards Organization (ISO). Both of these standards for defining single characters include the digits (0 to 9), the 26 uppercase letters (A to Z), the 26 lowercase letters (a to z), common mathematical symbols, and many nonprintable codes known as control characters.
It is common in engineering and mathematics to employ a notation in which one or more subscripts are appended to a variable that is a member of some larger set. Such a variable may be a member of a list of scalars, or it may represent an element in a vector, matrix, or Cartesian tensor. In engineering computation, we usually refer to subscripted variables as arrays. Since programming languages do not have a convenient way to append the subscripts, we actually denote them by placing them in parentheses or square brackets. Thus, an element usually written as Ajk becomes A(j,k) in Fortran and Matlab, and A[j] [k] in C++.
Arrays have properties that need to be understood in order to utilize them correctly in any programming language. The primary feature of an array is that it must have at least one subscript. The “rank” of an array is the number of subscripts, or dimensions, it has. Fortran allows an array to have up to seven subscripts, C++ allows four, and Matlab allows only two since it deals only with matrices. An array with two subscripts is called a rank-two array, and one with a single subscript is called a rank-one array, or a vector. Matrices are rank-two arrays that obey special mathematical operations. A scalar variable has no subscripts and is sometimes called a rank-zero array. Rank-one arrays with an extent of one are also viewed as a scalar.