Sunteți pe pagina 1din 18

Numerical programming in C

In this workshop you will gain experience in writing C code to solve numerical problems. You will develop simple
numerical algorithms and learn about numerical convergence, accuracy and stability.
1 Introduction
1.1 Algorithms
To investigate a real world problem we rst develop a mathematical model and then attempt to solve the
equations. Often this cannot be done analytically and instead we must use a computer. But how do we
use a computer programme to solve a mathematical problem? First we must develop an algorithm: a set
of instructions the computer can follow to perform the numerical task we are interested in. Next we need
to develop a programme to implement the algorithm. Importantly, we must then test our programme,
and nally we can run the programme to generate the results we need.
A good piece of advice when writing a new piece of code from scratch is to start from the basics. Break
the algorithm into steps then programme and test each step in turn. The smaller the piece of code
involved, the easier it is to nd problems. Making the code compile is the rst and easiest step. It
is then more dicult (and important) to check the numerical accuracy of the code. Although it may
compile and run, is the code actually doing what you want it to? It is important to be absolutely
condent that your code is working as you would expect before using it to generate new results.
1.2 Existing software
There are many existing numerical software packages that can help when solving numerical problems
computationally. These range from low level subroutines and functions, like the blas and lapack linear
algebra routines, to sophisticated software packages such as MatLab, Maple or IDL that come with
canned routines for solving standard problems. Most of these packages are excellent and their use can
make life much easier. However, you should never use these routines just as a black box. One rule of
thumb is that the more sophisticated a package, the more cautious you should be of using it without at
least some understanding of the underlying numerical methods that it employs.
1.3 Further reading
This workshop introduces numerical methods, but this is an enormous eld and, even in the areas covered
here, we will barely scratch the surface. For further information look at the excellent Numerical Recipes
in C by Press, Teukolsky, Vetterling and Flannery. This book covers important numerical techniques in
a broad range of subjects and also has a comprehensive list of references.
1
2 Numerical integration
Imagine we want to nd the denite integral
A =
_
b
a
f(x)dx. (1)
Perhaps the integral cannot be performed analytically, or perhaps f(x) is some numerical data stored in
a table. Then we will need to calculate A using a computer programme.
First, we need an algorithm. A is just the area under the curve f(x) from x = a to x = b. To nd the
area, we could draw f(x) on graph paper and count the squares underneath the curve. Here we will
develop a computer programme which will essentially do this for us. The key to the method is to split
up the unknown area A into smaller sections of area that we can calculate.
f
0
f
1
f
3
f
2
x
0
=a x
1
=a+h x
2
=a+2h x
3
=b
h
A
1
A
2
A
3
Figure 1: Trapezium rule algorithm on a uniform grid.
One way to do this is illustrated in Fig. 1. Here, the area A is split into trapeziums. In Fig. 1 we
divide the range of the integral (a x b) into 3 equal sections. If we know the value of f(x) at
each of the points x
0
= a, x
1
= a + h, x
2
= a + 2h and x
3
= b we can then calculate the area of each
trapezium section, for example A
1
= 0.5h(f
0
+f
1
), and sum the areas of all of the trapeziums to get an
approximation to the integral, A.
A A
1
+ A
2
+ A
3
=
1
2
h(f
0
+ f
1
) +
1
2
h(f
1
+ f
2
) +
1
2
h(f
2
+ f
3
)
= h
_
1
2
(f
0
+ f
3
) + f
1
+ f
2
_
. (2)
Obviously, A
1
+ A
2
+ A
3
is not a very accurate estimate for A, but the approximation can easily be
improved by splitting the range of the integral into smaller sections. The trapezium rule algorithm works
by approximating the function f(x) to rst order within each small range, h. For example, close to x = a,
f(a + ) = f(a) +
df
dx

a
+

2
2
d
2
f
dx
2

a
+ , (3)
where 0 < h. As the range is split into smaller sections, h becomes smaller and the second order
(and other higher order) corrections become less important. From Eqs. (2) and (3) it is clear that the
trapezium rule is accurate to within a constant multiplied by h
3
f

. In mathematical text books this is


usually denoted by 0(h
3
f

).
There are many higher order algorithms for numerical integration but, to paraphrase ref. [1], higher
order does not necessarily mean higher accuracy. The trapezium rule is usually good for most purposes,
2
but another important algorithm is Simpsons rule which, through a lucky cancellation of higher order
terms, is accurate to 0(h
5
f
(4)
). We should also briey mention open integration formulae. The trapezium
rule is a closed formula: it explicitly includes the end points f(a) and f(b) from Eq. 2. It is also possible
to develop algorithms which do not include these points and this can be advantageous in some situations.
For example if f(a) is a singular point. See ref. [1] for more information.
To develop a general trapezium rule algorithm to calculate A rst split the range b a into N equal
sections so x
0
= a, x
1
= a + h, , x
N1
= b where h = (b a)/(N 1). Then
A =
_
b
a
f(x)dx = h
_
1
2
(f
0
+ f
N1
) + f
1
+ f
2
+ + f
N2
_
+ 0(h
2
)
= h
_
1
2
(f
o
+ f
N1
) +
N2

i=1
f
i
_
. (4)
This is easily coded with a single for loop in a C programme.
2.0.1 Task: calculate
_

