CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-gwpy

A python package for gravitational-wave astrophysics

Pending
Overview
Eval results
Files

astrophysics.mddocs/

Astrophysics Calculations

Sensitivity and detection range calculations for gravitational-wave sources including binary inspirals and burst sources. These functions provide essential tools for understanding detector performance and planning astrophysical searches.

Capabilities

Range Calculations

Functions for calculating detection ranges for different types of gravitational-wave sources.

from gwpy.astro import (inspiral_range, inspiral_range_psd, 
                       burst_range, burst_range_spectrum,
                       sensemon_range, sensemon_range_psd,
                       range_timeseries, range_spectrogram)

def inspiral_range(psd, mass1=1.4, mass2=1.4, **kwargs):
    """
    Calculate binary inspiral detection range.
    
    Parameters:
    - psd: FrequencySeries, detector power spectral density
    - mass1: float, primary mass in solar masses  
    - mass2: float, secondary mass in solar masses
    - snr: float, signal-to-noise ratio threshold (default: 8)
    - fmin: float, minimum frequency for integration (Hz)
    - fmax: float, maximum frequency for integration (Hz)
    
    Returns:
    Quantity, detection range in Mpc
    """

def inspiral_range_psd(psd, **kwargs):
    """
    Calculate inspiral range from PSD (alias for inspiral_range).
    
    Parameters:
    - psd: FrequencySeries, power spectral density
    - **kwargs: additional parameters for inspiral_range
    
    Returns:
    Quantity, detection range in Mpc
    """

def burst_range(spectrum, **kwargs):
    """
    Calculate burst detection range.
    
    Parameters:
    - spectrum: FrequencySeries, amplitude spectral density
    - energy: float, gravitational-wave energy (solar masses × c²)
    - frequency: float, characteristic frequency (Hz)
    - snr: float, SNR threshold (default: 8)
    
    Returns:
    Quantity, detection range in Mpc
    """

def burst_range_spectrum(spectrum, **kwargs):
    """
    Calculate burst range from spectrum (alias for burst_range).
    
    Parameters:
    - spectrum: FrequencySeries, amplitude spectral density  
    - **kwargs: additional parameters for burst_range
    
    Returns:
    Quantity, detection range in Mpc
    """

def sensemon_range(psd, **kwargs):
    """
    Calculate SenseMon-style detection range.
    
    Parameters:
    - psd: FrequencySeries, power spectral density
    - mass1: float, primary mass (default: 1.4 solar masses)
    - mass2: float, secondary mass (default: 1.4 solar masses)
    
    Returns:
    Quantity, SenseMon range in Mpc
    """

def sensemon_range_psd(psd, **kwargs):
    """
    Calculate SenseMon range from PSD (alias for sensemon_range).
    
    Parameters:
    - psd: FrequencySeries, power spectral density
    - **kwargs: additional parameters for sensemon_range
    
    Returns:  
    Quantity, SenseMon range in Mpc
    """

def range_timeseries(timeseries, stride, **kwargs):
    """
    Calculate detection range as a time series.
    
    Parameters:
    - timeseries: TimeSeries, input strain data
    - stride: float, time between range calculations (seconds)
    - fftlength: float, FFT length for PSD calculation
    - overlap: float, overlap between FFT segments
    - **kwargs: additional range calculation parameters
    
    Returns:
    TimeSeries, detection range over time
    """

def range_spectrogram(timeseries, stride, **kwargs):
    """
    Calculate range as a time-frequency spectrogram.
    
    Parameters:
    - timeseries: TimeSeries, input strain data
    - stride: float, time between calculations
    - **kwargs: additional parameters
    
    Returns:
    Spectrogram, range evolution over time and frequency
    """

Usage Examples

Basic Range Calculations

from gwpy.timeseries import TimeSeries
from gwpy.astro import inspiral_range, burst_range

# Read strain data and calculate PSD
strain = TimeSeries.fetch_open_data('H1', 1126259446, 1126259478)
psd = strain.psd(fftlength=4, overlap=2)

# Calculate binary neutron star inspiral range
bns_range = inspiral_range(psd, mass1=1.4, mass2=1.4, snr=8)
print(f"BNS inspiral range: {bns_range:.1f}")

# Calculate black hole binary range  
bbh_range = inspiral_range(psd, mass1=30, mass2=30, snr=8)
print(f"BBH inspiral range: {bbh_range:.1f}")

# Calculate burst range for supernova-like sources
asd = psd.sqrt()
sn_range = burst_range(asd, energy=1e-8, frequency=235, snr=8)  
print(f"Supernova burst range: {sn_range:.1f}")

