Exercise solutions Day 5 (October 14)

Python course Wintersemester 2016/17 University of Heidelberg

18.01 Solution to the Illustris problems

Here we consider a halo at the three redshifts z=0, z=0.5 and z=1 and study the mass distribution.

In [ ]:
#required packages
import numpy as np
import matplotlib.pyplot as plt
import requests
import json
import h5py
%matplotlib inline

These two functions facilitate the communication with the illustris server:

(they are provided on the Illustris project web page)

In [189]:
def get(path, params=None):
    # make HTTP GET request to path
    headers = {"api-key":"17622d2de3e7b5f0d82da490499ade35"}
    r = requests.get(path, params=params, headers=headers)

    # raise exception if response code is not HTTP SUCCESS (200)
    r.raise_for_status()

    if r.headers['content-type'] == 'application/json':
        return r.json() # parse json responses automatically

    if 'content-disposition' in r.headers:
        filename = r.headers['content-disposition'].split("filename=")[1]
        with open(filename, 'wb') as f:
            f.write(r.content)
        return filename # return the filename string

    return r

def getsnapshot(id,num):
    params = {'dm':'Coordinates'}
    url = "http://www.illustris-project.org/api/Illustris-1/snapshots/" + str(num) + "/subhalos/" + str(id)
    subhalo = get(url)                                     # read the parameters of the subhalo in the json format
    return subhalo,get(url+"/cutout.hdf5", params=params)  # read the cutout of the subhalo into memory

1. Determine masses

In [187]:
redshifts=[1.0,0.5,0.0]
snapshots=[85,103,135]

id=1030 # 1030   75      543681
dm=6.3e6

for i in reversed(range(3)):
    sub,filename=getsnapshot(id,snapshots[i])   # get snapshot with ID=id and given snapshot number
    
    with h5py.File(filename) as f:
        dx = f['PartType1']['Coordinates'][:,0] - sub['pos_x']
        dy = f['PartType1']['Coordinates'][:,1] - sub['pos_y']
        dz = f['PartType1']['Coordinates'][:,2] - sub['pos_z']
        rr=np.sqrt(dx**2+dy**2+dz**2)
        
        print ('mass inside 1000kpc at z=',redshifts[i],
                   ' is M={0:5.2f} x 10^10 Msun'.format(len(rr[rr<1000])*dm/1e10))
mass inside 1000kpc at z= 0.0  is M= 0.47 x 10^10 Msun
mass inside 1000kpc at z= 0.5  is M= 0.16 x 10^10 Msun
mass inside 1000kpc at z= 1.0  is M= 0.09 x 10^10 Msun

Aside: Plot particle distributions

In [188]:
colors=['red','green','blue']
redshifts=[1.0,0.5,0.0]
snapshots=[85,103,135]
dict={0:[0,0],1:[0,1],2:[1,0]}   # this is used to associate sub-plots with the indices 0,1,2

id=543681 # 1030   75      543681

g, axarr = plt.subplots(2,2)     # a plot object with axes as described in axarr

for i in range(3):
    sub,filename=getsnapshot(id,snapshots[i])   # get snapshot with ID=id and given snapshot number
    with h5py.File(filename) as f:
        dx = f['PartType1']['Coordinates'][:,0] - sub['pos_x']
        dy = f['PartType1']['Coordinates'][:,1] - sub['pos_y']
        dz = f['PartType1']['Coordinates'][:,2] - sub['pos_z']
        print('x=',sub['pos_x'],'y=',sub['pos_y'],'z=',sub['pos_z'])
        
        axarr[dict[i][0],dict[i][1]].scatter (dx,dy,c=colors[i],alpha=0.3)
        axarr[dict[i][0],dict[i][1]].legend(['z='+str(redshifts[i])])
        axarr[dict[i][0],dict[i][1]].set_xlim(-200,200)
        axarr[dict[i][0],dict[i][1]].set_ylim(-200,200)
x= 70994.8 y= 35729.7 z= 8726.48
x= 47892.8 y= 67179.4 z= 21451.5
x= 53021.9 y= 36437.7 z= 25620.0

2. Determine the density profile \(\frac{dN}{dV}\).

