Exemple #1
0
    def antenna_pattern(self, skypoints, time=None, psi=0):
        """ Compute antenna response of a detector for a list of skypoints

            skypoints: Skypoint object or list of Skypoint objects
            time: time date when to compute the antenna pattern (default: None) 
            psi: optional polarization angle (default: 0)

            This function uses XLALComputeDetAMResponse() that takes equatorial 
            coordinates (RA and dec) and the Greenwich Mean Sidereal Time (GMST) 
            to define the sky location.

            If the skypoints are given in the equatorial coordinate system, 
            the antenna pattern is computed at the provided time if not None, otherwise
            it is computed at the reference time of the coordinate system of 
            the skypoints.

            If the skypoints are given in the geographic coordinate system,
            they are mapped to fiducial equatorial coordinate system with 
            GMST = 0 hr (REFDATE_GMST_ZERO).
        """

        if not isinstance(skypoints, list):
            skypoints = [skypoints]

        f = []
        for p in skypoints:

            assert isinstance(p, Skypoint), "Requires Skypoint objects"
            assert p.coordsystem.is_valid(), "Unsupported coordinate system"

            # XLALComputeDetAMResponse() requires equatorial coordinates.
            # We transform the points with Earth-fixed coordsystem ('geographic')
            # to the fiducial equatorial coordinates system with GMST = 0 hr.

            if p.coordsystem.name == 'geographic':
                assert time is None, \
                    'time cannot be forced for skypoints in the geographic coordinate system'
                p = p.transformed_to(FIDUCIAL_EQUATORIAL_COORDSYS_GMST_ZERO)
                gmst_rad = lal.GreenwichMeanSiderealTime(
                    p.coordsystem.ref_time)
            else:
                gmst_rad = lal.GreenwichMeanSiderealTime(time if time is not None else \
                                                     p.coordsystem.ref_time)

            # XLALComputeDetAMResponse() takes equatorial coordinates (RA and dec)
            # and gmst to define a sky location
            f.append(lal.ComputeDetAMResponse(self.descriptor.response, \
                                      *p.coords(fmt='lonlat',unit='radians'), \
                                      psi, gmst_rad))

        f = numpy.squeeze(numpy.array(f))

        if f.ndim == 1:
            return tuple(f)  # fplus, fcross
        else:
            return f[:, 0], f[:, 1]  # fplus, fcross
Exemple #2
0
def delay_and_amplitude_correct(event, ra, dec):
    # retrieve station metadata

    detector = lal.cached_detector_by_prefix[event.ifo]

    # delay-correct the event to the geocentre

    delay = lal.TimeDelayFromEarthCenter(detector.location, ra, dec,
                                         event.peak)
    event.peak -= delay
    event.period = event.period.shift(-delay)
    try:
        event.ms_peak -= delay
    except AttributeError:
        pass
    try:
        event.ms_period = event.ms_period.shift(-delay)
    except AttributeError:
        pass

    # amplitude-correct the event using the polarization-averaged
    # antenna response

    fp, fc = lal.ComputeDetAMResponse(
        detector.response, ra, dec, 0,
        lal.GreenwichMeanSiderealTime(event.peak))
    mean_response = math.sqrt(fp**2 + fc**2)
    event.amplitude /= mean_response
    event.ms_hrss /= mean_response

    # done

    return event
Exemple #3
0
 def optimal_orientation(self, t_gps):
     """Return the optimal orientation in right ascension and declination
        for a given GPS time.
     """
     ra = self.longitude + (lal.GreenwichMeanSiderealTime(t_gps) % (2*np.pi))
     dec = self.latitude
     return ra, dec
Exemple #4
0
def time_delay_geocentric(detector1, detector2, ra, dec, time):
    """
    Calculate time delay between two detectors in geocentric coordinates based on XLALArrivaTimeDiff in TimeDelay.c
    Parameters
    -------
    detector1: array_like
        Cartesian coordinate vector for the first detector in the geocentric frame
        generated by the Interferometer class as self.vertex.
    detector2: array_like
        Cartesian coordinate vector for the second detector in the geocentric frame.
        To get time delay from Earth center, use detector2 = np.array([0,0,0])
    ra: float
        Right ascension of the source in radians
    dec: float
        Declination of the source in radians
    time: float
        GPS time in the geocentric frame

    Returns
    -------
    float: Time delay between the two detectors in the geocentric frame

    """
    gmst = fmod(lal.GreenwichMeanSiderealTime(time), 2 * np.pi)
    theta, phi = ra_dec_to_theta_phi(ra, dec, gmst)
    omega = np.array([
        np.sin(theta) * np.cos(phi),
        np.sin(theta) * np.sin(phi),
        np.cos(theta)
    ])
    delta_d = detector2 - detector1
    return np.dot(omega, delta_d) / speed_of_light
Exemple #5
0
def magnitude_b(burst):
    gmst = lal.GreenwichMeanSiderealTime(burst.peak)
    fplus, fcross = lal.ComputeDetAMResponse(
        lal.cached_detector_by_prefix[burst.ifo].response,
        SimBurstUtils.MW_CENTER_J2000_RA_RAD,
        SimBurstUtils.MW_CENTER_J2000_DEC_RAD, 0.0, gmst)
    return (burst.ms_hrss**2.0 / (fplus**2.0 + fcross**2.0))**0.5
Exemple #6
0
    def test_antenna_pattern(self):
        vals = zip(self.ra, self.dec, self.pol, self.time)
        for ifo in self.d:
            fp = []
            fc = []
            for ra1, dec1, pol1, time1 in vals:
                gmst = lal.GreenwichMeanSiderealTime(time1)
                fp1, fc1 = tuple(
                    lal.ComputeDetAMResponse(ifo.response, ra1, dec1, pol1,
                                             gmst))
                fp.append(fp1)
                fc.append(fc1)

            fp2, fc2 = ifo.antenna_pattern(self.ra, self.dec, self.pol,
                                           self.time)

            fp = numpy.array(fp)
            fc = numpy.array(fc)

            diff1 = fp - fp2
            diff2 = fc - fc2
            diff = abs(numpy.concatenate([diff1, diff2]))
            tolerance = 1e-4
            print("Max antenna diff:", ifo.name, diff.max())

            self.assertLess(diff.max(), tolerance)
Exemple #7
0
    def test_optimal_orientation(self):
        for d1 in self.d:
            ra, dec = d1.optimal_orientation(self.time[0])
            ra1 = d1.longitude + lal.GreenwichMeanSiderealTime(self.time[0]) % (numpy.pi *2)
            dec1 = d1.latitude

            self.assertAlmostEqual(ra, ra1, 3)
            self.assertAlmostEqual(dec, dec1, 7)
Exemple #8
0
 def antenna_pattern(self, right_ascension, declination, polarization,
                     t_gps):
     """Return the detector response.
     """
     gmst = lal.GreenwichMeanSiderealTime(t_gps)
     return tuple(
         lal.ComputeDetAMResponse(self.response, right_ascension,
                                  declination, polarization, gmst))