Range Time Series Analysis

from gwpy.astro import range_timeseries
import matplotlib.pyplot as plt

# Calculate range evolution over time
range_ts = range_timeseries(strain, 
                           stride=60,        # 1-minute intervals
                           fftlength=8,      # 8-second FFTs
                           overlap=4,        # 4-second overlap
                           mass1=1.4,        # BNS masses
                           mass2=1.4)

# Plot range time series
plot = range_ts.plot(figsize=(12, 6))
plot.set_ylabel('Inspiral Range [Mpc]')
plot.set_title('H1 BNS Inspiral Range Evolution')
plot.show()

# Calculate statistics
print(f"Mean range: {range_ts.mean():.1f}")
print(f"Range std: {range_ts.std():.1f}")
print(f"Best range: {range_ts.max():.1f}")
print(f"Worst range: {range_ts.min():.1f}")

Multi-Detector Range Comparison

from gwpy.timeseries import TimeSeriesDict
from gwpy.astro import inspiral_range

# Get data from multiple detectors
data = TimeSeriesDict.fetch_open_data(['H1', 'L1'], 
                                     start=1126259446, 
                                     end=1126259478)

# Calculate PSDs and ranges
ranges = {}
psds = {}

for ifo, timeseries in data.items():
    psds[ifo] = timeseries.psd(fftlength=4, overlap=2)
    ranges[ifo] = inspiral_range(psds[ifo], mass1=1.4, mass2=1.4)

# Compare detector performance
print("BNS Inspiral Ranges:")
for ifo, r in ranges.items():
    print(f"  {ifo}: {r:.1f}")

# Plot PSD comparison
plot = psds['H1'].plot(label='H1', alpha=0.8)
plot.add_frequencyseries(psds['L1'], label='L1', alpha=0.8)
plot.set_xlabel('Frequency [Hz]')
plot.set_ylabel('PSD [strain²/Hz]')
plot.set_xlim(10, 2000)
plot.set_yscale('log')
plot.legend()
plot.title('Detector Sensitivity Comparison')
plot.show()

Mass-Dependent Range Analysis

import numpy as np

# Calculate range for different binary masses
masses = np.logspace(0, 2, 20)  # 1 to 100 solar masses
bns_ranges = []
bbh_ranges = []

for mass in masses:
    # Equal mass binaries
    bns_range = inspiral_range(psd, mass1=mass, mass2=mass, snr=8)
    bns_ranges.append(bns_range.value)

# Convert to arrays for plotting
ranges_array = np.array(bns_ranges)

# Plot mass-range relationship
plt.figure(figsize=(10, 6))
plt.loglog(masses, ranges_array, 'b-', linewidth=2, 
          label='Equal mass binaries')
plt.xlabel('Component Mass [M☉]')
plt.ylabel('Inspiral Range [Mpc]')
plt.title('Detection Range vs Binary Mass')
plt.grid(True, alpha=0.3)
plt.legend()

# Mark interesting mass ranges
plt.axvspan(1, 3, alpha=0.2, color='green', label='Neutron Stars')
plt.axvspan(5, 50, alpha=0.2, color='red', label='Stellar Black Holes')
plt.legend()
plt.show()

# Find optimal masses
max_idx = np.argmax(ranges_array)
print(f"Best range: {ranges_array[max_idx]:.1f} Mpc at {masses[max_idx]:.1f} M☉")

SenseMon Range Monitoring

from gwpy.astro import sensemon_range

# Calculate SenseMon range (standard monitoring metric)
sensemon_bns = sensemon_range(psd, mass1=1.4, mass2=1.4)
print(f"SenseMon BNS range: {sensemon_bns:.1f}")

# Compare with standard inspiral range
standard_bns = inspiral_range(psd, mass1=1.4, mass2=1.4)
print(f"Standard BNS range: {standard_bns:.1f}")
print(f"Ratio: {sensemon_bns/standard_bns:.2f}")

# Calculate range time series for monitoring
sensemon_ts = range_timeseries(strain, 
                              stride=300,  # 5-minute intervals
                              fftlength=32, # Long FFTs for stability
                              range_func=sensemon_range,
                              mass1=1.4, mass2=1.4)

# Plot for monitoring dashboard
plot = sensemon_ts.plot(figsize=(12, 4))
plot.set_ylabel('SenseMon Range [Mpc]')
plot.set_title('H1 Detector Range Monitor')
plot.axhline(sensemon_ts.mean().value, color='red', linestyle='--', 
            label=f'Mean: {sensemon_ts.mean():.1f}')
plot.legend()
plot.show()

Burst Source Range Analysis