1
2xdx using the trapezium rule
First code the algorithm. Split it into steps then programme and test each step separately. First
dene the number of steps N. Then calculate the step size, h=(pi-1.0)/(N-1.0).
Use a for loop to count over the N points. For each point calculate the required value of x and
f=2.0*x. An example section of code to calculate and print values of x from 0 to (N 1)h is
for (i=0;i<N;i++){
x=h*i;
printf("%e\n",x);
}
Output x and f to a le and plot the results (see the appendix on plotting with gnuplot) to check
that the code is working.
Sum each of the values of f with the appropriate weighting to calculate A. An example section of
code to calculate the sum of all the integers between 1 and N 1 is
A=0;
for (i=1;i<N;i++){
A=A+i;
}
printf("Sum = %e\n",A);
The important line is A=A+i: each time around the loop the new value of A is set to be equal to
the old value of A plus the current value of i. So, the rst time around the loop the old value of A
is 0 and the new value is then set to be 0+1 = 1. The second time around the loop the old value
of A is now 1, the value of i is 2 and so the new value of A is set to be 1+2 = 3.
The trapezium rule should be exact for this integral, independent of the number of points. Check
that this is the case.
2.0.2 Task: calculate
_
3/4
/3
sin(x)dx and examine the convergence
Modify the code to calculate
_
3/4
/3
sin(x)dx. First generate and plot x and f=sin(x) between /3
and 3/4.
Calculate and plot the integral as a function of the step size h (or, alternatively as a function of
the number of points, N). Compare the numerical result to the analytical result and see how the
numerical result converges to the exact value as h 0 (or N ).
3
2.0.3 Task: calculate
_
4
10
4
ln(x) sin(exp(x))dx
The rst few tasks simulate the type of testing procedure you should go through with any new piece of
code. Once the code has been tested you can then be more condent in applying it to unknown cases.
Investigate the convergence of
_
4
10
4
ln(x) sin(exp(x))dx as a function of the number of points in
the integral.
2.1 Nonlinear grids
The problem with the function in the previous task is that it varies rapidly near to the origin and slowly
everywhere else. A small step size is needed to get accurate values to the integrand but, away from the
origin, the small steps are unnecessary and by calculating them we are wasting computer time. One
solution is to break up the range of the integral and use dierent uniform step sizes in each range.
Another solution is to use a continuously varying step size. This type of grid is often useful in physical
problems where the length scale varies continuously. For example, exponential grids are often used in
atomic physics problems to deal with the rapid variation of the potential near to the nucleus.
2.1.1 Task: generalise the trapezium rule to non-uniform step size
Generalise the trapezium rule algorithm to the case of non-uniform step size. For each step you
will need to calculate the area of the associated trapezium (A
1
, A
2
and A
3
from Fig. 1) and then
sum the areas to get the nal result. First, keep the step size as h and test the new version of the
code against the previous version.
Generate an appropriate non-uniform grid. An example section of code to do this is
xmult=...;
x=1.e-4/xmult;
for (i=0;i<N;i++){
x=x*xmult;
if (i>0) h=x-xold; /* h is the step size */
xold=x;
}
Compare the convergence of the new algorithm with the uniform step size algorithm by plotting
the result for the integral as a function of N. What other types of non-uniform grid could you use?
4
3 Series
Computers are fantastic tools for performing simple numerical tasks again and again. This means that
sequences and series can be very easy to evaluate using a computer programme.
3.1 Power series
3.1.1 Task: evaluate the Maclaurin series for exp(x)
Use a single loop to evaluate the Maclaurin series for exp(x) with nmax terms in the expansion and
for a given value of x. nb. you can calculate the factorial with a similar code fragment to that used
in task 2.1.1 and you can sum up the series in a similar way to the example in task 2.0.1.
Hint: Think about the largest number you can represent as an integer and try calculating a range of factorials as
both integers and doubles. Can you explain what you see?
Calculate and plot the Maclaurin series result for e
10
and e
10
as a function of the number of terms
in the series. What is the smallest fractional dierence you can obtain between the exact and series
values? What limits this dierence, and why is the result dierent for e
10
and e
10
?
3.2 Fourier series
As another example let us examine the Fourier sine series for f(x) = 1 with 0 < x < l. This is
f(x) =

n=1
b
n
sin
nx
l
, (5)
where
b
n
=
_
4/(n) if n is odd,
0 otherwise.
(6)
As usual, to calculate f(x) we rst need an algorithm. Imagine that we would like to calculate f(x) for
a particular value of x (say x = 2). Once we have chosen a value of x we must evaluate the sum in Eq.
5 but, as before, we cannot compute an innite number of terms. Instead we have to cut o the series
at some maximum number of terms (n
max
) and later we will use our computer program to see how the
series converges as the number of terms increases. To calculate
f(x) =
n
max