Exemple #9
0
def getDetResp(detector, ra, dec, time=0., psi=0.):
    # Set time
    gmst = lal.GreenwichMeanSiderealTime(lal.LIGOTimeGPS(time))
    #Get Detector Responses
    det_response = lal.CachedDetectors[det_index[detector]].response
    # Get Fplus, Fcross
    fplus, fcross = lal.ComputeDetAMResponse(det_response, ra, dec, psi, gmst)
    return fplus, fcross
Exemple #10
0
    def __params_timedelay_and_response_map_call(new_params, ):
        """
        Function used in multi process map call to calculate some parameters required for
        waveform/likelihood evaluation.
        """
        #new_params=bagwacl_lal.cbc_params(new_params_ul)
        for wfm_idx, (pn_param, param_a, run_param) in enumerate(
                zip(pn_params, new_params, run_params)):

            #Note: ordering of parameters is determined here...
            param = bagwacl_lal.cbc_params_sub(*param_a)

            distance = param.distance
            rightascension = param.rightascension
            declination = param.declination
            polarisation = param.polarisation

            # Calculate PN params and init LAL TF2 data struct
            bagwacl_tf2.tf2_pn_params_init(
                pn_param,
                param.phase,
                run_param.deltaF,
                param.mass1 * lal.LAL_MSUN_SI,
                param.mass2 * lal.LAL_MSUN_SI,
                0.,  #param.S1z,
                0.,  #param.S2z,
                run_param.fStart,
                run_param.fEnd,
                distance * lal.LAL_PC_SI * 1e6,
                0.,  #param.lambda1,
                0.,  #param.lambda2,
                run_param.spinO,
                run_param.tidalO,
                run_param.phaseO,
                run_param.amplitudeO,
            )

            # Calculate effect of inclination of binary to LOS
            cfac = np.cos(param.inclination)
            pfac = 0.5 * (1 + cfac * cfac)

            # Calculate LALGPSTime structs from gpstimes doubles for each waveform using LAL
            gpst = lal.LIGOTimeGPS(param.time)
            #...calculate greenwich mean sideral times from gps times using LAL
            gmst = lal.GreenwichMeanSiderealTime(gpst)

            for ifo_idx, (detresp, detloc) in enumerate(run_param.detectors):
                # Calculate detector response functions for each waveform/IFO combo *and* scale det. resp. by distance...
                (fplus, fcross) = ComputeDetAMResponse(detresp, rightascension,
                                                       declination,
                                                       polarisation, gmst)
                ifo_response[2 * (wfm_idx * nIFOs + ifo_idx)] = fplus * pfac
                ifo_response[2 * (wfm_idx * nIFOs + ifo_idx) +
                             1] = fcross * cfac
                # Calculate signal arrival time for each IFO/waveform (relative to geocenter)
                timedelays[wfm_idx * nIFOs +
                           ifo_idx] = TimeDelayFromEarthCenter(
                               detloc, rightascension, declination, gpst)
Exemple #11
0
def gmst_0h(gps):
    """
	Truncate a LIGOTimeGPS to Greenwich mean sidereal 0 rad.
	"""
    gmst = lal.GreenwichMeanSiderealTime(gps)
    residual = gmst % (2.0 * math.pi)
    if residual:
        gmst -= residual
    return lal.GreenwichMeanSiderealTimeToGPS(gmst)
Exemple #12
0
 def __init__(self, Dmax=0, gps=1000000000, params=None):
     """
     Initialize event
     :param Dmax: maximum distance to consider
     :param gmst: greenwich mean sidereal time
     :param params: parameters in form used by first 2 years paper
     """
     if params is not None:
         self.D = params["distance"]
         self.ra = radians(params["RAdeg"])
         self.dec = radians(params["DEdeg"])
         try: self.gps = params['gps']
         except: 
             t = Time(params["MJD"], format='mjd')
             self.gps = lal.LIGOTimeGPS(int(t.gps), int(1e9 * (t.gps % 1)))
         self.gmst = lal.GreenwichMeanSiderealTime(self.gps)
         # self.gmst = float(t.sidereal_time("mean", "greenwich")/units.hourangle)
         self.phi = radians(params["coa-phase"])
         self.psi = radians(params["polarization"])
         self.cosi = cos(radians(params["inclination"]))
         self.mchirp = params["mass1"]**(3./5) * params["mass2"]**(3./5) * (params["mass1"] + params["mass2"])**(-1./5)
     elif Dmax:
         self.D = random.uniform(0, 1) ** (1. / 3) * Dmax
         self.ra = random.uniform(0, 2 * math.pi)
         self.dec = arcsin(random.uniform(-1, 1))
         self.gps = lal.LIGOTimeGPS(gps,0)
         self.gmst = lal.GreenwichMeanSiderealTime(self.gps)
         self.psi = random.uniform(0, 2 * math.pi)
         self.phi = random.uniform(0, 2 * math.pi)
         self.cosi = random.uniform(-1, 1)
         self.mchirp = 1.4 * 2**(-1./5)
     else: 
         raise ValueError("Must provide either list of params or maximum distance")
     # general content:
     self.xyz = detectors.xyz(self.ra - self.gmst, self.dec)
     self.ifos = []
     self.mirror = False
     self.detected = False
     self.localization = {}
     self.area = {}
     self.patches = {}
Exemple #13
0
def get_polarization_tensor(ra, dec, time, psi, mode):
    """
    Calculate the polarization tensor for a given sky location and time

    See Nishizawa et al. (2009) arXiv:0903.0528 for definitions of the polarisation tensors.
    [u, v, w] represent the Earth-frame
    [m, n, omega] represent the wave-frame
    Note: there is a typo in the definition of the wave-frame in Nishizawa et al.
    Parameters
    -------
    ra: float
        right ascension in radians
    dec: float
        declination in radians
    time: float
        geocentric GPS time
    psi: float
        binary polarisation angle counter-clockwise about the direction of propagation
    mode: str
        polarisation mode

    Returns
    -------
    array_like: A 3x3 representation of the polarization_tensor for the specified mode.

    """
    gmst = fmod(lal.GreenwichMeanSiderealTime(time), 2 * np.pi)
    theta, phi = ra_dec_to_theta_phi(ra, dec, gmst)
    u = np.array([
        np.cos(phi) * np.cos(theta),
        np.cos(theta) * np.sin(phi), -np.sin(theta)
    ])
    v = np.array([-np.sin(phi), np.cos(phi), 0])
    m = -u * np.sin(psi) - v * np.cos(psi)
    n = -u * np.cos(psi) + v * np.sin(psi)

    if mode.lower() == 'plus':
        return np.einsum('i,j->ij', m, m) - np.einsum('i,j->ij', n, n)
    elif mode.lower() == 'cross':
        return np.einsum('i,j->ij', m, n) + np.einsum('i,j->ij', n, m)
    elif mode.lower() == 'breathing':
        return np.einsum('i,j->ij', m, m) + np.einsum('i,j->ij', n, n)

    # Calculating omega here to avoid calculation when model in [plus, cross, breathing]
    omega = np.cross(m, n)
    if mode.lower() == 'longitudinal':
        return np.einsum('i,j->ij', omega, omega)
    elif mode.lower() == 'x':
        return np.einsum('i,j->ij', m, omega) + np.einsum('i,j->ij', omega, m)
    elif mode.lower() == 'y':
        return np.einsum('i,j->ij', n, omega) + np.einsum('i,j->ij', omega, n)
    else:
        raise ValueError("{} not a polarization mode!".format(mode))
