Numerical Methods With Python
Numerical Methods With Python
1 Introduction
You will be given light curve data for several RR Lyrae variables. This data
will be processed to find the periods and flux averaged magnitudes of the
stars.
2 Objectives
1. Plot the raw light curves.
http://www.univie.ac.at/tops/Period04/
1
Be sure to also download the Period04 manual and the tutorial data files.
Go through both tutorials in the manual.
After you have gone through these tutorials, you should be able to ex-
tract the dominant periods from the data. Record and report these periods
for each data set; you will need them later. Also show and describe a peri-
odogram.
For reference, typical RR Lyrae periods range from a few hours to 1.5
days.
In the event Period04 fails to work properly, then try Supersmoother.
The instructor should provide you this code.
http://skynet.unc.edu/ASTR101L/videos/folding/
Fold the light curves into their primary period(s) and plot the phased
data.
2
6 Fitting a Fourier Series to the Data
We want to fit the light curves to a Fourier series such that we have a con-
tinuous expression for the light curve. Fit the data to a nine-term Fourier
series using a χ2 minimization process. This is rigorously explained in chap-
ter 3, particularly section 3.6, of the appendix. The form of the Fourier
series follows:
X 9
m(t) = hmi + Ak sin (2πkf t + φk )
k=1
3
that for a star a distance d away, the flux F is given by
M
F = .
4πd2
Most RR Lyrae stars have M = 0.6. Knowing this, the distance d can be
found in parsecs:
m − M = −5 + 5 log10 d.
The flux averaged magnitude FAM is given by the integral of the flux in
phase space: Z 1
FAM = −2.5 F (Φ)dΦ
0
The flux averaged magnitude is used in various physical analyses of stellar
processes.
Integrate the phased light curves and report the values.
4
9 Appendix
5
34 Chapter 3. The method of least squares
Table 3.1: Observations of the periods, P (days), the absolute magnitude M, and the colour B − V,
of Cepheids in the LMC.
i = a 0 + a 1 xi − y i
i2 = (a0 + a1 xi − yi )2
n
X Xn
2
S = i = (a0 + a1 xi − yi )2
i=1 i=1
where n is the number of observations. Our problem is to minimize S with respect to a 0 and a1 . To
minimize with respect to a0 we find ∂S /∂a0 and set it to zero, and we do the same for a 1 :
n
X
∂S
= 2 (a0 + a1 xi − yi ) = 0
∂a0 i=1
n
X
∂S
= 2 (a0 + a1 xi − yi )xi = 0
∂a1 i=1
Therefore
n
X
(a0 + a1 xi − yi ) = 0
i=1
n
X
(a0 xi + a1 x2i − xi yi ) = 0
i=1
-6.5
-6
-5.5
-5
-4.5
M
-4
-3.5
-3
-2.5
0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
log(P)
Figure 3.1: The relationship between the absolute magnitude, M, and the logarithm of the period
(days), log P, for Cepheids in the LMC (from Table 3.1). The solid line is the least-squares fit when
log P is error free; the dashed line is the least-squares fit when M is error free.
P P P P
From the data in Table 3.1 we can clearly determine the sums xi , yi , xi yi and x2i . Therefore
the values of a0 and a1 can be found because we have two equations in two unknowns.
The above notation is clumsy, so let us represent the average of any quantity as follows:
n
1X
< x >= xi
n i=1
A = (X−1 )Y.
where X−1 is the inverse of the matrix X. In this simple case we do not need direct matrix inversion
and algebraic manipulation easily leads to the solution
< xy > − < x >< y >
a1 =
< x2 > − < x > 2
a0 = < y > −a1 < x >
36 Chapter 3. The method of least squares
Applying these equations to our problem of Cepheid variables, we find M = −2.547 log P − 1.619
(solid line in Fig. 3.1) . Given the observed values of log P i and Mi in Table 3.1, we can calculate
n n
1X 2 1X
σ2 = i = (a0 + a1 log Pi − Mi )2
n i=1 n i=1
using a0 = −1.619, a1 = −2.547, from which we can determine the standard deviation, σ, of the fit
to the straight line. This turns out to be σ = 0.28 mag. Therefore we can state that the equation
M = −2.547 log P − 1.619 allows the absolute magnitude, M, of any Cepheid with known period to
be determined with a standard deviation of 0.28 mag.
Now, we chose to assume on reasonable grounds that all the error resides in the absolute mag-
nitude, M (the y value) and that log P (the x value) is free of error. If we had, instead, assumed that
all the error can be attributed to log P, we will obtain different results. Re-writing the equation as
log P = a11 M − aa01 and repeating the calculations gives a 1 = −2.745, a0 = −1.397. This is quite
different (dashed line in Fig. 3.1), emphasizing that we need to think carefully in such matters.
In the first case we minimized the errors in y, assuming that the errors in x were negligible
compared to those in y. In the second case we minimized the errors in x, assuming that the errors in
y were negligible compared to those in x. What do we do if the errors in x are comparable to those
in y? This problem is beyond the scope of this course, but it will clearly lead to values of a 0 and a1
which lie somewhere between the two cases we have considered.
Exercises
3.1.1 Write a program which reads the data of Table 3.1 from a file and calculates the linear least-
squares coefficients a0 and a1 in M = a0 + a1 log P. Plot the data points and the straight line
thus calculated. [Answer: a0 = −1.619033, a1 = −2.547323.]
3.2 χ2 minimization
In the above discussion we assumed that the errors in the straight line fit belong to a single normal
distribution with standard deviation σ. What happens if the errors belong to different normal distri-
butions with different values of σ? This may happen if, for example, if the data were obtained with
instruments of different precisions, then some points deserve more weight than others. In cases like
these, we minimize χ2 where
n !2
2
X y − yi
χ = .
i=1
σi
Here yi is the variable subject to errors, y = a 0 + a1 xi is the function whose coefficients are required
(xi assumed to be free of error) and σi is the standard deviation of data point i. Least squares fitting
is just a special case of χ2 minimization with σi = σ = a constant.
Our problem is to minimize χ2 with respect to a0 and a1 for
n !2
2
X a0 + a 1 xi − y i
χ = .
i=1
σi
Equating the partial differentials with respect to a 0 and a1 to zero, we obtain the result
X X X 2
∆ = 1/σ2 x2 /σ2 − x/σ2
X X X X
a0 = y/σ2 x2 /σ2 − x/σ2 xy/σ2 /∆
X X X X
a1 = 1/σ2 xy/σ2 − x/σ2 y/σ2 /∆
When σ2i = σ2 = constant, we recover the least squares result.
3.3. Uncertainties in the coefficients 37
Exercises
3.2.1 Derive the equations for a0 and a1 using χ2 minimization of yi + i = a0 + a1 xi given the
standard deviations σi for each of the n data points.
3.2.2 Write a program to calculate the χ 2 estimates of a0 and a1 . Test your program by reading the
data of Table 3.1 and setting σi = log Pi . [Answer a0 = −1.511525 and a1 = −2.651885].
Xn !2
∂a1
σ2a1 = σ2i
i=1
∂y i
so we need partial derivatives with respect to each individual point, y i . These are
∂a0 X X
= 1/σ2i x2 /σ2 − xi /σ2i x/σ2 /∆
∂yi
!2 P 2 P P P 2
∂a0 1/σ2i x2 /σ2 − 2xi /σ2i x2 /σ2 x/σ2 + x2i /σ2i x/σ2
σ2i =
∂yi ∆2
P P 2 P P 2
Xn !2 1/σ2 x2 /σ2 −2 x/σ 2P x2 /σ2
P
x/σ2 +
P
x2 /σ2 x/σ2
∂a0
σ2i =
i=0
∂y i ∆2
P P 2 P P 2 P
1/σ2 x2 /σ2 − x2 /σ2 x/σ2 x2 /σ2
= =
∆2 ∆
∂a1 X X
= xi /σ2i 1/σ2 − 1/σ2 x/σ2 /∆
∂yi
!2 P 2 P P P 2
∂a1 x2i /σ2i 1/σ2 − 2xi /σ2i 1/σ2 x/σ2 + 1/σ2 x/σ2
σ2i =
∂yi ∆2
P P 2 P P P P 2
Xn !2 x2 /σ2 1/σ2 −2 x/σ 2 P 1/σ2 x/σ2 + 1/σ2 x/σ2
∂a1
σ2i =
i=1
∂y i ∆2
P 2 2 P 2 P P 2 P
x /σ 1/σ2 − 1/σ2 x/σ2 1/σ2
= =
∆2 ∆
P P P P P 2
So we finally obtain σ2a0 = x2 /σ2 /∆ and σ2a1 = 1/σ2 /∆ with ∆ = 1/σ2 x2 /σ2 − x/σ2 .
38 Chapter 3. The method of least squares
Exercises
3.3.1 Modify the program in Ex. 3.2.2 to calculate and print the standard deviations of a 0 and a1 .
Test your program by reading the data of Table 3.1 and setting σ i = 0.1 × log Pi . [Answer
a0 = −1.511525, σa0 = 0.034749, a1 = −2.651885, σa1 = 0.040433.]
M = a0 + a1 log P + a2 (B − V)
where, as before, the coefficients a0 , a1 , a2 can be determined using the method of least squares. Let
us see if we can apply the same technique to the general relationship y i + i = a0 + a1 x1i + a2 x2i ,
where we have written Mi = yi , log Pi = x1i and (B − V)i = x2i . Note that we assume (B − V) to be
free of error (which is not strictly true). We have
i = a0 + a1 x1i + a2 x2i − yi
i2 = (a0 + a1 x1 i + a2 x2i − yi )2
n
X X
S = i2 = (a0 + a1 x1i + a2 x2i − yi )2
i=1
Equating these to zero, you can see that we obtain three equations in the three unknowns, a 0 , a1 , a2 :
This time the simple algebraic solution is more laborious and it is best to use matrix methods. The
above system of equations is written as
1 < x1 > < x2 > a0 < y >
< x > < x2 > < x x > a = < x y > ,
1 1 1 2 1 1
< x2 > < x1 x2 > < x22 > a2 < x2 y >
3.4. Multivariate least squares 39
which means that the linear equation of the previous section is a special case with m = 1, while the
case described above has m = 2. m is therefore the number of independent variables. Our program
needs to construct two matrices, X and Y:
1 < x1 > < x2 > ... < xm > <y>
< x1 > < x21 > < x1 x2 > ... < x1 xm > < x1 y >
X = < x2 > < x1 x2 > < x22 > ... < x2 xm > , Y = < x2 y > .
... ... ... ... ... ...
< xm > < x1 xm > < x2 xm > ... < xm xm > < xm y >
Since we will be dealing with arrays and matrices, we must import the numpy module. Here is the
full least squares program.
Let’s examine this program. In line 1 we import the numpy module for the reason mentioned above.
In line 5 we ask the user to type in the number of independent variables. This is 1 for y = a 0 + a1 x1 ,
2 for y = a0 + a1 x1 + a2 x2 , etc. Note that the matrix X has dimension m1 × m1 where m1 is one more
than the number of independent variables, m. In line 7 we open the input file for reading and in line
22 we close it after we have read all the data. In line 8 we define x to be an m1 × m1 array and we
set all its elements to zero using the zeros statement. Remember that the Y matrix is a row matrix
with m1 rows and one column. In line 9 we define the y array and set the array elements to zero.
We will be using the x and y arrays to hold the running total as we loop through the data. In line
10 we define xi to be a list of zeros of size m1. The xi list will be used for reading the numbers,
xi1 , xi2 , ..., xim from each line of the file. The variable yi will hold y i which is the last number on
each line.
From line 12 to line 21 we read and operate on each line of the file as it is read, using n to count
the number of lines (i.e. the number of data points). We set xi[0] = 1 because we need not only
products such as < x1 x2 >, but also < x1 >=< 1 × x1 >=< x0 x1 > . In line 15 we store each number
in the line to the corresponding value of xi. In line 16 we store the last number in the line (the y i
value) to yi. In lines 18 and 19 we form the products x i j xik and add the result to the running total
in array x[j,k]. In line 20 we add the product y i xi j to the running total y[j,0].
By the time we reach line 22 we have read all the lines in the file and we have these sums in
arrays x and y as follows:
n
X
x[j, k] = xi j xik j,k = 0,1,2, ..., m
i=1
Xn
y[j, 0] = xi j yi j = 0,1,2, ..., m
i=1
These are just n times the required averages, < x j xk >, < x j y >, but since we will be dividing by
n after matrix inversion, there is really no need to divide these arrays by n. In lines 23 and 24 we
convert the arrays to the corresponding matrices which then allows us to form the inverse of matrix
X and multiply it by matrix Y to get the matrix of coefficients, A. The coefficients are our final
answers which we print out in a loop (lines 26 and 27).
Applying this method to our Cepheid problem, we obtain the following coefficients:
M = −2.145 − 3.117 log P + 1.486(B − V)
with a residual standard deviation of σ = 0.25 mag in M. This is certainly a useful improvement
over the simple linear relationship (σ = 0.28 mag) discussed in the previous section.
Exercises
3.4.1 Use the above program to calculate the linear least-squares coefficients in M = a 0 +a1 log P+
a2 (B−V) using the data of Table 3.1. [Answer: a 0 = −2.1452, a1 = −3.1173, a2 = 1.4857.]
This is just another example of the multivariate case described above. In the case of a quadratic,
y = a0 + a1 x + a2 x2 , we can simply put x1 = x, x2 = x2 and use the above program to obtain the
coefficients. For the general polynomial we just put x i = xi .
3.6. Fourier fitting 41
Exercises
3.5.1 The following program generates a cubic polynomial with coefficients in list a and with
simulated observational errors:
import random
fp = open("polynomial.dat","w")
a = [1.0, -2.0, 3.0, -4.0] # Change the coefficients as desired.
n = 100 # Number of data points.
for i in range(n):
x = 0.01*i # Value of x.
y = a[0]
for j in range(1,4):
y += a[j]*x**j # Value of y.
# Mess up y by adding some noise with standard deviation 0.1.
y += random.gauss(0.0,0.1)
fp.write("%8.3f %10.3f\n" % (x,y))
fp.close()
Use this program to generate (x, y) data in file polynomial.dat. Modify the general least-
squares program discussed in the previous section to read this file and to fit a polynomial of
any given degree. Test your program by comparing the resulting coefficients with those used
to generate the file. The values will not be identical because of the added scatter.
So finally we have
Y = a0 + a1 sin(ωt) + a2 cos(ωt) + a3 sin(2ωt) + a4 cos(2ωt) + ... + a2m−1 sin(mωt) + a2m cos(mωt).
Suppose we have some periodic data, it could be for example the light curve of a Cepheid.
We are given the time, ti , and magnitude, yi , of an observation and we have n such observations.
We know the period, P, and therefore ω = 2π/P. For any given observation we can put x 1i =
sin(ωti ), x2i = cos(ωti ), x3i = sin(2ωti ), x4i = cos(2ωti ), ..., etc. As you can see, this is the same
kind of problem as before,
Yi = yi + i = a0 + a1 x1i + a2 x2i + a3 x3i + ... + a2m−1 x(2m−1)i + a2m x2mi ,
42 Chapter 3. The method of least squares
and we use least squares to determine the unknown coefficients, a k . The python program is shown
below.
#
# Program to fit a time series with Fourier components.
#
from numpy import *
import math
Exercises
3.6.1 Write a program which asks for the period, the number of Fourier components, m, the con-
stant, c0 and the amplitude and phase of each Fourier component. Choose a suitable time
interval between each data point so as to cover one cycle with about 100 points and write the
time, t, and value, Y, to a file. Then use the above program to recover c 0 , the amplitudes and
phases.
where C is a constant. Since C is undetermined, the differential equation alone is not enough to
specify a particular solution. One way to select a particular solution is to give its value at some
point, y = y0 at x = x0 , which then allows C to be determined. We call this type of problem
an initial-value problem. In real life problems we encounter many differential equations that either
cannot be solved by elementary classical methods or for which the evaluation of the analytic solution
is quite difficult. In such instances we resort to numerical methods of solution.
h2 h3
yn+1 = yn + y0n h + y00
n + y000
n ....
2! 3!
If we choose h sufficiently small, we can neglect terms higher than the first so that
yn+1 ≈ yn + y0n h.
Given an initial value y = y0 at x = x0 and choosing step size h, we can calculate further values as
follows:
y1 = y0 + f (x0 , y0 )h
y2 = y1 + f (x1 , y1 )h
43
44 Chapter 4. Numerical integration and solutions of differential equations
y3 = y2 + f (x2 , y2 )h
...
yn+1 = yn + f (xn , yn )h
This is called Euler’s method for numerical solution of a first order differential equation. Fig. 4.1
shows a geometrical interpretation of the method. Given an initial point (x n , yn ) on the curve y(x),
the value of yn+1 is approximated by the straight line which passes through (x n , yn ) and having slope
y0n = f (xn , yn ).
y*(xn+h)
y(xn+h)
y(xn)
xn xn+h
Figure 4.1: In the Euler method, the value of y n+1 = y(xn + h) is found by approximating the curve
by a straight line passing through (x n , yn ) and having a slope of y0 (xn ).
y0 = 0.1
y1 = y0 + (y0 − x0 y20 )h = 0.10500
y2 = y1 + (y1 − x1 y21 )h = 0.11022
y3 = y2 + (y2 − x2 y22 )h = 0.11567
...
We continue in this way until we arrive at the solution for x n = 10.0, which is yn = 0.11103.
It can be shown that for small h, the dominant error per step is proportional to h 2 . To solve
the problem over a given range of x, the number of steps needed is proportional to h1 so it is to
be expected that the total error at the end of the fixed step will be proportional to h (error per step
times number of steps). For this reason, the Euler method is said to be first order. This makes the
Euler method less accurate (for the same h) than other higher-order techniques. The Euler method
can also be numerically unstable. This limitation, along with its slow convergence of error with h,
means that the Euler method is not often used.
4.3. Differential equations of second and higher order 45
Exercises
4.2.1 Write a program which uses Euler’s method to solve the differential equation
dy 1
=
dx 1 + x2
where y = 1 at x = 0. Start with a step size h = 0.1 and calculate values of (x, y) for each step
until x = 1.0. This equation has the analytical solution y = 1 + arctan(x); plot a graph showing
how the difference between the numerical and analytical solution varies as a function of x.
What is the error at x = 1 when (a) h = 0.1 [answer 0.025], (b) h = 0.01 [answer 0.0025], (c)
h = 0.001 [answer 0.00025].
d2 y
= f (x, y)
dx2
can be transformed into two first-order equations by introducing a new unknown v = dy/dx. So
therefore
dy
= v
dx
dv
= f (x, y).
dx
In order to solve a first order equation we require one initial condition. To solve this second-order
equation we require two initial conditions. In this example, we need to specify the value of y and
v = dy/dx at some value of x.
Any differential equation can be transformed into a series of first-order differential equations.
The solution by Euler’s method is just an extension of that for a single equation. Given initial values
y = y0 and v = v0 at x = x0 and choosing step size, h, we can calculate further values as follows:
y1 = y 0 + v 0 h
v1 = v0 + f (x0 , y0 )h
y2 = y 1 + v 1 h
v2 = v1 + f (x1 , y1 )h
...
yn+1 = yn + vn h
vn+1 = vn + f (xn , yn )h
46 Chapter 4. Numerical integration and solutions of differential equations
d2 y dy
+ 10 + 100y = 100| sin(t)|
dt 2 dt
with initial conditions y = 0.1, dy/dt = −0.5 at t = 0. We let y 0 = y and y1 = dy/dt so that
dy1
+ 10y1 + 100y0 = 100| sin(t)|.
dt
So the original second order differential equation becomes
dy0
= y1
dt
dy1
= 100| sin(t)| − 10y1 − 100y0
dt
with initial conditions y0 = 0.1, y1 = −0.5 at t = 0. A simple python code to solve this problem is
the following:
import math
h = 0.01
t = 0.0
y0 = 0.1
y1 = -0.5
while t < 10.0:
y0 = y0 + y1*h
y1 = y1 + (100.0*abs(math.sin(t)) - 10.0*y1 - 100.0*y0)*h
t = t + h
print t,y0,y1
In order to make the code quite general so that a differential equation of any order can be solved,
it is convenient to use a python list to store values of the variables y 0 , y1 , y2 , .... as shown in the
revised code below. In this code all we need to do to implement any arbitrary system of first-order
differential equations is to define the right hand side of each equation in function fun(n,x,y).
In calling this function, n is the equation number to be evaluated. In this example line 8 returns
y[1] because the first equation is dy 0 /dt = y1 and because y[1] stores y1 . Line 12 evaluates and
returns the RHS of the second differential equation, 100| sin(t)| − 10y 1 − 100y0 . We could add on
more equations as required. The function euler implements the Euler algorithm for each equation.
Whenever we call fun, we use the original values and store the updated values in a temporary list,
yt, in line 19. When we have finished evaluating all the differential equation, we copy the updated
values in the temporary list, yt, to the original list, y, in line 34.
The system of equations are started with the initial values of t and y in lines 24 and 25. The
program also needs to know the step size (line 27). In line 31 we begin the integration loop. Each
time the loop executes we integrate through one step by calling euler (line 33) and increasing the
value of t by the step size (line 35). We write out the values of t, y 0 and y1 in the loop. Results are
plotted in Fig. 4.2.
4.3. Differential equations of second and higher order 47
1 import math
2
3 # Functions to integrate.
4 # Place the RHS of 1st-order diff equations here.
5 def fun(n,x,y):
6 # The 1 st diff eqn:
7 if n == 0:
8 return y[1]
9
10 # The 2nd diff eqn:
11 if n == 1:
12 return 100.0*abs(math.sin(x)) - 10.0*y[1] - 100.0*y[0]
13 # etc....
14
15 # Integrates system of diff equations using Euler’s method.
16 def euler(order, h, x, y):
17 # Put updated values in temporary list
18 for i in range(order):
19 yt[i] = y[i] + h*fun(i,x,y)
20 return
21
22 fp = open("euler.dat","w")
23 # Initial values
24 t = 0.0
25 y = [0.1, -0.5]
26 # Step size
27 h = 0.01
28 # Order is number of 1-st order diff equations.
29 order = 2
30 yt = [0.0]*order # Temporary list
31 while t < 10.0:
32 # Integrate by one step.
33 euler(order, h, t, y)
34 y = yt # Update y from temporary list
35 t += h
36 fp.write("%10.5f %10.5f %10.5f\n" % (t, y[0], y[1]))
37 fp.close()
48 Chapter 4. Numerical integration and solutions of differential equations
1.1 1.5
1
0.9 1
0.8
0.7
0.5
y0 0.6
y1
0.5
0
0.4
0.3
0.2 -0.5
0.1
0 -1
0 2 4 6 8 10 12 0 2 4 6 8 10 12
t t
Exercises
4.3.1 Consider a planet orbiting the Sun. By Newton’s law of gravity, the components of accelera-
tion in the x- and y-directions are
d2 x GMx
= −
dt2 r3
d2 y GMy
= − 3
dt2 r
where r2 = x2 + y2 , G is the gravitational constant and M is the mass of the Sun. Re-write
these equations as four first-order differential equations. (a) These four differential equations
require four initial conditions. Choosing units such that GM = 1, the initial conditions at
t = 0 are x = 1.0, y = 0.0, dx/dt = 0.0, dy/dt = 1.0. Solve the differential equations using
Euler’s method and write values of t, x, y for 0 < t < 7 using a step size of 0.001 in t. Plot the
the orbit (y as a function of x). [Answer: a circle of unit radius centered at the origin].
(b) Repeat above solution using initial conditions x = 1.0, y = 0.0, dx/dt = 0.0, dy/dt =
1.1 at t = 0 for 0 < t < 10 and the same step size. Plot the orbit. [Answer: an ellipse].
h2 h3
yn+1 = yn + y0n h + y00
n + y000
n ....
2! 3!
The Runge-Kutta method assumes
yn+1 = yn + h(w1 k1 + w2 k2 + w3 k3 + . . .)
where wi is a weight and ki is a function of y and previous values of k. The idea behind Runge-Kutta
methods is to select wi and ki so that
h2 h3
h(w1 k1 + w2 k2 + w3 k3 + . . .) ≈ y0n h + y00
n + y000
n ....
2! 3!
4.4. The Runge-Kutta method 49
The fourth-order Runge-Kutta method (RK4) is the most widely used algorithm for solving an
ordinary first-order differential equation. It is given by the following equation:
h
yn+1 = yn + (k1 + 2k2 + 2k3 + k4 )
6
where
k1 = f (xn , yn )
!
h h
k2 = f xn + , y n + k1
2 2
!
h h
k3 = f xn + , y n + k2
2 2
k4 = f (xn + h, yn + hk3 )
Thus, the next value yn+1 is determined by the present value, y n , plus the product of the step size, h,
and an estimated slope. The slope is a weighted average of slopes:
• k2 is the slope at the midpoint of the interval, using slope k 1 to determine the value of y at the
point xn + h/2 using Euler’s method;
• k3 is again the slope at the midpoint, but now using the slope k 2 to determine the y-value;
• k4 is the slope at the end of the interval, with its y-value determined using k 3 .
In averaging the four slopes, greater weight is given to the slopes at the midpoint:
k1 + 2k2 + 2k3 + k4
slope = .
6
The RK4 method is a fourth-order method, meaning that the error per step is on the order of h 5 ,
while the total accumulated error has order h 4 .
Below is an implementation of the RK4 algorithm for the problem of Ex. 2.1. We use a list, y,
because we want to extend the code to differential equations of higher order. As in the Euler method,
we use a function fun which evaluates and returns the RHS of each 1st order differential equation.
In the rungeKutta function we start by saving the original value of y[n] to a temporary variable,
yn, because we do not want to use the updated value of y[n] when evaluating each equation. We
return with the update value of y[n]. In the calling program, note that we use a temporary list, yt,
to store the updated values of y (line 41), and that we call rungeKutta with the original values of
y. When all the equations have been evaluated, we update y (line 43).
The Runge-Kutta method is more accurate than the Euler method. For Ex. 2.1 and h = 0.1, the
maximum error is 0.01; for h = 0.01 it is 0.001; for h = 0.001 it is 0.0001.
50 Chapter 4. Numerical integration and solutions of differential equations
1 import math
2
3 # Functions to integrate.
4 # Place the RHS of 1st-order diff equations here.
5 def fun(n,x,y):
6 # The 1 st diff eqn:
7 if n == 0:
8 return y[1]
9
10 # The 2nd diff eqn:
11 if n == 1:
12 return 100.0*abs(math.sin(x)) - 10.0*y[1] - 100.0*y[0]
13
14 # etc....
15
16 # 4-th order Runge-Kutta Method. Do not change anything.
17 def rungeKutta(n,x,y,f,h):
18 yn = y[n] # Original value of y[n].
19 k1 = f(n,x,y)
20 y[n] = yn + 0.5*h*k1 # Function expects a list.
21 k2 = f(n, x+0.5*h, y)
22 y[n] = yn + 0.5*h*k2
23 k3 = f(n, x+0.5*h, y)
24 y[n] = yn + h*k3
25 k4 = f(n,x + h, y)
26 # Must use original value here.
27 return yn + h*(k1 + 2.0*k2 + 2.0*k3 + k4)/6.0
28
29 fp = open("rk4.dat","w")
30 # Order is the number of 1st-order diff equations.
31 order = 2
32 # Initial conditions:
33 t = 0.0
34 y = [0.1, -0.5]
35 yt = [0.0]*order # Temporary list
36 # Step size
37 h = 0.01
38 while t < 10.0:
39 for i in range(order):
40 # We use original y in function, not yt.
41 yt[i] = rungeKutta(i,t,y,fun,h)
42 t += h
43 y = yt
44 fp.write("%8.3f %16.7e %16.7e\n" % (t,y[0],y[1]))
45 fp.close()
4.4. The Runge-Kutta method 51
1
"a"
"b"
"c"
0.8 "d"
"e"
0.6
a
0.4
0.2
0
0 0.2 0.4 0.6 0.8 1
t
Exercises
4.4.1 Although the actual size of our Universe is ”unmeasurable”, one usually describes distances
at different times (which will change due to expansion or contraction) in terms of a cosmic
scale, R(t), with its present value denoted by R 0 = R(t0 ). Additionally, one can normalize R(t)
by its present value and define a cosmic scale factor, a(t) = R(t)/R 0 . The rate of change of
a(t) with time, ȧ(t) = H0 , is the present value of the Hubble parameter H 0 = 73 km s−1 per
megaparsec. The evolution of the scale factor with time is governed by the equation,
r
da Ω M ΩR
= 1 − Ω0 + + 2 + a 2 Ωλ ,
dt a a
where Ω0 = Ω M + ΩR + Ωλ , is the total density parameter, Ω M is the relative density of matter
(including dark matter), ΩR is the relative radiation energy density and Ω λ is the relative dark
energy density. In this equation the unit of time has been chosen so that the present age of the
universe, t0 = 1. It is thought that currently the values are Ω M ≈ 0.3, ΩR ≈ 0, Ωλ ≈ 0.7.
Use RK4 to determine the backwards evolution of the scale factor from its present value to
the start of the Big Bang at t = 0. Use a step size h = −0.01 to work backwards from the
initial condition t = 1, a = 1 and stop when t reaches zero. Plot a as a function of t for the
following choices:
Plot all on one graph; compare your results with Fig. 4.3.
52 Chapter 4. Numerical integration and solutions of differential equations
4.4.2 The gravitational potential, φ, is given by Poisson’s equation ∇ 2 φ = 4πGρ where G is the
gravitational constant and ρ(r) is the mass density. For the spherically-symmetric case, we
have !
1 d 2 dφ
r = 4πGρ
r2 dr dr
For stellar interiors the relationship ρ = λφ n , where λ and n are constants, is a reasonable
approximation. Putting r = ax where
(n + 1)Kλ(1−n)/n
a2 =
4πG
results in !
1 d 2 dφ
x = −φn .
x2 dx dx
At the centre of the star x = 0, φ = 1 and dφ/dx = 0.
d2 y
= ax(x − L)
dx2
where a is a constant. We know that the deflection will be zero at the two ends, so the boundary
conditions are y(0) = 0 and y(L) = 0. In this problem we are given two boundary conditions, as
required to solve a second order differential equation, but they are specified at two different points.
In order to solve this problem as an IVP, we need to know y 0 (0) or y0 (L).
One way to solve a BVP like this is to guess the missing boundary condition, thereby turning it
into an IVP. In the example, we guess the value of y 0 (0), solve this IVP and obtain y(L). In general,
y(L) will not have the required value. Using trial and error or some scientific approach, one makes
another guess at y0 (0) and tries to get as close to the required boundary value as possible. This is
called the shooting method because it is like aiming for a distant target with a gun.
We could try plotting the value of y(L) for different initial guesses of y 0 (0) and read off from the
graph what value of y0 (0) is required to give the correct value of y(L). If the graph turns out to be
a straight line, we only need two guesses. Suppose that the straight line can be written as y 0 (0) =
a0 + a1 y(L). For our first guess y01 (0) = a0 + a1 y1 (L) and for the second guess y02 (0) = a0 + a1 y2 (L).
Solving for the coefficients we obtain
!
y0 (0) − y01 (0)
y0 (0) = y01 (0) + 2 (y(L) − y1 (L)) .
y2 (L) − y1 (L)
So putting y(L) = 0 in this equation we can determine the required value of y 0 (0).
Once we have obtained a better guess by using the value given by the straight line, we can use
this guess to see how close it takes us to the required value of y(L). In general, we will find that the
4.5. Boundary value problems 53
answer does take us closer to the required value, but not close enough for our purposes. In that case
we define a value, , which defines how close we want to get to the required value. If the value we
obtain after using the straight line is y n (L), then we want to continue until |y n (L) − y(L)| < . If yn (L)
does not satisfy this condition, we repeat the procedure until it is eventually satisfied.
Let us see how we can program this BVP using the 4-th order Runge-Kutta code we have
developed. If we let y0 = y and y1 = dy/dx, we have
dy0 dy1
= y1 , = ax(x − L).
dx dx
In the example we will use a = 5 × 10−4 and L = 10. The code is shown below.
1 import math
2
3 # Functions to integrate.
4 # Place the RHS of 1st-order diff equations here.
5 def fun(n,x,y):
6 # The 1 st diff eqn:
7 if n == 0:
8 return y[1]
9 # The 2nd diff eqn:
10 if n == 1:
11 return 0.0005*x*(x - 10.0)
12
13 # 4-th order Runge-Kutta Method. Do not change anything.
14 def rungeKutta(n,x,y,f,h):
15 yn = y[n] # Original value of y[n].
16 k1 = f(n,x,y)
17 y[n] = yn + 0.5*h*k1 # Function expects a list.
18 k2 = f(n, x+0.5*h, y)
19 y[n] = yn + 0.5*h*k2
20 k3 = f(n, x+0.5*h, y)
21 y[n] = yn + h*k3
22 k4 = f(n,x + h, y)
23 # Must use original value here.
24 return yn + h*(k1 + 2.0*k2 + 2.0*k3 + k4)/6.0
25
26 # Integrates diff equation from x to x1
27 def ode(x,x1,h,y,fun,order):
28 while x < x1:
29 for i in range(order):
30 yt[i] = rungeKutta(i,x,y,fun,h)
31 x += h
32 return
33
34 # Calculates straight line coefs and returns value at y01
35 def fitline(y01,g1,g2,f1,f2):
36 a1 = (g2[1] - g1[1])/(f2[0] - f1[0])
37 a0 = g1[1] - a1*f1[0]
38 return a0 + a1*y01
54 Chapter 4. Numerical integration and solutions of differential equations
39
40 order = 2
41 # Starting and ending value of x
42 x0 = 0.0
43 x1 = 10.0
44 #
45 # Initial value with guess for y1(x0)
46 y0_0 = 0.0
47 y1_0 = 0.1 # A guess
48 #
49 # Required value for y0(x1)
50 y0_1 = 0.0
51 #
52 # Tolerance, step size
53 eps = 1.0e-4
54 h = 0.01
55 yt = [0.0]*order # Temporary list
56 #
57 # Initial guess
58 g1 = [y0_0, y1_0]
59 while 1:
60 # Integrate with first guess
61 y = [g1[0],g1[1]]
62 ode(x0,x1,h,y,fun,order)
63 # Test if required precision has been attained.
64 if abs(y0_1-yt[0]) < eps:
65 break
66 f1 = [yt[0], yt[1]]
67 # Take a second guess
68 g2 = [g1[0], g1[1]+0.1]
69 y = [g2[0],g2[1]]
70 # Integrate with second guess
71 ode(x0,x1,h,y,fun,order)
72 f2 = [yt[0],yt[1]]
73 # Fit a straight line and return new guess.
74 g1[1] = fitline(y0_1,g1,g2,f1,f2)
75 print "Initial conditions: ", g1
76 print "Boundary conditions: ", yt
We start at 40 by defining the order of the system (there are two 1st order equations, so order
= 2). The starting and ending values of x are 0 and 10 respectively. The initial conditions are
y0 (0) = 0, but y1 (0) is unknown and we have guessed y1 (0) = 0.1. Any other guess will be equally
valid. The required value of y0 (L) = 0 (line 50). We define a tolerance of 1.0 × 10 −4 , meaning that
the program will run until the initial guess leads to a value of y 0 (L) which differs from the required
value by less than this amount. In line 54 we define the step size and create a temporary list, yt. We
keep the initial conditions for the first guess in the list g1.
In line 59 we begin the loop which will continue until the tolerance level has been met. We start
with the first guess, g1, and integrate the differential equations all the way to x = L by calling the
function ode. After the call, yt contains the boundary conditions at x = L. We store this in list f1
4.5. Boundary value problems 55
(Line 66). Then we take a second guess, g2, in which we simply increment y 1 (0) by 0.1 (line 68).
We could have changed y1 (0) by some other value, this does not matter because all we want to do is
to take any two points so that we can fit a straight line. We then call ode again, this time using the
second guess as initial values and store the boundary conditions in f2 (line 72).
So now we have two guesses, g1 and g2, at the initial conditions which result in the boundary
conditions f1 and f2 respectively. With this information we can fit a straight line to g as a function
of f by calling fitline. fitline calculates the straight line coefficients, a0 and a1 in y 0 (0) =
a0 + a1 y(L) or g[1] = a 0 + a 1f[0] and returns a better guess for y 0 (0), which is used as the
next initial guess.
What happens if the dependence of y0 (0) on y(L) is not a straight line? In this case we will
obtain a value for y0 (0) which is closer to the true value than our initial guess. On the next iteration
we obtain an even better solution. The procedure is repeated until the specified tolerance is met.
The shooting method is conceptually easy to visualize, but sometimes it is not the best method
of solving a boundary value problem because the boundary conditions at the end point might be
very sensitive to the initial boundary conditions. This can sometimes be alleviated by choosing a
suitable value of x = x f between the initial value, x = x0 and the final value x = x1 (x f is called a
fitting point). The shooting method is used to integrate from x 0 to x f and also to integrate between
x1 and x f . The idea is to choose initial values at x 0 and x1 so that the values all match at the fitting
point.
A very powerful method begins by transforming the system of differential equations to a system
of finite difference equations. For example, we can replace the differential equation
dy
= f (x, y)
dx
with an algebraic equation relating function values at two points k and k − 1:
!
1 1
yk − yk−1 = k − xk−1 f
(x ) (x k + xk−1 ,
) (y k + yk−1 .
)
2 2
We are given values at some starting point k = k 0 and at some end point, k = k1 . We then have
a system of coupled linear equations which can be solved at all values of k. This is called the
relaxation method.
Exercises
4.5.1 Find a solution for the boundary value problem
d2 y
= 0.0005x(x − 10)
dx2
with y(0) = 0 and y(10) = 0 and plot the beam displacement as a function of x. [Answer:
y0 (0) = 0.04166 at x = 0.]
d2 y 1
=−
dx 2 (1 + y)2
5.1 Introduction
Many stars are variable in light, i.e. the brightness of the star changes with time. By plotting the
brightness as a function of time, it is sometimes possible to determine the period of variation just by
visual inspection, as in the example below. To find the period of variation, one determines the time
-0.4
-0.2
Magnitude
0.2
0.4
0.6
30 35 40 45 50 55 60 65 70
Time (days)
of the first maximum T 1 = 37.50, and the time of the last maximum T 2 = 64.45, and the number of
cycles between the two, n = 5. The period, P, is:
64.45 − 37.50
P= = 5.39 days.
5
Clearly, the larger the number of cycles, the more accurate the resulting period.
To show that the period is correct, one can fold the variation into cycles. The result is a phase
diagram, as shown in Fig. 5.2.
But what if our observations were not so well sampled? After all, stars can only be observed
at night, so there will be gaps in the data due to the daylight hours. There will also be gaps due to
cloudy weather. Real observations are more likely to resemble Fig. 5.3 instead of Fig. 5.1. Now
it is much more difficult to identify the maxima. Some other way must be found to determine the
period. One could guess the period, plot a phase diagram such as that of Fig. 5.2 and choose the
period which gives the best looking phase diagram. Proceeding in this way, and knowing that the
53
54 Chapter 5. Searching for hidden periods in data
-0.4
-0.2
Magnitude
0
0.2
0.4
0.6
0 0.2 0.4 0.6 0.8 1 1.2 1.4
Phase
-0.4
-0.2
Magnitude
0.2
0.4
period is somewhere around 5.4 days, we can construct a large number of phase diagrams close to
this period (Fig. 5.4) and choose the one with smallest scatter.
Constructing phase diagrams in this way is a rather laborious enterprise. It would be better
if we could just calculate the scatter about the best-fitting curve without necessarily plotting the
phase diagram. To do this we need to know the functional relationship between the brightness,
V, and the time, t. We see that the curve is roughly sinusoidal (though there is definitely a fair
degree of asymmetry), so we may assume that the true brightness of the i-th observation is given by
VT,i = a0 + A sin(ωti + φ). The angular frequency is ω = 2π P . We cannot measure VT,i , instead we
measure Vi , where VT,i = Vi + i , and i is the error in the observation. Thus we write
Vi + i = a0 + A sin(ωti + φ)
= a0 + A cos φ sin ωti + A sin φ cos ωti
= a0 + a1 sin ωti + a2 cos ωti
q
where the amplitude A = a21 + a22 . This kind of equation should now be familiar. Putting x i =
sin ωti , and yi = cos ωti , we see that this is just another example of multivariate least squares.
5.1. Introduction 55
-0.4
-0.2
0.2
0.4
-0.4
-0.2
Magnitude
0.2
0.4
-0.4
-0.2
0.2
0.4
0 0.2 0.4 0.6 0.8 1 1.2 1.4
Phase
Figure 5.4: Observations of Fig. 3 phased with P = 5.3 (top), 5.4 (middle) and 5.5 (bottom) days.
P
To find the solution which minimizes the sum of the squares of the errors, i2 , we equate the
partial derivatives with respect to a 0 , a1 and a2 to zero and we end up with the following simultane-
ous equations:
We could proceed in the usual way and solve these equations by matrix inversion. Since we will
be trying a large number of periods, and since we need a complete solution for each trial period,
this will require a great deal of computation. Fortunately, some good approximations can be made
which will allow an approximate solution without matrix inversion.
First, we assume that we have a large number of data points, in which case the mean of sin ωt
and cos ωt will be very close to zero. This is simply due to the fact that the sine and cosine curves
spend an equal time being positive as being negative. Making this approximation we have from the
56 Chapter 5. Searching for hidden periods in data
first equation a0 ≈< V >. In other words, the constant term is just the average value, < V >. It
is convenient to remove this constant term from the data, v i = Vi − < V >, so the remaining two
equations become
< v sin ωt > = a1 < sin2 ωt > +a2 < sin ωt cos ωt >
< v cos ωt > = a1 < sin ωt cos ωt > +a2 < cos2 ωt >
Note that < sin ωt cos ωt >= 12 < sin 2ωt > should also be very close to zero. What is the average
of sin2 θ over a cycle? This is how we work it out:
R 2π 2 R
1 2π
2 0
sin θdθ 2 0 (1 − cos 2θ)dθ 1 2π 1
< sin θ > = R 2π = = =
dθ 2π 2 2π 2
0
1
We can see that < cos2 θ >= 2 as well. Making these approximations, we have
< 2 > = a1 < sin ωt > +a2 < cos ωt > − < v >
The terms < sin ωt >, < cos ωt > are both approximately zero because the errors are random and
sin ωt and cos ωt average out to zero. Therefore
< v2 > + < v > = a1 < v sin ωt > +a2 < v cos ωt > .
Using < v >≈ − < 2 > and substituting for a1 , a2 , we finally have
< 2 > ≈ < v2 > −2 < v sin ωt >2 −2 < v cos ωt >2 .
Given a particular value of ω = 2π/P, we can evaluate the quantities on the right-hand
√ side
of this equation to obtain the standard deviation about the best-fitting sinusoid, σ = < >. By
2
calculating σ for a large number of trial periods and by plotting σ as a function of P, the period for
which σ is a minimum can be found. In Fig. 5.5 we show such a plot for the example discussed
above. The period which has minimum σ is found to be P = 5.376 days.
In practise, astronomers prefer not to use σ in this plot. We note that
1
< 2 >= σ2 =< v2 > − A2
2
5.1. Introduction 57
0.24
0.22
0.2
0.18
σ 0.16
0.14
0.12
0.1
0.08
0 2 4 6 8 10 12 14 16 18 20
Period
Figure 5.5: The standard deviation about the best-fitting sinusoid, σ, as a function of the period in
days.
0.35
0.3
0.25
Amplitude
0.2
0.15
0.1
0.05
0
0 0.5 1 1.5 2
Frequency (cycles/day)
Figure 5.6: The periodogram of the data shown in Fig. 5.3. The strongest peak occurs at f 0 ≈ 0.18,
but notice the peaks at f ≈ 0.82 and and f ≈ 1.18. These secondary peaks at 1 − f 0 and 1 + f0 are
called aliases.
and since < v2 > is a constant, the smaller the value of σ the larger the amplitude, A. Instead of
plotting σ as a function of P, it is convenient to plot A as a function of the frequency f = 1/P.
This is called the periodogram. The periodogram for our example is shown in Fig. 5.6. The highest
amplitude occurs at f = 0.186 cycles/day which is the same as P = 5.376 days.
The frequencies selected for the periodogram should be sufficiently closely spaced so as to
resolve the peaks. If the step size is too large, a peak might be missed, but if it is too small the
program might take too long to execute. It can be shown that the width of a peak is roughly 1/∆t
where ∆t is the time span covered by the data (latest time - earliest time). To ensure that a peak is
defined by at least four points, the frequency step size should be about 41 ∆t or smaller.
If the data were sampled at equal time intervals, δt, the highest frequency that can be extracted
is one-half the sampling frequency or 1/2δt. This is called the Nyquist frequency. For unequally
sampled data, the Nyquist frequency is undefined. The mean sampling frequency is n/∆t, where n
is the number of data points. One could define the mean Nyquist frequency as n/2∆t, but in most
cases this too low. A reasonable value is to search all frequencies up to a maximum of n/∆t.
58 Chapter 5. Searching for hidden periods in data
0.3
0.25
0.2
Amplitude
0.15
0.1
0.05
0
0 1 2 3 4 5 6 7 8 9 10
Frequency (cycles/day)
Figure 5.7: Periodogram of a star with P = 0.187 days showing the aliasing caused by daytime gaps
in the data.
Exercises
5.1.1 Write a program that reads the the time (in days) and the magnitude from file cepheid.dat.
The program should ask for the period and then write the phase and magnitude to another file.
Ensure the phase, φ, is in the range < φ < 1.5 and plot the phase diagram. The true period is
between 5.2 < P < 5.6 days. Determine the period by visual inspection of the phase diagram.
5.1.2 Write a program which asks for the starting frequency, stopping frequency and frequency step
size. The program then reads a file containing the time, t, and magnitude, V, for a variable
√ in separate python lists. For each frequency, f , the program calculates
star. Store these values
the amplitude A = 2 < v sin ωt >2 + < v cos ωt >2 , and writes f , A to an output file. Apply
the program to the data in file cepheid.dat and
(a) plot the periodogram and visually locate the frequency of maximum amplitude;
(b) use the polynomial-fitting program to fit a quadratic to A as a function of f . Find the
maximum of the quadratic and hence the best value of f .
5.2 Aliases
In Fig. 5.6, which is the periodogram of the data in Fig. 5.3, we see that in addition to the large peak
at f1 = 0.186 cycles/day, there are smaller peaks at f 2 = 0.814 and f3 = 1.186 cycles/day. Note that
f2 = 1 − f1 and f3 = 1 + f1 . These two peaks arise because there are gaps in the data set. Because
the data in Fig. 5.3 could only be obtained at night, there is a gap of about one day between each
measurement (sometimes more than one day if the night was cloudy). Because of these gaps, one
could squeeze in an extra cycle or leave out a cycle and still get a reasonable fit to the sinusoid. This
effect is responsible for the two weaker peaks which are called aliases.
Aliases are separated from the true peak by the frequency at which the data were sampled. In
astronomical observations done from a single site, the sampling frequency is 1 cycle/day so that one
always obtains aliases separated by 1 cycle/day from the true frequency. Aliasing occurs in any data
set which is not completely sampled. The more frequent the gaps, the larger the amplitude of the
alias peaks. In some cases it is not possible to determine a unique frequency because the aliases are
so strong.
Fig. 5.7 shows a periodogram which illustrates the concept of aliases rather clearly. This peri-
odogram is of a star with a period P = 0.187 days or f = 5.345 cycles/day. Notice that the highest
5.3. Harmonics 59
peak does, indeed have a frequency f = 5.345, but notice the 1 cycle/day aliases at 4.345 and 6.345
cycles/day. In this data set, there are frequent gaps lasting for 2 days so that the 2 cycle/day aliases
at 3.345 and 7.345 cycles/day are also quite strong. The other, smaller peaks are also due to aliasing.
5.3 Harmonics
In the model we have been using, we assume that the data is well represented by a pure sinusoid
Vi + i = a0 + A sin(ωti + φ).
This may be a good approximation in many cases, but not in others as shown in Fig. 5.8. This is
the light curve of the RR Lyrae star SS Leo which shows a very steep rise in brightness followed
by a gradual decline. The actual data from which the phase diagram of Fig. 5.8 was constructed is
shown in Fig. 5.9. Notice the very uneven data sampling and the large gaps.
The periodogram of SS Leo is shown in Fig. 5.10. First of all, notice that there are several peaks
of almost equal height at f = 0.60, 1.60, 2.20, 2.60, 3.20 and 4.20 cycles/day. These correspond to
periods of 1.67, 0.62, 0.45, 0.38, 0.31, and 0.24 days. It is clear, however, that at least some of the
frequencies 0.60, 1.60 and 2.60 cycles/day are aliases owing to the daily gaps in the data. The true
period is 0.6263441 days which is f = 1.60 cycles/day. Therefore the peaks at 0.60 and 2.60 are
aliases. In this case it is almost impossible to select the correct period owing to severe aliasing.
The other sequence of peaks at 2.23, 3.23 and 4.23 cycles/day also seem to be mostly aliases,
but this time it is due to the highly non-sinusoidal nature of the variations. The light curve is poorly
represented by a sine wave, but it is better described by a sine wave with frequency f = 0.61
cycles/day together with the first harmonic at twice the frequency, f = 1.20 cycles/day. Therefore
3.20 and 4.20 are the aliases of the first harmonic. Thus the periodogram is well described by just
two frequencies, 0.60 and 1.20 cycles/day, and their 1 cycle/day aliases. The second harmonic at
f = 1.80 cycles/day is also present, but is much weaker.
This example, although extreme, illustrates the limitations of the method. To describe the light
curve of SS Leo accurately requires several Fourier components and not just the fundamental fre-
quency. As a result the power that would otherwise be concentrated in the fundamental frequency
is spread over several components, lowering the amplitudes. In this example we have, in addition,
observations which are sparse and include large gaps. This further complicates the periodogram
which now includes not only the two Fourier components of highest amplitude (the fundamental
and first harmonic), but also their 1 cycle/day and 2 cycle/day aliases. Under these circumstances,
it is best to use a different period finding technique.
Exercises
5.3.1 Calculate and plot the periodogram of the data in file dsct.dat between 0 and 10 cycles/day.
10.2
10.4
Magnitude 10.6
10.8
11
11.2
11.4
11.6
0 0.2 0.4 0.6 0.8 1 1.2 1.4
Phase
Figure 5.8: Light curve of SS Leo (period 0.6263441 days) showing large departure from a pure
sinusoid.
10.2
10.4
10.6
Magnitude
10.8
11
11.2
11.4
11.6
0 5 10 15 20 25 30 35
Days
0.2
0.15
0.1
0.05
0
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5
Frequency
Harmonics (multiples of the fundamental frequency) as discussed above are not really independent
periods since they are just a consequence of departure from a pure sinusoid. Many stars pulsate in
more than one period. For example some δ Scuti stars have 3, 4, 5 or even more periods. We extract
the frequencies in such data in the same way as we do for the case of just one period - by using the
periodogram.
Fig. 5.11 shows the periodogram of a δ Scuti star with three periods. Note that one can immedi-
ately pick out the frequency with the highest amplitude at f 1 = 7.156 cycles/day and another one at
a frequency close to 5 cycles/day. The periodogram is very clean and we note that the 1 cycle/day
aliases are very small. This is due to the fact that the star was observed from three continents round
the clock - from Australia, South Africa and Chile.
In order to be able to detect further frequencies, we need to remove the frequency of highest
amplitude from the original data. This process is called prewhitening. We fit a Fourier curve to the
data using the known frequencies and subtract the fitted Fourier curve from the data. In other words,
we find the least-squares solution to
Vi + i = a0 + a1 sin 2π f1 ti + a2 cos 2π f1 ti
+ a3 sin 2π f2 ti + a4 cos 2π f2 ti
+ a5 sin 2π f3 ti + a6 cos 2π f3 ti + . . .
where f1 , f2 , f3 , . . . are the frequencies so far extracted. Using the solution, we calculate the value of
V at time, t, subtract the calculated value, V ci , from the observed value, Vi and write ti and Vi − Vci to
a new file. A new periodogram is calculated and the next frequency is determined until the peak is
not high enough to distinguish it from noise. The program listed below implements this procedure.
Fig. 5-11 to 5-14 is an example of this procedure. The periodogram of the original data in
Fig. 5.11 gives f1 = 7.156 cycles/day. Fitting and removing the sinusoid with this frequency gives
the periodogram in Fig. 5-12 from which we find the next frequency, f 2 = 4.921 cycles/day. Fitting
the original data by f1 and f2 gives Fig. 5-13 from which we obtain f 3 = 5.345 cycles/day. Remov-
ing f1 , f2 , f3 from the original data gives Fig. 5-14. The highest peak is only about twice the height
of the noise and we cannot therefore be certain that it is real. There is no definite criterion to judge
whether a peak is real or not, but a general rule of thumb is not to accept peaks unless they are at
least 4 times higher than the background noise. We conclude that there are three certain frequencies
in this star: f1 = 7.156, f2 = 4.921 and f3 = 5.345 cycles/day.
We chose a data set where the aliases were of very low amplitude. More often than not, the
aliases are at least half the height of the main peak and it may become impossible to decide on
whether a frequency is real or an aliases, especially if it is close to an aliases of another frequency.
Exercises
5.4.1 Using the program of Ex. 1.2 (calculating the periodogram) and the program listed on page 10
(prewhitening with given frequencies), determine the frequencies present in file multiperiod.dat.
These are the data used to generate Fig. 5.11 to 5.14 and you should obtain the same frequen-
cies.
62 Chapter 5. Searching for hidden periods in data
0.025
0.02
Amplitude
0.015
0.01
0.005
0
0 1 2 3 4 5 6 7 8 9 10
Frequency
0.01
0.008
0.006
0.004
0.002
0
0 1 2 3 4 5 6 7 8 9 10
Frequency
0.003
0.002
0.001
0
0 1 2 3 4 5 6 7 8 9 10
Frequency
0.0003
0.0002
0.0001
0
0 1 2 3 4 5 6 7 8 9 10
Frequency
f = []
nf = int(raw_input("Number of frequencies? "))
for i in range(nf):
str = raw_input("Frequency %d? " % (i+1))
f.append(float(str))
fp = open("data.in","r")
m1 = 2*nf + 1
x = zeros((m1,m1))
y = zeros((m1,1))
xi = [0.0]*m1
n = 0
day = []
mag = []
for line in fp:
t = float(line.split()[0])
yi = float(line.split()[1])
day.append(t)
mag.append(yi)
xi[0] = 1.0
for k in range(1,nf+1):
wt = twopi*f[k-1]*t
xi[2*k-1] = math.sin(wt)
xi[2*k] = math.cos(wt)
for j in range(m1):
for k in range(m1):
x[j,k] += xi[j]*xi[k]
y[j,0] += yi*xi[j]
n += 1
fp.close()
X = mat( x.copy() )
Y = mat( y.copy() )
A = X.I*Y
# Remove Fourier fit from data and write prewhitened data.
fp = open("data.pre","w")
for i in range(n):
dmag = mag[i]- fourier(nf,f,A,day[i])
fp.write("%10.4f %10.4f\n" % (day[i],dmag))
fp.close()