The density profile (objects per spherical shell) can be determined by

binning the separation array [r1,r2,r3,...] for all dark matter particles with plt.hist

In [88]:
nbins=20
maxradius=200
xp   = 0*np.ones((3,nbins))
xpsq = 0*xp
e    = 0*xp
count= 0*xp

id=1030 # 1030   75      543681
snapshots=[85,103,135]

for i in range(3):                             # loop through three snapshots/redshifts
    sub,filename=getsnapshot(id,snapshots[i])  # get json header and data file  
    with h5py.File(filename) as f:
        dx = f['PartType1']['Coordinates'][:,0] - sub['pos_x']
        dy = f['PartType1']['Coordinates'][:,1] - sub['pos_y']
        dz = f['PartType1']['Coordinates'][:,2] - sub['pos_z']
        
    rr = np.sqrt(dx**2 + dy**2 + dz**2)        # radii of all dm particles
  
    """ create a histogram with 20 bins
        num = counts
    and   x = bin boundaries
    are used """

    num,x,ax=plt.hist(rr,bins=nbins,range=(0,maxradius))
    
    xp[i,:]=(x[1:]+x[:-1])/2                   # the middle of each bin
    xpsq[i,:]=(4*np.pi/3)*x[1:]**3-x[:-1]**3   # spherical shell volume
    count[i,:]=num                             # counts
    e[i,:]=np.sqrt(num)                        # Poisson error bar

Work out the occupation in each bin, work out volumes, work out Poisson error bars.

In [91]:
%matplotlib inline
for i in range(3):
    plt.errorbar(xp[i,:],count[i,:]/xpsq[i,:],
                 yerr=e[i,:]/xpsq[i,:])

plt.xscale('log')
plt.yscale('log')
plt.xlim(1,100)
plt.ylim(1e-6,1)
plt.legend(['z=1.0','z=0.5','z=0.0'])
plt.xlabel('kpc')
Out[91]:
<matplotlib.text.Text at 0x7f0104987dd8>

3. We model the density profiles using analytical representations :

In [159]:
from scipy.optimize import curve_fit
def dark(x,a,rs):
    return a/((x/rs)*(1+(x/rs))**4)
In [157]:
# example: z=0
x=xp[2,:]
y=count[2,:]/xpsq[2,:]
err=e[2,:]/xpsq[i,:]
a=0.1
rs=12
In [170]:
keep = err>0 
popt, pcov = curve_fit(nfw, x[keep], y[keep], sigma=err[keep])
In [174]:
plt.loglog(x, y,x,dark(x,*popt))
plt.legend(['illustris','dark'])
Out[174]:
<matplotlib.legend.Legend at 0x7f0104555cc0>

19.01 ** Exercise for programming classes **

In [4]:
class Particle(object):
    
    def __init__(self, mass, x, y, z, vx, vy, vz):
        self.mass = mass
        self.x = x
        self.y = y
        self.z = z
        self.vx = vx
        self.vy = vy
        self.vz = vz
    
    def kinetic_energy(self):
        return 0.5 * self.mass * np.sqrt(self.vx ** 2 +
                                         self.vy ** 2 +
                                         self.vz ** 2)
    
    def distance(self, other):
        return np.sqrt((self.x - other.x) ** 2 +
                       (self.y - other.y) ** 2 +
                       (self.z - other.z) ** 2)
    
    def move(self, dt):
        self.x = self.x + self.vx * dt
        self.y = self.y + self.vy * dt
        self.z = self.z + self.vz * dt
        
class ChargedParticle(Particle):
    
    def __init__(self, mass, x, y, z, vx, vy, vz, charge):
        Particle.__init__(self, mass, x, y, z, vx, vy, vz)
        self.charge = charge
In [ ]:
p1 = Particle(2., 1., 2., 3., 1., -3., 4.)
print (p1.kinetic_energy())
In [ ]:
p1.move(1.)
print(p1.x, p1.y, p1.z)
In [ ]:
p1.move(1.)
print(p1.x, p1.y, p1.z)
In [ ]:
p2 = ChargedParticle(3., 4., 3., -2., -2., 3., 2., -1.)
p1.distance(p2)