# Calculate ranges for different burst sources
burst_sources = {
    'Core Collapse Supernova': {'energy': 1e-8, 'frequency': 235},
    'Stellar Collapse': {'energy': 1e-7, 'frequency': 150},
    'Cosmic String Cusp': {'energy': 1e-9, 'frequency': 300}
}

asd = psd.sqrt()
burst_ranges = {}

print("Burst Source Detection Ranges:")
for source, params in burst_sources.items():
    range_val = burst_range(asd, 
                          energy=params['energy'],
                          frequency=params['frequency'],
                          snr=8)
    burst_ranges[source] = range_val
    print(f"  {source}: {range_val:.1f}")

# Plot burst ranges vs frequency
frequencies = [params['frequency'] for params in burst_sources.values()]
ranges = [burst_ranges[source].value for source in burst_sources.keys()]

plt.figure(figsize=(10, 6))
plt.scatter(frequencies, ranges, s=100, alpha=0.7)
for i, source in enumerate(burst_sources.keys()):
    plt.annotate(source, (frequencies[i], ranges[i]), 
                xytext=(10, 10), textcoords='offset points')

plt.xlabel('Characteristic Frequency [Hz]')
plt.ylabel('Detection Range [Mpc]')
plt.title('Burst Source Detection Ranges')
plt.grid(True, alpha=0.3)
plt.show()

Range Spectrogram Analysis

from gwpy.astro import range_spectrogram

# Calculate range evolution in time-frequency
# (Note: This is a conceptual example - actual implementation may vary)
long_strain = TimeSeries.fetch_open_data('H1', 1126259446, 1126259446+3600)

# Create range spectrogram
range_spec = range_spectrogram(long_strain,
                              stride=60,     # 1-minute time bins
                              fftlength=8,   # 8-second FFTs
                              mass1=1.4, mass2=1.4)

# Plot range evolution
plot = range_spec.plot(figsize=(12, 8))
plot.set_xlabel('Time')
plot.set_ylabel('Frequency [Hz]')
plot.set_title('BNS Range Evolution')
plot.colorbar(label='Range [Mpc]')
plot.show()

Horizon Distance Calculations

# Calculate horizon distances (maximum possible range)
def horizon_distance(psd, mass1=1.4, mass2=1.4, **kwargs):
    """Calculate horizon distance (SNR=1 threshold)."""
    return inspiral_range(psd, mass1=mass1, mass2=mass2, snr=1, **kwargs)

# Calculate horizons for different mass combinations
mass_combinations = [
    (1.4, 1.4),   # BNS
    (10, 10),     # Light BBH  
    (30, 30),     # Heavy BBH
    (1.4, 10),    # NSBH
]

print("Horizon Distances (SNR=1):")
for m1, m2 in mass_combinations:
    horizon = horizon_distance(psd, mass1=m1, mass2=m2)
    detection_range = inspiral_range(psd, mass1=m1, mass2=m2, snr=8)
    
    print(f"  {m1:.1f}-{m2:.1f} M☉:")
    print(f"    Horizon: {horizon:.1f}")
    print(f"    Range (SNR=8): {detection_range:.1f}")
    print(f"    Ratio: {detection_range/horizon:.2f}")

Network Range Analysis

# Calculate network detection capabilities
def network_range(ranges):
    """Estimate network detection range from individual detector ranges."""
    # Simple approximation: geometric mean for coincident detection
    if len(ranges) == 2:
        return np.sqrt(ranges[0] * ranges[1])
    elif len(ranges) == 3:
        return (ranges[0] * ranges[1] * ranges[2])**(1/3)
    else:
        return np.mean(ranges)

# Multi-detector network analysis
detectors = ['H1', 'L1', 'V1']
individual_ranges = [150, 140, 90]  # Example ranges in Mpc

network_bns_range = network_range(individual_ranges)

print("Network Analysis:")
for ifo, r in zip(detectors, individual_ranges):
    print(f"  {ifo} range: {r} Mpc")
print(f"  Network range: {network_bns_range:.1f} Mpc")

# Calculate detection volume (proportional to range³)
volumes = [r**3 for r in individual_ranges]
network_volume = network_bns_range**3

print("\nDetection Volumes (relative):")
for ifo, v in zip(detectors, volumes):
    print(f"  {ifo}: {v/1000:.1f} (×10³)")
print(f"  Network: {network_volume/1000:.1f} (×10³)")

Install with Tessl CLI

npx tessl i tessl/pypi-gwpy

docs

astrophysics.md

detector.md

frequencyseries.md

index.md

plotting.md

segments.md

signal-processing.md

spectrogram.md

timeseries.md

tile.json