n=1
b
n
sin
nx
l
(7)
we can use a fragment of code that looks something like the following:
x = 2.0;
f = 0.0;
for (n=1; n<=nmax; n++) {
bn = ...;
f = f + bn * sin(n*pi*x/L);
}
On the rst line we tell the computer what value of x we are interested in (x=2.0). Then, for each
dierent value of n we calculate b
n
using the result from Eq. 6. The next line sums up each term in the
series. This uses exactly the same method as in task 2.0.1: the new value of f equals the old value of f
plus b
n
sin(nx/l).
To produce a plot of f(x) against x we need to calculate the series for a range of dierent x values, and
we can do this easily in our computer program with another loop. If we want to plot a graph with ixmax
points between some minimum (xmin) and maximum (xmax), x-values we must calculate the Fourier
series (as above) at each of the ixmax points. This can be done with the following code fragment:
5
for (ix=0; ix<ixmax; ix++){
x = ...
f = 0.0;
for (n=1; n<=nmax; n++) {
bn = ...;
f = f + bn * sin(n*pi*x/L);
}
printf("%e\t%e\n",x,f);
}
3.2.1 Task: Fourier series of a square wave
Code the algorithm to calculate the Fourier series of the square wave. As usual, test each part of
the code as you write it. Investigate the appropriate number of x points ixmax that you will need
to generate smooth curves. Then plot the Fourier series between l and 3l. Explain the shape of
the waves that you see.
Examine the convergence of the series then investigate (and plot) the Gibbs oscillations as a function
of nmax.
3.2.2 Task: Fourier series of more complex functions
Calculate the Fourier sine series for f(x) = 1/(2 e
x
) for 0 x < .
In this case we do not know the analytical result for b
n
. Instead we must calculate it numerically
from b
n
= (2/)
_

0
f(t) sin(nt)dt and this can be done with the code produced in task 2.0.1. Think
about the most ecient way to do this. You will need three loops: one loop over x, one loop over n
and one extra loop over t to perform the integral to evaluate b
n
. But, because b
n
does not depend
on x, it would be very inecient to simply nest the t loop inside the existing code: we only need
to calculate each b
n
once, not ixmax times. One solution is to calculate and store each b
n
in an
array variable at the start of the code, before the loop over x. Think of another ecient way to
rearrange the loops. Programme one method and compare the Fourier series result for f(x) to the
exact function. n.b. remember that array variables are dened by, for example, double b[10].
This will reserve space for 10 double precision numbers which can be accessed by b[0], b[1], etc.
6
4 Finding the roots of an equation
Often we may want to solve an equation f(x) = 0 where an exact analytical solution is not possible.
We could try to approximate the solutions, or we could attempt to solve the equation graphically or
numerically with a computer programme.
The way that most numerical equation solvers work is as follows: rst we guess a value for the root,
say x = x1. Then we use some information about the function or its derivatives at x = x1 to generate
a new, (hopefully) better, guess for the root at x = x
2
. We can then repeat this procedure and move
iteratively toward the real root (x = x
0
) until, after N iterations, we decide our guess x = x
N
x
0
is
good enough. i.e. when some set convergence condition is reached, either on the value of the function or
on the uncertainty in the position of the root. This idea of moving toward some goal through successive
iterations is a very common one in numerical methods - essentially because it is very easy to code.
To start most root nding algorithms we must rst bracket the root: if f(x) is continuous and f(a) and
f(b) have opposite signs then there must be at least one root in the interval (a, b), and we say that the
root is bracketed by a and b (see Fig. 2).
a b x
o
f(x)
x
Figure 2: Bracketing a root. The root at x
0
is in the interval (a, b). The vertical dotted line shows the new limit of the
interval at (a + b)/2 which will be imposed at the next iteration of the bisection method.
4.1 Bisection method
Here, we will investigate the bisection method. This is a simple and extremely robust method for nding
roots in 1D. In fact, once a root is correctly bracketed, the bisection method cannot fail. It works like
this: the root is bracketed with a lower limit x
l
= a and an upper limit x
u
= b. We know the root
lies somewhere between these two bounds, so our initial guess for the root, x
0
, is x
1
= (x
l
+ x
u
)/2 with
an error of (x
l
x
u
)/2. At each successive iteration we rene our guess for the root by reducing the
size of the interval in which we know the root must lie. We bisect the interval and test the value of the
function at (x
u
+ x
l
)/2. If the function has the same sign as the function at x
l
then the lower limit is
updated and we know the root now lies between (x
u
+ x
l
)/2 and x
u
. If the function has the same sign
as the value of the function at the upper limit (this is the case in Fig. 2) then the upper limit must be
updated and we know that x
l
< x
0
< (x
u
+ x
l
)/2.
There are a few special cases where you may have to take care. The most obvious is at a repeated root -
or indeed any double root where, although the interval (a, b) may contain a root, there is not necessarily
a sign change in f(x) between x = a and x = b. Also, if there are many roots between a and b the
bisection method will only nd one of them and, interestingly, if there is a singularity but no roots in
the initial interval the bisection method will converge to the point of the singularity.
The bisection method is usually quite good in 1D (given an appropriate initial interval in which to
search). However, in more than one dimension things get much more complicated very quickly. The
7
simplest algorithm which can work is the Newton-Raphson method, where both the function and its
derivative at the initial guess for a root are used to generate the next guess. There are also many other
advanced methods. See ref. [1] and references therein for more details.
4.1.1 Task: Code the algorithm
Write the code to implement the bisection method given an initial interval (a, b). You will need
to loop over iterations, bisecting the interval at each iteration until some convergence criteria is
reached. Think about how to implement this stopping condition. You will also need to think about
how to orient the search - how to update the appropriate bound to the interval at each iteration
depending on the sign of the function at each of the bounds and at the midpoint.
Test the code by solving x
3
7x
2
+ 14x 8 = 0 and comparing the numerical with the analytical
solution. Investigate the eect of changing the initial bounds a and b. What happens if these do
not bracket a root?
The ground state energy, E, of an electron in a nite square well of depth V
0
is found by solving
the transcendental equation
ka tan(ka) = (k
2
0
a
2
k
2
a
2
)
1
2
, (8)
where k
2
= 2mE/h
2
and k
2
0
= 2mV
0
/h
2
. Use your code to nd the ground state energy in an InAs
quantum well in GaAs with eective mass 0.027, well width 2a = 0.6 nm, and V
0
= 0.5 eV. Check
your solution graphically. There is a useful discussion on bracketing the root in this case in ref. [2].
8
5 Numerical dierentiation
It is common to have to dierentiate a data set numerically. If we have a set of tabulated data f
i
on
a uniform grid x
i
= ih, where h is the uniform step size, then an obvious rst guess for the numerical
derivative is
df
dx