Exemple #14
0
def vector_compute_arrival_time_at_detector(det, RA, DEC, tref, tref_geo=None):
    """
    Transfer a geocentered reference time 'tref' to the detector-based arrival time as a function of sky position.
    If the treef_geo argument is specified, then the tref_geo is used to calculate an hour angle as provided by
    `XLALGreenwichMeanSiderealTime`. This can significantly speed up the function with a sacrifice (likely minimal)
    of accuracy.
    """
    RA = np.array(RA, copy=False, ndmin=1)
    DEC = np.array(DEC, copy=False, ndmin=1)
    tref = np.array(tref, copy=False, ndmin=1)

    # Calculate hour angle
    if tref_geo is None:
        time = np.array([lal.GreenwichMeanSiderealTime(t) for t in tref],
                        dtype=float)
    else:
        # FIXME: Could be called once and moved outside
        time = lal.GreenwichMeanSiderealTime(tref_geo)
    hr_angle = np.array(time - RA, dtype=float)

    DEC = np.array(DEC, dtype=float)

    # compute spherical coordinate position of the detector
    # based on hour angle and declination
    cos_hr = np.cos(hr_angle)
    sin_hr = np.sin(hr_angle)

    cos_dec = np.cos(DEC)
    sin_dec = np.sin(DEC)
    # compute source vector
    source_xyz = np.array([cos_dec * cos_hr, cos_dec * -sin_hr, sin_dec])
    # get the detector vector
    # must be careful - the C function is designed to compute the
    # light time delay between two detectors as \vec{det2} - \vec{det1}.
    # But timeDelay.c gets the earth center time delay by passing
    # {0., 0., 0.} as the 2nd arg. So det_xyz needs an extra - sign.
    # FIXME: Could be called once and moved outside
    det_xyz = -lalsim.DetectorPrefixToLALDetector(det).location

    return tref + np.dot(np.transpose(det_xyz), source_xyz) / lal.C_SI
def simulate(seed_sim_inspiral,
             psds,
             responses,
             locations,
             measurement_error,
             f_low=None,
             waveform=None):
    from ..bayestar import filter

    seed, sim_inspiral = seed_sim_inspiral
    np.random.seed(seed)

    # Unpack some values from the row in the table.
    DL = sim_inspiral.distance
    ra = sim_inspiral.longitude
    dec = sim_inspiral.latitude
    inc = sim_inspiral.inclination
    # phi = sim_inspiral.coa_phase  # arbitrary?
    psi = sim_inspiral.polarization
    epoch = sim_inspiral.time_geocent
    gmst = lal.GreenwichMeanSiderealTime(epoch)

    f_low = f_low or sim_inspiral.f_lower
    waveform = waveform or sim_inspiral.waveform

    # Signal models for each detector.
    H = filter.sngl_inspiral_psd(waveform,
                                 mass1=sim_inspiral.mass1,
                                 mass2=sim_inspiral.mass2,
                                 spin1x=sim_inspiral.spin1x,
                                 spin1y=sim_inspiral.spin1y,
                                 spin1z=sim_inspiral.spin1z,
                                 spin2x=sim_inspiral.spin2x,
                                 spin2y=sim_inspiral.spin2y,
                                 spin2z=sim_inspiral.spin2z,
                                 f_min=f_low)

    return [
        simulate_snr(ra,
                     dec,
                     psi,
                     inc,
                     DL,
                     epoch,
                     gmst,
                     H,
                     S,
                     response,
                     location,
                     measurement_error=measurement_error)
        for S, response, location in zip(psds, responses, locations)
    ]
def complex_antenna_factor(det, RA, DEC, psi, tref):
    """
    Function to compute the complex-valued antenna pattern function:
    F+ + i Fx

    'det' is a detector prefix string (e.g. 'H1')
    'RA' and 'DEC' are right ascension and declination (in radians)
    'psi' is the polarization angle
    'tref' is the reference GPS time
    """
    detector = lalsim.DetectorPrefixToLALDetector(det)
    Fp, Fc = lal.ComputeDetAMResponse(detector.response, RA, DEC, psi, lal.GreenwichMeanSiderealTime(tref))

    return Fp + 1j * Fc
Exemple #17
0
def hrss_in_instrument(sim, instrument, offsetvector):
    """
	Given an injection and an instrument, compute and return the h_rss
	of the injection as should be observed in the instrument.  That is,
	project the waveform onto the instrument, and return the root
	integrated strain squared.
	"""
    # FIXME:  this function is really only correct for sine-Gaussian
    # injections.  that's OK because I only quote sensitivities in
    # units of hrss when discussing sine-Gaussians.
    #
    # the problem is the following.  first,
    #
    #	h = F+ h+ + Fx hx
    #
    # so
    #
    #	h^{2} = F+^2 h+^2 + Fx^2 hx^2 + 2 F+ Fx h+ hx
    #
    # which means to calculate the hrss in the instrument you need to
    # know:  mean-square h in the + polarization, mean-square h in the
    # x polarization, and the correlation between the polarizations <h+
    # hx>.  these could be recorded in the sim_burst table, but they
    # aren't at present.

    # semimajor and semiminor axes of polarization ellipse

    a = 1.0 / math.sqrt(2.0 - sim.pol_ellipse_e**2)
    b = a * math.sqrt(1.0 - sim.pol_ellipse_e**2)

    # hrss in plus and cross polarizations

    hplusrss = sim.hrss * (a * math.cos(sim.pol_ellipse_angle) -
                           b * math.sin(sim.pol_ellipse_angle))
    hcrossrss = sim.hrss * (b * math.cos(sim.pol_ellipse_angle) +
                            a * math.sin(sim.pol_ellipse_angle))

    # antenna response factors

    fplus, fcross = lal.ComputeDetAMResponse(
        lal.cached_detector_by_prefix[instrument].response, sim.ra, sim.dec,
        sim.psi,
        lal.GreenwichMeanSiderealTime(
            sim.time_at_instrument(instrument, offsetvector)))

    # hrss in detector

    return math.sqrt((fplus * hplusrss)**2 + (fcross * hcrossrss)**2)
