




























































































Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Ordinary and Partial Differential Equation Routines in C, C++, Fortran, Java ® , Maple ® , and MATLAB ®
Typology: Lecture notes
1 / 360
This page cannot be seen from the preview
Don't miss anything!





























































































Learn how to write technical applications in a modern object-oriented approach using Fortran 90 or 95. This book will teach you how to stop focusing on the traditional procedural abilities of Fortran and to employ the principles of object-oriented programming (OOP) to produce clear, highly efficient, executable codes. Get ready now to take advantage of all the features of the finalized, fully object-oriented Fortran 200X!
In addition to covering the OOP methodologies, the book covers the basic foundation of the language and good programming skills, making the book valuable also as a good migration tool for experienced Fortran programmers who want to pick up the OOP paradigm smoothly. The author highlights common themes by using comparisons with Matlab and C++ and uses nu- merous cross-referenced examples to convey all concepts quickly and clearly. Complete code for the examples is included on the accompanying CD-ROM.
Ed Akin is Professor of Mechanical Engineering and Professor of Compu- tational and Applied Mathematics at Rice University.
CAMBRIDGE UNIVERSITY PRESS Cambridge, New York, Melbourne, Madrid, Cape Town, Singapore, São Paulo
Cambridge University Press 40 West 20th Street, New York, NY 10011–4211, USA www.cambridge.org Information on this title:www.cambridge.org/
© Ed Akin 2003
This publication is in copyright. Subject to statutory exception and to the provisions of relevant collective licensing agreements, no reproduction of any part may take place without the written permission of Cambridge University Press.
First published 2003 Reprinted 2003
Printed in the United States of America
A catalogue record for this book is available from the British Library.
ISBN-13 978-0-521-52408-7 paperback ISBN-10 0-521-52408-3 paperback
Cambridge University Press has no responsibility for the persistence or accuracy of URLs for external or third-party Internet Web sites referred to in this publication and does not guarantee that any content on such Web sites is, or will remain, accurate or appropriate.
We make no warranties, express or implied, that the programs contained in this volume are free of error, or are consistent with any particular standard of merchantability, or that they will meet your requirements for any particular application. They should not be relied on for solving a problem whose incorrect solution could result in injury to a person or loss of property. If you do use the programs in such a manner, it is at your own risk. The authors and publisher disclaim all liability for direct or consequential damages resulting from your use of the programs.
x Preface
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 friendliness. 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 closes 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 a supplement. For the sake of completeness, Chapter 4 introduces language-specific details of the topics discussed in the first chapter. The Fortran 90/95 syntax is used there, but in several cases cross references are made to similar constructs in the C++ language and the Matlab environment. Although some readers may want to skip Chapter 4, it will help others learn the Fortran 90/95 syntax, or they may read related publications that use C++ or Matlab. All of the syntax of Fortran 90 is also given in an appendix. Since many Fortran applications relate to manipulating arrays or doing numerical ma- trix analysis, Chapter 5 presents a very detailed coverage of the powerful intrinsic features Fortran 90 has added to provide for more efficient operations with arrays. It has been demon- strated in the literature that object-oriented implementations of scientific projects requiring intensive operations with arrays execute much faster in Fortran 90 than in C++. Since Fortran 90 was designed for operations on vector and parallel machines, that chapter encourages the programmer to avoid unneeded serial loops and to replace them with more efficient intrinsic array functions. Readers not needing to use numerical matrix analysis may skip Chapter 5. Chapter 6 returns to object-oriented methods with a more detailed coverage of using object-oriented analysis and object-oriented design to create classes and demonstrates how to implement them using OOP in Fortran 90. Additional Fortran 90 examples of inheri- tance and polymorphism are given in Chapter 7. Object-oriented programs often require the objects to be stored in some type of “container” or data structure such as a stack or linked list. Fortran 90 object-oriented examples of typical containers are given in Chapter 8. Some specialized topics for more advanced users are given in Chapter 9, and so beginning programmers may skip that chapter. To summarize the two optional uses of this text: it is recommended that experienced Fortran programmers wishing to learn to use OOP cover Chapters 2, 3, 6, 7, 8, and 9, whereas persons studying Fortran for the first time should cover Chapters 1, 2, 3, and. Anyone needing to use numerical matrix analysis should also include Chapter 5. An OO glossary is included to aid in reading this text and the current literature on OOP. Another appendix on Fortran 90 gives an alphabetical listing of its intrinsic routines, a subject-based list of them, a detailed syntax of all the F90 statements, and a set of examples demonstrating the use of every statement. Selected solutions for many of the assignments are included in another appendix, along with comments on those solutions. The final ap- pendix gives the C++ versions of several of the F90 examples in the text. They are provided as an aid to understanding other OOP literature. Since F90 and Matlab are so similar, the corresponding Matlab versions often directly follow the F90 examples in the text.
Preface xi
We are all indebted to the hundreds of programmers who labor on various standards commit- tees to improve all programming languages continuously. Chapter 1 is a modification of in- troductory programming notes developed jointly with Prof. Don Johnson at Rice University. I would like to thank Tinsley Oden and the Texas Institute for Computational Mathematics for generously hosting my sabbatical leave, during which most of this work was developed, and Rice University for financing the sabbatical. Special thanks go to my wife, Kimberly, without whose support and infinite patience this book would not have been completed.
All of the program examples and selected solutions are included on the CD-ROM provided with the book. To be readable on various platforms they have been written with the ISO standard format. Additional files are provided to relate the ISO standard short file names to the full-length program names used in the book. Of course, the source files will have to be processed through a Fortran 90 or 95 or 2000 compiler to form executables. All of the figures are also provided as encapsulated PostScript ©R^ files.
Ed Akin, Rice University, 2002
Program Design
The programming process is similar in approach and creativity to writing a paper. In compo- sition, 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 pro- gramming, 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 reex- amined 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, com- municate 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 under- stand. 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. Similar situations happen in pro- gramming 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 language among those in use today. The C and C++ languages differ from it somewhat, but there are more similarities than not (see Bar-David [6], Barton and Nackman [7], Hanly [22], Hubbard [24], and Nielsen [30]). Matlab, written in C and Fortran, was created much later than these two, and its structure is so similar to the others that it can easily be mastered (see Hanselman and Little- field [23], and Pratap [33]). 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(see Adams et al. [1], Gehrke [17], Hahn [21], Kerrigan [25], and Press et al. [34]). The Fortran 200X 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 com- puter programming languages 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
1
2 Program Design
evolve? In the case of Matlab, the desire to express matrix-like expressions easily motivated its creation. The difference between Matlab and Fortran 90 is infinitesimally small compared with the gap between English and Swahili. An important difference between programming and composition is that in programming you are writing 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 detailed, 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 will then demand to know whether the time is in the morning or 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 that is, 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 programs. 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 to enhance or change it. A characteristic of programs that 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, reusing the “good” portions is easy accomplished. The three-audience situation has prompted most languages to support both computer- 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 mathematical 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 contain assumptions or leaps that confuse the reader. Otherwise, the document you write for yourself or others serves 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 comments, should help you and others understand how the computation proceeds. The existence of comments permits the writer to express the program’s outline directly in the program to help the reader comprehend the computation. These similarities highlight the parallels between composition and programming. Dif- ferences 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 read- ing 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 hand, simple errors, akin to
4 Program Design
Generationn Generationn+
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 cell radiate to its eight neighbors. The rules are applied to the n th generation to yield the next. The row of three filled cells becomes a column of three, for example. What is going to happen to this configuration in the next generation?
sketched in Figure 1.1. They differ in the way that the software development and mainte- nance 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.
The problem the program is to solve must be well specified. The programmer must broadly frame the program’s intent and context by answering several questions.
(^) What must the program accomplish? From operating the space shuttle to inverting a small matrix, some thought must be given to how the program will do what is needed. In technical terms, we need to define the algorithm employed in small-scale programs. In particular, numeric programs need to consider well how calculations are performed. For example, finding the roots of a general polynomial demands a numeric (non-closed form) solution. The choice of algorithm is influenced by the variations in polynomial order and the accuracy demanded. (^) What inputs are required and in what forms? Most programs interact with humans and other programs. This interaction needs to be clearly specified as to what format the data will take and when the data need to be requested or arrive. (^) 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 prewritten graphics environment. (^) What are the required and optional outputs, and what are their formats (printed, magnetic, graphical, 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. The data
1.2 Problem Definition 5
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 inter- pretation of the file’s contents is easy (recall the notion of being able to read tomorrow what is written today) and 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 pro- gramming process, the programmer should know what the program is to do and for whom the program is written. We do not 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 are defined first with additional detail filled in as needed. This approach contrasts with bottom–up design, where the detail is decided first and 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, which is usually motivated by the desire to get some- thing running and figure out later how to organize it all. That approach is prompted 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 has increased likelihood of working more quickly. The result of defining the programming problem is a specification : how the program is structured, what computations it performs, and how it should interact with the user.
An Extended Example: The Game of Life
To illustrate how to organize and write a simple program, let us structure a program that plays The Game of Life. Conway's “Game of Life" was popularized in Martin Gardner's Math- ematical Games column in the October 1970 and February 1971 issues of Scientific Amer- ican. 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 Figure 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, or dies of overcrowding if it has more than three neighbors.
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 genera- tion, 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? (^) Is the size of the array arbitrary or fixed? (^) Am I the only programmer?
1.3 Modular Program Design 7
Program Main Control
Subprogram #
Subprogram #
Figure 1.2: Modular program organization relies on self-contained routines in which 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.
used to express it. A program typically begins with a main segment that controls or directs the solution of the problem by dividing it into subtasks (see Figure 1.2). Each of these may well be decomposed into other routines. This stepwise refinement continues as long as necessary and as long as it benefits program clarity and efficiency. This modular program design is the key feature of modern programming design practice. Furthermore, routines can be tested individually and replaced or rewritten as needed. Before actually writing each routine, a job known in computer circles as the implementation , the program’s organization can be studied: Will the whole satisfy design specifications? Will the program execute efficiently? As the implementation proceeds, each routine’s interface is defined: How does it interact with its master – the routine that called it – and how are data exchanged between the two? In some languages, this interface can be prototyped : the routine’s interface – what it expects and what values it calculates – can be defined and the whole program merged and compiled to check for consistency without performing any calculations. In small programs, where you can have these routine definitions easily fitting onto one page, this prototyping can almost be performed visually. In complex programs, where there may be hundreds or thousands of routines, such prototyping really pays off. Once the interfaces begin to form, we ask whether they make sense: Do they exchange information efficiently? Does each routine have the information it needs, or should the program be reorganized so that data exchange can be accomplished more efficiently? From another viewpoint, you should develop a programming style that “hedges your bets:” programs should be written in such a way that allows their components to be used in a variety of contexts. Again, using a modular programming style, the fundamental components of the calculation should be expressed as a series of subroutines or functions, the interweaving of which is controlled by a main program that reads the input information and produces the output. A modular program can have its components extracted and used in other programs (program reuse) or interfaced to environments. So-called monolithic programs, which tend not to use routines and express the calculation as a single, long-winded program, should not be written. We emphasize that this modular design process proceeds without actually writing program statements. We use a programming-like language known as formal pseudocode to express in prose what routines call others and how. This prose might reexpress a graphic
8 Program Design
representation of program organization such as that shown in Figure 1.2. In addition, expressing the program’s design in pseudocode eases the transition to program composi- tion, the actual programming process. The components of formal pseudocode at this point are few:
(^) 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 interface; (^) conditionals that express changing the flow of a program; and (^) loops that express iteration.
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 lines above share the status of the sentence that characterizes most written languages. They are 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 it is possible to say we are multilingual 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 explicitly 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 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
call routine (input 1,..., input m, output 1,..., output n)