x
i

f
i
x
i
=
f
i+1
f
i
h
. (9)
This is a forward dierence and, although it is a reasonable estimate for the derivative, it is only accurate
to 0(hf

), so the error will reduce very slowly (linearly) with the step size, h. This is not very satisfactory,
especially if we cannot control the step size in the data - for example if our data set is pre-generated
from some experiment. However, we can do better than this by thinking about the Taylor series again.
Near to x
i
we can expand f(x
i
+ h) and f(x
i
h) as Taylor series,
f(x
i
+ h) = f(x
i
) + h
df
dx

x
i
+
h
2
2
d
2
f
dx
2

x
i
+
h
3
6
d
3
f
dx
3

x
i
+ ,
f(x
i
h) = f(x
i
) h
df
dx

x
i
+
h
2
2
d
2
f
dx
2

x
i

h
3
6
d
3
f
dx
3

x
i
+ , (10)
where f(x
i
+ h) = f
i+1
and f(x
i
h) = f
i1
. Clearly, if we take the dierence between f
i+1
and f
i1
the second order term vanishes and we can write a new (centred dierence) approximation to the rst
derivative,
df
dx

x
i
=
f
i+1
f
i1
2h
+ 0(h
2
f
(3)
). (11)
5.0.2 Task: calculate the rst derivative
(r) = exp(r/a
0
)/
_
a
3
0
is the wavefunction of the Hydrogen ground state, where a
0
is the Bohr
radius. Write a C-function, pr, to calculate the Hydrogen 1s probability density (P(r) = 4r
2

2
).
Calculate P(r) on a uniform grid and plot it.
Write the code to calculate the rst derivative using the centered dierence formula and your
probability density function from the previous task. You will need to supply a step size h, and
make two calls to pr. Plot and examine the rst derivative.
Combine this code with your root nding code from section 4 to calculate the most probable value
for the radius of an electron in a Hydrogen 1s orbital. Check that this agrees with the analytical
value.
5.0.3 Task: calculate the second derivative
Derive the classical 3 point formula for the second derivative by eliminating rst and third order
terms in Eqs. 10. Write some code to calculate the second dierence of P(r) using this formula
and a function to evaluate P(r).
Plot P(r) along with the rst and second derivative and explain what you see.
9
6 Multi-dimensional numerical integration
Many numerical problems are relatively easy in 1D but tend to get dicult very quickly in more than
one dimension. This is unfortunately the case with numerical integration. There are two problems that
we come up against. The rst and often more serious problem is that the limits of integration in N
dimensions can be a complicated. The second problem is that the number of function evaluations needed
to perform an N dimensional integral tends to rise as the Nth power. So if you need 1000 points in 1D,
you will need 10
6
points in 2D and 1000
N
points in N-dimensions.
However, if the number of dimensions is small, the cost of evaluating our function is cheap and, most
importantly, the limits run along co-ordinate axes (or are easily specied in closed form) it is easy to gen-
eralise the 1D integration code in section 2 to deal with N dimensions. For more complicated situations
there are many special methods that have been developed, for example Monte Carlo integration.
6.1 2D integrals with the trapezium rule
Before we look at Monte-Carlo integration we will rst generalise our trapezium rule integration code to
perform a simple 2D integral. Imagine we want to calculate the volume V under a surface g(x, y),
V =
_
x
2
x
1
_
y
2
y
1
g(x, y)dydx. (12)
As before, we can calculate our function on a uniform grid. Then evaluating
_
y
2
y
1
g(x, y)dy for each of
the x = x
i
points on our grid between x
1
and x
2
is easy. For each separate value of x
i
we can calculate
the area A(x
i
) under the curve, g(x
i
, y), with our standard trapezium rule code. Fig. 3 shows a surface
g(x, y) as a function of x and y. So, for example, the line along x = x
2
is essentially a plot of g(x
2
, y) as
a function of y and the area under this line is simply A(x
2
). We could calculate and store each of the y
integrals at each x
i
point, so A(x) =
_
y
2
y
1
g(x, y)dy. Then V =
_
x
2
x
1
A(x)dx. And, obviously, this integral
can also be calculated using our 1D trapezium code.
x
1
x
2
y
1
y
2
8
12
16
20
24
28
g(x,y)
10
15
20
x
1
x
2
A(x)
Figure 3: Left: surface g(x, y). For each xed x
i
we can calculate A(x
i
), the area under the curve g(x
i
, y) between y
1
and y
2
. This is shown in the right hand curve: A(x) =
_
y
2
y
1
g(x, y)dx.
So to calculate the 2D integral we need to perform a 1D integral over y for each required value of x
on our grid, store the result as an array variable, then perform another 1D integration over x. In fact,
things are simpler than this. We do not need to explicitly store A(x) at all, we just have to nest the y
integration loop inside the x integration loop.
If x
i
= ih
x
, 0 i < N, y
j
= jh
y
, 0 j < M and f(x
i
, y
j
) = g
i,j
then, from the trapezium rule,
V = h
x
_
1
2
(A
1
+ A
N1
) +
N2

