Gravitational-wave Detectors

The pycbc.detector module provides the pycbc.detector.Detector class to access information about gravitational wave detectors and key information about how their orientation and position affects their view of a source

Detector Locations

from pycbc.detector import Detector, get_available_detectors

# We can list the available detectors. This gives their detector abbreviation
# along with a longer name. Note that some of these are not physical detectors
# but may be useful for testing or study purposes

for abv in get_available_detectors():
    d = Detector(abv)

    # Note that units are all in radians
    print("{} Latitude {} Longitude {}".format(abv,
                                                  d.latitude,
                                                  d.longitude))
$ python ../examples/detector/loc.py
T1 Latitude 0.6226733601781139 Longitude 2.435363594690946
V0 Latitude 0.7615118398044829 Longitude 0.1833380521285067
V1 Latitude 0.7615118398044829 Longitude 0.1833380521285067
G1 Latitude 0.9118498274833728 Longitude 0.17116780434996115
H2 Latitude 0.810795263791696 Longitude -2.08405676916594
H1 Latitude 0.810795263791696 Longitude -2.08405676916594
L1 Latitude 0.5334231350225018 Longitude -1.5843093707829257
I1 Latitude 0.24841853018214574 Longitude 1.3340133249409996
C1 Latitude 0.5963790053711457 Longitude -2.061757445380561
E1 Latitude 0.7615118398044829 Longitude 0.1833380521285067
E2 Latitude 0.7629930799052169 Longitude 0.18405858870223898
E3 Latitude 0.7627046325725655 Longitude 0.18192996730110464
E0 Latitude 0.7627046325725655 Longitude 0.18192996730110464
K1 Latitude 0.6355068496865413 Longitude 2.396441015339088
U1 Latitude 1.5707963267948966 Longitude 0.0
A1 Latitude 0.5307987920242562 Longitude -1.5913706849572118
O1 Latitude 0.7915649933828905 Longitude 0.2085377567924929
X1 Latitude 0.8107054375131121 Longitude 0.10821041362369214
N1 Latitude 0.7299645670603321 Longitude 0.2211768494583896
B1 Latitude -0.5573418077694188 Longitude 2.0213821620185053

Light travel time between detectors

from pycbc.detector import Detector

for ifo1 in ['H1', 'L1', 'V1']:
    for ifo2 in ['H1', 'L1', 'V1']:
        dt = Detector(ifo1).light_travel_time_to_detector(Detector(ifo2))
        print("Direct Time from {} to {} is {} seconds".format(ifo1, ifo2, dt))
$ python ../examples/detector/travel.py
Direct Time from H1 to H1 is 0.0 seconds
Direct Time from H1 to L1 is 0.010012846152223925 seconds
Direct Time from H1 to V1 is 0.027287979933844225 seconds
Direct Time from L1 to H1 is 0.010012846152223925 seconds
Direct Time from L1 to L1 is 0.0 seconds
Direct Time from L1 to V1 is 0.026448341016726495 seconds
Direct Time from V1 to H1 is 0.027287979933844225 seconds
Direct Time from V1 to L1 is 0.026448341016726495 seconds
Direct Time from V1 to V1 is 0.0 seconds

Time source gravitational-wave passes through detector

from pycbc.detector import Detector
from astropy.utils import iers

# Make sure the documentation can be built without an internet connection
iers.conf.auto_download = False

# The source of the gravitational waves
right_ascension = 0.7
declination = -0.5

# Reference location will be the Hanford detector
# see the `time_delay_from_earth_center` method to use use geocentric time
# as the reference
dref = Detector("H1")

# Time in GPS seconds that the GW passes
time = 100000000

# Time that the GW will (or has) passed through the given detector
for ifo in ["H1", "L1", "V1"]:
    d = Detector(ifo)
    dt = d.time_delay_from_detector(dref, right_ascension, declination, time)
    st = "GW passed through {} {} seconds relative to passing by Hanford"
    print(st.format(ifo, dt))
$ python ../examples/detector/delay.py
GW passed through H1 0.0 seconds relative to passing by Hanford
GW passed through L1 0.0024441643159689237 seconds relative to passing by Hanford
GW passed through V1 -0.014733669722672925 seconds relative to passing by Hanford

