17.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.
#required packages
import numpy as np
import matplotlib.pyplot as plt
import requests
import json
import h5py
These two functions facilitate the communication with the illustris server:
def get(path, params=None):
# make HTTP GET request to path
headers = {"api-key":"2566d8dd2bcf9aefbb3d8b01080a877b"}
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
redshifts=[1.0,0.5,0.0]
snapshots=[85,103,135]
id=75 # 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 10kpc at z=',redshifts[i],
' is M={0:5.2f} x 10^10 Msun'.format(len(rr[rr<10])*dm/1e10))
mass inside 10kpc at z= 0.0 is M= 3.91 x 10^10 Msun mass inside 10kpc at z= 0.5 is M= 0.90 x 10^10 Msun mass inside 10kpc at z= 1.0 is M= 0.70 x 10^10 Msun
Aside: Plot particle distributions
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=75 # 1030 75 543681
# a plot object with axes as described in ax
g = plt.figure(figsize=(8,8))
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'])
ax=g.add_subplot(2,2,1+i)
ax.scatter (dx,dy,c=colors[i],alpha=0.3)
ax.legend(['z='+str(redshifts[i])])
ax.set_xlim(-100,100)
ax.set_ylim(-100,100)
x= 1185.92 y= 28402.8 z= 19596.4 x= 1003.55 y= 26964.2 z= 18095.4 x= 1069.38 y= 26254.8 z= 18840.5
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
nbins=20
maxradius=50
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=np.histogram(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.
%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')
Text(0.5, 0, 'kpc')
3. We model the density profiles using analytical representations :
from scipy.optimize import curve_fit
def dark(x,a,rs):
return a/((x/rs)*(1+(x/rs))**4)
# example: z=0
x=xp[2,:]
y=count[2,:]/xpsq[2,:]
err=e[2,:]/xpsq[i,:]
a=0.1
rs=12
keep = err>0
popt, pcov = curve_fit(dark, x[keep], y[keep], sigma=err[keep])
plt.loglog(x, y,x,dark(x,*popt))
plt.legend(['illustris','dark'])
<matplotlib.legend.Legend at 0x71597b4c4860>
18.01 Exercise for programming classes
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
p1 = Particle(2., 1., 2., 3., 1., -3, -1)
p1 = Particle(2., 1., 2., 3., 1., -3, -1)
print (p1.kinetic_energy())
3.3166247903554
p1.move(1.)
print(p1.x, p1.y, p1.z)
p1.move(1.)
print(p1.x, p1.y, p1.z)
import numpy as np
p2 = ChargedParticle(3., 4., 3., -2., 1, 1, 1, -1.)
p2.distance(p1)
7.681145747868608
19.01
diogenes=['Cede','paulum','e','sole']
dic={'Cede':'Go','paulum':'a little','e':'out of','sole':'the sun'}
answer = [dic[word] for word in diogenes]
print (answer)
['Go', 'a little', 'out of', 'the sun']
19.02
# using the solution from exercise 06.02 on day 02
def digit_sum(x):
ans=0
for digit in str(x):
ans=ans+int(digit)
return ans
answer=[num for num in range(0,1001) if digit_sum(num)==7]
print (answer)
[7, 16, 25, 34, 43, 52, 61, 70, 106, 115, 124, 133, 142, 151, 160, 205, 214, 223, 232, 241, 250, 304, 313, 322, 331, 340, 403, 412, 421, 430, 502, 511, 520, 601, 610, 700]