Exemple #18
0
 def fill_sim_inspiral_row(self, row):
     # using dummy values for many fields, should work for our purposes
     row.waveform = 'TaylorT4threePointFivePN'
     row.distance = self.distance
     total_mass = self.mass1 + self.mass2
     row.mass1 = self.mass1
     row.mass2 = self.mass2
     row.eta = self.mass1 * self.mass2 / total_mass**2
     row.mchirp = total_mass * row.eta**(3. / 5.)
     row.latitude = self.latitude
     row.longitude = self.longitude
     row.inclination = self.inclination
     row.polarization = self.polarization
     row.phi0 = 0
     row.f_lower = 20
     row.f_final = lal.C_SI ** 3 / \
             (6. ** (3. / 2.) * lal.PI * lal.G_SI * total_mass)
     row.spin1x = row.spin1y = row.spin1z = 0
     row.spin2x = row.spin2y = row.spin2z = 0
     row.alpha1 = 0
     row.alpha2 = 0
     row.alpha3 = 0
     row.alpha4 = 0
     row.alpha5 = 0
     row.alpha6 = 0
     row.alpha = 0
     row.beta = 0
     row.theta0 = 0
     row.psi0 = 0
     row.psi3 = 0
     row.geocent_end_time = int(self.end_time)
     row.geocent_end_time_ns = int(1e9 *
                                   (self.end_time - row.geocent_end_time))
     row.end_time_gmst = lal.GreenwichMeanSiderealTime(
         lal.LIGOTimeGPS(self.end_time))
     for d in 'lhvgt':
         row.__setattr__('eff_dist_' + d, row.distance)
         row.__setattr__(d + '_end_time', row.geocent_end_time)
         row.__setattr__(d + '_end_time_ns', row.geocent_end_time_ns)
     row.amp_order = 0
     row.coa_phase = 0
     row.bandpass = 0
     row.taper = self.taper
     row.numrel_mode_min = 0
     row.numrel_mode_max = 0
     row.numrel_data = None
     row.source = 'ANTANI'
Exemple #19
0
def radectoearth(ra, dec, gps, filename=''):
    gmst = np.mod(lal.GreenwichMeanSiderealTime(gps),
                  2 * np.pi) * 86400 / 2 / np.pi
    gmst_deg = gmst / 86400 * 360
    # ---- Compute Earth-based coordinates of targeted sky location, in degrees.
    phi_deg = ra - gmst_deg
    theta_deg = 90 - dec
    # ---- Convert to radians.
    phi = phi_deg * 2 * np.pi / 360
    phi = np.mod(phi + np.pi, 2 * np.pi) - np.pi
    theta = theta_deg * 2 * np.pi / 360
    if filename:
        with open(filename, 'w') as f:
            f.write('{0} {1}'.format(phi, theta))
            f.close()
        return
    else:
        return phi, theta
Exemple #20
0
def string_amplitude_in_instrument(sim, instrument, offsetvector):
    """
	Given a string cusp injection and an instrument, compute and return
	the amplitude of the injection as should be observed in the
	instrument.
	"""
    assert sim.waveform == "StringCusp"

    # antenna response factors

    fplus, fcross = lal.ComputeDetAMResponse(
        lal.cached_detector_by_prefix[instrument].response, sim.ra, sim.dec,
        sim.psi,
        lal.GreenwichMeanSiderealTime(
            sim.time_at_instrument(instrument, offsetvector)))

    # amplitude in detector

    return fplus * sim.amplitude
Exemple #21
0
def zenith_azimuth_to_ra_dec(zenith, azimuth, geocent_time, ifos):
    """
    Convert from the 'detector frame' to the Earth frame.

    Parameters
    kappa: float
        The zenith angle in the detector frame
    eta: float
        The azimuthal angle in the detector frame
    geocent_time: float
        GPS time at geocenter
    ifos: list
        List of Interferometer objects defining the detector frame

    Returns
    -------
    ra, dec: float
        The zenith and azimuthal angles in the sky frame.
    """
    theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, ifos)
    gmst = lal.GreenwichMeanSiderealTime(geocent_time)
    ra, dec = theta_phi_to_ra_dec(theta, phi, gmst)
    ra = ra % (2 * np.pi)
    return ra, dec
Exemple #22
0
    def test_antenna_pattern_vs_lal(self):
        gmst = lal.GreenwichMeanSiderealTime(self.gpstime)
        f_bilby = np.zeros((self.trial, 6))
        f_lal = np.zeros((self.trial, 6))

        for n, ifo_name in enumerate(self.ifo_names):
            response = lalsimulation.DetectorPrefixToLALDetector(
                self.lal_prefixes[ifo_name]).response
            ifo = self.ifos[n]
            for i in range(self.trial):
                ra = 2. * np.pi * np.random.uniform()
                dec = np.pi * np.random.uniform() - np.pi / 2.
                psi = np.pi * np.random.uniform()
                f_lal[i] = lal.ComputeDetAMResponseExtraModes(
                    response, ra, dec, psi, gmst)
                for m, pol in enumerate(self.polarizations):
                    f_bilby[i,
                            m] = ifo.antenna_response(ra, dec, self.gpstime,
                                                      psi, pol)

            std = np.std(f_bilby - f_lal, axis=0)
            for m, pol in enumerate(self.polarizations):
                with self.subTest(':'.join((ifo_name, pol))):
                    self.assertAlmostEqual(std[m], 0.0, places=7)
Exemple #23
0
def pick_coinc():
    """Pick a coincidence from the "First Two Years" paper."""
    filename = pkg_resources.resource_filename(
        __name__, '../data/first2years/2016/gstlal.xml.gz')
    xmldoc = utils.load_filename(filename, contenthandler=ContentHandler)
    root, = xmldoc.childNodes

    # Remove unneeded tables
    for lsctable in (lsctables.FilterTable, lsctables.SegmentTable,
                     lsctables.SegmentDefTable, lsctables.SimInspiralTable,
                     lsctables.SummValueTable, lsctables.SearchSummVarsTable):
        root.removeChild(lsctable.get_table(xmldoc))

    coinc_inspiral_table = table = lsctables.CoincInspiralTable.get_table(
        xmldoc)

    # Determine event with most recent sideral time
    gps_time_now = lal.GPSTimeNow()
    gmsts = np.asarray([lal.GreenwichMeanSiderealTime(_.end) for _ in table])
    gmst_now = lal.GreenwichMeanSiderealTime(gps_time_now)
    div, rem = divmod(gmst_now - gmsts, 2 * np.pi)
    i = np.argmin(rem)
    new_gmst = div[i] * 2 * np.pi + gmsts[i]
    old_time = table[i].end
    new_time = lal.LIGOTimeGPS()
    result = lal.GreenwichMeanSiderealTimeToGPS(new_gmst, new_time)
    result.disown()
    del result
    delta_t = new_time - old_time
    target_coinc_event_id = int(table[i].coinc_event_id)

    # Remove unneeded rows
    table[:] = [
        row for row in table
        if int(row.coinc_event_id) == target_coinc_event_id
    ]
    target_end_time = table[0].get_end()

    coinc_table = table = lsctables.CoincTable.get_table(xmldoc)
    table[:] = [
        row for row in table
        if int(row.coinc_event_id) == target_coinc_event_id
    ]

    table = lsctables.CoincMapTable.get_table(xmldoc)
    table[:] = [
        row for row in table
        if int(row.coinc_event_id) == target_coinc_event_id
    ]
    target_sngl_inspirals = frozenset(row.event_id for row in table)

    sngl_inspiral_table = table = lsctables.SnglInspiralTable.get_table(xmldoc)
    table[:] = [row for row in table if row.event_id in target_sngl_inspirals]

    table = lsctables.ProcessTable.get_table(xmldoc)
    table[:] = [row for row in table if row.program == 'gstlal_inspiral']
    target_process_ids = frozenset(row.process_id for row in table)

    table = lsctables.SearchSummaryTable.get_table(xmldoc)
    table[:] = [
        row for row in table if target_end_time in row.get_out()
        and row.process_id in target_process_ids
    ]
    target_process_ids = frozenset(row.process_id for row in table)

    table = lsctables.ProcessTable.get_table(xmldoc)
    table[:] = [row for row in table if row.process_id in target_process_ids]

    table = lsctables.ProcessParamsTable.get_table(xmldoc)
    table[:] = [row for row in table if row.process_id in target_process_ids]

    # Shift event times
    for row in coinc_inspiral_table:
        row.end += delta_t
    for row in sngl_inspiral_table:
        row.end += delta_t
        row.end_time_gmst = lal.GreenwichMeanSiderealTime(row.end)

    # The old version of gstlal used to produce the "First Two Years" data set
    # stored likelihood in the coinc_event.likelihood column, but newer
    # versions store the *natural log* of the likelihood here. The p_astro
    # calculation requires this to be log likelihood.
    for row in coinc_table:
        row.likelihood = np.log(row.likelihood)

    # Gstlal stores the template's SVD bank index in the Gamma1 column.
    # Fill this in so that we can calculate p_astro
    # (see :mod:`gwcelery.tasks.p_astro_gstlal`).
    for row in sngl_inspiral_table:
        row.Gamma1 = 16

    coinc_xml = io.BytesIO()
    utils.write_fileobj(xmldoc, coinc_xml)
    return coinc_xml.getvalue()
