Quick Start

This guide shows the basic usage of gwGenealogy for BBH population studies and hierarchical merger simulations.

Sample a BBH population

from gwGenealogy.binaries import BBHs, BBHRemnant, sample_masses, sample_spins
import numpy as np

# Power-law primary masses, isotropic Beta-distributed spins
m1, m2 = sample_masses(5000, m_min=5, m_max=50,
                        m1_distribution='powerlaw', alpha=-2.35, seed=42)
chi1, chi2 = sample_spins(5000, chi_max=1.0, spin_magnitude='beta',
                           spin_angles='isotropic', seed=43)

# Build BBH container from Cartesian spin vectors
a1 = np.linalg.norm(chi1, axis=1)
a2 = np.linalg.norm(chi2, axis=1)
bbh = BBHs(m1=m1, m2=m2, a1=a1, a2=a2,
           theta1=np.arccos(chi1[:, 2] / a1),
           theta2=np.arccos(chi2[:, 2] / a2),
           phi1=np.arctan2(chi1[:, 1], chi1[:, 0]) % (2 * np.pi),
           phi2=np.arctan2(chi2[:, 1], chi2[:, 0]) % (2 * np.pi))
print(bbh)

Compute remnant properties

# Precessing remnants (default: HBR mass/spin + gwmodel kick)
rem = BBHRemnant(bbh=bbh, precessing=True)
print(rem)
# rem.Mf — remnant mass
# rem.af — remnant spin
# rem.vkick — kick velocity [km/s]

# Nonprecessing remnants
rem_np = BBHRemnant(bbh=bbh, precessing=False)

Hierarchical mergers

from gwGenealogy.core import HierarchicalMergersInClusterPopulation

sim = HierarchicalMergersInClusterPopulation(
    n_samples=50000, chi_max=0.2, m_min=3, m_max=60,
    kick_model='gwmodel', seed=42)
data = sim.simulate(verbose=True)

# Paper-style mass-spin scatter + mass histogram
fig, axes = sim.plot_generations(data)

BH seed growth

from gwGenealogy.core import MonteCarloBHSeedGrowth

mc = MonteCarloBHSeedGrowth(
    v_esc=300, Z=0.005, chi_max=0.2, m_seed=10.0,
    m_targets=[250], kick_model='gwmodel', seed=42)
result = mc.simulate(n_experiments=10000, verbose=True)
print(f"P_ret = {result['P_ret']:.3f}")

# Sweep over escape velocities
import numpy as np
grid = mc.simulate_grid(np.linspace(50, 500, 20), n_experiments=1000)

Retention probability grids

from gwGenealogy.core import BBHRetentionProbabilityOverChiq
import numpy as np

grid = BBHRetentionProbabilityOverChiq(
    q_values=np.linspace(1, 10, 50),
    chi_max_values=np.linspace(0.01, 1, 50),
    v_esc_values=[50, 100, 200, 500],
    kick_models=['hlz', 'gwmodel'], seed=42)
grid.compute(verbose=True)
fig, axes = grid.plot_heatmap_all_vesc()

GWTC population sampling

from gwGenealogy.binaries import sample_gwtc_population, available_catalogs

available_catalogs()
pop = sample_gwtc_population(50000, catalog='gwtc5',
                              source='posterior', seed=42)
# pop['mass_1'], pop['a_1'], pop['chi_eff'], pop['redshift'], ...

Host environment retention

from gwGenealogy.hosts import (compute_multi_environment_retention,
                                ENVIRONMENTS, PlummerCluster)

# Environment-marginalised retention
ret = compute_multi_environment_retention(v_kick_array)
# ret['GC'], ret['NSC'], ret['EG'], ret['DG'], ret['MW'], ret['M31']

# Plummer cluster model
cluster = PlummerCluster(Mcl=1e6, r_h=3.0, cluster_type='GC')
cluster.merger_analysis(v_kick, M_bh=30.0)
print(f"P_ret = {cluster.P_ret:.3f}")