Download as pdf or txt
Download as pdf or txt
You are on page 1of 29

Brief introduction to scientific Python with

application to numerical relativity data


N. K. Johnson-McDaniel

National Workshop on Gravitational Wave Astronomy

Dibrugarh University

2-4.11.16
Python basics

Python is a simple language. In particular, it is interpreted,


not compiled, which makes it easy to use to carry out
small tasks interactively, or to test things when working on
a bigger problem.

However, it is also quite powerful for scientific applications


(that dont require super-high performance) due to the
many packages that have been developed for it.

One can either run commands in Python interactively in


the Python or IPython shells, or run a program by entering
python program.py in the shell.
Hello Worldinteractive

Lets try both methods.

First interactively: Open either the Python or IPython


shells (enter either python or ipython on the command
line).

Now enter
>>> print(Hello World!)

You should see the output immediately.


Hello Worldprogram
To create a hello world program (e.g., helloworld.py), open your favourite text editor
(e.g., vim or emacs; you want something that will save in plaintext).

All you need to enter is the same command as before, save, and enter python
helloworld.py in the command line.

However, its good to get in the habit of commenting/documenting ones programs, so


add something before it saying that this is a test program, and also giving your name
and the date. You need to use the # character at the start of the line to comment it out.

(One can also comment out blocks of text by enclosing them in """ """:

# This is a comment.

"""
This is also a comment.
"""
Arithmetic in Python

Go back to your Python shell, and experiment with basic


arithmetic: +, -, *, and / work as expected; ** gives
exponentiation.

Note that Python treats number without decimal points


as integerscompare, e.g., 1/10 and 1./10 (or 1/10.).
Importing packages, numpy, and scipy
However, if one wants to do anything more complicated mathematically
than simple arithmetic, one has to go beyond basic Python (or write
ones own function).

numpy is the standard numerical package for Python, though there are
also some useful mathematica things in scipy, notably special functions.
Well see how to import these (and how to import things in general).

Well still work in interactive mode:


>>> import numpy

loads numpy, and you can now, e.g., compute trigonometric functions.
Try
>>> numpy.sin(numpy.pi)
Importing packages, numpy, and scipy (cont.)

As you can probably already see, having to type numpy


all the time when coding would be extremely annoying.

Fortunately, there are several ways to make things


simpler.

The standard way is


>>> import numpy as np

Then one can write, e.g.,


>>> np.sin(np.pi)
Importing packages, numpy, and scipy (cont.)
If you just want a function or two from a package, you can just import these, e.g.,

>>> from numpy import sin, cos
>>> from scipy.special import gamma # Import gamma function
>>> from scipy.misc import factorial2 # Import double factorial

or even

>>> from numpy import sin as s

Of course

>>> from numpy import *

imports everything, but is generally frowned upon in Python circles as polluting
namespaces.
Numpy arrays

Numpy arrays are a very powerful tool for scientific


computations.

In particular, just like in Matlab, one can easily apply


functions to each element of the array, e.g. (assuming you
have imported numpy as np),

>>> np.sin([5, 6])

Similarly, one can perform arithmetic element-wise, e.g.,



>>> np.array([5,6])*np.array([1,2])
Numpy arrays (cont.)

One can also easily create arrays of a specified length


containing, e.g., just zeros or ones

>>> np.zeros(5)
>>> np.ones(5)

Similarly, one can create arrays of linearly spaced numbers



>>> np.linspace(0, 1, 10)

gives an array of 10 numbers evenly spaced between 0
and 1 and including both endpoints.
Numpy arrays (cont.)

Its also easy to pull out specific elements of arraysnote that


Python arrays are indexed from 0.

>>> a = np.linspace(0, 1, 10)
>>> a[0]
>>> a[9]

Note that a[10] gives an index out of range error.

Negative numbers index from the end (starting from -1)



>>> a[-1]
>>> a[-10]
Numpy arrays (cont.)
Now try, e.g.,

>>> b = np.sin(np.pi*a)

One can also easily find out where an arrays elements satisfy some
condition, e.g.,

>>> idx = np.where(b < 0.4)
>>> idx

Now one can use this array of indices to select out the elements of this
array that satisfy the condition and perform some other operation, e.g.,

>>> np.cos(a[idx]*b[idx])
Numpy arrays (cont.)

Similarly, finding maxima and minima is easy



>>> np.max(b)
>>> np.min(b)
Plotting

The standard Python plotting package is matplotlib,


usually loaded using

>>> import matplotlib.pyplot as plt

We can now plot data via



>>> plt.plot(x, y)
>>> plt.show()
>>> plt.scatter(x, y)
>>> plt.show()
Plotting labels

One can label axes using label=curve label and include


colour with things like color = red; plt.legend() includes
the legend, e.g.,

>>> plt.plot(x, y, label=curve, color = red)
>>> plt.legend()

Similarly, one can label axes (with LaTeX symbols, if you


are used to this)

>>> plt.xlabel($\eta$)
>>> plt.ylabel($M_f$)
Python and whitespace: For, if, and def (function)

While youre probably used to indenting things inside for,


if, or function definition blocks, Python makes this a
requirement.

Conversely, adding a space before a line that doesnt


need one raises an error (try this interactively!).

The following bits are best tried non interactively (though


its possible to do them interactively if you want to try):

for k in np.linspace(1,10,20):
print(k)
Python and whitespace: For, if, and def (function)

a = 5
if a > 1:
print(a)

def testfunc(x, y):


diff = x - y
return diff*diff - diff/3., 5.*diff
testfunc(1,1)
testfunc(3,1)
testfunc(3,1)[0]
associated with each QNM. The complex frequencies are tm
match ! !t m
match and t m
match , i.e.,
known functions of the final black-hole mass and spin and "
insp!plunge m
can be found in Ref. [55]. hm tmatch !
In this paper we model the ringdown modes as a linear N
Function practicecombination of eight QNMs, i.e., N 8. Mass and spin of
hm merger!RD m
"
tmatch
the final black hole Mf and af are computed from numeri-
cal data for mass ratios q 1, 2, 3, 4 and 6. Notably, we
Code up a function returning the following simple fits for the final mass (as k 0; 1; of
a fraction ..;N ! 3
2; .the
employ the fitting formula obtained by fitting the numeri-
total mass) and dimensionless spin of nonspinning binary black holes from Pan et al. PRD
cal results of Mf and
(2011) as functions
a
of , fthe symmetric mass ratio. and
0s 1 "

M 8 h_ insp!plunge m
tmatch !
1 @ ! 1A# ! 0:4333#2 ! 0:4392#3 ;
f
(29a) m
N
M 9 "
af p h _ merger!RD m
tmatch
2 3
12# ! 3:871# 4:028# : (29b) m
Mf
k 0 and N ! 3
Here (nu), also
The above known
formula in the literature as (eta, which I use belowsorry!) is defined as
2 differs from the analogous fitting for-
:= mgiven
mula 1m2/(min 1 + m2) [20]
Ref. andby hence
<0:3%ranges
in Mfromand
0 to<2%1/4. in
(Your
a , function
The should
matching taketimein ant m
match is fixe
f f
arbitrary value of , though, not necessarily
because of the more accurate numerical data used here. just a linspace array.) h m mode, i.e., t m
match t"
peak
The complex amplitudes A val !tm
mn in Eq. (28) are deter- match is an adjustable pa
In principle,
mined your function
by matching shouldmerger-ringdown
the EOB raise an error if your input outside
waveform of these
ducing thebounds,
difference butagainst n
lets
(28)ignore
with thethisEOB
to make this as simple
inspiral-plunge as possible.
waveform (13). In order modes (see Table I).

However, you might think about defining eta2 = eta*eta and eta3 = eta2*eta to reduce the
number of floating-point operations.
124052-7
Function practice (cont.)
Save your function in a Python file (say Mf_af_func.py), and then load it
into an interactive session using

>>> import Mf_af_func

OR

>>> from Mf_af_func import *

[You can also replace * with your function name.]

In the first case you call your function as Mf_af_func.functionname(eta),
while in the second case you just call it using functionname(eta).

Plot the final mass and spin functions versus eta.


3d plotting
To plot in 3d, one has to import something in addition to matplotlib.pyplot:

>>> from mpl_toolkits.mplot3d import Axes3D

Then one can create a figure and then create 3d axes (gca = get current axes,
creating them if necessary)

>>> fig = plt.figure()
>>> ax = plt.gca(projection=3d')

One can now plot



>>> ax.plot(x, y, z)
>>> plt.show()

Try to plot the final mass and spin fits versus eta in 3d.
Data reading
There are various ways of reading data into Python.

If one has a table with headers, np.genfromtxt is a very convenient way of reading in data.

Lets see this with a simple example where we make our own data.

Make a file with a header and three columns of data like



# Label data_1 data_2
First 0.4 5.3e-10
Second 100 7.0e8
Second_prime 100.5 7.4e10

and save it as something like data.txt.

Now load it into Python interactively using



>>> data = np.genfromtxt("path to data.txt, dtype=None, names=True)
Data reading

Now we can look at the data, e.g.,



>>> data
>>> data[Label]
>>> data[Label][2]
>>> data[data_1]

This ability to refer to the data columns by their


associated string in the header is very useful (and used in
the LIGO parameter estimation code).
Checking the final mass and spin fits
Read in the SXS catalogue data table (using np.genfromtxt after editing the header
appropriately),
-3
select out the nonspinning simulations (say, those with spin magnitudes less
than 10 ) and check that the fit is accurate for these.

Note that you will need to normalize the final mass by the sum of the initial masses and the
final spin by the final mass squared (since it is the fully dimensionful final spin).

You may also want to check that adding in spins introduces features in the final mass and
spin that are not captured by these nonspinning fits (as expected). You can even restrict to
just nonprecessing, aligned-spin cases (with x- and y-components close to 0) and find that
this is still the case.

You can at least look at the final masses and spins for high spin simulations (either
selecting them in Python or via the web interface to the SXS catalogue) and see that these
are not accurately captured by this nonspinning fit.

You can also experiment with plotting the residual of this fit versus spin quantitiesthe
dominant effect comes from the spins along the orbital angular momentum.
Looking at BBH data
We will now plot some BBH waveforms and coordinate tracks.

First well have a look at the metadata file, which tells you lots about
the simulation.

Now, we need to load in the waveform data (in


rhOverM_Asymptotic_GeometricUnits.h5), which requires reading
the HDF5 data format. Fortunately, there is a Python module that
does this:

>>> import h5py
>>> data_file = h5py.File("path to HDF5 file, r)

r means that we open to read.
Looking at BBH data
Now to look at the contents of an HDF5 file, one can do

>>> np.array([name for name in data_file])

OR

>>> for name in data_file:
. print name

This just shows the top directories. To access a given directory, just
use data_file[directory name]; this also works for subdirectories.

Alternatively, one can use the HDFView program.


Plotting a BBH waveform

Now one can pick an extrapolation order (or use the outermost
extraction radius) and plot the dominant quadrupolar part of
the waveform (l = m = 2 in this [spin-weighted] spherical
harmonic decomposition). You want Y_l2_m2.dat

These data are in the form



time, real part, imaginary part

You can pick out all the time values using [:,0], and the real and
imaginary parts similarly.

Try plotting the real part (versus time) first.


Plotting a BBH waveform (cont.)
Now you can try plotting the imaginary part and/or the amplitude and phase.

To get the phase, youll want to use np.arctan2(real, imaginary) and unwrap the
phase with np.unwrap.

You can also check that the initial angular velocity (after the junk) is twice the initial
orbital frequency given in the metadata using np.gradient (which youll need to apply
to both phase and time).

Also, you can try to find the minimum mass needed such that the minimum 2,2
mode frequency in this waveform is above LIGOs (current) minimum frequency of
20 Hz; note that 1 solar mass is ~5 s.

You can also look at the higher modesthe nonzero ones in this simple nonspinning
equal mass case are the ones with even m. You should try at least 3, 2 and 4, 4.
(The negative m modes are the same as the corresponding positive m modes up to
a sign in this simple case; this is not true in more complicated precessing cases.)
Analyzing a BBH waveform
You can also compute the radiated energy and see how much
2
comes from each mode. The expression is dElm/dt = |r dhlm/dt| /16.
np.cumsum can be used to integrate to get the total energy radiated
in each mode (making sure to include the gradient of the time in the
integrand).

Here you can see if you can reproduce (to good accuracy) the final
mass given in the metadata by subtracting off the radiated energy
from the initial ADM energy. [The expressions for the angular and
linear momentum radiated are rather more complicated but can be
found in Ruiz et al. GRG (2008).]

You can also check the peak luminosity from the 2,2 and 2,-2 modes
dominates and is very close to the value obtained for GW150914.
Plotting the trajectory

You can look at the trajectory of one of the black holes. This
is in Horizons.h5, where you want to look at
CoordCenterInertial.dat in AhA.dir or AhB.dir.

Here the columns are



time x y z

If you have the data, youll find that SXS:BBH:0078 has


much more interesting waveforms and trajectories (including
for the final black hole, in AhC.dir), due to the effects of
precession and recoil. Youll want to plot its trajectories in 3d.

You might also like