Exemple #24
0
def response(gpsTime, rightAscension, declination, inclination, polarization,
             unit, det):
    """
  response( gpsTime, rightAscension, declination, inclination,
              polarization, unit, detector )
  
  Calculates the antenna factors for a detector 'detector' (e.g. 'H1')
  at a given gps time (as integer) for a given sky location
  (rightAscension, declination) in some unit (degree/radians).
  This computation also takes into account a specific inclination
  and polarization.
  
  The returned values are: (f-plus, f-cross, f-average, q-value).
  
  Example: antenna.response( 854378604.780, 11.089, 42.308, 0, 0, 'radians', 'H1' )
  """

    # check the input arguments
    if unit == 'radians':
        ra_rad = rightAscension
        de_rad = declination
        psi_rad = polarization
        iota_rad = inclination
    elif unit == 'degree':
        ra_rad = rightAscension / 180.0 * pi
        de_rad = declination / 180.0 * pi
        psi_rad = polarization / 180.0 * pi
        iota_rad = inclination / 180.0 * pi
    else:
        raise ValueError, "Unknown unit %s" % unit

    # calculate GMST if the GPS time
    gps = lal.LIGOTimeGPS(gpsTime)
    gmst_rad = lal.GreenwichMeanSiderealTime(gps)

    # create detector-name map
    detMap = {
        'H1': 'LHO_4k',
        'H2': 'LHO_2k',
        'L1': 'LLO_4k',
        'G1': 'GEO_600',
        'V1': 'VIRGO',
        'T1': 'TAMA_300'
    }
    try:
        detector = detMap[det]
    except KeyError:
        raise ValueError, "ERROR. Key %s is not a valid detector name."\
              % (det)

    # get detector
    if detector not in inject.cached_detector.keys():
        raise ValueError, "%s is not a cached detector.  "\
              "Cached detectors are: %s" \
              % (det, inject.cached_detector.keys())

    # get the correct response data
    response = inject.cached_detector[detector].response

    # actual computation of antenna factors
    f_plus, f_cross = lal.ComputeDetAMResponse(response, ra_rad, de_rad,
                                               psi_rad, gmst_rad)

    f_ave = sqrt((f_plus * f_plus + f_cross * f_cross) / 2.0)
    ci = cos(iota_rad)
    cc = ci * ci

    # calculate q-value, e.g. ratio of effective to real distance
    # ref: Duncans PhD, eq. (4.3) on page 57
    f_q = sqrt(f_plus * f_plus * (1 + cc) * (1 + cc) / 4.0 +
               f_cross * f_cross * cc)

    # output
    return f_plus, f_cross, f_ave, f_q
Exemple #25
0
def localize(event,
             waveform='o2-uberbank',
             f_low=30.0,
             min_inclination=0,
             max_inclination=np.pi / 2,
             min_distance=None,
             max_distance=None,
             prior_distance_power=None,
             cosmology=False,
             mcmc=False,
             chain_dump=None,
             enable_snr_series=True,
             f_high_truncate=0.95):
    """Localize a compact binary signal using the BAYESTAR algorithm.

    Parameters
    ----------
    event : `ligo.skymap.io.events.Event`
        The event candidate.
    waveform : str, optional
        The name of the waveform approximant.
    f_low : float, optional
        The low frequency cutoff.
    min_distance, max_distance : float, optional
        The limits of integration over luminosity distance, in Mpc
        (default: determine automatically from detector sensitivity).
    prior_distance_power : int, optional
        The power of distance that appears in the prior
        (default: 2, uniform in volume).
    cosmology: bool, optional
        Set to enable a uniform in comoving volume prior (default: false).
    mcmc : bool, optional
        Set to use MCMC sampling rather than more accurate Gaussian quadrature.
    chain_dump : str, optional
        Save posterior samples to this filename if `mcmc` is set.
    enable_snr_series : bool, optional
        Set to False to disable SNR time series.
    f_high_truncate : float, optional
        Truncate the noise power spectral densities at this factor times the
        highest sampled frequency to suppress artifacts caused by incorrect
        PSD conditioning by some matched filter pipelines.

    Returns
    -------
    skymap : `astropy.table.Table`
        A 3D sky map in multi-order HEALPix format.
    """

    # Hide event parameters, but show all other arguments
    def formatvalue(value):
        if isinstance(value, Event):
            return '=...'
        else:
            return '=' + repr(value)

    frame = inspect.currentframe()
    argstr = inspect.formatargvalues(*inspect.getargvalues(frame),
                                     formatvalue=formatvalue)
    run_time = time.perf_counter()

    epoch, sample_rate, toas, snr_series, responses, locations, horizons = \
        condition(event, waveform=waveform, f_low=f_low,
                  enable_snr_series=enable_snr_series,
                  f_high_truncate=f_high_truncate)

    min_distance, max_distance, prior_distance_power, cosmology = \
        condition_prior(horizons, min_distance, max_distance,
                        prior_distance_power, cosmology)

    gmst = lal.GreenwichMeanSiderealTime(epoch)

    # Time and run sky localization.
    log.debug('starting computationally-intensive section')
    args = (min_inclination, max_inclination, min_distance, max_distance,
            prior_distance_power, cosmology, gmst, sample_rate, toas,
            snr_series, responses, locations, horizons)
    if mcmc:
        max_abs_t = 2 * snr_series.data.shape[1] / sample_rate
        skymap = localize_emcee(
            args=args,
            xmin=[0, -1, min_distance, -1, 0, 0],
            xmax=[2 * np.pi, 1, max_distance, 1, 2 * np.pi, 2 * max_abs_t],
            chain_dump=chain_dump)
    else:
        skymap, log_bci, log_bsn = core.toa_phoa_snr(*args)
        skymap = Table(skymap)
        skymap.meta['log_bci'] = log_bci
        skymap.meta['log_bsn'] = log_bsn

    # Convert distance moments to parameters
    try:
        distmean = skymap.columns.pop('DISTMEAN')
        diststd = skymap.columns.pop('DISTSTD')
    except KeyError:
        distmean, diststd, _ = distance.parameters_to_moments(
            skymap['DISTMU'], skymap['DISTSIGMA'])
    else:
        skymap['DISTMU'], skymap['DISTSIGMA'], skymap['DISTNORM'] = \
            distance.moments_to_parameters(distmean, diststd)

    # Add marginal distance moments
    good = np.isfinite(distmean) & np.isfinite(diststd)
    prob = (moc.uniq2pixarea(skymap['UNIQ']) * skymap['PROBDENSITY'])[good]
    distmean = distmean[good]
    diststd = diststd[good]
    rbar = (prob * distmean).sum()
    r2bar = (prob * (np.square(diststd) + np.square(distmean))).sum()
    skymap.meta['distmean'] = rbar
    skymap.meta['diststd'] = np.sqrt(r2bar - np.square(rbar))

    run_time = time.perf_counter() - run_time
    end_time = lal.GPSTimeNow()
    log.info('finished computationally-intensive section in %.3f s', run_time)

    # Fill in metadata and return.
    program, _ = os.path.splitext(os.path.basename(sys.argv[0]))
    skymap.meta.update(metadata_for_version_module(version))
    skymap.meta['creator'] = 'BAYESTAR'
    skymap.meta['origin'] = 'LIGO/Virgo'
    skymap.meta['gps_time'] = float(epoch)
    skymap.meta['runtime'] = float(run_time)
    skymap.meta['instruments'] = {single.detector for single in event.singles}
    skymap.meta['gps_creation_time'] = end_time
    skymap.meta['history'] = [
        '', 'Generated by calling the following Python function:',
        '{}.{}{}'.format(__name__, frame.f_code.co_name, argstr), '',
        'This was the command line that started the program:',
        ' '.join([program] + sys.argv[1:])
    ]

    return skymap
