How to Use

Data Structure

Most useful halo information is stored in the prop attribute of the class halo_props. And to modify what to be calculated, one will need to provide a new field dictionary similarly organized as the default field dictionary. Note that quantities related to temperature and luminosity are mostly hard coded, so you may need to modify the source code to meet your own requirements.

Generally there are four types of properties stored in the prop attribute: R (radius), M (mass), T (temperature) and S (entropy). And the following list explains the physical meaning of the terms in the default field dictionary.

R:

Key name

Description

vir

Virial radius \(R_{vir}\)

200

\(R_{200}\)

500

\(R_{500}\)

2500

\(R_{2500}\)

M (\(X = 200, 500\)):

Key name

Description

vir

Mass enclosed within \(R_{vir}\)

200

Mass enclosed within \(R_{200}\)

500

Mass enclosed within \(R_{500}\)

2500

Mass enclosed within \(R_{2500}\)

starX

Stellar mass enclosed within \(R_{X}\)

gasX

Gas mass enclosed within \(R_{X}\)

barX

Baryon mass enclosed within \(R_{X}\)

ismX

ISM mass enclosed within \(R_{X}\)

coldX

Cold gas mass enclosed within \(R_{X}\)

igrmX

IGrM mass enclosed within \(R_{X}\)

total_star

Stellar mass within halo, including contribution from subhalo

self_star

Stellar mass within halo, excluding contribution from subhalo

Before introducing temperature and entropy, I will first talk about the luminosities used during calculation:

Name

Key name

Description

\(L_X\)

Lx

0.5-2.0keV X-ray luminosity, including both continuum and line emission

\(L_{X, cont}\)

Lx_cont

0.5-2.0keV X-ray luminosity, only including continuum emission

\(L_{X, broad}\)

Lxb

0.1-2.4keV X-ray luminosity, including both continuum and line emission

\(L_{X, broad, cont}\)

Lxb_cont

0.1-2.4keV X-ray luminosity, only including continuum emission

T (only hot diffuse gas (IGrM) is taken into account when calculating temperature):

Key name

Description

x

Emission weighted temperature using \(L_X\)

x_cont

Emission weighted temperature using \(L_{X, cont}\)

mass

Mass weighted temperature

spec

Spectroscopic temperature (See Vikhlinin, 2006)

spec_corr

Spectroscopic (core-corrected) temperature

x_corr

Emission weighted (core-corrected) temperature using \(L_X\)

x_corr_cont

Emission weighted (core-corrected) temperature using \(L_{X, cont}\)

mass_corr

Mass weighted (core-corrected) temperature

spec500

Spectroscopic temperature at \(R_{500}\) (used when calculating entropy)

spec2500

Spectroscopic temperature at \(R_{2500}\) (used when calculating entropy)

S (calculated within a thin spherical shell with default thickness = 1 kpc):

Key name

Description

500

Entropy of the shell at \(R_{500}\)

2500

Entropy of the shell at \(R_{2500}\)

Besides, there are other attributes of class halo_props which might be useful, see Halo Analysis Module for details.

Examples

The following codes show how to use this package to analyze a simulation using GIZMO. For other outputs, you may need to change some details. Though the sricpts below is written in a .py file, Jupyter Notebook is strongly recommanded for doing the following analysis, just like the one done in Final_Tutorial.ipynb .

import pynbody as pnb
import astropy.constants as c
import astropy.units as u
import numpy as np
import h5py
import matplotlib.pyplot as plt
import os
from astropy.table import Table
import modules.cosmology as cos
import modules.gas_properties as g_p
import modules.halo_analysis as h_a
import modules.prepare_pyatomdb as ppat

# Load simulation snapshot
data_file = 'data_file'
param_file = 'param_file'
s = pnb.load(data_file, paramfile = param_file)
s.physical_units()

# To correct the bug in the units of internal energy in the
# temperary version of pynbody, check before you apply. pynbody still
# use comoving units to interprete GIZMO internal energy while GIZMO
# actually uses physical units by default. See 
# http://www.tapir.caltech.edu/~phopkins/Site/GIZMO_files/gizmo_documentation.html#snaps 
# for details.
s.gas['u'] /= pnb.array.SimArray(s.properties['a'], units='1')

s.gas['X_H'] = 1-s.gas['metals'][:,0]-s.gas['metals'][:,1] # hydrogen mass fraction
s.gas['nh'] = g_p.nh(s) # Hydrogen number density

# From internal energy to temperature. Skip this line for tipsy outputs.
s.gas['temp'] = g_p.temp(s)

