*********** Simulations *********** This document describes the simulation tools available in *astropath* for generating synthetic FRB populations and assigning them to host galaxies. Overview ======== The ``astropath.simulations`` module provides two main capabilities: 1. **FRB Generation** (``generate_frbs``) -- Create simulated FRB populations 2. **Host Assignment** (``assign_frbs_to_hosts``) -- Assign FRBs to galaxies These tools are useful for: * Testing PATH analysis pipelines * Validating host association methods * Estimating detection efficiencies * Studying selection effects and biases * Planning observations The primary function is ``generate_frbs()`` which produces FRBs with the following properties: * **DM**: Extragalactic dispersion measure (pc/cm\ :sup:`3`) * **z**: Redshift * **M_r**: Host galaxy absolute r-band magnitude * **m_r**: Host galaxy apparent r-band magnitude FRB populations can then be assigned to host galaxies from a catalog using ``assign_frbs_to_hosts()``, which produces realistic associations with localization errors. See :doc:`assign_host` for full details on host assignment. Supported Surveys ================= The module supports generation of FRBs for the following surveys: * **CHIME**: Canadian Hydrogen Intensity Mapping Experiment * **DSA**: Deep Synoptic Array (DSA-110) * **ASKAP**: Australian SKA Pathfinder (CRAFT surveys) * **CRAFT**: Alias for ASKAP * **CRAFT_ICS_1300**: CRAFT Incoherent Sum at 1300 MHz * **CRAFT_ICS_892**: CRAFT Incoherent Sum at 892 MHz * **CRAFT_ICS_1632**: CRAFT Incoherent Sum at 1632 MHz * **Parkes**: Parkes Multibeam * **FAST**: Five-hundred-meter Aperture Spherical Telescope Each survey has a unique P(z,DM) grid that captures the selection effects and sensitivity of that instrument. Basic Usage =========== The simplest usage generates FRBs by sampling directly from the survey-specific P(z,DM) grid:: from astropath.simulations import generate_frbs # Generate 1000 CHIME FRBs df = generate_frbs(1000, 'CHIME', seed=42) # View the results print(df.head()) # DM z M_r m_r # 0 234.0000 0.150000 -19.85432 18.234521 # 1 567.0000 0.420000 -21.23456 21.876543 # ... The output is a pandas DataFrame with columns for DM, z, M_r, and m_r. Generating for Different Surveys ================================ Simply change the survey name to generate FRBs with different selection functions:: # DSA-110 FRBs df_dsa = generate_frbs(1000, 'DSA', seed=42) # ASKAP/CRAFT FRBs df_askap = generate_frbs(1000, 'ASKAP', seed=42) # Compare redshift distributions print(f"CHIME median z: {df['z'].median():.3f}") print(f"DSA median z: {df_dsa['z'].median():.3f}") print(f"ASKAP median z: {df_askap['z'].median():.3f}") Using an Observed DM Catalog ============================ If you have observed DM values from a specific sample, you can use them to constrain the DM distribution via kernel density estimation:: import numpy as np # Example: DMs from a specific observing campaign observed_dms = np.array([362.4, 589.0, 364.5, 339.5, 322.2, 594.6]) # Generate FRBs with DMs sampled from a KDE of the observed values df = generate_frbs(1000, 'DSA', dm_catalog=observed_dms, seed=42) This fits a Gaussian KDE to the provided DM values and samples new DMs from that distribution, then draws redshifts from the P(z|DM) grid. Reproducibility =============== Use the ``seed`` parameter for reproducible results:: df1 = generate_frbs(100, 'CHIME', seed=42) df2 = generate_frbs(100, 'CHIME', seed=42) # These will be identical assert (df1['DM'] == df2['DM']).all() Custom Cosmology ================ By default, simulations use Planck18 cosmology. You can specify a different cosmology:: from astropy.cosmology import WMAP9 df = generate_frbs(1000, 'CHIME', cosmo=WMAP9, seed=42) Algorithm Details ================= The ``generate_frbs()`` function follows these steps: 1. **Load P(z,DM) Grid**: Survey-specific grids from the ``frb`` package capture the probability of detecting an FRB at redshift z with dispersion measure DM, accounting for telescope sensitivity and selection effects. 2. **Sample DM Values**: Either directly from the P(DM) marginal distribution of the grid, or from a KDE fit to user-provided catalog values. 3. **Sample Redshifts**: For each DM, sample z from P(z|DM) using inverse transform sampling on the cumulative distribution. 4. **Sample Host M_r**: Draw absolute magnitudes from the observed FRB host galaxy luminosity function (loaded from ``frb.galaxies.hosts.load_Mr_pdf()``). 5. **Compute Apparent Magnitudes**: Calculate m_r = M_r + distance_modulus(z) using the specified cosmology. Dependencies ============ The simulations module requires the ``frb`` package to be installed:: pip install frb This provides access to: * P(z,DM) grids for various surveys (``frb.dm.prob_dmz``) * Host galaxy magnitude distributions (``frb.galaxies.hosts``) API Reference ============= generate_frbs() --------------- ``generate_frbs(n_frbs, survey, dm_catalog=None, cosmo=None, seed=None)`` Generate a population of simulated FRBs. **Parameters:** * **n_frbs** (*int*) -- Number of FRBs to generate * **survey** (*str*) -- Survey name (e.g., 'CHIME', 'DSA', 'ASKAP') * **dm_catalog** (*np.ndarray, optional*) -- Optional array of observed DMs to sample from * **cosmo** (*astropy.cosmology, optional*) -- Cosmology for distance calculations (default: Planck18) * **seed** (*int, optional*) -- Random seed for reproducibility **Returns:** * **pandas.DataFrame** -- DataFrame with columns 'DM', 'z', 'M_r', 'm_r' **Raises:** * **ValueError** -- If survey is not recognized SURVEY_GRIDS ------------ ``SURVEY_GRIDS`` Dictionary mapping survey names to their P(z,DM) grid filenames. Available surveys: CHIME, DSA, ASKAP, CRAFT, CRAFT_ICS_1300, CRAFT_ICS_892, CRAFT_ICS_1632, Parkes, FAST. Example: Full Workflow ====================== Here is a complete example generating FRBs and examining their properties:: import matplotlib.pyplot as plt from astropath.simulations import generate_frbs # Generate FRBs for three surveys surveys = ['CHIME', 'DSA', 'ASKAP'] dfs = {s: generate_frbs(5000, s, seed=42) for s in surveys} # Plot redshift distributions fig, axes = plt.subplots(1, 3, figsize=(12, 4)) for ax, survey in zip(axes, surveys): df = dfs[survey] ax.hist(df['z'], bins=30, alpha=0.7, density=True) ax.set_xlabel('Redshift z') ax.set_ylabel('Density') ax.set_title(f'{survey}: median z = {df["z"].median():.2f}') plt.tight_layout() plt.savefig('frb_redshift_comparison.png') # Print summary statistics for survey in surveys: df = dfs[survey] print(f"\n{survey}:") print(f" DM range: {df['DM'].min():.0f} - {df['DM'].max():.0f} pc/cm^3") print(f" z range: {df['z'].min():.3f} - {df['z'].max():.3f}") print(f" m_r range: {df['m_r'].min():.1f} - {df['m_r'].max():.1f}") Next Steps ========== After generating FRBs, you can assign them to host galaxies using the host assignment tools. See :doc:`assign_host` for complete documentation on: * Assigning FRBs to galaxies by magnitude matching * Applying realistic localization errors * Generating observed and true FRB coordinates * Analyzing offset distributions For a complete end-to-end simulation workflow, see the example in :doc:`nb/Simulate_Generate_FRBs`.