Antenna Patterns and Projecting a Signal into the Detector Frame

from pycbc.detector import Detector
from pycbc.waveform import get_td_waveform

# Time, orientation and location of the source in the sky
ra = 1.7
dec = 1.7
pol = 0.2
inc = 0
time = 1000000000

# We can calcualate the antenna pattern for Hanford at
# the specific sky location
d = Detector("H1")

# We get back the fp and fc antenna pattern weights.
fp, fc = d.antenna_pattern(ra, dec, pol, time)
print("fp={}, fc={}".format(fp, fc))

# These factors allow us to project a signal into what the detector would
# observe

## Generate a waveform
hp, hc = get_td_waveform(approximant="IMRPhenomD", mass1=10, mass2=10,
                         f_lower=30, delta_t=1.0/4096, inclination=inc,
                         distance=400)

## Apply the factors to get the detector frame strain
ht = fp * hp + fc * hc


# The projection process can also take into account the rotation of the
# earth using the project wave function.
hp.start_time = hc.start_time = time
ht2 = d.project_wave(hp, hc, ra, dec, pol)
$ python ../examples/detector/ant.py
No CuPy
No CuPy or GPU PhenomHM module.
No CuPy or GPU response available.
No CuPy or GPU interpolation available.
fp=-0.38548547608335076, fc=0.7059872046149982

Adding a custom detector / overriding existing ones

PyCBC supports observatories with arbitrary locations. For the study of possible new observatories you can add them explicitly within a script or by means of a config file to make the detectors visible to all codes that use the PyCBC detector interfaces.

An example of adding a detector directly within a script.

import matplotlib.pyplot as plt
from pycbc.detector import add_detector_on_earth, Detector
import pycbc.psd
import numpy as np

# Set up potential Cosmic Explorer detector locations

# 40 km detector
lon = -125 / 180.0 * np.pi
lat = 46 / 180.0 * np.pi
yangle = 100.0 / 180.0 * np.pi 
# yangle is the rotation clockwise from pointing north at 0
# xangle can also be specified and allows for detectors that don't have
# 90 degree opening between arms. By default we assume xangle is yangle + pi/2
add_detector_on_earth("C4", lon, lat, yangle=yangle,
                      xlength=40000, ylength=40000)

# 20 km detector
# Arm length is optional, but if provided, you can accurately calcuale
# high-frequency corrects if you provide a frequency argument to the
# antenna pattern method
lon = -94 / 180.0 * np.pi
lat = 29 / 180.0 * np.pi
yangle = 160.0 / 180.0 * np.pi
add_detector_on_earth("C2", lon, lat, yangle=yangle,
                      xlength=20000, ylength=20000)

ra, dec = np.meshgrid(np.arange(0, np.pi*2.0, .1), 
                      np.arange(-np.pi / 2.0, np.pi / 2.0, .1))
ra = ra.flatten()
dec = dec.flatten()

pol = 0
time = 1e10 + 8000 # A time when ra ~ lines up with lat/lon coordinates

for d in [Detector("C4"), Detector("C2")]:
    fp, fc = d.antenna_pattern(ra, dec, pol, time)

    plt.figure()
    plt.subplot(111, projection="mollweide")
    ra[ra>np.pi] -= np.pi * 2.0
    plt.scatter(ra, dec, c=fp**2.0 + fc**2.0)
    plt.title("Mollweide")
    plt.grid(True)
    plt.show()

(Source code)

_images/custom_00.png

(png, hires.png, pdf)

_images/custom_01.png

(png, hires.png, pdf)

The following demonstrates a config file which similarly can provide custom observatory information. The options are the same as for the direct function calls. To tell PyCBC the location of the config file, set the PYCBC_DETECTOR_CONFIG variable to the location of the file e.g. PYCBC_DETECTOR_CONFIG=/some/path/to/detectors.ini. The following would provide new detectors ‘f1’ and ‘f2’.

[detector-f1]
method = earth_normal
latitude = 0.7120943348136864
longitude = -1.9861846887695471
yangle = 2.0943951023931953

[detector-f2]
method = earth_normal
latitude = -0.5497787143782138
longitude = -2.0594885173533086
yangle = 2.5830872929516078