s.gas['ElectronAbundance'].units = '1'
s.gas['ne'] = s.gas['ElectronAbundance'] * s.gas['nh'].in_units('cm**-3') # eletron number density
s.gas['volume'] = s.gas['mass']/s.gas['rho'] # volume of gas particles
# Filtering hot diffuse gas particles
hotdiffusegas = s.gas[pnb.filt.HighPass('temp', '5e5 K')&pnb.filt.LowPass('nh', '0.13 cm**-3')]

# Calculate mass fraction of the included elements. The first column must be Hydrogen mass fraction.
# And you will need to change the codes for tipsy outputs.
s.gas['mass_fraction'] = np.zeros(s.gas['metals'].shape)
s.gas['mass_fraction'][:, 0] = s.gas['X_H']
s.gas['mass_fraction'][:, 1:] = s.gas['metals'][:, 1:]

# Abundance relative to solar abundance, don't have to be in gizmo format. 
# See function documentation for details.
hotdiffusegas['abundance'] = g_p.abundance_to_solar(hotdiffusegas['mass_fraction'])

# Find proper pytspec calibration file. See pytspec documentation for details.
cal_dat_dir = 'cal_dat_dir'
cal_dat_files = os.listdir(cal_dat_dir)
cal_redshift = []
for calfile in cal_dat_files:
    cal_redshift += [eval(calfile[10:17])]
calfile_idx = np.abs(np.array(cal_redshift) - s.properties['z']).argmin()
cal_file = (cal_dat_dir + cal_dat_files[calfile_idx])

# Calculate luminosity

# Recommend uncomment the following lines for the first time to generate 
# the emissivity files containing all available elements in pyatomdb.

# ----------start-------------
# e_band = [0.5, 2.0]
# e_filename = 'your_dir/{:.1f}-{:.1f}keV_emissivity_all_elements.hdf5'.format(e_band[0], e_band[1])
# ppat.load_emissivity_file(filename=e_filename, energy_band=e_band)
# -----------end--------------

narrow_band_file = 'your_dir/0.5-2.0keV_emissivity_all_elements.hdf5'
broad_band_file =  'your_dir/0.1-2.4keV_emissivity_all_elements.hdf5'

# gas property initialization
Emission_type = ['Lx', 'Lxb', 'Lx_cont', 'Lxb_cont']
for i in Emission_type:
    s.gas[i] = 0
    s.gas[i].units = 'erg s**-1'

# Total X-ray luminosity
hotdiffusegas['Lx'] = g_p.calcu_luminosity(gas=hotdiffusegas, filename=narrow_band_file, \
                                           mode='total', band=[0.5, 2.0])
hotdiffusegas['Lxb'] = g_p.calcu_luminosity(gas=hotdiffusegas, \
                            filename=broad_band_file, mode='total', band=[0.1, 2.4])

# Only continuum emission are taken into account
hotdiffusegas['Lx_cont'] = g_p.calcu_luminosity(gas=hotdiffusegas, filename=narrow_band_file, \
                                                mode='cont', band=[0.5, 2.0])
hotdiffusegas['Lxb_cont'] = g_p.calcu_luminosity(gas=hotdiffusegas, \
                                    filename=broad_band_file, mode='cont', band=[0.1, 2.4])


# Halo Analysis
h = s.halos() # Load halo catalogue
# Initialize the class halo_props. The datatype here means 
# we use gizmo outputs and amiga halo finder
halo = h_a.halo_props(h, datatype='gizmo_ahf')

galaxy_low_limit = 64 * s.star['mass'].mean() # Limits above which galaxies are considered as luminous
halo.init_relationship(galaxy_low_limit=galaxy_low_limit)

# To check if there is any unusual halo_ids and host_ids. 
# For ahf results of Liang's data, there are some halos whose 
# "hostHalo" ID is not recorded in "#ID". I choose to ignore these halos.
print("Error List:", halo.errorlist)

# Calculate halo radii and masses
halo.calcu_radii_masses(halo_id_list=halo.host_list)
# Calculate some specific masses, like starX, gasX, etc.
halo.calcu_specific_masses(halo_id_list=halo.host_list)
# Calculate temperatures and luminosities. Calculate these quantities 
# together will save some time. But individual methods are also provided 
# in halo_props if you want to calculate separately.
halo.calcu_temp_lumi(cal_file=cal_file, halo_id_list=halo.host_list)
# Calculate entropy.
halo.calcu_entropy(cal_file=cal_file, halo_id_list=halo.host_list)
# Save data.
halo.savedata('file_name_whatever_you_like.hdf5', halo_id_list=halo.host_list)

The main calculation part is now done. If you want to see what plots you can get based on above calculation, see Plots.ipynb.