A Practical Introduction to Computational Physics and Scientiﬁc Computing

A Practical Introduction to Computational Physics and Scientiﬁc Computing

AUTHORED BY KONSTANTINOS N. ANAGNOSTOPOULOS

Physics Department, National Technical University of Athens, Zografou Campus, 15780 Zografou,
Greece

konstant@mail.ntua.gr, www.physics.ntua.gr/~konstant/

PUBLISHED BY KONSTANTINOS N. ANAGNOSTOPOULOS

and the

NATIONAL TECHNICAL UNIVERSITY OF ATHENS

Book Website:

www.physics.ntua.gr/~konstant/ComputationalPhysics

©Konstantinos N. Anagnostopoulos 2014, 2016

First Published 2014

Version^{1}
1.1.20161207100600

Cover: Design by K.N. Anagnostopoulos. The front cover picture is a snapshot taken during Monte Carlo simulations of hexatic membranes. Work done with Mark J. Bowick. Relevant video at youtu.be/Erc7Q6YXfLk

This book and its cover(s) are subject to copyright. They are licensed under the Creative Commons
Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit

creativecommons.org/licenses/by-sa/4.0/

The book is accompanied by software available at the book’s website. All the software, unless the copyright does not belong to the author, is open source, covered by the GNU public license, see www.gnu.org/licenses/. This is explicitly mentioned at the end of the respective source ﬁles.

ISBN 978-1-312-46441-4 (lulu.com, vol. I)

ISBN 978-1-312-46488-9 (lulu.com, vol. II)

Foreword

1 The Computer

1.1 The Operating System

1.1.1 Filesystem

1.1.2 Commands

1.1.3 Looking for Help

1.2 Text Processing Tools – Filters

1.3 Programming with Emacs

1.3.1 Calling Emacs

1.3.2 Interacting with Emacs

1.3.3 Basic Editing

1.3.4 Cut and Paste

1.3.5 Windows

1.3.6 Files and Buﬀers

1.3.7 Modes

1.3.8 Emacs Help

1.3.9 Emacs Customization

1.4 The Fortran Programming Language

1.4.1 The Foundation

1.4.2 Details

1.4.3 Arrays

1.5 Gnuplot

1.6 Shell Scripting

2 Kinematics

2.1 Motion on the Plane

2.1.1 Plotting Data

2.1.2 More Examples

2.2 Motion in Space

2.3 Trapped in a Box

2.3.1 The One Dimensional Box

2.3.2 Errors

2.3.3 The Two Dimensional Box

2.4 Applications

2.5 Problems

3 Logistic Map

3.1 Introduction

3.2 Fixed Points and Cycles

3.3 Bifurcation Diagrams

3.4 The Newton-Raphson Method

3.5 Calculation of the Bifurcation Points

3.6 Liapunov Exponents

3.7 Problems

4 Motion of a Particle

4.1 Numerical Integration of Newton’s Equations

4.2 Prelude: Euler Methods

4.3 Runge–Kutta Methods

4.3.1 A Program for the 4th Order Runge–Kutta

4.4 Comparison of the Methods

4.5 The Forced Damped Oscillator

4.6 The Forced Damped Pendulum

4.7 Appendix: On the Euler–Verlet Method

4.8 Appendix: 2nd order Runge–Kutta Method

4.9 Problems

5 Planar Motion

5.1 Runge–Kutta for Planar Motion

5.2 Projectile Motion

5.3 Planetary Motion

5.4 Scattering

5.4.1 Rutherford Scattering

5.4.2 More Scattering Potentials

5.5 More Particles

5.6 Problems

6 Motion in Space

6.1 Adaptive Stepsize Control for Runge–Kutta Methods

6.2 Motion of a Particle in an EM Field

6.3 Relativistic Motion

6.4 Problems

7 Electrostatics

7.1 Electrostatic Field of Point Charges

7.2 The Program – Appetizer and ... Desert

7.3 The Program – Main Dish

7.4 The Program - Conclusion

7.5 Electrostatic Field in the Vacuum

7.6 Results

7.7 Poisson Equation

7.8 Problems

8 Diﬀusion Equation

8.1 Introduction

8.2 Heat Conduction in a Thin Rod

8.3 Discretization

8.4 The Program

8.5 Results

8.6 Diﬀusion on the Circle

8.7 Analysis

8.8 Problems

9 The Anharmonic Oscillator

9.1 Introduction

9.2 Calculation of the Eigenvalues of

9.3 Results

9.4 The Double Well Potential

9.5 Problems

10 Time Independent Schrödinger Equation

10.1 Introduction

10.2 The Inﬁnite Potential Well

10.3 Bound States

10.4 Measurements

10.5 The Anharmonic Oscillator - Again...

10.6 The Lennard–Jones Potential

10.7 Problems

11 The Random Walker

11.1 (Pseudo)Random Numbers

11.2 Using Pseudorandom Number Generators

11.3 Random Walks

11.4 Problems

12 Monte Carlo Simulations

12.1 Statistical Physics

12.2 Entropy

12.3 Fluctuations

12.4 Correlation Functions

12.5 Sampling

12.5.1 Simple Sampling

12.5.2 Importance Sampling

12.6 Markov Processes

12.7 Detailed Balance Condition

12.8 Problems

13 Simulation of the Ising Model

13.1 The Ising Model

13.2 Metropolis

13.3 Implementation

13.3.1 The Program

13.3.2 Towards a Convenient User Interface

13.4 Thermalization

13.5 Autocorrelations

13.6 Statistical Errors

13.6.1 Errors of Independent Measurements

13.6.2 Jackknife

13.6.3 Bootstrap

13.7 Appendix: Autocorrelation Function

13.8 Appendix: Error Analysis

13.8.1 The Jackknife Method

13.8.2 The Bootstrap Method

13.8.3 Comparing the Methods

13.9 Problems

14 Critical Exponents

14.1 Critical Slowing Down

14.2 Wolﬀ Cluster Algorithm

14.3 Implementation

14.3.1 The Program

14.4 Production

14.5 Data Analysis

14.6 Autocorrelation Times

14.7 Temperature Scaling

14.8 Finite Size Scaling

14.9 Calculation of

14.10 Studying Scaling with Collapse

14.11 Binder Cumulant

14.12 Appendix: Scaling

14.12.1 Binder Cumulant

14.12.2 Scaling

14.12.3 Finite Size Scaling

14.13 Appendix: Critical Exponents

14.13.1 Deﬁnitions

14.13.2 Hyperscaling Relations

14.14 Problems

Bibliography

Index

1 The Computer

1.1 The Operating System

1.1.1 Filesystem

1.1.2 Commands

1.1.3 Looking for Help

1.2 Text Processing Tools – Filters

1.3 Programming with Emacs

1.3.1 Calling Emacs

1.3.2 Interacting with Emacs

1.3.3 Basic Editing

1.3.4 Cut and Paste

1.3.5 Windows

1.3.6 Files and Buﬀers

1.3.7 Modes

1.3.8 Emacs Help

1.3.9 Emacs Customization

1.4 The Fortran Programming Language

1.4.1 The Foundation

1.4.2 Details

1.4.3 Arrays

1.5 Gnuplot

1.6 Shell Scripting

2 Kinematics

2.1 Motion on the Plane

2.1.1 Plotting Data

2.1.2 More Examples

2.2 Motion in Space

2.3 Trapped in a Box

2.3.1 The One Dimensional Box

2.3.2 Errors

2.3.3 The Two Dimensional Box

2.4 Applications

2.5 Problems

3 Logistic Map

3.1 Introduction

3.2 Fixed Points and Cycles

3.3 Bifurcation Diagrams

3.4 The Newton-Raphson Method

3.5 Calculation of the Bifurcation Points

3.6 Liapunov Exponents

3.7 Problems

4 Motion of a Particle

4.1 Numerical Integration of Newton’s Equations

4.2 Prelude: Euler Methods

4.3 Runge–Kutta Methods

4.3.1 A Program for the 4th Order Runge–Kutta

4.4 Comparison of the Methods

4.5 The Forced Damped Oscillator

4.6 The Forced Damped Pendulum

4.7 Appendix: On the Euler–Verlet Method

4.8 Appendix: 2nd order Runge–Kutta Method

4.9 Problems

5 Planar Motion

5.1 Runge–Kutta for Planar Motion

5.2 Projectile Motion

5.3 Planetary Motion

5.4 Scattering

5.4.1 Rutherford Scattering

5.4.2 More Scattering Potentials

5.5 More Particles

5.6 Problems

6 Motion in Space

6.1 Adaptive Stepsize Control for Runge–Kutta Methods

6.2 Motion of a Particle in an EM Field

6.3 Relativistic Motion

6.4 Problems

7 Electrostatics

7.1 Electrostatic Field of Point Charges

7.2 The Program – Appetizer and ... Desert

7.3 The Program – Main Dish

7.4 The Program - Conclusion

7.5 Electrostatic Field in the Vacuum

7.6 Results

7.7 Poisson Equation

7.8 Problems

8 Diﬀusion Equation

8.1 Introduction

8.2 Heat Conduction in a Thin Rod

8.3 Discretization

8.4 The Program

8.5 Results

8.6 Diﬀusion on the Circle

8.7 Analysis

8.8 Problems

9 The Anharmonic Oscillator

9.1 Introduction

9.2 Calculation of the Eigenvalues of

9.3 Results

9.4 The Double Well Potential

9.5 Problems

10 Time Independent Schrödinger Equation

10.1 Introduction

10.2 The Inﬁnite Potential Well

10.3 Bound States

10.4 Measurements

10.5 The Anharmonic Oscillator - Again...

10.6 The Lennard–Jones Potential

10.7 Problems

11 The Random Walker

11.1 (Pseudo)Random Numbers

11.2 Using Pseudorandom Number Generators

11.3 Random Walks

11.4 Problems

12 Monte Carlo Simulations

12.1 Statistical Physics

12.2 Entropy

12.3 Fluctuations

12.4 Correlation Functions

12.5 Sampling

12.5.1 Simple Sampling

12.5.2 Importance Sampling

12.6 Markov Processes

12.7 Detailed Balance Condition

12.8 Problems

13 Simulation of the Ising Model

13.1 The Ising Model

13.2 Metropolis

13.3 Implementation

13.3.1 The Program

13.3.2 Towards a Convenient User Interface

13.4 Thermalization

13.5 Autocorrelations

13.6 Statistical Errors

13.6.1 Errors of Independent Measurements

13.6.2 Jackknife

13.6.3 Bootstrap

13.7 Appendix: Autocorrelation Function

13.8 Appendix: Error Analysis

13.8.1 The Jackknife Method

13.8.2 The Bootstrap Method

13.8.3 Comparing the Methods

13.9 Problems

14 Critical Exponents

14.1 Critical Slowing Down

14.2 Wolﬀ Cluster Algorithm

14.3 Implementation

14.3.1 The Program

14.4 Production

14.5 Data Analysis

14.6 Autocorrelation Times

14.7 Temperature Scaling

14.8 Finite Size Scaling

14.9 Calculation of

14.10 Studying Scaling with Collapse

14.11 Binder Cumulant

14.12 Appendix: Scaling

14.12.1 Binder Cumulant

14.12.2 Scaling

14.12.3 Finite Size Scaling

14.13 Appendix: Critical Exponents

14.13.1 Deﬁnitions

14.13.2 Hyperscaling Relations

14.14 Problems

Bibliography

Index

This book has been written assuming that the reader executes all
the commands presented in the text and follows all the
instructions at the same time. If this advice is neglected, then
the book will be of little help and some parts of the text may seem
incomprehensible.

The book’s website is at

http://www.physics.ntua.gr/~konstant/ComputationalPhysics/

From there, you can can download the accompanying software, which
contains, among other things, all the programs presented in the book.

Some conventions: Text using the font shown below refers to commands given using a shell (the “command line”), input and output of programs, code written in Fortran (or any other programming language), as well as to names of ﬁles and programs:

> echo Hello world

Hello world

Hello world

When a line starts with the prompt

>

then the text that follows is a command, which can be given from the command line of a terminal. The second line, Hello World, is the output of the command.

The contents of a ﬁle with Fortran code is listed below:

program add

z = 1.0

y = 2.0

x = z + y

print *, x

end program add

z = 1.0

y = 2.0

x = z + y

print *, x

end program add

What you need in order to work on your PC:

- An operating system of the GNU/Linux family and its basic tools.
- A Fortran compiler. The gfortran compiler is freely available for all major operating systems under an open source license at http://www.gfortran.org.
- An advanced text editor, suitable for editing code in several programming
languages, like Emacs
^{2}. - A good plotting program, suitable for data analysis, like gnuplot
^{3}. - The shell tcsh
^{4}. - The programs awk
^{5}, grep, sort, cat, head, tail, less. Make sure that they are available in your computer environment.

If you have installed a GNU/Linux distribution on your computer, all of the above can be installed easily. For example, in a Debian like distribution (Ubuntu, ...) the commands

> sudo apt-get install tcsh emacs gnuplot-x11 gnuplot-doc

> sudo apt-get install gfortran gawk gawk-doc binutils

> sudo apt-get install manpages-dev coreutils liblapack3

> sudo apt-get install gfortran gawk gawk-doc binutils

> sudo apt-get install manpages-dev coreutils liblapack3

install all the necessary tools.

If you don’t wish to install GNU/Linux on your computer, you can try the following:

- Boot your computer using a usb/DVD live GNU/Linux, like Ubuntu
^{6}. This will not make any permanent changes in your hard drive but it will start and run slower. On the other hand, you may save all your computing environment and documents and use it on any computer you like. - Install Cygwin
^{7}in your Microsoft Windows. It is a very good solution for Microsoft-addicted users. If you choose the full installation, then you will ﬁnd all the tools needed in this book. - Mac OS X is based on Unix. It is possible to install all the software needed in this book and follow the material as presented. Search the internet for instructions, e.g. google “gfortran for Mac”, “emacs for Mac”, “tcsh for Mac”, etc.

This book is the culmination of my ten years’ experience in teaching three introductory, undergraduate level, scientiﬁc computing/computational physics classes at the National Technical University of Athens. It is suitable mostly for junior or senior level science courses, but I am currently teaching its ﬁrst chapters to sophomores without a problem. A two semester course can easily cover all the material in the book, including lab sessions for practicing.

Why another book in computational physics? Well, when I started teaching those classes there was no bibliography available in Greek, so I was compelled to write lecture notes for my students. Soon, I realized that my students, majoring in physics or applied mathematics, were having a hard time with the technical details of programming and computing, rather than with the physics concepts. I had to take them slowly by the hand through the “howto” of computing, something that is reﬂected in the philosophy of this book. Hoping that this could be useful to a wider audience, I decided to translate these notes in English and put them in an order and structure that would turn them into “a book”.

I also decided to make the book freely available on the web. I was partly motivated by my anger caused by the increase of academic (e)book prices to ridiculous levels during times of plummeting publishing costs. Publishers play a diminishing role in academic publishing. They get an almost ready-made manuscript in electronic form by the author. They need to take no serious investment risk on an edition, thanks to print-on-demand capabilities. They have virtually zero cost ebook publishing. Moreover, online bookstores have decreased costs quite a lot. Academic books need no advertisement budget, their success is due to their academic reputation. I don’t see all of these reﬂected on reduced book prices, quite the contrary, I’m afraid.

My main motivation, however, is the freedom that independent publishing would give me in improving, expanding and changing the book in the future. It is great to have no length restrictions for the presentation of the material, as well as not having to report to a publisher. The reader/instructor that ﬁnds the book long, can read/print the portion of the book that she ﬁnds useful for her.

This is not a reference book. It uses some interesting, I hope, physics problems
in order to introduce the student to the fundamentals of solving a scientiﬁc
problem numerically. At the same time, it keeps an eye in the direction of
advanced and high performance scientiﬁc computing. The reader should follow the
instructions given in each chapter, since the book teaches by example. Several
skills are taught through the solution of a particular problem. My lectures take
place in a (large) computer lab, where the students are simultaneously doing
what I am doing (and more). The program that I am editing and the
commands that I am executing are shown on a large screen, displaying my
computer monitor and actions live. The book provides no systematic
teaching of a programming language or a particular tool. A very basic
introduction is given in the ﬁrst chapter and then the reader learns whatever is
necessary for the solution of her problem. There is more than one way to do
it^{8}
and the problems can be solved by following a basic or a fancy way, depending on
the student’s computational literacy. The book provides the necessary tools for
both. A bibliography is provided at the end of the book, so that the missing pieces
of a puzzle can be sought in the literature.

This is also not a computational physics playground. Of course I hope that the
reader will have fun doing what is in the book, but my goal is to provide an
experience that will set the solid foundation for her becoming a high performance
computing, number crunching, heavy duty data analysis expert in the future. This
is why the programming language of the core numerical algorithms has
been chosen to be Fortran, a highly optimized, scientiﬁcally oriented,
programming language. The computer environment is set in a Unix family
operating system, enriched by all the powerful GNU tools provided by the
FSF^{9} .
These tools are indispensable in the complicated data manipulation needed in
scientiﬁc research, which requires ﬂexibility and imagination. Of course,
Fortran is not the best choice for heavy duty object oriented programming,
and is not optimal for interacting with the operating system. The
philosophy^{10}
is to let Fortran do what is best for, number crunching, and leave data
manipulation and ﬁle administration to external, powerful tools. Tools, like
awk, shell scripting, gnuplot, Perl and others, are quite powerful and
complement all the weaknesses of Fortran mentioned before. The plotting
program is chosen to be gnuplot, which provides very powerful tools to
manipulate the data and create massive and complicated plots. It can
also create publication quality plots and contribute to the “fun part” of
the learning experience by creating animations, interactive 3d plots etc.
All the tools used in the book are open source software and they are
accessible to everyone for free. They can be used in a Linux environment, but
they can also be installed and used in Microsoft Windows and Mac OS
X.

The other hard part in teaching computational physics to scientists and engineers is to explain that the approach of solving a problem numerically is quite diﬀerent from solving it analytically. Usually, students of this level are coming with a background in analysis and fundamental physics. It is hard to put them into the mode of thinking about solving a problem using only additions, multiplications and some logical operations. The hardest part is to explain the discretization of a model deﬁned analytically, which can be done in many ways, depending on the accuracy of the approximation. Then, one has to extrapolate the numerical solution, in order to obtain a good approximation of the analytic one. This is done step by step in the book, starting with problems in simple motion and ending with discussing ﬁnite size scaling in statistical physics models in the vicinity of a continuous phase transition.

The book comes together with additional material which can be found at the web page
of the book^{11} .
The accompanying software contains all the computer programs presented in the
book, together with useful tools and programs solving some of the exercises of
each chapter. Each chapter has problems complementing the material
covered in the text. The student needs to solve them in order to obtain
hands on experience in scientiﬁc computing. I hope that I have already
stressed enough that, in order for this book to be useful, it is not enough
to be read in a café or in a living room, but one needs to do what it
says.

Hoping that this book will be useful to you as a student or as an instructor, I would like to ask you to take some time to send me feedback for improving and/or correcting it. I would also appreciate fan mail or, if you are an expert, a review of the book. If you use the book in a class, as a main textbook or as supplementary material, I would also be thrilled to know about it. Send me email at konstantmail.ntua.gr and let me know if I can publish, anonymously or not, (part of) what you say on the web page (otherwise I will only use it privately for my personal ego-boost). Well, nothing is given for free: As one of my friends says, some people are payed in dollars and some others in ego-dollars!

Have fun computing scientiﬁcally!

Athens, 2014.

The Computer

The aim of this chapter is to lay the grounds for the development of the computational skills which are necessary in the following chapters. It is not an in depth exposition but a practical training by example. For a more systematic study of the topics discussed, we refer to the bibliography. Many of the references are freely available in the web.

The are many choices that one has to make when designing a computer
project. These depend on the needs for numerical eﬃciency, on available
programming hours, on the needs for extensibility and upgradability and so on. In
this book we will get the ﬂavor of a project that is mostly scientiﬁcally and
number crunching oriented. One has to make the best of the available computing
resources and have powerful tools available for a productive analysis of the data.
Such an environment, found in most of today’s supercomputers, that oﬀers
ﬂexibility, dependability, simplicity, powerful tools for data analysis and eﬀective
compilers is provided by the family of the Unix operating systems. The
GNU/Linux operating system is a Unix variant that is freely available
and most of its utilities are open source software. The voluntary work of
millions of excellent programmers worldwide has built the most stable,
fastest and highest quality software available for scientiﬁc computing today.
Thanks to the idea of the open source software pioneered by Richard
Stallman^{1}
this giant collaboration has been made possible.

Another choice that we have to make is the programming language, and this is going to be Fortran. Fortran has been built mainly for numerical applications and it has been used by many scientists and engineers because of its eﬃciency in high performance computing. The language is simple and compilers are able to optimize, parallelize and vectorize the code very eﬃciently. There is a lot of scientiﬁc and engineering software available in libraries written in Fortran, which has been used and tested extensively for many years. This is a crucial factor for scientiﬁc software, so that it can be trusted to be eﬃcient and free of errors. Fortran is not the best choice for interacting with the operating system or for text processing. This shortcoming can be easily overcome by the use of external tools and Fortran can be left to do what she has been designed for: number crunching. Its structure is simple and can be used both for procedural and object oriented programming, in such a way that, it will not make the life of an inexperienced programmer diﬃcult, and at the same time provide high level, abstract and powerful tools for high performance, modular, object oriented, programming needed in a large and complicated project.

Fortran, as well as other languages like C, C++ and Java, is a language that needs to be compiled by a compiler. Other languages, like perl, awk, shell scripting, Macsyma, Mathematica, Octave, Matlab, , are interpreted line by line. These languages can be simple in their use, but they can be prohibitively slow when it comes to a numerically demanding program. A compiler is a tool that analyzes the whole program and optimizes the computer instructions executed by the computer. But if programming time is more valuable, then a simple, interpreted language can lead to faster results.

Another choice that we make in this book, and we mention it because it is not the
default in most Linux distributions, is the choice of shell. The shell is a program that
“connects” the user to the operating system. In this book, we will teach how to use a
shell^{2}
to “send” commands to the operating system, which is the most eﬀective way to
perform complicated tasks. We will use the shell tcsh, although most of the
commands can be interpreted by most popular shells. Shell scripting is simpler in
this shell, although shells like bash provide more powerful tools, mostly needed
for complicated system administration tasks. That may cause a small
inconvenience to some readers, since tcsh is not preinstalled in Linux
distributions^{3} .

The Unix family of operating systems oﬀer an environment where complicated tasks can be accomplished by combining many diﬀerent tools, each of which performs a distinct task. This way, one can use the power of each tool, so that trivial but complicated parts of a calculation don’t have to be programmed. This makes the life of a researcher much easier and much more productive, since research requires from us to try many things before we understand how to compute what we are looking for.

In the Unix operating system everything is a ﬁle, and ﬁles are organized in a unique and uniﬁed ﬁlesystem. Documents, pictures, music, movies, executable programs are ﬁles. But also directories or devices, like hard disks, monitors, mice, sound cards etc, are, from the point of view of the operating system, ﬁles. In order for a music ﬁle to be played by your computer, the music data needs to be written to a device ﬁle, connected by the operating system to the sound card. The characters you type in a terminal are read from a ﬁle “the keyboard”, and written to a ﬁle “the monitor” in order to be displayed. Therefore, the ﬁrst thing that we need to understand is the structure of the Unix ﬁlesystem.

There is at least one path in the ﬁlesystem associated with each ﬁle. There are two types of paths, relative paths and absolute paths. These are two examples:

bin/RungeKutta/rk.exe

/home/george/bin/RungeKutta/rk.exe

/home/george/bin/RungeKutta/rk.exe

The paths shown above may refer to the same or a diﬀerent ﬁle. This
depends on “where we are”. If “we are” in the directory /home/george,
then both paths refer to the same ﬁle. If on the other way “we are” in a
directory /home/john or /home/george/CompPhys, then the paths
refer^{4}
to two diﬀerent ﬁles. In the last two cases, the paths refer to the ﬁles

/home/john/bin/RungeKutta/rk.exe

/home/george/CompPhys/bin/RungeKutta/rk.exe

/home/george/CompPhys/bin/RungeKutta/rk.exe

respectively. How can we tell the diﬀerence? An absolute path always begins with the / character, whereas a relative path does not. When we say that “we are in a directory”, we refer to a position in the ﬁlesystem called the current directory, or working directory. Every process in the operating system has a unique current directory associated with it.

The ﬁlesystem is built on its root and looks like a tree positioned upside down.
The symbol of the root is the character / The root is a directory. Every directory
is a ﬁle that contains a list of ﬁles, and it is connected to a unique directory, its
parent directory . Its list of ﬁles contains other directories, called its subdirectories,
which all have it as their parent directory. All these ﬁles are the contents
of the directory. Therefore, the ﬁlesystem is a tree of directories with
the root directory at its top which branch to its subdirectories, which in
their turn branch into other subdirectories and so on. There is practically
no limit to how large this tree can become, even for a quite demanding
environment^{5} .

A path consists of a string of characters, with the characters / separating its components, and refers to a unique location in the ﬁlesystem. Every component refers to a ﬁle. All, but the last one, must be directories in a hierarchy, from parent directory to subdirectory. The only exception is a possible / in the beginning, which refers to the root directory. Such an example can be seen in ﬁgure 1.1.

In a Unix ﬁlesystem there is complete freedom in the choice of the location of the
ﬁles^{6} .
Fortunately, there are some universally accepted conventions respected by almost
everyone. One expects to ﬁnd home directories in the directory /home,
conﬁguration ﬁles in the directory /etc, application executables in directories
with names such as /bin, /usr/bin, /usr/local/bin, software libraries in
directories with names such as /lib, /usr/lib etc.

There are some important conventions in the naming of the paths. A single dot “.” refers to the current directory and a double dot “..” to the parent directory. Similarly, a tilde “~” refers to the home directory of the user. Assume, e.g., that we are the user george running a process with a current directory /home/george/Music/Rock (see ﬁgure 1.1). Then, the following paths refer to the same ﬁle /home/george/Doc/lyrics.doc:

../../Doc/lyrics.doc

~/Doc/lyrics.doc

~george/Doc/lyrics.doc

./../../Doc/lyrics.doc

~/Doc/lyrics.doc

~george/Doc/lyrics.doc

./../../Doc/lyrics.doc

Notice that ~ and ~george refer to the home directory of the user george (ourselves), whereas ~mary refer to the home directory of another user, mary.

We are now going to introduce the basic commands for ﬁlesystem navigation and
manipulation^{7} .
The command cd (=change directory) changes the current directory, whereas the
command pwd (=print working directory) prints the current directory:

> cd /usr/bin

> pwd

/usr/bin

> cd /usr/local/lib

> pwd

/usr/local/lib

> cd

> pwd

/home/george

> cd -

> pwd

/usr/local/lib

> cd ../../

> pwd

/usr

> pwd

/usr/bin

> cd /usr/local/lib

> pwd

/usr/local/lib

> cd

> pwd

/home/george

> cd -

> pwd

/usr/local/lib

> cd ../../

> pwd

/usr

The argument of the command cd is an absolute or a relative path. If the path is correct and we have the necessary permissions, the command changes the current directory to this path. If no path is given, then the current directory changes to the home directory of the user. If the character - is given instead of a path, then the command changes the current directory to the previous current directory.

The command mkdir creates new directories, whereas the command rmdir removes empty directories. Try:

> mkdir new

> mkdir new/01

> mkdir new/01/02/03

mkdir: cannot create directory ‘new/01/02/03’: No such file or

directory

> mkdir -p new/01/02/03

> rmdir new

rmdir: ‘new’: Directory not empty

> rmdir new/01/02/03

> rmdir new/01/02

> rmdir new/01

> rmdir new

> mkdir new/01

> mkdir new/01/02/03

mkdir: cannot create directory ‘new/01/02/03’: No such file or

directory

> mkdir -p new/01/02/03

> rmdir new

rmdir: ‘new’: Directory not empty

> rmdir new/01/02/03

> rmdir new/01/02

> rmdir new/01

> rmdir new

Note that the command mkdir cannot create directories more than one level down the ﬁlesystem, whereas the command mkdir -p can. The “switch” -p makes the behavior of the command diﬀerent than the default one.

In order to list the contents of a directory, we use the command ls (=list):

> ls

BE.eps Byz.eps Programs srBE_xyz.eps srB_xyz.eps

B.eps Bzy.eps srBd_xyz.eps srB_xy.eps

> ls Programs

Backup rk3_Byz.f90 rk3.f90

plot-commands rk3_Bz.f90 rk3_g.f90

BE.eps Byz.eps Programs srBE_xyz.eps srB_xyz.eps

B.eps Bzy.eps srBd_xyz.eps srB_xy.eps

> ls Programs

Backup rk3_Byz.f90 rk3.f90

plot-commands rk3_Bz.f90 rk3_g.f90

The ﬁrst command is given without an argument and it lists the contents of the current directory. The second one, lists the contents of the subdirectory of the current directory Programs. If the argument is a list of paths pointing to regular ﬁles, then the command prints the names of the paths. Another way of giving the command is

[literate={-}{{\texttt{-}}}1]%[basicstyle=\ttfamily]

> ls -l

total 252

-rw-r--r-- 1 george users 24284 May 1 12:08 BE.eps

-rw-r--r-- 1 george users 22024 May 1 11:53 B.eps

-rw-r--r-- 1 george users 29935 May 1 13:02 Byz.eps

-rw-r--r-- 1 george users 48708 May 1 12:41 Bzy.eps

drwxr-xr-x 4 george users 4096 May 1 23:38 Programs

-rw-r--r-- 1 george users 41224 May 1 22:56 srBd_xyz.eps

-rw-r--r-- 1 george users 23187 May 1 21:13 srBE_xyz.eps

-rw-r--r-- 1 george users 24610 May 1 20:29 srB_xy.eps

-rw-r--r-- 1 george users 23763 May 1 20:29 srB_xyz.eps

> ls -l

total 252

-rw-r--r-- 1 george users 24284 May 1 12:08 BE.eps

-rw-r--r-- 1 george users 22024 May 1 11:53 B.eps

-rw-r--r-- 1 george users 29935 May 1 13:02 Byz.eps

-rw-r--r-- 1 george users 48708 May 1 12:41 Bzy.eps

drwxr-xr-x 4 george users 4096 May 1 23:38 Programs

-rw-r--r-- 1 george users 41224 May 1 22:56 srBd_xyz.eps

-rw-r--r-- 1 george users 23187 May 1 21:13 srBE_xyz.eps

-rw-r--r-- 1 george users 24610 May 1 20:29 srB_xy.eps

-rw-r--r-- 1 george users 23763 May 1 20:29 srB_xyz.eps

The switch -l makes ls to list the contents of the current directory together with
useful information on the ﬁles in 9 columns. The ﬁrst column lists the permissions
of the ﬁles (see below). The second one lists the number of links of the
ﬁles^{8} .
The third one lists the user who is the owner of each ﬁle. The fourth one lists the
group that is assigned to the ﬁles. The ﬁfth one lists the size of the ﬁle in bytes
(=8 bits). The next three ones list the modiﬁcation time of the ﬁle and the last
one the paths of the ﬁles.

File permissions^{9}
are separated in three classes: owner permissions, group permissions and other
permissions. Each class is given three speciﬁc permissions, r=read, w=write and
x=execute. For regular ﬁles, read permission eﬀectively means access to the ﬁle
for reading/copying, write permission means permission to modify the contents of
the ﬁle and execute permission means permission to execute the ﬁle as a
command^{10} .
For directories, read permission means that one is able to read the names of the
ﬁles in the directory (but not make it as current directory with the cd command),
write permission means to be able to modify its contents (i.e. create, delete, and
rename ﬁles) and execute permission grants permission to access/modify the
contents of the ﬁles (but not list the names of the ﬁles, this is granted by the read
permission).

The command ls -l lists permissions in three groups. The owner (positions 2-4), the group (positions 5-7) and the rest of the world (others - positions 8-10). For example

[literate={-}{{\texttt{-}}}1]

-rw-r--r--

-rwxr-----

drwx--x--x

-rw-r--r--

-rwxr-----

drwx--x--x

In the ﬁrst case, the owner has read and write but not execute permissions and the group+others have only read permissions. In the second case, the user has read, write and execute permissions, the group has read permissions and others have no permissions at all. In the last case, the user has read, write and execute permissions, whereas the group and the world have only execute permissions. The ﬁrst character d indicates a special ﬁle, which in this case is a directory. All special ﬁles have this position set to a character, while regular ﬁles have it set to -.

File permissions can be modiﬁed by using the command chmod:

> chmod u+x file

> chmod og-w file1 file2

> chmod a+r file

> chmod og-w file1 file2

> chmod a+r file

Using the ﬁrst command, the owner (u user) obtains (+) permission to execute (x) the ﬁle named file. Using the second one, the rest of the world (o others) and the group (ggroup) loose (-) the write (w) permission to the ﬁles named file1 and file2. Using the third one, everyone (aall) obtain read (r) permission on the ﬁle named file.

We will close this section by discussing some commands which are used for administering ﬁles in the ﬁlesystem. The command cp (copy) copies the contents of ﬁles into other ﬁles:

> cp file1.f90 file2.f90

> cp file1.f90 file2.f90 file3.f90 Programs

> cp file1.f90 file2.f90 file3.f90 Programs

If the ﬁle file2.f90 does not exist, the ﬁrst command copies the contents of file1.f90 to a new ﬁle file2.f90. If it already exists, it replaces its contents by the contents of the ﬁle file2.f90. In order for the second command to be executed, Programs needs to be a directory. Then, the contents of the ﬁles file1.f90, file2.f90, file3.f90 are copied to indentical ﬁles in the directory Programs. Of course, we assume that the user has the appropriate privileges for the command to be executed successfully.

The command mv “moves”, or renames, ﬁles:

> mv file1.f90 file2.f90

> mv file1.f90 file2.f90 file3.f90 Programs

> mv file1.f90 file2.f90 file3.f90 Programs

The ﬁrst command renames the ﬁle file1.f90 to file2.f90. The second one moves ﬁles file1.f90, file2.f90, file3.f90 into the directory Programs.

The command rm (remove) deletes
ﬁles^{11} .
Beware, the command is unforgiving: after deletion, a ﬁle cannot be restored into the
ﬁlesystem^{12} .
Therefore, after executing successfully the following commands

> ls

file1.f90 file2.f90 file3.f90 file4.csh

> rm file1.f90 file2.f90 file3.f90

> ls

file4.csh

file1.f90 file2.f90 file3.f90 file4.csh

> rm file1.f90 file2.f90 file3.f90

> ls

file4.csh

the ﬁles file1.f90, file2.f90, file3.f90 do not exist in the ﬁlesystem anymore. A more prudent use of the command demands the ﬂag -i. Then, before deletion we are asked for conﬁrmation:

> rm -i *

rm: remove regular file ‘file1.f90’? y

rm: remove regular file ‘file2.f90’? y

rm: remove regular file ‘file3.f90’? y

rm: remove regular file ‘file4.csh’? n

> ls

file4.csh

rm: remove regular file ‘file1.f90’? y

rm: remove regular file ‘file2.f90’? y

rm: remove regular file ‘file3.f90’? y

rm: remove regular file ‘file4.csh’? n

> ls

file4.csh

When we type y, the ﬁle is deleted, when we type n, the ﬁle is not deleted.

We cannot remove directories the same way. It is possible to use the command
rmdir in order to remove empty directories. In order to delete directories together
with their contents (including subdirectories and their contents) use the
command^{13}
rm -r. For example, assume that the contents of the directories dir1 and
dir1/dir2 are the ﬁles:

./dir1

./dir1/file2.f90

./dir1/file1.f90

./dir1/dir2

./dir1/dir2/file3.f90

./dir1/file2.f90

./dir1/file1.f90

./dir1/dir2

./dir1/dir2/file3.f90

Then the results of the following commands are:

> rm dir1

rm: cannot remove ‘dir1’: Is a directory

> rm dir1/dir2

rm: cannot remove ‘dir1/dir2’: Is a directory

> rmdir dir1

rmdir: dir1: Directory not empty

> rmdir dir1/dir2

rmdir: dir1/dir2: Directory not empty

> rm -r dir1

rm: cannot remove ‘dir1’: Is a directory

> rm dir1/dir2

rm: cannot remove ‘dir1/dir2’: Is a directory

> rmdir dir1

rmdir: dir1: Directory not empty

> rmdir dir1/dir2

rmdir: dir1/dir2: Directory not empty

> rm -r dir1

The last command removes all ﬁles (assuming that we have write permissions for all directories and subdirectories). Alternatively, we can empty the contents of all directories ﬁrst, and then remove them with the command rmdir:

> cd dir1/dir2; rm file3.f90

> cd .. ; rmdir dir2

> rm file1.f90 file2.f90

> cd .. ; rmdir dir1

> cd .. ; rmdir dir2

> rm file1.f90 file2.f90

> cd .. ; rmdir dir1

Note that by using a semicolon, we can execute two or more commands on the same line.

Commands in a Unix operating system are ﬁles with execute permission. When we write a sentence on the command line, like

> ls -l test.f90 test.dat

the shell reads its and interprets it. The shell is a program that creates a interface between a user and the operating system. The ﬁrst word (ls) of the sentence is interpreted as a command. The rest of the words are the arguments of the command and the program can use them (or not) at the discretion of its programmer. There is a special convention for arguments that begin with a - (e.g. -l, --help, --version, -O3). They are called options or switches, and they act as virtual switches that make the program act in a particular way. We have already seen that the program ls gives a diﬀerent output with the switch -l.

In order for a command to be executed, the shell looks for a ﬁle that has the
same name as the command (here a ﬁle named ls). In order to understand where
the shell looks for such a ﬁle, we should digress a little bit and explain the use
of shell variables and environment variables. These have a name, which
is a string of permissible characters, and their values are obtained by
preceding their name with the $ character. For example the variable PATH has
value $PATH. The values of the environment variables can be set with the
command^{14}
setenv and of the shell variables with the command set:

> setenv MYVAR test-env

> set myvar = test-shell

> echo $MYVAR $myvar

test-env test-shell

> set myvar = test-shell

> echo $MYVAR $myvar

test-env test-shell

Two special variables are the variables PATH and path:

>echo $PATH

/usr/local/bin:/usr/bin:/bin:/usr/X11/bin

>echo $path

/usr/local/bin /usr/bin /bin /usr/X11/bin

/usr/local/bin:/usr/bin:/bin:/usr/X11/bin

>echo $path

/usr/local/bin /usr/bin /bin /usr/X11/bin

The ﬁrst one is an environment variable and the second one is a shell variable. Their values are set by the shell, and we don’t need to worry about them, unless we want to change them. Their value is a string of characters whose components should be valid paths to directories. In the ﬁrst case, the components are separated by a :, while in the second case, by one or more spaces. In the example shown above, the shell searches each component of the path or PATH variables (in this order) until it ﬁnds a ﬁle ls in their contents. If it succeeds and the ﬁle has execute permissions, then the program in this ﬁle is executed. If it fails, then it prints an error message. Try the commands:

> which ls

/bin/ls

> ls -l /bin/ls

-rwxr-xr-x 1 root root 93560 Sep 28 2006 /bin/ls

/bin/ls

> ls -l /bin/ls

-rwxr-xr-x 1 root root 93560 Sep 28 2006 /bin/ls

We see that the program that the ls command executes the program in the ﬁle /bin/ls.

The arguments of a command are passed on to the program that the command executes for possible interpretation. For example:

> ls -l test.f90 test.dat

The argument -l is the switch that results in a long listing of the ﬁles. The arguments test.f90 and test.dat are interpreted by the program ls as paths that it will look up for ﬁle information.

You can use the * (wildcard) character as a shorthand notation for a group of ﬁles. For example, in the command shown below

> ls -l *.f90 *.dat

the shell will expand *.f90 and *.dat to a list of all ﬁles whose names end with .f90 or .dat. Therefore, if the current directory contains the ﬁles test.f90, test1.f90, myprog.f90, test.dat, hello.dat, the arguments that will be passed on to the command ls are

> ls -l myprog.f90 test1.f90 test.f90 hello.dat test.dat

For each command there are three special ﬁles associated with it. The ﬁrst one is the standard input (stdin), the second one is the standard output (stdout) and the third one the standard error (stderr). These are ﬁles where the program can print or read data from. By default, these ﬁles are the terminal that the user uses to execute the command. In this case, when the program reads data from the stdin, then it reads the data that we type to the terminal using the keyboard. When the program writes data to the stdout or to the stderr, then the data is written to the terminal.

The advantage of using these special ﬁles in order to read/write data is that the user can redirect the input/output to these ﬁles to any ﬁle she wants. Using the character > at the end of a command redirects the stdout to the ﬁle whose name is written after >. For example:

> ls

file1.f90 file2.f90 file3.f90 file4.csh

> ls > results

> ls

file1.f90 file2.f90 file3.f90 file4.csh results

file1.f90 file2.f90 file3.f90 file4.csh

> ls > results

> ls

file1.f90 file2.f90 file3.f90 file4.csh results

The ﬁrst of the above commands, prints the contents of the current working directory to the terminal. The second command redirects data written to the stdout to the ﬁle results. After executing the command, the ﬁle results is created and its contents are the names of the ﬁles file1.f90 file2.f90 file3.f90 file4.csh. If the ﬁle results does not exist (as in the above example), the ﬁle is created. If it already exists, it is truncated and its contents replaced by the data written to the stdout of the command. If we want to append data without erasing the existing contents, then we should use the string of characters >>. Therefore, if we give the command

> ls >> results

after executing the previous commands, then the contents of the ﬁle results will be

file1.f90 file2.f90 file3.f90 file4.csh

file1.f90 file2.f90 file3.f90 file4.csh results

file1.f90 file2.f90 file3.f90 file4.csh results

The redirection of the stdin is accomplished by the use of the
character < while that of the stderr by the use of the string of
characters^{15}
>&. We will see more examples in section 1.2.

It is possible to redirect the stdout of a command to be the stdin of another command. This is very useful for creating ﬁlters. A ﬁlter is a command that creates a ﬂow of data between two or more programs. This process is called piping. Pipes are creating by using the character |

> cmd1 | cmd2 | cmd3 | ... | cmdN

Using the syntax shown above, the stdout of the command cmd1 is redirected to the stdin of the command cmd2, the stdout of the command cmd2 is redirected to the stdin of the command cmd3 etc. More examples will be presented in section 1.2.

Unix got itself a reputation for not being user friendly. This is far from the truth. Although there is a steep learning curve, detailed documentation for almost everything is available online.

The key for a comfortable ride is to learn how to use the help system available on your computer and on the internet. Most of the commands are self documented. A simple test, like the one shown below, will help you with the basic usage of most of the commands:

[literate={-}{{\texttt{-}}}1]

> cmd --help

> cmd -h

> cmd -help

> cmd -\?

> cmd --help

> cmd -h

> cmd -help

> cmd -\?

For example, try the command ls --help. For a window application, start from the menu “Help”. You should not be afraid and/or lazy and you should proceed with careful searching and reading.

For example, let’s assume that you have heard about a command that sounds like printf, or something like that. The ﬁrst level of online help is the man (=manual) command that searches the “man pages”. Read the output of the command

> man printf

The command info usually provides more detailed and user friendly documentation. It has basic browsing capabilities like the browsers you use to read pages from the internet. Try the command

> info printf

> man -k printf

> whatis printf

> whatis printf

will inform you that there are other, possibly related, commands with names like fprintf, fwprintf, wprintf, sprintf...:

> whatis printf

printf (1) - format and print data

printf (1p) - write formatted output

printf (3) - formatted output conversion

printf (3p) - print formatted output

printf [builtins] (1) - bash built-in commands, see bash(1)

printf (1) - format and print data

printf (1p) - write formatted output

printf (3) - formatted output conversion

printf (3p) - print formatted output

printf [builtins] (1) - bash built-in commands, see bash(1)

The second column printed by the whatis command is the “section” of the man pages. In order to gain access to the information in a particular section, you have to give it as an argument to the man command:

> man 1 printf

> man 1p printf

> man 3 printf

> man 3p printf

> man bash

> man 1p printf

> man 3 printf

> man 3p printf

> man bash

Section 1 of the man pages contains information of ordinary command line commands, section 3 contains information on functions in libraries of the C language. Section 2 contains information on commands used for system administration. You may browse the directory /usr/share/man, or read the man page of the man command (use the command man man for that!).

By using the command

[literate={-}{{\texttt{-}}}1]

> printf --help

> printf --help

we obtain plenty of memory refreshing information. The command

> locate printf

shows us many ﬁles related to the command printf. The commands

> which printf

> where printf

> where printf

give information on the location of the executable(s) of the command printf.

Another useful feature of the shell is the command or it ﬁlename completion.
This means that we can write only the ﬁrst characters of the name
of a command or ﬁlename and then press simultaneously the keys
[Ctrl-d]^{16}
(i.e. press the key Ctrl and the key of the letter d at the same time). Then the shell will
complete the name of the command up to the point that is is unique with the given string of
characters^{17} :

> pri[Ctrl-d]

printafm printf printenv printnodetest

printafm printf printenv printnodetest

Try to type an x on the command line and then type [Ctrl-d]. You will learn all the commands that are available and whose name begins with an x: xterm, xeyes, xclock, xcalc, ...

Finally, the internet contains a wealth of information. Google your blues... and you will be rewarded!

For doing data analysis, we will need powerful tools for manipulating data in text ﬁles. These are ﬁles that consist solely of printable characters. Some tools that can be used in order to construct complicated and powerful ﬁlters are the programs cat, less, head, tail, grep, sort and awk.

Suppose that we have data in a ﬁle named
data^{18}
which contains information on the contents of a food warehouse and their
prices:

bananas 100 pieces 1.45

apples 325 boxes 1.18

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

apples 325 boxes 1.18

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

> cat data

prints the contents of the ﬁle data to the stdout. In general, this command prints the contents of all ﬁles given in its arguments or the stdin if none is given. Since the stdin and the stdout can be redirected, the command

> cat < data > data1

takes the contents of the ﬁle data from the stdin and prints them to the stdout, which in this case is the ﬁle data1. This command has the same result as the command:

> cp data data1

The command

> cat data data1 > data2

prints the contents of the ﬁle data and then the contents of the ﬁle data1 to the stdout. Since the stdout is redirected to the ﬁle data2, data2 contains the data of both ﬁles.

> less gfortran.txt

you can browse the data contained in the ﬁle gfortran.txt one page at a time. Press [space] in order to “turn” a page, [b] to turn back a page. Press the up and down arrows to move one line backwards/forward. Press [g] in order to jump to the beginning of the ﬁle and press [G] in order to jump to the end. Press [h] in order to get a help message and press [q] in order to quit.

> head -n 1 data

bananas 100 pieces 1.45

> tail -n 2 data

bread 62 kilos 0.60

ham 85 kilos 3.56

> tail -n 2 data | head -n 1

bread 62 kilos 0.60

bananas 100 pieces 1.45

> tail -n 2 data

bread 62 kilos 0.60

ham 85 kilos 3.56

> tail -n 2 data | head -n 1

bread 62 kilos 0.60

print the ﬁrst line, the last two lines and the second to the last line of the ﬁle data to the stdout respectively. Note that, by piping the stdout of the command tail to the stdin of the command head, we are able to construct the ﬁlter “print the line before the last one”.

The command sort sorts the contents of a ﬁle by comparing each line of its text with all others. The sorting is alphabetical, unless otherwise set by using options. For example

> sort data

apples 325 boxes 1.18

bananas 100 pieces 1.45

bread 62 kilos 0.60

ham 85 kilos 3.56

pears 34 kilos 2.46

apples 325 boxes 1.18

bananas 100 pieces 1.45

bread 62 kilos 0.60

ham 85 kilos 3.56

pears 34 kilos 2.46

For reverse sorting, try sort -r data. We can also sort by comparing speciﬁc ﬁelds of each line. By default, ﬁelds are words separated by one or more spaces. For example, in order to sort w.r.t. the second column of the ﬁle data, we can use the switch -k 2 (=second ﬁeld). Furthermore, we can use the switch -n for numerical sorting:

> sort -k 2 -n data

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

bananas 100 pieces 1.45

apples 325 boxes 1.18

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

bananas 100 pieces 1.45

apples 325 boxes 1.18

If we omit the switch -n, the comparison of the lines is performed based on character sorting of the second ﬁeld and the result is

> sort -k 2 data

bananas 100 pieces 1.45

apples 325 boxes 1.18

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

bananas 100 pieces 1.45

apples 325 boxes 1.18

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

The last column contains ﬂoating point numbers (not integers). In order to sort by the values of such numbers we should use the switch -g:

> sort -k 4 -g data

bread 62 kilos 0.60

apples 325 boxes 1.18

bananas 100 pieces 1.45

pears 34 kilos 2.46

ham 85 kilos 3.56

bread 62 kilos 0.60

apples 325 boxes 1.18

bananas 100 pieces 1.45

pears 34 kilos 2.46

ham 85 kilos 3.56

The command grep processes a text ﬁle line by line, searching for a given string of characters. When this string is found anywhere in a line, this line is printed to the stdout. The command

> grep kilos data

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

pears 34 kilos 2.46

bread 62 kilos 0.60

ham 85 kilos 3.56

prints each line containing the string “kilos”. If we want to search for all line not containing the string “kilos”, then we add the switch -v:

> grep -v kilos data

bananas 100 pieces 1.45

apples 325 boxes 1.18

bananas 100 pieces 1.45

apples 325 boxes 1.18

We can use a regular expression for searching a whole family of strings of characters. These monsters need a full book for discussing them in detail! But it is not hard to learn how to use some simple forms of regular expressions. Here are some examples:

> grep ^b data

bananas 100 pieces 1.45

bread 62 kilos 0.60

> grep ’0$’ data

bread 62 kilos 0.60

> grep ’3[24]’ data

apples 325 boxes 1.18

pears 34 kilos 2.46

bananas 100 pieces 1.45

bread 62 kilos 0.60

> grep ’0$’ data

bread 62 kilos 0.60

> grep ’3[24]’ data

apples 325 boxes 1.18

pears 34 kilos 2.46

The ﬁrst one, prints each line whose ﬁrst character is a b. The second one, prints each line that ends with a 0. The third one, prints each line contaning the strings 32 or 34.

By far, the strongest tool in our toolbox is the awk program. By default, awk analyzes a text ﬁle line by line. Each word (or ﬁeld in the awk jargon) of these lines is stored in a set of variables with names $1, $2, .... The variable $0 contains the full line currently processed, whereas the variable NF counts the number of ﬁelds in the current line. The variable NR counts the number of lines of the ﬁle processed so far by awk.

An awk program can be written in the command line. A set of commands within { ... } is executed for each line of input. The constructs BEGIN{ ... } and END{ ... } contain commands executed, only once, before and after the processing of the ﬁle respectively. For example, the command

> awk ’{print $1,"total value= ",$2*$4}’ data

bananas total value= 145

apples total value= 383.5

pears total value= 83.64

bread total value= 37.2

ham total value= 302.6

bananas total value= 145

apples total value= 383.5

pears total value= 83.64

bread total value= 37.2

ham total value= 302.6

prints the name of the product (1st column = $1) and the total value stored in the warehouse (2nd column = $2) (4th column = $4). More examples are given below:

> awk ’{value += $2*$4}END{print "Total= ",value}’ data

Total= 951.94

> awk ’{av += $4}END{print "Average Price= ",av/NR}’ data

Average Price= 1.85

> awk ’{print $2^2 * sin($4) + exp($4)}’ data

Total= 951.94

> awk ’{av += $4}END{print "Average Price= ",av/NR}’ data

Average Price= 1.85

> awk ’{print $2^2 * sin($4) + exp($4)}’ data

The ﬁrst one calculates the total value of all products: The processing of each line results in the increment (+=) of the variable value by the product of the second and fourth ﬁelds. In the end (END{ ... }), the string Total= is printed, together with the ﬁnal value of the variable value. This is an easy way for computing the sum of the values calculated for each line. The second command, calculates and prints an average. The sum is calculated in each line and stored in the variable av. In the end, we print the quotient of the sum of all values by the number of lines that have been processed (NR). The last command shows a (crazy) mathematical expression based on numerical values found in each line of the ﬁle data: It computes the square of the second ﬁeld times the sine of the fourth ﬁeld plus the exponential of the fourth ﬁeld.

There is much more potential in the commands presented above. Reading the documentation and getting experience by using them will provide you with very strong tools in order to accomplish complicated tasks.

For a programmer that spends many hours programming every day, the
environment and the tools available for editing the commands of a large and
complicated program determine, to a large extent, the quality of her life! An
editor edits the contents of a text ﬁle, that consists solely of printable characters.
Such editors, available in most Linux environments, are the programs gedit,
vim, pico, nano, zile... They provide basic functionality such as adding,
removing or changing text within a ﬁle as well as more complicated functions,
such as copying, pasting, searching and replacing text etc. There are many
functions that are particularly useful to a programmer, such as detecting and
formatting keywords of a particular programming language, pretty printing,
closing scopes etc, which can be very useful for comfortable programming and for
spotting errors. A very powerful and “knowledgeable” editor, oﬀering many
such functions for several programming languages, is the GNU Emacs
editor^{19} .
Emacs is open source software, it is available for free and
can be used in most available operating systems. It is
programmable^{20}
and the user can automate most of her everyday repeated tasks and conﬁgure it to
her liking. There is a full interaction with the operating system, in fact Emacs has
been built with the ambition of becoming an operating system. For example, a
programmer can edit a Fortran ﬁle, compile it, debug it and run it, everything
done with Emacs commands.

> emacs &

Note the character & at the end of the line. This makes the particular command to run in the background. Without it, the shell waits until a command exits in order to return the prompt.

In a desktop environment, Emacs starts in its own window. For a quick and
dirty editing session, or in the case that a windows environment is not
available^{21} ,
we can run Emacs in a terminal mode. Then, we omit the & at the end of the line
and we run the command

> emacs -nw

The switch -nw forces Emacs to run in terminal mode.

We can interact with Emacs in various ways. Newbies will prefer buttons and menus that oﬀer a simple and intuitive interface. For advanced usage, however, we recommend that you make an eﬀort to learn the keyboard shortcuts. There are also thousands of functions available to be used interactively. They are called from a “command line”, called the minibuﬀer in the Emacs jargon.

Keyboard shortcuts are usually combinations of keystrokes that
consist of the simultaneous pressing of the Ctrl or Alt keys together
with other keys. Our convention is that a key sequence starting with a C-
means that the characters that follow are keys simultaneously pressed
with the Ctrl key. A key sequance starting with a M- means that the
characters that follow are keys simultaneously pressed with the Alt
key^{22} .
Some commands have shortcuts consisting of two or more composite keystrokes.
For example by C-x C-c we mean that we have to press simultaneously the Ctrl
key together with x and then press simultaneously the Ctrl key together with c.
This sequence is a shortcut to the command that exits Emacs. Another example is
C-x 2 which means to press the Ctrl key together with x and then press only the
key 2. This is a shortcut to the command that splits a window horizontally to two
equal parts.

The most useful shortcuts are M-x (press the Alt key siumutaneously with the x key) and C-g. The ﬁrst command takes us to the minibuﬀer where we can give a command by typing its name. For example, type M-x and then type save-buffers-kill-emacs in the minibuﬀer (this will terminate Emacs). The second one is an “SOS button” that interrupts anything Emacs does and returns control to the working buﬀer. This can be pretty handy when a command hangs or destroys our work and we need to interrupt it.

The conventions for the mouse events are as follows: With Mouse-1, Mouse-2 and Mouse-3 we denote a simple click with the left, middle and right buttons of the mouse respectively. With Drag-Mouse-1 we mean to press the left button of the mouse and at the same time drag the mouse.

We summarize the possible ways of giving a command in Emacs with the following examples that have the same eﬀect: Open a ﬁle and put its contents in a buﬀer for editing.

- By pressing the toolbar button that looks like a white sheet of paper (see ﬁgure 1.2).
- By choosing the FileVisit New File menu entry.
- By typing the keyboard shortcut C-x C-f.
- By typing the name of the command in the minibuﬀer: M-x find-file

The number of available commands increases from the top to the bottom of the above list.

In order to edit a ﬁle, Emacs places the contents of a ﬁle in a buﬀer. Such a buﬀer is a chunk of computer memory where the contents of the ﬁle are copied and it is not the ﬁle itself. When we make changes to the contents of a buﬀer, the ﬁle remains intact. For our changes to take eﬀect and be written to the ﬁle, we have to save the buﬀer. Then, the contents of the buﬀer are written back to the ﬁle. It is important to understand the following cycle of events:

- Read a ﬁle’s contents to a buﬀer.
- Edit buﬀer contents.
- Write (save) buﬀer’s contents back into the ﬁle.

Emacs may have more than one buﬀers open for editing simultaneously. By default, the
name of the buﬀer is the same as the name of the ﬁle that is edited, although this is not
necessary^{23} .
The name of a buﬀer is written in the modeline of the window of the buﬀer, as
can be seen in ﬁgure 1.3.

If Emacs crashes or exits before we save our edits, it is possible to recover (part of) them. There is a command M-x recover-file that will guide us through the necessary recovery steps, or we can look for a ﬁle that has the same name as the buﬀer we were editing surrounded by two #. For example, if we were editing the ﬁle file.f90, the automatically saved changes can be found in the ﬁle #file.f90#. Auto saving is done periodically by Emacs and its frequency can be controlled by the user.

The point where we insert text while editing is called “the point”. This is right before the
blinking cursor^{24} .
Each buﬀer has another position marked by “the mark”. A point and the
mark deﬁne a “region” in the buﬀer. This is a part of the text in the
buﬀer where the functions of Emacs can act (e.g. copy, cut, change case,
spelling etc.). We can set the region by setting a point and then press
C-SPC^{25}
or give the command M-x set-mark-command. This deﬁnes the current point to
be the mark. Then we can move the cursor to another point which will deﬁne
a region together with the mark that we set. Alternatively we can use
Drag-Mouse-1 (hold the left mouse button and drag the mouse) and
mark a region. The mark can be set with Mouse-3, i.e. with a simple click
of the right button of the mouse. Therefore by Mouse-1 at a point and
then Mouse-3 at a diﬀerent point will set the region between the two
points.

We can open a ﬁle in a buﬀer with the command C-x C-f, and then by typing its path. If the ﬁle already exists, its contents are copied to a buﬀer, otherwise a new buﬀer is created. Then:

- We can browse the buﬀer’s contents with the Up/Down/Left/Right arrows. Alternatively, by using the commands C-n, C-p, C-f and C-b.
- If the buﬀer is large, we can browse its contents one page at a time by using the Page Up/Page Dn keys. Alternatively, by using the commands C-v, M-v.
- Enter text at the points simply by typing it.
- Delete characters before the point by using the Backspace key and after the point by using the Delete key. The command C-d deletes a forward character.
- Erase all the characters in a line that lie ahead of the point by using the command C-k.
- Open a new line by using Enter or C-o.
- Go to the ﬁrst character of a line by using Home and the last one by using End. Alternatively, by using the commands C-a and C-e, respectively.
- Go to the ﬁrst character of the buﬀer with the key C-Home and the last one with the key C-End. Alternatively, with M-x beginning-of-buffer and M-x end-of-buffer.
- Jump to any line we want: Type M-x goto-line and then the line number.
- Search for text after the point: Press C-s and then the text you are looking for. This is an incremental search and the point jumps immediately to the ﬁrst string that matches the search. The same search can be repeated by pressing C-s repeatedely.

When we ﬁnish editing (or frequently enough so that we don’t loose our work due to an unfortunate event), we save the changes in the buﬀer, either by pressing the save icon on the toolbar, or by pressing the keys C-s, or by giving the command M-x save-buffer.

Use the instructions below for slightly more advanced editing:

- Undo! Some of the changes described below can be catastrophic. Emacs has a great Undo function that keeps in its memory many of the changes inﬂicted by our editing commands. By repeatedely pressing C-/, we undo the changes we made. Alternatively, we can use C-x u or the menu entry EditUndo. Remember that C-g interrupts any Emacs process currently running in the buﬀer.
- Cut text by using the mouse: Click with Mouse-1 at the point before the beginning of the text and then Mouse-3 at the point after the end. A second Mouse-3 and the region is ... gone (in fact it is written in the “kill ring” and it is available for pasting)!
- Cut text by using a keyboard shortcut: Set the mark by C-SPC at the point before the beginning of the text that you want to cut. Then move the cursor after the last character of the text that you want to cut and type C-w.
- Copy text by using the mouse: Drag the mouse Drag-Mouse-1 and mark the region that you want to copy. Alternatively, Mouse-1 at the point before the beginning of the text and then Mouse-3 at the point after the end.
- Copy text by using a keyboard shortcut: Set the mark at the beginning of the text with C-SPC and then move the cursor after the last character of the text. Then type M-w.
- Pasting text with the mouse: We click the middle button
^{26}Mouse-2 at the point that we want to insert the text from the kill ring (the copied text). - Pasting text with a keyboard shortcut: We move the point to the desired insertion point and type C-y.
- Pasting text from previous copying: A fast choice is the menu entry EditPaste from kill manu and then select from the copied texts. The keyboard shortcut is to ﬁrst type C-y and then M-y repeatedly, until the text that we want is yanked.
- Insert the contents of a ﬁle: Move the point to the desired place and type C-x i and the path of the ﬁle. Alternatively, give the command M-x insert-file.
- Insert the contents of a buﬀer: We can insert the contents of a whole buﬀer at a point by giving the command M-x insert-buffer.
- Replace text: We can replace text interactively with the command M-x query-replace, then type the string we want to replace, and then the replacement string. Then, we will be asked whether we want the change to be made and we can answer by typing y (yes), n (no), q (quit the replacements). A , (comma) makes only one replacement and quits (useful if we know that this is the last change that we want to make). If we are conﬁdent, we can change all string in a buﬀer, no questions asked, by giving the command M-x replace-string.
- Change case: We can change the case in the words of a region with the commands M-x upcase-region, M-x capitalize-region and M-x downcase-region. Try it.

We note that cutting and pasting can be made between diﬀerent windows of the same or diﬀerent buﬀers.

Sometimes it is very convenient to edit one or more diﬀerent buﬀers in two or
more windows. The term “windows” in Emacs refers to regions of the same Emacs
desktop window. In fact, a desktop window running an Emacs session is referred
to as a frame in the Emacs jargon. Emacs can split a frame in two or
more windows, horizontally or/and vertically. Study ﬁgure 1.5 on page
81 for details. We can also open a new frame and edit several buﬀers
simultaneously^{27} .
We can manipulate windows and frames as follows:

- Position the point at the center of the window and clear the screen from garbage: C-l (careful: l not 1).
- Split a window in two, horizontally: C-x 2.
- Split a window in two, vertically: C-x 3.
- Delete all other windows (remain only with the current one): C-x 1.
- Delete the current windows (the others remain): C-x 0.
- Move the cursor to the other window: Mouse-1 or C-x o.
- Change the size of window: Use Drag-Mouse-1 on the line separating two windows (the mode line). Use C-^, C-} for making a change of the horizontal/vertical size of a window respectively.
- Create a new frame: C-x 5 2.
- Delete a frame: C-x 5 0.
- Move the cursor to a diﬀerent frame: With Mouse-1 or with C-x 5 o.

You can have many windows in a dumb terminal. This is a blessing when a dekstop environment is not available. Of course, in that case you cannot have many frames.

- Open a ﬁle: C-x C-f or M-x find-file.
- Save a buﬀer: C-x C-s or M-x save buffer. With C-x C-c or M-x save-buffers-kill-emacs we can also exit Emacs. From the menu: FileSave. From the toolbar: click on the save icon.
- Save buﬀer contents to a diﬀerent ﬁle: C-x C-w or M-x write-file. From the menu: FileSave As. From the toolbar: click on the “save as” icon.
- Save all buﬀers: C-x s or M-x save-some-buffers.
- Connect a buﬀer to a diﬀerent ﬁle: M-x set-visited-filename.
- Kill a buﬀer: C-x k.
- Change the buﬀer of the current window: C-x b. Also, use the menu Buffers, then choose the name of the buﬀer.
- Show the list of all buﬀers: C-x C-b. From the menu: Buffers List All Buffers. By typing Enter next to the name of the buﬀer, we make it appear in the window. There are several buﬀer administration commands. Learn about them by typing C-h m when the cursor is in the Bufer List window.
- Recover data from an edited buﬀer: If Emacs crashed, do not despair. Start a new Emacs and type M-x recover-file and follow the instructions. The command M-x recover-session recovers all unsaved buﬀers.
- Backup ﬁles: When you save a buﬀer, the previous contents of the ﬁle become a backup ﬁle. This is a ﬁle whose path is the same as the original’s ﬁle with a ~ appended in the end. For example a ﬁle test.f90 will have as a backup the ﬁle test.f90~. Emacs has version control, and you can conﬁgure it to keep as many versions of your edits as you want.
- Directory browsing and directory administration commands: C-x d or M-x dired. You can act on the ﬁles of a directory (open, delete, rename, copy etc) by giving appropriate commands. When the cursor is in the dired window, type C-h m to read the relevant documentation.

Each buﬀer can be in diﬀerent modes. Each mode may activate diﬀerent commands or editing environment. For example each mode can color keywords relevant to the mode and/or bind keys to diﬀerent commands. There exist major modes, and a buﬀer can be in only one of them. There are also minor modes, and a buﬀer can be in one or more of them. Emacs activates major and minor modes by default for each ﬁle. This usually depends on the ﬁlename but there are also other ways to control this. The user can change both major and minor modes at will, using appropriate commands.

Active modes are shown in a parenthesis on the mode line (see ﬁgures 1.3 and 1.5.

- M-x f90-mode: This mode is of special interest in this book since we will edit a lot of Fortran code. We need it activated in buﬀers that contain a Fortran program and its most useful characteristics are automatic code alignment by pressing the key TAB, the coloring of Fortran commands, variables and other structural constructs (subroutines, if statements, do loops, variable declarations, statement labels etc). Another interesting function is the one that comments out a whole region of code, as well as the inverse function.
- M-x c-mode: For ﬁles containing programs written in the C language. Related modes are the c++-mode, java-mode, perl-mode, awk-mode, python-mode, makefile-mode, octave-mode, gnuplot-mode, mathematica-mode and others.
- latex-mode: For ﬁles containing LATEX text formatting commands.
- text-mode: For editing simple text ﬁles (.txt).
- fundamental-mode: The basic mode, when one that ﬁts better doesn’t exist...

Some interesting minor modes are:

- M-x auto-fill-mode: When a line becomes too long, it is wrapped automatically. A related command to do that for the whole region is M-x fill-region, and for a paragraph M-x fill-paragraph.
- M-x overwite-mode: Instead of inserting characters at the point, overwrite the existing ones. By giving the command several times, we toggle between activating and deactivating the mode.
- M-x read-only mode: When visiting a ﬁle with valuable data that we don’t want to change by mistake, we can activate this mode so that changes will not be allowed by Emacs. When we open a ﬁle with the command C-x C-r or M-x find-file-read-only this mode is activated. We can toggle this mode on and oﬀ with the command C-x C-q (M-x toggle-read-only). See the mode line of the buﬀer jack.c in ﬁgure 1.5 which contains a string %%. By clicking on the %% we can toggle the read-only mode on and oﬀ.
- flyspell-mode: Spell checking as we type.
- font-lock-mode: Colors the structural elements of the buﬀer which are deﬁned by the major mode (e.g. the commands of a Fortran program).

In a desktop environment, we can choose modes from the menu of the mode line. By clicking with Mouse-3 on the name of a mode we are oﬀered options for (de)activating minor modes. With a Mouse-1 we can (de)activate the read-only mode with a click on :%% or :-- respectively. See ﬁgure 1.5.

Emacs’ documentation is impressive. For newbies, we recommend to follow the mini course oﬀered by the Emacs tutorial. You can start the tutorial by typing C-h t or select Help Emacs Tutorial from the menu. Enjoy... The Emacs man page (give the man emacs command in the command line) will give you a summary of the basic options when calling Emacs from the command line.

A quite detailed manual can be found in the Emacs info
pages^{28} .
Using info needs some training, but using the Emacs interface is quite intuitive
and similar to using a web browser. Type the command C-h r (or choose
HelpEmacs Tutorial from the menu) and you will open the front page of
the emacs manual in a new window. By using the keys SPC and Backspace we can
read the documentation page by page. When you ﬁnd a link (similar to web page
hyperlinks), you can click on it in order to open to read the topic it refers to.
Using the navigation icons on the toolbar, you can go to the previous or to
the next pages, go up one level etc. There are commands that can be
given by typing single characters. For example, type d in order to jump to
the main info directory. There you can ﬁnd all the available manuals in
the info system installed on your computer. Type g (emacs) and go to
the top page of the Emacs manual. Type g (info) and read the info
manual.

Emacs is structured in an intuitive and user friendly way. You will learn a lot from the names of the commands: Almost all names of Emacs commands consist of whole words, separated by a hyphen “-”, which almost form a full sentence. These make them quite long sometimes, but by using auto completion of their names this does not pose a grave problem.

- auto completion: The names of the commands are auto completed by typing a TAB one or more times. E.g., type M-x in order to go to the minibuﬀer. Type capi[TAB] and the command autocompletes to capitalize-. By typing [TAB] for a second time, a new window opens and oﬀers the options for completing to two possible commands: capitalize-region and capitalize-word. Type an extra r[TAB] and the command auto completes to the only possible choice capitalize-region. You can see all the commands that start with an s by typing M-x s[TAB][TAB]. Sure, there are many... Click on the *Completions* buﬀer and browse the possibilities. A lot will become clear just by reading the names of the commands. By typing M-x [TAB][TAB], all available commands will appear in your buﬀer!
- keyboard shortcuts: If you don’t remember what happens when you type C-s, no problem: Type C-h k and then the ... forgotten key sequence C-s. Conversely, have you forgotten what is the keyboard shortcut of the command save-buffer? Type C-h w and then the command.
- functions: Are you looking for a command, e.g. save-something -I-forgot? Type C-h f and then save-[TAB] in order to browse over diﬀerent choices. Use Mouse-2 in order to select the command you are interested in, or type and complete the rest of its name (you may use [TAB] again). Read about the function in the *Help* buﬀer that opens.
- variables: Do the same after typing C-h v in order to see a variable’s value and documentation.
- command apropos: Have you forgotten the exact name of a command? No problem... Type C-h a and a keyword. All commands related to the keyword you typed will appear in a buﬀer. Use C-h d for even more information.
- modes: When in a buﬀer, type C-h m and read information about the active modes of the buﬀer.
- info: Type C-h i
- Have you forgotten everything mentioned above? Just type C-h ?

You can customize everything in Emacs. From key bindings to programming your own functions in the Elisp language. The most common way for a user to customize her Emacs sessions, is to put all her customization commands in the ﬁle /.emacs in her home directory. Emacs reads and executes all these commands just before starting a session. Such a .emacs ﬁle is given below:

; Define F1 key to save the buffer

(global-set-key [f1] ’save-buffer)

; Define Control-c s to save the buffer

(global-set-key "\C-cs" ’save-some-buffers)

; Define Meta-s (Alt-s) to interactively search forward

(global-set-key "\M-s" ’isearch-forward)

; Define M-x is to interactively search forward

(defalias ’is ’isearch-forward)

; Define M-x fm to set fortran-mode for the buffer

(defun fm() (interactive) (f90-mode))

; Define M-x sign to sign my name

(defun sign() (interactive) (insert "K. N. Anagnostopoulos"))

(global-set-key [f1] ’save-buffer)

; Define Control-c s to save the buffer

(global-set-key "\C-cs" ’save-some-buffers)

; Define Meta-s (Alt-s) to interactively search forward

(global-set-key "\M-s" ’isearch-forward)

; Define M-x is to interactively search forward

(defalias ’is ’isearch-forward)

; Define M-x fm to set fortran-mode for the buffer

(defun fm() (interactive) (f90-mode))

; Define M-x sign to sign my name

(defun sign() (interactive) (insert "K. N. Anagnostopoulos"))

Everything after a ; is a comment. Functions/commands are enclosed in parentheses. The ﬁrst three ones bind the keys F1, C-c s and M-s to the commands save-buffer, save-some-buffers and isearch-forward respectively. The next one deﬁnes an alias of a command. This means that, when we give the command M-x is in the minibuﬀer, then the command isearch-forward will be executed. The last two commands are the deﬁnitions of the functions (fm) and (sign), which can be called interactively from the minibuﬀer.

For more complicated examples google “emacs .emacs ﬁle” and you will see
other users’ .emacs ﬁles. You may also customize Emacs from the menu
commands OptionsCustomize Emacs. For learning the Elisp language, you
can read the manual “Emacs Lisp Reference Manual” found at the address

www.gnu.org/software/emacs/manual/elisp.html

In this section, we give a very basic introduction to the Fortran programming language. This is not a systematic exposition and you are expected to learn what is needed in this book by example. So, please, if you have not already done it, get in front of a computer and do what you read. You can ﬁnd many good tutorials and books introducing Fortran in a more complete way in the bibliography.

The ﬁrst program that one writes when learning a new programming language is the “Hello World!” program. This is the program that prints “Hello World!” on your screen:

program hello

!print a message to the world:

print *, ’Hello World!’ !this is a comment

end program hello

!print a message to the world:

print *, ’Hello World!’ !this is a comment

end program hello

Commands, or statements, in Fortran are strings of characters
separated by blanks (“words”) that we are allowed to write from
the 1st to the 132nd column of a ﬁle. Each line starts a new
command^{29} .
We can put more than one command on each line by separating them with a
semicolon (;). Everything after an exclamation mark (!) is a comment.
Proliferation of comments is necessary for documenting our code. Good
documentation of our code is an integral part of programming. If the code is
planned to be read by others, or by us at a later time, make sure to explain in
detail what each line is supposed to do. You and your collaborators will save a
lot of time in the process of debugging, improving and extending your
code.

The main entry to the program is deﬁned by the command program name, where name can be any string of alphanumeric characters and an underscore. When the program runs, it starts executing commands at this point. The end of the program, as well as of any other program unit (functions, subroutines, modules), is deﬁned by the line end program name.

The ﬁrst (and only) command given in the above program is the print command. It prints the string of characters “Hello World!” to the stdout. The “*,” is part of the syntax and it is not printed, of course. Fortran does not distinguish capital from small letters, so we could have written PRINT, Print, prINt, ... A string of characters in Fortran is enclosed in single or double quotes (’Hello World!’ or "Hello World!" is equivalent).

In order to execute the commands in a program, it is necessary to compile it. This is a job done by a program called the compiler that translates the human language programming statements into binary commands that can be loaded to the computer memory for execution. There are many Fortran compilers available, and you should learn which compilers are available for use in your computing environment. Typical names for Fortran compilers are gfortran, f90, ifort, g95, .... You should ﬁnd out which compiler is best suited for your program and spend time reading its documentation carefully. It is important to learn how to use a new compiler so that you can ﬁnely tune it to optimize the performance of your program.

We are going to use the open source and freely available compiler
gfortran, which can be installed on most popular operating
systems^{30} .
The compilation command is:

> gfortran hello.f90 -o hello

The switch -o deﬁnes the name of the executable ﬁle, which in our case is hello. If the compilation is successful, the program runs with the command:

> ./hello

Hello world!

Hello world!

Now, we will try a simple calculation. Given the radius of a circle we will compute its length and area. The program can be found in the ﬁle area_01.f90:

program circle_area

PI = 3.141593

R = 4.0

print *,’Perimeter= ’,2.0*PI*R

print *,’Area= ’,PI*R**2

end program circle_area

PI = 3.141593

R = 4.0

print *,’Perimeter= ’,2.0*PI*R

print *,’Area= ’,PI*R**2

end program circle_area

The ﬁrst two commands deﬁne the values of the variables PI and R. These
variables are of type REAL, which are ﬂoating point numbers. Fortran
has implicit rules that can be used to deﬁne the type of variables. By
default, variables whose name starts with i, j, k, l, m and n are of
INTEGER type. These are exact whole numbers. All other variables are of type
REAL^{31} .
We can override these implicit rules by explicitly declaring the type of a variable
or by changing the implicit rules with the use of the implicit statement. The
following two commands have two eﬀects: Computing the length
and the area of the circle and printing the results. The expressions
2.0*PI*R and PI*R**2 are evaluated before being printed by the print
command. The multiplication and raising to a power operators are * and **,
respectively. Note the explicit decimal points at the constants 2.0 and 4.0.
If we write 2 or 4 instead, then these are going to be constants of the
INTEGER type and by using them the wrong way we may obtain surprising
results^{32} .
We compile and run the program with the commands:

> gfortran area_01.f90 -o area

> ./area

Perimeter= 25.13274

Area= 50.26548

> ./area

Perimeter= 25.13274

Area= 50.26548

We will now try a process that repeats itself for many times. We will calculate the length and area of 10 circles of diﬀerent radii , . We will store the values of the radii in an array R(10) of the REAL type. The code can be found in the ﬁle area_02.f90:

program circle_area

dimension R(10)

PI = 3.141593

R(1) = 2.28

do i=2,10

R(i) = R(i-1) + 1.0

enddo

do i = 1,10

perimeter = 2*PI*R(i)

area = PI*R(i)**2

print *,i,’) R= ’,R(i),’ perimeter= ’,perimeter

print *,i,’) R= ’,R(i),’ area = ’,area

enddo

end program circle_area

dimension R(10)

PI = 3.141593

R(1) = 2.28

do i=2,10

R(i) = R(i-1) + 1.0

enddo

do i = 1,10

perimeter = 2*PI*R(i)

area = PI*R(i)**2

print *,i,’) R= ’,R(i),’ perimeter= ’,perimeter

print *,i,’) R= ’,R(i),’ area = ’,area

enddo

end program circle_area

The command dimension R(10) deﬁnes an array of length 10. This way, the elements of the array are referred by an index that takes value from 1 to 10. For example R(4) is the fourth element of the array.

do i = 2, 10

...

enddo

...

enddo

we can write commands that are repeatedly executed while the
INTEGER variable i takes values from 2 to 10 with increasing
step^{33}
equal to 1. The command:

R(i) = R(i-1) + 1.0

deﬁnes the i-th radius to have a value which is larger by the (i-1)-th by 1. For the loop
to work correctly, we must deﬁne the initial value of R(1), otherwise the ﬁnal result is
undeﬁned^{34} .
The second loop uses the deﬁned R-values in order to do the computation and
printing of the results.

Now, we will write an interactive version of the program. Instead of hard coding the values of the radii, we will interact with the user asking her to give her own values. The program will read the 10 values of the radii from the standard input (stdin). The program can be found in the ﬁle area_03.f90:

program circle_area

implicit none

integer,parameter :: N=10

real ,parameter :: PI=3.141593

real ,dimension(N) :: R

real :: area,perimeter

integer :: i

do i=1,N

print*,’Enter radius of circle: ’

read *, R(i)

print*,’i= ’,i,’ R(i)= ’,R(i)

enddo

open(UNIT=13,FILE=’AREA.DAT’)

do i = 1,N

perimeter = 2*PI*R(i)

area = PI*R(i)**2

write(13,*)i,’) R= ’,R(i),’ area= ’,area,&

’ perimeter= ’,perimeter

enddo

close(13)

end program circle_area

implicit none

integer,parameter :: N=10

real ,parameter :: PI=3.141593

real ,dimension(N) :: R

real :: area,perimeter

integer :: i

do i=1,N

print*,’Enter radius of circle: ’

read *, R(i)

print*,’i= ’,i,’ R(i)= ’,R(i)

enddo

open(UNIT=13,FILE=’AREA.DAT’)

do i = 1,N

perimeter = 2*PI*R(i)

area = PI*R(i)**2

write(13,*)i,’) R= ’,R(i),’ area= ’,area,&

’ perimeter= ’,perimeter

enddo

close(13)

end program circle_area

The ﬁrst statement in the above program is implicit none! This statement
deactivates the implicit rules of Fortran, and the programmer is obliged to declare
all variables in a program unit. It is highly recommendable that you always use
this option... You might spend a little more time typing the declarations, but this
eﬀort cannot be compared to the pain looking for bugs due to typos in the names of
variables^{35} !
We will follow this practice throughout the book.

The declarations of the variables follow this statement. The variables N and i are declared to be of the INTEGER type, whereas the variables PI, area, perimeter and R(N) are declared to be of the REAL type. The variables PI and N are speciﬁed to be parameters. Parameters are given speciﬁc values which cannot be changed during the execution of the program.

The array elements R(i) are read using the command read:

read *, R(i)

The command read reads from the stdin. The user types the values at the terminal and then presses [Enter]. We can read more than one variables with one read command.

In order to print data to a ﬁle, we have to connect it to a unit. Each unit is
represented by any number between 0 and 99. Some numbers are reserved for special
units^{36} .
The connection of a unit to a ﬁle is done with the open
command. When this is done, we can write to the ﬁle with the
command^{37}
write(n,*), where n is the unit number. When we are done writing to a ﬁle we
should use the command close(n). Then the unit number is available to be used
for a diﬀerent ﬁle. The ﬂow of commands is like

open(UNIT=13,FILE=’AREA.DAT’)

...

write(13,*) ....

...

close(13)

...

write(13,*) ....

...

close(13)

The name of the ﬁle is determined by the option FILE=’AREA.DAT’ of the open statement. Uppercase or lowercase characters in the ﬁlename make a diﬀerence. The option FILE=’path’ can use any valid path in the ﬁlesystem, provided that we have the necessary permissions.

The line

write(13,*)i,’) R= ’,R(i),’ area= ’,area,&

’ perimeter= ’,perimeter

’ perimeter= ’,perimeter

shows us how to continue a line containing a long statement to the next one. We place a & at the end of the line and then continue writing the statement to the next. This can happen up to 39 times.

The next step will be to learn how to deﬁne and use functions and subroutines. The program below shows how to deﬁne a subroutine area_of_circle, which computes the length and area of a circle of given radius. The following program can be found in the ﬁle area_04.f90:

program circle_area

implicit none

integer,parameter :: N=10

real ,parameter :: P=3.141593

real ,dimension(N):: R

real :: area,perimeter

integer :: i

do i=1,N

print*,’Enter radius of circle: ’

read *, R(i)

print*,’i= ’,i,’ R(i)= ’,R(i)

enddo

open(UNIT=13,FILE=’AREA.DAT’)

do i = 1,N

call area_of_circle(R(i),perimeter,area)

write(13,*)i,’) R= ’,R(i),’ area= ’,area,&

’ perimeter= ’,perimeter

enddo

close(13)

end program circle_area

subroutine area_of_circle(R,L,A)

implicit none

real :: R,L,A

real,parameter :: PI = 3.141593 , PI2 = 2.0*PI

L= PI2*R

A= PI*R*R

return

end subroutine area_of_circle

implicit none

integer,parameter :: N=10

real ,parameter :: P=3.141593

real ,dimension(N):: R

real :: area,perimeter

integer :: i

do i=1,N

print*,’Enter radius of circle: ’

read *, R(i)

print*,’i= ’,i,’ R(i)= ’,R(i)

enddo

open(UNIT=13,FILE=’AREA.DAT’)

do i = 1,N

call area_of_circle(R(i),perimeter,area)

write(13,*)i,’) R= ’,R(i),’ area= ’,area,&

’ perimeter= ’,perimeter

enddo

close(13)

end program circle_area

subroutine area_of_circle(R,L,A)

implicit none

real :: R,L,A

real,parameter :: PI = 3.141593 , PI2 = 2.0*PI

L= PI2*R

A= PI*R*R

return

end subroutine area_of_circle

The calculation of the length and the area of the circle is performed by the call to the subroutine:

call area_of_circle(R(i),perimeter,area)

The command call calls a subroutine and transfers the control of the program within the subroutine. The above subroutine has the arguments (R(i),perimeter,area). The argument R(i) is an input variable. It provides the necessary data to the subroutine in order to perform its computation. The arguments perimeter and area are intended for output. Upon return of the subroutine to the main program, they store the result of the computation. The user of a subroutine must learn how to use its arguments in order to be able to call it in her program. These must be documented carefully by the programmer of the subroutine.

The actual program executed by the subroutine is between the lines:

subroutine area_of_circle(R,L,A)

...

end subroutine area_of_circle

...

end subroutine area_of_circle

The arguments (R,L,A) must be declared in the subroutine and need not have the
same names as the ones that we use when we call it. A change of their values within
the subroutine will change the values of the corresponding variables in the calling
program^{38} .
Therefore, the statements L=PI2*R and A=PI*R*R change the values of the
variables perimeter and area to the desired values. The command return
returns the control to the calling program. The parameters PI and PI2 are
“private” to the subroutine. Their names and values are invisible outside the
subroutine. Similarly, the variables i, N, ..., deﬁned in the main program, are
invisible within the subroutine.

We summarize all of the above in a program trionymo.f90, which computes the roots of a second degree polynomial:

! =============================================================

! Program to compute roots of a 2nd order polynomial

! Tasks: Input from user,logical statements,

! use of functions,stop

! Accuracy in floating point arithmetic

! e.g. IF(x.eq.0.0)

!

! Tests: a,b,c= 1 2 3 D= -8

! a,b,c= 1 -8 16 D= 0 x1= 4

! a,b,c= 1 -1 -2 D= 9. x1= 2. x2= -1.

! a,b,c= 2.3 -2.99 -16.422 x1= 3.4 x2= -2.1

! But: 6.8(x-4.3)**2 = 6.8 x**2 -58.48*x+125.732

! a,b,c= 6.8 -58.48 125.73199

! D= 0.000204147349 x1= 4.30105066 x2= 4.29894924

! a,b,c= 6.8 -58.48 125.732, D= -0.000210891725 < 0!!

! =============================================================

program trionymo

implicit none

real :: a,b,c,D

real :: x1,x2

real :: Discriminant

print*,’Enter a,b,c:’

read *,a,b,c

! Test if we have a well defined polynomial of 2nd degree:

if( a .eq. 0.0) stop ’trionymo: a=0’

! Compute the discriminant (= diakrinousa)

D = Discriminant(a,b,c)

print *, ’Discriminant: D= ’,D

! Compute the roots in each case: D>0, D=0, D<0 (no roots)

if(D .gt. 0.0 )then

call roots(a,b,c,x1,x2)

print *,’Roots: x1= ’,x1,’ x2= ’,x2

else if (D .eq. 0.0) then

call roots(a,b,c,x1,x2)

print *,’Double Root: x1= ’,x1

else

print *,’No real roots’

endif

end program trionymo

! =============================================================

! This is the function that computes the discriminant

! A function returns a value. This value is assigned with the

! statement:

! Discriminant = <value>

! i.e. we simply assign anywhere in the program a variable with

! the name of the function.

! =============================================================

real function Discriminant(a,b,c)

implicit none

real :: a,b,c

Discriminant = b**2 - 4.0 * a * c

end function Discriminant

! =============================================================

! The subroutine that computes the roots.

! =============================================================

subroutine roots(a,b,c,x1,x2)

implicit none

real :: a,b,c

real :: x1,x2

real :: D, Discriminant

if(a .eq. 0.0) stop ’roots: a=0’

D = Discriminant(a,b,c)

if(D.ge.0.0)then

D = sqrt(D)

else

print *,’roots: Sorry, cannot compute roots, D<0=’,D

stop

endif

x1 = (-b + D)/(2.0*a)

x2 = (-b - D)/(2.0*a)

end subroutine roots

! Program to compute roots of a 2nd order polynomial

! Tasks: Input from user,logical statements,

! use of functions,stop

! Accuracy in floating point arithmetic

! e.g. IF(x.eq.0.0)

!

! Tests: a,b,c= 1 2 3 D= -8

! a,b,c= 1 -8 16 D= 0 x1= 4

! a,b,c= 1 -1 -2 D= 9. x1= 2. x2= -1.

! a,b,c= 2.3 -2.99 -16.422 x1= 3.4 x2= -2.1

! But: 6.8(x-4.3)**2 = 6.8 x**2 -58.48*x+125.732

! a,b,c= 6.8 -58.48 125.73199

! D= 0.000204147349 x1= 4.30105066 x2= 4.29894924

! a,b,c= 6.8 -58.48 125.732, D= -0.000210891725 < 0!!

! =============================================================

program trionymo

implicit none

real :: a,b,c,D

real :: x1,x2

real :: Discriminant

print*,’Enter a,b,c:’

read *,a,b,c

! Test if we have a well defined polynomial of 2nd degree:

if( a .eq. 0.0) stop ’trionymo: a=0’

! Compute the discriminant (= diakrinousa)

D = Discriminant(a,b,c)

print *, ’Discriminant: D= ’,D

! Compute the roots in each case: D>0, D=0, D<0 (no roots)

if(D .gt. 0.0 )then

call roots(a,b,c,x1,x2)

print *,’Roots: x1= ’,x1,’ x2= ’,x2

else if (D .eq. 0.0) then

call roots(a,b,c,x1,x2)

print *,’Double Root: x1= ’,x1

else

print *,’No real roots’

endif

end program trionymo

! =============================================================

! This is the function that computes the discriminant

! A function returns a value. This value is assigned with the

! statement:

! Discriminant = <value>

! i.e. we simply assign anywhere in the program a variable with

! the name of the function.

! =============================================================

real function Discriminant(a,b,c)

implicit none

real :: a,b,c

Discriminant = b**2 - 4.0 * a * c

end function Discriminant

! =============================================================

! The subroutine that computes the roots.

! =============================================================

subroutine roots(a,b,c,x1,x2)

implicit none

real :: a,b,c

real :: x1,x2

real :: D, Discriminant

if(a .eq. 0.0) stop ’roots: a=0’

D = Discriminant(a,b,c)

if(D.ge.0.0)then

D = sqrt(D)

else

print *,’roots: Sorry, cannot compute roots, D<0=’,D

stop

endif

x1 = (-b + D)/(2.0*a)

x2 = (-b - D)/(2.0*a)

end subroutine roots

The program reads the coeﬃcients of the polynomial . After a check whether , it computes the discriminant by calling the function Discriminant(a,b,c). The only diﬀerence between a function and a subroutine is that the ﬁrst one returns a value of a given type. We don’t need to use the command call in order to run the commands of a function, this is done by computing its value in an expression. The type of the value returned must be declared both in the program that uses the function (real :: Discriminant) and at the entry point of its program unit (real function Discriminant(a,b,c)). The value returned to the calling program is the one assigned to the variable that has the same name as the function:

real function Discriminant(a,b,c)

...

Discriminant = b**2 - 4.0 * a * c

...

end function Discriminant

...

Discriminant = b**2 - 4.0 * a * c

...

end function Discriminant

Notice the use of the comparison operators .gt. (strictly greater than) and .eq.
(equal to)^{39} :

if(D .gt. 0.0 )then

...

else if (D .eq. 0.0) then

...

else

...

endif

...

else if (D .eq. 0.0) then

...

else

...

endif

You may skip this paragraph during a ﬁrst reading of the book. It is intended mainly to be a reference when reading the later chapters.

There are more types of variables built in Fortran. In the program listed below, we show how to use CHARACTER variables, ﬂoating point numbers of double precision REAL(8) and complex numbers of single and double precision, COMPLEX and COMPLEX(8) respectively:

program f90_vars

implicit none

character(100) :: string

real(4) :: x !single precision, same as real :: x

real(8) :: x8 !equivalent to: double precision x8

!real(16) :: x16 !may not be supported by all compilers

!Complex Numbers:

complex(4) :: z !single precision, same as complex :: z

complex(8) :: z8 !double precision

!A string: a character array:

string = ’Hello World!’ !string smaller size, leaves blanks

!TRIM: trim blanks

print *,’A string ::’, string, ’::’,TRIM(string),’::’

print *,’join them::’, string // string ,’::’

print *,’join them::’, TRIM(string) // TRIM(string),’::’

!Reals with increasing accuracy: Determine PI=3.14159...

x = 4.0 *atan(1.0 )

!Use D for double precision exponent

x8 = 4.0D0*atan(1.0D0)

!Use Q for quadriple precision exponent

!x16 = 4.0Q0*atan(1.0Q0)

print *,’x4= ’,x,’ x8= ’,x8 !,’ x16= ’,x16

print *,’x4: ’,range(x ),precision(x ),EPSILON(x ),&

TINY(x ),HUGE(x )

print *,’x8: ’,range(x8),precision(x8),EPSILON(x8),&

TINY(x8),HUGE(x8)

!Complex numbers: single precision

z = (2.0,1.0)*cexp((3.0,-1.0))

print *,’z= ’,z,’ Re(z)= ’,REAL(z),’ Im(z)= ’,IMAG(z),&

’ |z|= ’,ABS(z),’ z*= ’,CONJG(z)

!Complex numbers: double precision

z8 = (2.0D0,1.0D0)*cdexp((3.0D0,-1.0D0))

print *,’z= ’,z8,’ Re(z)= ’,DBLE(z8),’ Im(z)= ’,DIMAG(z8),&

’ |z|= ’,CDABS(z8),’ z*= ’,DCONJG(z8)

print *,’z4: ’,range(z ),precision(z )

print *,’z8: ’,range(z8),precision(z8)

end program f90_vars

implicit none

character(100) :: string

real(4) :: x !single precision, same as real :: x

real(8) :: x8 !equivalent to: double precision x8

!real(16) :: x16 !may not be supported by all compilers

!Complex Numbers:

complex(4) :: z !single precision, same as complex :: z

complex(8) :: z8 !double precision

!A string: a character array:

string = ’Hello World!’ !string smaller size, leaves blanks

!TRIM: trim blanks

print *,’A string ::’, string, ’::’,TRIM(string),’::’

print *,’join them::’, string // string ,’::’

print *,’join them::’, TRIM(string) // TRIM(string),’::’

!Reals with increasing accuracy: Determine PI=3.14159...

x = 4.0 *atan(1.0 )

!Use D for double precision exponent

x8 = 4.0D0*atan(1.0D0)

!Use Q for quadriple precision exponent

!x16 = 4.0Q0*atan(1.0Q0)

print *,’x4= ’,x,’ x8= ’,x8 !,’ x16= ’,x16

print *,’x4: ’,range(x ),precision(x ),EPSILON(x ),&

TINY(x ),HUGE(x )

print *,’x8: ’,range(x8),precision(x8),EPSILON(x8),&

TINY(x8),HUGE(x8)

!Complex numbers: single precision

z = (2.0,1.0)*cexp((3.0,-1.0))

print *,’z= ’,z,’ Re(z)= ’,REAL(z),’ Im(z)= ’,IMAG(z),&

’ |z|= ’,ABS(z),’ z*= ’,CONJG(z)

!Complex numbers: double precision

z8 = (2.0D0,1.0D0)*cdexp((3.0D0,-1.0D0))

print *,’z= ’,z8,’ Re(z)= ’,DBLE(z8),’ Im(z)= ’,DIMAG(z8),&

’ |z|= ’,CDABS(z8),’ z*= ’,DCONJG(z8)

print *,’z4: ’,range(z ),precision(z )

print *,’z8: ’,range(z8),precision(z8)

end program f90_vars

Some interesting points of the program above are:

- The number K in the declaration REAL(K):: x refers to the number of bytes allocated to the variable x. For K=4 we have single precision (same as REAL), for K=8 double precision and for K=16 quadruple precision. The latter is not always available. In the declarations COMPLEX(K), K refers to the number of bytes allocated to the real and imaginary parts of the complex number.
- We always use the exponent notation D in double precision constants, even if the exponent 0. Otherwise the constants are of single precision and we loose the desired accuracy.
- When we want to state the precision of the return value of an intrinsic function explicitly, we usually add a d at the beginning of its name (e.g. expdexp, ABSDABS. When we want to use the complex version of a function, we usually add a c at the beginning of its name (e.g. expcexp, ABSCABS). Modify the program in order to achieve higher accuracy in the calculation of and , by using double precision variables.
- The maximum number of characters in the CHARACTER variable string is 100, and this is declared by the statement CHARACTER(100).
- When we print a CHARACTER variable, all its characters are printed, including trailing blanks. This is very annoying and we can use the function TRIM in order to remove them.
- The operator // joins two CHARACTER variables or constants. Notice the eﬀect of the function TRIM in the above program.

Another important point to discuss is how to be able to access the same variables
from diﬀerent program units. So far, we simply mentioned that variables have a scope
within each function and subroutine. If we wish to have access to the same location of
memory^{40}
from diﬀerent program units, then we use the COMMON statement which deﬁnes a
common block. See the following example:

! ---------------------------------------

program f90_common

implicit none

real :: k1=1.0,k2=1.0,k3=1.0

common /CONSTANTS/k1,k2

print *,’main: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

call s1 !prints k1 and k2 but not k3

call s2 !changes the value of k2 but not k3

print *,’main: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

end program f90_common

! ---------------------------------------

subroutine s1()

implicit none

real k1,k2,k3

common /CONSTANTS/k1,k2

print *,’s1: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

end subroutine s1

! ---------------------------------------

subroutine s2()

implicit none

real k1,k2,k3

common /CONSTANTS/k1,k2

k2 = 2.0

k3 = 2.0

end subroutine s2

program f90_common

implicit none

real :: k1=1.0,k2=1.0,k3=1.0

common /CONSTANTS/k1,k2

print *,’main: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

call s1 !prints k1 and k2 but not k3

call s2 !changes the value of k2 but not k3

print *,’main: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

end program f90_common

! ---------------------------------------

subroutine s1()

implicit none

real k1,k2,k3

common /CONSTANTS/k1,k2

print *,’s1: k1= ’,k1,’ k2= ’,k2,’ k3= ’,k3

end subroutine s1

! ---------------------------------------

subroutine s2()

implicit none

real k1,k2,k3

common /CONSTANTS/k1,k2

k2 = 2.0

k3 = 2.0

end subroutine s2

The common block has the name CONSTANTS and we can refer to it from any program unit. Each program unit that uses this common block must use the same declaration, although the names of variables are allowed to be diﬀerent. The common block CONSTANTS points to the same location in the computer memory, where we expect to ﬁnd the values of two real variables. These variables (k1 and k2) are used and have their values changed in the subroutines s1 and s2. The variable k3, is a diﬀerent variable in each program unit. The program prints

main: k1= 1.000000 k2= 1.000000 k3= 1.000000

s1: k1= 1.000000 k2= 1.000000 k3= -2.8117745E-05

main: k1= 1.000000 k2= 2.000000 k3= 1.000000

s1: k1= 1.000000 k2= 1.000000 k3= -2.8117745E-05

main: k1= 1.000000 k2= 2.000000 k3= 1.000000

One of the weaknesses of Fortran is that it does not have a convenient control for Input/Output (I/O). For complicated I/O and text manipulation we will use other programs that can do a better job, like awk, perl, shell scripting, or programs written in C/C++. It is important to know some details about I/O commands in Fortran, mainly the speciﬁcations that control the accuracy of printed ﬂoating point numbers. So far, I/O commands, like print, write, read, used a * in order to control the printing of numbers. But we can replace the * with explicit format directives as follows:

program f90_format1

implicit none

integer :: i

real :: x

real, dimension(10) :: a

real(8) :: x8

i = 123456

x = 2.0 *atan2(1.0,0.0)

print ’(A5,I6,F12.7)’,’x,i= ’,i,x

x8 = 2.0D0*atan2(1.0D0,0.0D0)

write(6,’(F18.16,E24.17,G24.17,G24.17)’) x8,x8,&

1.0D15*x8,1.0D18*x8

write(6,’(3F20.16)’) x8,x8/2.0,cos(x8)

write(6,’(200F12.6)’)(a(i), i=1,10)

end program f90_format1

implicit none

integer :: i

real :: x

real, dimension(10) :: a

real(8) :: x8

i = 123456

x = 2.0 *atan2(1.0,0.0)

print ’(A5,I6,F12.7)’,’x,i= ’,i,x

x8 = 2.0D0*atan2(1.0D0,0.0D0)

write(6,’(F18.16,E24.17,G24.17,G24.17)’) x8,x8,&

1.0D15*x8,1.0D18*x8

write(6,’(3F20.16)’) x8,x8/2.0,cos(x8)

write(6,’(200F12.6)’)(a(i), i=1,10)

end program f90_format1

Note the parentheses within the single quotes: (A5,I6,F12.7) is a format directive for the print statement. The A is for printing a CHARACTER, the I for printing an INTEGER and the F for printing a ﬂoating point number. The numbers after the letter declare the number of spaces used for printing each one. Beware! If the printing space is not enough, Fortran will not print and you will ﬁnd a series of * in place of the value of your result! Bummer... In order to estimate the number of spaces needed for a ﬂoating point number, you have to include the space taken by the decimal point, the sign, the exponent character, the sign of the exponent and the digits needed for the exponent. Plus a space to separate the numbers in between... So, be generous and give plenty of printing space. In the example shown above, A5 denotes a character of 5 spaces, I6 and integer of 6 spaces and F12 a ﬂoating point number of 12 spaces. The decimal point in F12.7 means that we want a ﬂoating point with the accuracy of 7 signiﬁcant digits.

The format directive (F18.16,E24.17,G24.17,G24.17) shows how to print double precision variables. These provide an accuracy of 16-17 signiﬁcant digits and there is no need for keeping more digits. The command E prints a number in scientiﬁc form with an exponent. The command G prints the exponent when it is needed. The numbers before the letters denote multiplicity. Therefore 3F20.16 instructs the printing of 3 ﬂoating point numbers by reserving 20 spaces and using 16 signiﬁcant digits for each one of them.

The command write(6,’(200F12.6)’)(a(i), i=1,10) shows how to print a large array using an implicit loop. We used many more spaces than actually needed (200F12.16) which is OK. If the array gets larger by increasing the range of i, then we will have enough room for printing in the same line. The program prints (we have folded the long line in order to make it visible):

x,i= 123456 3.1415927

3.1415926535897931 0.31415926535897931E+01 3141592653589793.0

0.31415926535897933E+19

3.1415926535897931 1.5707963267948966 -1.0000000000000000

0.000000 0.000000 0.000000 ....

3.1415926535897931 0.31415926535897931E+01 3141592653589793.0

0.31415926535897933E+19

3.1415926535897931 1.5707963267948966 -1.0000000000000000

0.000000 0.000000 0.000000 ....

We can organize the format commands by using the FORMAT statement. Then, we use labeled statements in order to refer to them. Labels are numbers put in the beginning of a line which should be unique to a program unit and are within the range 1-99999. We can transfer the control of the program to such a line with a goto command or by using the label in the I/O statements print, write and read as in the example shown below:

program f90_format2

implicit none

integer i

real x, a(10)

real*8 x8

i = 123456

x = 2.0 *atan2(1.0,0.0)

print 100,’x,i= ’,i,x

x8 = 2.0D0*atan2(1.0D0,0.0D0)

write(6,123) x8,x8,&

1.0D15*x8,1.0D18*x8

write(6,4444) x8,x8/2.0,cos(x8)

write(6,9999)(a(i), i=1,10)

100 FORMAT(A5,I6,F12.7)

123 FORMAT(F18.16,E24.17,G24.17,G24.17)

4444 FORMAT(3F20.16)

9999 FORMAT(200F12.6)

end program f90_format2

implicit none

integer i

real x, a(10)

real*8 x8

i = 123456

x = 2.0 *atan2(1.0,0.0)

print 100,’x,i= ’,i,x

x8 = 2.0D0*atan2(1.0D0,0.0D0)

write(6,123) x8,x8,&

1.0D15*x8,1.0D18*x8

write(6,4444) x8,x8/2.0,cos(x8)

write(6,9999)(a(i), i=1,10)

100 FORMAT(A5,I6,F12.7)

123 FORMAT(F18.16,E24.17,G24.17,G24.17)

4444 FORMAT(3F20.16)

9999 FORMAT(200F12.6)

end program f90_format2

The reader should also study the Fortran intrinsic functions shown in table 1.2, page 181.

You may skip this section during the ﬁrst reading of this book. It will be useful to come back here later.

Arrays are related data of the same type which can be accessed by using one or more indices. For example, after a declaration real, dimension(10) :: A, the expressions

A(1), A(2), ... , A(10)

refer to its 10 real values. The indices can be integer expressions, for example

A(i), B(2*i+3), C(INT(x+y(j)))

where in the last case we used the integer value of the intrinsic function INT(x), which returns the integer part of x. Note that, arrays and functions enclose indices and arguments between parentheses (...) which are of the same style, and the compiler must look at their declarations in order to tell the diﬀerence. Examples of array declarations are

real, dimension(10) :: a,b

real, dimension(20) :: c,d

real, dimension(20) :: c,d

which declare the arrays a, b, c, d, which are of the real kind, with elements a(1) ... a(10), b(1) ... b(10), c(1) ... c(20) and d(1) ... d(20). An equivalent declaration is

real :: a(10), b(10), c(20), d(20)

or

integer, parameter :: n1 = 10, n2 = 20

real, dimension(n1) :: a, c(n2)

real :: b(n1), d(n2)

real, dimension(n1) :: a, c(n2)

real :: b(n1), d(n2)

In the last form, we show how to use constant parameters for declaring the size of arrays. For the declarations shown above, the lower bound of all arrays is 1 and the upper bound for a and b is 10 and for c and d is 20. The upper and lower bound of arrays can be explicitly determined. The declarations

integer, parameter :: n1 = 10, n2 = 20

real, dimension(0:n1) :: a

real, dimension(-n1:n2) :: c

real, dimension(0:n1) :: a

real, dimension(-n1:n2) :: c

deﬁne the real array a with 11 values a(0) ... a(10) and the array c with 31 values c(-10) c(-9) ... c(-1) c(0) c(1) ... c(20).

The arrays shown above have dimension 1 and they are like vectors.
We can declare arrays of more than one dimension. This means
that we need more than one indices in order to determine an array
element^{41} .
Therefore, the declaration

integer, dimension(2,2) :: a

deﬁnes an integer array with values a(1,1), a(1,2), a(2,1) and a(2,2). The following declarations deﬁne two three dimensional real arrays a and b:

integer, parameter :: n1 = 10, n2 = 20, n3 = 2*n1+n2

real, dimension(n1,n2,n3) :: a

real, dimension(-n1:n1,0:n2,13:n3) :: b

real, dimension(n1,n2,n3) :: a

real, dimension(-n1:n1,0:n2,13:n3) :: b

Some important deﬁnitions used in the bibliography are:

- array: Variables of the same type to which we refer with one or more indices. Variables with only one value are called scalar.
- An array’s dimension has an upper bound and a lower bound which deﬁne the allowed range of index values. If the lower bound is omitted in a declaration, then it takes the value 1.
- The rank of an array is the number of its dimensions, i.e. the number of indices needed to determine its values.
- The extent of a dimension it the number of its elements. It is equal to (upper bound)-(lower bound)+1.
- The size of an array is the total number of its elements. For a one dimensional array, its size is equal to its extent, whereas for a multi dimensional one, it is equal to the product of the extents of all of its dimensions.
- The shape of an array is its rank and extents of all its dimensions.

The values of arrays can be set the same way as scalars:

integer :: i

real :: a(4), b(2,2)

b(1,1) = 2.0 ; b(1,2) = 4.0

b(2,1) = 3.4 ; b(2,2) = 7.8

do i=1,4

a(i) = 1.0

enddo

real :: a(4), b(2,2)

b(1,1) = 2.0 ; b(1,2) = 4.0

b(2,1) = 3.4 ; b(2,2) = 7.8

do i=1,4

a(i) = 1.0

enddo

Alternatively we can use the name of the array as one object:

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

b = 0.0

b = 0.0

The ﬁrst line deﬁnes the values of an array by using an array constructor. The second line deﬁnes all elements of the array b to be equal to 0. This is an example of a very convenient feature of the Fortran language. If all the arrays in an expression are conformable, then we can use the intrinsic Fortran operations to act on whole arrays. Two arrays are conformable if they have the same shape or if one of them is a scalar. Therefore the program

integer :: i,j

real :: x,y,a(10),b(10),c(4,4),d(4,4)

do i=1,10

a(i) = b(i)

enddo

do j=1,4

do i=1,4

c(i,j) = x*d(i,j)+y

enddo

enddo

real :: x,y,a(10),b(10),c(4,4),d(4,4)

do i=1,10

a(i) = b(i)

enddo

do j=1,4

do i=1,4

c(i,j) = x*d(i,j)+y

enddo

enddo

is equivalent to

integer :: i,j

real :: x,y,a(10),b(10),c(4,4),d(4,4)

a = b

c = x*d+y

real :: x,y,a(10),b(10),c(4,4),d(4,4)

a = b

c = x*d+y

Many Fortran intrinsic functions are elemental. This means that their arguments can be arrays, in which case the function acts on each array element separately. For example, the commands

integer :: i,j

real :: x,y,a(10),b(10),c(4,4),d(4,4)

c = sin(d) + x*exp(-2.0*d)

call random_number(a)

real :: x,y,a(10),b(10),c(4,4),d(4,4)

c = sin(d) + x*exp(-2.0*d)

call random_number(a)

set c(i,j) = sin(d(i,j))+x*exp(-2.0*d(i,j)) for all i and j, and the elements of a(i) equal to a random number uniformly distributed in the interval . We should stress that in order for two arrays to be conformable, it is not necessary that they have the same lower and upper bounds. For example, the command b=c*d in the following program has the same eﬀect as the do loop:

integer :: i

real :: b(0:19), c(10:29), d(-9:10)

b = c*d

do i=1,20

b(i-1) = c(i+9) * d(i-10)

enddo

real :: b(0:19), c(10:29), d(-9:10)

b = c*d

do i=1,20

b(i-1) = c(i+9) * d(i-10)

enddo

In the following, we mention some useful functions that act on arrays. Assume that

real :: a(-10:10), b(-10:10), c(10,10), d(10,10), e(10,10)

then

- LBOUND(a) and UBOUND(a) return the lower bound and the upper bound of the array a. In the above example LBOUND(a) = -10 and UBOUND(a) = 10.
- c = TRANSPOSE(d) sets c(i,j)=d(j,i).
- e = MATMUL(c,d) sets the array e equal to the matrix product c, d. I.e. e(i,j)=c(i,k)*d(k,j). Be careful, the command e=c*d sets e(i,j)=c(i,j)*d(i,j).
- SUM(a) computes the sum of all the elements of a.

I.e. SUM(a) = a(i) - PRODUCT(a) computes the product of all the elements of a.

I.e. PRODUCT(a) = a(i) - DOT_PRODUCT(a,b) computes the inner product of a, b.

I.e. DOT_PRODUCT(a,b) = a(i)*b(i) - MAXVAL(a) and MINVAL(a) return the maximum and minimum values in the array a respectively.

You can ﬁnd more functions and documentation in the bibliography [11, 10]. In the following, we provide some information related to the Input/Output (I/O) of arrays. Input (“reading”) and output (“writing”) of array values can be done by reading and writing their elements in any order we want. In the example below, we read the array a and write the array b in two diﬀerent ways:

integer :: i,j

real :: a(4), b(2,2)

do i=1,4

read *,a(i)

enddo

read *, (a(i), i=1,4)

do j=1,2

do i=1,2

print *,b(i,j)

enddo

enddo

print *,( (b(i,j), i=1,2), j=1,2)

real :: a(4), b(2,2)

do i=1,4

read *,a(i)

enddo

read *, (a(i), i=1,4)

do j=1,2

do i=1,2

print *,b(i,j)

enddo

enddo

print *,( (b(i,j), i=1,2), j=1,2)

Inside the do loops, input and output is done one element per line from/to
standard input/output. The commands (a(i), i=1,4) and ( (b(i,j) i=1,2),
j=1,2) are implied do loops and read/write from/to the same line. During input,
if the number of values for a are exhausted, then the program tries to read
values from the following line(s). Similarly, if the output of b exhausts the
maximum number of characters per line, then the output continues in the next
line^{42} .
Try it...

We can also preform I/O of arrays without explicit reference to their elements. In this case, the arrays are read/written in a speciﬁed order. For example, the program

real :: a(4), b(2,2)

read *, a

read *, b

print *, a,b

read *, a

read *, b

print *, a,b

reads the values a(1) a(2) a(3) a(4) from the stdin. Then, it continues reading b(1,1), b(2,1), b(1,2), b(2,2) from the next line (record). Notice that the array b is read in a column major way. Printing a and b, will print a(1) a(2) a(3) a(4) and b(1,1), b(2,1), b(1,2), b(2,2) in two diﬀerent records (also in column major mode).

Finally, we summarize some of the Fortran capabilities in array manipulation. More details can be found in the bibliography. Read the comments in the program for a partial explanation of each command:

program arrays

implicit none

integer :: i,j,n,m

real :: a(3), b(3,3), c(3,3)=-99.0, d(3,3)=-99.0, s

integer :: semester(1000),grade(1000)

logical :: pass(1000)

!construct the matrix: use the RESHAPE function

!|1.1 -1.2 -1.3|

!|2.1 2.2 -2.3|

!|3.1 3.2 3.3|

b = RESHAPE((/ 1.1, 2.1, 3.1, & !(notice rows<->columns)

-1.2, 2.2, 3.2, &

-1.3, -2.3, 3.3 /),(/3,3/))

!same matrix, now exchange rows and columns: ORDER=(/2,1/)

b = RESHAPE((/ 1.2, -1.2, -1.3, &

2.1, 2.2, -2.3, &

3.1, 3.2, 3.3 /),(/3,3/),ORDER=(/2,1/))

a = b(:,2) !a assigned the second column of b: a(i)=b(i,2)

a = b(1,:) !a assigned the first row of b: a(i)=b(1,i)

a = 2.0*b(:,3)+sin(b(2,:))!a(i)= 2*b(i,3)+sin(b(2,i))

a = 1.0+2.0*exp(-a)+b(:,3)!a(i)= 1+2*exp(-a(i))+b(i,3)

s = SUM(b) !returns sum of all elements of b

s = SUM(b,MASK=(b.gt.0))!returns sum of positive elements of b

a = SUM(b,DIM=1) !each a(i) is the sum of the columns of b

a = SUM(b,DIM=2) !each a(i) is the sum of the rows of b

!repeat all the above using PRODUCT!

!all instructions may be executed in parallel at any order!

FORALL(i=1:3) c(i,i) = a(i) !set the diagonal of c

!compute upper bounds of indices in b:

n=UBOUND(b,DIM=1);m=UBOUND(b,DIM=2)

!log needs positive argument, add a restriction ("mask")

FORALL(i=1:n,j=1:m, b(i,j).gt.0.0 ) c(i,j) = log(b(i,j))

!upper triangular part of matrix:

!careful, j=i+1:m NOT permitted

FORALL(i=1:n,j=1:m, i .lt. j ) c(i,j) = b(i,j)

!each statement executed BEFORE the next one!

FORALL(i=2:n-1,j=2:n-1)

!all right hand side evaluated BEFORE the assignment

!i.e., the OLD values of b averaged and then assigned to b

b(i,j)=(b(i+1,j)+b(i-1,j)+b(i,j+1)+b(i,j-1))/4.0

c(i,j)=1.0/b(i+1,j+1) !the NEW values of b are assigned

END FORALL

! assignment but only for elements b(i,j) which are not 0

WHERE (b .ne. 0.0) c = 1.0/b

!MATMUL(b,c) is evaluated, then d is assigned the result only

!at positions where b>0.

WHERE (b .gt. 0.0) d = MATMUL(b,c)

WHERE (grade .ge. 5 )

semester = semester + 1 !student’s semester increases by 1

pass = .true.

ELSEWHERE

pass = .false.

END WHERE

end program arrays

implicit none

integer :: i,j,n,m

real :: a(3), b(3,3), c(3,3)=-99.0, d(3,3)=-99.0, s

integer :: semester(1000),grade(1000)

logical :: pass(1000)

!construct the matrix: use the RESHAPE function

!|1.1 -1.2 -1.3|

!|2.1 2.2 -2.3|

!|3.1 3.2 3.3|

b = RESHAPE((/ 1.1, 2.1, 3.1, & !(notice rows<->columns)

-1.2, 2.2, 3.2, &

-1.3, -2.3, 3.3 /),(/3,3/))

!same matrix, now exchange rows and columns: ORDER=(/2,1/)

b = RESHAPE((/ 1.2, -1.2, -1.3, &

2.1, 2.2, -2.3, &

3.1, 3.2, 3.3 /),(/3,3/),ORDER=(/2,1/))

a = b(:,2) !a assigned the second column of b: a(i)=b(i,2)

a = b(1,:) !a assigned the first row of b: a(i)=b(1,i)

a = 2.0*b(:,3)+sin(b(2,:))!a(i)= 2*b(i,3)+sin(b(2,i))

a = 1.0+2.0*exp(-a)+b(:,3)!a(i)= 1+2*exp(-a(i))+b(i,3)

s = SUM(b) !returns sum of all elements of b

s = SUM(b,MASK=(b.gt.0))!returns sum of positive elements of b

a = SUM(b,DIM=1) !each a(i) is the sum of the columns of b

a = SUM(b,DIM=2) !each a(i) is the sum of the rows of b

!repeat all the above using PRODUCT!

!all instructions may be executed in parallel at any order!

FORALL(i=1:3) c(i,i) = a(i) !set the diagonal of c

!compute upper bounds of indices in b:

n=UBOUND(b,DIM=1);m=UBOUND(b,DIM=2)

!log needs positive argument, add a restriction ("mask")

FORALL(i=1:n,j=1:m, b(i,j).gt.0.0 ) c(i,j) = log(b(i,j))

!upper triangular part of matrix:

!careful, j=i+1:m NOT permitted

FORALL(i=1:n,j=1:m, i .lt. j ) c(i,j) = b(i,j)

!each statement executed BEFORE the next one!

FORALL(i=2:n-1,j=2:n-1)

!all right hand side evaluated BEFORE the assignment

!i.e., the OLD values of b averaged and then assigned to b

b(i,j)=(b(i+1,j)+b(i-1,j)+b(i,j+1)+b(i,j-1))/4.0

c(i,j)=1.0/b(i+1,j+1) !the NEW values of b are assigned

END FORALL

! assignment but only for elements b(i,j) which are not 0

WHERE (b .ne. 0.0) c = 1.0/b

!MATMUL(b,c) is evaluated, then d is assigned the result only

!at positions where b>0.

WHERE (b .gt. 0.0) d = MATMUL(b,c)

WHERE (grade .ge. 5 )

semester = semester + 1 !student’s semester increases by 1

pass = .true.

ELSEWHERE

pass = .false.

END WHERE

end program arrays

The code shown above can be found in the ﬁle f90_arrays.f90 of the accompanying software.

Plotting data is an indispensable tool for their qualitative, but also quantitative, analysis. Gnuplot is a high quality, open source, plotting program that can be used for generating publication quality plots, as well as for heavy duty analysis of a large amount of scientiﬁc data. Its great advantage is the possibility to use it from the command line, as well as from shell scripts and other programs. Gnuplot is programmable and it is possible to call external programs in order manipulate data and create complicated plots. There are many mathematical functions built in gnuplot and a fit command for non linear ﬁtting of data. There exist interactive terminals where the user can transform a plot by using the mouse and keyboard commands.

This section is brief and only the features, necessary for the
following chapters, are discussed. For more information visit the oﬃcial
page of gnuplot http://gnuplot.info. Try the rich demo gallery at
http://gnuplot.info/screenshots/, where you can ﬁnd the type of
graph that you want to create and obtain an easy to use recipe for it.
The book [14] is an excellent place to look for many of gnuplot’s
secrets^{43} .

You can start a gnuplot session with the gnuplot command:

> gnuplot

G N U P L O T

Version X.XX

....

The gnuplot FAQ is available from www.gnuplot.info/faq/

....

Terminal type set to ’wxt’

gnuplot>

G N U P L O T

Version X.XX

....

The gnuplot FAQ is available from www.gnuplot.info/faq/

....

Terminal type set to ’wxt’

gnuplot>

There is a welcome message and then a prompt gnuplot> is issued waiting for your command. Type a command an press [Enter]. Type quit in order to quit the program. In the following, when we show a prompt gnuplot>, it is assumed that the command after the prompt is executed from within gnuplot.

Plotting a function is extremely easy. Use the command plot and x as the independent variable
of the function^{44} .
The command

gnuplot> plot x

plots the function which is a straight line with slope 1. In order to plot many functions simultaneously, you can write all of them in one line:

gnuplot> plot [-5:5][-2:4] x, x**2, sin(x),besj0(x)

The above command plots the functions , , and .
Within the square brackets [:], we set the limits of the and axes,
respectively. The bracket [-5:5] sets and the bracket
[-2:4] sets . You may leave the job of setting such
limits to gnuplot, by omitting some, or all of them, from the respective
positions in the brackets. For example, typing [1:][:5] changes the lower
and upper limits of and and leaves the upper and lower limits
unchanged^{45} .

In order to plot data points , we can read their values from ﬁles. Assume that a ﬁle data has the following numbers recorded in it:

# x y1 y2

0.5 1.0 0.779

1.0 2.0 0.607

1.5 3.0 0.472

2.0 4.0 0.368

2.5 5.0 0.287

3.0 6.0 0.223

0.5 1.0 0.779

1.0 2.0 0.607

1.5 3.0 0.472

2.0 4.0 0.368

2.5 5.0 0.287

3.0 6.0 0.223

The ﬁrst line is taken by gnuplot as a comment line, since it begins with a #. In fact, gnuplot ignores everything after a #. In order to plot the second column as a function of the ﬁrst, type the command:

gnuplot> plot "data" using 1:2 with points

The name of the ﬁle is within double quotes. After the keyword using, we instruct gnuplot which columns to use as the and coordinates, respectively. The keywords with points instructs gnuplot to add each pair to the plot with points.

The command

gnuplot> plot "data" using 1:3 with lines

plots the third column as a function of the ﬁrst, and the keywords with lines instruct gnuplot to connect each pair with a straight line segment.

We can combine several plots together in one plot:

gnuplot> plot "data" using 1:3 with points, exp(-0.5*x)

gnuplot> replot "data" using 1:2

gnuplot> replot 2*x

gnuplot> replot "data" using 1:2

gnuplot> replot 2*x

The ﬁrst line plots the 1st and 3rd columns in the ﬁle data together with the function . The second line adds the plot of the 1st and 2nd columns in the ﬁle data and the third line adds the plot of the function .

There are many powerful ways to use the keyword using. Instead of column numbers, we can put mathematical expressions enclosed inside brackets, like using (...):(...). Gnuplot evaluates each expression within the brackets and plots the result. In these expressions, the values of each column in the ﬁle data are represented as in the awk language. $i are variables that expand to the number read from columns i=1,2,3,.... Here are some examples:

gnuplot> plot "data" using 1:($2*sin($1)*$3) with points

gnuplot> replot 2*x*sin(x)*exp(-x/2)

gnuplot> replot 2*x*sin(x)*exp(-x/2)

The ﬁrst line plots the 1st column of the ﬁle data together with the value , where , and are the numbers in the 2nd, 1st and 3rd columns respectively. The second line adds the plot of the function .

gnuplot> plot "data" using (log($1)):(log($2**2))

gnuplot> replot 2*x+log(4)

gnuplot> replot 2*x+log(4)

The ﬁrst line plots the logarithm of the 1st column together with the logarithm of the square of the 2nd column.

We can plot the data written to the standard output of any command. Assume that there is a program called area that prints the perimeter and area of a circle to the stdout in the form shown below:

> ./area

R= 3.280000 area= 33.79851

R= 6.280000 area= 123.8994

R= 5.280000 area= 87.58257

R= 4.280000 area= 57.54895

R= 3.280000 area= 33.79851

R= 6.280000 area= 123.8994

R= 5.280000 area= 87.58257

R= 4.280000 area= 57.54895

The interesting data is at the second and fourth columns. These can be plotted directly with the gnuplot command:

gnuplot> plot "< ./area" using 2:4

All we have to do is to type the full command after the < within the double quotes. We can create complicated ﬁlters using pipes as in the following example:

gnuplot> plot \

"< ./area|sort -g -k 2|awk ’{print log($2),log($4)}’" \

using 1:2

"< ./area|sort -g -k 2|awk ’{print log($2),log($4)}’" \

using 1:2

The ﬁlter produces data to the stdout, by combining the action of the commands area, sort and awk. The data printed by the last program is in two columns and we plot the results using 1:2.

In order to save plots in ﬁles, we have to change the terminal that gnuplot outputs the plots. Gnuplot can produce plots in several languages (e.g. PDF, postscript, SVG, LATEX, jpeg, png, gif, etc), which can be interpreted and rendered by external programs. By redirecting the output to a ﬁle, we can save the plot to the hard disk. For example:

gnuplot> plot "data" using 1:3

gnuplot> set terminal jpeg

gnuplot> set output "data.jpg"

gnuplot> replot

gnuplot> set output

gnuplot> set terminal wxt

gnuplot> set terminal jpeg

gnuplot> set output "data.jpg"

gnuplot> replot

gnuplot> set output

gnuplot> set terminal wxt

The ﬁrst line makes the plot as usual. The second one sets the output to be in the JPEG format and the third one sets the name of the ﬁle to which the plot will be saved. The fourth lines repeats all the previous plotting commands and the ﬁfth one closes the ﬁle data.jpg. The last line chooses the interactive terminal wxt to be the output of the next plot. High quality images are usually saved in the PDF, encapsulated postcript or SVG format. Use set terminal pdf,postscript eps or svg, respectively.

And now a few words for 3-dimensional (3d) plotting. The next example uses the command splot in order to make a 3d plot of the function . After you make the plot, you can use the mouse in order to rotate it and view it from a diﬀerent perspective:

gnuplot> set pm3d

gnuplot> set hidden3d

gnuplot> set size ratio 1

gnuplot> set isosamples 50

gnuplot> splot [-2:2][-2:2] exp(-x**2-y**2)

gnuplot> set hidden3d

gnuplot> set size ratio 1

gnuplot> set isosamples 50

gnuplot> splot [-2:2][-2:2] exp(-x**2-y**2)

If you have data in the form and you want to create a plot of , write the data in a ﬁle, like in the following example:

-1 -1 2.000

-1 0 1.000

-1 1 2.000

0 -1 1.000

0 0 0.000

0 1 1.000

1 -1 2.000

1 0 1.000

1 1 2.000

-1 0 1.000

-1 1 2.000

0 -1 1.000

0 0 0.000

0 1 1.000

1 -1 2.000

1 0 1.000

1 1 2.000

Note the empty line that follows the change of the value of the ﬁrst column. If the name of the ﬁle is data3, then you can plot the data with the commands:

gnuplot> set pm3d

gnuplot> set hidden3d

gnuplot> set size ratio 1

gnuplot> splot "data3" with lines

gnuplot> set hidden3d

gnuplot> set size ratio 1

gnuplot> splot "data3" with lines

We close this section with a few words on parametric plots. A parametric plot on the plane (2-dimensions) is a curve , where is a parameter. A parametric plot in space (3-dimensions) is a surface , where are parameters. The following commands plot the circle and the sphere :

gnuplot> set parametric

gnuplot> plot sin(t),cos(t)

gnuplot> splot cos(u)*cos(v),cos(u)*sin(v),sin(u)

gnuplot> plot sin(t),cos(t)

gnuplot> splot cos(u)*cos(v),cos(u)*sin(v),sin(u)

Complicated system administration tasks are not among the strengths of the Fortran programming language. But in a typical GNU/Linux environment, there exist many powerful tools that can be used very eﬀectively for this purpose. This way, one can use Fortran for the high performance and scientiﬁc computing part of the project and leave the administration and trivial data analysis tasks to other, external, programs.

One can avoid repeating the same sequence of commands by coding them in a ﬁle. An example can be found in the ﬁle script01.csh:

#!/bin/tcsh -f

gfortran area_01.f90 -o area

./area

gfortran area_02.f90 -o area

./area

gfortran area_03.f90 -o area

./area

gfortran area_04.f90 -o area

./area

gfortran area_01.f90 -o area

./area

gfortran area_02.f90 -o area

./area

gfortran area_03.f90 -o area

./area

gfortran area_04.f90 -o area

./area

This is a very simple shell script. The ﬁrst line instructs the operating
system that the lines that follow are to be interpreted by the program
/bin/tcsh^{46} .
This can be any program in the system, which in our case is the tcsh shell.
The following lines are valid commands for the shell, one in each line.
They compile the Fortran programs found in the ﬁles that we created in
section 1.4 with gfortran, and then they run the executable ./area. In
order to execute the commands in the ﬁle, we have to make sure that the
ﬁle has the appropriate execute permissions. If not, we have to give the
command:

> chmod u+x script01.csh

Then we simply type the path to the ﬁle script01.csh

> ./script01.csh

and the above commands are run the one after the other. Some of the versions of the programs that we wrote are asking for input from the stdin, which, normally, you have to type on the terminal. Instead of interacting directly with the program, we can write the input data to a ﬁle Input, and run the command

./area < Input

A more convenient solution is to use the, so called, “Here Document”.
A “Here Document” is a section of the script that is treated as if it
were a separate ﬁle. As such, it can be used as input to programs by
sending its “contents” to the stdin of the command that runs the
program^{47} .
The “Here Document” does not appear in the ﬁlesystem and we don’t need to
administer it as a regular ﬁle. An example of using a “Here Document” can be
found in the ﬁle script02.csh:

#!/bin/tcsh -f

gfortran area_04.f90 -o area

./area <<EOF

1.0

2.0

3.0

4.0

5.0

6.0

7.0

8.0

9.0

10.0

EOF

gfortran area_04.f90 -o area

./area <<EOF

1.0

2.0

3.0

4.0

5.0

6.0

7.0

8.0

9.0

10.0

EOF

The stdin of the command ./area is redirected to the contents between the lines

./area <<EOF

...

EOF

...

EOF

The string EOF marks the beginning and the end of the “Here Document”, and can be any string you like. The last EOF has to be placed exactly in the beginning of the line.

The power of shell scripting lies in its programming capabilities: Variables, arrays, loops and conditionals can be used in order to create a complicated program. Shell variables can be used as discussed in section 1.1.2: The value of a variable name is $name and it can be set with the command set name = value. An array is deﬁned, for example, by the command

set R = (1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0)

and its data can be accessed using the syntax $R[1] ... $R[10].

Lets take a look at the following script:

#!/bin/tcsh -f

set files = (area_01.f90 area_02.f90 area_03.f90 area_04.f90)

set R = (1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0)

echo "Hello $USER Today is " ‘date‘

foreach file ($files)

echo "# ----------- Working on file $file "

gfortran $file -o area

./area <<EOF

$R[1]

$R[2]

$R[3]

$R[4]

$R[5]

$R[6]

$R[7]

$R[8]

$R[9]

$R[10]

EOF

echo "# ----------- Done "

if( -f AREA.DAT ) cat AREA.DAT

end

set files = (area_01.f90 area_02.f90 area_03.f90 area_04.f90)

set R = (1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0)

echo "Hello $USER Today is " ‘date‘

foreach file ($files)

echo "# ----------- Working on file $file "

gfortran $file -o area

./area <<EOF

$R[1]

$R[2]

$R[3]

$R[4]

$R[5]

$R[6]

$R[7]

$R[8]

$R[9]

$R[10]

EOF

echo "# ----------- Done "

if( -f AREA.DAT ) cat AREA.DAT

end

The ﬁrst two lines of the script deﬁne the values of the arrays files (4 values) and R (10 values). The command echo echoes its argument to the stdin. $USER is the name of the user running the script. ‘date‘ is an example of command substitution: When a command is enclosed between backquotes and is part of a string, then the command is executed and its stdout is pasted back to the string. In the example shown above, ‘date‘ is replaced by the current date and time in the format produced by the date command.

The foreach loop

foreach file ($files)

...

end

...

end

is executed once for each of the 4 values of the array files. Each time the value of the variable file is set equal to one of the values area_01.f90, area_02.f90, area_03.f90, area_04.f90. These values can be used by the commands in the loop. Therefore, the command gfortran $file -o area compiles a diﬀerent ﬁle each time that it is executed by the loop.

The last line in the loop

if( -f AREA.DAT ) cat AREA.DAT

is a conditional. It executes the command cat AREA.DAT if the condition -f AREA.DAT is true. In this case, -f constructs a logical expression which is true when the ﬁle AREA.DAT exists.

We close this section by presenting a more complicated and advanced
script. It only serves as a demonstration of the shell scripting capabilities.
For more information, the reader is referred to the bibliography
[16, 17, 18, 19, 20]. Read carefully the commands, as well as the
comments which follow the # mark. Then, write the commands to a ﬁle
script04.csh^{48} ,
make it an executable ﬁle with the command chmod u+x script04.csh and give
the command

> ./script04.csh This is my first serious tcsh script

The script will run with the words “This is my ﬁrst serious tcsh script” as its arguments. Notice how these arguments are manipulated by the script. Then, the script asks for the values of the radii of ten or more circles interactively, so that it will compute their perimeter and area. Type them on the terminal and then observe the script’s output, so that you understand the function of each command. You will not regret the time investment!

#!/bin/tcsh -f

# Run this script as:

# ./script04.csh Hello this is a tcsh script

#----------------------------------------------------------------------

# ‘command‘ is command substitution: it is replaced by stdout of command

set now = ‘date‘ ; set mypc = ‘uname -a‘

# Print information: variables are expanded within double quotes

echo "I am user $user working on the computer $HOST" #HOST is predefined

echo "Today the date is : $now" #now is defined above

echo "My home directory is : $home" #home is predefined

echo "My current directory is: $cwd" #cwd changes with cd

echo "My computer runs : $mypc" #mypc is defined above

echo "My process id is : $$ " #$$ is predefined

# Manipulate the command line: ($#argv is number of elements in array argv)

echo "The command line has $#argv arguments"

echo "The name of the command I am running is: $0"

echo "Arguments 3rd to last of the command : $argv[3-]" #third to last

echo "The last argument is : $argv[$#argv]" #last element

echo "All arguments : $argv"

# Ask user for input: enter radii of circles

echo -n "Enter radii of circles: " # variable $< stores one line of input

set Rs = ($<) #Rs is now an array with all words entered by user

if($#Rs < 10 )then #make a test, need at least 10 of them

echo "Need more than 10 radii. Exiting...."

exit(1)

endif

echo "You entered $#Rs radii, the first is $Rs[1] and the last $Rs[$#Rs]"

echo "Rs= $Rs"

# Now, compute the perimeter of each circle:

foreach R ($Rs)

# -v rad=$R set the awk variable rad equal to $R. pi=atan2(0,-1)=3.14...

set l = ‘awk -v rad=$R ’BEGIN{print 2*atan2(0,-1)*rad}’‘

echo "Circle with R= $R has perimeter $l"

end

# alias defines a command to do what you want: use awk as a calculator

alias acalc ’awk "BEGIN{print \!* }"’ # \!* substitutes args of acalc

echo "Using acalc to compute 2+3=" ‘acalc 2+3‘

echo "Using acalc to compute cos(2*pi)=" ‘acalc cos(2*atan2(0,-1))‘

# Now do the same loop over radii as above in a different way

# while( expression ) is executed as long as "expression" is true

while($#Rs > 0) #executed as long as $Rs contains radii

set R = $Rs[1] #take first element of $Rs

shift Rs #now $Rs has one less element:old $Rs[1] has vanished

set a = ‘acalc atan2(0,-1)*${R}*${R}‘ # =pi*R*R calculated by acalc

# construct a filename to save the result from the value of R:

set file = area${R}.dat

echo "Circle with R= $R has area $a" > $file #save result in a file

end #end while

# Now look for our files: save their names in an array files:

set files = (‘ls -1 area*.dat‘)

if( $#files == 0) echo "Sorry, no area files found"

echo "--------------------------------------------"

echo "files: $files"

ls -l $files

echo "--------------------------------------------"

echo "And the results for the area are:"

foreach f ($files)

echo -n "file ${f}: "

cat $f

end

# now play a little bit with file names:

echo "--------------------------------------------"

set f = $files[1] # test permissions on first file

# -f, -r, -w, -x, -d test existence of file, rwxd permissions

# the ! negates the expression (true -> false, false -> true)

echo "testing permissions on files:"

if( -f $f ) echo "$file exists"

if( -r $f ) echo "$file is readable by me"

if( -w $f ) echo "$file is writable by be"

if(! -w /bin/ls) echo "/bin/ls is NOT writable by me"

if(! -x $f ) echo "$file is NOT an executable"

if( -x /bin/ls) echo "/bin/ls is executable by me"

if(! -d $f ) echo "$file is NOT a directory"

if( -d /bin ) echo "/bin is a directory"

echo "--------------------------------------------"

# transform the name of a file

set f = $cwd/$f # add the full path in $f

set filename = $f:r # removes extension .dat

set extension = $f:e # gets extension .dat

set fdir = $f:h # gets directory of $f

set base = ‘basename $f‘ # removes directory name

echo "file is: $f"

echo "filename is: $filename"

echo "extension is: $extension"

echo "directory is: $fdir"

echo "basename is: $base"

# now transform the name to one with different extension:

set newfile = ${filename}.jpg

echo "jpeg name is: $newfile"

echo "jpeg base is:" ‘basename $newfile‘

if($newfile:e == jpg)echo ‘basename $newfile‘ " is a picture"

echo "--------------------------------------------"

# Now save all data in a file using a "here document"

# A here document starts with <<EOF and ends with a line

# starting exactly with EOF (EOF can be any string as below)

# In a "here document" we can use variables and command

# substitution:

cat <<AREAS >> areas.dat

# This file contains the areas of circle of given radii

# Computation done by ${user} on ${HOST}. Today is ‘date‘

‘cat $files‘

AREAS

# now see what we got:

if( -f areas.dat) cat areas.dat

# You can use a "here document" as standard input to any command:

# use gnuplot to save a plot: gnuplot does the job and exits...

gnuplot <<GNU

set terminal jpeg

set output "areas.jpg"

plot "areas.dat" using 4:7 title "areas.dat",\

pi*x*x title "pi*R^2"

set output

GNU

# check our results: display the jpeg file using eog

if( -f areas.jpg) eog areas.jpg &

# Run this script as:

# ./script04.csh Hello this is a tcsh script

#----------------------------------------------------------------------

# ‘command‘ is command substitution: it is replaced by stdout of command

set now = ‘date‘ ; set mypc = ‘uname -a‘

# Print information: variables are expanded within double quotes

echo "I am user $user working on the computer $HOST" #HOST is predefined

echo "Today the date is : $now" #now is defined above

echo "My home directory is : $home" #home is predefined

echo "My current directory is: $cwd" #cwd changes with cd

echo "My computer runs : $mypc" #mypc is defined above

echo "My process id is : $$ " #$$ is predefined

# Manipulate the command line: ($#argv is number of elements in array argv)

echo "The command line has $#argv arguments"

echo "The name of the command I am running is: $0"

echo "Arguments 3rd to last of the command : $argv[3-]" #third to last

echo "The last argument is : $argv[$#argv]" #last element

echo "All arguments : $argv"

# Ask user for input: enter radii of circles

echo -n "Enter radii of circles: " # variable $< stores one line of input

set Rs = ($<) #Rs is now an array with all words entered by user

if($#Rs < 10 )then #make a test, need at least 10 of them

echo "Need more than 10 radii. Exiting...."

exit(1)

endif

echo "You entered $#Rs radii, the first is $Rs[1] and the last $Rs[$#Rs]"

echo "Rs= $Rs"

# Now, compute the perimeter of each circle:

foreach R ($Rs)

# -v rad=$R set the awk variable rad equal to $R. pi=atan2(0,-1)=3.14...

set l = ‘awk -v rad=$R ’BEGIN{print 2*atan2(0,-1)*rad}’‘

echo "Circle with R= $R has perimeter $l"

end

# alias defines a command to do what you want: use awk as a calculator

alias acalc ’awk "BEGIN{print \!* }"’ # \!* substitutes args of acalc

echo "Using acalc to compute 2+3=" ‘acalc 2+3‘

echo "Using acalc to compute cos(2*pi)=" ‘acalc cos(2*atan2(0,-1))‘

# Now do the same loop over radii as above in a different way

# while( expression ) is executed as long as "expression" is true

while($#Rs > 0) #executed as long as $Rs contains radii

set R = $Rs[1] #take first element of $Rs

shift Rs #now $Rs has one less element:old $Rs[1] has vanished

set a = ‘acalc atan2(0,-1)*${R}*${R}‘ # =pi*R*R calculated by acalc

# construct a filename to save the result from the value of R:

set file = area${R}.dat

echo "Circle with R= $R has area $a" > $file #save result in a file

end #end while

# Now look for our files: save their names in an array files:

set files = (‘ls -1 area*.dat‘)

if( $#files == 0) echo "Sorry, no area files found"

echo "--------------------------------------------"

echo "files: $files"

ls -l $files

echo "--------------------------------------------"

echo "And the results for the area are:"

foreach f ($files)

echo -n "file ${f}: "

cat $f

end

# now play a little bit with file names:

echo "--------------------------------------------"

set f = $files[1] # test permissions on first file

# -f, -r, -w, -x, -d test existence of file, rwxd permissions

# the ! negates the expression (true -> false, false -> true)

echo "testing permissions on files:"

if( -f $f ) echo "$file exists"

if( -r $f ) echo "$file is readable by me"

if( -w $f ) echo "$file is writable by be"

if(! -w /bin/ls) echo "/bin/ls is NOT writable by me"

if(! -x $f ) echo "$file is NOT an executable"

if( -x /bin/ls) echo "/bin/ls is executable by me"

if(! -d $f ) echo "$file is NOT a directory"

if( -d /bin ) echo "/bin is a directory"

echo "--------------------------------------------"

# transform the name of a file

set f = $cwd/$f # add the full path in $f

set filename = $f:r # removes extension .dat

set extension = $f:e # gets extension .dat

set fdir = $f:h # gets directory of $f

set base = ‘basename $f‘ # removes directory name

echo "file is: $f"

echo "filename is: $filename"

echo "extension is: $extension"

echo "directory is: $fdir"

echo "basename is: $base"

# now transform the name to one with different extension:

set newfile = ${filename}.jpg

echo "jpeg name is: $newfile"

echo "jpeg base is:" ‘basename $newfile‘

if($newfile:e == jpg)echo ‘basename $newfile‘ " is a picture"

echo "--------------------------------------------"

# Now save all data in a file using a "here document"

# A here document starts with <<EOF and ends with a line

# starting exactly with EOF (EOF can be any string as below)

# In a "here document" we can use variables and command

# substitution:

cat <<AREAS >> areas.dat

# This file contains the areas of circle of given radii

# Computation done by ${user} on ${HOST}. Today is ‘date‘

‘cat $files‘

AREAS

# now see what we got:

if( -f areas.dat) cat areas.dat

# You can use a "here document" as standard input to any command:

# use gnuplot to save a plot: gnuplot does the job and exits...

gnuplot <<GNU

set terminal jpeg

set output "areas.jpg"

plot "areas.dat" using 4:7 title "areas.dat",\

pi*x*x title "pi*R^2"

set output

GNU

# check our results: display the jpeg file using eog

if( -f areas.jpg) eog areas.jpg &

awk | search for and process patterns in a ﬁle, |

cat | display, or join, ﬁles |

cd | change working directory |

chmod | change the access mode of a ﬁle |

cp | copy ﬁles |

date | display current time and date |

df | display the amount of available disk space |

diff | display the diﬀerences between two ﬁles |

du | display information on disk usage |

echo | echo a text string to output |

find | ﬁnd ﬁles |

grep | search for a pattern in ﬁles |

gzip | compress ﬁles in the gzip (.gz) format (gunzip to uncompress) |

head | display the ﬁrst few lines of a ﬁle |

kill | send a signal (like KILL) to a process |

locate | search for ﬁles stored on the system (faster than ﬁnd) |

less | display a ﬁle one screen at a time |

ln | create a link to a ﬁle |

lpr | print ﬁles |

ls | list information about ﬁles |

man | search information about command in man pages |

mkdir | create a directory |

mv | move and/or rename a ﬁle |

ps | report information on the processes run on the system |

pwd | print the working directory |

rm | remove (delete) ﬁles |

rmdir | remove (delete) a directory |

sort | sort and/or merge ﬁles |

tail | display the last few lines of a ﬁle |

tar | store or retrieve ﬁles from an archive ﬁle |

top | dynamic real-time view of processes |

wc | counts lines, words and characters in a ﬁle |

whatis | list man page entries for a command |

where | show where a command is located in the path (alternatively: whereis) |

which | locate an executable program using ”path” |

zip | create compressed archive in the zip format (.zip) |

unzip | get/list contents of zip archive |

Kinematics

When a particle moves on the plane, its position can be given in Cartesian coordinates . These, as a function of time, describe the particle’s trajectory. The position vector is , where and are the unit vectors on the and axes respectively. The velocity vector is where

The acceleration is given byIn this section we study the kinematics of a particle trajectory, therefore we assume that the functions are known. By taking their derivatives, we can compute the velocity and the acceleration of the particle in motion. We will write simple programs that compute the values of these functions in a time interval , where is the initial and is the ﬁnal time. The continuous functions are approximated by a discrete sequence of their values at the times such that .

We will start the design of our program by forming a generic template to be used in all of the problems of interest. Then we can study each problem of particle motion by programming only the equations of motion without worrying about the less important tasks, like input/output, user interface etc. Figure 2.2 shows a ﬂowchart of the basic steps in the algorithm. The ﬁrst part of the program declares variables and deﬁnes the values of the ﬁxed parameters (like , , etc). The program starts by interacting with the user (“user interface”) and asks for the values of the variables , , , , . The program prints these values to the stdout so that the user can check them for correctness and store them in her data.

The main calculation is performed in a loop executed while . The values of the positions and the velocities are calculated and printed in a ﬁle together with the time . At this point we ﬁx the format of the program output, something that is very important to do it in a consistent and convenient way for easing data analysis. We choose to print the values t, x, y, vx, vy in ﬁve columns in each line of the output ﬁle.

The speciﬁc problem that we are going to solve is the computation of the trajectory of the circular motion of a particle on a circle with center and radius with constant angular velocity . The position on the circle can be deﬁned by the angle , as can be seen in ﬁgure 2.3. We deﬁne the initial position of the particle at time to be .

The equations giving the position of the particle at time are

Taking the derivative w.r.t. we obtain the velocity and the acceleration We note that the above equations imply that (, , tangent to the trajectory) and ( and anti-parallel, ).The data structure is quite simple. The constant angular velocity is stored in the REAL variable omega. The center of the circle , the radius of the circle and the angle are stored in the REAL variables x0, y0, R, theta. The times at which we calculate the particle’s position and velocity are deﬁned by the parameters and are stored in the REAL variables t0, tf, dt. The current position is calculated and stored in the REAL variables x, y and the velocity in the REAL variables vx, vy. The declarations of the variables are put in the beginning of the program:

real :: x0,y0,R,x,y,vx,vy,t,t0,tf,dt

real :: theta,omega

real, parameter :: PI=3.1415927

real :: theta,omega

real, parameter :: PI=3.1415927

were we deﬁned the value^{1}
of by using the parameter speciﬁcation.

The user interface of the program is the interaction of the program with the
user and, in our case, it is the part of the program where the user enters the
parameters omega, x0, y0, R, t0, tf, dt. The program issues a prompt
with the names the variables expected to be read. This is done using
simple print statements. The variables are read from the stdin by simple
read statements and the values entered by the user are printed to the
stdout^{2} :

print *,’# Enter omega:’

read *,omega

print *,’# Enter center of circle (x0,y0) and radius R:’

read *,x0,y0,R

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# omega= ’,omega

print *,’# x0= ’,x0,’ y0= ’,y0,’ R= ’,R

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

read *,omega

print *,’# Enter center of circle (x0,y0) and radius R:’

read *,x0,y0,R

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# omega= ’,omega

print *,’# x0= ’,x0,’ y0= ’,y0,’ R= ’,R

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

Next, the program initializes the state of the computation. This includes checking
the validity of the parameters entered by the user, so that the computation will be
possible. For example, the program computes the expression 2.0*PI/omega, where
it is assumed that omega has a non zero value. We will also demand that
and . An if statement will make those checks and if the
parameters have illegal values, the stop statement will stop the program
execution^{3} .
The program opens the ﬁle Circle.dat for writing the calculated values of the
position and the velocity of the particle.

if(R .le. 0.0) stop ’Illegal value of R’

if(omega .le. 0.0) stop ’Illegal value of omega’

print *,’# T= ’,2.0*PI/omega

open(unit=11,file=’Circle.dat’)

if(omega .le. 0.0) stop ’Illegal value of omega’

print *,’# T= ’,2.0*PI/omega

open(unit=11,file=’Circle.dat’)

If or the corresponding stop statements are executed which end the program execution. The optional error messages are included after the stop statements which are printed to the stdout. The value of the period is also calculated and printed for reference.

The open statement uses unit 11 for writing to the ﬁle Circle.dat.
The choice of the unit number is free for the programmer to choose.
We recommend using the units 10 to 99 for input/output to
ﬁles^{4} .

The main calculation is performed within the loop

t = t0

do while(t .le. tf)

.........

t = t + dt

enddo

do while(t .le. tf)

.........

t = t + dt

enddo

The ﬁrst statement sets the initial value of the time. The statements between the do while(condition) and enddo are executed as long as condition has a .TRUE. value. The statement t=t+dt increments the time and this is necessary in order not to enter into an inﬁnite loop. he commands put in place of the dots ......... calculate the position and the velocity and print them to the ﬁle Circle.dat:

theta = omega * (t-t0)

x = x0+R*cos(theta)

y = y0+R*sin(theta)

vx = -omega*R*sin(theta)

vy = omega*R*cos(theta)

write(11,*)t,x,y,vx,vy

x = x0+R*cos(theta)

y = y0+R*sin(theta)

vx = -omega*R*sin(theta)

vy = omega*R*cos(theta)

write(11,*)t,x,y,vx,vy

Notice the use of the intrinsic functions sin and cos that calculate the sine and cosine of an angle expressed in radians. We use the intermediate variable theta in order to store the phase . The command write(11,*) writes the variables t,x,y,vx,vy to the unit 11, which has been associated to the ﬁle Circle.dat with the open statement shown above.

The program is stored in the ﬁle Circle.f90 and can be found in the accompanied software. The extension .f90 is used by the compiler in order to denote source code written in free format Fortran language. Compilation and running can be done using the commands:

> gfortran Circle.f90 -o cl

> ./cl

> ./cl

The switch -o cl forces the compiler gfortran to write the binary commands executed by the
program to the ﬁle^{5}
cl. The command ./cl loads the program instructions to the computer memory
for execution. When the programs starts execution, it ﬁrst asks for the
parameter data and then performs the calculation. A typical session looks
like:

> gfortran Circle.f90 -o cl

> ./cl

# Enter omega:

1.0

# Enter center of circle (x0,y0) and radius R:

1.0 1.0 0.5

# Enter t0,tf,dt:

0.0 20.0 0.01

# omega= 1.

# x0= 1. y0= 1. R= 0.5

# t0= 0. tf= 20. dt= 0.00999999978

# T= 6.28318548

> ./cl

# Enter omega:

1.0

# Enter center of circle (x0,y0) and radius R:

1.0 1.0 0.5

# Enter t0,tf,dt:

0.0 20.0 0.01

# omega= 1.

# x0= 1. y0= 1. R= 0.5

# t0= 0. tf= 20. dt= 0.00999999978

# T= 6.28318548

The lines shown above that start with a # character are printed by the program and lines without # are the values of the parameters entered interactively by the user. The user types in the parameters and then presses the Enter key in order for the program to read them. Here we have , , , , and .

You can execute the above program many times for diﬀerent values of the parameter by writing the parameter values in a ﬁle using an editor. For example, in the ﬁle Circle.in type the following data:

1.0 omega

1.0 1.0 0.5 (x0, y0) , R

0.0 20.0 0.01 t0 tf dt

1.0 1.0 0.5 (x0, y0) , R

0.0 20.0 0.01 t0 tf dt

Each line has the parameters read by the program with a read statement (a record). The rest of the line is ignored by the program and the user can write anything she likes as a comment on how to use the parameters. The program can read the above values of the parameters with the command:

> ./cl < Circle.in > Circle.out

The command ./cl runs the commands found in the executable ﬁle ./cl. The < Circle.in redirects the contents of the ﬁle Circle.in to the standard input (stdin) of the command ./cl. This way the program reads in the values of the parameters from the contents of the ﬁle Circle.in. The > Circle.out redirects the standard output (stdout) of the command ./cl to the ﬁle Circle.out. Its contents can be inspected after the execution of the program with the command cat:

> cat Circle.out

# Enter omega:

# Enter center of circle (x0,y0) and radius R:

# Enter t0,tf,dt:

# omega= 1.

# x0= 1. y0= 1. R= 0.5

# t0= 0. tf= 20. dt= 0.00999999978

# T= 6.28318548

# Enter omega:

# Enter center of circle (x0,y0) and radius R:

# Enter t0,tf,dt:

# omega= 1.

# x0= 1. y0= 1. R= 0.5

# t0= 0. tf= 20. dt= 0.00999999978

# T= 6.28318548

We list the full program in Circle.f90 below:

!============================================================

!File Circle.f90

!Constant angular velocity circular motion

!Set (x0,y0) center of circle, its radius R and omega.

!At t=t0, the particle is at theta=0

!------------------------------------------------------------

program Circle

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,t0,tf,dt

real :: theta,omega

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega:’

read *,omega

print *,’# Enter center of circle (x0,y0) and radius R:’

read *,x0,y0,R

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# omega= ’,omega

print *,’# x0= ’,x0,’ y0= ’,y0,’ R= ’,R

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(R .le. 0.0) stop ’Illegal value of R’

if(omega .le. 0.0) stop ’Illegal value of omega’

print *,’# T= ’,2.0*PI/omega

open(unit=11,file=’Circle.dat’)

!------------------------------------------------------------

!Compute:

t = t0

do while(t .le. tf)

theta = omega * (t-t0)

x = x0+R*cos(theta)

y = y0+R*sin(theta)

vx = -omega*R*sin(theta)

vy = omega*R*cos(theta)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Circle

!File Circle.f90

!Constant angular velocity circular motion

!Set (x0,y0) center of circle, its radius R and omega.

!At t=t0, the particle is at theta=0

!------------------------------------------------------------

program Circle

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,t0,tf,dt

real :: theta,omega

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega:’

read *,omega

print *,’# Enter center of circle (x0,y0) and radius R:’

read *,x0,y0,R

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# omega= ’,omega

print *,’# x0= ’,x0,’ y0= ’,y0,’ R= ’,R

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(R .le. 0.0) stop ’Illegal value of R’

if(omega .le. 0.0) stop ’Illegal value of omega’

print *,’# T= ’,2.0*PI/omega

open(unit=11,file=’Circle.dat’)

!------------------------------------------------------------

!Compute:

t = t0

do while(t .le. tf)

theta = omega * (t-t0)

x = x0+R*cos(theta)

y = y0+R*sin(theta)

vx = -omega*R*sin(theta)

vy = omega*R*cos(theta)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Circle

We use gnuplot for plotting the data produced by our programs. The ﬁle Circle.dat has the time t and the components x, y, vx, vy in ﬁve columns. Therefore we can plot the functions and by using the gnuplot commands:

gnuplot> plot "Circle.dat" using 1:2 with lines title "x(t)"

gnuplot> replot "Circle.dat" using 1:3 with lines title "y(t)"

gnuplot> replot "Circle.dat" using 1:3 with lines title "y(t)"

The second line puts the second plot together with the ﬁrst one. The results can be seen in ﬁgure 2.4.

Let’s see now how we can make the plot of the function . We can do that using
the raw data from the ﬁle Circle.dat within gnuplot, without having to write a new
program. Note that . The function atan2 is available in
gnuplot^{6}
as well as in Fortran. Use the online help system in gnuplot in order to see its
usage:

gnuplot> help atan2

The ‘atan2(y,x)‘ function returns the arc tangent (inverse

tangent) of the ratio of the real parts of its arguments.

‘atan2‘ returns its argument in radians or degrees, as

selected by ‘set angles‘, in the correct quadrant.

The ‘atan2(y,x)‘ function returns the arc tangent (inverse

tangent) of the ratio of the real parts of its arguments.

‘atan2‘ returns its argument in radians or degrees, as

selected by ‘set angles‘, in the correct quadrant.

Therefore, the right way to call the function is atan2(y-y0,x-x0). In our case x0=y0=1 and x, y are in the 2nd and 3rd columns of the ﬁle Circle.dat. We can construct an expression after the using command as in page 153, where $2 is the value of the second and $3 the value of the third column:

gnuplot> x0 = 1 ; y0 = 1

gnuplot> plot "Circle.dat" using 1:(atan2($3-y0,$2-x0)) \

with lines title "theta(t)",pi,-pi

gnuplot> plot "Circle.dat" using 1:(atan2($3-y0,$2-x0)) \

with lines title "theta(t)",pi,-pi

The second command is broken in two lines by using the character ∖ so that it ﬁts conveniently
in the text^{7} .
Note how we deﬁned the values of the variables x0, y0 and how we used them in the
expression atan2($3-x0,$2-y0). We also plot the lines which graph the constant
functions and which mark the limit values of . The gnuplot
variable^{8}
pi is predeﬁned and can be used in formed expressions. The result can be seen in
the left plot of ﬁgure 2.4.

The velocity components as function of time as well as the trajectory can be plotted with the commands:

gnuplot> plot "Circle.dat" using 1:4 title "v_x(t)" \

with lines

gnuplot> replot "Circle.dat" using 1:5 title "v_y(t)" \

with lines

gnuplot> plot "Circle.dat" using 2:3 title "x-y"

with lines

with lines

gnuplot> replot "Circle.dat" using 1:5 title "v_y(t)" \

with lines

gnuplot> plot "Circle.dat" using 2:3 title "x-y"

with lines

We close this section by showing how to do a simple animation of
the particle trajectory using gnuplot. There is a ﬁle animate2D.gnu in
the accompanied software which you can copy in the directory where
you have the data ﬁle Circle.dat. We are not going to explain how it
works^{9}
but how to use it in order to make your own animations. The ﬁnal result
is shown in ﬁgure 2.5. All that you need to do is to deﬁne the data
ﬁle^{10} ,
the initial time t0, the ﬁnal time tf and the time step dt. These times can be
diﬀerent from the ones we used to create the data in Circle.dat. A full
animation session can be launched using the commands:

gnuplot> file = "Circle.dat"

gnuplot> set xrange [0:1.6]; set yrange [0:1.6]

gnuplot> t0 = 0; tf = 20 ; dt = 0.1

gnuplot> load "animate2D.gnu"

gnuplot> set xrange [0:1.6]; set yrange [0:1.6]

gnuplot> t0 = 0; tf = 20 ; dt = 0.1

gnuplot> load "animate2D.gnu"

The ﬁrst line deﬁnes the data ﬁle that animate2D.gnu reads data from. The second line sets the range of the plots and the third line deﬁnes the time parameters used in the animation. The ﬁnal line launches the animation. If you want to rerun the animation, you can repeat the last two commands as many times as you want using the same or diﬀerent parameters. E.g. if you wish to run the animation at “half the speed” you should simply redeﬁne dt=0.05 and set the initial time to t0=0:

gnuplot> t0 = 0; dt = 0.05

gnuplot> load "animate2D.gnu"

gnuplot> load "animate2D.gnu"

We are now going to apply the steps described in the previous section to other examples of motion on the plane. The ﬁrst problem that we are going to discuss is that of the small oscillations of a simple pendulum. Figure 2.6 shows the single oscillating degree of freedom , which is the small angle that the pendulum forms with the vertical direction.

The motion is periodic with angular frequency and period . The angular velocity is computed from which gives

We have chosen the initial conditions and . In order to write the equations of motion in the Cartesian coordinate system shown in ﬁgure 2.6 we use the relations These are similar to the equations (2.3) and (2.4) that we used in the case of the circular motion of the previous section. Therefore the structure of the program is quite similar. Its ﬁnal form, which can be found in the ﬁle SimplePendulum.f90, is:
!==============================================================

!File SimplePendulum.f90

!Set pendulum original position at theta0 with no initial speed

!--------------------------------------------------------------

program SimplePendulum

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: l,x,y,vx,vy,t,t0,tf,dt

real :: theta,theta0,dtheta_dt,omega

real, parameter ::PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter l: ’

read *,l

print *,’# Enter theta0:’

read *,theta0

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# l= ’,l ,’ theta0= ’,theta0

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

omega = sqrt(g/l)

print *,’# omega= ’,omega,’ T= ’,2.0*PI/omega

open(unit=11,file=’SimplePendulum.dat’)

!------------------------------------------------------------

!Compute:

t = t0

do while(t .le. tf)

theta = theta0*cos(omega*(t-t0))

dtheta_dt = -omega*theta0*sin(omega*(t-t0))

x = l*sin(theta)

y = -l*cos(theta)

vx = l*dtheta_dt*cos(theta)

vy = l*dtheta_dt*sin(theta)

write(11,100)t,x,y,vx,vy,theta,dtheta_dt

t = t + dt

enddo

close(11)

100 FORMAT(7G15.7)

end program SimplePendulum

!File SimplePendulum.f90

!Set pendulum original position at theta0 with no initial speed

!--------------------------------------------------------------

program SimplePendulum

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: l,x,y,vx,vy,t,t0,tf,dt

real :: theta,theta0,dtheta_dt,omega

real, parameter ::PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter l: ’

read *,l

print *,’# Enter theta0:’

read *,theta0

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# l= ’,l ,’ theta0= ’,theta0

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

omega = sqrt(g/l)

print *,’# omega= ’,omega,’ T= ’,2.0*PI/omega

open(unit=11,file=’SimplePendulum.dat’)

!------------------------------------------------------------

!Compute:

t = t0

do while(t .le. tf)

theta = theta0*cos(omega*(t-t0))

dtheta_dt = -omega*theta0*sin(omega*(t-t0))

x = l*sin(theta)

y = -l*cos(theta)

vx = l*dtheta_dt*cos(theta)

vy = l*dtheta_dt*sin(theta)

write(11,100)t,x,y,vx,vy,theta,dtheta_dt

t = t + dt

enddo

close(11)

100 FORMAT(7G15.7)

end program SimplePendulum

We note that the acceleration of gravity is hard coded in the program and that the user can only set the length of the pendulum. The data ﬁle SimplePendulum.dat produced by the program, contains two extra columns with the current values of and the angular velocity . The statement write(11,100) writes to the unit 11 according to the format set by the FORMAT statement, found in the line labeled by the label 100. This is done so that we can be sure that the data is printed in one line for each value of (see the discussion on page 119).

A simple session for the study of the above problem is shown
below^{11} :

> gfortran SimplePendulum.f90 -o sp

> ./sp

# Enter l:

1.0

# Enter theta0:

0.314

# Enter t0,tf,dt:

0 20 0.01

# l= 1. theta0= 0.31400001

# t0= 0. tf= 20. dt= 0.00999999978

# omega= 3.132092 T= 2.0060668

> gnuplot

gnuplot> plot "SimplePendulum.dat" u 1:2 w l t "x(t)"

gnuplot> plot "SimplePendulum.dat" u 1:3 w l t "y(t)"

gnuplot> plot "SimplePendulum.dat" u 1:4 w l t "v_x(t)"

gnuplot> replot "SimplePendulum.dat" u 1:5 w l t "v_y(t)"

gnuplot> plot "SimplePendulum.dat" u 1:6 w l t "theta(t)"

gnuplot> replot "SimplePendulum.dat" u 1:7 w l t "theta’(t)"

gnuplot> plot [-0.6:0.6][-1.1:0.1] "SimplePendulum.dat" \

u 2:3 w l t "x-y"

gnuplot> file = "SimplePendulum.dat"

gnuplot> t0=0;tf=20.0;dt=0.1

gnuplot> set xrange [-0.6:0.6];set yrange [-1.1:0.1]

gnuplot> load "animate2D.gnu"

> ./sp

# Enter l:

1.0

# Enter theta0:

0.314

# Enter t0,tf,dt:

0 20 0.01

# l= 1. theta0= 0.31400001

# t0= 0. tf= 20. dt= 0.00999999978

# omega= 3.132092 T= 2.0060668

> gnuplot

gnuplot> plot "SimplePendulum.dat" u 1:2 w l t "x(t)"

gnuplot> plot "SimplePendulum.dat" u 1:3 w l t "y(t)"

gnuplot> plot "SimplePendulum.dat" u 1:4 w l t "v_x(t)"

gnuplot> replot "SimplePendulum.dat" u 1:5 w l t "v_y(t)"

gnuplot> plot "SimplePendulum.dat" u 1:6 w l t "theta(t)"

gnuplot> replot "SimplePendulum.dat" u 1:7 w l t "theta’(t)"

gnuplot> plot [-0.6:0.6][-1.1:0.1] "SimplePendulum.dat" \

u 2:3 w l t "x-y"

gnuplot> file = "SimplePendulum.dat"

gnuplot> t0=0;tf=20.0;dt=0.1

gnuplot> set xrange [-0.6:0.6];set yrange [-1.1:0.1]

gnuplot> load "animate2D.gnu"

The next example is the study of the trajectory of a particle shot near the earth’s
surface^{12}
when we consider the eﬀect of air resistance to be negligible. Then, the equations
describing the trajectory of the particle and its velocity are given by the
parametric equations

The structure of the program is similar to the previous ones. The user enters the magnitude of the particle’s initial velocity and the shooting angle in degrees. The initial time is taken to be . The program calculates and and prints them to the stdout. The data is written to the ﬁle Projectile.dat. The full program is listed below and it can be found in the ﬁle Projectile.f90 in the accompanied software:

!============================================================

!File Projectile.f90

!Shooting a projectile near the earth surface.

!No air resistance.

!Starts at (0,0), set (v0,theta).

!------------------------------------------------------------

program Projectile

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,tf,dt

real :: theta,v0x,v0y,v0

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter v0,theta (in degrees):’

read *,v0,theta

print *,’# Enter tf,dt:’

read *, tf,dt

print *,’# v0= ’,v0,’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if( v0 .le. 0.0) stop ’Illegal value of v0<=0’

if( theta .le. 0.0 .or. theta .ge. 90.0) &

stop ’Illegal value of theta’

theta = (PI/180.0)*theta !convert to radians

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# v0x = ’,v0x,’ v0y= ’,v0y

open(unit=11,file=’Projectile.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = v0x * t

y = v0y * t - 0.5*g*t*t

vx = v0x

vy = v0y - g*t

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Projectile

!File Projectile.f90

!Shooting a projectile near the earth surface.

!No air resistance.

!Starts at (0,0), set (v0,theta).

!------------------------------------------------------------

program Projectile

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,tf,dt

real :: theta,v0x,v0y,v0

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter v0,theta (in degrees):’

read *,v0,theta

print *,’# Enter tf,dt:’

read *, tf,dt

print *,’# v0= ’,v0,’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if( v0 .le. 0.0) stop ’Illegal value of v0<=0’

if( theta .le. 0.0 .or. theta .ge. 90.0) &

stop ’Illegal value of theta’

theta = (PI/180.0)*theta !convert to radians

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# v0x = ’,v0x,’ v0y= ’,v0y

open(unit=11,file=’Projectile.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = v0x * t

y = v0y * t - 0.5*g*t*t

vx = v0x

vy = v0y - g*t

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Projectile

A typical session for the study of this problem is shown below:

> gfortran Projectile.f90 -o pj

> ./pj

# Enter v0,theta (in degrees):

10 45

# Enter tf,dt:

1.4416 0.001

# v0= 10.0000000 theta= 45.000000 o (degrees)

# t0= 0.0000000 tf= 1.4416000 dt= 1.00000005E-03

# v0x = 7.0710678 v0y= 7.0710678

> gnuplot

gnuplot> plot "Projectile.dat" using 1:2 w l t "x(t)"

gnuplot> replot "Projectile.dat" using 1:3 w l t "y(t)"

gnuplot> plot "Projectile.dat" using 1:4 w l t "v_x(t)"

gnuplot> replot "Projectile.dat" using 1:5 w l t "v_y(t)"

gnuplot> plot "Projectile.dat" using 2:3 w l t "x-y"

gnuplot> file = "Projectile.dat"

gnuplot> set xrange [0:10.3];set yrange [0:10.3]

gnuplot> t0=0;tf=1.4416;dt=0.05

gnuplot> load "animate2D.gnu"

> ./pj

# Enter v0,theta (in degrees):

10 45

# Enter tf,dt:

1.4416 0.001

# v0= 10.0000000 theta= 45.000000 o (degrees)

# t0= 0.0000000 tf= 1.4416000 dt= 1.00000005E-03

# v0x = 7.0710678 v0y= 7.0710678

> gnuplot

gnuplot> plot "Projectile.dat" using 1:2 w l t "x(t)"

gnuplot> replot "Projectile.dat" using 1:3 w l t "y(t)"

gnuplot> plot "Projectile.dat" using 1:4 w l t "v_x(t)"

gnuplot> replot "Projectile.dat" using 1:5 w l t "v_y(t)"

gnuplot> plot "Projectile.dat" using 2:3 w l t "x-y"

gnuplot> file = "Projectile.dat"

gnuplot> set xrange [0:10.3];set yrange [0:10.3]

gnuplot> t0=0;tf=1.4416;dt=0.05

gnuplot> load "animate2D.gnu"

Next, we will study the eﬀect of air resistance of the form . The solutions to the equations of motion

with initial conditions , and are

Programming the above equations is as easy as before, the only diﬀerence being that the user needs to provide the value of the constant . The full program can be found in the ﬁle ProjectileAirResistance.f90 and it is listed below:

!============================================================

!File ProjectileAirResistance.f90

!Shooting a projectile near the earth surface

!with air resistance

!Starts at (0,0), set k, (v0,theta).

!------------------------------------------------------------

program ProjectileAirResistance

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,tf,dt,k

real :: theta,v0x,v0y,v0

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter k, v0,theta (in degrees):’

read *,k, v0,theta

print *,’# Enter tf,dt:’

read *, tf,dt

print *,’# k = ’,k

print *,’# v0= ’,v0,’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if( v0 .le. 0.0) stop ’Illegal value of v0<=0’

if( k .le. 0.0) stop ’Illegal value of k <=0’

if( theta .le. 0.0 .or. theta .ge. 90.0) &

stop ’Illegal value of theta’

theta = (PI/180.0)*theta !convert to radians

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# v0x = ’,v0x,’ v0y= ’,v0y

open(unit=11,file=’ProjectileAirResistance.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = (v0x/k)*(1.0-exp(-k*t))

y = (1.0/k)*(v0y+(g/k))*(1.0-exp(-k*t))-(g/k)*t

vx = v0x*exp(-k*t)

vy = (v0y+(g/k))*exp(-k*t)-(g/k)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program ProjectileAirResistance

!File ProjectileAirResistance.f90

!Shooting a projectile near the earth surface

!with air resistance

!Starts at (0,0), set k, (v0,theta).

!------------------------------------------------------------

program ProjectileAirResistance

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x0,y0,R,x,y,vx,vy,t,tf,dt,k

real :: theta,v0x,v0y,v0

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter k, v0,theta (in degrees):’

read *,k, v0,theta

print *,’# Enter tf,dt:’

read *, tf,dt

print *,’# k = ’,k

print *,’# v0= ’,v0,’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if( v0 .le. 0.0) stop ’Illegal value of v0<=0’

if( k .le. 0.0) stop ’Illegal value of k <=0’

if( theta .le. 0.0 .or. theta .ge. 90.0) &

stop ’Illegal value of theta’

theta = (PI/180.0)*theta !convert to radians

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# v0x = ’,v0x,’ v0y= ’,v0y

open(unit=11,file=’ProjectileAirResistance.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = (v0x/k)*(1.0-exp(-k*t))

y = (1.0/k)*(v0y+(g/k))*(1.0-exp(-k*t))-(g/k)*t

vx = v0x*exp(-k*t)

vy = (v0y+(g/k))*exp(-k*t)-(g/k)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program ProjectileAirResistance

We also list the commands of a typical session of the study of the problem:

> gfortran ProjectileAirResistance.f90 -o pja

> ./pja

# Enter k, v0,theta (in degrees):

5.0 10.0 45

# Enter tf,dt:

0.91 0.001

# k = 5.

# v0= 10. theta= 45.o (degrees)

# t0= 0. tf= 0.910000026 dt= 0.00100000005

# v0x = 7.07106781 v0y= 7.07106781

> gnuplot

gnuplot> v0x = 10*cos(pi/4) ; v0y = 10*sin(pi/4)

gnuplot> g = 9.81 ; k = 5

gnuplot> plot [:][:v0x/k+0.1] "ProjectileAirResistance.dat" \

using 1:2 with lines title "x(t)",v0x/k

gnuplot> replot "ProjectileAirResistance.dat" \

using 1:3 with lines title "y(t)",\

-(g/k)*x+(g/k**2)+v0y/k

gnuplot> plot [:][-g/k-0.6:] "ProjectileAirResistance.dat" \

using 1:4 with lines title "v_x(t)",0

gnuplot> replot "ProjectileAirResistance.dat" \

using 1:5 with lines title "v_y(t)",-g/k

gnuplot> plot "ProjectileAirResistance.dat" \

using 2:3 with lines title "With air resistance k=5.0"

gnuplot> replot "Projectile.dat" \

using 2:3 with lines title "No air resistance k=0.0"

gnuplot> file = "ProjectileAirResistance.dat"

gnuplot> set xrange [0:1.4];set yrange [0:1.4]

gnuplot> t0=0;tf=0.91;dt=0.01

gnuplot> load "animate2D.gnu"

> ./pja

# Enter k, v0,theta (in degrees):

5.0 10.0 45

# Enter tf,dt:

0.91 0.001

# k = 5.

# v0= 10. theta= 45.o (degrees)

# t0= 0. tf= 0.910000026 dt= 0.00100000005

# v0x = 7.07106781 v0y= 7.07106781

> gnuplot

gnuplot> v0x = 10*cos(pi/4) ; v0y = 10*sin(pi/4)

gnuplot> g = 9.81 ; k = 5

gnuplot> plot [:][:v0x/k+0.1] "ProjectileAirResistance.dat" \

using 1:2 with lines title "x(t)",v0x/k

gnuplot> replot "ProjectileAirResistance.dat" \

using 1:3 with lines title "y(t)",\

-(g/k)*x+(g/k**2)+v0y/k

gnuplot> plot [:][-g/k-0.6:] "ProjectileAirResistance.dat" \

using 1:4 with lines title "v_x(t)",0

gnuplot> replot "ProjectileAirResistance.dat" \

using 1:5 with lines title "v_y(t)",-g/k

gnuplot> plot "ProjectileAirResistance.dat" \

using 2:3 with lines title "With air resistance k=5.0"

gnuplot> replot "Projectile.dat" \

using 2:3 with lines title "No air resistance k=0.0"

gnuplot> file = "ProjectileAirResistance.dat"

gnuplot> set xrange [0:1.4];set yrange [0:1.4]

gnuplot> t0=0;tf=0.91;dt=0.01

gnuplot> load "animate2D.gnu"

Long commands have been continued to the next line as before. We deﬁned the gnuplot variables v0x, v0y, g and k to have the values that we used when running the program. We can use them in order to construct the asymptotes of the plotted functions of time. The results are shown in ﬁgures 2.9 and 2.10.

The last example of this section will be that of the anisotropic harmonic oscillator. The force on the particle is

| (2.11) |

where the “spring constants” and are diﬀerent in the directions of the axes and . The solutions of the dynamical equations of motion for , , and are

If the angular frequencies and satisfy certain relations, the trajectories of the particle are closed and self intersect at a given number of points. The proof of these relations, as well as their numerical conﬁrmation, is left as an exercise for the reader. The program listed below is in the ﬁle Lissajoux.f90:
!============================================================

!File Lissajous.f90

!Lissajous curves (special case)

!x(t)= cos(o1 t), y(t)= sin(o2 t)

!------------------------------------------------------------

program Lissajous

implicit none

!------------------------------------------------------------

!Declaration of variables

real x0,y0,R,x,y,vx,vy,t,t0,tf,dt

real o1,o2,T1,T2

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega1 and omega2:’

read *,o1,o2

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# o1= ’,o1, ’ o2= ’,o2

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(o1.le.0.0 .or. o2.le.0.0) stop ’omega1 or omega2<=0’

T1 = 2.0*PI/o1

T2 = 2.0*PI/o2

print *,’# T1= ’,T1,’ T2= ’,T2

open(unit=11,file=’Lissajous.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = cos(o1*t)

y = sin(o2*t)

vx = -o1*sin(o1*t)

vy = o2*cos(o2*t)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Lissajous

!File Lissajous.f90

!Lissajous curves (special case)

!x(t)= cos(o1 t), y(t)= sin(o2 t)

!------------------------------------------------------------

program Lissajous

implicit none

!------------------------------------------------------------

!Declaration of variables

real x0,y0,R,x,y,vx,vy,t,t0,tf,dt

real o1,o2,T1,T2

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega1 and omega2:’

read *,o1,o2

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# o1= ’,o1, ’ o2= ’,o2

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(o1.le.0.0 .or. o2.le.0.0) stop ’omega1 or omega2<=0’

T1 = 2.0*PI/o1

T2 = 2.0*PI/o2

print *,’# T1= ’,T1,’ T2= ’,T2

open(unit=11,file=’Lissajous.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = cos(o1*t)

y = sin(o2*t)

vx = -o1*sin(o1*t)

vy = o2*cos(o2*t)

write(11,*)t,x,y,vx,vy

t = t + dt

enddo

close(11)

end program Lissajous

We have set in the program above. The user must enter the two angular frequencies and and the corresponding times. A typical session for the study of the problem is shown below:

> gfortran Lissajous.f90 -o lsj

> ./lsj

# Enter omega1 and omega2:

3 5

# Enter tf,dt:

10.0 0.01

# o1= 3. o2= 5.

# t0= 0. tf= 10. dt= 0.00999999978

# T1= 2.09439516 T2= 1.2566371

>gnuplot

gnuplot> plot "Lissajous.dat" using 1:2 w l t "x(t)"

gnuplot> replot "Lissajous.dat" using 1:3 w l t "y(t)"

gnuplot> plot "Lissajous.dat" using 1:4 w l t "v_x(t)"

gnuplot> replot "Lissajous.dat" using 1:5 w l t "v_y(t)"

gnuplot> plot "Lissajous.dat" using 2:3 w l t "x-y for 3:5"

gnuplot> file = "Lissajous.dat"

gnuplot> set xrange [-1.1:1.1];set yrange [-1.1:1.1]

gnuplot> t0=0;tf=10;dt=0.1

gnuplot> load "animate2D.gnu"

> ./lsj

# Enter omega1 and omega2:

3 5

# Enter tf,dt:

10.0 0.01

# o1= 3. o2= 5.

# t0= 0. tf= 10. dt= 0.00999999978

# T1= 2.09439516 T2= 1.2566371

>gnuplot

gnuplot> plot "Lissajous.dat" using 1:2 w l t "x(t)"

gnuplot> replot "Lissajous.dat" using 1:3 w l t "y(t)"

gnuplot> plot "Lissajous.dat" using 1:4 w l t "v_x(t)"

gnuplot> replot "Lissajous.dat" using 1:5 w l t "v_y(t)"

gnuplot> plot "Lissajous.dat" using 2:3 w l t "x-y for 3:5"

gnuplot> file = "Lissajous.dat"

gnuplot> set xrange [-1.1:1.1];set yrange [-1.1:1.1]

gnuplot> t0=0;tf=10;dt=0.1

gnuplot> load "animate2D.gnu"

The results for and are shown in ﬁgure 2.11.

By slightly generalizing the methods described in the previous section, we will study the motion of a particle in three dimensional space. All we have to do is to add an extra equation for the coordinate and the component of the velocity . The structure of the programs will be exactly the same as before.

The ﬁrst example is the conical pendulum, which can be seen in ﬁgure 2.12. The particle moves on the plane with constant angular velocity . The equations of motion are derived from the relations

| (2.13) |

where . Their solution^{14}
is

| (2.17) |

and when , .

In the program that we will write, the user must enter the parameters , , the ﬁnal time and the time step . We take . The convention that we follow for the output of the results is that they should be written in a ﬁle where the ﬁrst 7 columns are the values of , , , , , and . Each line in this ﬁle is long and, in order to prevent Fortran from breaking it into two separate lines, we have to give an explicit format speciﬁcation. See the discussion on page 119. The full program is listed below:

!============================================================

!File ConicalPendulum.f90

!Set pendulum angular velocity omega and display motion in 3D

!------------------------------------------------------------

program ConicalPendulum

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: l,r,x,y,z,vx,vy,vz,t,tf,dt

real :: theta,cos_theta,sin_theta,omega

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter l,omega: ’

read *,l,omega

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# l= ’,l ,’ omega= ’,omega

print *,’# T= ’,2.0*PI/omega,’ omega_min= ’,sqrt(g/l)

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

cos_theta = g/(omega*omega*l)

if( cos_theta .ge. 1) stop ’cos(theta)>= 1’

sin_theta = sqrt(1.0-cos_theta*cos_theta)

z = -g/(omega*omega) !they remain constant throught

vz= 0.0 !the motion

r = g/(omega*omega)*sin_theta/cos_theta

open(unit=11,file=’ConicalPendulum.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = r*cos(omega*t)

y = r*sin(omega*t)

vx = -r*sin(omega*t)*omega

vy = r*cos(omega*t)*omega

write(11,100)t,x,y,z,vx,vy,vz

t = t + dt

enddo

close(11)

100 FORMAT(20G15.7)

end program ConicalPendulum

!File ConicalPendulum.f90

!Set pendulum angular velocity omega and display motion in 3D

!------------------------------------------------------------

program ConicalPendulum

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: l,r,x,y,z,vx,vy,vz,t,tf,dt

real :: theta,cos_theta,sin_theta,omega

real, parameter :: PI=3.1415927,g=9.81

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter l,omega: ’

read *,l,omega

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# l= ’,l ,’ omega= ’,omega

print *,’# T= ’,2.0*PI/omega,’ omega_min= ’,sqrt(g/l)

print *,’# t0= ’,0.0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

cos_theta = g/(omega*omega*l)

if( cos_theta .ge. 1) stop ’cos(theta)>= 1’

sin_theta = sqrt(1.0-cos_theta*cos_theta)

z = -g/(omega*omega) !they remain constant throught

vz= 0.0 !the motion

r = g/(omega*omega)*sin_theta/cos_theta

open(unit=11,file=’ConicalPendulum.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

do while(t .le. tf)

x = r*cos(omega*t)

y = r*sin(omega*t)

vx = -r*sin(omega*t)*omega

vy = r*cos(omega*t)*omega

write(11,100)t,x,y,z,vx,vy,vz

t = t + dt

enddo

close(11)

100 FORMAT(20G15.7)

end program ConicalPendulum

In order to compile and run the program we can use the commands shown below:

> gfortran ConicalPendulum.f90 -o cpd

> ./cpd

# Enter l,omega:

1.0 6.28

# Enter tf,dt:

10.0 0.01

# l= 1. omega= 6.28000021

# T= 1.00050724 omega_min= 3.132092

# t0= 0. tf= 10. dt= 0.00999999978

> ./cpd

# Enter l,omega:

1.0 6.28

# Enter tf,dt:

10.0 0.01

# l= 1. omega= 6.28000021

# T= 1.00050724 omega_min= 3.132092

# t0= 0. tf= 10. dt= 0.00999999978

The results are recorded in the ﬁle ConicalPendulum.dat. In order to plot the functions , , , , , we give the following gnuplot commands:

> gnuplot

gnuplot> plot "ConicalPendulum.dat" u 1:2 w l t "x(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:3 w l t "y(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:4 w l t "z(t)"

gnuplot> plot "ConicalPendulum.dat" u 1:5 w l t "v_x(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:6 w l t "v_y(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:7 w l t "v_z(t)"

gnuplot> plot "ConicalPendulum.dat" u 1:2 w l t "x(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:3 w l t "y(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:4 w l t "z(t)"

gnuplot> plot "ConicalPendulum.dat" u 1:5 w l t "v_x(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:6 w l t "v_y(t)"

gnuplot> replot "ConicalPendulum.dat" u 1:7 w l t "v_z(t)"

The results are shown in ﬁgure 2.13.

In order to make a three dimensional plot of the trajectory, we should use the gnuplot command splot:

gnuplot> splot "ConicalPendulum.dat" u 2:3:4 w l t "r(t)"

The result is shown in ﬁgure 2.14. We can click on the trajectory and rotate it and view it from a diﬀerent angle. We can change the plot limits with the command:

gnuplot> splot [-1.1:1.1][-1.1:1.1][-0.3:0.0] \

"ConicalPendulum.dat" using 2:3:4 w l t "r(t)"

"ConicalPendulum.dat" using 2:3:4 w l t "r(t)"

We can animate the trajectory of the particle by using the ﬁle animate3D.gnu from the accompanying software. The commands are similar to the ones we had to give in the two dimensional case for the planar trajectories when we used the ﬁle animate2D.gnu:

gnuplot> file = "ConicalPendulum.dat"

gnuplot> set xrange [-1.1:1.1];set yrange [-1.1:1.1]

gnuplot> set zrange [-0.3:0]

gnuplot> t0=0;tf=10;dt=0.1

gnuplot> load "animate3D.gnu"

gnuplot> set xrange [-1.1:1.1];set yrange [-1.1:1.1]

gnuplot> set zrange [-0.3:0]

gnuplot> t0=0;tf=10;dt=0.1

gnuplot> load "animate3D.gnu"

The result can be seen in ﬁgure 2.15.

The program animate3D.gnu can be used on the data ﬁle of any program that prints t x y z as the ﬁrst words on each of its lines. All we have to do is to change the value of the file variable in gnuplot.

Next, we will study the trajectory of a charged particle in a homogeneous magnetic ﬁeld . At time , the particle is at and its velocity is , see ﬁgure 2.16.

The magnetic force on the particle is and the equations of motion are

By integrating the above equations with the given initial conditions we obtain Integrating once more, we obtain the position of the particle as a function of time where we have chosen . This choice places the center of the circle, which is the projection of the trajectory on the plane, to be at the origin of the coordinate system. The trajectory is a helix with radius and pitch .We are now ready to write a program that calculates the trajectory given by (2.20) . The user enters the parameters and , shown in ﬁgure 2.16, as well as the angular frequency (Larmor frequency). The components of the initial velocity are and . The initial position is calculated from the equation . The program can be found in the ﬁle ChargeInB.f90:

!===========================================================

!File ChargeInB.f90

!A charged particle of mass m and charge q enters a magnetic

!field B in +z direction. It enters with velocity

!v0x=0,v0y=v0 cos(theta),v0z=v0 sin(theta), 0<=theta<pi/2

!at the position x0=-v0y/omega, omega=q B/m

!

!Enter v0 and theta and see trajectory from

!t0=0 to tf at step dt

!------------------------------------------------------------

program ChargeInB

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x,y,z,vx,vy,vz,t,tf,dt

real :: x0,y0,z0,v0x,v0y,v0z,v0

real :: theta,omega

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega: ’

read *,omega

print *,’# Enter v0, theta (degrees):’

read *,v0,theta

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# omega= ’,omega ,’ T= ’,2.0*PI/omega

print *,’# v0= ’,v0, ’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0, ’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(theta.lt.0.0 .or. theta.ge.90.0)stop ’Illegal 0<theta<90’

theta = (PI/180.0)*theta !convert to radians

v0y = v0*cos(theta)

v0z = v0*sin(theta)

print *,’# v0x= ’,0.0,’ v0y= ’,v0y,’ v0z= ’,v0z

x0 = - v0y/omega

print *,’# x0= ’,x0, ’ y0= ’,0.0,’ z0= ’,0.0

print *,’# xy plane: Circle with center (0,0) and R= ’,ABS(x0)

print *,’# step of helix: s=v0z*T= ’,v0z*2.0*PI/omega

open(unit=11,file=’ChargeInB.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

vz = v0z

do while(t .le. tf)

x = x0*cos(omega*t)

y = -x0*sin(omega*t)

z = v0z*t

vx = v0y*sin(omega*t)

vy = v0y*cos(omega*t)

write(11,100)t,x,y,z,vx,vy,vz

t = t + dt

enddo

close(11)

100 FORMAT(20G15.7)

end program ChargeInB

!File ChargeInB.f90

!A charged particle of mass m and charge q enters a magnetic

!field B in +z direction. It enters with velocity

!v0x=0,v0y=v0 cos(theta),v0z=v0 sin(theta), 0<=theta<pi/2

!at the position x0=-v0y/omega, omega=q B/m

!

!Enter v0 and theta and see trajectory from

!t0=0 to tf at step dt

!------------------------------------------------------------

program ChargeInB

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: x,y,z,vx,vy,vz,t,tf,dt

real :: x0,y0,z0,v0x,v0y,v0z,v0

real :: theta,omega

real, parameter :: PI=3.1415927

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter omega: ’

read *,omega

print *,’# Enter v0, theta (degrees):’

read *,v0,theta

print *,’# Enter tf,dt:’

read *,tf,dt

print *,’# omega= ’,omega ,’ T= ’,2.0*PI/omega

print *,’# v0= ’,v0, ’ theta= ’,theta,’o (degrees)’

print *,’# t0= ’,0.0, ’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

if(theta.lt.0.0 .or. theta.ge.90.0)stop ’Illegal 0<theta<90’

theta = (PI/180.0)*theta !convert to radians

v0y = v0*cos(theta)

v0z = v0*sin(theta)

print *,’# v0x= ’,0.0,’ v0y= ’,v0y,’ v0z= ’,v0z

x0 = - v0y/omega

print *,’# x0= ’,x0, ’ y0= ’,0.0,’ z0= ’,0.0

print *,’# xy plane: Circle with center (0,0) and R= ’,ABS(x0)

print *,’# step of helix: s=v0z*T= ’,v0z*2.0*PI/omega

open(unit=11,file=’ChargeInB.dat’)

!------------------------------------------------------------

!Compute:

t = 0.0

vz = v0z

do while(t .le. tf)

x = x0*cos(omega*t)

y = -x0*sin(omega*t)

z = v0z*t

vx = v0y*sin(omega*t)

vy = v0y*cos(omega*t)

write(11,100)t,x,y,z,vx,vy,vz

t = t + dt

enddo

close(11)

100 FORMAT(20G15.7)

end program ChargeInB

A typical session in which we calculate the trajectories shown in ﬁgures 2.17 and 2.18 is shown below:

> gfortran ChargeInB.f90 -o chg

> ./chg

# Enter omega:

6.28

# Enter v0, theta (degrees):

1.0 20

# Enter tf,dt:

10 0.01

# omega= 6.28000021 T= 1.00050724

# v0= 1. theta= 20.o (degrees)

# t0= 0. tf= 10. dt= 0.00999999978

# v0x= 0. v0y= 0.939692616 v0z= 0.342020124

# x0= -0.149632573 y0= 0. z0= 0.

# xy plane: Circle with center (0,0) and R= 0.149632573

# step of helix: s=v0z*T= 0.342193604

> gnuplot

gnuplot> plot "ChargeInB.dat" u 1:2 w l title "x(t)"

gnuplot> replot "ChargeInB.dat" u 1:3 w l title "y(t)"

gnuplot> replot "ChargeInB.dat" u 1:4 w l title "z(t)"

gnuplot> plot "ChargeInB.dat" u 1:5 w l title "v_x(t)"

gnuplot> replot "ChargeInB.dat" u 1:6 w l title "v_y(t)"

gnuplot> replot "ChargeInB.dat" u 1:7 w l title "v_z(t)"

gnuplot> splot "ChargeInB.dat" u 2:3:4 w l title "r(t)"

gnuplot> file = "ChargeInB.dat"

gnuplot> set xrange [-0.65:0.65];set yrange [-0.65:0.65]

gnuplot> set zrange [0:1.3]

gnuplot> t0=0;tf=3.5;dt=0.1

gnuplot> load "animate3D.gnu"

> ./chg

# Enter omega:

6.28

# Enter v0, theta (degrees):

1.0 20

# Enter tf,dt:

10 0.01

# omega= 6.28000021 T= 1.00050724

# v0= 1. theta= 20.o (degrees)

# t0= 0. tf= 10. dt= 0.00999999978

# v0x= 0. v0y= 0.939692616 v0z= 0.342020124

# x0= -0.149632573 y0= 0. z0= 0.

# xy plane: Circle with center (0,0) and R= 0.149632573

# step of helix: s=v0z*T= 0.342193604

> gnuplot

gnuplot> plot "ChargeInB.dat" u 1:2 w l title "x(t)"

gnuplot> replot "ChargeInB.dat" u 1:3 w l title "y(t)"

gnuplot> replot "ChargeInB.dat" u 1:4 w l title "z(t)"

gnuplot> plot "ChargeInB.dat" u 1:5 w l title "v_x(t)"

gnuplot> replot "ChargeInB.dat" u 1:6 w l title "v_y(t)"

gnuplot> replot "ChargeInB.dat" u 1:7 w l title "v_z(t)"

gnuplot> splot "ChargeInB.dat" u 2:3:4 w l title "r(t)"

gnuplot> file = "ChargeInB.dat"

gnuplot> set xrange [-0.65:0.65];set yrange [-0.65:0.65]

gnuplot> set zrange [0:1.3]

gnuplot> t0=0;tf=3.5;dt=0.1

gnuplot> load "animate3D.gnu"

In this section we will study the motion of a particle that is free, except when
bouncing elastically on a wall or on certain obstacles. This motion is calculated by
approximate algorithms that introduce systematic errors. These types of
errors^{15}
are also encountered in the study of more complicated dynamics, but the
simplicity of the problem will allow us to control them in a systematic and easy to
understand way.

The simplest example of such a motion is that of a particle in a “one dimensional box”. The particle moves freely on the axis for , as can be seen in ﬁgure 2.19. When it reaches the boundaries and it bounces and its velocity instantly reversed. Its potential energy is

| (2.21) |

which has the shape of an inﬁnitely deep well. The force within the box and at the position of the walls.

Initially we have to know the position of the particle as well as its velocity (the sign of depends on the direction of the particle’s motion) at time . As long as the particle moves within the box, its motion is free and

For a small enough change in time , so that there is no bouncing on the wall in the time interval , we have that Therefore we could use the above relations in our program and when the particle bounces oﬀ a wall we could simple reverse its velocity . The devil is hiding in the word “when”. Since the time interval is ﬁnite in our program, there is no way to know the instant of the collision with accuracy better than . However, our algorithm will change the direction of the velocity at time , when the particle will have already crossed the wall. This will introduce a systematic error, which is expected to decrease with decreasing . One way to implement the above idea is by constructing the loop
do while(t .le. tf)

write(11,*)t,x,v

x = x + v*dt

t = t + dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

write(11,*)t,x,v

x = x + v*dt

t = t + dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

where the last line gives the testing condition for the wall collision and the subsequent change of the velocity.

The full program that realizes the proposed algorithm is listed below and can be found in the ﬁle box1D_1.f90. The user can set the size of the box L, the initial conditions x0 and v0 at time t0, the ﬁnal time tf and the time step dt:

!============================================================

!File box1D_1.f90

!Motion of a free particle in a box 0<x<L

!Use integration with time step dt: x = x + v*dt

!------------------------------------------------------------

program box1D

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: L,x0,v0,t0,tf,dt,t,x,v

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L:’

read *,L

print *,’# L = ’,L

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,v0:’

read *,x0,v0

print *,’# x0= ’,x0,’ v0= ’,v0

if(x0 .lt. 0.0 .or. x0 .gt. L) stop ’illegal value of x0.’

if(v0 .eq. 0.0 ) stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

t = t0

x = x0

v = v0

open(unit=11,file=’box1D_1.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,*)t,x,v

x = x + v*dt

t = t + dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

close(11)

end program box1D

!File box1D_1.f90

!Motion of a free particle in a box 0<x<L

!Use integration with time step dt: x = x + v*dt

!------------------------------------------------------------

program box1D

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: L,x0,v0,t0,tf,dt,t,x,v

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L:’

read *,L

print *,’# L = ’,L

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,v0:’

read *,x0,v0

print *,’# x0= ’,x0,’ v0= ’,v0

if(x0 .lt. 0.0 .or. x0 .gt. L) stop ’illegal value of x0.’

if(v0 .eq. 0.0 ) stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

t = t0

x = x0

v = v0

open(unit=11,file=’box1D_1.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,*)t,x,v

x = x + v*dt

t = t + dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

close(11)

end program box1D

The computed data is recorded in the ﬁle box1D_1.dat in three columns. Compiling, running and plotting the trajectory using gnuplot can be done as follows:

> gfortran box1D_1.f90 -o box1

> ./box1

# Enter L:

10

# L = 10.

# Enter x0,v0:

0 1.0

# x0= 0. v0= 1.

# Enter t0,tf,dt:

0 100 0.01

# t0= 0. tf= 100. dt= 0.00999999978

> gnuplot

gnuplot> plot "box1D_1.dat" using 1:2 w l title "x(t)",\

0 notitle,10 notitle

gnuplot> plot [:][-1.2:1.2] "box1D_1.dat" \

using 1:3 w l title "v(t)"

> ./box1

# Enter L:

10

# L = 10.

# Enter x0,v0:

0 1.0

# x0= 0. v0= 1.

# Enter t0,tf,dt:

0 100 0.01

# t0= 0. tf= 100. dt= 0.00999999978

> gnuplot

gnuplot> plot "box1D_1.dat" using 1:2 w l title "x(t)",\

0 notitle,10 notitle

gnuplot> plot [:][-1.2:1.2] "box1D_1.dat" \

using 1:3 w l title "v(t)"

The trajectory is shown in ﬁgure 2.20. The eﬀects of the systematic errors can be easily seen by noting that the expected collisions occur every units of time. Therefore, on the plot to the right of ﬁgure 2.20, the reversal of the particle’s motion should have occurred at , .

The reader should have already realized that the above mentioned error can be made to vanish by taking arbitrarily small . Therefore, we naively expect that as long as we have the necessary computer power to take as small as possible and the corresponding time intervals as many as possible, we can achieve any precision that we want. Well, that is true only up to a point. The problem is that the next position is determined by the addition operation x+v*dt and the next moment in time by t+dt. Floating point numbers of the REAL type have a maximum accuracy of approximately 7 signiﬁcant decimal digits. Therefore, if the operands x and v*dt are real numbers diﬀering by more than 7 orders of magnitude (v*dt x), the eﬀect of the addition x+v*dt=x, which is null! The reason is that the ﬂoating point unit of the processor has to convert both numbers x and v*dt into a representation having the same exponent and in doing so, the corresponding signiﬁcant digits of the smaller number v*dt are lost. The result is less catastrophic when v*dt x with , but some degree of accuracy is also lost at each addition operation. And since we have accumulation of such errors over many intervals tt+dt, the error can become signiﬁcant and destroy our calculation for large enough times. A similar error accumulates in the determination of the next instant of time t+dt, but we will discuss below how to make this contribution to the total error negligible. The above mentioned errors can become less detrimental by using ﬂoating point numbers of greater accuracy than the REAL type. For example REAL(8) numbers have approximately 16 signiﬁcant decimal digits. But again, the precision is ﬁnite and the same type of errors are there only to be revealed by a more demanding and complicated calculation.

The remedy to such a problem can only be a change in the algorithm. This is not always possible, but in the case at hand this is easy to do. For example, consider the equation that gives the position of a particle in free motion

| (2.24) |

Let’s use the above relation for the parts of the motion between two collisions. Then, all we have to do is to reverse the direction of the motion and reset the initial position and time to be the position and time of the collision. This can be done by using the loop:

t = t0

do while(t .le. tf)

x = x0 + v0*(t-t0)

write(11,*)t,x,v0

if( x .lt. 0.0 .or. x .gt. L)then

x0 = x

t0 = t

v0 = -v0

endif

t = t + dt

do while(t .le. tf)

x = x0 + v0*(t-t0)

write(11,*)t,x,v0

if( x .lt. 0.0 .or. x .gt. L)then

x0 = x

t0 = t

v0 = -v0

endif

t = t + dt

In the above algorithm, the error in the time of the collision is not
vanishing but we don’t have the “instability” problem of the dt
limit^{16} .
Therefore we can isolate and study the eﬀect of each type of error. The full
program that implements the above algorithm is given below and can be found in
the ﬁle box1D_2.f90:

!============================================================

!File box1D_2.f90

!Motion of a free particle in a box 0<x<L

!Use constant velocity equation: x = x0 + v0*(t-t0)

!Reverse velocity and redefine x0,t0 on boundaries

!------------------------------------------------------------

program box1D

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: L,x0,v0,t0,tf,dt,t,x,v

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L:’

read *,L

print *,’# L = ’,L

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,v0:’

read *,x0,v0

print *,’# x0= ’,x0,’ v0= ’,v0

if(x0 .lt. 0.0 .or. x0 .gt. L) stop ’illegal value of x0.’

if(v0 .eq. 0.0 ) stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

t = t0

open(unit=11,file=’box1D_2.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

x = x0 + v0*(t-t0)

write(11,*)t,x,v0

if( x .lt. 0.0 .or. x .gt. L)then

x0 = x

t0 = t

v0 = -v0

endif

t = t + dt

enddo

close(11)

end program box1D

!File box1D_2.f90

!Motion of a free particle in a box 0<x<L

!Use constant velocity equation: x = x0 + v0*(t-t0)

!Reverse velocity and redefine x0,t0 on boundaries

!------------------------------------------------------------

program box1D

implicit none

!------------------------------------------------------------

!Declaration of variables

real :: L,x0,v0,t0,tf,dt,t,x,v

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L:’

read *,L

print *,’# L = ’,L

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,v0:’

read *,x0,v0

print *,’# x0= ’,x0,’ v0= ’,v0

if(x0 .lt. 0.0 .or. x0 .gt. L) stop ’illegal value of x0.’

if(v0 .eq. 0.0 ) stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

t = t0

open(unit=11,file=’box1D_2.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

x = x0 + v0*(t-t0)

write(11,*)t,x,v0

if( x .lt. 0.0 .or. x .gt. L)then

x0 = x

t0 = t

v0 = -v0

endif

t = t + dt

enddo

close(11)

end program box1D

Compiling and running the above program is done as before and the results
are stored in the ﬁle box1D_2.dat. The algorithm can be improved in
order to compute the exact solution. We leave that as an exercise for the
reader^{17} .

In this section we will study the eﬀect of the systematic errors that we encountered in the previous section in more detail. We considered two types of errors: First, the systematic error of determining the instant of the collision of the particle with the wall. This error is reduced by taking a smaller time step . Then, the systematic error that accumulates with each addition of two numbers with increasing diﬀerence in their orders of magnitude. This error is increased with decreasing . The competition of the two eﬀects makes the optimal choice of the result of a careful analysis. Such a situation is found in many interesting problems, therefore it is quite instructive to study it in more detail.

When the exact solution of the problem is not known, the systematic errors are controlled by studying the behavior of the solution as a function of . If the solutions are converging in a region of values of , one gains conﬁdence that the true solution has been determined up to the accuracy of the convergence.

In the previous sections, we studied two diﬀerent algorithms, programmed in
the ﬁles box1D_1.f90 and box1D_2.f90. We will refer to them as “method
1” and “method 2” respectively. We will study the convergence of the
results as by ﬁxing all the parameters except and then study
the dependence of the results on . We will take , ,
, , , so that the particle will collide with the
wall every 10 units of time. We will measure the position of the particle
^{18}
as a function of and study its convergence to a
limit^{19}
as .

The analysis requires a lot of repetitive work: Compiling, setting the parameter values, running the program and calculating the value of for many values of . We write the values of the parameters read by the program in a ﬁle box1D_anal.in:

10 L

0 1.0 x0 v0

0 95 0.05 t0 tf dt

0 1.0 x0 v0

0 95 0.05 t0 tf dt

Then we compile the program

> gfortran box1D_1.f90 -o box

and run it with the command:

> cat box1D_anal.in | ./box

By using the pipe |, we send the contents of box1D_anal.in to the stdin of the command ./box by using the command cat. The result can be found in the last line of the ﬁle box1D_1.dat:

> tail -n 1 box1D_1.dat

94.9511948 5.45000267 -1.

94.9511948 5.45000267 -1.

The third number in the above line is the value of the velocity. In a ﬁle
box1D_anal.dat we write and the ﬁrst two numbers coming out from
the command tail. Then we decrease the value in the ﬁle
box1D_anal.in and run again. We repeat for 12 more times until reaches the
value^{20} .
We do the same^{21}
using method 2 and we place the results for in two new columns in the
ﬁle box1D_anal.dat. The result is

# ------------------------------------------

# dt t1_95 x1(95) x2(95)

# ------------------------------------------

0.050000 94.95119 5.450003 5.550126

0.025000 94.97849 5.275011 5.174837

0.012500 94.99519 5.124993 5.099736

0.006250 94.99850 4.987460 5.063134

0.003125 94.99734 5.021894 5.035365

0.001563 94.99923 5.034538 5.017764

0.000781 94.99939 4.919035 5.011735

0.000391 94.99979 4.695203 5.005493

0.000195 95.00000 5.434725 5.001935

0.000098 94.99991 5.528124 5.000745

0.000049 94.99998 3.358000 5.000330

0.000024 94.99998 2.724212 5.000232

0.000012 94.99999 9.240705 5.000158

# dt t1_95 x1(95) x2(95)

# ------------------------------------------

0.050000 94.95119 5.450003 5.550126

0.025000 94.97849 5.275011 5.174837

0.012500 94.99519 5.124993 5.099736

0.006250 94.99850 4.987460 5.063134

0.003125 94.99734 5.021894 5.035365

0.001563 94.99923 5.034538 5.017764

0.000781 94.99939 4.919035 5.011735

0.000391 94.99979 4.695203 5.005493

0.000195 95.00000 5.434725 5.001935

0.000098 94.99991 5.528124 5.000745

0.000049 94.99998 3.358000 5.000330

0.000024 94.99998 2.724212 5.000232

0.000012 94.99999 9.240705 5.000158

Convergence is studied in ﬁgure 2.21. The 1st method maximizes its accuracy for , whereas for the error becomes % and the method becomes useless. The 2nd method has much better behavior that the 1st one.

We observe that as decreases, the ﬁnal value of approaches the expected . Why don’t we obtain , especially when is an integer? How many steps does it really take to reach , when the expected number of those is ? Each time you take a measurement, issue the command

> wc -l box1D_1.dat

which measures the number of lines in the ﬁle box1D_1.dat and compare this number with the expected one. The result is interesting:

# ----------------------

# dt N N0

# ----------------------

0.050000 1900 1900

0.025000 3800 3800

0.012500 7601 7600

0.006250 15203 15200

0.003125 30394 30400

0.001563 60760 60780

0.000781 121751 121638

0.000391 243753 242966

0.000195 485144 487179

0.000098 962662 969387

0.000049 1972589 1938775

0.000024 4067548 3958333

0.000012 7540956 7916666

# dt N N0

# ----------------------

0.050000 1900 1900

0.025000 3800 3800

0.012500 7601 7600

0.006250 15203 15200

0.003125 30394 30400

0.001563 60760 60780

0.000781 121751 121638

0.000391 243753 242966

0.000195 485144 487179

0.000098 962662 969387

0.000049 1972589 1938775

0.000024 4067548 3958333

0.000012 7540956 7916666

where the second column has the number of steps computed by the program and the third one has the expected number of steps. We observe that the accuracy decreases with decreasing and in the end the diﬀerence is about 5%! Notice that the last line should have given , an error comparable to the period of the particle’s motion.

We conclude that one important source of accumulation of systematic errors is the calculation of time. This type of errors become more signiﬁcant with decreasing . We can improve the accuracy of the calculation signiﬁcantly if we use the multiplication t=t0+i*dt instead of the addition t=t+dt, where i is a step counter:

!t = t + dt ! Not accurate, avoid

t = t0 + i*dt ! Better accuracy, prefer

t = t0 + i*dt ! Better accuracy, prefer

The main loop in the program box1D_1.f90 becomes:

t = t0

x = x0

v = v0

i = 0

do while(t .le. tf)

write(11,*)t,x,v

i = i + 1

x = x + v*dt

t = t0 + i*dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

x = x0

v = v0

i = 0

do while(t .le. tf)

write(11,*)t,x,v

i = i + 1

x = x + v*dt

t = t0 + i*dt

if(x .lt. 0.0 .or. x .gt. L) v = -v

enddo

The full program can be found in the ﬁle box1D_4.f90 of the accompanying software. We call this “method 3”. We perform the same change in the ﬁle box1D_2.f90, which we store in the ﬁle box1D_5.f90. We call this “method 4”. We repeat the same analysis using methods 3 and 4 and we ﬁnd that the problem of calculating time accurately practically vanishes. The result of the analysis can be found on the right plot of ﬁgure 2.21. Methods 2 and 4 have no signiﬁcant diﬀerence in their results, whereas methods 1 and 3 do have a dramatic diﬀerence, with method 3 decreasing the error more than tenfold. The problem of the increase of systematic errors with decreasing does not vanish completely due to the operation x=x+v*dt. This type of error is harder to deal with and one has to invent more elaborate algorithms in order to reduce it signiﬁcantly. This will be discussed further in chapter 4.

A particle is conﬁned to move on the plane in the area and . When it reaches the boundaries of this two dimensional box, it bounces elastically oﬀ its walls. The particle is found in an inﬁnite depth orthogonal potential well. The particle starts moving at time from and our program will calculate its trajectory until time with time step . Such a trajectory can be seen in ﬁgure 2.23.

If the particle’s position and velocity are known at time , then at time they will be given by the relations

The collision of the particle oﬀ the walls is modeled by reﬂection of the normal component of the velocity when the respective coordinate of the particle crosses the wall. This is a source of the systematic errors that we discussed in the previous section. The central loop of the program is:
i = i + 1

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

if(x .lt. 0.0 .or. x .gt. Lx) vx = -vx

if(y .lt. 0.0 .or. y .gt. Ly) vy = -vy

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

if(x .lt. 0.0 .or. x .gt. Lx) vx = -vx

if(y .lt. 0.0 .or. y .gt. Ly) vy = -vy

The full program can be found in the ﬁle box2D_1.f90. Notice that we introduced two counters nx and ny of the particle’s collisions with the walls:

!============================================================

!File box2D_1.f90

!Motion of a free particle in a box 0<x<Lx 0<y<Ly

!Use integration with time step dt: x = x + vx*dt y=y+vy*dt

!------------------------------------------------------------

program box2D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: Lx,Ly,x0,y0,v0x,v0y,t0,tf,dt,t,x,y,vx,vy

integer :: i,nx,ny

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter Lx,Ly:’

read *,Lx,Ly

print *,’# Lx = ’,Lx,’ Ly= ’,Ly

if( Lx .le. 0.0) stop ’Lx must be positive.’

if( Ly .le. 0.0) stop ’Ly must be positive.’

print *,’# Enter x0,y0,v0x,v0y:’

read *,x0,y0,v0x,v0y

print *,’# x0= ’,x0,’ y0= ’,y0,’ v0x= ’,v0x,’ v0y= ’,v0y

if(x0 .lt. 0.0 .or. x0 .gt. Lx) stop ’illegal value x0’

if(y0 .lt. 0.0 .or. y0 .gt. Ly) stop ’illegal value y0’

if(v0x**2+v0y**2.eq. 0.0 ) stop ’illegal value v0=0’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

i = 0

nx = 0 ; ny = 0

t = t0

x = x0 ; y = y0

vx = v0x; vy = v0y

open(unit=11,file=’box2D_1.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,*)t,x,y,vx,vy

i = i + 1

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

if(x .lt. 0.0 .or. x .gt. Lx) then

vx = -vx

nx = nx + 1

endif

if(y .lt. 0.0 .or. y .gt. Ly) then

vy = -vy

ny = ny + 1

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# nx= ’,nx,’ ny= ’,ny

end program box2D

!File box2D_1.f90

!Motion of a free particle in a box 0<x<Lx 0<y<Ly

!Use integration with time step dt: x = x + vx*dt y=y+vy*dt

!------------------------------------------------------------

program box2D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: Lx,Ly,x0,y0,v0x,v0y,t0,tf,dt,t,x,y,vx,vy

integer :: i,nx,ny

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter Lx,Ly:’

read *,Lx,Ly

print *,’# Lx = ’,Lx,’ Ly= ’,Ly

if( Lx .le. 0.0) stop ’Lx must be positive.’

if( Ly .le. 0.0) stop ’Ly must be positive.’

print *,’# Enter x0,y0,v0x,v0y:’

read *,x0,y0,v0x,v0y

print *,’# x0= ’,x0,’ y0= ’,y0,’ v0x= ’,v0x,’ v0y= ’,v0y

if(x0 .lt. 0.0 .or. x0 .gt. Lx) stop ’illegal value x0’

if(y0 .lt. 0.0 .or. y0 .gt. Ly) stop ’illegal value y0’

if(v0x**2+v0y**2.eq. 0.0 ) stop ’illegal value v0=0’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

i = 0

nx = 0 ; ny = 0

t = t0

x = x0 ; y = y0

vx = v0x; vy = v0y

open(unit=11,file=’box2D_1.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,*)t,x,y,vx,vy

i = i + 1

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

if(x .lt. 0.0 .or. x .gt. Lx) then

vx = -vx

nx = nx + 1

endif

if(y .lt. 0.0 .or. y .gt. Ly) then

vy = -vy

ny = ny + 1

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# nx= ’,nx,’ ny= ’,ny

end program box2D

A typical session for the study of a particle’s trajectory could be:

> gfortran box2D_1.f90 -o box

> ./box

# Enter Lx,Ly:

10.0 5.0

# Lx = 10. Ly= 5.

# Enter x0,y0,v0x,v0y:

5.0 0.0 1.27 1.33

# x0= 5. y0= 0. v0x= 1.27 v0y= 1.33

# Enter t0,tf,dt:

0 50 0.01

# t0= 0. tf= 50. dt= 0.01

# Number of collisions:

# nx= 6 ny= 13

> gnuplot

gnuplot> plot "box2D_1.dat" using 1:2 w l title "x (t)"

gnuplot> replot "box2D_1.dat" using 1:3 w l title "y (t)"

gnuplot> plot "box2D_1.dat" using 1:4 w l title "vx(t)"

gnuplot> replot "box2D_1.dat" using 1:5 w l title "vy(t)"

gnuplot> plot "box2D_1.dat" using 2:3 w l title "x-y"

> ./box

# Enter Lx,Ly:

10.0 5.0

# Lx = 10. Ly= 5.

# Enter x0,y0,v0x,v0y:

5.0 0.0 1.27 1.33

# x0= 5. y0= 0. v0x= 1.27 v0y= 1.33

# Enter t0,tf,dt:

0 50 0.01

# t0= 0. tf= 50. dt= 0.01

# Number of collisions:

# nx= 6 ny= 13

> gnuplot

gnuplot> plot "box2D_1.dat" using 1:2 w l title "x (t)"

gnuplot> replot "box2D_1.dat" using 1:3 w l title "y (t)"

gnuplot> plot "box2D_1.dat" using 1:4 w l title "vx(t)"

gnuplot> replot "box2D_1.dat" using 1:5 w l title "vy(t)"

gnuplot> plot "box2D_1.dat" using 2:3 w l title "x-y"

Notice the last line of output from the program: The particle bounces oﬀ the vertical walls 6 times (nx=6) and from the horizontal ones 13 (ny=13). The gnuplot commands construct the diagrams displayed in ﬁgures 2.22 and 2.23.

In order to animate the particle’s trajectory, we can copy the ﬁle box2D_animate.gnu of the accompanying software to the current directory and give the gnuplot commands:

gnuplot> file = "box2D_1.dat"

gnuplot> Lx = 10 ; Ly = 5

gnuplot> t0 = 0 ; tf = 50; dt = 1

gnuplot> load "box2D_animate.gnu"

gnuplot> t0 = 0 ; dt = 0.5; load "box2D_animate.gnu"

gnuplot> Lx = 10 ; Ly = 5

gnuplot> t0 = 0 ; tf = 50; dt = 1

gnuplot> load "box2D_animate.gnu"

gnuplot> t0 = 0 ; dt = 0.5; load "box2D_animate.gnu"

The last line repeats the same animation at half speed. You can also use the ﬁle animate2D.gnu discussed in section 2.1.1. We add new commands in the ﬁle box2D_animate.gnu so that the plot limits are calculated automatically and the box is drawn on the plot. The arrow drawn is not the position vector with respect to the origin of the coordinate axes, but the one connecting the initial with the current position of the particle.

The next step should be to test the accuracy of your results. This can be done by generalizing the discussion of the previous section and is left as an exercise for the reader.

In this section we will study simple examples of motion in a box with diﬀerent types of obstacles. We will start with a game of ... mini golf. The player shoots a (point) “ball” which moves in an orthogonal box of linear dimensions and and which is open on the side. In the box there is a circular “hole” with center at and radius . If the “ball” falls in the “hole”, the player wins. If the ball leaves out of the box through its open side, the player loses. In order to check if the ball is in the hole when it is at position , all we have to do is to check whether .

Initially we place the ball at the position at time . The player hits the ball which leaves with initial velocity of magnitude at an angle degrees with the axis. The program is found in the ﬁle MiniGolf.f90 and is listed below:

!============================================================

!File MiniGolf.f

!Motion of a free particle in a box 0<x<Lx 0<y<Ly

!The box is open at x=0 and has a hole at (xc,yc) of radius R

!Ball is shot at (0,Ly/2) with speed v0, angle theta (degrees)

!Use integration with time step dt: x = x + vx*dt y=y+vy*dt

!Ball stops in hole (success) or at x=0 (failure)

!------------------------------------------------------------

program MiniGolf

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: Lx,Ly,x0,y0,v0x,v0y,t0,tf,dt,t,x,y,vx,vy

real(8) :: v0,theta,xc,yc,R,R2

real(8), parameter :: PI=3.14159265358979324D0

integer :: i,nx,ny

character(7) :: result

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter Lx,Ly:’

read *,Lx,Ly

print *,’# Lx = ’,Lx,’ Ly= ’,Ly

if( Lx .le. 0.0) stop ’Lx must be positive.’

if( Ly .le. 0.0) stop ’Ly must be positive.’

print *,’# Enter hole position and radius: (xc,yc), R:’

read *,xc,yc,R

print *,’# (xc,yc)= ( ’,xc,’ , ’,yc,’ ) R= ’,R

print *,’# Enter v0, theta(degrees):’

read *,v0,theta

print *,’# v0= ’,v0,’ theta= ’,theta,’ degrees’

if(v0 .le. 0.0D0 ) stop ’illegal value of v0.’

if(ABS(theta).ge. 90.0D0) stop ’illegal value of theta.’

print *,’# Enter dt:’

read *,dt

print *,’# dt= ’,dt

!------------------------------------------------------------

!Initialize

t0 = 0.0D0

x0 = 0.00001D0 ! small but non-zero

y0 = Ly/2.0

R2 = R*R

theta = (PI/180.0D0)*theta

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# x0= ’,x0,’ y0= ’,y0,’ v0x= ’,v0x,’ v0y= ’,v0y

i = 0

nx = 0 ; ny = 0

t = t0

x = x0 ; y = y0

vx = v0x; vy = v0y

open(unit=11,file=’MiniGolf.dat’)

!------------------------------------------------------------

!Compute:

do while( .TRUE. ) !forever!

write(11,*)t,x,y,vx,vy

i = i + 1

t = t0 + i*dt

x = x + vx*dt

y = y + vy*dt

if(x .gt. Lx) then

vx = -vx

nx = nx + 1

endif

if(y .lt. 0.0 .or. y .gt. Ly) then

vy = -vy

ny = ny + 1

endif

if(x .le. 0.0D0)then

result = ’Failure’

exit !exit do loop

endif

if( ((x-xc)*(x-xc)+(y-yc)*(y-yc)) .le. R2)then

result = ’Success’

exit !exit do loop

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# Result= ’,result,’ nx= ’,nx,’ ny= ’,ny

end program MiniGolf

!File MiniGolf.f

!Motion of a free particle in a box 0<x<Lx 0<y<Ly

!The box is open at x=0 and has a hole at (xc,yc) of radius R

!Ball is shot at (0,Ly/2) with speed v0, angle theta (degrees)

!Use integration with time step dt: x = x + vx*dt y=y+vy*dt

!Ball stops in hole (success) or at x=0 (failure)

!------------------------------------------------------------

program MiniGolf

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: Lx,Ly,x0,y0,v0x,v0y,t0,tf,dt,t,x,y,vx,vy

real(8) :: v0,theta,xc,yc,R,R2

real(8), parameter :: PI=3.14159265358979324D0

integer :: i,nx,ny

character(7) :: result

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter Lx,Ly:’

read *,Lx,Ly

print *,’# Lx = ’,Lx,’ Ly= ’,Ly

if( Lx .le. 0.0) stop ’Lx must be positive.’

if( Ly .le. 0.0) stop ’Ly must be positive.’

print *,’# Enter hole position and radius: (xc,yc), R:’

read *,xc,yc,R

print *,’# (xc,yc)= ( ’,xc,’ , ’,yc,’ ) R= ’,R

print *,’# Enter v0, theta(degrees):’

read *,v0,theta

print *,’# v0= ’,v0,’ theta= ’,theta,’ degrees’

if(v0 .le. 0.0D0 ) stop ’illegal value of v0.’

if(ABS(theta).ge. 90.0D0) stop ’illegal value of theta.’

print *,’# Enter dt:’

read *,dt

print *,’# dt= ’,dt

!------------------------------------------------------------

!Initialize

t0 = 0.0D0

x0 = 0.00001D0 ! small but non-zero

y0 = Ly/2.0

R2 = R*R

theta = (PI/180.0D0)*theta

v0x = v0*cos(theta)

v0y = v0*sin(theta)

print *,’# x0= ’,x0,’ y0= ’,y0,’ v0x= ’,v0x,’ v0y= ’,v0y

i = 0

nx = 0 ; ny = 0

t = t0

x = x0 ; y = y0

vx = v0x; vy = v0y

open(unit=11,file=’MiniGolf.dat’)

!------------------------------------------------------------

!Compute:

do while( .TRUE. ) !forever!

write(11,*)t,x,y,vx,vy

i = i + 1

t = t0 + i*dt

x = x + vx*dt

y = y + vy*dt

if(x .gt. Lx) then

vx = -vx

nx = nx + 1

endif

if(y .lt. 0.0 .or. y .gt. Ly) then

vy = -vy

ny = ny + 1

endif

if(x .le. 0.0D0)then

result = ’Failure’

exit !exit do loop

endif

if( ((x-xc)*(x-xc)+(y-yc)*(y-yc)) .le. R2)then

result = ’Success’

exit !exit do loop

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# Result= ’,result,’ nx= ’,nx,’ ny= ’,ny

end program MiniGolf

In order to run it, we can use the commands:

> gfortran MiniGolf.f90 -o mg

> ./mg

# Enter Lx,Ly:

10 5

# Lx = 10. Ly= 5.

# Enter hole position and radius: (xc,yc), R:

8 2.5 0.5

# (xc,yc)= ( 8. , 2.5 ) R= 0.5

# Enter v0, theta(degrees):

1 80

# v0= 1. theta= 80. degrees

# Enter dt:

0.01

# dt= 0.01

# x0= 1.E-05 y0= 2.5 v0x= 0.173648178 v0y= 0.984807753

# Number of collisions:

# Result= Success nx= 0 ny= 9

> ./mg

# Enter Lx,Ly:

10 5

# Lx = 10. Ly= 5.

# Enter hole position and radius: (xc,yc), R:

8 2.5 0.5

# (xc,yc)= ( 8. , 2.5 ) R= 0.5

# Enter v0, theta(degrees):

1 80

# v0= 1. theta= 80. degrees

# Enter dt:

0.01

# dt= 0.01

# x0= 1.E-05 y0= 2.5 v0x= 0.173648178 v0y= 0.984807753

# Number of collisions:

# Result= Success nx= 0 ny= 9

You should construct the plots of the position and the velocity of the particle. You can also use the animation program found in the ﬁle MiniGolf_animate.gnu for fun. Copy it from the accompanying software to the current directory and give the gnuplot commands:

gnuplot> file = "MiniGolf.dat"

gnuplot> Lx = 10;Ly = 5

gnuplot> xc = 8; yc = 2.5 ; R = 0.5

gnuplot> t0 = 0; dt = 0.1

gnuplot> load "MiniGolf_animate.gnu"

gnuplot> Lx = 10;Ly = 5

gnuplot> xc = 8; yc = 2.5 ; R = 0.5

gnuplot> t0 = 0; dt = 0.1

gnuplot> load "MiniGolf_animate.gnu"

The results are shown in ﬁgure 2.24.

The next example with be three dimensional. We will study the motion of a particle conﬁned within a cylinder of radius and height . The collisions of the particle with the cylinder are elastic. We take the axis of the cylinder to be the axis and the two bases of the cylinder to be located at and . This is shown in ﬁgure 2.26.

The collisions of the particle with the bases of the cylinder are easy to program: we follow the same steps as in the case of the simple box. For the collision with the cylinder’s side, we consider the projection of the motion on the plane. The projection of the particle moves within a circle of radius and center at the intersection of the axis with the plane. This is shown in ﬁgure 2.25. At the collision, the component of the velocity is reﬂected , whereas remains the same. The velocity of the particle before the collision is

and after the collision is From the relations and , , we have that The inverse relations are With the transformation , the new velocity in Cartesian coordinates will be The transformation , will be performed in the
subroutine reflectVonCircle(vx,vy,x,y,xc,yc,R). Upon entry
to the subroutine, we provide the initial velocity (vx,vy), the collision
point (x,y), the center of the circle (xc,yc) and the radius of the
circle^{22}
R. Upon exit from the subroutine, (vx,vy) have been replaced with the new
values^{23}
.

The program can be found in the ﬁle Cylinder3D.f90 and is listed below:

!============================================================

!File Cylinder3D.f90

!Motion of a free particle in a cylinder with axis the z-axis,

!radius R and 0<z<L

!Use integration with time step dt: x = x + vx*dt

! y = y + vy*dt

! z = z + vz*dt

!Use subroutine reflectVonCircle for collisions at r=R

!------------------------------------------------------------

program Cylinder3D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: x0,y0,z0,v0x,v0y,v0z,t0,tf,dt,t,x,y,z,vx,vy,vz

real(8) :: L,R,R2,vxy,rxy,r2xy,xc,yc

integer :: i,nr,nz

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter R, L:’

read *,R,L

print *,’# R= ’,R,’ L= ’,L

if( R .le. 0.0) stop ’R must be positive.’

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,y0,z0,v0x,v0y,v0z:’

read *,x0,y0,z0,v0x,v0y,v0z

rxy = DSQRT(x0*x0+y0*y0)

print *,’# x0 = ’,x0 ,’ y0 = ’,y0 ,’ z0= ’,z0, ’ rxy= ’,rxy

print *,’# v0x= ’,v0x,’ v0y= ’,v0y,’ v0z= ’,v0z

if(rxy .gt. R )stop ’illegal value of rxy > R’

if(z0 .lt. 0.0D0 )stop ’illegal value of z0 < 0’

if(z0 .gt. L )stop ’illegal value of z0 > L’

if(v0x**2+v0y**2+v0z**2.eq.0.0)stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

i = 0

nr = 0 ; nz = 0

t = t0

x = x0 ; y = y0 ; z = z0

vx = v0x; vy = v0y; vz = v0z

R2 = R*R

xc = 0.0D0 !center of circle which is the projection of the

yc = 0.0D0 !cylinder on the xy plane

open(unit=11,file=’Cylinder3D.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,100)t,x,y,z,vx,vy,vz

i = i + 1

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

z = z + vz*dt

if(z .lt. 0.0 .or. z .gt. L) then

vz = -vz ! reflection on cylinder caps

nz = nz + 1

endif

r2xy = x*x+y*y

if( r2xy .gt. R2)then

call reflectVonCircle(vx,vy,x,y,xc,yc,R)

nr = nr + 1

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# nr= ’,nr,’ nz= ’,nz

100 FORMAT(100G28.16)

end program Cylinder3D

!------------------------------------------------------------

!============================================================

!------------------------------------------------------------

subroutine reflectVonCircle(vx,vy,x,y,xc,yc,R)

implicit none

real(8) :: vx,vy,x,y,xc,yc,R

real(8) :: theta,cth,sth,vr,vth

theta = atan2(y-yc,x-xc)

cth = cos(theta)

sth = sin(theta)

vr = vx*cth + vy *sth

vth = -vx*sth + vy *cth

vx = -vr*cth - vth*sth !reflect vr -> -vr

vy = -vr*sth + vth*cth

x = xc + R*cth !put x,y on the circle

y = yc + R*sth

end subroutine reflectVonCircle

!File Cylinder3D.f90

!Motion of a free particle in a cylinder with axis the z-axis,

!radius R and 0<z<L

!Use integration with time step dt: x = x + vx*dt

! y = y + vy*dt

! z = z + vz*dt

!Use subroutine reflectVonCircle for collisions at r=R

!------------------------------------------------------------

program Cylinder3D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8) :: x0,y0,z0,v0x,v0y,v0z,t0,tf,dt,t,x,y,z,vx,vy,vz

real(8) :: L,R,R2,vxy,rxy,r2xy,xc,yc

integer :: i,nr,nz

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter R, L:’

read *,R,L

print *,’# R= ’,R,’ L= ’,L

if( R .le. 0.0) stop ’R must be positive.’

if( L .le. 0.0) stop ’L must be positive.’

print *,’# Enter x0,y0,z0,v0x,v0y,v0z:’

read *,x0,y0,z0,v0x,v0y,v0z

rxy = DSQRT(x0*x0+y0*y0)

print *,’# x0 = ’,x0 ,’ y0 = ’,y0 ,’ z0= ’,z0, ’ rxy= ’,rxy

print *,’# v0x= ’,v0x,’ v0y= ’,v0y,’ v0z= ’,v0z

if(rxy .gt. R )stop ’illegal value of rxy > R’

if(z0 .lt. 0.0D0 )stop ’illegal value of z0 < 0’

if(z0 .gt. L )stop ’illegal value of z0 > L’

if(v0x**2+v0y**2+v0z**2.eq.0.0)stop ’illegal value of v0 = 0.’

print *,’# Enter t0,tf,dt:’

read *,t0,tf,dt

print *,’# t0= ’,t0,’ tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

i = 0

nr = 0 ; nz = 0

t = t0

x = x0 ; y = y0 ; z = z0

vx = v0x; vy = v0y; vz = v0z

R2 = R*R

xc = 0.0D0 !center of circle which is the projection of the

yc = 0.0D0 !cylinder on the xy plane

open(unit=11,file=’Cylinder3D.dat’)

!------------------------------------------------------------

!Compute:

do while(t .le. tf)

write(11,100)t,x,y,z,vx,vy,vz

i = i + 1

t = t0 + i *dt

x = x + vx*dt

y = y + vy*dt

z = z + vz*dt

if(z .lt. 0.0 .or. z .gt. L) then

vz = -vz ! reflection on cylinder caps

nz = nz + 1

endif

r2xy = x*x+y*y

if( r2xy .gt. R2)then

call reflectVonCircle(vx,vy,x,y,xc,yc,R)

nr = nr + 1

endif

enddo

close(11)

print *,’# Number of collisions:’

print *,’# nr= ’,nr,’ nz= ’,nz

100 FORMAT(100G28.16)

end program Cylinder3D

!------------------------------------------------------------

!============================================================

!------------------------------------------------------------

subroutine reflectVonCircle(vx,vy,x,y,xc,yc,R)

implicit none

real(8) :: vx,vy,x,y,xc,yc,R

real(8) :: theta,cth,sth,vr,vth

theta = atan2(y-yc,x-xc)

cth = cos(theta)

sth = sin(theta)

vr = vx*cth + vy *sth

vth = -vx*sth + vy *cth

vx = -vr*cth - vth*sth !reflect vr -> -vr

vy = -vr*sth + vth*cth

x = xc + R*cth !put x,y on the circle

y = yc + R*sth

end subroutine reflectVonCircle

Notice that the function atan2 is used for computing the angle theta. This function, when called with two arguments atan2(y,x), returns the angle in radians. The correct quadrant of the circle where lies is chosen. The angle that we want to compute is given by atan2(y-yc,x-xc). Then we apply equations (2.29) and (2.31) and in the last two lines we enforce the particle to be at the point , exactly on the circle.

A typical session is shown below:

> gfortran Cylinder3D.f90 -o cl

> ./cl

# Enter R, L:

10.0 10.0

# R= 10. L= 10.

# Enter x0,y0,z0,v0x,v0y,v0z:

1.0 2.2 3.1 0.93 -0.89 0.74

# x0 = 1. y0 = 2.2 z0= 3.1 rxy= 2.41660919

# v0x= 0.93 v0y= -0.89 v0z= 0.74

# Enter t0,tf,dt:

0.0 500.0 0.01

# t0= 0. tf= 500. dt= 0.01

# Number of collisions:

# nr= 33 nz= 37

> ./cl

# Enter R, L:

10.0 10.0

# R= 10. L= 10.

# Enter x0,y0,z0,v0x,v0y,v0z:

1.0 2.2 3.1 0.93 -0.89 0.74

# x0 = 1. y0 = 2.2 z0= 3.1 rxy= 2.41660919

# v0x= 0.93 v0y= -0.89 v0z= 0.74

# Enter t0,tf,dt:

0.0 500.0 0.01

# t0= 0. tf= 500. dt= 0.01

# Number of collisions:

# nr= 33 nz= 37

In order to plot the position and the velocity as a function of time, we use the following gnuplot commands:

gnuplot> file="Cylinder3D.dat"

gnuplot> plot file using 1:2 with lines title " x(t)",\

file using 1:3 with lines title " y(t)",\

file using 1:4 with lines title " z(t)"

gnuplot> plot file using 1:5 with lines title "v_x(t)",\

file using 1:6 with lines title "v_y(t)",\

file using 1:7 with lines title "v_z(t)"

gnuplot> plot file using 1:2 with lines title " x(t)",\

file using 1:3 with lines title " y(t)",\

file using 1:4 with lines title " z(t)"

gnuplot> plot file using 1:5 with lines title "v_x(t)",\

file using 1:6 with lines title "v_y(t)",\

file using 1:7 with lines title "v_z(t)"

We can also compute the distance of the particle from the cylinder’s axis as a function of time using the command:

gnuplot> plot file using 1:(sqrt($2**2+$3**2)) w l t "r(t)"

In order to plot the trajectory, together with the cylinder, we give the commands:

gnuplot> L = 10 ; R = 10

gnuplot> set urange [0:2.0*pi]

gnuplot> set vrange [0:L]

gnuplot> set parametric

gnuplot> splot file using 2:3:4 with lines notitle,\

R*cos(u),R*sin(u),v notitle

gnuplot> set urange [0:2.0*pi]

gnuplot> set vrange [0:L]

gnuplot> set parametric

gnuplot> splot file using 2:3:4 with lines notitle,\

R*cos(u),R*sin(u),v notitle

The command set parametric is necessary if one wants to make a parametric plot of a surface . The cylinder (without the bases) is given by the parametric equations with , .

We can also animate the trajectory with the help of the gnuplot script ﬁle Cylinder3D_animate.gnu. Copy the ﬁle from the accompanying software to the current directory and give the gnuplot commands:

gnuplot> R=10;L=10;t0=0;tf=500;dt=10

gnuplot> load "Cylinder3D_animate.gnu"

gnuplot> load "Cylinder3D_animate.gnu"

The result is shown in ﬁgure 2.26.

The last example will be that of a simple model of a spacetime wormhole. This
is a simple spacetime geometry which, in the framework of the theory of general
relativity, describes the connection of two distant areas in space which are
asymptotically ﬂat. This means, that far enough from the wormhole’s mouths,
space is almost ﬂat - free of gravity. Such a geometry is depicted in ﬁgure 2.27.
The distance traveled by someone through the mouths could be much smaller
than the distance traveled outside the wormhole and, at least theoretically,
traversable wormholes could be used for interstellar/intergalactic traveling and/or
communications between otherwise distant areas in the universe. Of course we
should note that such macroscopic and stable wormholes are not known to
be possible to exist in the framework of general relativity. One needs
an exotic type of matter with negative energy density which has never
been observed. Such exotic geometries may realize microscopically as
quantum ﬂuctuations of spacetime and make the small scale structure of the
geometry^{24}
a “spacetime foam”.

We will study a very simple model of the above geometry on the plane with a particle moving
freely in it^{25} .

We take the two dimensional plane and cut two equal disks of radius with centers at distance like in ﬁgure 2.28. We identify the points on the two circles such that the point 1 of the left circle is the same as the point 1 on the right circle, the point 2 on the left with the point 2 on the right etc. The two circles are given by the parametric equations , , for the right circle and , , for the left. Points on the two circles with the same are identiﬁed. A particle entering the wormhole from the left circle with velocity is immediately exiting from the right with velocity as shown in ﬁgure 2.28.

Then we will do the following:

- Write a program that computes the trajectory of a particle moving in the geometry of ﬁgure 2.28. We set the limits of motion to be and . We will use periodic boundary conditions in order to deﬁne what happens when the particle attempts to move outside these limits. This means that we identify the line with the line as well as the line with the line. The user enters the parameters , and as well as the initial conditions , where . The user will also provide the time parameters and for motion in the time interval with step .
- Plot the particle’s trajectory with , , in the geometry with .
- Find a closed trajectory which does not cross the boundaries , and determine whether it is stable under small perturbations of the initial conditions.
- Find other closed trajectories that go through the mouths of the wormhole and study their stability under small perturbations of the initial conditions.
- Add to the program the option to calculate the distance traveled by the particle. If the particle starts from and moves in the direction to the , position, draw the trajectory and calculate the distance traveled on paper. Then conﬁrm your calculation from the numerical result coming from your program.
- Change the boundary conditions, so that the particle bounces oﬀ elastically at , and replot all the trajectories mentioned above.

Deﬁne the right circle by the parametric equations

| (2.32) |

and the left circle by the parametric equations

| (2.33) |

The particle’s position changes at time by

for for given , and as long as . If the point is outside the boundaries , , we redeﬁne , in each case respectively. Points deﬁned by the same value of are identiﬁed, i.e. they represent the same points of space. If the point crosses either one of the circles or , then we take the particle out from the other circle.Crossing the circle is determined by the relation

| (2.35) |

The angle is calculated from the equation

| (2.36) |

and the point is mapped to the point where

| (2.37) |

as can be seen in ﬁgure 2.29. For mapping , we ﬁrst calculate the vectors

| (2.38) |

so that the velocity

| (2.39) |

where the radial components are and . Therefore, the relations that give the “emerging” velocity are:

| (2.40) |

Similarly we calculate the case of entering from and emerging from . The condition now is:

| (2.41) |

The angle is given by

| (2.42) |

and the point is mapped to the point where

| (2.43) |

For mapping , we calculate the vectors

| (2.44) |

so that the velocity

| (2.45) |

The emerging velocity is:

| (2.46) |

Systematic errors are now coming from crossing the two mouths of the wormhole. There are no systematic errors from crossing the boundaries , (why?). Try to think of ways to control those errors and study them.

The closed trajectories that we are looking for come from the initial conditions

| (2.47) |

and they connect points 1 of ﬁgure 2.28. They are unstable, as can be seen by taking .

The closed trajectories that cross the wormhole and “wind” through space can come from the initial conditions

and cross the points and respectively. They are also unstable, as can be easily veriﬁed by using the program that you will write. The full program is listed below:
!============================================================

program WormHole2D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8), parameter :: PI=3.14159265358979324D0

real(8) :: Lx,Ly,L,R,d

real(8) :: x0,y0,v0,theta

real(8) :: t0,tf,dt

real(8) :: t,x,y,vx,vy

real(8) :: xc1,yc1,xc2,yc2,r1,r2

integer :: i

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L,d,R:’

read *,L,d,R

print *,’# L= ’,L,’ d= ’,d,’ R= ’,R

if( L .le. d+2.0D0*R) stop ’L <= d+2*R’

if( d .le. 2.0D0*R) stop ’d <= 2*R’

print *,’# Enter (x0,y0), v0, theta(degrees):’

read *,x0,y0,v0,theta

print *,’# x0= ’,x0,’ y0 = ’,y0

print *,’# v0= ’,v0,’ theta= ’,theta,’ degrees’

if(v0 .le. 0.0D0 ) stop ’illegal value of v0.’

print *,’# Enter tf, dt:’

read *,tf,dt

print *,’# tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

theta = (PI/180.0D0)*theta

i = 0

t = 0.0D0

x = x0 ; y = y0

vx = v0*cos(theta); vy = v0*sin(theta)

print *,’# x0= ’,x,’ y0= ’,y,’ v0x= ’,vx,’ v0y= ’,vy

!Wormhole’s centers:

xc1 = 0.5D0*d; yc1 = 0.0D0

xc2 = -0.5D0*d; yc2 = 0.0D0

!Box limits coordinates:

Lx = 0.5D0*L; Ly = 0.5D0*L

!Test if already inside cut region:

r1 = sqrt((x-xc1)**2+(y-yc1)**2)

r2 = sqrt((x-xc2)**2+(y-yc2)**2)

if( r1 .le. R ) stop ’r1 <= R’

if( r2 .le. R ) stop ’r2 <= R’

!Test if outside box limits:

if(ABS(x) .ge. Lx) stop ’|x| >= Lx’

if(ABS(y) .ge. Ly) stop ’|y| >= Ly’

open(unit=11,file=’Wormhole.dat’)

!------------------------------------------------------------

!Compute:

do while( t .lt. tf )

write(11,*)t,x,y,vx,vy

i = i+1

t = i*dt

x = x + vx*dt; y = y + vy*dt

! Toroidal boundary conditions:

if( x .gt. Lx) x = x - L

if( x .lt. -Lx) x = x + L

if( y .gt. Ly) y = y - L

if( y .lt. -Ly) y = y + L

! Test if inside the cut disks

r1 = sqrt((x-xc1)**2+(y-yc1)**2)

r2 = sqrt((x-xc2)**2+(y-yc2)**2)

if( r1 .lt. R)then

! Notice: we pass r1 as radius of circle, not R

call crossC1(x,y,vx,vy,dt,r1,d)

else if( r2 .lt. R)then

call crossC2(x,y,vx,vy,dt,r2,d)

endif

! small chance here that still in C1 or C2, but OK since

! another dt-advance given at the beginning of do-loop

enddo !do while( t .lt. tf )

end program WormHole2D

!=======================================================

subroutine crossC1(x,y,vx,vy,dt,R,d)

implicit none

real(8) :: x,y,vx,vy,dt,R,d

real(8) :: vr,v0 !v0 -> vtheta

real(8) :: theta,xc,yc

print *,’# Inside C1: (x,y,vx,vy,R)= ’,x,y,vx,vy,R

xc = 0.5D0*d !center of C1

yc = 0.0D0

theta = atan2(y-yc,x-xc)

x = -xc - R*cos(theta) !new x-value, y invariant

!Velocity transformation:

vr = vx*cos(theta)+vy*sin(theta)

v0 = -vx*sin(theta)+vy*cos(theta)

vx = vr*cos(theta)+v0*sin(theta)

vy = -vr*sin(theta)+v0*cos(theta)

!advance x,y, hopefully outside C2:

x = x + vx*dt

y = y + vy*dt

print *,’# Exit C2: (x,y,vx,vy )= ’,x,y,vx,vy

end subroutine crossC1

!=======================================================

subroutine crossC2(x,y,vx,vy,dt,R,d)

implicit none

real(8), parameter :: PI=3.14159265358979324D0

real(8) :: x,y,vx,vy,dt,R,d

real(8) :: vr,v0 !v0 -> vtheta

real(8) :: theta,xc,yc

print *,’# Inside C2: (x,y,vx,vy,R)= ’,x,y,vx,vy,R

xc = -0.5D0*d !center of C2

yc = 0.0D0

theta = PI-atan2(y-yc,x-xc)

x = -xc + R*cos(theta) !new x-value, y invariant

!Velocity transformation:

vr = -vx*cos(theta)+vy*sin(theta)

v0 = vx*sin(theta)+vy*cos(theta)

vx = -vr*cos(theta)-v0*sin(theta)

vy = -vr*sin(theta)+v0*cos(theta)

!advance x,y, hopefully outside C1:

x = x + vx*dt

y = y + vy*dt

print *,’# Exit C1: (x,y,vx,vy )= ’,x,y,vx,vy

end subroutine crossC2

program WormHole2D

implicit none

!------------------------------------------------------------

!Declaration of variables

real(8), parameter :: PI=3.14159265358979324D0

real(8) :: Lx,Ly,L,R,d

real(8) :: x0,y0,v0,theta

real(8) :: t0,tf,dt

real(8) :: t,x,y,vx,vy

real(8) :: xc1,yc1,xc2,yc2,r1,r2

integer :: i

!------------------------------------------------------------

!Ask user for input:

print *,’# Enter L,d,R:’

read *,L,d,R

print *,’# L= ’,L,’ d= ’,d,’ R= ’,R

if( L .le. d+2.0D0*R) stop ’L <= d+2*R’

if( d .le. 2.0D0*R) stop ’d <= 2*R’

print *,’# Enter (x0,y0), v0, theta(degrees):’

read *,x0,y0,v0,theta

print *,’# x0= ’,x0,’ y0 = ’,y0

print *,’# v0= ’,v0,’ theta= ’,theta,’ degrees’

if(v0 .le. 0.0D0 ) stop ’illegal value of v0.’

print *,’# Enter tf, dt:’

read *,tf,dt

print *,’# tf= ’,tf,’ dt= ’,dt

!------------------------------------------------------------

!Initialize

theta = (PI/180.0D0)*theta

i = 0

t = 0.0D0

x = x0 ; y = y0

vx = v0*cos(theta); vy = v0*sin(theta)

print *,’# x0= ’,x,’ y0= ’,y,’ v0x= ’,vx,’ v0y= ’,vy

!Wormhole’s centers:

xc1 = 0.5D0*d; yc1 = 0.0D0

xc2 = -0.5D0*d; yc2 = 0.0D0

!Box limits coordinates:

Lx = 0.5D0*L; Ly = 0.5D0*L

!Test if already inside cut region:

r1 = sqrt((x-xc1)**2+(y-yc1)**2)

r2 = sqrt((x-xc2)**2+(y-yc2)**2)

if( r1 .le. R ) stop ’r1 <= R’

if( r2 .le. R ) stop ’r2 <= R’

!Test if outside box limits:

if(ABS(x) .ge. Lx) stop ’|x| >= Lx’

if(ABS(y) .ge. Ly) stop ’|y| >= Ly’

open(unit=11,file=’Wormhole.dat’)

!------------------------------------------------------------

!Compute:

do while( t .lt. tf )

write(11,*)t,x,y,vx,vy

i = i+1

t = i*dt

x = x + vx*dt; y = y + vy*dt

! Toroidal boundary conditions:

if( x .gt. Lx) x = x - L

if( x .lt. -Lx) x = x + L

if( y .gt. Ly) y = y - L

if( y .lt. -Ly) y = y + L

! Test if inside the cut disks

r1 = sqrt((x-xc1)**2+(y-yc1)**2)

r2 = sqrt((x-xc2)**2+(y-yc2)**2)

if( r1 .lt. R)then

! Notice: we pass r1 as radius of circle, not R

call crossC1(x,y,vx,vy,dt,r1,d)

else if( r2 .lt. R)then

call crossC2(x,y,vx,vy,dt,r2,d)

endif

! small chance here that still in C1 or C2, but OK since

! another dt-advance given at the beginning of do-loop

enddo !do while( t .lt. tf )

end program WormHole2D

!=======================================================

subroutine crossC1(x,y,vx,vy,dt,R,d)

implicit none

real(8) :: x,y,vx,vy,dt,R,d

real(8) :: vr,v0 !v0 -> vtheta

real(8) :: theta,xc,yc

print *,’# Inside C1: (x,y,vx,vy,R)= ’,x,y,vx,vy,R

xc = 0.5D0*d !center of C1

yc = 0.0D0

theta = atan2(y-yc,x-xc)

x = -xc - R*cos(theta) !new x-value, y invariant

!Velocity transformation:

vr = vx*cos(theta)+vy*sin(theta)

v0 = -vx*sin(theta)+vy*cos(theta)

vx = vr*cos(theta)+v0*sin(theta)

vy = -vr*sin(theta)+v0*cos(theta)

!advance x,y, hopefully outside C2:

x = x + vx*dt

y = y + vy*dt

print *,’# Exit C2: (x,y,vx,vy )= ’,x,y,vx,vy

end subroutine crossC1

!=======================================================

subroutine crossC2(x,y,vx,vy,dt,R,d)

implicit none

real(8), parameter :: PI=3.14159265358979324D0

real(8) :: x,y,vx,vy,dt,R,d

real(8) :: vr,v0 !v0 -> vtheta

real(8) :: theta,xc,yc

print *,’# Inside C2: (x,y,vx,vy,R)= ’,x,y,vx,vy,R

xc = -0.5D0*d !center of C2

yc = 0.0D0

theta = PI-atan2(y-yc,x-xc)

x = -xc + R*cos(theta) !new x-value, y invariant

!Velocity transformation:

vr = -vx*cos(theta)+vy*sin(theta)

v0 = vx*sin(theta)+vy*cos(theta)

vx = -vr*cos(theta)-v0*sin(theta)

vy = -vr*sin(theta)+v0*cos(theta)

!advance x,y, hopefully outside C1:

x = x + vx*dt

y = y + vy*dt

print *,’# Exit C1: (x,y,vx,vy )= ’,x,y,vx,vy

end subroutine crossC2

It is easy to compile and run the program. See also the ﬁles Wormhole.csh and Wormhole_animate.gnu of the accompanying software and run the gnuplot commands:

gnuplot> file = "Wormhole.dat"

gnuplot> R=1;d=5;L=20;

gnuplot> ! ./Wormhole.csh

gnuplot> t0=0;dt=0.2;load "Wormhole_animate.gnu"

gnuplot> R=1;d=5;L=20;

gnuplot> ! ./Wormhole.csh

gnuplot> t0=0;dt=0.2;load "Wormhole_animate.gnu"

You are now ready to answer the rest of the questions that we asked in our list.

- Change the program Circle.f90 so that it prints the number of full circles traversed by the particle.
- Add all the necessary tests on the parameters entered by the user in the program Circle.f90, so that the program is certain to run without problems. Do the same for the rest of the programs given in the same section.
- A particle moves with constant angular velocity on a circle that has the origin of the coordinate system at its center. At time , the particle is at . Write the program CircularMotion.f90 that will calculate the particle’s trajectory. The user should enter the parameters . The program should print the results like the program Circle.f90 does.
- Change the program SimplePendulum.f90 so that the user could enter a non zero initial velocity.
- Study the limit in the projectile motion given by equations
(2.10) . Expand and keep the non
vanishing terms as . Then keep the next order leading terms
which have a smaller power of . Program these relations in a ﬁle

ProjectileSmallAirResistance.f90. Consider the initial conditions and calculate the range of the trajectory numerically by using the two programs

ProjectileSmallAirResistance.f90, ProjectileAirResistance.f90. Determine the range of values of for which the two results agree within 5% accuracy. - Write a program for a projectile which moves through a ﬂuid with ﬂuid resistance proportional to the square of the velocity. Compare the range of the trajectory with the one calculated by the program ProjectileAirResistance.f90 for the parameters shown in ﬁgure 2.10.
- Change the program Lissajous.f90 so that the user can enter a diﬀerent amplitude and initial phase in each direction. Study the case where the amplitudes are the same and the phase diﬀerence in the two directions are . Repeat by taking the amplitude in the direction to be twice as much the amplitude in the direction.
- Change the program ProjectileAirResistance.f90, so that it can calculate also the case.
- Change the program ProjectileAirResistance.f90 so that it can calculate the trajectory of the particle in three dimensional space. Plot the position coordinates and the velocity components as a function of time. Plot the three dimensional trajectory using splot in gnuplot and animate the trajectory using the gnuplot script animate3D.gnu.
- Change the program ChargeInB.f90 so that it can calculate the number of full revolutions that the projected particle’s position on the plane makes during its motion.
- Change the program box1D_1.f90 so that it prints the number of the particle’s collisions on the left wall, on the right wall and the total number of collisions to the stdout.
- Do the same for the program box1D_2.f90. Fill the table on page 311 the number of calculated collisions and comment on the results.
- Run the program box1D_1.f90 and choose L= 10, v0=1. Decrease the step dt up to the point that the particle stops to move. For which value of dt this happens? Increase v0=10,100. Until which value of dt the particle moves now? Why?
- Change the REAL declarations to REAL(8) in the program box1D_1.f90. Add explicit exponents D0 to all constants (e.g. 0.00.0D0). Compare your results to those obtained in section 2.3.2. Repeat problem 2.13. What do you observe?
- Change the program box1D_1.f90 so that you can study non elastic collisions , with the walls.
- Change the program box2D_1.f90 so that you can study inelastic collisions with the walls, such that , , .
- Use the method of calculating time in the programs box1D_4.f90 and box1D_5.f90 in order to produce the results in ﬁgure 2.21.
- Particle falls freely moving in the vertical direction. It starts with zero velocity at height . Upon reaching the ground, it bounces inelastically such that with a parameter. Write the necessary program in order to study numerically the particle’s motion and study the cases .
- Generalize the program of the previous problem so that you can study the case . Animate the calculated trajectories.
- Study the motion of a particle moving inside the box of ﬁgure 2.30. Count
the number of collisions of the particle with the walls before it leaves the
box.

- Study the motion of the point particle on the “billiard table” of ﬁgure 2.31.
Count the number of collisions with the walls before the particle enters into
a hole. The program should print from which hole the particle left the table.

- Write a program in order to study the motion of a particle in the box of
ﬁgure 2.32. At the center of the box there is a disk on which the particle
bounces oﬀ elastically (Hint: use the routine reflectVonCircle of the
program Cylinder3D.f90).

- In the box of the previous problem, put four disks on which the particle
bounces of elastically like in ﬁgure 2.33.

- Consider the arrangement of ﬁgure 2.34. Each time the particle bounces
elastically oﬀ a circle, the circle disappears. The game is over successfully if
all the circles vanish. Each time the particle bounces oﬀ on the wall to the
left, you lose a point. Try to ﬁnd trajectories that minimize the number of
lost points.

Logistic Map

Nonlinear diﬀerential equations model interesting dynamical systems in physics, biology and other branches of science. In this chapter we perform a numerical study of the discrete logistic map as a “simple mathematical model with complex dynamical properties” [21] similar to the ones encountered in more complicated and interesting dynamical systems. For certain values of the parameter of the map, one ﬁnds chaotic behavior giving us an opportunity to touch on this very interesting topic with important consequences in physical phenomena. Chaotic evolution restricts out ability for useful predictions in an otherwise fully deterministic dynamical system: measurements using slightly diﬀerent initial conditions result in a distribution which is indistinguishable from the distribution coming from sampling a random process. This scientiﬁc ﬁeld is huge and active and we refer the reader to the bibliography for a more complete introduction [21, 22, 23, 24, 25, 26, 27, 38].

The most celebrated application of the logistic map comes from the study of population growth in biology. One considers populations which reproduce at ﬁxed time intervals and whose generations do not overlap.

The simplest (and most naive) model is the one that makes the reasonable assumption that the rate of population growth of a population is proportional to the current population:

| (3.1) |

The general solution of the above equation is showing an exponential population growth for an decline for . It is obvious that this model is reasonable as long as the population is small enough so that the interaction with its environment (adequate food, diseases, predators etc) can be neglected. The simplest model that takes into account some of the factors of the interaction with the environment (e.g. starvation) is obtained by the introduction of a simple non linear term in the equation so that

| (3.2) |

The parameter gives the maximum growth rate of the population and controls the ability of the species to maintain a certain population level. The equation (3.2) can be discretized in time by assuming that each generation reproduces every and that the n-th generation has population where . Then and equation (3.1) becomes

| (3.3) |

where . The solutions of the above equation are well approximated by so that we have population growth when and decline when . Equation (3.2) can be discretized as follows:

| (3.4) |

Deﬁning we obtain the logistic map

| (3.5) |

We deﬁne the functions

| (3.6) |

(their only diﬀerence is that, in the ﬁrst one, is considered as a given parameter), so that

| (3.7) |

where we use the notation , , , for function composition. In what follows, the derivative of will be useful:

| (3.8) |

Since we interpret to be the fraction of the population with
respect to its maximum value, we should have for
each^{1}
. The function has one global maximum for which is equal
to . Therefore, if , then , which for an
appropriate choice of will lead to for some value of .
Therefore, the interval of values of which is of interest for our model
is

| (3.9) |

The logistic map (3.5) may be viewed as a ﬁnite diﬀerence equation and it
is a one step inductive relation. Given an initial value , a sequence
of values is produced. This will be
referred^{2}
to as the trajectory of . In the following sections we will study the properties of
these trajectories as a function of the parameter .

The solutions of the logistic map are not known except in special cases. For we have

| (3.10) |

and for^{3}

| (3.11) |

For , whereas for we have periodic trajectories resulting in rational and non periodic resulting in irrational . For other values of we have to resort to a numerical computation of the trajectories of the logistic map.

It is obvious that if the point is a solution of the equation , then for every . For the function we have two solutions

| (3.12) |

We will see that for appropriate values of , these solutions are attractors of most of the trajectories. This means that for a range of values for the initial point , the sequence approaches asymptotically one of these points as . Obviously the (measure zero) sets of initial values and result in trajectories attracted by and respectively. In order to determine which one of the two values is preferred, we need to study the stability of the ﬁxed points and . For this, assume that for some value of , is inﬁnitesimally close to the ﬁxed point so that

Since
| (3.14) |

where we used the Taylor expansion of the analytic function about and the relation , we have that . Then we obtain

| (3.15) |

Therefore, if we obtain and the ﬁxed point is stable: the sequence approaches asymptotically. If then the sequence deviates away from and the ﬁxed point is unstable. The limiting case should be studied separately and it indicates a change in the stability properties of the ﬁxed point. In the following discussion, these points will be shown to be bifurcation points.

For the function with we have that and . Therefore, if the point is an attractor, whereas the point is irrelevant. When , the point results in , therefore is unstable. Any initial value near deviates from it. Since for we have that , the point is an attractor. Any initial value approaches . When we have the limiting case and we say that at the critical value the ﬁxed point bifurcates to the two ﬁxed points and .

As increases, the ﬁxed points continue to bifurcate. Indeed, when we have that and for the point becomes unstable. Consider the solution of the equation . If is one of its solutions and for some we have that , then and (therefore is also a solution). If are two such diﬀerent solutions with , , then the trajectory is periodic with period 2. The points , are such that they are real solutions of the equation

| (3.16) |

and at the same time they are not the solutions of the
equation^{4}
, the polynomial above can be written in the form (see [22] for more
details)

| (3.17) |

By expanding the polynomials (3.16) , (3.17) and comparing their coeﬃcients we conclude that , and . The roots of the trinomial in (3.17) are determined by the discriminant . For the values of of interest (), the discriminant becomes positive when and we have two diﬀerent solutions

| (3.18) |

When we have one double root, therefore a unique ﬁxed point.

The study of the stability of the solutions of requires
the same steps that led to the equation (3.15) and we determine if the
absolute value of is greater, less or equal to one. By noting
that^{5}
, we see that for
, and for ,
. For the intermediate values the
derivatives for . Therefore, these points are stable
solutions of and the points bifurcate to ,
for . Almost all trajectories with initial points in the interval
are attracted by the periodic trajectory with period 2, the “2-cycle”
.

Using similar arguments we ﬁnd that the ﬁxed points ,
bifurcate to the eight ﬁxed points , when .
These are real solutions of the equation that gives the 4-cycle .
For , the points , are a stable
4-cycle which is an attractor of almost all trajectories of the logistic
map^{6} .
Similarly, for the 16 ﬁxed points of the equation
give a stable 8-cycle, for a stable 16-cycle
etc^{7} .
This is the phenomenon which is called period doubling which continues ad
inﬁnitum. The points are getting closer to each other as increases so that
. As we will see, marks the onset
of the non-periodic, chaotic behavior of the trajectories of the logistic
map.

Computing the bifurcation points becomes quickly intractable and we have to resort to a numerical computation of their values. Initially we will write a program that computes trajectories of the logistic map for chosen values of and . The program can be found in the ﬁle logistic.f90 and is listed below:

!===========================================================

!Discrete Logistic Map

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1

! ----- Input:

print *,’# Enter NSTEPS, r, x0:’

read *,NSTEPS,r,x0

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

! ----- Initialize:

open(unit=33,file=’log.dat’)

write(33,*) 0,x0

! ----- Calculate:

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*)i,x1

x0 = x1

enddo

close(33)

end program logistic_map

!Discrete Logistic Map

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1

! ----- Input:

print *,’# Enter NSTEPS, r, x0:’

read *,NSTEPS,r,x0

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

! ----- Initialize:

open(unit=33,file=’log.dat’)

write(33,*) 0,x0

! ----- Calculate:

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*)i,x1

x0 = x1

enddo

close(33)

end program logistic_map

The program is compiled and run using the commands:

> gfortran logistic.f90 -o l

> echo "100 0.5 0.1" | ./l

> echo "100 0.5 0.1" | ./l

The command echo prints to the stdout the values of the parameters NSTEPS=100, r=0.5 and x0=0.1. Its stdout is redirected to the stdin of the command ./l by using a pipe via the symbol |, from which the program reads their value and uses them in the calculation. The results can be found in two columns in the ﬁle log.dat and can be plotted using gnuplot. The plots are put in ﬁgure 3.1 and we can see the ﬁrst two bifurcations when goes past the values and . Similarly, we can study trajectories which are -cycles when crosses the values .

Another way to depict the 2-cycles is by constructing the cobweb plots: We start from the point and we calculate the point , where . This point belongs on the curve . The point is then projected on the diagonal and we obtain the point . We repeat times obtaining the points and on and respectively. The ﬁxed points are at the intersections of these curves and, if they are attractors, the trajectories will converge on them. If we have a -cycle, we will observe a periodic trajectory going through points which are solutions to the equation . This exercise can be done by using the following program, which can be found in the ﬁle logistic1.f90:

!===========================================================

! Discrete Logistic Map

! Map the trajectory in 2d space (plane)

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1

! ----- Input:

print *,’# Enter NSTEPS, r, x0:’

read *,NSTEPS,r,x0

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

! ----- Initialize:

open(unit=33,file=’trj.dat’)

! ----- Calculate:

write(33,*) 0, x0,0

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*) 2*i-3,x0,x1

write(33,*) 2*i-2,x1,x1

x0 = x1

enddo

close(33)

end program logistic_map

! Discrete Logistic Map

! Map the trajectory in 2d space (plane)

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1

! ----- Input:

print *,’# Enter NSTEPS, r, x0:’

read *,NSTEPS,r,x0

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

! ----- Initialize:

open(unit=33,file=’trj.dat’)

! ----- Calculate:

write(33,*) 0, x0,0

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*) 2*i-3,x0,x1

write(33,*) 2*i-2,x1,x1

x0 = x1

enddo

close(33)

end program logistic_map

Compiling and running this program is done exactly as in the case of the program in logistic.f90. We can plot the results using gnuplot. The plot in ﬁgure 3.2 can be constructed using the commands:

gnuplot> set size square

gnuplot> f(x) = r*x*(1.0-x)

gnuplot> r = 3.3

gnuplot> plot "<echo 50 3.3 0.2|./l;cat trj.dat" using 2:3 w l

gnuplot> replot f(x) ,f(f(x)),x

gnuplot> f(x) = r*x*(1.0-x)

gnuplot> r = 3.3

gnuplot> plot "<echo 50 3.3 0.2|./l;cat trj.dat" using 2:3 w l

gnuplot> replot f(x) ,f(f(x)),x

The plot command shown above, runs the program exactly as it is done on the command line. This is accomplished by using the symbol <, which reads the plot from the stdout of the command "echo 50 3.3 0.2|./l;cat trj.dat". Only the second command "echo trj.dat" writes to the stdout, therefore the plot is constructed from the contents of the ﬁle trj.dat. The following line adds the plots of the functions , and of the diagonal . Figures 3.2 and 3.3 show examples of attractors which are ﬁxed points, 2-cycles and 4-cycles. An example of a non periodic trajectory is also shown, which exhibits chaotic behavior which can happen when .

The bifurcations of the ﬁxed points of the logistic map discussed in the previous section can be conveniently shown on the “bifurcation diagram”. We remind to the reader that the ﬁrst bifurcations happen at the critical values of r

| (3.19) |

where , , and .
For we have ﬁxed points , of
. By plotting these points as a function of we construct
the bifurcation diagram. These can be calculated numerically by using the
program bifurcate.f90. In this program, the user selects the values of that
she needs to study and for each one of them the program records the point of the
-cycles^{8}
, . This is easily done by computing the
logistic map several times until we are sure that the trajectories reach the stable
state. The parameter NTRANS in the program determines the number of points
that we throw away, which should contain all the transient behavior. After
NTRANS steps, the program records NSTEPS points, where NSTEPS should be
large enough to cover all the points of the -cycles or depict a dense
enough set of values of the non periodic orbits. The program is listed
below:

!===========================================================

! Bifurcation Diagram of the Logistic Map

!===========================================================

program bifurcation_diagram

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

integer,parameter :: NTRANS = 500 !Number of discarded steps

integer,parameter :: NSTEPS = 100 !Number of recorded steps

integer,parameter :: RSTEPS = 2000 !Number of values of r

integer :: i

real(8) :: r,dr,x0,x1

! ----- Initialize:

open(unit=33,file=’bif.dat’)

dr = (rmax-rmin)/RSTEPS !Increment in r

! ----- Calculate:

r = rmin

do while ( r .le. rmax)

x0 = 0.5D0

! ---- Transient steps: skip

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0)

x0 = x1

enddo

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*) r,x1

x0 = x1

enddo

r = r + dr

enddo ! do while

close(33)

end program bifurcation_diagram

! Bifurcation Diagram of the Logistic Map

!===========================================================

program bifurcation_diagram

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

integer,parameter :: NTRANS = 500 !Number of discarded steps

integer,parameter :: NSTEPS = 100 !Number of recorded steps

integer,parameter :: RSTEPS = 2000 !Number of values of r

integer :: i

real(8) :: r,dr,x0,x1

! ----- Initialize:

open(unit=33,file=’bif.dat’)

dr = (rmax-rmin)/RSTEPS !Increment in r

! ----- Calculate:

r = rmin

do while ( r .le. rmax)

x0 = 0.5D0

! ---- Transient steps: skip

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0)

x0 = x1

enddo

do i=1,NSTEPS

x1 = r * x0 * (1.0D0-x0)

write(33,*) r,x1

x0 = x1

enddo

r = r + dr

enddo ! do while

close(33)

end program bifurcation_diagram

The program can be compiled and run using the commands:

> gfortran bifurcate.f90 -o b

> ./b;

> ./b;

The left plot of ﬁgure 3.4 can be constructed by the gnuplot commands:

gnuplot> plot "bif.dat" with dots

We observe the ﬁxed points and the -cycles for . When goes past , the trajectories become non-periodic and exhibit chaotic behavior. Chaotic behavior will be discussed more extensively in the next section. For the time being, we note that if we measure the distance between the points , we ﬁnd that it decreases constantly with so that

| (3.20) |

where is the Feigenbaum constant. An additional constant , deﬁned by the quotient of the separation of adjacent elements of period doubled attractors from one double to the next , is

| (3.21) |

It is also interesting to note the appearance of a 3-cycle right after
! By using the theorem of Sharkovskii, Li and
Yorke^{9}
showed that any one dimensional system has 3-cycles, therefore it will have cycles
of any length and chaotic trajectories. The stability of the 3-cycle can be studied
from the solutions of in exactly the same way that we did in
equations (3.16) and (3.17) (see [22] for details). Figure 3.5 magniﬁes a branch
of the 3-cycle. By magnifying diﬀerent regions in the bifurcation plot, as shown in
the right plot of ﬁgure 3.4, we ﬁnd similar shapes to the branching of the 3-cycle.

Figure 3.4 shows that between intervals of chaotic behavior we obtain “windows” of periodic trajectories. These are inﬁnite but countable. It is also quite interesting to note that if we magnify a branch withing these windows, we obtain a diagram that is similar to the whole diagram! We say that the bifurcation diagram exhibits self similarity. There are more interesting properties of the bifurcation diagram and we refer the reader to the bibliography for a more complete exposition.

We close this section by mentioning that the qualitative properties
of the bifurcation diagram are the same for a whole class of functions.
Feigenbaum discovered that if one takes any function that is concave
and has a unique global maximum, its bifurcation diagram behaves
qualitatively the same way as that of the logistic map. Examples of such
functions^{10}
studied in the literature are , and .
The constants and of equations (3.20) and (3.21) are the same
of all these mappings. The functions that result in chaotic behavior are
studied extensively in the literature and you can ﬁnd a list of those in
[28].

In order to determine the bifurcation points, one has to solve the nonlinear, polynomial, algebraic equations and . For this reason, one has to use an approximate numerical calculation of the roots, and the simple Newton-Raphson method will prove to be a good choice.

Newton-Raphson’s method uses an initial guess for the solution of the equation and computes a sequence of points that presumably converges to one of the roots of the equation. The computation stops at a ﬁnite , when we decide that the desired level of accuracy has been achieved. In order to understand how it works, we assume that is an analytic function for all the values of used in the computation. Then, by Taylor expanding around we obtain

| (3.22) |

If we wish to have , we choose

| (3.23) |

The equation above gives the Newton-Raphson method for one equation of one variable . Diﬀerent choices for will possibly lead to diﬀerent roots. When , are non zero at the root and is bounded, the convergence of the method is quadratic with the number of iterations. This means that there is a neighborhood of the root such that the distance is . If the root has multiplicity larger than 1, convergence is slower. The proofs of these statements are simple and can be found in [29].

The Newton-Raphson method is simple to program and, most of the times, suﬃcient for the solution of many problems. In the general case it works well only close enough to a root. We should also keep in mind that there are simple reasons for the method to fail. For example, when for some , the method stops. For functions that tend to as , it is easy to make a bad choice for that does not lead to convergence to a root. Sometimes it is a good idea to combine the Newton-Raphson method with the bisection method. When the derivative diverges at the root we might get into trouble. For example, the equation with , does not lead to a convergent sequence. In some cases, we might enter into non-convergent cycles [8]. For some functions the basin of attraction of a root (the values of that will converge to the root) can be tiny. See problem 13.

As a test case of our program, consider the equation

| (3.24) |

which results from the solution of Schrödinger’s equation for the energy spectrum of a quantum mechanical particle of mass in a one dimensional potential well of depth and width . The parameters and . Given , we solve for which gives the energy . The function and its derivative are

The program of the Newton-Raphson method for solving the equation can be found in the ﬁle nr.f90:
!===========================================================

!Newton Raphson for a function of one variable

!===========================================================

program NewtonRaphson

implicit none

real(8), parameter :: rho = 15.0D0

real(8), parameter :: eps = 1D-6

integer, parameter :: NMAX = 1000

real(8) :: x0, x1, err, g, gp

integer :: i

print *, ’Enter x0: ’

read *, x0

err = 1.0D0

print *,’iter x error ’

print *,’-------------------------------------------------’

print *, 0,x0,err

do i=1,NMAX

!value of function g(x):

g = x0*tan(x0)-sqrt(rho*rho-x0*x0)

!value of the derivative g’(x):

gp = x0/sqrt(rho*rho-x0*x0)+x0/(cos(x0)**2)+tan(x0)

x1 = x0 - g/gp

err = ABS(x1-x0)

print *,i,x1,err

if(err .lt. eps) exit

x0 = x1

enddo

end program NewtonRaphson

!Newton Raphson for a function of one variable

!===========================================================

program NewtonRaphson

implicit none

real(8), parameter :: rho = 15.0D0

real(8), parameter :: eps = 1D-6

integer, parameter :: NMAX = 1000

real(8) :: x0, x1, err, g, gp

integer :: i

print *, ’Enter x0: ’

read *, x0

err = 1.0D0

print *,’iter x error ’

print *,’-------------------------------------------------’

print *, 0,x0,err

do i=1,NMAX

!value of function g(x):

g = x0*tan(x0)-sqrt(rho*rho-x0*x0)

!value of the derivative g’(x):

gp = x0/sqrt(rho*rho-x0*x0)+x0/(cos(x0)**2)+tan(x0)

x1 = x0 - g/gp

err = ABS(x1-x0)

print *,i,x1,err

if(err .lt. eps) exit

x0 = x1

enddo

end program NewtonRaphson

In the program listed above, the user is asked to set the initial point . We ﬁx rho . It is instructive to make the plot of the left and right hand sides of (3.24) and make a graphical determination of the roots from their intersections. Then we can make appropriate choices of the initial point . Using gnuplot, the plots are made with the commands:

gnuplot> g1(x) = x*tan(x)

gnuplot> g2(x) = sqrt(rho*rho-x*x)

gnuplot> plot [0:20][0:20] g1(x), g2(x)

gnuplot> g2(x) = sqrt(rho*rho-x*x)

gnuplot> plot [0:20][0:20] g1(x), g2(x)

The compilation and running of the program can be done as follows:

> gfortran nr.f90 -o n

> echo "1.4"|./n

Enter x0:

iter x error

-------------------------------------------------

0 1.3999999999999999 1.0000000000000000

1 1.5254292024457967 0.12542920244579681

2 1.5009739120496131 2.4455290396183660E-002

3 1.4807207017202200 2.0253210329393090E-002

4 1.4731630533073483 7.5576484128716537E-003

5 1.4724779331237687 6.8512018357957949E-004

6 1.4724731072313519 4.8258924167932093E-006

7 1.4724731069952235 2.3612845012621619E-010

> echo "1.4"|./n

Enter x0:

iter x error

-------------------------------------------------

0 1.3999999999999999 1.0000000000000000

1 1.5254292024457967 0.12542920244579681

2 1.5009739120496131 2.4455290396183660E-002

3 1.4807207017202200 2.0253210329393090E-002

4 1.4731630533073483 7.5576484128716537E-003

5 1.4724779331237687 6.8512018357957949E-004

6 1.4724731072313519 4.8258924167932093E-006

7 1.4724731069952235 2.3612845012621619E-010

We conclude that one of the roots of the equation is . The reader can compute more of these roots by following these steps by herself.

The method discussed above can be easily generalized to the case of two equations. Suppose that we need to solve simultaneously two algebraic equations and . In order to compute a sequence , , , , , that may converge to a root of the above system of equations, we Taylor expand the two functions around

Deﬁning and and setting , , we obtain This is a linear system of equations where and , with . Solving for we obtain The iterations stop when become small enough.As an example, consider the equations with , . We have , , , . The program can be found in the ﬁle nr2.f90:

!===========================================================

!Newton Raphson of two functions of two variables

!===========================================================

program NewtonRaphson2

implicit none

real(8), parameter :: eps = 1D-6

integer, parameter :: NMAX = 1000

real(8) :: A(2,2),b(2),dx(2)

real(8) :: x,y, err

integer :: i

print *, ’Enter x0,y0: ’

read *, x,y

err = 1.0D0

print *,’iter x y error ’

print *,’-------------------------------------------------’

print *, 0,x,y,err

do i=1,NMAX

b(1) = -(2.0D0*x*x-3.0D0*x*y+y-2.0D0) ! -g1(x,y)

b(2) = -(3.0D0*x + x*y + y - 1.0D0) ! -g2(x,y)

! dg1/dx dg1/dy

A(1,1) = 4.0D0*x-3.0D0*y; A(1,2) = 1.0D0-3.0D0*x

! dg2/dx dg2/dy

A(2,1) = 3.0D0+y ; A(2,2) = 1.0D0+x

call solve2x2(A,B,dx)

x = x + dx(1)

y = y + dx(2)

err = 0.5D0*SQRT(dx(1)**2+dx(2)**2)

print *,i,x,y,err

if(err .lt. eps) exit

enddo

end program NewtonRaphson2

!===========================================================

subroutine solve2x2(A,b,dx)

implicit none

real(8) :: A(2,2),b(2),dx(2)

real(8) :: num1,num2,det

num1 = A(2,2)*b(1)-A(1,2)*b(2)

num2 = A(1,1)*b(2)-A(2,1)*b(1)

det = A(1,1)*A(2,2)-A(1,2)*A(2,1)

if(det .eq. 0.0D0) stop ’solve2x2: det=0’

dx(1)= num1/det

dx(2)= num2/det

end subroutine solve2x2

!Newton Raphson of two functions of two variables

!===========================================================

program NewtonRaphson2

implicit none

real(8), parameter :: eps = 1D-6

integer, parameter :: NMAX = 1000

real(8) :: A(2,2),b(2),dx(2)

real(8) :: x,y, err

integer :: i

print *, ’Enter x0,y0: ’

read *, x,y

err = 1.0D0

print *,’iter x y error ’

print *,’-------------------------------------------------’

print *, 0,x,y,err

do i=1,NMAX

b(1) = -(2.0D0*x*x-3.0D0*x*y+y-2.0D0) ! -g1(x,y)

b(2) = -(3.0D0*x + x*y + y - 1.0D0) ! -g2(x,y)

! dg1/dx dg1/dy

A(1,1) = 4.0D0*x-3.0D0*y; A(1,2) = 1.0D0-3.0D0*x

! dg2/dx dg2/dy

A(2,1) = 3.0D0+y ; A(2,2) = 1.0D0+x

call solve2x2(A,B,dx)

x = x + dx(1)

y = y + dx(2)

err = 0.5D0*SQRT(dx(1)**2+dx(2)**2)

print *,i,x,y,err

if(err .lt. eps) exit

enddo

end program NewtonRaphson2

!===========================================================

subroutine solve2x2(A,b,dx)

implicit none

real(8) :: A(2,2),b(2),dx(2)

real(8) :: num1,num2,det

num1 = A(2,2)*b(1)-A(1,2)*b(2)

num2 = A(1,1)*b(2)-A(2,1)*b(1)

det = A(1,1)*A(2,2)-A(1,2)*A(2,1)

if(det .eq. 0.0D0) stop ’solve2x2: det=0’

dx(1)= num1/det

dx(2)= num2/det

end subroutine solve2x2

In order to guess the region where the real roots of the systems lie, we make a 3-dimensional plot using gnuplot:

gnuplot> set isosamples 20

gnuplot> set hidden3d

gnuplot> splot 2*x**2-3*x*y+y-2,3*x+y*x+y-1,0

gnuplot> set hidden3d

gnuplot> splot 2*x**2-3*x*y+y-2,3*x+y*x+y-1,0

We plot the functions together with the plane . The intersection of the three surfaces determine the roots we are looking for. Compiling and running the program can be done by using the commands:

> gfortran nr2.f90 -o n

> echo 2.2 1.5 |./n

Enter x0,y0:

iter x y error

--------------------------------------

0 2.20000000 1.50000000 1.0000

1 0.76427104 0.26899383 0.9456

2 0.73939531 -0.68668275 0.4780

3 0.74744506 -0.71105605 1.2834E-002

4 0.74735933 -0.71083147 1.2019E-004

5 0.74735932 -0.71083145 1.2029E-008

> echo 0 1 |./n

.................

5 -0.10899022 1.48928857 4.3461E-012

> echo -5 0|./n

6 -6.13836909 -3.77845711 3.2165E-013

> echo 2.2 1.5 |./n

Enter x0,y0:

iter x y error

--------------------------------------

0 2.20000000 1.50000000 1.0000

1 0.76427104 0.26899383 0.9456

2 0.73939531 -0.68668275 0.4780

3 0.74744506 -0.71105605 1.2834E-002

4 0.74735933 -0.71083147 1.2019E-004

5 0.74735932 -0.71083145 1.2029E-008

> echo 0 1 |./n

.................

5 -0.10899022 1.48928857 4.3461E-012

> echo -5 0|./n

6 -6.13836909 -3.77845711 3.2165E-013

The computation above leads to the roots , , .

The Newton-Raphson method for many variables becomes hard quite soon: One needs to calculate the functions as well as their derivatives, which is prohibitively expensive for many problems. It is also hard to determine the roots, since the method converges satisfactorily only very close to the roots. We refer the reader to [8] for more information on how one can deal with these problems.

In order to determine the bifurcation points for we will solve the algebraic equations and . At these points, -cycles become unstable and -cycles appear and are stable. This happens when , where . We will look for solutions for .

We deﬁne the functions and as in equation (3.6) . We will solve the algebraic equations:

According to the discussion of the previous section, in order to calculate the roots of these equations we have to solve the linear system (3.28) , where the coeﬃcients are The derivatives will be calculated approximately using ﬁnite diﬀerences and similarly for the second derivatives We are now ready to write the program for the Newton-Raphson method like in the previous section. The only diﬀerence is the approximate calculation of the derivatives using the relations above and the calculation of the function by a routine that will compose the function -times. The program can be found in the ﬁle bifurcationPoints.f90:
!===========================================================

! bifurcationPoints.f

! Calculate bifurcation points of the discrete logistic map

! at period k by solving the condition

! g1(x,r) = x - F(k,x,r) = 0

! g2(x,r) = dF(k,x,r)/dx+1 = 0

! determining when the Floquet multiplier becomes 1

! F(k,x,r) iterates F(x,r) = r*x*(x-1) k times

! The equations are solved by using a Newton-Raphson method

!===========================================================

program bifurcationPoints

implicit none

real(8),parameter :: tol=1.0D-10

integer :: k,iter

real(8) :: r0,x0

real(8) :: A(2,2),B(2),dX(2)

real(8) :: error

real(8) :: F,dFdx,dFdr,d2Fdx2,d2Fdrdx

! ---- Input:

print *,’# Enter k,r0,x0:’

read *,k,r0,x0

print *,’# Period k= ’,k

print *,’# r0= ’,r0,’ x0= ’,x0

! ---- Initialize

error = 1.0D0 !initial large value of error>tol

iter = 0

do while(error .gt. tol)

! ---- Calculate jacobian matrix

A(1,1) = 1.0D0-dFdx(k,x0,r0)

A(1,2) = -dFdr (k,x0,r0)

A(2,1) = d2Fdx2 (k,x0,r0)

A(2,2) = d2Fdrdx (k,x0,r0)

B(1) = -x0 + F(k,x0,r0)

B(2) = -dFdx (k,x0,r0)-1.0D0

! ---- Solve a 2x2 linear system:

call solve2x2(A,B,dX)

x0 = x0 + dX(1)

r0 = r0 + dX(2)

error = 0.5D0*sqrt(dX(1)**2+dX(2)**2)

iter = iter+1

print*,iter,’x0= ’,x0,’ r0= ’,r0,’ err=’,error

enddo !do while(error .gt. tol)

end program bifurcationPoints

!===========================================================

!Function F(k,x,r) and its derivatives

real(8) function F(k,x,r)

implicit none

real(8) :: x,r,x0

integer k,i

x0 = x

do i=1,k

x0 = r*x0*(1.0D0-x0)

enddo

F = x0

end function F

! ----------------------------------

real(8) function dFdx(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*x

dFdx = (F(k,x+eps,r)-F(k,x-eps,r))/(2.0D0*eps)

end function dFdx

! ----------------------------------

real(8) function dFdr(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*r

dFdr = (F(k,x,r+eps)-F(k,x,r-eps))/(2.0D0*eps)

end function dFdr

! ----------------------------------

real(8) function d2Fdx2(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*x

d2Fdx2 = (F(k,x+eps,r)-2.0D0*F(k,x,r)+F(k,x-eps,r))/(eps*eps)

end function d2Fdx2

! ----------------------------------

real(8) function d2Fdrdx(k,x,r)

implicit none

real(8) :: x,r,epsx,epsr

real(8) :: F

integer k

epsx = 1.0D-6*x

epsr = 1.0D-6*r

d2Fdrdx = (F(k,x+epsx,r+epsr)-F(k,x+epsx,r-epsr) &

-F(k,x-epsx,r+epsr)+F(k,x-epsx,r-epsr)) &

/(4.0D0*epsx*epsr)

end function d2Fdrdx

!===========================================================

subroutine solve2x2(A,b,dx)

implicit none

real(8) :: A(2,2),b(2),dx(2)

real(8) :: num1,num2,det

num1 = A(2,2)*b(1) - A(1,2)*b(2)

num2 = A(1,1)*b(2) - A(2,1)*b(1)

det = A(1,1)*A(2,2)- A(1,2)*A(2,1)

if(det .eq. 0.0D0) stop ’solve2x2: det = 0’

dx(1) = num1/det

dx(2) = num2/det

end subroutine solve2x2

! bifurcationPoints.f

! Calculate bifurcation points of the discrete logistic map

! at period k by solving the condition

! g1(x,r) = x - F(k,x,r) = 0

! g2(x,r) = dF(k,x,r)/dx+1 = 0

! determining when the Floquet multiplier becomes 1

! F(k,x,r) iterates F(x,r) = r*x*(x-1) k times

! The equations are solved by using a Newton-Raphson method

!===========================================================

program bifurcationPoints

implicit none

real(8),parameter :: tol=1.0D-10

integer :: k,iter

real(8) :: r0,x0

real(8) :: A(2,2),B(2),dX(2)

real(8) :: error

real(8) :: F,dFdx,dFdr,d2Fdx2,d2Fdrdx

! ---- Input:

print *,’# Enter k,r0,x0:’

read *,k,r0,x0

print *,’# Period k= ’,k

print *,’# r0= ’,r0,’ x0= ’,x0

! ---- Initialize

error = 1.0D0 !initial large value of error>tol

iter = 0

do while(error .gt. tol)

! ---- Calculate jacobian matrix

A(1,1) = 1.0D0-dFdx(k,x0,r0)

A(1,2) = -dFdr (k,x0,r0)

A(2,1) = d2Fdx2 (k,x0,r0)

A(2,2) = d2Fdrdx (k,x0,r0)

B(1) = -x0 + F(k,x0,r0)

B(2) = -dFdx (k,x0,r0)-1.0D0

! ---- Solve a 2x2 linear system:

call solve2x2(A,B,dX)

x0 = x0 + dX(1)

r0 = r0 + dX(2)

error = 0.5D0*sqrt(dX(1)**2+dX(2)**2)

iter = iter+1

print*,iter,’x0= ’,x0,’ r0= ’,r0,’ err=’,error

enddo !do while(error .gt. tol)

end program bifurcationPoints

!===========================================================

!Function F(k,x,r) and its derivatives

real(8) function F(k,x,r)

implicit none

real(8) :: x,r,x0

integer k,i

x0 = x

do i=1,k

x0 = r*x0*(1.0D0-x0)

enddo

F = x0

end function F

! ----------------------------------

real(8) function dFdx(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*x

dFdx = (F(k,x+eps,r)-F(k,x-eps,r))/(2.0D0*eps)

end function dFdx

! ----------------------------------

real(8) function dFdr(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*r

dFdr = (F(k,x,r+eps)-F(k,x,r-eps))/(2.0D0*eps)

end function dFdr

! ----------------------------------

real(8) function d2Fdx2(k,x,r)

implicit none

real(8) :: x,r,eps

real(8) :: F

integer k

eps = 1.0D-6*x

d2Fdx2 = (F(k,x+eps,r)-2.0D0*F(k,x,r)+F(k,x-eps,r))/(eps*eps)

end function d2Fdx2

! ----------------------------------

real(8) function d2Fdrdx(k,x,r)

implicit none

real(8) :: x,r,epsx,epsr

real(8) :: F

integer k

epsx = 1.0D-6*x

epsr = 1.0D-6*r

d2Fdrdx = (F(k,x+epsx,r+epsr)-F(k,x+epsx,r-epsr) &

-F(k,x-epsx,r+epsr)+F(k,x-epsx,r-epsr)) &

/(4.0D0*epsx*epsr)

end function d2Fdrdx

!===========================================================

subroutine solve2x2(A,b,dx)

implicit none

real(8) :: A(2,2),b(2),dx(2)

real(8) :: num1,num2,det

num1 = A(2,2)*b(1) - A(1,2)*b(2)

num2 = A(1,1)*b(2) - A(2,1)*b(1)

det = A(1,1)*A(2,2)- A(1,2)*A(2,1)

if(det .eq. 0.0D0) stop ’solve2x2: det = 0’

dx(1) = num1/det

dx(2) = num2/det

end subroutine solve2x2

Compiling and running the program can be done as follows:

> gfortran bifurcationPoints.f90 -o b

> echo 2 3.5 0.5 |./b

# Enter k,r0,x0:

# Period k= 2

# r0= 3.5000000000000 x0= 0.50000000000

1 x0= 0.4455758353187 r0= 3.38523275827 err= 6.35088E-002

2 x0= 0.4396562547624 r0= 3.45290970406 err= 3.39676E-002

3 x0= 0.4399593001407 r0= 3.44949859951 err= 1.71226E-003

4 x0= 0.4399601690333 r0= 3.44948974267 err= 4.44967E-006

5 x0= 0.4399601689937 r0= 3.44948974281 err= 7.22160E-011

> echo 2 3.5 0.85 | ./b

.................

4 x0= 0.8499377795512 r0= 3.44948974275 err= 1.85082E-011

> echo 4 3.5 0.5 |./b

.................

5 x0= 0.5235947861540 r0= 3.54409035953 err= 1.86318E-011

> echo 4 3.5 0.35 | ./b

.................

5 x0= 0.3632903374118 r0= 3.54409035955 err= 5.91653E-013

> echo 2 3.5 0.5 |./b

# Enter k,r0,x0:

# Period k= 2

# r0= 3.5000000000000 x0= 0.50000000000

1 x0= 0.4455758353187 r0= 3.38523275827 err= 6.35088E-002

2 x0= 0.4396562547624 r0= 3.45290970406 err= 3.39676E-002

3 x0= 0.4399593001407 r0= 3.44949859951 err= 1.71226E-003

4 x0= 0.4399601690333 r0= 3.44948974267 err= 4.44967E-006

5 x0= 0.4399601689937 r0= 3.44948974281 err= 7.22160E-011

> echo 2 3.5 0.85 | ./b

.................

4 x0= 0.8499377795512 r0= 3.44948974275 err= 1.85082E-011

> echo 4 3.5 0.5 |./b

.................

5 x0= 0.5235947861540 r0= 3.54409035953 err= 1.86318E-011

> echo 4 3.5 0.35 | ./b

.................

5 x0= 0.3632903374118 r0= 3.54409035955 err= 5.91653E-013

The above listing shows the points of the 2-cycle and some of the points of the 4-cycle. It is also possible to compare the calculated value with the expected one . Improving the accuracy of the calculation is left as an exercise for the reader who has to control the systematic errors of the calculations and achieve better accuracy in the computation of .

We have seen that when , the trajectories of the logistic map become non periodic and exhibit chaotic behavior. Chaotic behavior mostly means sensitivity of the evolution of a dynamical system to the choice of initial conditions. More precisely, it means that two diﬀerent trajectories constructed from inﬁnitesimally close initial conditions, diverge very fast from each other. This implies that there is a set of initial conditions that densely cover subintervals of whose trajectories do not approach arbitrarily close to any cycle of ﬁnite length.

Assume that two trajectories have , as initial points and . When the points , have a distance that for small enough increases exponentially with (the “time”), i.e.

| (3.34) |

the system is most likely exhibiting chaotic
behavior^{11} .
The exponent is called a Liapunov exponent. A useful equation for the
calculation of is

| (3.35) |

This relation can be easily proved by considering inﬁnitesimal so that . Then we obtain

We can show by induction that and by taking the logarithm and the limits we can prove (3.35) .A ﬁrst attempt to calculate the Liapunov exponents could be made by using the deﬁnition (3.34) . We modify the program logistic.f90 so that it calculates two trajectories whose initial distance is epsilon:

!===========================================================

!Discrete Logistic Map:

!Two trajectories with close initial conditions.

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1,x0t,x1t,epsilon

! ----- Input:

print *,’# Enter NSTEPS, r, x0, epsilon:’

read *,NSTEPS,r,x0,epsilon

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

print *,’# epsilon = ’,epsilon

x0t = x0+epsilon

! ----- Initialize:

open(unit=33,file=’lia.dat’)

write(33,*) 1,x0,x0t,ABS(x0t-x0)/epsilon

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

x1t = r * x0t * (1.0D0-x0t)

write(33,*)i,x1,x1t,ABS(x1t-x1)/epsilon

x0 = x1; x0t = x1t

enddo

close(33)

end program logistic_map

!Discrete Logistic Map:

!Two trajectories with close initial conditions.

!===========================================================

program logistic_map

implicit none

integer :: NSTEPS,i

real(8) :: r,x0,x1,x0t,x1t,epsilon

! ----- Input:

print *,’# Enter NSTEPS, r, x0, epsilon:’

read *,NSTEPS,r,x0,epsilon

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

print *,’# epsilon = ’,epsilon

x0t = x0+epsilon

! ----- Initialize:

open(unit=33,file=’lia.dat’)

write(33,*) 1,x0,x0t,ABS(x0t-x0)/epsilon

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

x1t = r * x0t * (1.0D0-x0t)

write(33,*)i,x1,x1t,ABS(x1t-x1)/epsilon

x0 = x1; x0t = x1t

enddo

close(33)

end program logistic_map

After running the program, the quantity is found at the fourth column of the ﬁle lia.dat. The curves of ﬁgure 3.7 can be constructed by using the commands:

> gfortran liapunov1.f90 -o l

> gnuplot

gnuplot> set logscale y

gnuplot> plot \

"<echo 200 3.6 0.2 1e-15 |./l;cat lia.dat" u 1:4 w l

> gnuplot

gnuplot> set logscale y

gnuplot> plot \

"<echo 200 3.6 0.2 1e-15 |./l;cat lia.dat" u 1:4 w l

The last line plots the stdout of the command "echo 200 3.6 0.2 1e-15 |./l;cat lia.dat", i.e. the contents of the ﬁle lia.dat produced after running our program using the parameters NSTEPS , r , x0 and epsilon . The gnuplot command set logscale y, puts the y axis in a logarithmic scale. Therefore an exponential function is shown as a straight line and this is what we see in ﬁgure 3.7: The points tend to lie on a straight line as decreases. The slopes of these lines are equal to the Liapunov exponent . Deviations from the straight line behavior indicates corrections and systematic errors, as we point out in ﬁgure 3.7. A diﬀerent initial condition results in a slightly diﬀerent value of , and the true value can be estimated as the average over several such choices. We estimate the error of our computation from the standard error of the mean. The reader should perform such a computation as an exercise.

One can perform a ﬁt of the points to the exponential function in the following way: Since , we can make a ﬁt to a straight line instead. Using gnuplot, the relevant commands are:

gnuplot> fit [5:53] a*x+b \

"<echo 500 3.6 0.2 1e-15 |./l;cat lia.dat"\

using 1:(log($4)) via a,b

gnuplot> replot exp(a*x+b)

"<echo 500 3.6 0.2 1e-15 |./l;cat lia.dat"\

using 1:(log($4)) via a,b

gnuplot> replot exp(a*x+b)

The command shown above ﬁts the data to the function a*x+b by taking the 1st column and the logarithm of the 4th column (using 1:(log($4))) of the stdout of the command that we used for creating the previous plot. We choose data for which ([5:53]) and the ﬁtting parameters are a,b (via a,b). The second line, adds the ﬁtted function to the plot.

Now we are going to use equation (3.35) for calculating . This equation is approximately correct when (a) we have already reached the steady state and (b) in the large limit. For this reason we should study if we obtain a satisfactory convergence when we (a) “throw away” a number of NTRANS steps, (b) calculate the sum (3.35) for increasing NSTEPS= (c) calculate the sum (3.35) for many values of the initial point . This has to be carefully repeated for all values of since each factor will contribute diﬀerently to the quality of the convergence: In regions that manifest chaotic behavior (large ) convergence will be slower. The program can be found in the ﬁle liapunov2.f90:

!===========================================================

!Discrete Logistic Map:

!Liapunov exponent from sum_i ln|f’(x_i)|

! NTRANS: number of discarded iteration in order to discard

! transient behavior

! NSTEPS: number of terms in the sum

!===========================================================

program logistic_map

implicit none

integer :: NTRANS,NSTEPS,i

real(8) :: r,x0,x1,sum

! ----- Input:

print *,’# Enter NTRANS,NSTEPS, r, x0:’

read *,NTRANS,NSTEPS,r,x0

print *,’# NTRANS = ’,NTRANS

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

sum = log(ABS(r*(1.0D0-2.0D0*x0)))

! ----- Initialize:

open(unit=33,file=’lia.dat’)

write(33,*) 1,x0,sum

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

sum = sum + log(ABS(r*(1.0D0-2.0D0*x1)))

write(33,*)i,x1,sum/i

x0 = x1

enddo

close(33)

end program logistic_map

!Discrete Logistic Map:

!Liapunov exponent from sum_i ln|f’(x_i)|

! NTRANS: number of discarded iteration in order to discard

! transient behavior

! NSTEPS: number of terms in the sum

!===========================================================

program logistic_map

implicit none

integer :: NTRANS,NSTEPS,i

real(8) :: r,x0,x1,sum

! ----- Input:

print *,’# Enter NTRANS,NSTEPS, r, x0:’

read *,NTRANS,NSTEPS,r,x0

print *,’# NTRANS = ’,NTRANS

print *,’# NSTEPS = ’,NSTEPS

print *,’# r = ’,r

print *,’# x0 = ’,x0

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

sum = log(ABS(r*(1.0D0-2.0D0*x0)))

! ----- Initialize:

open(unit=33,file=’lia.dat’)

write(33,*) 1,x0,sum

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

sum = sum + log(ABS(r*(1.0D0-2.0D0*x1)))

write(33,*)i,x1,sum/i

x0 = x1

enddo

close(33)

end program logistic_map

After NTRANS steps, the program calculates NSTEPS times the sum of the terms . At each step the sum divided by the number of steps i is printed to the ﬁle lia.dat. Figure 3.6 shows the results for . This is a point where the system exhibits strong chaotic behavior and convergence is achieved after we compute a large number of steps. Using NTRANS and NSTEPS the achieved accuracy is about % with . The main contribution to the error comes from the diﬀerent paths followed by each initial point chosen. The plot can be constructed with the gnuplot commands:

> gfortran liapunov2.f90 -o l

> gnuplot

gnuplot> plot \

"<echo 2000 70000 3.8 0.20 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.35 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.50 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.75 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.90 |./l;cat lia.dat" u 1:3 w l

> gnuplot

gnuplot> plot \

"<echo 2000 70000 3.8 0.20 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.35 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.50 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.75 |./l;cat lia.dat" u 1:3 w l,\

"<echo 2000 70000 3.8 0.90 |./l;cat lia.dat" u 1:3 w l

The plot command runs the program using the parameters NTRANS , NSTEPS , r and x0 and plots the results from the contents of the ﬁle lia.dat.

In order to determine the regions of chaotic behavior we have to study the dependence of the Liapunov exponent on the value of . Using our experience coming from the careful computation of before, we will run the program for several values of using the parameters NTRANS , NSTEPS from the initial point x0 . This calculation gives accuracy of the order of %. If we wish to measure carefully and estimate the error of the results, we have to follow the steps described in ﬁgures 3.7 and 3.8. The program can be found in the ﬁle liapunov3.f90 and it is a simple modiﬁcation of the previous program so that it can perform the calculation for many values of .

!===========================================================

!Discrete Logistic Map:

!Liapunov exponent from sum_i ln|f’(x_i)|

!Calculation for r in [rmin,rmax] with RSTEPS steps

! RSTEPS: values or r studied: r=rmin+(rmax-rmin)/RSTEPS

! NTRANS: number of discarded iteration in order to discard

! transient behavior

! NSTEPS: number of terms in the sum

! xstart: value of initial x0 for every r

!===========================================================

program logistic_map

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

real(8),parameter :: xstart = 0.2D0

integer,parameter :: RSTEPS = 1000

integer,parameter :: NSTEPS = 60000

integer,parameter :: NTRANS = 2000

integer :: i,ir

real(8) :: r,x0,x1,sum,dr

open(unit=33,file=’lia.dat’)

dr = (rmax-rmin)/(RSTEPS-1)

do ir=0,RSTEPS-1

r = rmin+ir*dr

x0= xstart

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

sum = log(ABS(r*(1.0D0-2.0D0*x0)))

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

sum = sum + log(ABS(r*(1.0D0-2.0D0*x1)))

x0 = x1

enddo

write(33,*)r,sum/NSTEPS

enddo !do ir=0,RSTEPS-1

close(33)

end program logistic_map

!Discrete Logistic Map:

!Liapunov exponent from sum_i ln|f’(x_i)|

!Calculation for r in [rmin,rmax] with RSTEPS steps

! RSTEPS: values or r studied: r=rmin+(rmax-rmin)/RSTEPS

! NTRANS: number of discarded iteration in order to discard

! transient behavior

! NSTEPS: number of terms in the sum

! xstart: value of initial x0 for every r

!===========================================================

program logistic_map

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

real(8),parameter :: xstart = 0.2D0

integer,parameter :: RSTEPS = 1000

integer,parameter :: NSTEPS = 60000

integer,parameter :: NTRANS = 2000

integer :: i,ir

real(8) :: r,x0,x1,sum,dr

open(unit=33,file=’lia.dat’)

dr = (rmax-rmin)/(RSTEPS-1)

do ir=0,RSTEPS-1

r = rmin+ir*dr

x0= xstart

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

sum = log(ABS(r*(1.0D0-2.0D0*x0)))

! ----- Calculate:

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

sum = sum + log(ABS(r*(1.0D0-2.0D0*x1)))

x0 = x1

enddo

write(33,*)r,sum/NSTEPS

enddo !do ir=0,RSTEPS-1

close(33)

end program logistic_map

The program can be compiled and run using the commands:

> gfortran liapunov3.f90 -o l

> ./l &

> ./l &

The character & makes the program ./l to run in the background. This is recommended for programs that run for a long time, so that the shell returns the prompt to the user and the program continues to run even after the shell is terminated.

The data are saved in the ﬁle lia.dat and we can make the plot shown in ﬁgure 3.7 using gnuplot:

gnuplot> plot "lia.dat" with lines notitle ,0 notitle

Now we can compare ﬁgure 3.9 with the bifurcation diagram shown in ﬁgure 3.4. The intervals with correspond to stable -cycles. The intervals where correspond to manifestation of strong chaos. These intervals are separated by points with where the system exhibits weak chaos. This means that neighboring trajectories diverge from each other with a power law instead of an exponential, where is a positive exponent that needs to be determined. The parameter is the one usually used in the literature. Strong chaos is obtained in the limit. For larger , switching between chaotic and stable periodic trajectories is observed each time changes sign. The critical values of can be computed with relatively high accuracy by restricting the calculation to a small enough neighborhood of the critical point. You can do this using the program listed above by setting the parameters rmin and rmax.

We can also study the chaotic properties of the trajectories of the logistic map by computing the distribution of the values of in the interval . After the transitional period, the distribution for the cycles will have support only at the points of the cycles, whereas for the chaotic regimes it will have support on subintervals of . The distribution function is independent for most of the initial points of the trajectories. If one obtains a large number of points from many trajectories of the logistic map, it will be practically impossible to understand that these are produced by a deterministic rule. For this reason, chaotic systems can be used for the production of pseudorandom numbers, as we will see in chapter 11. By measuring the entropy, which is a measure of disorder in a system, we can quantify the “randomness” of the distribution. As we will see in chapter 12, it is given by the equation

| (3.37) |

where is the probability of observing the state . In our case, we can make an approximate calculation of by dividing the interval to subintervals of width . For given we obtain a large number of values of the logistic map and we compute the histogram of their distribution in the intervals . The probability density is obtained from the limit of as becomes large and small (large ). Indeed, converges to . We will deﬁne .

The program listed below calculates for chosen values of , and then the entropy is calculated using (3.37) . It is a simple modiﬁcation of the program in liapunov3.f90 where we add the parameter NHIST counting the number of intervals for the histograms. The probability density is calculated in the array p(NHIST). The program can be found in the ﬁle entropy.f90:

!===========================================================

!Discrete Logistic Map:

!Entropy calculation from S=-sum_i p_i ln p_i

!Calculation for r in [rmin,rmax] with RSTEPS steps

! RSTEPS: values or r studied: r=rmin+(rmax-rmin)/RSTEPS

! NHIST : number of histogram bins for calculation of p_i

! NSTEPS: number of values of x in the histograms

! NTRANS: number of discarted iteration in order to discard

! transient behavior

! xstart: value of initial x0 for every r

!===========================================================

program logistic_map

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

real(8),parameter :: xstart = 0.2D0

integer,parameter :: RSTEPS = 1000

integer,parameter :: NHIST = 10000

integer,parameter :: NTRANS = 2000

integer,parameter :: NSTEPS = 5000000

real(8),parameter :: xmin=0.0D0,xmax=1.0D0

integer :: i,ir,isum,n

real(8) :: r,x0,x1,sum,dr,dx

real(8) :: p(NHIST),S

open(unit=33,file=’entropy.dat’)

p = 0.0D0

dr = (rmax-rmin)/(RSTEPS-1)

dx = (xmax-xmin)/(NHIST -1)

do ir=0,RSTEPS-1

r = rmin+ir*dr

x0= xstart

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

!make histogram:

n=INT(x0/dx)+1;p(n)=p(n)+1.0D0

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

n = INT(x1/dx)+1

p(n)=p(n)+1.0D0

x0 = x1

enddo

!p(k) is now histogram of x-values.

!Normalize so that sum_k p(k)*dx=1

!to get probability distribution:

p = p/NSTEPS/dx

!sum all non zero terms: p(n)*log(p(n))*dx

S = -SUM(p*log(p),MASK=p.gt.0.0D0)*dx

write(33,*)r,S

enddo !do ir=0,RSTEPS-1

close(33)

!print the last probability distribution:

open(unit=34,file=’entropy_hist.dat’)

do n=1,NHIST

x0 = xmin +(n-1)*dx + 0.5D0*dx

write(34,*) r,x0,p(n)

enddo

close(34)

end program logistic_map

!Discrete Logistic Map:

!Entropy calculation from S=-sum_i p_i ln p_i

!Calculation for r in [rmin,rmax] with RSTEPS steps

! RSTEPS: values or r studied: r=rmin+(rmax-rmin)/RSTEPS

! NHIST : number of histogram bins for calculation of p_i

! NSTEPS: number of values of x in the histograms

! NTRANS: number of discarted iteration in order to discard

! transient behavior

! xstart: value of initial x0 for every r

!===========================================================

program logistic_map

implicit none

real(8),parameter :: rmin = 2.5D0

real(8),parameter :: rmax = 4.0D0

real(8),parameter :: xstart = 0.2D0

integer,parameter :: RSTEPS = 1000

integer,parameter :: NHIST = 10000

integer,parameter :: NTRANS = 2000

integer,parameter :: NSTEPS = 5000000

real(8),parameter :: xmin=0.0D0,xmax=1.0D0

integer :: i,ir,isum,n

real(8) :: r,x0,x1,sum,dr,dx

real(8) :: p(NHIST),S

open(unit=33,file=’entropy.dat’)

p = 0.0D0

dr = (rmax-rmin)/(RSTEPS-1)

dx = (xmax-xmin)/(NHIST -1)

do ir=0,RSTEPS-1

r = rmin+ir*dr

x0= xstart

do i=1,NTRANS

x1 = r * x0 * (1.0D0-x0 )

x0 = x1

enddo

!make histogram:

n=INT(x0/dx)+1;p(n)=p(n)+1.0D0

do i=2,NSTEPS

x1 = r * x0 * (1.0D0-x0 )

n = INT(x1/dx)+1

p(n)=p(n)+1.0D0

x0 = x1

enddo

!p(k) is now histogram of x-values.

!Normalize so that sum_k p(k)*dx=1

!to get probability distribution:

p = p/NSTEPS/dx

!sum all non zero terms: p(n)*log(p(n))*dx

S = -SUM(p*log(p),MASK=p.gt.0.0D0)*dx

write(33,*)r,S

enddo !do ir=0,RSTEPS-1

close(33)

!print the last probability distribution:

open(unit=34,file=’entropy_hist.dat’)

do n=1,NHIST

x0 = xmin +(n-1)*dx + 0.5D0*dx

write(34,*) r,x0,p(n)

enddo

close(34)

end program logistic_map

For the calculation of the distribution functions and the entropy we have to choose the parameters which control the systematic error. The parameter NTRANS should be large enough so that the transitional behavior will not contaminate our results. Our measurements must be checked for being independent of its value. The same should be done for the initial point xstart. The parameter NHIST controls the partitioning of the interval and the width , so it should be large enough. The parameter NSTEPS is the number of “measurements” for each value of and it should be large enough in order to reduce the “noise” in . It is obvious that NSTEPS should be larger when becomes smaller. Appropriate choices lead to the plots shown in ﬁgures 3.10 and 3.11 for , and . We see that stronger chaotic behavior means a wider distribution of the values of .

The entropy is shown in ﬁgure 3.12. The stable periodic trajectories lead to small entropy, whereas the chaotic ones lead to large entropy. There is a sudden increase in the value of the entropy at the beginning of chaos at , which increases even further as the chaotic behavior becomes stronger. During the intermissions of the chaotic behavior there are sudden drops in the value of the entropy. It is quite instructive to compare the entropy diagrams with the corresponding bifurcation diagrams (see ﬁgure 3.4) and the Liapunov exponent diagrams (see ﬁgure 3.9). The entropy is increasing until reaches its maximum value 4, but this is not done smoothly. By magnifying the corresponding areas in the plot, we can see an inﬁnite number of sudden drops in the entropy in intervals of that become more and more narrow.

Several of the programs that you need to write for solving the problems of this chapter can be found in the Problems directory of the accompanying software of this chapter.

- Conﬁrm that the trajectories of the logistic map for are falling oﬀ
exponentially for large enough .
- Choose and plot the trajectories for with step for . Put the axis in a logarithmic scale. From the resulting curves discuss whether you obtain an exponential falloﬀ.
- Fit the points for to the function and determine
the ﬁtting parameters and . How do these parameters depend on
the initial point ? You can use the following gnuplot commands for
your calculation:
gnuplot> !gfortran logistic.f90 -o l

gnuplot> a=0.7;c=0.4;

gnuplot> fit [10:] c*exp(-a*x) \

"<echo 1000 0.5 0.5|./l;cat log.dat" via a,c

gnuplot> plot c*exp(-a*x),\

"<echo 1000 0.5 0.5|./l;cat log.dat" w lAs you can see, we set NSTEPS = 1000, r = 0.5, x0 = 0.5. By setting the limits [10:] to the fit command, the ﬁt includes only the points , therefore avoiding the transitional period and the deviation from the exponential falloﬀ for small .

- Repeat for with step and for . As you will be approaching , you will need to discard more points from near the origin. You might also need to increase NSTEPS. You should always check graphically whether the ﬁtted exponential function is a good ﬁt to the points for large . Construct a table for the values of as a function of .

The solutions of the equation (3.3) is . How is this related to the values that you computed in your table?

- Consider the logistic map for . Choose NSTEPS=100 and calculate the corresponding trajectories for x0=0.2, 0.3, 0.5, 0.7, 0.9. Plot them on the same graph. Calculate the ﬁxed point and compare your result to the known value . Repeat for x0= for . What do you conclude about the point ?
- Consider the logistic map for . Calculate the stable point and compare your result to the known value . How large should NSTEPS be chosen each time? You may choose x0=0.3.
- Consider the logistic map for . Take x0=0.3, 0.5, 0.9 and
NSTEPS=300 and plot the resulting trajectories. Calculate the ﬁxed points
and by using the command tail log.dat. Increase NSTEPS and
repeat so that you make sure that the trajectory has converged to the
2-cycle. Compare their values to the ones given by equation (3.18) . Make
the following plots:
gnuplot> plot \

"<echo 300 3.2 0.3|./l;awk ’NR%2==0’ log.dat" w l

gnuplot> replot \

"<echo 300 3.2 0.3|./l;awk ’NR%2==1’ log.dat" w lWhat do you observe?

- Repeat the previous problem for . How big should NSTEPS be chosen so that you obtain and with an accuracy of 6 signiﬁcant digits?
- Repeat the previous problem for and . Choose NSTEPS = 1000, x0 = 0.5. Show that the trajectories approach a 4-cycle and an 8-cycle respectively. Calculate the ﬁxed points - and -.
- Plot the functions , , , for given on the same
graph. Use the commands:
gnuplot> set samples 1000

gnuplot> f(x) = r*x*(1-x)

gnuplot> r=1;plot [0:1] x,f(x),f(f(x)),f(f(f(f(x))))The command r=1 sets the value of . Take , , , , . Determine the ﬁxed points and the -cycles from the intersections of the plots with the diagonal .

- Construct the cobweb plots of ﬁgures 3.2 and 3.4 for and . Repeat by dropping from the plot an increasing number of initial points, so that in the end only the -cycles will remain. Do the same for .
- Construct the bifurcation diagrams shown in ﬁgure 3.4.
- Construct the bifurcation diagram of the logistic map for and for . Compute the ﬁrst four bifurcation points with an accuracy of 5 signiﬁcant digits by magnifying the appropriate parts of the plots. Take NTRANS=15000.
- Construct the bifurcation diagram of the logistic map for . Compute graphically the bifurcation points for . Make sure that your results are stable against variations of the parameters NTRANS, NSTEPS as well as from the choice of branching point. From the known values of for , and from the dependence of your results on the choices of NTRANS, NSTEPS, estimate the accuracy achieved by this graphical method. Compute the ratios and compare your results to equation (3.20) .
- Choose the values of in equation (3.24) so that you obtain only one energy level. Compute the resulting value of the energy. When do we have three energy levels?
- Consider the polynomial . Find the roots obtained by the Newton-Raphson method when you choose . What do you conclude concerning the basins of attraction of each root of the polynomial? Make a plot of the polynomial in a neighborhood of its roots and try other initial points that will converge to each one of the roots.
- Use the Newton-Raphson method in order to compute the 4-cycle of the logistic map. Use appropriate areas of the bifurcation diagram so that you can choose the initial points correctly. Check that your result for is the same for all . Tune the parameters chosen in your calculation on order to improve the accuracy of your measurements.
- Repeat the previous problem for the 8-cycle and .
- Repeat the previous problem for the 16-cycle and .
- Calculate the critical points for of the logistic map using the Newton-Raphson method. In order to achieve that, you should determine the bifurcation points graphically in the bifurcation diagram ﬁrst and then choose the initial points in the Newton-Raphson method appropriately. The program in bifurcationPoints.f90 should read the parameters eps, epsx, epsr from the stdin so that they can be tuned for increasing . If these parameters are too small the convergence will be unstable and if they are too large you will have large systematic errors. Using this method, try to reproduce table 3.1
- Calculate the ratios of equation (3.20) using the results of table 3.1. Calculate Feigenbaum’s constant and comment on the accuracy achieved by your calculation.
- Estimate Feigenbaum’s constant and the critical value by
assuming that for large enough , . This behavior
is a result of equation (3.20) . Fit the results of table 3.1 to this
function and calculate and . This hypothesis is conﬁrmed in
ﬁgure 3.13 where we can observe the exponential convergence of
to . Construct the same plot using the parameters of your
calculation.

Hint: You can use the following gnuplot commands:gnuplot> nmin=2;nmax=17

gnuplot> r(x)= rc-c*d**(-x)

gnuplot> fit [nmin:nmax] r(x) "rcrit" u 1:2 via rc,c,d

gnuplot> plot "rcrit", r(x)

gnuplot> print rc,dThe ﬁle rcrit contains the values of table 3.1. You should vary the parameters nmin, nmax and repeat until you obtain a stable ﬁt.

- Use the Newton-Raphson method to calculate the ﬁrst three bifurcation points after the appearance of the 3-cycle for . Choose one bifurcation point of the 3-cycle, one of the 6-cycle and one of the 12-cycle and magnify the bifurcation diagram in their neighborhood.
- Consider the map describing the evolution of a population
(3.38) - Plot the functions , , , for , , , for . For which values of do you expect to obtain stable -cycles?
- For the same values of plot the trajectories with initial points . For each make a separate plot.
- Use the Newton-Raphson method in order to determine the points for as well as the ﬁrst two bifurcation points of the 3-cycle.
- Construct the bifurcation diagram for . Determine the point marking the onset of chaos as well as the point where the 3-cycle starts. Magnify the diagram around a branch that you will choose.
- Estimate Feigenbaum’s constant as in problem 17. Is your result compatible with the expectation of universality for the value of ? Is the value of the same as that of the logistic map?

- Consider the sine map:
(3.39) - Plot the functions , , , , for , , , , . Which values of are expected to lead to stable -cycles?
- For the same values of , plot the trajectories with initial points . Make one plot for each .
- Use the Newton-Raphson method in order to determine the points for as well as the ﬁrst two bifurcation points of the 3-cycle.
- Construct the bifurcation diagram for . Within which limits do the values of lie in? Repeat for . What do you observe? Determine the point marking the onset of chaos as well as the point where the 3-cycle starts. Magnify the diagram around a branch that you will choose.

- Consider the map:
(3.40) - Construct the bifurcation diagram for . Within which limits do the values of lie in? Determine the point marking the onset of chaos as well as the point where the 3-cycle starts. Magnify the diagram around a branch that you will choose.
- Use the Newton-Raphson method in order to determine the points for as well as the ﬁrst two bifurcation points of the 3-cycle.

- Consider the tent map:
(3.41) Construct the bifurcation diagram for . Within which limits do the values of lie in? On the same graph, plot the functions , .

Magnify the diagram in the area and . At which point do the two disconnected intervals within which take their values merge into one? Magnify the areas , and , and determine the merging points of two disconnected intervals within which take their values.

- Consider the Gauss map (or mouse map):
(3.42) Construct the bifurcation diagram for and . Make your program to take as the initial point of the new trajectory to be the last one of the previous trajectory and choose for . Repeat for . What do you observe? Note that as is increased, we obtain bifurcations and “anti-bifurcations”.

- Consider the circle map:
(3.43) (Make sure that your program keeps the values of so that ). Construct the bifurcation diagram for and .

- Use the program in liapunov.f90 in order to compute the distance between two trajectories of the logistic map for that originally are at a distance . Choose and calculate the Liapunov exponent by ﬁtting to a straight line appropriately. Compute the mean value and the standard error of the mean.
- Calculate the Liapunov exponent for for the logistic map. Use both ways mentioned in the text. Choose at least 5 diﬀerent initial points and calculate the mean and the standard error of the mean of your results. Compare the values of that you obtain with each method and comment.
- Compute the critical value numerically as the limit for the logistic map with an accuracy of nine signiﬁcant digits. Use the calculation of the Liapunov exponent given by equation (3.35) .
- Compute the values of of the logistic map numerically for which we (a) enter a stable 3-cycle (b) reenter into the chaotic behavior. Do the calculation by computing the Liapunov exponent and compare your results with the ones obtained from the bifurcation diagram.
- Calculate the Liapunov exponent using equation (3.35) for the following maps: and construct the diagrams similar to the ones in ﬁgure 3.9. Compare your plots with the respective bifurcation diagrams (you may put both graphs on the same plot). Use two diﬀerent initial points for the Gauss map ( ) and observe the diﬀerences. For the circle map ( ) study carefully the values .
- Reproduce the plots in ﬁgures 3.10, 3.11 and 3.12. Compute the function for , , and . Determine the points where you have stronger chaos by observing and the corresponding values of the entropy. Compute the entropy for by taking RSTEPS=2000 and estimate the values of where we enter to and exit from chaos. Compare your results with the computation of the Liapunov exponent.
- Consider the Hénon map:
- Construct the two bifurcation diagrams for and for , . Check if the values that we will use below correspond to stable periodic trajectories or chaotic behavior.
- Write a program in a ﬁle attractor.f90 which will take NINIT = NL NL initial conditions NL on a NLNL lattice of the square , . Each of the points will evolve according to equation (3.45) for NSTEPS steps. The program will print the points to the stdout. Choose , , NL.
- Choose , and plot the points for on the same diagram.
- Choose , and plot the points for on the same diagram.
- Choose , and plot the points for
on the same diagram. Observe the Hénon strange
attractor and its fractal properties. It is characterized by a
Hausdorﬀ
^{12}dimension . Then magnify the regions

- Consider the Duﬃng map:
- Construct the two bifurcation diagrams for and for , . Choose four diﬀerent initial conditions . What do you observe?
- Use the program attractor.f90 from problem 33 in order to study the attractor of the map for , .

- Consider the Tinkerbell map:
- Choose , , , . Plot a trajectory on the plane by plotting the points for with .
- Use the program attractor.f90 from problem 33 in order to study the attractor of the map for the values of the parameters , , , given above. Choose , , , , .
- Repeat the previous question by taking .

Motion of a Particle

In this chapter we will study the numerical solution of classical equations of motion of one dimensional mechanical systems, e.g. a point particle moving on the line, the simple pendulum etc. We will make an introduction to the numerical integration of ordinary diﬀerential equations with initial conditions and in particular to the Euler and Runge-Kutta methods. We study in detail the examples of the damped harmonic oscillator and of the damped pendulum under the inﬂuence of an external periodic force. The latter system is nonlinear and exhibits interesting chaotic behavior.

Consider the problem of the solution of the dynamical equations of motion of one particle under the inﬂuence of a dynamical ﬁeld given by Newton’s law. The equations can be written in the form

| (4.1) |

where

| (4.2) |

From the numerical analysis point of view, the problems that we will discuss are initial value problems for ordinary diﬀerential equations where the initial conditions

| (4.3) |

determine a unique solution . The equations (4.1) are of second order with respect to time and it is convenient to write them as a system of twice as many ﬁrst order equations:

| (4.4) |

In particular, we will be interested in the study of the motion of a particle moving on a line (1 dimension), therefore the above equations become

When the particle moves on the plane (2 dimensions) the equations of motion become

As a ﬁrst attempt to tackle the problem, we will study a simple pendulum of length in a homogeneous gravitational ﬁeld (ﬁgure 4.1).

The equations of motion are given by the diﬀerential equations

which can be rewritten as a ﬁrst order system of diﬀerential equations The equations above need to be written in a discrete form appropriate for a numerical solution with the aid of a computer. We split the interval of time of integration to equal intervals An improved algorithm is the Euler–Verlet method which is of second order and gives
total error^{3}
. This is given by the equations

The price that we have to pay is that we have to use a two step relation in order to advance the solution to the next step. This implies that we have to carefully determine the initial conditions of the problem which are given only at one given time . We make one Euler time step backwards in order to deﬁne the value of . If the initial conditions are , , then we deﬁne

| (4.12) |

It is important that at this step the error introduced is not larger than , otherwise it will spoil and eventually dominate the total error of the method introduced by the intermediate steps. At the last step we also have to take

| (4.13) |

Even though the method has smaller total error than the Euler method, it
becomes unstable for small enough due to roundoﬀ errors. In particular, the
second equation in (4.11) gives the angular velocity as the ratio of two small
numbers. The problem is that the numerator is the result of the subtraction of
two almost equal numbers. For small enough , this diﬀerence has to be
computed from the last digits of the ﬁnite representation of the numbers
and in the computer memory. The accuracy in the determination of
decreases until it eventually becomes exactly zero. For the ﬁrst
equation of (4.11) , the term is smaller by a factor compared to the
term in Euler’s method. At some point, by decreasing , we obtain
and the accuracy of the method vanishes due to the ﬁnite
representation of real number in the memory of the computer. When the
numbers and diﬀer from each other by more that
approximately seven orders of magnitude, adding the ﬁrst one to the
second is equivalent to adding zero and the contribution of the acceleration
vanishes^{4} .

Writing programs that implement the methods discussed so far is quite simple. We will write a program that compares the results from all three methods Euler, Euler–Cromer and Euler–Verlet. The main program is mainly a user interface, and the computation is carried out by three subroutines euler, euler_cromer and euler_verlet. The user must provide the function accel(x) which gives the angular acceleration as a function of x. The variable x in our problem corresponds to the angle . For starters we take accel(x)= -10.0 * sin(x), the acceleration of the simple pendulum.

The data structure is very simple: Three real arrays REAL T(P), X(P) and V(P) store the times , the angles and the angular velocities for . The user determines the time interval for the integration from to and the number of discrete times Nt. The latter should be less than P, the size of the arrays. She also provides the initial conditions and . After this, we call the main integration routines which take as input the initial conditions, the time interval of the integration and the number of discrete times Xin,Vin,Tfi,Nt. The output of the routines is the arrays T,X,V which store the results for the time, position and velocity respectively. The results are printed to the ﬁles euler.dat, euler_cromer.dat and euler_verlet.dat.

After setting the initial conditions and computing the time step , the integration in each of the subroutines is performed in do loops which advance the solution for time . The results are stored at each step in the arrays T,X,V. For example, the central part of the program for Euler’s method is:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h

X(i) = X(i-1)+V(i-1)*h

V(i) = V(i-1)+accel(X(i-1))*h

enddo

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h

X(i) = X(i-1)+V(i-1)*h

V(i) = V(i-1)+accel(X(i-1))*h

enddo

Some care has to be taken in the case of the Euler–Verlet method where one has to initialize the ﬁrst two steps, as well as take special care for the last step for the velocity:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

X0 = X(1) - V(1) * h + accel(X(1)) *h*h/2.0

T(2) = h

X(2) = 2.0*X(1) - X0 + accel(X(1)) *h*h

do i = 3,Nt

..............

enddo

V(Nt)= (X(Nt)-X(Nt-1))/h

X(1) = Xin

V(1) = Vin

X0 = X(1) - V(1) * h + accel(X(1)) *h*h/2.0

T(2) = h

X(2) = 2.0*X(1) - X0 + accel(X(1)) *h*h

do i = 3,Nt

..............

enddo

V(Nt)= (X(Nt)-X(Nt-1))/h

The full program can be found in the ﬁle euler.f90 and is listed below:

!=========================================================

!Program to integrate equations of motion for accelerations

!which are functions of x with the method of Euler,

!Euler-Cromer and Euler-Verlet.

!The user sets initial conditions and the subroutines return

!X(t) and V(t)=dX(t)/dt in arrays T(1..Nt),X(1..Nt),V(1..Nt)

!The user provides number of times Nt and the final

!time Tfi. Initial time is assumed to be t_i=0 and the

!integration step h = Tfi/(Nt-1)

!The user programs a real function accel(x) which gives the

!acceleration dV(t)/dt as function of X.

!NOTE: T(1) = 0 T(Nt) = Tfi

!=========================================================

program diff_eq_euler

implicit none

integer,parameter:: P=110000 ! The size of the arrays

real,dimension(P):: T,X,V ! time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi ! initial conditions

integer :: Nt,i

!The user provides initial conditions X_0,V_0 final time t_f

!and Nt:

print *,’Enter X_0,V_0,t_f,Nt (t_i=0):’

read(5,*)Xin,Vin,Tfi,Nt

!This check is necessary in order to avoid memory

!access violations:

if(Nt .ge. P )then

print *,’Nt must be strictly less than P. Nt,P= ’,Nt,P

stop

endif

!Xin= X(1), Vin=V(1), T(1)=0 and the routine gives evolution in

!T(2..Nt), X(2..Nt), V(2..Nt) which we print in a file

call euler(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler.dat")

do i=1,Nt

!Each line in data file has time, position, velocity:

write(20,*) T(i),X(i),V(i)

enddo

close(20) !we close the unit to be reused below

!------------------------------------

!We repeat everything for each method

call euler_cromer(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler_cromer.dat")

do i=1,Nt

write(20,*) T(i),X(i),V(i)

enddo

close(20)

!------------------------------------

call euler_verlet(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler_verlet.dat")

do i=1,Nt

write(20,*) T(i),X(i),V(i)

enddo

close(20)

!------------------------------------

end program diff_eq_euler

!=========================================================

!Function which returns the value of acceleration at

!position x used in the integration subroutines

!euler, euler_cromer and euler_verlet

!=========================================================

real function accel(x)

implicit none

real x

accel = -10.0*sin(x)

end function accel

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_k)=X(k), dx/dt(t_k)=V(k), t_k=T(k) k=1..Nt

!where for k=1 we have the initial condition.

!=========================================================

subroutine euler(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt) :: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,accel !**declare the function accel**

!Initial conditions set here:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

!h is the time step Dt

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h ! time advances by Dt=h

X(i) = X(i-1)+V(i-1)*h ! advancement of position

V(i) = V(i-1)+accel(X(i-1))*h !and velocity.

enddo

end subroutine euler

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler-Cromer method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_i)=X(i), dx/dt(t_i)=V(i), t_i=T(i) i=1..Nt

!where for i=1 we have the initial condition.

!=========================================================

subroutine euler_cromer(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt):: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,accel

T(1) = 0.0

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h

V(i) = V(i-1)+accel(X(i-1))*h

!here is the difference compared to Euler

X(i) = X(i-1)+V(i)*h

enddo

end subroutine euler_cromer

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler - Verlet method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_i)=X(i), dx/dt(t_i)=V(i), t_i=T(i) i=1..Nt

!where for i=1 we have the initial condition.

!=========================================================

subroutine euler_verlet(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt):: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,h2,X0,o2h

real :: accel

!Initial conditions set here:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1) ! time step

h2 = h*h ! time step squared

o2h = 0.5/h ! h/2

!We have to initialize one more step: X0 corresponds to ’X(0)’

X0 = X(1) - V(1) * h + accel(X(1)) *h2/2.0

T(2) = h

X(2) = 2.0*X(1) - X0 + accel(X(1)) *h2

!Now i starts from 3:

do i = 3,Nt

T(i) = T(i-1)+h

X(i) = 2.0*X(i-1) - X(i-2) + accel(X(i-1))*h2

V(i-1) = o2h * (X(i)-X(i-2))

enddo

!Notice that we have one more step for the velocity:

V(Nt)= (X(Nt)-X(Nt-1))/h

end subroutine euler_verlet

!Program to integrate equations of motion for accelerations

!which are functions of x with the method of Euler,

!Euler-Cromer and Euler-Verlet.

!The user sets initial conditions and the subroutines return

!X(t) and V(t)=dX(t)/dt in arrays T(1..Nt),X(1..Nt),V(1..Nt)

!The user provides number of times Nt and the final

!time Tfi. Initial time is assumed to be t_i=0 and the

!integration step h = Tfi/(Nt-1)

!The user programs a real function accel(x) which gives the

!acceleration dV(t)/dt as function of X.

!NOTE: T(1) = 0 T(Nt) = Tfi

!=========================================================

program diff_eq_euler

implicit none

integer,parameter:: P=110000 ! The size of the arrays

real,dimension(P):: T,X,V ! time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi ! initial conditions

integer :: Nt,i

!The user provides initial conditions X_0,V_0 final time t_f

!and Nt:

print *,’Enter X_0,V_0,t_f,Nt (t_i=0):’

read(5,*)Xin,Vin,Tfi,Nt

!This check is necessary in order to avoid memory

!access violations:

if(Nt .ge. P )then

print *,’Nt must be strictly less than P. Nt,P= ’,Nt,P

stop

endif

!Xin= X(1), Vin=V(1), T(1)=0 and the routine gives evolution in

!T(2..Nt), X(2..Nt), V(2..Nt) which we print in a file

call euler(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler.dat")

do i=1,Nt

!Each line in data file has time, position, velocity:

write(20,*) T(i),X(i),V(i)

enddo

close(20) !we close the unit to be reused below

!------------------------------------

!We repeat everything for each method

call euler_cromer(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler_cromer.dat")

do i=1,Nt

write(20,*) T(i),X(i),V(i)

enddo

close(20)

!------------------------------------

call euler_verlet(Xin,Vin,Tfi,Nt,T,X,V)

open(unit=20,file="euler_verlet.dat")

do i=1,Nt

write(20,*) T(i),X(i),V(i)

enddo

close(20)

!------------------------------------

end program diff_eq_euler

!=========================================================

!Function which returns the value of acceleration at

!position x used in the integration subroutines

!euler, euler_cromer and euler_verlet

!=========================================================

real function accel(x)

implicit none

real x

accel = -10.0*sin(x)

end function accel

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_k)=X(k), dx/dt(t_k)=V(k), t_k=T(k) k=1..Nt

!where for k=1 we have the initial condition.

!=========================================================

subroutine euler(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt) :: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,accel !**declare the function accel**

!Initial conditions set here:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

!h is the time step Dt

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h ! time advances by Dt=h

X(i) = X(i-1)+V(i-1)*h ! advancement of position

V(i) = V(i-1)+accel(X(i-1))*h !and velocity.

enddo

end subroutine euler

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler-Cromer method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_i)=X(i), dx/dt(t_i)=V(i), t_i=T(i) i=1..Nt

!where for i=1 we have the initial condition.

!=========================================================

subroutine euler_cromer(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt):: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,accel

T(1) = 0.0

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1)

do i = 2,Nt

T(i) = T(i-1)+h

V(i) = V(i-1)+accel(X(i-1))*h

!here is the difference compared to Euler

X(i) = X(i-1)+V(i)*h

enddo

end subroutine euler_cromer

!=========================================================

!Driver routine for integrating equations of motion

!using the Euler - Verlet method

!Input:

!Xin=X(1), Vin=V(1) -- initial condition at t=0,

!Tfi the final time and Nt the number of times

!Output:

!The arrays T(1..Nt), X(1..Nt), V(1..Nt) which

!gives x(t_i)=X(i), dx/dt(t_i)=V(i), t_i=T(i) i=1..Nt

!where for i=1 we have the initial condition.

!=========================================================

subroutine euler_verlet(Xin,Vin,Tfi,Nt,T,X,V)

implicit none

integer :: Nt

real,dimension(Nt):: T,X,V !time t,x(t),v(t)=dx/dt

real :: Xin,Vin,Tfi

integer :: i

real :: h,h2,X0,o2h

real :: accel

!Initial conditions set here:

T(1) = 0.0

X(1) = Xin

V(1) = Vin

h = Tfi/(Nt-1) ! time step

h2 = h*h ! time step squared

o2h = 0.5/h ! h/2

!We have to initialize one more step: X0 corresponds to ’X(0)’

X0 = X(1) - V(1) * h + accel(X(1)) *h2/2.0

T(2) = h

X(2) = 2.0*X(1) - X0 + accel(X(1)) *h2

!Now i starts from 3:

do i = 3,Nt

T(i) = T(i-1)+h

X(i) = 2.0*X(i-1) - X(i-2) + accel(X(i-1))*h2

V(i-1) = o2h * (X(i)-X(i-2))

enddo

!Notice that we have one more step for the velocity:

V(Nt)= (X(Nt)-X(Nt-1))/h

end subroutine euler_verlet

Compiling the running the program can be done with the commands:

> gfortran euler.f90 -o euler

> ./euler

Enter X_0,V_0,t_f,Nt (t_i=0):

0.2 0.0 6.0 1000

> ls euler*.dat

euler_cromer.dat euler.dat euler_verlet.dat

> head -n 5 euler.dat

0.000000 0.2000000 0.000000

6.0060062E-03 0.2000000 -1.1932093E-02

1.2012012E-02 0.1999283 -2.3864185E-02

1.8018018E-02 0.1997850 -3.5792060E-02

2.4024025E-02 0.1995700 -4.7711499E-02

> ./euler

Enter X_0,V_0,t_f,Nt (t_i=0):

0.2 0.0 6.0 1000

> ls euler*.dat

euler_cromer.dat euler.dat euler_verlet.dat

> head -n 5 euler.dat

0.000000 0.2000000 0.000000

6.0060062E-03 0.2000000 -1.1932093E-02

1.2012012E-02 0.1999283 -2.3864185E-02

1.8018018E-02 0.1997850 -3.5792060E-02

2.4024025E-02 0.1995700 -4.7711499E-02

The last command shows the ﬁrst 5 lines of the ﬁle euler.dat. We see the data for the time, the position and the velocity stored in 3 columns. We can graph the results using gnuplot:

gnuplot> plot "euler.dat" using 1:2 with lines

gnuplot> plot "euler.dat" using 1:3 with lines

gnuplot> plot "euler.dat" using 1:3 with lines

These commands result in plotting the positions and the velocities as a function of time respectively. We can add the results of all methods to the last plot with the commands:

gnuplot> replot "euler_cromer.dat" using 1:3 with lines

gnuplot> replot "euler_verlet.dat" using 1:3 with lines

gnuplot> replot "euler_verlet.dat" using 1:3 with lines

The results can be seen in ﬁgures 4.2–4.7. Euler’s method is unstable unless we take a quite small time step. The Euler–Cromer method behaves impressively better. The results converge and remain constant for Nt. The Euler–Verlet method converges much faster, but roundoﬀ errors kick in soon. This is more obvious in ﬁgure 4.7 where the initial angular position is large. For small angles we can compare with the solution one obtains for the harmonic pendulum ():

In ﬁgures 4.2–4.4 we observe that the results agree with the above formulas for the values of where the methods converge. This way we can check our program for bugs. The plot of the functions above can be done with the following gnuplot commands
gnuplot> set dummy t

gnuplot> omega2 = 10

gnuplot> X0 = 0.2

gnuplot> V0 = 0.0

gnuplot> omega = sqrt(omega2)

gnuplot> x(t) = X0 * cos(omega * t) +(V0/omega)*sin(omega*t)

gnuplot> v(t) = V0 * cos(omega * t) -(omega*X0)*sin(omega*t)

gnuplot> plot x(t), v(t)

gnuplot> omega2 = 10

gnuplot> X0 = 0.2

gnuplot> V0 = 0.0

gnuplot> omega = sqrt(omega2)

gnuplot> x(t) = X0 * cos(omega * t) +(V0/omega)*sin(omega*t)

gnuplot> v(t) = V0 * cos(omega * t) -(omega*X0)*sin(omega*t)

gnuplot> plot x(t), v(t)

The results should not be compared only graphically since subtle diﬀerences can remain unnoticed. It is more desirable to plot the diﬀerences of the theoretical values from the numerically computed ones which can be done using the commands:

gnuplot> plot "euler.dat" using 1:($2-x($1)) with lines

gnuplot> plot "euler.dat" using 1:($3-v($1)) with lines

gnuplot> plot "euler.dat" using 1:($3-v($1)) with lines

The command using 1:($2-x($1)) puts the values found in the
ﬁrst column on the axis and the value found in the second column
minus the value of the function x(t) for equal to the value found in
the ﬁrst column on the axis. This way, we can make the plots shown
in^{6}
ﬁgures 4.11-4.14.

Euler’s method is a one step ﬁnite diﬀerence method of ﬁrst order. First order means that the total error introduced by the discretization of the integration interval by discrete times is of order , where is the time step of the integration. In this section we will discuss a generalization of this approach where the total error will be of higher order in . This is the class of Runge-Kutta methods which are one step algorithms where the total discretization error is of order . The local error introduced at each step is of order leading after steps to a maximum error of order

| (4.15) |

In such a case we say that we have a Runge-Kutta method of order. The price one has to pay for the increased accuracy is the evaluation of the derivatives of the functions in more than one points in the interval .

Let’s consider for simplicity the problem with only one unknown function which evolves in time according to the diﬀerential equation:

| (4.16) |

Consider the ﬁrst order method ﬁrst. The most naive approach would be to take the derivative to be given by the ﬁnite diﬀerence

| (4.17) |

By Taylor expanding, we see that the error at each step is , therefore the error after integrating from is . Indeed,

| (4.18) |

The geometry of the step is shown in ﬁgure 4.8. We start from point 1 and by linearly extrapolating in the direction of the derivative we determine the point .

We can improve the method above by introducing an intermediate point 2. This process is depicted in ﬁgure 4.9. We take the point 2 in the middle of the interval by making a linear extrapolation from in the direction of the derivative . Then we use the slope at point 2 as an estimator of the derivative within this interval, i.e. . We use to linearly extrapolate from to . Summarizing, we have that

For the procedure described above we have to evaluate twice at each step, thereby doubling the computational eﬀort. The error at each step (4.19) becomes , however, giving a total error of . So for given computational time, (4.19) is superior to (4.17) . We can further improve the accuracy gain by using the Runge–Kutta method of
4th order. In this case we have 4 evaluations of the derivative per step, but the
total error becomes now and the method is superior to that of (4.19)
^{7}.
The process followed is explained geometrically in ﬁgure 4.10. We use 3
intermediate points for evolving the solution from to . Point 2 is
determined by linearly extrapolating from to the midpoint of the
interval by using the direction given by the derivative
, i.e. . We calculate the derivative
at the point 2 and we use it in order to determine
point 3, also located at the midpoint of the interval . Then we calculate
the derivative at the point 3 and we use
it to linearly extrapolate to the end of the interval , thereby
obtaining point 4, i.e. . Then we calculate the derivative
at the point 4, and we use all four derivative
and as estimators of the derivative of the function in the interval
. If each derivative contributes with a particular weight in this
estimate, the discretization error can become . Such a choice is

We remind to the reader the fact that by decreasing the discretization errors decrease, but that roundoﬀ errors will start showing up for small enough . Therefore, a careful determination of that minimizes the total error should be made by studying the dependence of the results as a function of .

Consider the problem of the motion of a particle in one dimension. For this, we have to integrate a system of two diﬀerential equations (4.5) for two unknown functions of time and so that

| (4.21) |

In this case, equations (4.20) generalize to:

Programming this algorithm is quite simple. The main program is an interface between the user and the driver routine of the integration. The user enters the initial and ﬁnal times Ti and Tf and the number of discrete time points Nt. The initial conditions are X10, X20. The main data structure consists of three real arrays T(P), X1(P), X2(P) which store the times and the corresponding values of the functions and , . The main program calls the driver routine RK(T,X1,X2,Ti,Tf,X10,X20,Nt) which “drives” the heart of the program, the subroutine RKSTEP(t,x1,x2,dt) which performs one integration step using equations (4.22) . RKSTEP evolves the functions x1, x2 at time t by one step dt. The routine RK stores the calculated values in the arrays T, X1 and X2 at each step. When RK returns the control to the main program, all the results are stored in T, X1 and X2, which are subsequently printed in the ﬁle rk.dat. The full program is listed below and can be found in the ﬁle rk.f90:

!========================================================

!Program to solve a 2 ODE system using Runge-Kutta Method

!User must supply derivatives

!dx1/dt=f1(t,x1,x2) dx2/dt=f2(t,x1,x2)

!as real functions

!Output is written in file rk.dat

!========================================================

program rk_solve

implicit none

integer, parameter :: P=110000

real,dimension(P) :: T,X1,X2

real :: Ti,Tf,X10,X20

integer :: Nt

integer :: i

!Input:

print *,’Runge-Kutta Method for 2-ODEs Integration’

print *,’Enter Nt,Ti,TF,X10,X20:’

read *, Nt,Ti,Tf,X10,X20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

if(Nt.gt.P) stop ’Nt>P’

!The Calculation:

call RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

!Output:

open(unit=11,file=’rk.dat’)

do i=1,Nt

write(11,*)T(i),X1(i),X2(i)

enddo

close(11)

end program rk_solve

!========================================================

!The functions f1,f2(t,x1,x2) provided by the user

!========================================================

real function f1(t,x1,x2)

implicit none

real :: t,x1,x2

f1=x2 !dx1/dt= v = x2

end function f1

!--------------------------------------------------------

real function f2(t,x1,x2)

implicit none

real :: t,x1,x2

f2=-10.0D0*x1 !harmonic oscillator

end function f2

!========================================================

!RK(T,X1,X2,Ti,Tf,X10,X20,Nt) is the driver

!for the Runge-Kutta integration routine RKSTEP

!Input: Initial and final times Ti,Tf

! Initial values at t=Ti X10,X20

! Number of steps of integration: Nt-1

! Size of arrays T,X1,X2

!Output: real arrays T(Nt),X1(Nt),X2(Nt) where

!T(1) = Ti X1(1) = X10 X2(1) = X20

! X1(k) = X1(at t=T(k)) X2(k) = X2(at t=T(k))

!T(Nt)=TF

!========================================================

subroutine RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

implicit none

integer :: Nt

real,dimension(Nt):: T,X1,X2

real :: Ti,Tf,X10,X20

real :: dt

real :: TS,X1S,X2S !values of time and X1,X2 at given step

integer :: i

!Initialize variables:

dt = (Tf-Ti)/(Nt-1)

T (1) = Ti

X1(1) = X10

X2(1) = X20

TS = Ti

X1S = X10

X2S = X20

!Make RK steps: The arguments of RKSTEP

!are replaced with the new ones!

do i=2,Nt

call RKSTEP(TS,X1S,X2S,dt)

T (i) = TS

X1(i) = X1S

X2(i) = X2S

enddo

end subroutine RK

!========================================================

!Subroutine RKSTEP(t,x1,x2,dt)

!Runge-Kutta Integration routine of ODE

!dx1/dt=f1(t,x1,x2) dx2/dt=f2(t,x1,x2)

!User must supply derivative functions:

!real function f1(t,x1,x2)

!real function f2(t,x1,x2)

!Given initial point (t,x1,x2) the routine advances it

!by time dt.

!Input : Inital time t and function values x1,x2

!Output: Final time t+dt and function values x1,x2

!Careful!: values of t,x1,x2 are overwritten...

!========================================================

subroutine RKSTEP(t,x1,x2,dt)

implicit none

real :: t,x1,x2,dt

real :: f1,f2

real :: k11,k12,k13,k14,k21,k22,k23,k24

real :: h,h2,h6

h =dt !h =dt, integration step

h2 =0.5D0*h !h2=h/2

h6 =h/6.0 !h6=h/6

k11=f1(t,x1,x2)

k21=f2(t,x1,x2)

k12=f1(t+h2,x1+h2*k11,x2+h2*k21)

k22=f2(t+h2,x1+h2*k11,x2+h2*k21)

k13=f1(t+h2,x1+h2*k12,x2+h2*k22)

k23=f2(t+h2,x1+h2*k12,x2+h2*k22)

k14=f1(t+h ,x1+h *k13,x2+h *k23)

k24=f2(t+h ,x1+h *k13,x2+h *k23)

t =t+h

x1 =x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2 =x2+h6*(k21+2.0D0*(k22+k23)+k24)

end subroutine RKSTEP

!Program to solve a 2 ODE system using Runge-Kutta Method

!User must supply derivatives

!dx1/dt=f1(t,x1,x2) dx2/dt=f2(t,x1,x2)

!as real functions

!Output is written in file rk.dat

!========================================================

program rk_solve

implicit none

integer, parameter :: P=110000

real,dimension(P) :: T,X1,X2

real :: Ti,Tf,X10,X20

integer :: Nt

integer :: i

!Input:

print *,’Runge-Kutta Method for 2-ODEs Integration’

print *,’Enter Nt,Ti,TF,X10,X20:’

read *, Nt,Ti,Tf,X10,X20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

if(Nt.gt.P) stop ’Nt>P’

!The Calculation:

call RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

!Output:

open(unit=11,file=’rk.dat’)

do i=1,Nt

write(11,*)T(i),X1(i),X2(i)

enddo

close(11)

end program rk_solve

!========================================================

!The functions f1,f2(t,x1,x2) provided by the user

!========================================================

real function f1(t,x1,x2)

implicit none

real :: t,x1,x2

f1=x2 !dx1/dt= v = x2

end function f1

!--------------------------------------------------------

real function f2(t,x1,x2)

implicit none

real :: t,x1,x2

f2=-10.0D0*x1 !harmonic oscillator

end function f2

!========================================================

!RK(T,X1,X2,Ti,Tf,X10,X20,Nt) is the driver

!for the Runge-Kutta integration routine RKSTEP

!Input: Initial and final times Ti,Tf

! Initial values at t=Ti X10,X20

! Number of steps of integration: Nt-1

! Size of arrays T,X1,X2

!Output: real arrays T(Nt),X1(Nt),X2(Nt) where

!T(1) = Ti X1(1) = X10 X2(1) = X20

! X1(k) = X1(at t=T(k)) X2(k) = X2(at t=T(k))

!T(Nt)=TF

!========================================================

subroutine RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

implicit none

integer :: Nt

real,dimension(Nt):: T,X1,X2

real :: Ti,Tf,X10,X20

real :: dt

real :: TS,X1S,X2S !values of time and X1,X2 at given step

integer :: i

!Initialize variables:

dt = (Tf-Ti)/(Nt-1)

T (1) = Ti

X1(1) = X10

X2(1) = X20

TS = Ti

X1S = X10

X2S = X20

!Make RK steps: The arguments of RKSTEP

!are replaced with the new ones!

do i=2,Nt

call RKSTEP(TS,X1S,X2S,dt)

T (i) = TS

X1(i) = X1S

X2(i) = X2S

enddo

end subroutine RK

!========================================================

!Subroutine RKSTEP(t,x1,x2,dt)

!Runge-Kutta Integration routine of ODE

!dx1/dt=f1(t,x1,x2) dx2/dt=f2(t,x1,x2)

!User must supply derivative functions:

!real function f1(t,x1,x2)

!real function f2(t,x1,x2)

!Given initial point (t,x1,x2) the routine advances it

!by time dt.

!Input : Inital time t and function values x1,x2

!Output: Final time t+dt and function values x1,x2

!Careful!: values of t,x1,x2 are overwritten...

!========================================================

subroutine RKSTEP(t,x1,x2,dt)

implicit none

real :: t,x1,x2,dt

real :: f1,f2

real :: k11,k12,k13,k14,k21,k22,k23,k24

real :: h,h2,h6

h =dt !h =dt, integration step

h2 =0.5D0*h !h2=h/2

h6 =h/6.0 !h6=h/6

k11=f1(t,x1,x2)

k21=f2(t,x1,x2)

k12=f1(t+h2,x1+h2*k11,x2+h2*k21)

k22=f2(t+h2,x1+h2*k11,x2+h2*k21)

k13=f1(t+h2,x1+h2*k12,x2+h2*k22)

k23=f2(t+h2,x1+h2*k12,x2+h2*k22)

k14=f1(t+h ,x1+h *k13,x2+h *k23)

k24=f2(t+h ,x1+h *k13,x2+h *k23)

t =t+h

x1 =x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2 =x2+h6*(k21+2.0D0*(k22+k23)+k24)

end subroutine RKSTEP

In this section we will check our programs for correctness and accuracy w.r.t. discretization and roundoﬀ errors. The simplest test is to check the results against a known analytic solution of a simple model. This will be done for the simple harmonic oscillator. Our programs will need small changes which are summarized below. First, we will need to use higher accuracy variables and we will change all variables of type REAL to REAL(8). For this we need to change the corresponding declarations in the beginning of each (sub)program. For each numerical constant in the program we need to put an explicit exponent with the letter D instead of an E. For example 0.5 0.5D0, 1.2E-3 1.2D-3 etc. Then we need to alter the functions that compute the acceleration of the particle to give . We will take (). Therefore the relevant part of the program in euler.f90 becomes

real(8) function accel(x)

implicit none

real(8) :: x

accel = -10.0D0*x

end function accel

implicit none

real(8) :: x

accel = -10.0D0*x

end function accel

and that of the program in rk.f90 becomes

real(8) function f2(t,x1,x2)

implicit none

real(8) :: t,x1,x2

f2=-10.0D0*x1

end function f2

implicit none

real(8) :: t,x1,x2

f2=-10.0D0*x1

end function f2

The programs are run for a given time interval to with the initial conditions , . The time step is varied by varying the number of steps Nt-1. The computed numerical solution is compared to the well known solution for the simple harmonic oscillator

We study the deviation and as a function of the time step . The results are shown in ﬁgures 4.11–4.14. We note that for the Euler method and the Euler–Cromer method, the errors are of order as expected. However, the latter has smaller errors compared to the ﬁrst one. For the Euler–Verlet method, the error turns out to be of order whereas for the 4th order Runge–Kutta is of orderAnother way for checking the numerical results is by looking at a conserved quantity, like the energy, momentum or angular momentum, and study its deviation from its original value. In our case we study the mechanical energy

| (4.24) |

which is computed at each step. The deviation is shown in ﬁgures 4.15–4.18.

In this section we will study a simple harmonic oscillator subject to a damping force proportional to its velocity and an external periodic driving force, which for simplicity will be taken to have a sinusoidal dependence in time,

| (4.25) |

where and is the angular frequency of the driving force.

Consider initially the system without the inﬂuence of the driving
force, i.e. with . The real solutions of the diﬀerential
equation^{9}
which are ﬁnite for are given by

| (4.26) |

| (4.27) |

In the last case, the solution oscillates with an amplitude decreasing exponentially with time.

In the case, the general solution is obtained from the sum of a special solution and the solution of the homogeneous equation . A special solution can be obtained from the ansatz , which when substituted in (4.25) and solved for and we ﬁnd that

| (4.29) |

and

| (4.30) |

The solution decreases exponentially with time and eventually only remains. The only case where this is not true, is when we have resonance without damping for , . In that case the solution is

| (4.31) |

The ﬁrst two terms are the same as that of the simple harmonic oscillator. The last one increases the amplitude linearly with time, which is a result of the inﬂux of energy from the external force to the oscillator.

Our program will be a simple modiﬁcation of the program in rk.f90. The main routines RK(T,X1,X2,T0,TF,X10,X20,Nt) and RKSTEP(t,x1,x2,dt) remain as they are. We only change the user interface. The basic parameters , , , are entered interactively by the user from the standard input stdin. These parameters should be accessible also by the function f2(t,x1,x2), and one way to be able to do this, is to store them in variables which are placed in a common block. Such variables are accessible to all subprograms that declare a common block with the same name using a COMMON declaration. Such a declaration is shown in the following lines

real(8) :: omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

which when written in a (sub)program, the (sub)program gains access to the “memory position” params where the values of the variables are stored. Another point that needs our attention is the function f2(t,x1,x2) which now takes the velocity x2 in its arguments:

real(8) function f2(t,x1,x2)

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2,a

a = a_0*cos(omega*t)

f2=-omega_02*x1-gamma*x2+a

end function f2

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2,a

a = a_0*cos(omega*t)

f2=-omega_02*x1-gamma*x2+a

end function f2

The main program found in the ﬁle dlo.f90 is listed below. The subroutines RK, RKSTEP are the same as in rk.f90 and should also be included in the same ﬁle.

!========================================================

!Program to solve Damped Linear Oscillator

!using 4th order Runge-Kutta Method

!Output is written in file dlo.dat

!========================================================

program dlo_solve

implicit none

integer, parameter :: P=110000

real(8),dimension(P):: T,X1,X2

real(8) :: Ti,Tf,X10,X20

real(8) :: Energy

real(8) :: omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

integer :: Nt, i

!Input:

print *,’Runge-Kutta Method for DLO Integration’

print *,’Enter omega_0, omega, gamma, a_0:’

read *, omega_0,omega,gamma,a_0

omega_02 = omega_0*omega_0

omega2 = omega *omega

print *, ’omega_0= ’,omega_0,’ omega= ’, omega

print *, ’gamma= ’,gamma, ’ a_0= ’,a_0

print *,’Enter Nt,Ti,TF,X10,X20:’

read *, Nt,Ti,Tf,X10,X20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

if(Nt.gt.P) stop ’Nt>P’

!The Calculation:

call RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

!Output:

open(unit=11,file=’dlo.dat’)

write(11,*)’# Damped Linear Oscillator - dlo’

write(11,*)’# omega_0= ’,omega_0,’ omega= ’, omega,&

’ gamma= ’,gamma,’ a_0= ’,a_0

do i=1,Nt

Energy = 0.5D0*X2(i)*X2(i)+0.5D0*omega_02*X1(i)*X1(i)

write(11,*)T(i),X1(i),X2(i),Energy

enddo

close(11)

end program dlo_solve

!========================================================

!The functions f1,f2(t,x1,x2) provided by the user

!========================================================

real(8) function f1(t,x1,x2)

implicit none

real(8) t,x1,x2

f1=x2 !dx1/dt= v = x2

end function f1

!--------------------------------------------------------

real(8) function f2(t,x1,x2)

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2,a

a = a_0*cos(omega*t)

f2=-omega_02*x1-gamma*x2+a

end function f2

!Program to solve Damped Linear Oscillator

!using 4th order Runge-Kutta Method

!Output is written in file dlo.dat

!========================================================

program dlo_solve

implicit none

integer, parameter :: P=110000

real(8),dimension(P):: T,X1,X2

real(8) :: Ti,Tf,X10,X20

real(8) :: Energy

real(8) :: omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

integer :: Nt, i

!Input:

print *,’Runge-Kutta Method for DLO Integration’

print *,’Enter omega_0, omega, gamma, a_0:’

read *, omega_0,omega,gamma,a_0

omega_02 = omega_0*omega_0

omega2 = omega *omega

print *, ’omega_0= ’,omega_0,’ omega= ’, omega

print *, ’gamma= ’,gamma, ’ a_0= ’,a_0

print *,’Enter Nt,Ti,TF,X10,X20:’

read *, Nt,Ti,Tf,X10,X20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

if(Nt.gt.P) stop ’Nt>P’

!The Calculation:

call RK(T,X1,X2,Ti,Tf,X10,X20,Nt)

!Output:

open(unit=11,file=’dlo.dat’)

write(11,*)’# Damped Linear Oscillator - dlo’

write(11,*)’# omega_0= ’,omega_0,’ omega= ’, omega,&

’ gamma= ’,gamma,’ a_0= ’,a_0

do i=1,Nt

Energy = 0.5D0*X2(i)*X2(i)+0.5D0*omega_02*X1(i)*X1(i)

write(11,*)T(i),X1(i),X2(i),Energy

enddo

close(11)

end program dlo_solve

!========================================================

!The functions f1,f2(t,x1,x2) provided by the user

!========================================================

real(8) function f1(t,x1,x2)

implicit none

real(8) t,x1,x2

f1=x2 !dx1/dt= v = x2

end function f1

!--------------------------------------------------------

real(8) function f2(t,x1,x2)

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2,a

a = a_0*cos(omega*t)

f2=-omega_02*x1-gamma*x2+a

end function f2

The results are shown in ﬁgures 4.19–4.22. Figure 4.19 shows the transition from a damped motion for to an oscillating motion with damping amplitude for . The exponential decrease of the amplitude is shown in ﬁgure 4.21, whereas the dependence of the period from the damping coeﬃcient is shown in ﬁgure 4.22. Motivated by equation (4.28) , written in the form

| (4.32) |

we construct the plot in ﬁgure 4.22. The right hand side of the equation is put on the horizontal axis, whereas the left hand side on the vertical. Equation (4.32) predicts that both quantities are equal and all measurements should lie on a particular line, the diagonal . The period can be estimated from the time between two consecutive extrema of or two consecutive zeros of the velocity (see ﬁgure 4.19).

Finally it is important to study the trajectory of the system in phase space. This can
be seen^{10}
in ﬁgure 4.20. A point in this space is a state of the system and a trajectory
describes the evolution of the system’s states in time. We see that all such
trajectories end up as to the point , independently of the
initial conditions. Such a point is an example of a system’s attractor.

Next, we add the external force and study the response of the system to it. The system exhibits a transient behavior that depends on the initial conditions. For large enough times it approaches a steady state that does not depend on (almost all of) the initial conditions. This can be seen in ﬁgure 4.23. This is easily understood for our system by looking at equations (4.26) – (4.28) . We see that the steady state becomes dominant when the exponentials have damped away. can be written in the form

These equations are veriﬁed in ﬁgure 4.24 where we study the dependence of the amplitude on the angular frequency of the driving force. Finally we study the trajectory of the system in phase space. As we can see in ﬁgure 4.20, this time the attractor is an ellipse, which is a one dimensional curve instead of a zero dimensional point. For large enough times, all trajectories approach their attractor asymptotically.In this section we will study a non-linear dynamical system which exhibits interesting chaotic behavior. This is a simple model which, despite its deterministic nature, the prediction of its future behavior becomes intractable after a short period of time. Consider a simple pendulum in a constant gravitational ﬁeld whose motion is damped by a force proportional to its velocity and it is under the inﬂuence of a vertical, harmonic external driving force:

| (4.34) |

In the equation above, is the angle of the pendulum with the vertical axis, is the damping coeﬃcient, is the pendulum’s natural angular frequency, is the angular frequency of the driving force and is the amplitude of the external angular acceleration caused by the driving force.

In the absence of the driving force, the damping coeﬃcient drives the system to the point , which is an attractor for the system. This continues to happen for small enough , but for the behavior of the system becomes more complicated.

The program that integrates the equations of motion of the system can be obtained by making trivial changes to the program in the ﬁle dlo.f90. This changes are listed in detail below, but we note that X1 , X2 , a_0 . The ﬁnal program can be found in the ﬁle fdp.f90. It is listed below, with the understanding that the commands in between the dots are the same as in the programs found in the ﬁles dlo.f90, rk.f90.

!========================================================

!Program to solve Forced Damped Pendulum

!using 4th order Runge-Kutta Method

!Output is written in file fdp.dat

!========================================================

program dlo_solve

implicit none

integer, parameter :: P=1010000

................................

Energy = 0.5D0*X2(i)*X2(i)+omega_02*(1.0D0-cos(X1(i)))

................................

end program dlo_solve

!--------------------------------------------------------

real(8) function f2(t,x1,x2)

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2

f2=-(omega_02+2.0D0*a_0*cos(omega*t))*sin(x1)-gamma*x2

end function f2

!========================================================

subroutine RKSTEP(t,x1,x2,dt)

implicit none

................................

real(8),parameter :: pi =3.14159265358979324D0

real(8),parameter :: pi2=6.28318530717958648D0

................................

x1 =x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2 =x2+h6*(k21+2.0D0*(k22+k23)+k24)

if( x1 .gt. pi) x1 = x1 - pi2

if( x1 .lt. -pi) x1 = x1 + pi2

end subroutine RKSTEP

!Program to solve Forced Damped Pendulum

!using 4th order Runge-Kutta Method

!Output is written in file fdp.dat

!========================================================

program dlo_solve

implicit none

integer, parameter :: P=1010000

................................

Energy = 0.5D0*X2(i)*X2(i)+omega_02*(1.0D0-cos(X1(i)))

................................

end program dlo_solve

!--------------------------------------------------------

real(8) function f2(t,x1,x2)

implicit none

real(8) omega_0,omega,gamma,a_0,omega_02,omega2

common /params/omega_0,omega,gamma,a_0,omega_02,omega2

real(8) t,x1,x2

f2=-(omega_02+2.0D0*a_0*cos(omega*t))*sin(x1)-gamma*x2

end function f2

!========================================================

subroutine RKSTEP(t,x1,x2,dt)

implicit none

................................

real(8),parameter :: pi =3.14159265358979324D0

real(8),parameter :: pi2=6.28318530717958648D0

................................

x1 =x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2 =x2+h6*(k21+2.0D0*(k22+k23)+k24)

if( x1 .gt. pi) x1 = x1 - pi2

if( x1 .lt. -pi) x1 = x1 + pi2

end subroutine RKSTEP

The ﬁnal lines in the program are added so that the angle is kept within the interval .

In order to study the system’s properties we will set , , and unless we explicitly state otherwise. The natural period of the pendulum is whereas that of the driving force is . For , with , the point is an attractor, which means that the pendulum eventually stops at its stable equilibrium point. For the attractor is a closed curve, which means that the pendulum at its steady state oscillates indeﬁnitely without circling through its unstable equilibrium point at . The period of motion is found to be twice that of the driving force. For the attractor is an open curve, because at its steady state the pendulum crosses the point. The period of the motion becomes equal to that of the driving force. For we have period doubling for critical values of , but the trajectory is still periodic. For even larger values of the system enters into a chaotic regime where the trajectories are non periodic. For we ﬁnd the system in a periodic steady state again, whereas for – we have period doubling. For we enter into a chaotic regime again etc. These results can be seen in ﬁgures 4.27–4.29. The reader should construct the bifurcation diagram of the system by solving problem 20 of this chapter.

We can also use the so called Poincaré diagrams in order to study the chaotic
behavior of a system. These are obtained by placing a point in phase
space when the time is an integer multiple of the period of the driving
force. Then, if for example the period of the motion is equal to that of
the period of the driving force, the Poincaré diagram consists of only
one point. If the period of the motion is an –multiple of the period of
the driving force then the Poincaré diagram consists of only points.
Therefore, in the period doubling regime, the points of the Poincaré diagram
double at each period doubling point. In the chaotic regime, the Poincaré
diagram consists of an inﬁnite number of points which belong to sets that
have interesting fractal structure. One way to construct the Poincaré
diagram numerically, is to process the data of the output ﬁle fdp.dat using
awk^{11} :

awk -v o=$omega -v nt=$Nt -v tf=$TF \

’BEGIN{T=6.283185307179/o;dt=tf/nt;} $1%T<dt{print $2,$3}’\

fdp.dat

’BEGIN{T=6.283185307179/o;dt=tf/nt;} $1%T<dt{print $2,$3}’\

fdp.dat

where $omega, $Nt, $TF are the values of the angular frequency ,
the number of points of time and the ﬁnal time . We calculate
the period T and the time step dt in the program. Then we print
those lines of the ﬁle where the time is an integer multiple of the
period^{12} .
This is accomplished by the modulo operation $1 % T. The value of the
expression $1 % T < dt is true when the remainder of the division of the ﬁrst
column ($1) of the ﬁle fdp.dat with the period T is smaller than dt. The results
in the chaotic regime are displayed in ﬁgure 4.30.

We close this section by discussing another concept that helps us in the analysis of the dynamical properties of the pendulum. This is the concept of the basin of attraction which is the set of initial conditions in phase space that lead the system to a speciﬁc attractor. Take for example the case for in the regime where the pendulum at its steady state has a circular trajectory with a positive or negative direction. By taking a large sample of initial conditions and recording the direction of the resulting motion after the transient behavior, we obtain ﬁgure 4.31.

Equations (4.11) can be obtained from the Taylor expansion

By adding and subtracting the above equations we obtain which give equations (4.11) From the ﬁrst equation and equations (4.9) we obtain:
| (4.37) |

When we perform a numerical integration, we are interested in the total error accumulated after integration steps. In this method, these errors must be studied carefully:

- The error in the velocity does not accumulate because it is given by the diﬀerence of the positions .
- The accumulation of the errors for the position is estimated as
follows: Assume that is the total accumulated error from the
integration from time to . Then according to the expansions
(4.36) the error for the ﬁrst step is .
Then
^{13}For the next steps we obtain Then, inductively, if , we obtain Finally(4.38)

Therefore the total error is .

We also mention the Velocity Verlet method or the Leapfrog method. In this case we use the velocity explicitly:

The last step uses the acceleration which should depend only on the position and not on the velocity.The Verlet methods are popular in molecular dynamics simulations of many body systems. One of their advantages is that the constraints of the system of particles are easily encoded in the algorithm.

In this appendix we will show how the choice of the intermediate point 2 in equation (4.17) reduces the error by a power of . This choice is special, since by choosing another point (e.g. ) the result would have not been the same. Indeed, from the relation

| (4.40) |

By Taylor expanding around the point we obtain

| (4.41) |

Therefore

Note that for the vanishing of the term it is necessary to place the intermediate point at time .This is not a unique choice. This can be most easily seen by a diﬀerent analysis of the Taylor expansion. Expanding around the point we obtain

where we have set , etc. We deﬁne and we will determine the conditions so that the terms of the last equation in the error are identical with those of equation (4.43) . By expanding we obtain Substituting in (4.44) we obtain All we need is to choose The choice , , leads to equation (4.19) . Some other choices in the bibliography are and .

- Prove that the total error in the Euler–Cromer method is of order .
- Reproduce the results in ﬁgures 4.11–4.18
- Improve your programs so that there is no accumulation of roundoﬀ error in the calculation of time when h is very small for the methods Euler, Euler-Cromer, Euler-Verlet and Runge-Kutta. Repeat the analysis of the previous problem.
- Make the appropriate changes in your programs of the Euler, Euler-Cromer, Euler-Verlet and Runge-Kutta methods so that all ﬂoating variables change from REALREAL(8). Repeat the analysis of the previous problem.
- Compare the results obtained from the Euler, Euler-Cromer, Euler-Verlet,
Runge-Kutta methods for the following systems where the analytic solution
is known:
- Particle falling in a constant gravitational ﬁeld. Consider the case , , .
- Particle falling in a constant gravitational ﬁeld moving in a ﬂuid from which exerts a force on the particle. Consider the case , , . Calculate the limiting velocity of the particle numerically and compare the value obtained to the theoretical expectation.
- Repeat for the case of a force of resistance of magnitude .

- Consider the damped harmonic oscillator
(4.48) Take , and calculate its mechanical energy as a function of time. Is it monotonic? Why? (show that ). Repeat for . When is the system oscillating and when it’s not? Calculate numerically the critical value of for which the system passes from a non oscillating to an oscillating regime. Compare your results with the theoretical expectations.

- Reproduce the results of ﬁgures 4.19–4.22.
- Reproduce the results of ﬁgures 4.23–4.26. Calculate the phase numerically and compare with equation (4.33) .
- Consider a simple model for a swing. Take the damped harmonic oscillator and a driving force which periodically exerts a momentary push with angular frequency . Deﬁne “momentary” to be an impulse given by the acceleration by an appropriately small time interval . The acceleration is for all other times. Calculate the amplitude for and .
- Consider a “half sine” driving force on a damped harmonic oscillator
- Consider the driving force on a damped oscillator given by
- Write a program that simulates identical, independent harmonic oscillators. Take and choose random initial conditions for each one of them. Study their trajectories in phase space and check whether they cross each other. Comment on your results.
- Place the harmonic oscillators of the previous problem in a small square in phase space whose center is at the origin of the axes. Consider the evolution of the system in time. Does the shape of the rectangle change in time? Does the area change in time? Explain...
- Repeat the previous problem when each oscillator is damped with . Take .
- Consider the forced damped oscillator with , , . Study the transient behavior of the system in the plots of , for .
- Consider the forced damped pendulum with , , and study the phase space trajectories for 0.1, 0.19, 0.21, 0.25, 0.5, 0.71, 0.79, 0.85, 1.02, 1.031, 1.033, 1.05, 1.08, 1.1, 1.4, 1.8, 3.1, 3.5, 3.8, 4.2, 4.42, 4.44, 4.445, 4.447, 4.4488. Consider both the transient behavior and the steady state motion.
- Reproduce the results in ﬁgures 4.30.
- Reproduce the results in ﬁgures 4.31.
- Consider the forced damped oscillator with
- Consider the forced damped pendulum with
- Construct the bifurcation diagram by plotting the points .
- Repeat by plotting the points .
- Check whether your results depend on the choice of , . Repeat your analysis for , .
- Study the onset of chaos: Take with and with and compute with the given accuracy the value where the system enters into the chaotic behavior regime.
- The plot the points for . Put 2000 points for each value of and commend on the strength of the chaotic behavior of the pendulum.

Planar Motion

In this chapter we will study the motion of a particle moving on the plane under the inﬂuence of a dynamical ﬁeld. Special emphasis will be given to the study of the motion in a central ﬁeld, like in the problem of planetary motion and scattering. We also study the motion of two or more interacting particles moving on the plane, which requires the solution of a larger number of dynamical equations. These problems can be solved numerically by using Runge–Kutta integration methods, therefore this chapter extends and applies the numerical methods studied in the previous chapter.

In two dimensions, the initial value problem that we are interested in, is solving the system of equations (4.6)

The 4th order Runge-Kutta method can be programmed by making small modiﬁcations of the program in the ﬁle rk.f90. In order to facilitate the study of many diﬀerent dynamical ﬁelds, for each ﬁeld we put the code of the respective acceleration in a diﬀerent ﬁle. The code which is common for all the forces, namely the user interface and the implementation of the Runge–Kutta method, will be put in the ﬁle rk2.f90. The program that computes the acceleration will be put in a ﬁle named rk_XXX.f90, where XXX is a string of characters that identiﬁes the force. For example, the ﬁle rk2_hoc.f90 contains the program computing the acceleration of the simple harmonic oscillator, the ﬁle rk2_g.f90 the acceleration of a constant gravitational ﬁeld etc.

Diﬀerent force ﬁelds will require the use of one or more coupling constants which need to be accessible to the code in the main program and some subroutines. For this reason, we will provide two variables k1, k2 in a common block:

real(8) :: k1,k2

common /couplings/k1,k2

common /couplings/k1,k2

This common block will be accessed by the acceleration functions f3 and f4, the function energy and the main program where the user will enter the values of k1 and k2. The initial conditions are stored in the variables X10 , X20 , V10 , V20 , and the values of the functions of time will be stored in the arrays X1(P) , X2(P) , V1(P) , V2(P) . The integration is performed by a call to the subroutine

call RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt)

The results are written to the ﬁle rk2.dat. Each line in this ﬁle contains the time, position, velocity and the total mechanical energy, where the energy is calculated by the function energy(t,x1,x2,v1,v2):

open(unit=11,file=’rk2.dat’)

do i=1,Nt

write(11,*)T(i),X1(i),X2(i),V1(i),V2(i),&

energy(T(i),X1(i),X2(i),V1(i),V2(i))

enddo

do i=1,Nt

write(11,*)T(i),X1(i),X2(i),V1(i),V2(i),&

energy(T(i),X1(i),X2(i),V1(i),V2(i))

enddo

The code for the function energy, which is diﬀerent for each force ﬁeld, is written in the same ﬁle with the acceleration. The code for the subroutine RKSTEP(t,x1,x2,x3,x4,dt) should be extended in order to integrate four instead of two functions. The full code is listed below:

!========================================================

!Program to solve a 4 ODE system using Runge-Kutta Method

!User must supply derivatives

!dx1/dt=f1(t,x1,x2,x3,x4) dx2/dt=f2(t,x1,x2,x3,x4)

!dx3/dt=f3(t,x1,x2,x3,x4) dx4/dt=f4(t,x1,x2,x3,x4)

!as real(8) functions

!Output is written in file rk2.dat

!========================================================

program rk2_solve

implicit none

integer,parameter :: P=1010000

real(8),dimension(P):: T,X1,X2,V1,V2

real(8) :: Ti,Tf,X10,X20,V10,V20

integer :: Nt, i

real(8) :: k1,k2

common /couplings/k1,k2

real(8) :: energy,E0,EF,DE

!Input:

print *,’Runge-Kutta Method for 4-ODEs Integration’

print *,’Enter coupling constants:’

read *, k1,k2

print *,’k1= ’,k1,’ k2= ’,k2

print *,’Enter Nt,Ti,Tf,X10,X20,V10,V20:’

read *, Nt,Ti,TF,X10,X20,V10,V20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

print *,’ V1(Ti)=’,V10,’ V2(Ti)=’,V20

!The Calculation:

call RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt)

!Output:

open(unit=11,file=’rk2.dat’)

do i=1,Nt

write(11,*)T(i),X1(i),X2(i),V1(i),V2(i),&

energy(T(i),X1(i),X2(i),V1(i),V2(i))

enddo

close(11)

!Rutherford scattering angles:

print *,’v-angle: ’,atan2(V2(Nt),V1(Nt))

print *,’b-angle: ’,2.0D0*atan(k1/(V10*V10*X20))

E0 = energy(Ti ,X10 ,X20 ,V10 ,V20 )

EF = energy(T(Nt),X1(Nt),X2(Nt),V1(Nt),V2(Nt))

DE = ABS(0.5D0*(EF-E0)/(EF+E0))

print *,’E0,EF, DE/E= ’,E0,EF,DE

end program rk2_solve

!========================================================

!The velocity functions f1,f2(t,x1,x2,v1,v2)

!========================================================

real(8) function f1(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

f1=v1 !dx1/dt= v1

end function f1

!--------------------------------------------------------

real(8) function f2(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

f2=v2 !dx2/dt= v2

end function f2

!========================================================

!RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt) is the driver

!for the Runge-Kutta integration routine RKSTEP

!Input: Initial and final times Ti,Tf

! Initial values at t=Ti X10,X20,V10,V20

! Number of steps of integration: Nt-1

! Size of arrays T,X1,X2,V1,V2

!Output: real arrays T(Nt),X1(Nt),X2(Nt),

! V1(Nt),V2(Nt) where

!T(1) = Ti X1(1) = X10 X2(1) = X20 V1(1) = V10 V2(1) = V20

! X1(k) = X1(at t=T(k)) X2(k) = X2(at t=T(k))

! V1(k) = V1(at t=T(k)) V2(k) = V2(at t=T(k))

!T(Nt)= Tf

!========================================================

subroutine RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt)

implicit none

integer :: Nt

real(8),dimension(Nt)::T,X1,X2,V1,V2

real(8) :: Ti ,Tf

real(8) :: X10,X20

real(8) :: V10,V20

real(8) :: dt

real(8) :: TS,X1S,X2S !values of time and X1,X2 at given step

real(8) :: V1S,V2S

integer :: i

!Initialize variables:

dt = (Tf-Ti)/(Nt-1)

T (1) = Ti

X1(1) = X10; X2(1) = X20

V1(1) = V10; V2(1) = V20

TS = Ti

X1S = X10; X2S = X20

V1S = V10; V2S = V20

!Make RK steps: The arguments of RKSTEP are

!replaced with the new ones

do i=2,Nt

call RKSTEP(TS,X1S,X2S,V1S,V2S,dt)

T(i) = TS

X1(i) = X1S; X2(i) = X2S

V1(i) = V1S; V2(i) = V2S

enddo

end subroutine RK

!========================================================

!Subroutine RKSTEP(t,x1,x2,dt)

!Runge-Kutta Integration routine of ODE

!dx1/dt=f1(t,x1,x2,x3,x4) dx2/dt=f2(t,x1,x2,x3,x4)

!dx3/dt=f3(t,x1,x2,x3,x4) dx4/dt=f4(t,x1,x2,x3,x4)

!User must supply derivative functions:

!real function f1(t,x1,x2,x3,x4)

!real function f2(t,x1,x2,x3,x4)

!real function f3(t,x1,x2,x3,x4)

!real function f4(t,x1,x2,x3,x4)

!Given initial point (t,x1,x2) the routine advances it

!by time dt.

!Input : Inital time t and function values x1,x2,x3,x4

!Output: Final time t+dt and function values x1,x2,x3,x4

!Careful!: values of t,x1,x2,x3,x4 are overwritten...

!========================================================

subroutine RKSTEP(t,x1,x2,x3,x4,dt)

implicit none

real(8) :: t,x1,x2,x3,x4,dt

real(8) :: f1,f2,f3,f4

real(8) :: k11,k12,k13,k14,k21,k22,k23,k24

real(8) :: k31,k32,k33,k34,k41,k42,k43,k44

real(8) :: h,h2,h6

h =dt !h =dt, integration step

h2=0.5D0*h !h2=h/2

h6=h/6.0D0 !h6=h/6

k11=f1(t,x1,x2,x3,x4)

k21=f2(t,x1,x2,x3,x4)

k31=f3(t,x1,x2,x3,x4)

k41=f4(t,x1,x2,x3,x4)

k12=f1(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k22=f2(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k32=f3(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k42=f4(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k13=f1(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k23=f2(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k33=f3(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k43=f4(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k14=f1(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k24=f2(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k34=f3(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k44=f4(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

t =t+h

x1=x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2=x2+h6*(k21+2.0D0*(k22+k23)+k24)

x3=x3+h6*(k31+2.0D0*(k32+k33)+k34)

x4=x4+h6*(k41+2.0D0*(k42+k43)+k44)

end subroutine RKSTEP

!Program to solve a 4 ODE system using Runge-Kutta Method

!User must supply derivatives

!dx1/dt=f1(t,x1,x2,x3,x4) dx2/dt=f2(t,x1,x2,x3,x4)

!dx3/dt=f3(t,x1,x2,x3,x4) dx4/dt=f4(t,x1,x2,x3,x4)

!as real(8) functions

!Output is written in file rk2.dat

!========================================================

program rk2_solve

implicit none

integer,parameter :: P=1010000

real(8),dimension(P):: T,X1,X2,V1,V2

real(8) :: Ti,Tf,X10,X20,V10,V20

integer :: Nt, i

real(8) :: k1,k2

common /couplings/k1,k2

real(8) :: energy,E0,EF,DE

!Input:

print *,’Runge-Kutta Method for 4-ODEs Integration’

print *,’Enter coupling constants:’

read *, k1,k2

print *,’k1= ’,k1,’ k2= ’,k2

print *,’Enter Nt,Ti,Tf,X10,X20,V10,V20:’

read *, Nt,Ti,TF,X10,X20,V10,V20

print *,’Nt = ’,Nt

print *,’Time: Initial Ti =’,Ti,’ Final Tf=’,Tf

print *,’ X1(Ti)=’,X10,’ X2(Ti)=’,X20

print *,’ V1(Ti)=’,V10,’ V2(Ti)=’,V20

!The Calculation:

call RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt)

!Output:

open(unit=11,file=’rk2.dat’)

do i=1,Nt

write(11,*)T(i),X1(i),X2(i),V1(i),V2(i),&

energy(T(i),X1(i),X2(i),V1(i),V2(i))

enddo

close(11)

!Rutherford scattering angles:

print *,’v-angle: ’,atan2(V2(Nt),V1(Nt))

print *,’b-angle: ’,2.0D0*atan(k1/(V10*V10*X20))

E0 = energy(Ti ,X10 ,X20 ,V10 ,V20 )

EF = energy(T(Nt),X1(Nt),X2(Nt),V1(Nt),V2(Nt))

DE = ABS(0.5D0*(EF-E0)/(EF+E0))

print *,’E0,EF, DE/E= ’,E0,EF,DE

end program rk2_solve

!========================================================

!The velocity functions f1,f2(t,x1,x2,v1,v2)

!========================================================

real(8) function f1(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

f1=v1 !dx1/dt= v1

end function f1

!--------------------------------------------------------

real(8) function f2(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

f2=v2 !dx2/dt= v2

end function f2

!========================================================

!RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt) is the driver

!for the Runge-Kutta integration routine RKSTEP

!Input: Initial and final times Ti,Tf

! Initial values at t=Ti X10,X20,V10,V20

! Number of steps of integration: Nt-1

! Size of arrays T,X1,X2,V1,V2

!Output: real arrays T(Nt),X1(Nt),X2(Nt),

! V1(Nt),V2(Nt) where

!T(1) = Ti X1(1) = X10 X2(1) = X20 V1(1) = V10 V2(1) = V20

! X1(k) = X1(at t=T(k)) X2(k) = X2(at t=T(k))

! V1(k) = V1(at t=T(k)) V2(k) = V2(at t=T(k))

!T(Nt)= Tf

!========================================================

subroutine RK(T,X1,X2,V1,V2,Ti,Tf,X10,X20,V10,V20,Nt)

implicit none

integer :: Nt

real(8),dimension(Nt)::T,X1,X2,V1,V2

real(8) :: Ti ,Tf

real(8) :: X10,X20

real(8) :: V10,V20

real(8) :: dt

real(8) :: TS,X1S,X2S !values of time and X1,X2 at given step

real(8) :: V1S,V2S

integer :: i

!Initialize variables:

dt = (Tf-Ti)/(Nt-1)

T (1) = Ti

X1(1) = X10; X2(1) = X20

V1(1) = V10; V2(1) = V20

TS = Ti

X1S = X10; X2S = X20

V1S = V10; V2S = V20

!Make RK steps: The arguments of RKSTEP are

!replaced with the new ones

do i=2,Nt

call RKSTEP(TS,X1S,X2S,V1S,V2S,dt)

T(i) = TS

X1(i) = X1S; X2(i) = X2S

V1(i) = V1S; V2(i) = V2S

enddo

end subroutine RK

!========================================================

!Subroutine RKSTEP(t,x1,x2,dt)

!Runge-Kutta Integration routine of ODE

!dx1/dt=f1(t,x1,x2,x3,x4) dx2/dt=f2(t,x1,x2,x3,x4)

!dx3/dt=f3(t,x1,x2,x3,x4) dx4/dt=f4(t,x1,x2,x3,x4)

!User must supply derivative functions:

!real function f1(t,x1,x2,x3,x4)

!real function f2(t,x1,x2,x3,x4)

!real function f3(t,x1,x2,x3,x4)

!real function f4(t,x1,x2,x3,x4)

!Given initial point (t,x1,x2) the routine advances it

!by time dt.

!Input : Inital time t and function values x1,x2,x3,x4

!Output: Final time t+dt and function values x1,x2,x3,x4

!Careful!: values of t,x1,x2,x3,x4 are overwritten...

!========================================================

subroutine RKSTEP(t,x1,x2,x3,x4,dt)

implicit none

real(8) :: t,x1,x2,x3,x4,dt

real(8) :: f1,f2,f3,f4

real(8) :: k11,k12,k13,k14,k21,k22,k23,k24

real(8) :: k31,k32,k33,k34,k41,k42,k43,k44

real(8) :: h,h2,h6

h =dt !h =dt, integration step

h2=0.5D0*h !h2=h/2

h6=h/6.0D0 !h6=h/6

k11=f1(t,x1,x2,x3,x4)

k21=f2(t,x1,x2,x3,x4)

k31=f3(t,x1,x2,x3,x4)

k41=f4(t,x1,x2,x3,x4)

k12=f1(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k22=f2(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k32=f3(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k42=f4(t+h2,x1+h2*k11,x2+h2*k21,x3+h2*k31,x4+h2*k41)

k13=f1(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k23=f2(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k33=f3(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k43=f4(t+h2,x1+h2*k12,x2+h2*k22,x3+h2*k32,x4+h2*k42)

k14=f1(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k24=f2(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k34=f3(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

k44=f4(t+h ,x1+h *k13,x2+h *k23,x3+h *k33,x4+h *k43)

t =t+h

x1=x1+h6*(k11+2.0D0*(k12+k13)+k14)

x2=x2+h6*(k21+2.0D0*(k22+k23)+k24)

x3=x3+h6*(k31+2.0D0*(k32+k33)+k34)

x4=x4+h6*(k41+2.0D0*(k42+k43)+k44)

end subroutine RKSTEP

Consider a particle in the constant gravitational ﬁeld near the surface of the earth which moves with constant acceleration so that

| (5.2) |

The particle moves on a parabolic trajectory that depends on the initial conditions

where is the direction of the initial velocity and is the maximum height of the trajectory.The acceleration ( f3 , f4) and the mechanical energy is coded in the ﬁle rk2_g.f90:

!========================================================

!The acceleration functions f3,f4(t,x1,x2,v1,v2) provided

!by the user

!========================================================

!Free fall in constant gravitational filed with

!g = -k2

real(8) function f3(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f3=0.0D0 !dx3/dt=dv1/dt=a1

end function f3

!--------------------------------------------------------

real(8) function f4(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f4=-k1 !dx4/dt=dv2/dt=a2

end function f4

!--------------------------------------------------------

real(8) function energy(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

energy = 0.5D0*(v1*v1+v2*v2) + k1*x2

end function energy

!The acceleration functions f3,f4(t,x1,x2,v1,v2) provided

!by the user

!========================================================

!Free fall in constant gravitational filed with

!g = -k2

real(8) function f3(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f3=0.0D0 !dx3/dt=dv1/dt=a1

end function f3

!--------------------------------------------------------

real(8) function f4(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f4=-k1 !dx4/dt=dv2/dt=a2

end function f4

!--------------------------------------------------------

real(8) function energy(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

energy = 0.5D0*(v1*v1+v2*v2) + k1*x2

end function energy

In order to calculate a projectile’s trajectory you may use the following commands:

> gfortran -O2 rk2.f90 rk2_g.f90 -o rk2

> ./rk2

Runge-Kutta Method for 4-ODEs Integration

Enter coupling constants:

10.0 0.0

k1= 10.000000 k2= 0.000000

Enter Nt,Ti,Tf,X10,X20,V10,V20:

20000 0.0 0.2 0.0 0.0 1.0 1.0

Nt= 20000

Time: Initial Ti = 0.000000 Final Tf= 0.200000

X1(Ti)= 0.000000 X2(Ti)= 0.000000

V1(Ti)= 1.000000 V2(Ti)= 1.000000

> ./rk2

Runge-Kutta Method for 4-ODEs Integration

Enter coupling constants:

10.0 0.0

k1= 10.000000 k2= 0.000000

Enter Nt,Ti,Tf,X10,X20,V10,V20:

20000 0.0 0.2 0.0 0.0 1.0 1.0

Nt= 20000

Time: Initial Ti = 0.000000 Final Tf= 0.200000

X1(Ti)= 0.000000 X2(Ti)= 0.000000

V1(Ti)= 1.000000 V2(Ti)= 1.000000

The analysis of the results contained in the ﬁle rk2.dat can be done using gnuplot:

gnuplot> set terminal x11 1

gnuplot> plot "rk2.dat" using 1:2 with lines title "x(t)"

gnuplot> set terminal x11 2

gnuplot> plot "rk2.dat" using 1:3 with lines title "y(t)"

gnuplot> set terminal x11 3

gnuplot> plot "rk2.dat" using 1:4 with lines title "vx(t)"

gnuplot> set terminal x11 4

gnuplot> plot "rk2.dat" using 1:5 with lines title "vy(t)"

gnuplot> set terminal x11 5

gnuplot> plot "rk2.dat" using 1:($6-1.0) w lines t "E(t)-(0)"

gnuplot> set terminal x11 6

gnuplot> set size square

gnuplot> set title "Trajectory"

gnuplot> plot "rk2.dat" using 2:3 with lines notit

gnuplot> plot "rk2.dat" using 1:2 with lines title "x(t)"

gnuplot> set terminal x11 2

gnuplot> plot "rk2.dat" using 1:3 with lines title "y(t)"

gnuplot> set terminal x11 3

gnuplot> plot "rk2.dat" using 1:4 with lines title "vx(t)"

gnuplot> set terminal x11 4

gnuplot> plot "rk2.dat" using 1:5 with lines title "vy(t)"

gnuplot> set terminal x11 5

gnuplot> plot "rk2.dat" using 1:($6-1.0) w lines t "E(t)-(0)"

gnuplot> set terminal x11 6

gnuplot> set size square

gnuplot> set title "Trajectory"

gnuplot> plot "rk2.dat" using 2:3 with lines notit

The results can be seen in ﬁgures 5.1 and 5.2. We note a small increase in the mechanical energy which is due to the accumulation of numerical errors.

We can animate the trajectory by writing a script of gnuplot commands in a ﬁle rk2_animate.gpl

icount = icount+skip

plot "<cat -n rk2.dat" \

using 3:($1<= icount ? $4: 1/0) with lines notitle

# pause 1

if(icount < nlines ) reread

plot "<cat -n rk2.dat" \

using 3:($1<= icount ? $4: 1/0) with lines notitle

# pause 1

if(icount < nlines ) reread

Before calling the script, the user must set the values of the variables icount, skip and nlines. Each time gnuplot reads the script, it plots icount number of lines from rk2.dat. Then the script is read again and a new plot is made with skip lines more than the previous one, unless icount < nlines. The plotted “ﬁle” "<cat -n rk2.dat" is the standard output (stdout) of the command cat -n rk2.dat which prints to the stdout the contents of the ﬁle rk2.dat line by line, together with the line number. Therefore the plot command reads data which are the line number, the time, the coordinate , the coordinate etc. The keyword using in

using 3:($1<= icount ? $4: 1/0)

instructs the plot command to use the 3rd column on the horizontal axis and if the ﬁrst column is less than icount ($1<= icount) put on the vertical axis the value of the 4th column if the ﬁrst column is less than icount. Otherwise ($1 > icount) it prints an undeﬁned number (1/0) which makes gnuplot print nothing at all. You may also uncomment the command pause if you want to make the animation slower. In order to run the script from gnuplot, issue the commands

gnuplot> icount = 10

gnuplot> skip = 200

gnuplot> nlines = 20000

gnuplot> load "rk2_animate.gpl"

gnuplot> skip = 200

gnuplot> nlines = 20000

gnuplot> load "rk2_animate.gpl"

The scripts shown above can be found in the accompanying software. More scripts can be found there that automate many of the boring procedures. The usage of two of these is explained below. The ﬁrst one is in the ﬁle rk2_animate.csh:

> ./rk2_animate.csh -h

Usage: rk2_animate.csh -t [sleep time] -d [skip points] <file>

Default file is rk2.dat

Other options:

-x: set lower value in xrange

-X: set lower value in xrange

-y: set lower value in yrange

-Y: set lower value in yrange

-r: automatic determination of x-y range

> ./rk2_animate.csh -r -d 500 rk2.dat

Usage: rk2_animate.csh -t [sleep time] -d [skip points] <file>

Default file is rk2.dat

Other options:

-x: set lower value in xrange

-X: set lower value in xrange

-y: set lower value in yrange

-Y: set lower value in yrange

-r: automatic determination of x-y range

> ./rk2_animate.csh -r -d 500 rk2.dat

The last line is a command that animates a trajectory read from the ﬁle rk2.dat. Each animation frame contains 500 more points than the previous one. The option -r calculates the plot range automatically. The option -h prints a short help message.

A more useful script is in the ﬁle rk2.csh.

> ./rk2.csh -h

Usage: rk2.csh -f <force> k1 k2 x10 x20 v10 v20 STEPS t0 tf

Other Options:

-n Do not animate trajectory

Available forces (value of <force>):

1: ax=-k1 ay= -k2 y Harmonic oscillator

2: ax= 0 ay= -k1 Free fall

3: ax= -k2 vx ay= -k2 vy - k1 Free fall + \

air resistance ~ v

4: ax= -k2 |v| vx ay= -k2 |v|vy - k1 Free fall + \

air resistance ~ v^2

5: ax= k1*x1/r^3 ay= k1*x2/r^3 Coulomb Force

....

Usage: rk2.csh -f <force> k1 k2 x10 x20 v10 v20 STEPS t0 tf

Other Options:

-n Do not animate trajectory

Available forces (value of <force>):

1: ax=-k1 ay= -k2 y Harmonic oscillator

2: ax= 0 ay= -k1 Free fall

3: ax= -k2 vx ay= -k2 vy - k1 Free fall + \

air resistance ~ v

4: ax= -k2 |v| vx ay= -k2 |v|vy - k1 Free fall + \

air resistance ~ v^2

5: ax= k1*x1/r^3 ay= k1*x2/r^3 Coulomb Force

....

The option -h prints operating instructions. A menu of forces is available, and a choice can be made using the option -f. The rest of the command line consists of the parameters read by the program in rk2.f90, i.e. the coupling constants k1, k2, the initial conditions x10, x20, v10, v20 and the integration parameters STEPS, t0 and tf. For example, the commands

[literate={-}{{\texttt{-}}}1]

> rk2.csh -f 2 -- 10.0 0.0 0.0 0.0 1.0 1.0 20000 0.0 0.2

> rk2.csh -f 1 -- 16.0 1.0 0.0 1.0 1.0 0.0 20000 0.0 6.29

> rk2.csh -f 5 -- 10.0 0.0 -10 0.2 10. 0.0 20000 0.0 3.00

> rk2.csh -f 2 -- 10.0 0.0 0.0 0.0 1.0 1.0 20000 0.0 0.2

> rk2.csh -f 1 -- 16.0 1.0 0.0 1.0 1.0 0.0 20000 0.0 6.29

> rk2.csh -f 5 -- 10.0 0.0 -10 0.2 10. 0.0 20000 0.0 3.00

compute the trajectory of a particle in the constant gravitational ﬁeld discussed above, the trajectory of an anisotropic harmonic oscillator (k1 = , k2 = ) and the scattering of a particle in a Coulomb ﬁeld – try them! I hope that you will have enough curiosity to look “under the hood” of the scripts and try to modify them or create new ones. Some advise to the lazy guys: If you need to program your own force ﬁeld follow the recipe: Write the code of your acceleration ﬁeld in a ﬁle named e.g. rk2_myforce.f90 as we did with rk2_g.f90. Edit the ﬁle rk2.csh and modify the line

set forcecode = (hoc g vg v2g cb)

to

set forcecode = (hoc g vg v2g cb myforce)

(the variable $forcecode may have more entries than the ones shown above). Count the order of the string myforce, which is 6 in our case. In order to access this force ﬁeld from the command line, use the option -f 6:

> rk2.csh -f 6 -- .......

Now, we will study the eﬀect of the air resistance on the motion of the projectile. For small velocities this is a force proportional to the velocity , therefore

By taking we obtain the motion of a particle with terminal velocity ( const., ).The acceleration caused by the air resistance is programmed in the ﬁle (k1 , k2 ) rk2_vg.f90:

!========================================================

!The acceleration functions f3,f4(t,x1,x2,v1,v2) provided

!by the user

!========================================================

!Free fall in constant gravitational filed with

!ax = -k2 vx ay = -k2 vy - k1

real(8) function f3(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f3=-k2*v1 !dx3/dt=dv1/dt=a1

end function f3

!--------------------------------------------------------

real(8) function f4(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f4=-k2*v2-k1 !dx4/dt=dv2/dt=a2

end function f4

!The acceleration functions f3,f4(t,x1,x2,v1,v2) provided

!by the user

!========================================================

!Free fall in constant gravitational filed with

!ax = -k2 vx ay = -k2 vy - k1

real(8) function f3(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f3=-k2*v1 !dx3/dt=dv1/dt=a1

end function f3

!--------------------------------------------------------

real(8) function f4(t,x1,x2,v1,v2)

implicit none

real(8) :: t,x1,x2,v1,v2

real(8) :: k1,k2

common /couplings/k1,k2

f4=-k2*v2-k1 !dx4/dt=dv2/dt=a2

end function f4

The results are shown in ﬁgure 5.3 where we see the eﬀect of an increasing air resistance on the particle trajectory. The eﬀect of a resistance force of the form is shown in ﬁgure 5.4.

Consider the simple planetary model of a “sun” of mass and a planet “earth” at distance from the sun and mass such that . According to Newton’s law of gravity, the earth’s acceleration is

| (5.6) |

where , , . When the hypothesis is not valid, the two body problem is reduced to that of the one body problem with the mass replaced by the reduced mass

| (5.7) |

The force of gravity is conservative and the mechanical energy

| (5.8) |

is conserved. If we choose the origin of the coordinate axes to be the center of the force, the equations of motion (5.6) become

where . This is a system of two coupled diﬀerential equations for the functions , . The trajectories are conic sections which are either an ellipse (bound states - “planet”), a parabola (e.g. escape to inﬁnity when the particle starts moving with speed equal to the escape velocity) or a hyperbola (e.g. scattering).Kepler’s third law of planetary motion states that the orbital period of a planet satisﬁes the equation

| (5.10) |

where is the semi-major axis o