i=2
A
i
_
, (13)
10
where
A
i
= h
y
_
_
1
2
(g
i,1
+ g
i,M1
) +
M2

j=2
g
i,j
_
_
. (14)
6.1.1 Task: Write the code to calculate V =
_

0
_
/2
1
y exp(x
2
y
2
+ y)dxdy
Modify your code to calculate V using Eqs. 13 and 14. First make sure you have tested your code
on a 2D function that you can integrate analytically. Then calculate the integral above.
6.2 Monte Carlo integration
Unsurprisingly (given the gambling connotations) the Monte Carlo method works with random numbers.
If we randomly pick N points at position x
i
within a multi-dimensional volume V , then the integral of
f(x
i
) over the volume is given by
_
fdV = V f V
_
f
2
f
2
N
_
1/2
, (15)
where
f =
1
N
N1

i=0
f(x
i
). (16)
Monte Carlo methods are very useful for integrals with complicated boundaries but, because the error
in the integral only falls o like 1/

N, it can be very dicult to obtain high accuracy.


So, how does it work? First, let us look at what happens in 1 dimension. Eq. 15 becomes
_
b
a
f(x)dx =
b a
N
N1

i=0
f(x
i
), (17)
and you can see that this is very similar to the trapezium rule result from Eq. 4 without the end point
corrections (on a uniform grid, h = (b a)/(N 1)). In the Monte Carlo algorithm we randomly select
N points between a and b, weight each point by the value of the function at that point, then sum the
weights to get the approximation to the integral.
-0.5
0
0.5
0
0.5
1
0
1
2
3
4
5
6
7
g(x,y)
s
0
0.5
1
-1 -0.5 0 0.5 1
Area, A
Region, s
Figure 4: Left: surface g(x, y). Right: region, s, over which we would like to integrate the function.
The Monte Carlo algorithm comes into its own for higher dimensional integrals, especially those with
complicated limits. Fig. 4 shows an example 2D function g(x, y) which we would like to integrate over
the region s. First we pick N random points (x
i
, y
i
) over the area A (in our example this is the rectangle
from 1 < x < 1 and 0 < y < 1). If the point is inside the region s we weight it by the value of the
function g(x
i
, y
i
) at this point. If it is outside the region s we set its weight to 0. Then our approximation
11
to the integral is just the sum of the weights multiplied by the area A and divided by the number of
points N.
This method can be explained with the following analogy. Imagine throwing stones at random into a
eld of area A in which there is a pond (region s). To nd the area of the pond, simply compare the
number of stones, p, that fall into the pond with the total number of stones N that are thrown. The area
of the pond is then simply Ap/N (and calculating the area of the region s is equivalent to integrating
the function g(x, y) = 1 over this region).
So the key steps of a Monte Carlo integration are simple: 1. generate a random number. 2. convert
that to a position inside a given xed area or N-dimensional volume. 3. test if the position is inside the
region s of the integral. 4. weight the point appropriately. To generate a pseudo-random number you
will need to call the C-function rand() which will return an integer between 0 and the system variable
RAND MAX. The pseudo-random number generator is often seeded with a call to srand(time(0)) and so
you will need to include the <time.h> header le in your code.
6.2.1 Task: Use the Monte Carlo method to calculate
_ _
r5
e
(x
2
+y
2
)
dxdy, r =
_
x
2
+ y
2
As usual, break your code into steps and test each step independently. Think about how to choose
the area A to maximise the eciency of the code - obviously it must include all of s, but be as
close to s as possible so fewer points are wasted. Calculate the Monte Carlo error and show that
the numerical result converges to the expected result as N is increased.
6.2.2 Task: a 3D integral
This example is taken directly from numerical recipes. We wish to nd the mass of a component
of complex shape: the component is a section cut from a torus dened by the conditions z
2
+
(
_
x
2
+ y
2
3)
2
1 m
2
, x 1 m, and y 3 m, and the density is (x, y, z) = (10+x
3/2
) kg/m
3
.
Calculate the mass using the Monte Carlo algorithm.
Hint: First think about the extremes of the values of x, y and z that are allowed. You should be able to work out
the bounds on x, y and z and use these to set the size of the Monte Carlo sampling volume.
12
7 Numerical solution of dierential equations
7.1 Euler integration
The key step to solving a dierential equation numerically is to discretise the equation - to write it as
a dierence equation on a grid. As an example, let us investigate the second order dierential equation
governing the motion of a particle in 1 dimension,
d
2
x
dt
2
=
1
m
F(v, x, t), (18)
where m is the mass of the particle and F(x, v, t) is the force acting on the particle. We rst write our
second order dierential equation as two coupled rst order dierential equations. If v = dx/dt, then
dv
dt
=
1
m
F(v, x, t)
dx
dt
= v, (19)
is exactly equivalent to Eq. 18. These coupled dierential equations are then easily discretised: to rst
order, we can use the forward dierencing scheme from section 5 to give,
v
i+1
= v
i
+
t
_
1
m
F(v
i
, x
i
, t
i
)
_
(20)
x
i+1
= x
i
+
t
v
i
. (21)
This is the Euler integration method and, as we saw in section 5, its accuracy is strongly dependent on
the step size,
t
. However it is conceptually simple, and easy to program.
To turn Eqs. 20 and 21 into a numerical scheme we think of them as update equations. In an initial
value problem, we know x
0
, v
0
and F
0
, the values of the position, velocity and force at t = t
0
. But, then,
from Eqs. 20 and 21 we can calculate v
1
and x
1
at time t
1
= t
0
+
t
. So, we increment the time by
t
and update the velocity and position. At the next time step, we then know x
1
, v
1
and F
1
so we can use
the update equations to generate v
2
and x
2
, and so on.
7.1.1 Task: code the Euler method and numerically solve the equations for a harmonic
oscillator
For harmonic motion, F = kxr(v, x). Begin with an undamped oscillator with r = 0, m = 1 kg,
k = 2.5 N/m, and dt = 0.1 s. Start the particle from x = 0 with an initial velocity and investigate
the motion over a few cycles of oscillation. Compare with the analytical solution. What is going
wrong? How small does dt have to be to give reasonable agreement between the numerical and
analytical solutions?
7.2 Runge-Kutta integration
The problem with the Euler method is that it is only accurate to rst order and if
t
is large, the
method is unstable. In each of the coupled dierence equations (Eq. 20 and Eq. 21), we have ignored
terms proportional to
2
t
. If these are not negligible, our dierence equations are not equivalent to
the second order dierential equation (Eq. 18), and this means we are getting the physics wrong. For
example, in Eq. 21 we are missing a term proportional to
2
t
d
2
x/dt
2
which is equivalent to adding an
additional non-physical acceleration to our particle. This sort of problem is particularly signicant in
cases where the general solution contains both an exponentially decaying and an exponentially growing
part. In physical problems we usually want the part of the solution that decays with time. But, any
numerical error will start to mix in a small proportion of the exponentially growing solution and, after
a long enough time, this erroneous part to the solution will come to dominate our numerical result.
13
However, the good news is that we can easily do better than the Euler method by using the centred
dierence approximation from section 5. Then, our update equations become
v
i+1
= v
i1
+ 2
t
_
1
m
F(v
i
, x
i
, t
i
)
_
(22)
x
i+1
= x
i1
+ 2
t
v
i
. (23)
This is a second order scheme with error 0(
3
t
) called the midpoint or second order Runge-Kutta method.
In this scheme we calculate the position and velocity at time steps 2
t
, and we use some additional
information, the position at velocity at an intermediate step
t
, to improve the accuracy of our update
equations. So, how does this work numerically? We start with the initial conditions, v
i1
= v
0
and
x
i1
= x
0
at t
i1
= t
0
. Then we make an estimate of the position and velocity at the intermediate time
step: v
i
= v
i1
+
t
F
i1
/m and x
i
= x
i1
+
t
v
i1
. Finally, these estimates are used to generate the
updated position and velocity at t
0
+ 2
t
using Eqs. 22, and 23. Because this method works on a time
step of 2
t
, in text books you will often see a slightly dierent notation: a full time step of = 2
t
and an intermediate time step of /2 =
t
. It is also relatively easy to use the Taylor series (Eq. 3)
to generate higher order Runge-Kutta schemes and fourth order Runge-Kutta is often used for physical
problems.
7.2.1 Task: code the midpoint method and investigate the undamped oscillator in task
7.1.1
Start with 2
t
= 0.1. How large can you make 2
t
before the results start to become unphysical?
7.2.2 Task: investigate a damped driven oscillator
Include some frictional force r = v
n
in the equation of motion and drive the oscillator with a
forcing function Asin(t). Start the oscillator from rest away from x = 0 and investigate the
transient and steady state parts of the motion. Plot curves to demonstrate the key features of the
motion for dierent values of and .
7.2.3 Task: Lissajous gures
Plot the position against velocity for a damped driven oscillator. Investigate the motion for dierent
values of and A, and for dierent initial conditions. Explain what you see.
14
8 Partial dierential equations
One way to solve a partial dierential equation numerically is to discretise it and rewrite it as a nite
dierence equation. We can do this easily on a regular grid using the Taylor expansion again. For
example, take the Laplace equation for the electric potential with no sources of charge,

