Stellar Evolution and 1G BH Masses

[1]:
import warnings
warnings.filterwarnings('ignore', 'Wswiglal-redir-stdio')

from gwGenealogy.stellar import (
    IMF_kroupa, sample_kroupa_masses,
    sample_zams_masses, evolve_stars,
    sample_1g_bh_masses_from_stellar_collapse,
    sample_maxwellian_kick,
)
from gwGenealogy.utils import set_rcparams
import numpy as np
import matplotlib.pyplot as plt

set_rcparams()
lal.MSUN_SI != Msun

Kroupa IMF

[2]:
m = np.logspace(-1.5, 2.5, 5000)
imf_values = IMF_kroupa(m)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Analytic IMF
axes[0].loglog(m, imf_values, lw=2)
axes[0].set_xlabel(r'$M\;[M_\odot]$')
axes[0].set_ylabel(r'$\xi(M)$')
axes[0].set_title('Kroupa IMF')

# Sampled masses
sampled_masses = sample_kroupa_masses(10000, m_min=0.08, m_max=150, seed=42)
axes[1].hist(sampled_masses, bins=100, density=True, histtype='step', lw=2, label='samples')
axes[1].set_xlabel(r'$M\;[M_\odot]$')
axes[1].set_ylabel('Density')
axes[1].set_xscale('log')
axes[1].set_yscale('log')
axes[1].set_title('Kroupa IMF samples (N=10000)')
axes[1].legend()

plt.tight_layout()
plt.show()
../_images/notebooks_02_stellar_evolution_3_0.png

Stellar evolution: ZAMS to BH remnant

[3]:
m_zams = sample_zams_masses(5000, m_zams_min=10, m_zams_max=150, imf='kroupa', seed=42)

m_rem_lowZ  = evolve_stars(m_zams, Z=0.0002, model='Fryer12_delayed')
m_rem_highZ = evolve_stars(m_zams, Z=0.02,   model='Fryer12_delayed')

fig, ax = plt.subplots(figsize=(8, 6))
ax.scatter(m_zams, m_rem_lowZ,  s=2, alpha=0.4, label='Z = 0.0002')
ax.scatter(m_zams, m_rem_highZ, s=2, alpha=0.4, label='Z = 0.02')
ax.plot([10, 150], [10, 150], 'k--', alpha=0.3, label='1:1')
ax.set_xlabel(r'$M_{\rm ZAMS}\;[M_\odot]$')
ax.set_ylabel(r'$M_{\rm rem}\;[M_\odot]$')
ax.set_title('Fryer12 delayed: ZAMS to BH remnant')
ax.legend()
plt.tight_layout()
plt.show()
../_images/notebooks_02_stellar_evolution_5_0.png

SEVN delayed model

[4]:
m_rem_sevn = evolve_stars(m_zams, Z=0.005, model='SEVN_delayed')
m_rem_f12  = evolve_stars(m_zams, Z=0.005, model='Fryer12_delayed')

fig, ax = plt.subplots(figsize=(8, 6))
ax.scatter(m_zams, m_rem_f12,  s=2, alpha=0.4, label='Fryer12 delayed')
ax.scatter(m_zams, m_rem_sevn, s=2, alpha=0.4, label='SEVN delayed')
ax.plot([10, 150], [10, 150], 'k--', alpha=0.3, label='1:1')
ax.set_xlabel(r'$M_{\rm ZAMS}\;[M_\odot]$')
ax.set_ylabel(r'$M_{\rm rem}\;[M_\odot]$')
ax.set_title('Comparison at Z = 0.005')
ax.legend()
plt.tight_layout()
plt.show()
../_images/notebooks_02_stellar_evolution_7_0.png

1G BH mass pool

[5]:
bh_masses = sample_1g_bh_masses_from_stellar_collapse(
    10000, Z=0.005, imf='kroupa', m_zams_min=10, m_zams_max=150, seed=42
)

fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(bh_masses, bins=50, density=True, histtype='step', lw=2)
ax.set_xlabel(r'$M_{\rm BH}\;[M_\odot]$')
ax.set_ylabel('Density')
ax.set_title('1G BH mass distribution (Z=0.005, Kroupa IMF)')
plt.tight_layout()
plt.show()

print(f"Median BH mass: {np.median(bh_masses):.2f} Msun")
print(f"Min BH mass:    {np.min(bh_masses):.2f} Msun")
print(f"Max BH mass:    {np.max(bh_masses):.2f} Msun")
../_images/notebooks_02_stellar_evolution_9_0.png
Median BH mass: 11.06 Msun
Min BH mass:    0.01 Msun
Max BH mass:    38.30 Msun

Natal kicks

[6]:
kicks = sample_maxwellian_kick(sigma=265, n_samples=5000, seed=42)

fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(kicks, bins=50, density=True, histtype='step', lw=2)
ax.set_xlabel('Kick velocity [km/s]')
ax.set_ylabel('Density')
ax.set_title(r'Maxwellian natal kick ($\sigma = 265$ km/s)')
plt.tight_layout()
plt.show()

print(f"Mean kick: {np.mean(kicks):.1f} km/s")
../_images/notebooks_02_stellar_evolution_11_0.png
Mean kick: 423.6 km/s
[ ]: