Halo Catalog Analysis Example: calculating radial profiles

In this example, we’ll show how to start from a subhalo catalog and calculate how various halo properties vary as a function of 3d distance from some nearby halo, i.e. we’ll show how to use Halotools to calculate radial profiles. In particular, we’ll see how the radial_profile_3d function can be used to show that the mass accretion rate of halos decreases as those halos approach a nearby cluster. We’ll also show how to calculate the mean number density of some sample as a function of cluster-centric distance.

Before following this tutorial, make sure you understand Halo Catalog Analysis Example: halo properties as a function of host halo mass, as that tutorial covers basic material such as how halo data is organized into an Astropy Table object.

There is also an IPython Notebook in the following location that can be used as a companion to the material in this section of the tutorial:

halotools/docs/notebooks/halocat_analysis/basic_examples/halo_catalog_analysis_tutorial2.ipynb

By following this tutorial together with this notebook, you can play around with your own variations of the calculation as you learn the basic syntax.

Example 1: \(dM_{\rm vir}/dt\) vs. halo-centric distance

Let’s start out by selecting two samples of halos from the Bolshoi simulation: one sample of group-mass host halos, and a second sample of lower-mass halos.

Note that the lower-mass sample includes subhalos since we do not make a halo_upid cut.

from halotools.sim_manager import CachedHaloCatalog
halocat = CachedHaloCatalog(simname = 'bolshoi', redshift = 0)

host_mask = ((halocat.halo_table['halo_upid'] == -1) &
                (halocat.halo_table['halo_mvir'] > 1e13) &
                (halocat.halo_table['halo_mvir'] < 2e13))
group_mass_hosts = halocat.halo_table[host_mask]

low_mass_mask = ((halocat.halo_table['halo_mpeak'] > 5e11) &
                (halocat.halo_table['halo_mpeak'] < 6e11))
low_mass_halos = halocat.halo_table[low_mass_mask]

The first question we will ask of these is halos is the following: how does the mass accretion rate of (sub)halos vary as a function of the distance to the group-mass hosts?

As with all mock_observables functions that accept 3d positions for arguments, we first format our positions into an array of the expected shape.

from halotools.mock_observables import return_xyz_formatted_array

group_mass_hosts_pos = return_xyz_formatted_array(group_mass_hosts['halo_x'], group_mass_hosts['halo_y'], group_mass_hosts['halo_z'])
low_mass_halos_pos = return_xyz_formatted_array(low_mass_halos['halo_x'], low_mass_halos['halo_y'], low_mass_halos['halo_z'])

The radial_profile_3d function accepts two different kinds of inputs for the separation bins. If you pass in rbins_normalized and normalize_rbins_by, then this combination of arguments allows you to calculate how various quantities vary as a function of, for example, \(x = r / R_{\rm vir}.\) The way this works is that the normalize_rbins_by argument stores the value of \(R_{\rm vir}\) for each point in sample1, and the rbins_normalized argument will be interpreted as referring to the distance of points in sample2 scaled by this value.

In the following call to the radial_profile_3d function, we will calculate the mean mass accretion rate of the lower-mass halos in 20 bins linearly spaced between \(0.1 < r / R_{\rm vir} < 15.\)

from halotools.mock_observables import radial_profile_3d

rbins_normalized = np.linspace(0.1, 15, 20)
rbins_midpoints = (rbins_normalized[:-1] + rbins_normalized[1:])/2.

result = radial_profile_3d(group_mass_hosts_pos, low_mass_halos_pos, low_mass_halos['halo_dmvir_dt_tdyn'],
                           rbins_normalized = rbins_normalized,
                           normalize_rbins_by = group_mass_hosts['halo_rvir'],
                           period=halocat.Lbox)
plt.plot(rbins_midpoints, result, color='blue')

plt.xlabel(r'$r / R_{\rm vir}$', fontsize=20)
plt.ylabel(r'$\langle dM_{\rm vir} / dt \rangle$  $[M_{\odot}/{\rm yr}]$', fontsize=20)
plt.xticks(size=15); plt.yticks(size=15)
../../../../../_images/radial_profile_halocat_tutorial_fig1.png

As we’ll see in the next example, if you instead want to calculate the profile of a quantity as a function of the absolute distance rather than some scaled distance, you can use the rbins_absolute argument instead.

Example 2: Number density vs host-centric distance

In this next example, we’ll calculate the answer to the following question: how does the abundance of (sub)halos vary as a function of host-centric distance to our sample of group-mass host halos?

The radial_profile_3d function has a return_counts argument that can be used to additionally return the number of objects as a function of the input distance. In the following call to the radial_profile_3d function, we will calculate the mean mass accretion rate of the lower-mass halos in 20 bins linearly spaced in r between \(0.5 {\\rm Mpc} < r < 10 {\\rm Mpc}.\)

rbins_absolute = np.linspace(0.5, 10, 20)
rbins_midpoints = (rbins_absolute[:-1] + rbins_absolute[1:])/2.

result, counts = radial_profile_3d(group_mass_hosts_pos, low_mass_halos_pos, low_mass_halos['halo_dmvir_dt_tdyn'],
                           rbins_absolute = rbins_absolute,
                           period=halocat.Lbox, return_counts = True)

shell_volumes = 4*np.pi*(rbins_midpoints**2)*np.diff(rbins_absolute)
mean_number_density = (counts/float(len(group_mass_hosts)))/shell_volumes
plt.plot(rbins_midpoints, mean_number_density, color='blue')

plt.xlabel(r'$r$  $[Mpc]$', fontsize=20)
plt.ylabel(r'$\langle dN/dV \rangle$  $[M_{\odot}/{\rm Mpc^{3}}]$', fontsize=20)
plt.xticks(size=15); plt.yticks(size=15)
../../../../../_images/radial_profile_halocat_tutorial_fig2.png