2
v = 0, (24)
or, in 2 dimensions,

2
v
x
2
+

2
v
dy
2
= 0. (25)
To discretise this equation on a uniform grid rst Taylor expand v
i,j
= v(x, y). If v
i+1,j
= v(x + h, y),
v
i1,j
= v(x h, y), v
i,j+1
= v(x, y + h), and v
i,j1
= v(x, y h), then
v
i+1,j
= v
i,j
+ h
v
x
+
h
2
2

2
v
x
2
+
h
3
6

3
v
x
3
+ , (26)
v
i1,j
= v
i,j
h
v
x
+
h
2
2

2
v
x
2

h
3
6

3
v
x
3
+ , (27)
v
i,j+1
= v
i,j
+ h
v
y
+
h
2
2

2
v
y
2
+
h
3
6

3
v
y
3
+ (28)
v
i,j1
= v
i,j
h
v
y
+
h
2
2

2
v
y
2

h
3
6

3
v
y
3
+ , (29)
If we sum Eqs. 26, 27, 28 and 29 we nd,
v
i+1,j
+ v
i1,j
+ v
i,j+1
+ v
i,j1
= 4v
i,j
+ h
2
_

2
v
x
2
+

2
v
y
2
_
+ 0(h
4
f
(4)
), (30)
and, because
2
v = 0, we have
v
i,j
=
1
4
(v
i+1,j
+ v
i1,j
+ v
i,j+1
+ v
i,j1
) . (31)
This is the nite dierence version of the Laplace equation (Eq. 25) on a uniform grid with h
x
= h
y
.
You should also be able to derive the equivalent nite dierence equation if h
x
= h
y
.
We can solve this equation using Jacobis method. Although this is slow and has been superseded by
many other faster methods it is a good place to start. In Jacobis method we start our solution o from
some arbitrary initial state and let it relax toward the actual solution iteratively.
First dene a uniform grid, x
i
= ih, y
j
= jh, with i = 0 N 1, and j = 0 N 1. Fix the values of
the potential at the edge of the grid according to the physical boundary conditions of the problem then
relax the solution over a number of iterations. At each successive iteration the potential at grid point
i, j is set to be equal to to the average of the potential on the surrounding grid points at the previous
iteration. So, for iteration n + 1,
v
n+1
i,j
=
1
4
_
v
n
i+1,j
+ v
n
i1,j
+ v
n
i,j+1
+ v
n
i,j1
_
. (32)
As n the changes to v
i,j
become negligible and v
i,j
converges to the true solution of the nite
dierence Laplace equation, Eq. 31.
A small tweak to Jacobis method gives the Gauss-Sidel method which is even easier to code. In Gauss-
Sidel we use the updated values of the potential on the right hand side of Eq. 32 as soon as they become
available. This avoids the need to store the values of the potential at both the n and the n+1 iterations.
So, how would we go about following this procedure computationally? First dene the grid as a 2D array
(double v[N][N]) then set the boundary elements of the array accordingly. Next, loop over iterations,
15
n, until some stopping criteria is reached (you must decide when the solution is good enough). For each
iteration, loop over all the i and j elements of the 2D array updating v[i][j] according to Eq. 31.
In physical problems, there are two common types of boundary condition. In the rst type, or Dirichlet
boundary condition, the values of the solution v
i,j
are xed at the boundaries. This is the type of
boundary condition we will deal with here. Alternatively, when Neumann boundary conditions are
imposed, the values of the derivative of the solution are xed at each boundary.
0
L
0 L
v=2
v=sin(x/L)
v
=
0
v
=
0
v=0
0
0.5
1
1.5
2
0 L
0
L
0
L
0 L
Figure 5: Solution to Laplace equation in 2D for an example system. Left: arrangement of gates. Centre: Grey-scale
map of the calculated potential v(x, y) on a 100100 grid. Right: equipotential lines.
8.0.4 Task: write a Gauss-Sidel code to solve the Laplace equation in 2D with Dirichlet
boundary conditions
Think about the stopping criterion and how to decide when your solution is acceptable.
8.0.5 Task: calculate the potential in a 2D gated system
Use the following boundary conditions: v(0, y) = 0, v(L, y) = 0, v(x, L) = 0, v(x, 0) = 2 sin(x/L):
the system is grounded on 3 edges and the potential on the bottom gate is set to vary sinusoidally.
Calculate the potential on a few dierent N N grids (eq. 10 10, 50 50, and 100 100).
Demonstrate that the number of iterations needed to converge the solution varies as N
2
. (This
slow convergence is one of the reasons that Gauss-Sidel is no longer used for realistic problems,
although a minor change - over relaxation with Chebyshev acceleration - makes the Gauss-Sidel
scheme competitive for small scale calculations). Examine your converged 2D solutions, v(x, y),
with gnuplot.
Solve the Laplace equation analytically for the above case. Compare your numerical and analytical
solutions. How would you modify your code to improve the agreement?
Investigate a more complex geometry of gates. Produce some gures to show the key features of
the potential. An example with a square terminal of xed potential in the middle of the system is
shown in Fig. 5.
References
[1] Numerical Recipes in C, W. H. Press, S. A. Teukolsky, W. T. Vetterling and B. P. Flannery, 2nd.
Ed. Cambridge University Press.
[2] Quantum Mechanics A. I. M. Rae, 5th. Ed., Taylor & Francis.
Mervyn Roy (2010)
16
A Plotting data with gnuplot
Your code can be used to generate a great deal of data and you will often need to analyse this. For
example, you may wish to calculate, output and plot a graph of some function (x) as a function of
x. There are many ways to accomplish this (for example by calling the pgplot subroutines) but one
relatively easy way is to use a plotting package called gnuplot. This is widely used free software that is
available for both Windows and Linux.
A.1 Simple plots
In your C code you will need to output the data you wish to plot as an ascii or text le containing columns
of numbers separated by tabs (\t) or by spaces. Once in ascii format your data can be plotted by a
whole range of packages, for example you could import your data into Microsoft Origin or even Excel
on CFS (although Excel plots look very poor in publications). On the Universitys SPECTRE system
gnuplot will do the job of plotting your data. gnuplot can be used to generate sophisticated publication
quality graphs, but its main advantage is that it makes it extremely easy to produce quick and simple
plots which will help you to analyse data as you go along.
To run gnuplot simply type gnuplot at the command line. This will give you the gnuplot command line
prompt (gnuplot> ). Now, imagine that your ascii data le (data.txt) contains three columns, rst x,
then f(x), then (x). To plot (x) against x you simply need to type
gnuplot> plot data.txt using 1:3 with line
This plots the data in le data.txt using columns 1 and 3 with a line. gnuplot understands some
abreviations so, for example with line can be replaced by w l and using by us. To plot with
points just use w p instead of w l and to plot column 1 against column 2 simply replace 1:3 with
1:2. To overlay two curves on top of each other the syntax is also simple. For example
gnuplot> plot data.txt using 1:2 w l, data.txt using 1:3 w l
plots two lines on the same graph, f(x) (column 2) against x (column 1) and (x) (column 3) against x.
gnuplot can also perform operations on the data in the columns, for example
gnuplot> plot data.txt us ($1*$1):2 w l
would plot f(x) against x
2
. You might also want to change the x range (xra) or y range (yra) of a plot.
The syntax is simple, for example,
set xra[0:10]
will set the x range of the plot from 0 to 10.
gnuplot can perform many more sophisticated operations like curve tting or surface and contour plots.
It is fairly intuitive software and it comes with a comprehensive help. To access the help type ? at the
command prompt or look on-line for one of the many gnuplot tutorials or reference manuals.
A.2 Functions
gnuplot will generate and plot standard and user dened functions. This can be very useful if you want
to compare your data to a function, or if you just wish to quickly look at the behaviour of a given
function. The syntax to dene a function is simple. For example,
gnuplot> f(x)=sin(x)
gnuplot> g(x,y)=exp(0.1*(x**2+y**2))
f(x) and g(x, y) can then be plotted in 1D or 2D,
17
gnuplot> plot f(x)
gnuplot> splot g(x,y)
To change the number of points gnuplot uses to plot a function in 1D you will need to change the
samples variable. For example set samples 50 will tell gnuplot to use 50 points in the x direction. The
splot command above is short for surface plot and is a nice way to represent 2D data. You can use
a variety of styles, for example the w pm3d style gives lled shaded surfaces and the set view map
will give a top down view that is often useful. To change the number of points in the y direction when
plotting a function in 2D you will need to set the isosamples variable.
gnuplot> set samples 50 ; set isosamples 50
gnuplot> set view map;
gnuplot> splot g(x,y) w pm3d
A.3 Plotting 2D data
As well as plotting surfaces for functions, gnuplot can also be used to visualise a 2D data set. For surface
plots, gnuplot expects at least 3 columns. For example, a column containing x, a column containing y,
and a third column containing the result, g(x, y), for that particular x and y. The best way to organise
the data is in blocks: the rst block should contain a specic value of x and all the values of y and
g(x, y) at that particular value of x. Next, gnuplot will expect a blank line to separate blocks. Then, the
next block should contain the next value of x and all the values of y and g(x, y) in the data set at that
particular x. And so on. For example, a 2D data set with g(x, y) = exp(xy), 0 x < 2 and 0 y < 3
with just 2 points in x and 3 points in y would look like:
0.000000e+00 0.000000e+00 1.000000e+00
0.000000e+00 1.000000e+00 1.000000e+00
0.000000e+00 2.000000e+00 1.000000e+00
1.000000e+00 0.000000e+00 1.000000e+00
1.000000e+00 1.000000e+00 2.718282e+00
1.000000e+00 2.000000e+00 7.389056e+00
When the data is in this format all of the various gnuplot surface options like pm3d can be used.
A.4 gnuplot printing
By default, gnuplot is set to plot to the X11 terminal or the wxt terminal (the screen). You can change
the terminal to many dierent types (type ? set term to get a complete list). Perhaps the most useful
types for generating hard copies are the postscript or png types. Postscript is used by most journals for
publication quality plots, but png might also be useful for inserting into PowerPoint presentations. For
example, to create a postscript le of a plot of column 1 vs 3 in data.txt, type
gnuplot> set term post
gnuplot> set out plot.ps
gnuplot> plot data.txt us 1:3 w l
This will write the postscript output to a le called plot.ps. On SPECTRE you can view postscript
les with a viewer called evince. To view the le plot.ps simply type evince plot.ps at the command
line. You can also easily convert postscript output to other formats. For example, to convert plot.ps to
a pdf le simply type epstopdf plot.ps at the command line.
In gnuplot there are many dierent options available for enhancing the look and style of your plots. Try
searching the help for information on axis labels, the key and plot styles. Also, try help set term post
for information on the dierent postscript options that are available.
Mervyn Roy (2010)
18

S-ar putea să vă placă și