Exemple #26
0
progress.max = len(opts.fitsfilenames)

matplotlib.rc('path', simplify=True, simplify_threshold=1)

if opts.colormap is None:
    colors = ['k'] * len(opts.fitsfilenames)
else:
    colors = matplotlib.cm.get_cmap(opts.colormap)
    colors = colors(np.linspace(0, 1, len(opts.fitsfilenames)))
for count_records, (color,
                    fitsfilename) in enumerate(zip(colors,
                                                   opts.fitsfilenames)):
    progress.update(count_records, fitsfilename)
    skymap, metadata = fits.read_sky_map(fitsfilename, nest=None)
    nside = hp.npix2nside(len(skymap))
    gmst = lal.GreenwichMeanSiderealTime(metadata['gps_time']) % (2 * np.pi)

    indices = np.argsort(-skymap)
    region = np.empty(skymap.shape)
    region[indices] = 100 * np.cumsum(skymap[indices])
    plot.healpix_contour(region,
                         nest=metadata['nest'],
                         dlon=-gmst,
                         colors=[color],
                         linewidths=0.5,
                         levels=[opts.contour],
                         alpha=opts.alpha)

progress.update(-1, 'saving figure')

# Add a white outline to all text to make it stand out from the background.
Exemple #27
0
def ligolw_sky_map(sngl_inspirals,
                   approximant,
                   amplitude_order,
                   phase_order,
                   f_low,
                   min_distance=None,
                   max_distance=None,
                   prior=None,
                   method="toa_snr",
                   reference_frequency=None,
                   psds=None,
                   nside=-1):
    """Convenience function to produce a sky map from LIGO-LW rows. Note that
    min_distance and max_distance should be in Mpc."""

    if method == "toa_snr" and prior is None:
        raise ValueError(
            "For method='toa_snr', the argument prior is required.")

    ifos = [sngl_inspiral.ifo for sngl_inspiral in sngl_inspirals]

    # Extract masses from the table.
    mass1s = np.asarray(
        [sngl_inspiral.mass1 for sngl_inspiral in sngl_inspirals])
    mass2s = np.asarray(
        [sngl_inspiral.mass2 for sngl_inspiral in sngl_inspirals])

    # Extract SNRs from table.
    # FIXME: should get complex SNR, but MBTAOnline events don't populate the
    # coa_phase column, and we are not using coa_phase yet.
    snrs = np.asarray([sngl_inspiral.snr for sngl_inspiral in sngl_inspirals])

    # Extract TOAs from table.
    toas_ns = np.asarray(
        [sngl_inspiral.get_end().ns() for sngl_inspiral in sngl_inspirals],
        dtype=np.int64)

    # Optionally apply reference frequency shift.
    if reference_frequency is not None:
        toas_ns -= [
            int(
                round(1e9 *
                      lalsimulation.SimInspiralTaylorF2ReducedSpinChirpTime(
                          reference_frequency, m1 * lal.LAL_MSUN_SI,
                          m2 * lal.LAL_MSUN_SI, 0, 4)))
            for m1, m2 in zip(mass1s, mass2s)
        ]

    # Convert TOAs from nanoseconds to seconds.
    toas = 1e-9 * toas_ns

    # Find average Greenwich mean sidereal time of event.
    mean_toa_ns = sum(toas_ns) // len(toas_ns)
    epoch = lal.LIGOTimeGPS(0, long(mean_toa_ns))
    gmst = lal.GreenwichMeanSiderealTime(epoch)

    # Power spectra for each detector.
    if psds is None:
        psds = [timing.get_noise_psd_func(ifo) for ifo in ifos]

    # Signal models for each detector.
    signal_models = [
        timing.SignalModel(mass1, mass2, psd, f_low, approximant,
                           amplitude_order, phase_order)
        for mass1, mass2, psd in zip(mass1s, mass2s, psds)
    ]

    # Get SNR=1 horizon distances for each detector.
    horizons = [
        signal_model.get_horizon_distance() for signal_model in signal_models
    ]

    # Estimate TOA uncertainty (squared) using CRB or BRB evaluated at MEASURED
    # values of the SNRs.
    s2_toas = [
        np.square(signal_model.get_toa_uncert(np.abs(snr)))
        for signal_model, snr in zip(signal_models, snrs)
    ]

    # Look up physical parameters for detector.
    detectors = [
        lalsimulation.InstrumentNameToLALDetector(str(ifo)) for ifo in ifos
    ]
    responses = [det.response for det in detectors]
    locations = [det.location for det in detectors]

    # Use half the minimum effective distance as the default value for
    # min_distance and twice the maximum effective distance as the default
    # value for max_distance.
    if min_distance is None or max_distance is None:
        effective_distances = np.asarray(horizons) / np.abs(snrs)
        if min_distance is None:
            min_distance = 0.5 * min(effective_distances)
        if max_distance is None:
            max_distance = 2 * max(effective_distances)

    # Time and run sky localization.
    start_time = time.time()
    if method == "toa":
        prob = sky_map.tdoa(gmst, toas, s2_toas, locations, nside=nside)
    elif method == "toa_snr":
        prob = sky_map.tdoa_snr(gmst,
                                toas,
                                snrs,
                                s2_toas,
                                responses,
                                locations,
                                horizons,
                                min_distance,
                                max_distance,
                                prior,
                                nside=nside)
    else:
        raise ValueError("Unrecognized method: %s" % method)
    end_time = time.time()

    # Find elapsed run time.
    elapsed_time = end_time - start_time

    # Done!
    return prob, epoch, elapsed_time
