Gerson J. Ferreira: Introduction To Computational Physics
Gerson J. Ferreira: Introduction To Computational Physics
Ferreira
Nanosciences Group www.infis.ufu.br/gnano
DOI: 10.13140/RG.2.2.33138.30401
This is a draft!
Please check for an updated version on
http://www.infis.ufu.br/gerson
Currently, I’m not working on this text. I’ll start to review and
improve/finish some sections next semester.
Introduction 1
i
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
ii
Contents
Bibliography 93
iii
Introduction
T HESE ARE THE CLASS NOTES for the course “Computational Physics”, lectured to the
students of Bacharelado em Física de Materiais, Física Médica, and Licenciatura em
Física from the Instituto de Física, Universidade Federal de Uberlândia (UFU). Since
this is an optional class intended for students with diverse knowledge and interest in
Computational Physics and computer languages, I choose to discuss selected topics
superficially with practical examples that may interest the students. Advanced topics
are proposed as home projects to the students accordingly to their interest.
The ideal environment to develop the proposed lessons is the Linux operational
system, preferably Debian-based distributions1 . However, for the lessons and examples
of this notes we use the recently developed Julia2 language, which is open-source and
available to any OS (GNU/Linux, Apple OS X, and MS Windows). This language has a
syntax similar to MATLAB, but with many improvements. Julia was explicitly developed
for numerical calculus focusing in vector and matrix operations, linear algebra, and
distributed parallel execution. Moreover, a large community of developers bring extra
functionalities to Julia via packages, e.g.: the PyPlot package is plotting environment
based on matplotlib, and the ODE package provides efficient implementation of adap-
tive Runge-Kutta algorithms for ordinary differential equations. Complementing Julia,
we may discuss other efficient plotting tools like gnuplot3 and Asymptote4 .
Home projects. When it comes to Computational Physics, each problem has a
certain degree of difficulty, computational cost (processing time, memory, ...) and chal-
lenges to overcome. Therefore, the home projects can be taken by group of students
accordingly to the difficulty of the project. It is desirable for each group to choose a sec-
ond programming language (C/C++, Python, ...) for the development, complementing
their studies. The projects shall be presented as a short report describing the problem,
computational approach, code developed, results and conclusions. Preferably, the text
should be written in LATEX to be easily attached to this notes.
Text-books. Complementing these notes, complete discussions on the proposed
topics can be found on the books available at UFU’s library[1, 2, 3, 4, 5, 6], and other
references presented throughout the notes.
1
CHAPTER 1
Introduction to the Julia language
Hello world!
3
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
notebook interface running on Jupiter (iPython). You can easily upload, download and
edit your codes. Moreover, you can sync your files with Google Drive or GIT.
In JuliaBox you can run your code on the Amazon server for free up to six hours.
After that your virtual machine will be shut down and you have to login again.
Unfortunately, my personal experience with JuliaBox is that it is not stable yet. It’s
still beta. The kernel seems to die often, but it could be a problem with my network
(proxy?). Despite this, I encourage you to try it. Once this is stable, it will be a great tool.
Debian/Ubuntu/Fedora Linux
You can also find the installation files for Linux distributions on the Downloads section
of Julia’s webpage. However, one of the great advantages of using Linux is the advanced
packaging system that helps you keep your software always up to date. Many third-party
applications will have PPA repositories for Ubuntu Linux. So here I’ll quick teach you
how to use them.
If you are new to Linux. Try Ubuntu. Their PPA repositories are great.
Ubuntu Linux
Depending on the Ubuntu version you are using, you may also need to add the PPA
for Julia’s dependent packages: ppa:staticfloat/julia-deps.
That is it! If all goes well, Julia is already installed. If there is a new version and you
want to upgrade, go again to the terminal and run:
4
Julia’s PPA: https://launchpad.net/~staticfloat/+archive/ubuntu/juliareleases
4
Introduction to the Julia language
As a suggestion for the new Linux users, there are a few graphical interfaces for the
APT packaging system. I recommend you to use Synaptic. If it is not already installed,
simply update your package list as in the examples above, and run sudo apt-get
install synaptic.
If you are new to Linux, get ready to be comfortable using the Terminal as it makes
your life much easier. For instance, many of the commands above are probably new to
you. To learn more about them, you can check the man-pages on the Terminal. Please
check and read the content of the commands below (to exit press q).
Debian Linux
Unfortunately you cannot find Debian packages of Julia in their main webpage. In
Debian Jessie (stable) repository you will find an old version of Julia (0.3.2 the last
time I’ve checked). To get the newest version of Julia, you will need Debian Stretch
(testing) or Sid (unstable). It could be tricky to install a Stretch package in Jessie, so I
don’t recommend this. If you use Debian Jessie, the best choice is to use the Generic
Linux binaries from Julia’s webpage Download section.
Here’s my suggestion on how to install the Generic Linux Binaries:
First go to Julia’s webpage Download section and get the file for the 64bit or 32bit
version, depending on your computer architecture. The file name will be something like
julia-0.4.3-linux-x86_64.tar.gz. The .tar.gz extension indicates a compressed
file (like a zip file). You may save it to your Downloads folder.
Open the Terminal. Next we will extract the files from the .tar.gz file and move it
to a better location. Follow these commands:
5
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
The last command, ls, will list the files on the directory. Among other personal
files of yours, you’ll see the .tar.gz file and a new folder with a name similar to
julia-a2f713dea5. Let’s now move it to a better location, back to the Terminal:
# to add Julia’s binary to the path where the system looks for binaries,
# add the following line at the end of the file
export PATH=$PATH:$HOME/.julia/bin
# save the file and close the text editor
Great! If all goes well, close your Terminal and open it again. Every time the Terminal
(BASH) starts, it runs the content of .bashrc. The line we have added to this file tell the
system to search Julia’s folder for binaries. Therefore, now you can start Julia in bash
running $ julia.
1.1.3 iJulia
iJulia6 provides the Jupiter interactive environment for Julia (similar to iPhyton). This is
equivalent to the interface of JuliaBox, but this one runs locally on your computer. To
install it, go to the Terminal, start Julia’s command line interface and run:
5
Juno: junolab.org
6
iJulia: github.com/JuliaLang/IJulia.jl
6
Introduction to the Julia language
The commands above will start the iJulia server, which you have to access via you
Internet browser at 127.0.0.1:8888.
julia>
7
Emacs: http://www.gnu.org/software/emacs/tour/.
To install Emacs on Ubuntu: sudo apt-get install emacs.
7
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
The first thing you see is a message on how to ask for help. Very encouraging! Since
we don’t know anything, let’s ask for help. Follow the instruction on the screen. Type
“?help” for help. Great! From the command line there’s always an easy access to a short
documentation that you can use to check what an specific Julia command does and
what parameters does it take.
Try some simple commands:
Above you can see that Julia (as any other computing language) takes angles in radi-
ans, there’s a pre-defined constant for π, the exponentiation is done with the circumflex
symbol (x y = x^y), you can assign value to variables with =, print text and values on
the screen with println, and if you finish a line with a semicolon (;), it suppresses the
output. That’s a good start.
In the examples above, note that you don’t need to type the multiplication symbol *
between numbers and variables. Julia understands the implied multiplication. This is a
nice trick to keep your expressions short and easy to read.
Let’s try another basic set of commands:
Here we are defining a function f(x,a) that takes two parameters. Note that even
tough we have set the variable a=0 on the previous line, the function definition does
not use this value, since it belongs to a different scope.
The commands whos() and workspace() are very helpful to keep track of memory
use, and to clean all user-defined values and start again when needed.
8
Introduction to the Julia language
• To exit Julia’s command line interface: quit(), or exit(), or press CTRL+D (= ^D).
As usual, let’s use the circumflex symbol ^ to refer to CTRL key for short notation.
• To run a script file, run include("file.jl"). Always use the .jl extension so
your text editor recognizes that your are writing a Julia script;
• Use the TAB key to complete what you are typing (or a list of suggestions). Try to
type "in" and press TAB twice, you’ll see a list of possible commands that start
with "in". Continue the command pressing c to form "inc" and press TAB again.
It’s magic!
• You can go through your commands history pressing the up and down arrows of
your keyboard. Also, you can search the command history pressing ^R and typing
any part of the command you want to recall.
2. In the Terminal, do not start Julia! From the shell, run: $ julia script.jl
9
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
• π = pi = 3.1415926535897...;
• For complex numbers, use im for the imaginary part. For example: z = 5 + 3im;
The variable types in Julia are dynamic. This means that you don’t have to declare it
beforehand. For instance:
The difference between the types is how the variable is stored in memory. A floating-
point number is the computer version of the Real numbers8 . Float64 means that Julia
is using the double precision format (64 bits), which uses one bit for the sign, 11 bits for
the exponent, and 52 bits for the fraction. Since 1 byte = 8 bits, each Float64 variable
will need 8 bytes memory. For instance, if you have a 100 × 100 real matrix, there will
8
For more details, check Wikipedia’s entry on the double precision (64bits) floating-point num-
bers: https://en.wikipedia.org/wiki/Double-precision_floating-point_format, and a gen-
eral discussion on floating-point numbers https://en.wikipedia.org/wiki/Floating_point.
10
Introduction to the Julia language
be 1002 Float64 numbers to store, consuming 80.000 bytes = 80 kB. You can check the
memory use of any variable with the command sizeof(...). Evidently, a 64 bit integer,
e.g. Julia’s Int64, also needs 8 bytes of memory. So, what’s the advantage of having both
Float64 and Int64 types of variables? I’ll leave this to you as a problem.
Note that the complex variable takes twice the memory of a real or integer variable.
It has to store both the real and imaginary part. Boolean variables uses only a single bit!
Since it can be only true or false, a single bit suffices.
In the first line the argument of the function fact(n::Int64) is declared as an Int64.
If you try to call this function as fact(4), you will get the correct result, 24. But if you
call fact(4.0), it fails. The function is not defined for an arbitrary argument.
If you have a function that for different types of parameters may take different
forms, you can use function overloading. This is an extremely useful feature of modern
computer languages. For instance, the factorial of a real number can be defined via the
Riemann’s Γ function,
We, humans, use the same letter Γ for these functions. But the expression used to
calculate the result depends on the parameter being integer or real. Function overload-
ing is the computer language way of making the decision between the two forms of Γ
above. Check the next example.
11
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
function Gamma(t::Float64)
println("We’ll learn how to calculate numerical integrals later...");
end
Try to define all these functions in Julia. Simply copy and paste the code from here.
Now, if you call Gamma(5) the result will be 24 again. But for Gamma(5.0) you will get the
message above from println. Simply call Gamma with no parameters or parenthesis, and
you see the message “Gamma (generic function with 2 methods)”. The different
methods are the different implementations of Gamma with distinct type or number of
parameters.
Please note that this is a simple illustrative example. In practice Julia has built-in
functions to calculate both the factorial and Riemann’s Γ. Built-in are always more
efficient.
12
Introduction to the Julia language
In the example above we have defined a new type of variable (Particle), and we
create two variables of this type representing particles q 1 and q 2 . The parameters (posi-
tion and momenta) are the “fields” of this new variable type. The parameters passed to
these variables when defining q 1 and q 2 are called the “initialization”. Once the variables
exist, you can access and modify its contents using the construct variable.field, as
in the example where we update the position of particle q 1 .
This is a silly example, but eventually we will use this to help us solve the motion of
planets using Newtonian dynamics.
Instead of the declaring the composite type using the type keyword, you could
also work with the immutable, i.e. immutable Particle · · · end. Immutable types work
as constants, they cannot be modified. The main difference between “mutable” and
immutable types is that the first is passed by reference to a function, while the second is
passed by copy9 .
1.3.3 Tuples
In mathematics, a tuple is a ordered list of elements. For instance, a vector r can
be written as 3-tuple: (x, y, z). Julia’s documentation describes this type of data as an
abstraction of the arguments of a function.
sols = squareroot(9);
println("The square roots are ", sols[1], " and ", sols[2]);
In the example above, while the sqrt(· · · ) native command returns only the positive
square root, our example function returns a tuple with all square roots. The tuple is
stored in the variable sols, and each value can be accessed via the construct sols[1],
sols[2], · · · sols[i] · · · sols[n], where the number of elements in the tuple can be
checked with the length command. In the simple example above, we will always have
two entries in the tuple. However, some codes may return a tuple of an arbitrary number
of entries. In this case you may use the command length to check the number of
elements.
In Julia, a tuple is an immutable type: you cannot change its contents, and it will be
passed to a function by copy. Therefore tuples are appropriated when you need to pass
9
If you are a beginner, you probably don’t know what “passing by reference/copy” means. Don’t
worry, we’ll get to that.
13
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
vector or matrix parameters by copy, instead of by reference. Apart from this, they are
equivalent to arrays.
matA*veccol # (3 × 3) · (3 × 1) = (3 × 1)
Run the example above line by line and check the outputs to understand what
each command does. You will see that there’s no difference between the conjugate and
the transpose conjugate here, because we are dealing with real values. Try defining a
complex matrix or vector and check again the difference between these two commands.
There’s a lot more you can do with vectors and matrices of course. We’ll learn
more tricks as we go along. For instance, a nice trick to construct matrices is the
comprehension construct that we present in Section 1.4.5. Other relevant commands
that we may use later on are:
• ones(n), and ones(n,p): equivalent to zeros, but all elements are set to 1;
14
Introduction to the Julia language
• eye(n): creates the identity matrix of order n. This is different than ones(n,n);
• linspace(start, stop, n): creates a range from start to stop with n ele-
ments. To convert the range into an array, use the collect command;
• diagm(v[, k]): constructs a matrix with the k-th diagonal set to the vector v
and all other elements to zero;
To access or change the value of a matrix or array element, use matA[i,j] and veclin[i],
respectively. The first access the element (i , j ) of matrix matA, the second access the
element i of vector veclin from the example above. You may also use ranges to access
multiple elements. Check the example:
15
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Concatenation
Array concatenation in Julia follow the vector and matrix constructions as shown in the
example below. Additionally, one may also explicitly call the *cat-functions: cat, vcat,
hcat, hvcat.
In the next example we start with a vector x and add more elements to it, either
increasing its size, or transforming it into a matrix. Please check carefully the outputs of
each line to understand the different types of concatenations.
# creates a 5x2 matrix setting y as the first column and z as the second
m = [y z];
Keep in mind that concatenation can be useful to store results in a list of initially
unknown size. You can concatenate new results of some calculation into the list as
needed. However, this is not efficient for large vectors, in which case it is better to
known the size of the vector and initialize it beforehand.
In the next example we calculate the average and standard deviation of a list of
random numbers as a function of the number of random values sampled. This is not
the most efficient way to calculate and store the results, but exemplifies an interesting
way of using concatenation.
16
Introduction to the Julia language
subplot(212)
plot(stddev)
The scope of a variable refers to the region of the code where the variable is accessible.
You may, for instance, define two functions of x, say f (x) and g (x). Here x is suppose
to be a free variable, being assigned to a value only when you call the functions; e.g. if
you call f (3) the code inside the function f (x) uses x = 3. From a mathematical point of
view this is quite obvious, right?
What about for a computer? Is it obvious? Remember that a computer does whatever
you code him to do. Therefore the definition of the scope of a variable is extremely
important. Otherwise you and the computer could have some misunderstandings.
Check the example:
17
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
x # still has the same value. Calling h(2) didn’t change it.
x = pi/180;
Do you understand all outputs of the example above? Please try to follow what
happens at each line, and what value of x is used. The variable x is first initialized in the
global scope (x = 3). However, when we define a function, its arguments are threated
as local variables. Therefore the variable x that appears in f (x) is not the same as the
global x. It has the same name, but refers to a different scope. Check Problem 1.4.
Besides the functions, new scopes are initiated every time you start a new code
block; e.g. for and while loops.
18
Introduction to the Julia language
if x < y
println("x is less than y")
elseif x > y
println("x is greater than y")
else
println("x is equal to y")
end
As usual, it checks if the first statement is true (x < y), if so, it runs the first block.
Otherwise it goes to the second condition (elseif). If all conditions are false, it runs
the else block. You may have many elseif conditions if you need it... or none. Both
elseif and else blocks are optional.
One can also use Short-Circuits (AND: &&, OR: ||). The && is the boolean AND
operator. For instance, (testA && testB) will return TRUE only if both testA and
testB are TRUE, and it returns FALSE otherwise. For efficiency it first evaluate testA, if
it returns FALSE, Julia does not have to evaluate testB. Do you agree?
What about the OR operator ||? What is the outcome of (testA || testB) for
different results of testA and textB?
Write and run the following code. It uses a comprehension to create a matrix. We’ll
learn about comprehensions later, but you can probably guess what it does just by
reading the code. Try to understand the content of the matrices Tand and Tor.
19
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
It’s possible to compare numbers to infinity (Inf = ∞), or undefined results (NaN
= not a number). For instance, (1/0 == Inf) returns TRUE, and (0/0 == NaN) also
returns TRUE. Other comparisons can be made with the help of predefined functions:
Essentially, every predefined function that starts with is... is a boolean test. To
find others, open the command line interface, type ?is and press TAB twice.
20
Introduction to the Julia language
When we use while, usually a variable used for the stop condition must be initial-
ized outside, and updated inside the block. Here we initialize with n=0, and update it
incrementing as n += 2, which is a short notation for n = n + 2.
The code above could be better written with a for statement:
1.4.5 Comprehensions
Comprehensions use the structure of the for loop to easily create arrays. For instance,
Example 1.21 uses comprehensions to create the Truth Tables. The general form is
21
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
The first variable after the for refers to the line, and the second to the column. It is
possible to go further into multi-dimensional arrays (tensors), but let’s keep it simple
for now. The extension to n-dimensions is evident.
Now the data is stored in the variable data. Let’s multiply this data by 2, and
save it in a different file. But now the columns will be separated by commas.
Open the file “bar.dat” in the text editor to see the result. This is the format
of a CSV file. You would get the same result running:
22
Introduction to the Julia language
3. Addition + and −
For more details and the full list of operations, please check Julia’s documentation.
Question [1] How to read a data file that has complex numbers?
Answer: The readdlm command will return a string "3+1im" instead of the complex
number 3+1im. In this case the data file will be read as type Any. Below I wrote two
codes to overcome this.
In the first code I read the data as ASCIIString and use parse and eval to evaluate
the expression in Julia as if it were a code. For instance, if one entry of the data file is
"2+2", it will actually be evaluated to 4. Here’s the code:
The problem with the code above is that it first reads everything as Strings. A second
choice would be:
23
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
This version reads the data as type Any and uses the user-defined function myparse(x)
to convert to a number only the Strings.
However, a better choice would be to save the real and imaginary parts separated
in the first place. If your data is a n-columns file, save real part in one column and the
imaginary in the next. If you are saving matrices, save the real and imaginary in different
files. This way is probably more efficient, since for large files the parse(x) command
will probably be too slow.
Answer: Please check the Python and Matplotlib installation. The simple code tested
in class should work:
using PyPlot
x = linspace(0,2pi,50);
plot(x, sin(x), color="red", linewidth=2.0, linestyle="-")
plot(x, cos(x), color="blue", linewidth=2.0, linestyle="--")
Answer: Tuples are immutable and array are mutable. The example in the text and
problem proposed below do not explore this difference. I’ll replace them with a better
discussion soon. The main difference is that a Tuple will be passed to functions by copy,
while arrays are passed by reference. But I still have to confirm this with some example.
1.8 Problems
Problem 1.1: Int64 and Float64
If both formats Int64 and Float64 use the same amount of memory, why having
both defined instead of always using Float64? Check, for instance, Ref. [2] and the
Wikipedia pages mentioned earlier.
24
Introduction to the Julia language
1
if x < −a
ψ(x) = (x/a) 2
if − a ≤ x ≤ +a (1.3)
1 if x > +a
b) Use linspace to create a range of 100 values for x between -2a and 2a. Calculate
ψ(x) over this range using your function. Use PyPlot or any other plotting package to
plot your function.
25
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Write a code to generate a large set of random numbers (start with 1000) and use the
hist(...) command to generate a histogram, and use PyPlot to plot it. The output of
hist(...) is tricky. Pay attention to it and check Julia’s documentation.
a) Use the rand(...) function to get an uniform distribution between [0, 1);
b) Use the randn(...) function to get a standard normal distribution.
26
CHAPTER 2
Differential and Integral Calculus
Step by step.
∂ ∆x
v(t ) = x(t ) = lim . (2.1)
∂t ∆t →0 ∆t
Using similar considerations your teacher has shown you that the integral is a sum
of infinitesimal contributions:
Z t N
v(t 0 )d t 0 = x(0) + lim v t n0 ∆t 0 ,
X ¡ ¢
x(t ) = x(0) + (2.2)
0 ∆t →0 n=0
0
where the sum runs over a discrete set labeled by the integer 0 ≤ n ≤ N that maps the
time axes 0 ≤ t n0 ≤ t .
If you remember all that, great! The main idea behind the numerical differential
and integral calculus is to drop the infinitesimal limit and simply work with the finite
differences. Of course this is too simple. It would be a crude approximation1 . We can do
better! But let’s take it step by step... discrete steps.
27
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
The interpolation then deals with the unknown data in between discrete points.
Let’s start plotting sin(x) between 0 ≤ x ≤ 2π with a few points only.
In the example above, the first plot draws the circles, while the second draws the
lines connecting them, equivalent to the green dots in Fig. 2.1(a). The lines are a linear
interpolation of the points. Try again with more or less points to see how it goes.
Figure 2.1: Starting with the sin(x) defined at only 7 points (blue dotes), we calculate the
(a) constant (rectangle) (b) linear (trapezoidal) and (c) quadratic interpolation points
(green dots). In both panels the yellow lines show the exact sin(x) for comparison.
Linear interpolation
Say you have a finite set of points {x n } labeled by the integer 0 ≤ n ≤ N , and you known
the function f (x) at these points. You may use the data from the example above with
f (x) = si n(x), or any other function of your choice.
Now, assume you want the value of the function at an general point x that does
not belong to your discrete set {x n }, but lies within x a ≤ x ≤ x b . Here b = a + 1 labeling
consecutive points of the set. The most general expression for a linear interpolation
connecting the points {x a , f (x a )} and {x b , f (x b )} is f˜(x) = C 1 x + C 0 . We’ll use the ∼
symbol to refer to the interpolated function. We want f˜(x) to match f (x) at x a and x b ,
therefore we have
f˜(x a ) = C 1 x a +C 0 = f (x a ), (2.3)
f˜(x b ) = C 1 x b +C 0 = f (x b ), (2.4)
28
Differential and Integral Calculus
which defines a system of two equations and two unknowns (C 1 and C 0 ). This set of
equations can be cast in a matrix form M ·C = F , where the matrix M , coefficient vector
C and function vector F are show explicitly below:
µ ¶µ ¶ µ ¶
xa 1 C1 f (x a )
= . (2.5)
xb 1 C 0 f (x b )
This equation can be easily solved as C = M −1 · F . Evidently, you are able to express
the result with paper & pencil in this case. However, the matrix form makes it easy
to generalize to higher order interpolation. The next example shows a code for linear
interpolation, see Fig. 2.1(a). In Problem 2.2 I ask you to generalize this code for a
quadratic interpolation that should result in Fig. 2.1(b).
As you can see, the quadratic interpolation is already very close to the exact sin(x)
function. The interpolations usually work well because we are almost always deal-
ing with analytical functions, smooth functions. Complicated functions will probably
require advanced methods. For instance, functions with singularities.
29
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
30
Differential and Integral Calculus
Rectangle rule
The simplest quadrature method is the rectangle rule. Here the function is assumed
to be constant and equal to f (x n ) within the interval x n − ∆x/2 ≤ x ≤ x n + ∆x/2. The
interpolation resulting from this rule can be seen in Fig. 2.1(a). Consequently, the
integral over this range is
Z x n + ∆x
2
f (x)d x ≈ f (x n )∆x + O (∆x). (2.6)
x n − ∆x
2
Trapezoidal rule
The trapezoidal rule uses the linear interpolation shown in Fig. 2.1(b). It is easy to see
(Prob. 2.3) that this leads to
Z x n+1 f (x n+1 ) + f (x n )
f (x)d x ≈ ∆x + O (∆x 2 ). (2.7)
xn 2
Simpson’s rule
If we use the parabolic interpolation, we get Simpson’s rule:
Z x n+2 h i ∆x
f (x)d x ≈ f (x n ) + 4 f (x n+1 ) + f (x n+2 ) + O (∆x 4 ). (2.8)
xn 3
From Fig. 2.1 one can already expect that the Simpson’s rule to give better results
and the rectangular or trapezoidal rules.
31
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
is then used to integrate the function. Problem 2.1 asks you to implement other rules.
Try to change the number of points in the example, as well as other more complicated
functions.
We may also need to integrate an exact function f (x). This is done in the next
example. Now the trapezoildal function receives the function to be integrated, the
limits of integration, and the number of points to consider. Here the function is never
written as a vector. Instead, it is calculated as needed. This will be more efficient for
integration with a large number of points, as it doesn’t store the function as the vector
flst as above.
32
Differential and Integral Calculus
Note that these examples illustrate two distinct cases. In Example 2.3 we assume
that some previous calculation has given us the axis discretized into the vector xlst as
well as the function f(x) calculated at these points and stored in the vector flst. This
means that we are assuming that we don’t have direct access to an exact function f(x).
Example 2.4 presents the opposite case. Here we do have access to the exact function
f(x). In this case one may use more sophisticated quadrature schemes. We present
some of them in the next section.
Z b Z c Z b
f (x)d x = f (x)d x + f (x)d x. (2.9)
a a c
Then you evaluate each integral on the right hand side independently. You can estimate
the error of each subinterval comparing the result of the trapezoidal and Simpson’s
rules. If the error of a subinterval is too big, you split it into a new pair of subintervals
and repeat the test until you converge to the desired error.
An efficient implementation of such methods can be difficult, and I leave it as a
challenge for the experienced programmers in Problem 2.4.
Luckily, Julia has already the function quadgk. Check its documentation for details
on the parameters. The next example shows a basic usage.
2
Those who are interested in mode details, please check the Wikipedia page for Adaptive quadrature
and references within: https://en.wikipedia.org/wiki/Adaptive_quadrature
33
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
For more advanced routines and multi-dimensional integration, please check Julia’s
Cubature package3 .
(
4 if x 2 + y 2 ≤ 1,
f (x, y) = (2.10)
0 otherwise,
Z
π= f (x, y)d xd y, (2.11)
Ω
1 X N
RN = f (x i , y i ). (2.12)
N i =1
p
The main advantage of the Monte Carlo integral is that its error decays with 1/ (n)
independently of the dimensions of the integral. Therefore this method becomes very
interesting for high-dimensional integrals.
3
Cubature package: https://github.com/stevengj/Cubature.jl
34
Differential and Integral Calculus
∂ f ¯¯ h 2 ∂2 f ¯¯ h 3 ∂3 f ¯¯
¯ ¯ ¯
f (x i + h) = f (x i ) + h + + +··· (2.14)
∂x ¯xi 2! ∂x 2 ¯xi 3! ∂x 3 ¯xi
Here x i is an arbitrary point of our discrete grid labeled by the integer i , and h is the
step size between discrete points.
Let’s use the Taylor expansion to get the 1st and 2nd derivatives of f (x) using two or
three points of the discrete axis x → x i .
∂ f ¯¯
¯
f (x i + h) − f (x i )
≈ + O (h) (2.15)
∂x xi¯ h
∂ f ¯¯ h 2 ∂2 f ¯¯ h 3 ∂3 f ¯¯
¯ ¯ ¯
f (x i − h) = f (x i ) − h + − +··· (2.16)
∂x ¯xi 2! ∂x 2 ¯xi 3! ∂x 3 ¯xi
∂ f ¯¯
¯
f (x i ) − f (x i − h)
≈ + O (h) (2.17)
∂x xi¯ h
35
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
∂ f ¯¯ h 3 ∂3 f ¯¯
¯ ¯
f (x i + h) − f (x i − h) = 2h +2 +··· (2.18)
∂x ¯xi 3! ∂x 3 ¯xi
and now we can truncate the sum on the h 3 term to get
∂ f ¯¯
¯
f (x i + h) − f (x i − h)
≈ + O (h 2 ) (2.19)
∂x xi¯ 2h
∂2 f ¯¯
¯
2
f (x i + h) + f (x i − h) = 2 f (x i ) + h +··· (2.20)
∂x 2 ¯xi
Rearranging the expansion truncated on the h 4 term give us
∂2 f ¯¯
¯
f (x i + h) − 2 f (x i ) + f (x i − h)
≈ + O (h 2 ) (2.21)
∂x xi
2 ¯ h2
Run the next example to get a plot comparing the first derivatives with the exact
result. Try changing the step size from h=pi/5 to pi/50 and pi/500.
# plot the exact result and the approximate derivatives for comparison
clf();
plot(xgrid, cos(xgrid); label="cos(x)")
plot(xgrid, fwd; label="forward")
plot(xgrid, bwd; label="backward")
plot(xgrid, sym; label="symmetric")
legend()
36
Differential and Integral Calculus
In the example above we are assuming that we have access to the exact function f (x)
at any point. This is similar to what we saw in Example 2.4. What happens if we consider
a situation similar to the one in Example 2.3? There we assume that we only known the
function f (x) via the discrete points set by xlst and flst. Check Problem 2.7.
f2 − 0
for i=1, f 10 = , (2.22)
2h
f3 − f1
i=2, f 20 = , (2.23)
2h
f4 − f2
i=3, f 30 = , (2.24)
2h
f5 − f3
i=4, f 40 = , (2.25)
2h
f i +1 − f i −1
· · · f i0 = , (2.26)
2h
f N − f N −2
i=N-1, f N0 −1 = , (2.27)
2h
0 − f N −1
i=N, f N0 = . (2.28)
2h
The zero in the first and last lines refer to f 0 = 0 and f N +1 = 0, respectively.
The set of equations above can be put in a matrix form as
f 10 0 1 0 0 0 0 0 0 0 f1
f 20
−1 0
1 0 0 0 0 0 0 f2
f 30
0 −1 0
1 0 0 0 0 0 f 3
f 40
0 0 −1 0 1 0 0 0 0 f4
1
···
= 0 0 0 −1 0 1 0 0 0 · · · (2.29)
2h
f i0 0 0 0 0 −1 0 1 0 0 f i
···
0 0 0 0 0 −1 0 1 0 ···
f 0 0 0 0 0 0 0 −1 0 1 f N −1
N −1
f N0 0 0 0 0 0 0 0 −1 0 fN
37
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
plot(x, dfdx);
N
X
(g ∗ f )[n] = g [m] f [n + N − 1 − m], (2.30)
m=1
where the index of our arrays g [m] and f [m] start at 1 and N is the length of the array g .
Let’s refer to the vector g as the kernel, and f as our function. If the kernel is
1
g = 2h (1, 0, −1), the convolution above becomes
1 ³ ´
(g ∗ f )[n] = f [n + 1] − f [n − 1] , (2.31)
2h
which is exactly our definition of the symmetric first derivative.
In Julia we can use the conv function for 1D convolutions and the conv2 function
for two-dimensional (2D) convolutions. The next example can be compared with the
previous one. Note that after the convolution we remove end points to keep dfdx with
the same size of x.
plot(x, dfdx);
38
Differential and Integral Calculus
2
∂
In 2D the kernel becomes a matrix. For instance, the kernel for ∂x∂y represented by
the symmetric differences with step sizes h x and h y in each direction is
0 1 0
1
g= 1 0 −1 . (2.32)
4h x h y
0 −1 0
∂2
# 2D kernel for ∂x∂y
kernel = (1.0/(4*hx*hy))*[0.0 1.0 0.0; 1.0 0.0 -1.0; 0.0 -1.0 0.0];
2.4 Problems
Problem 2.1: Checking the simplest numerical integration
Consider the functions f (x) = sin(x), g (x) = cos(x), and h(x) = e x .
4
ForwardDiff package: https://github.com/JuliaDiff/ForwardDiff.jl
39
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
a) What is the exact result for the integration of f (x) and g (x) on the interval 0 ≤ x ≤
2π? Try also the interval 0 ≤ x ≤ π. What about the integration of h(x) over 0 ≤ x ≤ 1?
b) Try to numerically integrate these functions as initially described at the introduc-
tion of this chapter. Simply dropping the infinitesimal limit. Compare the deviation
from the exact result as a function of the number of points used in the integration in a
log-plot. Useful Julia commands: linspace, sum.
c) Complement item (b) as you learn new methods: compare the precision of dif-
ferent methods (trapezoidal, Simpson’s, Boole’s, Monte Carlo, ...) as a function of the
number of points being used.
2
• f (x) = e −x , for −5 < x < 5;
p
• f (x) = x, for 0 < x < 10;
40
Differential and Integral Calculus
(a) Write a code to calculate the first and second derivatives using this discrete data.
Here the derivative step size has to be equal to the grid step size. You will face a difficulty
at the end points. What can you do?
(b) Test your code with the functions of the previous Problem. Try to vary the grid
step to see how the precision changes.
41
CHAPTER 3
Ordinary Differential Equations
ENIAC, since 1946
E NIAC, Electronic Numerical Integrator And Computer[8], the first electronic general-
purpose computer was designed by the US Army and finished in 19461 . The goal was
to numerically calculate artillery firing tables by solving sets of differential equations.
Essentially, F = m a with quite a few complications.
Indeed we are surrounded by differential equations...
∂2 r
m =F, (3.1)
∂t 2
∂ψ(r , t )
iħ = H ψ(r , t ), (3.2)
∂t
∂B ∂D
∇ · D = ρ, ∇ · B = 0, ∇ × E = − , ∇×H = +J, (3.3)
∂t ∂t
∂u
− α2 ∇2 u = 0, (3.4)
∂t
∂2 u
− c 2 ∇2 u = 0, (3.5)
∂t 2
... to list a few. We also have Navier–Stokes, general relativity, etc.
The unknown quantity in a differential equation can be a function of a single vari-
able, like the position that depends on time on Newton’s second law above (r ≡ r (t )).
These cases are labeled ordinary differential equations (ODE). If your function
depends upon two or more variables and your differential equation has derivatives
on these variables, it becomes a partial differential equation (PDE). This is the
case of all other examples above. PDEs and ODEs are intrinsically different. Important
theorems of ODEs do not apply for PDEs.
1
ENIAC: https://en.wikipedia.org/wiki/ENIAC
43
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Let us compare two simple cases, the Laplace equation in one and two dimensions:
∂2 f (x)
= 0, (3.6)
∂x 2
∂ ∂2
µ 2 ¶
+ f (x, y) = 0. (3.7)
∂x 2 ∂y 2
The 1D equation is a second order ODE. This means that its most general solution
is defined up to two unknown constants. These are set by two initial or boundary
conditions. In this case the general solution is a straight line,
f (x) = c 1 x + c 0 , (3.8)
where p(z), q(z̄) are arbitrary analytical functions of the complex variables z = x + i y,
z̄ = x − i y.
For now, we will focus on ordinary differential equations (ODE). These can
be classified into three main categories: (i) initial value problems; (ii) boundary-value
problems; and (iii) eigenvalue problems. Here we shall restrict ourselves to introductory
discussion on the methods to solve ODEs, for more details please check Ref. [4, 6].
d 2r F
= , (3.10)
dt2 m
requiring the initial position r (0) and velocity v (0) to be solved.
Let’s consider the one dimensional case to guide our discussion. Just keep in mind
that a generalization to more dimensions is immediate. First we write Newton’s second
law in a more general form of an arbitrary second order initial value problem ODE,
d 2x
= g (x, t ). (3.11)
dt2
44
Ordinary Differential Equations
This second order ODE can be split into a pair of coupled first order ODEs using
v = dd xt , yielding
dv dx
= g (x, t ), and = v. (3.12)
dt dt
Finally, we can define vectors y = [v(t ), x(t )] and f = [g (x, t ), v(t )], such that the
equations above can be cast as
dy
= f. (3.13)
dt
d 2 θ(t )
= −ω2 sin[θ(t )], (3.14)
dt2
where θ(t ) is the angular displacement, ω2 = g /`, g ≈ 9.8 m/ss is the gravity, and ` is
the pendulum length.
For small oscillations we can use Taylor’s expansion to write sin θ ≈ θ. This leads to
the usual harmonic solution θ(t ) = A cos(ωt + φ), where the amplitude A and the phase
constant φ are set by initial conditions.
Let us try to go beyond the small oscillations approximation and numerically solve
Eq. (3.14) with the content of the next sections. To do this, first we have to rewrite
Eq. (3.14) in the form of a set of coupled first order ODEs as we generically did above.
With this purpose, define2 x(t ) = θ(t ) and v(t ) = θ̇(t ). The pendulum equation of motion
becomes
dv dx
= −ω2 sin[θ(t )], and = v, (3.15)
dt dt
which can be cast as Eq. (3.13) setting y = [θ̇(t ), θ(t )] and f = [−ω2 sin[θ(t )], θ̇(t )]. To
solve these equations we will have to specify the initial position x(0) and velocity v(0).
In the next sections we will learn methods to solve differential equations and apply
them to this pendulum example. Note that replacing sin θ → θ in Eq. (3.15) allows you
to compare the results with the exact solution for small oscillations. Once you have
confidence that your code works, put back the full sin θ dependence and compare the
small oscillations solution with the numerical solution for large amplitudes.
2 dθ
Within the text I’ll use the dot notation to refer to time derivatives, i.e. θ̇ =
dt
45
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
y (t + τ) − y (t )
= f (t ), (3.16)
τ
where τ is the discrete time step. Labeling the discrete time t n = t 0 + nτ with integers n,
we can define y (t n ) = yn and f (yn , t n ) = fn . Rewriting the equation above give us
In the example above, the initial displacement of the pendulum is set by −π < x 0 <
π. For |x 0 | ¿ π you should see a good agreement with the exact solution for small
oscillations. For large |x 0 | . π the numerical simulation will show plateaus as the
46
Ordinary Differential Equations
pendulum slows down near x 0 = ±π. However, to see this you will probably have to
reduce τ to recover stability. Try to play with the parameters.
Our prototype for a differential equation, Eq. (3.13), can be written in a integral form as
Z t +τ ³ ´
y (t + τ) = y (t ) + f y(t 0 ), t 0 d t 0 . (3.18)
t
If we approximate the integrand by a constant value evaluated at the lower limit of the
integral, i.e. f (y(t 0 ), t 0 ) = f (y(t ), t ), we get the Euler method again: yn+1 = yn + τfn .
Instead, if we approximate the integrand by the midpoint, i.e. f (y(t 0 ), t 0 ) = f (y(t +
τ/2), t + τ/2), we get
τ´ ³
yn+1 = yn + τf yn+ 1 , t n +
. (3.19)
2 2
But we don’t known yn+1/2 . To proceed, there’s two possibilities:
(i) The explicit RK2 is obtained using the Euler method to express yn+1/2 = yn + 12 τfn ,
yielding
³ τ τ´
yn+1 = yn + τf yn + f (yn , t n ), t n + . (3.20)
2 2
(ii) The implicit RK2 is obtained if we use the midpoint rule to express yn+1/2 =
1
(y + yn+1 ),
2 n
³y +y
n n+1 τ´
yn+1 = yn + τf , tn + . (3.21)
2 2
47
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
[k]
superscript [k] in yn+1 refers to the level of the iteration. Then iterate the solution until
convergence,
[0]
yn+1 = yn + τfn , (3.22)
³ yn + y [0] τ´
[1] n+1
yn+1 = yn + τf , tn +
, (3.23)
2 2
³ yn + y [1] τ´
[2] n+1
yn+1 = yn + τf , tn + , (3.24)
2 2
..
. (3.25)
³ yn + y [k] τ´
[k+1] n+1
yn+1 = yn + τf , tn + . (3.26)
2 2
[k+1] [k]
Convergence is achieved for large k when yn+1 − yn+1 is sufficiently small.
It is also possible to solve Eq. (3.20) using root-finding algorithms (e.g. Newton’s
method). Notice that the only unknown quantity is yn+1 , so let’s define an auxiliary
function R(yn+1 ),
³y +y
n n+1 τ´
R(yn+1 ) = yn+1 − yn − τf , tn + , (3.27)
2 2
such that the possible solutions of Eq. (3.20) are the roots of R(yn+1 ) = 0.
The RK4 method is by far the most popular method to solve ODEs. Its implementation
is not much more complicated than the simples Euler method, but its precision is far
superior with a global error O (τ4 ). The iteration rule for the RK4 is
k1 = f (yn , t n ), (3.28)
³ τ τ´
k2 = f yn + k1 , t n + , (3.29)
2 2
³ τ τ´
k3 = f yn + k2 , t n + , (3.30)
³ 2 2´
k4 = f yn + τk3 , t n + τ , (3.31)
τ³ ´
yn+1 = yn + k1 + 2k2 + 2k3 + k4 . (3.32)
6
Since this is the most used method for differential equations, I will not show an
implementation example here. Instead, I’ll leave its implementation as a problem for
the students.
48
Ordinary Differential Equations
Stiff differential equations are those where the numerical solution require a step size
excessively small when compared with the actual smoothness of the solution. Typically,
a stiff equation solved with a large step size show spurious oscillations.
A stiff differential equation can be as simple as
d y(t )
= −15y(t ), (3.33)
dt
which has an exact solution y(t ) = e −15t for the initial condition y(0) = 1. Note that
y(t ) > 0 for any t > 0. However, if you apply the Euler method with f (y) = −15y, you will
get y n+1 = y n − 15τy n . For a large τ, the Euler method may give negative values for y n+1 .
Indeed, if you solve this equation with the Euler method with a large τ, you will get the
spurious oscillations.
A better choice is to use the implicit RK2 method. In this particular case you can
actually solve Eq. (3.21) analytically for y n+1 to obtain
1 − 15
2 τ
y n+1 = yn . (3.34)
1 + 15
2
τ
The next example compares the numerical solutions of this stiff equation obtained
with the explicit and implicit RK2 methods. Try running it with different step sizes τ and
compare the results.
49
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
texact = 0:0.01:1;
exact = exp(-15*texact); # exact solution for comparison
d y(t )
= y 2 (t ) − y 3 (t ). (3.35)
dt
50
Ordinary Differential Equations
If you put f (y) = y 2 − y 3 into the implicit RK2 Eq. (3.21), you will have three roots. Which
one should you use? In this case it is better to use the fixed-point iteration method.
In the next example implement the implicit RK2 method using the fixed-point
iteration for an initial condition y(0) = y 0 within a time range 0 ≤ t ≤ 2/y 0 . For small y 0
the ODE become stiff and the solution will require a very small τ to converge. You may
use the implementation of the explicit RK2 from the previous example to compare with
the implicit RK2 again.
tau = (2.0/y0)/5;
te, ye = explicitRK2(rhs, 0.0, 2/y0, tau, y0, 1e-6);
ti, yi = implicitRK2(rhs, 0.0, 2/y0, tau, y0, 1e-6);
clf();
plot(te, ye; label="Explicit");
plot(ti, yi; label="Implicit");
legend();
axis([0.0, 2/y0, -0.1, 1.5]);
After checking the results of the code above, try reducing the step size to tau =
(2.0/y0)/10 and tau = (2.0/y0)/100 to see how the solution improves. Next, reduce
y 0 to y0 = 0.01 and the solutions will split again. Reduce τ until they match. Now try
for y0 = 0.0001. As you try different parameters, zoom in into the y = 1 plateau to see
the oscillations on the solutions.
51
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
dy
= F (t , y ), (3.36)
dt
Input: F is a function that receives the current time t and the corresponding vector
y, and returns a vector as defined by the right hand side of Eq. (3.36). Additionally,
odeXX receives a vector with the initial conditions y0 (= y (0)), and the range of time
over which the solution is desired (tspan). The last parameter, keywords, are allow
you to set extra configurations, like the error tolerance. I suggest you use the key-
word points=:specified, so that the output is returned only for each time instant in
tspan = 0:dt:tmax.
Output: the package returns tout and yout. The first, tout, is the list of time in-
stants t at which the solution y (t ) was calculated. If the keyword points=:specified
was passed to odeXX, then tout = tspan, otherwise it may vary. The solutions y (t ) are
returned in yout.
Next I adapt Example 3.1 to run with the ODE package.
3
ODE: https://github.com/JuliaLang/ODE.jl
52
Ordinary Differential Equations
53
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
problem at hand. For instance, on an oscillating string one might have the ends of
the string fixed. In this case the string oscillation obey the wave equation, while fixed
ends conditions must be imposed on the solution, constraining it to have vanishing
oscillation at these end points. The solution of an differential equation that satisfy the
specified boundary conditions is unique. For ordinary differential equations, there’s
two types of possible cases: (i) Dirichlet; and (ii) Neumann boundary conditions.
To guide our discussion, let’s consider a general second order differential equation
of the form
54
Ordinary Differential Equations
takes the form of the Poisson equation, ∇2 φ(r ) = −ρ(r )/²0 . In three-dimensions the
Poisson equation is a partial differential equation (PDE), as it has partial derivatives in
x, y and z. In this Chapter we are interested in ordinary differential equations (ODE)
only, therefore we will consider the one-dimensional case of the Poisson equation,
∂2 φ(z) ρ(z)
=− . (3.38)
∂z 2 ε0
Let’s say that we have metallic contacts setting the electrostatic potential at z = ±L
as φ(±L) = ±φ0 , and there’s a narrow charge distribution at z = 0 set as ρ(z) = qδ(z).
You can solve this problem analytically to get
q φ0
φ(z) = − (|z| − 1) + z. (3.39)
2²0 L
This solution is shown in Fig. 3.1.
Figure 3.1: Illustration of the solution of the Poisson equation given by Eq. (3.39) for
φ0 = L = q = ε0 = 1.
In this section we will learn how to solve this equation numerically for an arbitrary
charge distribution ρ(z). Since it might be difficult to deal with δ distributions numeri-
(z−z 0 )2
−
cally, we shall consider, for instance, a Gaussian charge distribution ρ(z) = qe 2Γ2 ,
where z 0 is the center of the distribution and Γ is the broadening. For z 0 = 0 and small Γ
you should be able to reproduce the analytical solution above.
d d
½ · ¸ ¾
p(x) + q(x) y(x) = λw(x)y(x) + r (x), (3.40)
dx dx
where p(x), q(x), w(x) and r (x) are functions specified by the problem at hand, while
y(x) is the unknown function that we want to find. For instance, the Poisson equation
55
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
in Eq. (3.38) is set by using p(x) = 1, q(x) = w(x) = 0, and r (x) = −ρ(x)/²0 . If w(x) 6= 0, λ
is the eigenvalue of the equation. We will discuss the class of eigenvalue problems later
on in this Chapter, therefore, for now we shall take w(x) = 0. Moreover, let’s assume that
p(x), q(x) and r (x) are analytical functions over the domain of interest.
Let’s first present useful properties of the Sturm-Liouville equation without properly
deriving them. For the derivations and more details please check specialized books on
Mathematical Physics [refs] and Problem 3.4. Next, we use these properties to establish
the Wronskian method to solve Sturm-Liouville problems.
x p 1 (x 0 ) 0
½ Z ¾
W (x) = W (x 0 ) exp − d x , (3.41)
x0 p(x 0 )
where p 1 (x) = ddx p(x), and x 0 belongs to the domain of x. Therefore, if the Wronskian
W (x 0 ) 6= 0 in a specific point x = x 0 , it will be non-zero over the whole domain.
(3) Uniqueness of the solution. Since the Sturm-Liouville equation is a second-order
ordinary differential equation, its solution is unique if it satisfies the ODE and two
boundary (or initial) conditions. As a consequence, its most general solution can be
written as a linear combination of two linearly independent solutions.
The properties above form the basis need to solve the homogeneous Sturm-Liouville
problem using the Wronskian method. But before discussing this method, let’s check
the inhomogeneous Sturm-Liouville problem.
56
Ordinary Differential Equations
where y a (x) and y b (x) are linearly independent solutions of the homogeneous equation
L y a/b (x) = 0, and y p (x) is the particular solution of the inhomogeneous equation
L y p (x) = r (x). Here again, a and b are arbitrary constants to be set by the boundary
conditions.
Since y a (x) and y b (x) are solutions of the homogeneous case, property (2) above
follows and the Wronskian criteria can be used to verify or assure that y a (x) and y b (x)
are linearly independent solutions. Property (3) also follows, and the solution y(x)
is unique if and only if it satisfies the ODE L y(x) = r (x) and the required boundary
conditions.
First step: find two linearly independent solutions of the homogeneous equation
Our first step is to find a pair of linearly independent solutions y a (x) and y b (x) that
satisfy the homogeneous equation L y a/b (x) = 0. Accordingly to property (2) above,
y a (x) and y b (x) will be linearly independent if their Wronskian W (x) 6= 0 at any point x.
For practical purposes, we chose to analyze the Wronskian at x = x 0 , the left end point
of the domain. Namely, we want
W (x 0 ) = y a0 (x 0 )y b (x 0 ) − y a (x 0 )y b0 (x 0 ) 6= 0. (3.43)
Notice that the final solution will be given by y(x) in Eq. (3.42). Therefore the bound-
ary conditions must apply to y(x), and not to the auxiliary functions y a (x), y b (x) or
y p (x). Therefore we can attribute auxiliary boundary conditions to y a (x) and y b (x) in
order to assure that the condition above is satisfied and the pair [y a (x), y b (x)] forms a
set of linearly independent solutions. For instance, we may choose
y a (x 0 ) = 0, and y a0 (x 0 ) = A 1 , (3.44)
y b (x 0 ) = A 2 , and y b0 (x 0 ) = 0, (3.45)
57
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
use any initial-value problem method to obtain y a/b (x) satisfying the homogeneous
equation L y a/b (x) = 0.
Now we need to find y p (x). Since this is also an auxiliary function, its boundary or initial
conditions are arbitrary. Let’s use y p (x 0 ) = B 1 and y p0 (x 0 ) = B 2 , where B 1 and B 2 are
arbitrary constants. We can use any method of the initial-value problems to find y p (x).
In this final step we must impose the physical boundary conditions stated by our
problem. Namely, we want y(x 0 ) = y 0 and y(x 1 ) = y 1 . From Eq. (3.42) we have
y(x 0 ) = a y a (x 0 ) + b y b (x 0 ) + y p (x 0 ) = b A 2 + B 1 , (3.46)
y(x 1 ) = a y a (x 1 ) + b y b (x 1 ) + y p (x 1 ), (3.47)
where y a (x 0 ) = 0, y b (x 0 ) = A 2 , and y p (x 0 ) = B 1 where the auxiliary initial conditions
set above. The quantities y a (x 1 ), y b (x 1 ), and y p (x 1 ) are known from the solution of the
auxiliary initial-value problems. To satisfy the boundary conditions, the coefficients a
and b must be
y0 − B1
b= , (3.48)
A2
y 1 − b y b (x 1 ) − y p (x 1 )
a= . (3.49)
y a (x 1 )
Combining these quantities, we have the all terms of Eq. (3.42) to compose our final
solution y(x).
Let’s apply the Wronskian method to the Poisson equation to reproduce numerically the
example of the beginning of this section; see Fig. 3.1. Let’s write the Poisson equation,
Eq. (3.38), using the notation of the Sturm-Liouville problem above. It reads
d2
y(x) = −ρ(x). (3.50)
d x2
Next we use the ODE package to numerically find the auxiliary functions y a (x), y b (x)
and y p (x) with appropriate initial conditions, and combine them at the end to satisfy
the physical boundary condition.
The homogeneous version of the 1D Poisson’s equation (ρ(x) = 0) is actually Laplace’s
equation in one dimension. Therefore it would be quite easy to find the solutions y a (x)
and y b (x) of the homogeneous equation. It’s simply y a (x) = (x − x 0 )A 1 and y b (x) = A 2 .
However, in the numerical code below we choose to find these simple solutions numeri-
cally just to exemplify how to proceed in a more difficult scenario.
58
Ordinary Differential Equations
# find y a (z)
A1 = 1.0;
ic = [0.0; A1]; # = (0, A1)
rhs(z, y) = [y[2]; 0.0];
za, ya = ode45(rhs, ic, z; points=:specified);
ya = map(k->k[1], ya);
# find y b (z)
A2 = 1.0;
ic = [A2; 0.0]; # = (A2, 0)
rhs(z, y) = [y[2]; 0.0];
zb, yb = ode45(rhs, ic, z; points=:specified);
yb = map(k->k[1], yb);
# find y p (z)
B1 = 0.0;
B2 = 0.0;
ic = [B1; B2]; # = (B1, B2)
rhs(z, y) = [y[2]; -rho(z)/eps0];
zp, yp = ode45(rhs, ic, z; points=:specified);
yp = map(k->k[1], yp);
plot(z, y);
Let’s consider a free electron in one dimension colliding with a general shaped barrier
set by a potential V (x). The dynamics of this electron is tunnel or scatter back as it
collides with the barrier. Here we want to calculate the transmission T and reflection R
probabilities.
59
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
1 ∂2
H =− + V (x). (3.51)
2 ∂x 2
Here we use atomic units (ħ = 1, m = 1), and V (x) is a general potential within the
scattering region 0 ≤ x ≤ L, and V (x) = 0 outside these limits.
Referring to the region x < 0 as I, let’s assume that the state of the electron is a linear
combination of the injected and reflected waves,
ψI (x) = e i kx + r e −i kx , (3.52)
p
where r the reflection coefficient, and k = 2ε and ε is the electron energy.
We’ll refer to the region x > L as III. There the electron state is composed solely by
the transmitted wave,
ψI I I (x) = t e i kx , (3.53)
ψa (0) ψb (0) −1
0 a 1
ψ0 (0) ψ0b (0) i k 0 b i k
a
= . (3.55)
ψa (L) ψb (L) 0 e i kL r 0
ψ0a (L) ψ0b (L) 0 i ke i kL t 0
From the equation above one can easily extract t . The transmission probability is
then T = |t |2 , and the reflection probability is R = |r |2 . You can check that T + R = 1.
In Fig. 3.2 we consider the transmission over a Gaussian barrier
1 2
V (x) = e − 2 (x−x0 ) , (3.56)
60
Ordinary Differential Equations
Figure 3.2: (left) Illustration of the Gaussian barrier with the injected, reflected and
transmitted waves at outer regions. (right) Transmission probability T as a function of
the energy ε.
d 2 y(x)
+ |y(x)| = 0, (3.57)
d x2
which is non-linear due to the absolute value in the second term. Consider that the
boundary conditions are y(0) = 0 and y(4) = −2.
One way of solving this problem is the shooting method. First, convert the second order
differential equations into a pair of coupled first order differential equations. This is
the same procedure used at the begging of this chapter. We know how to solve initial
value problems easily. Therefore, consider the auxiliary initial conditions y(0) = y 0 and
y 0 (0) = v 0 . Clearly, it is useful to set y 0 = 0 to automatically satisfy our desired boundary
condition. The problem is v 0 . What is the appropriate value of v 0 that satisfies our
boundary conditions? Namely, we want v 0 set such the evolution of the initial value
problem yields y(4) = −2.
In the general there’s no direct approach to find the appropriate value of v 0 . In our
example there’s actually two possible values of v 0 that yield y(4) = −2. To see this, I
invite the reader to write a code using the Runge-Kutta method, or Julia’s ODE package
61
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
to solve the initial value problem for the example above for different values of the initial
condition y 0 (0) = v 0 . Evolving the solution from x = 0 to x = 4 we can extract y(4) for
each value of v 0 and plot y(4) × v 0 . This is shown in Fig. 3.3.
Figure 3.3: (top) Values of y(4) obtained propagating the non-linear differential equation
from x = 0 to x = 4 with the initial condition set to y 0 (0) = v 0 , as a function of v 0 . There
are two values of v 0 that satisfy y(4) = −2, as indicated by the circles. (bottom) The two
possible solutions y(x) × x for the values of v 0 found to satisfy the boundary conditions.
d d
½ · ¸ ¾
p(x) + q(x) y(x) = λw(x)y(x), (3.58)
dx dx
where p(x), q(x) and w(x) are given functions set by the problem at hand, while λ and
y(x) are the unknowns that we want to find. For a set of boundary conditions, this
differential equation have solutions only for particular values of λ, which are called the
eigenvalues. The solutions y(x) associated with each particular λ are the eigenfunctions.
If we vectorize this equation using finite differences, the function y(x) becomes a vector:
the eigenvector.
There are many ways of solving an eigenvalue problem numerically. But let’s first
check two analytical cases to serve as examples where we can try our numerical ap-
proaches.
62
Ordinary Differential Equations
∂2 y(x, t ) 1 ∂2 y(x, t )
− 2 = 0, (3.59)
∂x 2 v ∂t 2
where y(x, t ) is the string profile as a function of space x and time t , and v is the wave
velocity on the string. This is a partial differential equation, but in this chapter we are
dealing with ordinary differential equations only. Since for now we are only interested
in the normal modes of oscillation, we can Fourier transform the equation from time t
to frequency w, yielding
∂2 y(x, ω) ω2
+ 2 y(x, ω) = 0. (3.60)
∂x 2 v
This equation takes the form of eigenvalue Sturm-Liouville problem if we set p(x) = 1,
q(x) = 0, w(x) = 1, and ω2 = v 2 λ.
If the string is fixed at its end points, the boundary conditions are y(0, t ) = 0, and
y(`, t ) = 0 for any t . Equivalently, the Fourier transformed y(x, ω) also satisfy the same
boundary conditions: y(0, ω) = 0 and y(`, ω) = 0 for any ω.
You have probably seen already in a theoretical physics class that the solution to
this problem is
³ω ´
n
y(x, ωn ) = A sin x , (3.61)
v
where k n = ωn /v are the quantized wave-numbers, ωn = nπv/` are the quantized
frequencies, and the integer n labels the normal modes of oscillation. The amplitude A
depends on the initial condition, which we will not consider yet.
1 ∂2
− ψ(x) + V (x)ψ(x) = εψ(x), (3.62)
2 ∂x 2
where we use atomic units (ħ = 1, m = 1) for simplicity. Here V (x) is the electrostatic
potential that confines the electron, ψ(x) is the wave-function and ε is the energy. This
is indeed a Sturm-Liouville eigenvalue problem where the eigenvalue is the energy ε
and the eigenfunction is the wave-function ψ(x).
If the electron is trapped in a box of width ` with hard walls, such that V (x) = 0
for 0 < x < `, and V (x) = ∞ outside, the wave-function must satisfy the boundary-
conditions ψ(0) = 0 and ψ(`) = 0.
63
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Since V (x) = 0 inside the box, the problem is very similar to the oscillations on a
string. The solutions are
d2
½ ¾
+ q(x) y(x) = λy(x). (3.64)
d x2
We have seen in Chapter 2.3.2 that we can represent the derivative operators as
matrices. Particularly, the second derivative becomes
y 100 −2 1 0 0 0 0 0 0 0 y1
y 200
1 −2 1
0 0 0 0 0 0 y2
y 300
0
1 −2 1 0 0 0 0 0 y3
y 400
0 0 1 −2 1 0 0 0 0 y4
d2 1
y(x) = ··· = 0 0 0 1 −2 1 0 0 0 · · · , (3.65)
2 2
dx h
y i00 0 0 0 0 1 −2 1 0 0 yi
··· 0 0 0 0 0 1 −2 1 0 ···
y 00 0 0 0 0 0 0 1 −2 1 y N −1
N −1
00
yN 0 0 0 0 0 0 0 1 −2 yN
where y i = y(x i ) and x i are the discretized coordinates. I leave as an exercise to the
reader to check that this representation is compatible with the boundary conditions
y(0) = 0 and y(`) = 0. Here the discretization of x is such that x 0 = 0 and x N +1 = `, and
h is the discrete step.
The q(x) term is local, therefore its matrix representation is diagonal,
q1 0 0 0 0 0 0 0 0
y1
0 q 0 0 0 0 0 0 0
2
0 0 q
y2
3 0 0 0 0 0 0
y3
0 0 0 q4 0 0 0 0 0
y4
..
q(x)y(x) = 0 0 0 0
. 0 0 0 0
···.
(3.66)
0 0 0 0 0 qi 0 0 0 yi
..
0 0 0 0
0 0 . 0 0
···
y N −1
0 0 0 0 0 0 0 q N −1 0
0 0 0 0 0 0 0 0 qN yN
where q i = q(x i ).
64
Ordinary Differential Equations
With these matrix representations, the differential equation above can be put in
a matrix form H y = λy. The matrix H is the sum of matrices above, y is the vector
composed by y i , and λ is the eigenvalue.
The most used numerical package to solve eigenvalue problems in a matrix form is
Lapack4 . The language Julia has this package natively implemented in the command
eig. Try to run the next example.
subplot(311)
plot(evecs[:,1]) # plot the first eigenvector
subplot(312)
plot(evecs[:,2]) # plot the second eigenvector
subplot(313)
plot(evecs[:,3]) # plot the third eigenvector
As you can see in the example, the eig command receives the matrix H and returns
its eigenvalues in a vector and the eigenvetors as a matrix. In this eigenvector matrix,
each eigenvector is set as a column. Therefore evecs[:,n] returns the eigenvetor n.
3.4 Problems
Problem 3.1: Damped harmonic oscillator
Implement a code to solve the damped harmonic oscillator problem,
d 2 x(t ) d x(t )
2
= −ω20 x(t ) − γ . (3.67)
dt dt
Fix ω0 = 2π and solve the equation for different values of γ to show the solutions for the
three possible regimes: (i) subcritical; (ii) critical; and (iii) supercritical. You will find the
exact solutions on the volume 2 of Ref. [9].
4
Lapack: http://www.netlib.org/lapack/
65
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
x p 1 (x 0 ) 0
½ Z ¾
W (x) = W (x 0 ) exp − dx , (3.68)
x0 p(x 0 )
(b) Show that if the Wronskian is nonzero over the whole domain, the solutions
y a (x) and y b (x) are linearly independent.
66
Ordinary Differential Equations
1 2
V (x) = V0 e − 2 (x−c) , (3.69)
with amplitude V0 = −10, centered at c = `/2, hard-wall boundary conditions ψ(0) =
ψ(`) = 0, and ` = 10.
(b) Make a plot of the potential well and the first three eigenstates.
(c) Compare the solution with a discretization of x with 100 points and with 500
points. The result (eigenvalues and eigenvectors) must not change much if the imple-
mentation is correct.
67
CHAPTER 4
Fourier Series and Transforms
2π? Or not 2π?
The Fourier series and transform are among the most essential mathematical tools
for physicists. TO DO: Finish introduction...
Hereafter we assume that we are dealing with well behaved functions on their rele-
vant domains. We shall restrict ourselves to the one-dimensinal cases. Generalizations
are imediate.
Every periodic function f (x) can be expanded as a sum of trigonometric functions.
If the period of the function is λ, i.e. f (x + λ) = f (x), the Fourier series reads
∞
c n e −i kn x ,
X
f (x) = (4.1)
n=−∞
where k n = 2πn/λ, n ∈ Z, and e i θ = cos θ + i sin θ. The coefficients c n are obtained from
the orthogonality of the trigonometric functions, yielding
1
Z x 0 +λ
cn = f (x)e i kn x d x. (4.2)
λ x0
Here x 0 is arbitrary and the integral runs over one period of the function f (x).
If the function f (x) is not periodic, but we are interested in a finite domain x ∈
[x i , x f ], one can extend consider a periodic extension of f (x) outside the domain, such
that the Fourier series applies with the replacements x 0 → x i and λ → x f − x i . The
periodic extension of f (x) shall match the original function on the desired domain.
Consider now that the function f (x) lives in the symmetric domain x ∈ [− λ2 , λ2 ],
and we take the limit λ → ∞. The discrete k n now become infinitesimally close, ∆k =
k n+1 − k n = 2π
λ → 0, and become P a continuous variable k n → k. Converting the sum
λ
p
d k, and replacing c n → ( 2π/λ) f˜(k), the
R
in Eq. (4.1) into an integral with n → 2π
Fourier series, Eq. (4.1), becomes
69
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Z ∞
1
f (x) = p f˜(k)e −i kx d k, (4.3)
2π Z−∞
1 ∞
f˜(k) = p f (x)e i kx d x, (4.4)
2π −∞
where f˜(k) = F [ f (x)] is the Fourier transform of f (x), and reciprocally, f (x) = F −1 [ f˜(k)]
is the inverse Fourier transform of f˜(k).
The next example shows the Fourier series. Play with the function and parameters.
subplot2grid((2,2),(1,0))
vlines(Ni, 0, real(Ci)); # real part of c n
scatter(Ni, real(Ci));
xlabel(L"$ n $")
ylabel(L"$ Re\{c_n\} $")
subplot2grid((2,2),(1,1))
vlines(Ni, 0, imag(Ci)); # imaginary part of c n
scatter(Ni, imag(Ci));
xlabel(L"$ n $")
ylabel(L"$ Im\{c_n\} $")
tight_layout();
70
Fourier Series and Transforms
Typically, when we work with a function f (x), where x is a space coordinate, the
Fourier transform is set in terms of the wave-number k as above. If our signal is a
function of time, i.e. f (t ), the Fourier transform is usually set in terms of the angular
frequency ω as f˜(ω) = F [ f (t )].
Linearity
Let f (x) = Ag (x) + Bh(x), the Fourier transform of f (x) is f˜(k) = A g̃ (k) + B h̃(k).
Translation
71
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Scaling
For a real number A in f (x) = g (Ax), the Fourier transform scales as f˜(k) = |A|
1
g̃ (k/A).
Derivatives
n
Let f n (x) = ddx n g (x) denote the n-th derivative of g (x), the transform is f˜n (k) = (i k)n g̃ (k).
n
Equivalently, for f (x) = x n g (x), the transform is f˜(k) = i n ddk n g̃ (k).
Convolution
N ·
2π(n − 1)(m − 1)
¸
f˜m =
X
f n exp −i . (4.6)
n=1 N
1 XN ·
2π(m − 1)(n − 1)
¸
fn = ˜
f m exp +i . (4.7)
N m=1 N
Since we have to calculate all elements to compose the full array f˜ or f , the DFT or
the inverse DFT takes N 2 operations. In contrast, the Fast Fourier Transform (FFT)
algorithm takes N log2 N operations, which is much smaller than N 2 for large N .
72
Fourier Series and Transforms
First, the command fft acts on an array f and returns its Fourier transform f˜.
Reciprocally, the command ifft acts on the array f˜ and returns its inverse Fourier
transform f .
Notice that the array f represents the function f (x) over the discrete domain set
by the array x. Conversely, the array f˜ represents the Fourier transformed function
f˜(k). The FFT implementation defines the domain of reciprocal space as k ∈ [0, ∆x 2π
).
π 2π
However, due to the periodicity of the Fourier transforms, the region ∆x ≤ k < ∆x is
π
actually equivalent to the region − ∆x ≤ k < 0. Typically, it is more interesting to work on
π π
the reciprocal space in the symmetric domain k ∈ [− ∆x , ∆x ).
One can use the commands fftshift and ifftshift to switch between these
2π
choices of domains above. The fftshit converts the domain from k ∈ [0, ∆x ) into
π π
k ∈ [− ∆x , ∆x ), while ifftshift performs the inverse operation.
1
FFTW: http://www.fftw.org/
73
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
The next example uses the translation property of the Fourier transform to shift a
function by x 0 . Check also Problem 4.2.
# function in x-space
f = exp(-x.^2); # example function
# function in k-space
# and using fftshift to put ft in the symmetric domain
ft = fftshift(fft(f));
tight_layout();
74
Fourier Series and Transforms
π
r h i
f˜(ω) = i A δ(ω − ω0 ) − δ(ω + ω0 ) , (4.8)
2
and the power spectrum will show two δ-peaks at ω = ±ω0 . This is the expected solution
of the simple pendulum in the harmonic regime. Check Problem 4.3.
What if our signal was noisy? There are many types of noise depending on their source2 .
Musical instruments may suffer from high frequency noises due to electronic systems,
bad cables, etc. The type of noise is defined by its power spectrum. For instance, a white
noise shows a constant power spectrum will all frequencies contributing equally. Hence
the name white noise as a reference to white light. The power spectrum of a pink noise
decays with 1/ω and appears in most natural phenomena. The types of noise got their
names as colors due to the initial analogy of white noise and white light.
In the next example, let’s artificially generate and eliminate a white noise. The result
is shown if Fig. 4.3. The white noise in this example have a power spectrum S(ω) ≈ 10,
and two signal peaks show at ω = ±2π. To filter the white noise we simply collect only
the fft components that are above the white noise power spectrum.
2
The colors of noise (Wikipedia)
75
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
g2 = g.*(s .> 15); # filters the spectrum above the white noise (~10)
s2 = log(1e-10+abs2(g2)); # spectrum of the filtered signal
f2 = ifft(g2); # recovers filtered signal with inverse FFT
clf();
subplot(211)
# first half shows original signal with noise
# second half shows filtered signal, now clean
n2 = Int(N/2);
plot(t[1:n2], f[1:n2])
plot(t[n2:end], f2[n2:end]);
xlabel(L"t")
ylabel(L"f(t)");
subplot(212)
# plot the power spectra
plot(w-pi/dt, fftshift(s));
plot(w-pi/dt, fftshift(s2));
xlim([-3w0, 3w0]);
xlabel(L"\omega")
ylabel(L"\logS(\omega)");
tight_layout();
76
Fourier Series and Transforms
Diffusion equation
∂ ∂2 ∂
u(x, t ) − D 2 u(x, t ) + v u(x, t ) = 0, (4.9)
∂t ∂x ∂x
where D is the diffusion coefficient, v is the drift velocity and u(x, t ) is density of the
diffusing material (mass, temperature, ...).
In this very simple homogeneous case, the diffusion equation has an analytical
solution. For an initial condition u(x, 0) = δ(x),
(x − v t )2
· ¸
1
u(x, t ) = p exp − , (4.10)
2 πD t 4D t
which is a Gaussian packet with a center that moves as x = v t , and broadens with time.
To find this solution, we first take the Fourier transform x → k of the diffusion
equation,
∂
ũ(k, t ) + Dk 2 ũ(k, t ) + i vk ũ(k, t ) = 0. (4.11)
∂t
2
It is easy to show that the solution in k-space is ũ(k, t ) = e (−i vk−d k )t ũ(k, 0), where
ũ(k, 0) is the Fourier transform of the initial condition u(x, 0). If the initial condition
77
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
p
is u(x, 0) = δ(x), then ũ(k, 0) = 1/ 2π. The inverse Fourier transform can be easily
calculated analytically to obtain the solution u(x, t ) above.
In this simple case we don’t really need any numerical calculation. However, you
could eventually face a more general diffusion equation (inhomogeneous, vectorial,
coupled, ...) for which numerical methods will be necessary. Therefore, let’s illustrate
the numerical method assuming that you are able to obtain the solution in k-space
ũ(k, t ) analytically, but you are not able to calculate the Fourier transform of the initial
condition ũ(k, 0) = F [u(x, 0)] and the inverse Fourier transform to recover u(x, t ) =
F −1 [ũ(k, t )]. The next example solves this problem.
D = 1; # diffusion coefficient
v = 1; # drift velocity
78
Fourier Series and Transforms
Schrödinger equation
∂
iħ ψ(x, t ) = H (x, p, t )ψ(x, t ). (4.12)
∂t
Vτ Tτ Vτ
ψ(x, t + τ) ≈ e −i 2ħ e −i ħ e −i 2ħ ψ(x, t ). (4.13)
Here we start with half of a time step, τ/2 using V, followed by a full time step τ using T ,
and another τ/2 to complete the evolution from t to t + τ. This spitting of the operators
generates an error of order O (τ3 ).
More interestingly, this approach can be extremely efficient if we notice that T is a
function of momentum p, and, therefore, can be better represented in Fourier space
(see the derivative property of the Fourier transform). The trick is to carry all operations
using V in coordinates space, while those involving T are done in k-space. In terms of
Fourier transforms, the evolution reads
V τ −1 h T̃ τ h Vτ
µ ¶ µ ¶ µ ¶ ii
ψ(x, t + τ) ≈ exp −i F exp −i F exp −i ψ(x, t ) , (4.14)
2ħ ħ 2ħ
where T = − 12 p 2 .
4.4 Problems
Problem 4.1: Discrete Fourier Transform
Implement your own version of the Discrete Fourier Transform (DFT) in Julia and
compare the results with Example 4.2. Your implementation will not be efficient and
you shall use Julia’s native fft function always. However, implementing the DFT will
help you learn more about coding.
79
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
d2
2
x(t ) = −ω20 sin[x(t )] − γv x (t ). (4.15)
dt
For now, let’s neglect the damping term, γ = 0. Consider ω0 = 2π and the initial
conditions x(0) = x 0 , and v(0) = 0. Prepare your code to run over 0 ≤ t ≤ 100.
(a) Find x(t ) for small amplitudes set by x 0 = 0.01π and use the fft command to
obtain x̃(ω). Plot the spectral function S(ω) = |x̃(ω)|2 for −2ω0 ≤ ω ≤ 2ω0 .
(b) Do the same for large amplitudes set by x 0 = 0.99π.
(c) Discuss the differences between the power spectra of cases (a) and (b).
d2
2
x(t ) = −ω20 sin[x(t )] − γv x (t ) + f 0 cos(ω1 t ). (4.16)
dt
(a) Solve this differential equation for x(0) = 0, v x (0) = 2, and 0 < t < 3000, using the
parameters ω0 = 1, ω1 = 2/3, γ = 1/2, and f 0 = 0.9.
(b) Now try it with the same parameters, except for f 0 = 1.15.
(c) The power spectrum is S(ω) = |x̃(ω)|2 , where x̃(ω) is the Fourier transform (t → ω)
of x(t ). Compare the power spectra of cases (a) and (b). The first is periodic, and
will show narrow peaks at the motion frequencies. The second is chaotic, almost all
frequencies contribute and the power spectrum is fractal.
80
CHAPTER 5
Statistics (TO DO)
What are the odds of having a silly quote here?
81
CHAPTER 6
Partial Differential Equations (TO DO)
83
CHAPTER 7
Plotting (not finished)
... is worth a thousand words
Here we will discuss PyPlot to take advantage of its extensive documentation and
large number of users, making it easy to find examples online.
7.1 PyPlot
7.1.1 Installing PyPlot
To install PyPlot, first you need Python and Matplotlib. If you use Ubuntu Linux, run
Now you can install PyPlot. Start Julia and run: julia> Pkg.add("PyPlot").
1
PyPlot: https://github.com/stevengj/PyPlot.jl
2
Gaston: https://github.com/mbaz/Gaston.jl
3
Gadfly: http://dcjones.github.io/Gadfly.jl/
4
Winston: https://github.com/nolta/Winston.jl
85
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
Since the PyPlot package is an interface to Python’s Matplotlib, one may use its
extensive documentation5 .
matplotlib.pyplot.foo(...) −→ plt[:foo](...),
where on the left we have Python’s notation, and on the right Julia’s.
For instance, you can generate an histogram using:
Latex
It is very easy to use Latex with Julia’s PyPlot package. Implicitly, PyPlot uses the La-
TexStrings package6 . Therefore on can use Latex commands on a string simply con-
structing it prepending with a L. For instance:
5
Matplotlib’s documentation: http://matplotlib.org/
6
LaTeXStrings: https://github.com/stevengj/LaTeXStrings.jl
86
Plotting (not finished)
plot(x, f);
xlabel(L"$\theta$ [rads]");
ylabel(L"$\sin\theta$ [a.u.]");
Subplot
The subplot command allows you to break the plot window into a grid. Say that the
desired grid has N lines and M columns, the command subplot(N, M, j) specifies that
you want to plot the next figure into the j -th element of the grid, with the elements
sorted in a “Z” shape. Try the next example.
clf();
subplot(2,2,1);
plot(x, sin(x));
xlabel(L"$x$");
ylabel(L"$\sin(x)$");
subplot(2,2,2);
plot(x, cos(x));
xlabel(L"$x$");
ylabel(L"$\cos(x)$");
subplot(2,2,3);
plot(x, exp(-x.^2));
xlabel(L"$x$");
ylabel(L"Gaussian");
subplot(2,2,4);
plot(x, sin(x)./x);
xlabel(L"$x$");
ylabel(L"$sinc(x)$");
87
Introduction to Computational Physics Gerson J. Ferreira - INFIS - UFU - Brazil
In the example above we are using Latex notation for the labels. The final command
tight_layout(...) allows you to adjust the subplots spacings to better adjust them within
the figure. This avoids overlapping of the labels and plots.
Even more control can be achieved with the command subplot2grid(...), which
we use in Fig. 4.1, for instance. Here the shape is passed as a tuple (N , M ) to the first
parameter, the second parameter takes the location of the desired plot an ordered pair
(i , j ), where 0 ≤ i ≤ (N − 1) and 0 ≤ j ≤ (M − 1). The top-left corner grid element is
(i , j ) = (0, 0), and the bottom-right is (i , j ) = (N − 1, M − 1). The subplot2grid becomes
more interesting as the next parameters allow you to set a rowspan and a colspan to
span over cells. Check again Example 4.1.
Labels
Legends
Figures are shown by default on screen as PNG. However, you can save it to files with
many different formats using the savefig(...) command. Check the example:
savefig("myplot.png");
savefig("myplot.pdf");
savefig("myplot.svg");
savefig("myplot.eps");
Please check the full documentation of the matplotlib for more details. You can
choose the paper size, dpi, etc.
88
Plotting (not finished)
Animations
Here’s an example of using Julia and matplotlib to create animations7 . The PyCall
package is used to import the animation library from Python, since it is not natively
implemented in PyPlot.
# save the animation to a MP4 file using ffmpeg and the x264 codec
video[:save]("anim.mp4", extra_args=["-vcodec", "libx264"])
7
Adapted from Andee Kaplan’s presentation at http://heike.github.io/stat590f/gadfly/
andee-graphics/
89
CHAPTER 8
Other topics (TO DO)
... and yet another silly quote
91
Bibliography
[1] Cláudio Scherer. Métodos computacionais da Física. Editora Livraria da Física, São
Paulo, 2010.
[2] Neide Bertoldi Franco. Cálculo numérico. Pearson Prentice Hall, São Paulo, 2006.
[3] Selma Arenales and Artur Darezzo. Cálculo numérico: aprendizagem com apoio de
software. Thomson Learning, São Paulo, 2008.
[4] J. C. Butcher. Numerical methods for ordinary differential equations. John Wiley &
Sons Ltd., Chichester, England, 2008.
[7] Edwin Abbott Abbott. Flatland: A romance of many dimensions. Princeton Univer-
sity Press, 2015.
[8] Thomas Haigh, Mark Priestley, and Crispin Rope. Eniac in Action: Making and
Remaking the Modern Computer. Mit Press, 2016.
[9] Herch Moysés Nussenzveig. Curso de física básica. Edgard Blücher, 2002, 2007.
93