import healpy as hp
import lal
from lalinference import fits
from lalinference import plot

fig = plt.figure(figsize=(opts.figure_width, opts.figure_height), frameon=False)
ax = plt.subplot(111,
    projection='mollweide' if opts.geo else 'astro mollweide')
ax.cla()
ax.grid()

skymap, metadata = fits.read_sky_map(infilename, nest=None)
nside = hp.npix2nside(len(skymap))

if opts.geo:
    dlon = -lal.GreenwichMeanSiderealTime(lal.LIGOTimeGPS(metadata['gps_time'])) % (2*np.pi)
else:
    dlon = 0

# Convert sky map from probability to probability per square degree.
probperdeg2 = skymap / hp.nside2pixarea(nside, degrees=True)

# Plot sky map.
vmax = probperdeg2.max()
plot.healpix_heatmap(
    probperdeg2, dlon=dlon, nest=metadata['nest'],
    vmin=0., vmax=vmax, cmap=plt.get_cmap(opts.colormap))

if opts.colorbar:
    # Plot colorbar.
    cb = plot.colorbar()
def localize(event,
             waveform='o2-uberbank',
             f_low=30.0,
             min_distance=None,
             max_distance=None,
             prior_distance_power=None,
             cosmology=False,
             method='toa_phoa_snr',
             nside=-1,
             chain_dump=None,
             enable_snr_series=True,
             f_high_truncate=0.95):
    """Convenience function to produce a sky map from LIGO-LW rows. Note that
    min_distance and max_distance should be in Mpc.

    Returns a 'NESTED' ordering HEALPix image as a Numpy array.
    """
    frame = inspect.currentframe()
    argstr = inspect.formatargvalues(*inspect.getargvalues(frame))
    start_time = lal.GPSTimeNow()

    singles = event.singles
    if not enable_snr_series:
        singles = [single for single in singles if single.snr is not None]

    ifos = [single.detector for single in singles]

    # Extract SNRs from table.
    snrs = np.ma.asarray([
        np.ma.masked if single.snr is None else single.snr
        for single in singles
    ])

    # Look up physical parameters for detector.
    detectors = [
        lalsimulation.DetectorPrefixToLALDetector(str(ifo)) for ifo in ifos
    ]
    responses = np.asarray([det.response for det in detectors])
    locations = np.asarray([det.location for det in detectors])

    # Power spectra for each detector.
    psds = [single.psd for single in singles]
    psds = [
        timing.InterpolatedPSD(filter.abscissa(psd),
                               psd.data.data,
                               f_high_truncate=f_high_truncate) for psd in psds
    ]

    log.debug('calculating templates')
    H = filter.sngl_inspiral_psd(waveform, f_min=f_low, **event.template_args)

    log.debug('calculating noise PSDs')
    HS = [filter.signal_psd_series(H, S) for S in psds]

    # Signal models for each detector.
    log.debug('calculating Fisher matrix elements')
    signal_models = [timing.SignalModel(_) for _ in HS]

    # Get SNR=1 horizon distances for each detector.
    horizons = np.asarray([
        signal_model.get_horizon_distance() for signal_model in signal_models
    ])

    weights = np.ma.asarray([
        1 / np.square(signal_model.get_crb_toa_uncert(snr))
        for signal_model, snr in zip(signal_models, snrs)
    ])

    # Center detector array.
    locations -= np.sum(locations * weights.reshape(-1, 1),
                        axis=0) / np.sum(weights)

    if cosmology:
        log.warn('Enabling cosmological prior. ' 'This feature is UNREVIEWED.')

    if enable_snr_series:
        log.warn('Enabling input of SNR time series. '
                 'This feature is UNREVIEWED.')
        snr_series = [single.snr_series for single in singles]
        if all(s is None for s in snr_series):
            snr_series = None
    else:
        snr_series = None

    # Maximum barycentered arrival time error:
    # |distance from array barycenter to furthest detector| / c + 5 ms.
    # For LHO+LLO, this is 15.0 ms.
    # For an arbitrary terrestrial detector network, the maximum is 26.3 ms.
    max_abs_t = np.max(np.sqrt(np.sum(np.square(locations / lal.C_SI),
                                      axis=1))) + 0.005

    if snr_series is None:
        log.warn(
            "No SNR time series found, so we are creating a zero-noise "
            "SNR time series from the whitened template's autocorrelation "
            "sequence. The sky localization uncertainty may be "
            "underestimated.")

        acors, sample_rates = zip(
            *[filter.autocorrelation(_, max_abs_t) for _ in HS])
        sample_rate = sample_rates[0]
        deltaT = 1 / sample_rate
        nsamples = len(acors[0])
        assert all(sample_rate == _ for _ in sample_rates)
        assert all(nsamples == len(_) for _ in acors)
        nsamples = nsamples * 2 - 1

        snr_series = []
        for acor, single in zip(acors, singles):
            series = lal.CreateCOMPLEX8TimeSeries('fake SNR', 0, 0, deltaT,
                                                  lal.StrainUnit, nsamples)
            series.epoch = single.time - 0.5 * (nsamples - 1) * deltaT
            acor = np.concatenate((np.conj(acor[:0:-1]), acor))
            series.data.data = single.snr * filter.exp_i(single.phase) * acor
            snr_series.append(series)

    # Ensure that all of the SNR time series have the same sample rate.
    # FIXME: for now, the Python wrapper expects all of the SNR time sries to
    # also be the same length.
    deltaT = snr_series[0].deltaT
    sample_rate = 1 / deltaT
    if any(deltaT != series.deltaT for series in snr_series):
        raise ValueError('BAYESTAR does not yet support SNR time series with '
                         'mixed sample rates')

    # Ensure that all of the SNR time series have odd lengths.
    if any(len(series.data.data) % 2 == 0 for series in snr_series):
        raise ValueError('SNR time series must have odd lengths')

    # Trim time series to the desired length.
    max_abs_n = int(np.ceil(max_abs_t * sample_rate))
    desired_length = 2 * max_abs_n - 1
    for i, series in enumerate(snr_series):
        length = len(series.data.data)
        if length > desired_length:
            snr_series[i] = lal.CutCOMPLEX8TimeSeries(
                series, length // 2 + 1 - max_abs_n, desired_length)

    # FIXME: for now, the Python wrapper expects all of the SNR time sries to
    # also be the same length.
    nsamples = len(snr_series[0].data.data)
    if any(nsamples != len(series.data.data) for series in snr_series):
        raise ValueError('BAYESTAR does not yet support SNR time series of '
                         'mixed lengths')

    # Perform sanity checks that the middle sample of the SNR time series match
    # the sngl_inspiral records. Relax valid interval slightly from
    # +/- 0.5 deltaT to +/- 0.6 deltaT for floating point roundoff error.
    for single, series in zip(singles, snr_series):
        if np.abs(0.5 * (nsamples - 1) * series.deltaT +
                  float(series.epoch - single.time)) >= 0.6 * deltaT:
            raise ValueError('BAYESTAR expects the SNR time series to be '
                             'centered on the single-detector trigger times')

    # Extract the TOAs in GPS nanoseconds from the SNR time series, assuming
    # that the trigger happened in the middle.
    toas_ns = [
        series.epoch.ns() + 1e9 * 0.5 *
        (len(series.data.data) - 1) * series.deltaT for series in snr_series
    ]

    # Collect all of the SNR series in one array.
    snr_series = np.vstack([series.data.data for series in snr_series])

    # Center times of arrival and compute GMST at mean arrival time.
    # Pre-center in integer nanoseconds to preserve precision of
    # initial datatype.
    epoch = sum(toas_ns) // len(toas_ns)
    toas = 1e-9 * (np.asarray(toas_ns) - epoch)
    # FIXME: np.average does not yet support masked arrays.
    # Replace with np.average when numpy 1.13.0 is available.
    mean_toa = np.sum(toas * weights) / np.sum(weights)
    toas -= mean_toa
    epoch += int(np.round(1e9 * mean_toa))
    epoch = lal.LIGOTimeGPS(0, int(epoch))
    gmst = lal.GreenwichMeanSiderealTime(epoch)

    # Translate SNR time series back to time of first sample.
    toas -= 0.5 * (nsamples - 1) * deltaT

    # If minimum distance is not specified, then default to 0 Mpc.
    if min_distance is None:
        min_distance = 0

    # If maximum distance is not specified, then default to the SNR=4
    # horizon distance of the most sensitive detector.
    if max_distance is None:
        max_distance = max(horizons) / 4

    # If prior_distance_power is not specified, then default to 2
    # (p(r) ~ r^2, uniform in volume).
    if prior_distance_power is None:
        prior_distance_power = 2

    # Raise an exception if 0 Mpc is the minimum effective distance and the
    # prior is of the form r**k for k<0
    if min_distance == 0 and prior_distance_power < 0:
        raise ValueError(
            ('Prior is a power law r^k with k={}, '
             'undefined at min_distance=0').format(prior_distance_power))

    # Time and run sky localization.
    log.debug('starting computationally-intensive section')
    if method == 'toa_phoa_snr':
        skymap, log_bci, log_bsn = _sky_map.toa_phoa_snr(
            min_distance, max_distance, prior_distance_power, cosmology, gmst,
            sample_rate, toas, snr_series, responses, locations, horizons)
        skymap = Table(skymap)
        skymap.meta['log_bci'] = log_bci
        skymap.meta['log_bsn'] = log_bsn
    elif method == 'toa_phoa_snr_mcmc':
        skymap = localize_emcee(
            logl=_sky_map.log_likelihood_toa_phoa_snr,
            loglargs=(gmst, sample_rate, toas, snr_series, responses,
                      locations, horizons),
            logp=toa_phoa_snr_log_prior,
            logpargs=(min_distance, max_distance, prior_distance_power,
                      max_abs_t),
            xmin=[0, -1, min_distance, -1, 0, 0],
            xmax=[2 * np.pi, 1, max_distance, 1, 2 * np.pi, 2 * max_abs_t],
            nside=nside,
            chain_dump=chain_dump)
    else:
        raise ValueError('Unrecognized method: %s' % method)

    # Convert distance moments to parameters
    distmean = skymap.columns.pop('DISTMEAN')
    diststd = skymap.columns.pop('DISTSTD')
    skymap['DISTMU'], skymap['DISTSIGMA'], skymap['DISTNORM'] = \
        distance.moments_to_parameters(distmean, diststd)

    # Add marginal distance moments
    good = np.isfinite(distmean) & np.isfinite(diststd)
    prob = (moc.uniq2pixarea(skymap['UNIQ']) * skymap['PROBDENSITY'])[good]
    distmean = distmean[good]
    diststd = diststd[good]
    rbar = (prob * distmean).sum()
    r2bar = (prob * (np.square(diststd) + np.square(distmean))).sum()
    skymap.meta['distmean'] = rbar
    skymap.meta['diststd'] = np.sqrt(r2bar - np.square(rbar))

    log.debug('finished computationally-intensive section')
    end_time = lal.GPSTimeNow()

    # Fill in metadata and return.
    program, _ = os.path.splitext(os.path.basename(sys.argv[0]))
    skymap.meta['creator'] = 'BAYESTAR'
    skymap.meta['origin'] = 'LIGO/Virgo'
    skymap.meta['vcs_info'] = vcs_info
    skymap.meta['gps_time'] = float(epoch)
    skymap.meta['runtime'] = float(end_time - start_time)
    skymap.meta['instruments'] = {single.detector for single in singles}
    skymap.meta['gps_creation_time'] = end_time
    skymap.meta['history'] = [
        '', 'Generated by calling the following Python function:',
        '{}.{}{}'.format(__name__, frame.f_code.co_name, argstr), '',
        'This was the command line that started the program:',
        ' '.join([program] + sys.argv[1:])
    ]

    return skymap
for sim_inspiral in progress.iterate(sim_inspiral_table):

    # Unpack some values from the row in the table.
    m1 = sim_inspiral.mass1
    m2 = sim_inspiral.mass2
    f_low = sim_inspiral.f_lower if opts.f_low is None else opts.f_low
    DL = sim_inspiral.distance
    ra = sim_inspiral.longitude
    dec = sim_inspiral.latitude
    inc = sim_inspiral.inclination
    phi = sim_inspiral.coa_phase
    psi = sim_inspiral.polarization
    epoch = lal.LIGOTimeGPS(sim_inspiral.geocent_end_time,
                            sim_inspiral.geocent_end_time_ns)
    gmst = lal.GreenwichMeanSiderealTime(epoch)
    waveform = (sim_inspiral.waveform
                if opts.waveform is None else opts.waveform)

    # FIXME: Set tranverse spin components to 0
    sim_inspiral.spin1x = 0
    sim_inspiral.spin1y = 0
    sim_inspiral.spin2x = 0
    sim_inspiral.spin2y = 0

    # Pre-evaluate some trigonometric functions that we will need.
    u = np.cos(inc)
    u2 = np.square(u)

    # Signal models for each detector.
    H = filter.sngl_inspiral_psd(waveform,