예제 #1
0
    def test_get_observed_strain(self):

        # Let's check with Hanford
        theta_GW, phi_GW = np.pi / 3, 0
        antennas = gwu.antenna_responses_from_sky_localization(
            8, -70, "2015-09-14 09:50:45")
        Fc_H, Fp_H = antennas.hanford

        expected_strain = self.psi4.get_strain(theta_GW,
                                               phi_GW,
                                               0.1,
                                               trim_ends=False)
        expected_strain = (expected_strain.real() * Fp_H -
                           expected_strain.imag() * Fc_H)

        strain = self.psi4.get_observed_strain(
            8,
            -70,
            "2015-09-14 09:50:45",
            theta_GW,
            phi_GW,
            0.1,
            trim_ends=False,
        )

        self.assertEqual(strain.hanford, expected_strain)
예제 #2
0
    def test_antenna_responses(self):

        antenna_gw150914 = gwu.antenna_responses_from_sky_localization(
            8, -70, "2015-09-14 09:50:45")

        # This test is extremely weak: the numbers that are here were
        # obtained with the function itself
        self.assertAlmostEqual(antenna_gw150914.hanford[0], 0.173418558)
        self.assertAlmostEqual(antenna_gw150914.hanford[1], 0.734266762)
        self.assertAlmostEqual(antenna_gw150914.livingston[0], 0.030376784)
        self.assertAlmostEqual(antenna_gw150914.livingston[1], -0.569292709)
        self.assertAlmostEqual(antenna_gw150914.virgo[0], -0.11486789)
        self.assertAlmostEqual(antenna_gw150914.virgo[1], 0.57442590)
예제 #3
0
    def get_observed_strain(
        self,
        right_ascension,
        declination,
        time_utc,
        theta_gw,
        phi_gw,
        pcut,
        *args,
        window_function=None,
        polarization=0,
        l_max=None,
        trim_ends=True,
        **kwargs,
    ):
        r"""Return the strain accounting for all the multipoles and the spin
        weighted spherical harmonics as observed by Hanford, Livingston and
        Virgo.

        .. math::

             h_+(r,t)
             -     i h_\times(r,t) = \sum_{l=2}^{l=l_{\mathrm{max}}}
             \sum_{m=-l}^{m=l} h(r, t)^{lm} {}_{-2}Y_{lm}(\theta, \phi)

        Here \theta and \phi are the arguments ``theta_gw`` and ``phi_gw``.

        Then, for each detector

        .. math::

             h(r,t) = F_\times h_\times(theta_gw, phi_gw) + F_+ h_+(theta_gw, phi_gw)

        :param right_ascension: Right ascension of the source in degrees.
        :type right_ascension: float
        :param declination: Declination of the source in degrees.
        :type declination: float
        :param time_utc: UTC time of the event
        :type declination: str
        :param theta_gw, phi_gw: Spherical coordinates of the observer
                                 from the binary's frame, taking the angular
                                 momentum of the binary to
                                 point along the z-axis.
        :type theta_gw, phi_gw: floats
        :param pcut: Period that enters the fixed-frequency integration.
                     Typically, the longest physical period in the signal.
        :type pcut: float
        :param window_function: If not None, apply ``window_function`` to the
                                series before computing the strain.
        :type window_function: callable, str, or None
        :param trim_ends: If True, a portion of the resulting strain is removed
                          at both the initial and final times. The amount removed
                          is equal to pcut.
        :type trim_ends: bool
        :param l_max: Ignore multipoles with l > l_max
        :type l_max: int

        :returns: :math:`r (h^+ - i rh^\times)`
        :rtype: Detectors of :py:class:`~.TimeSeries`

        """

        # Detectors contains three fields, one for each detector
        antennas = gw_utils.antenna_responses_from_sky_localization(
            right_ascension, declination, time_utc, polarization)

        # We collect all the strains in a list, then we convert it in a
        # nameduples Detectors
        strains = []

        # Loop over the detectors in Detectors
        # antennas and coords are namedtuples Detectors
        for (Fc, Fp) in antennas:
            strain = self.get_strain(
                theta_gw,
                phi_gw,
                pcut,
                *args,
                window_function=window_function,
                l_max=l_max,
                trim_ends=trim_ends,
                **kwargs,
            )
            # What have that:
            # strain.real = hp
            # strain.imag = -hc
            strains.append(strain.real() * Fp - strain.imag() * Fc)

        return Detectors(*strains)
예제 #4
0
def network_mismatch(
    h1,
    h2,
    right_ascension,
    declination,
    time_utc,
    fmin=0,
    fmax=np.inf,
    noises=None,
    num_polarization_shifts=200,
    num_time_shifts=1000,
    time_shift_start=-20,
    time_shift_end=20,
    force_numba=False,
):
    """Compute network mismatch between strains h1 and h2.

    This is a wrapper around :py:meth:`~.mismatch_from_strains` (read the
    docstring for more information) that prepares the correct antenna patterns
    from the sky localization of the event. Moreover, it makes sure that noises
    (that has to be a gw_utils.Detectors object, or None) is correctly ordered.

    :param h1: First strain.
    :type h1: :py:class:`~.TimeSeries`
    :param h2: Second strain (the one that will be modified).
    :type h2: :py:class:`~.TimeSeries`
    :param right_ascension: Right ascension of the source in the sky.
    :type right_ascension: float
    :param declination: Declination of the source in the sky.
    :type declination: float
    :param time_utc: Time UTC of the event.
    :type time_utc: float
    :param fmin: Lower limit of the integration.
    :type fmin: float
    :param fmax: Higher limit of the integration.
    :type fmax: float
    :param noises: Power spectral density of the noise for all the detectors.
    :type noises: :py:class:`~.Detector`, or None
    :param num_polarization_shifts: How many points to divide the range
                                    (0, 2 pi) in the polarization shift.
    :type num_polarization_shifts: int
    :param num_time_shifts: How many points to divide the range
                            (time_shift_start, time_shift_end) in the
                            time shift.
    :type num_time_shifts: int
    :param time_shift_start: Minimum time shift applied. Search will be done
                             linearly up to time_shift_end.
    :type time_shift_start: float
    :param time_shift_end: Largest value of time shift applied.
    :type time_shift_end: float
    :param force_numba: Use numba irrespectively of the size of the input.
    :type force_numba: bool

    """

    antenna_patterns = gwu.antenna_responses_from_sky_localization(
        right_ascension, declination, time_utc)

    # Transform Detectors to lists (they are already properly ordered)
    if noises is not None:
        if isinstance(noises, gwu.Detectors):
            # We select thos antennas for which the corresponding noise is not
            # -1
            antenna_patterns = [
                ap for ap, noise in zip(antenna_patterns, noises)
                if noise != -1
            ]
            # We remove all the noises that are -1. This modifies the list.
            noises = [noise for noise in noises if noise != -1]
        else:
            raise TypeError("noises has to be None or of type Detectors")
    else:
        # All three detectors
        antenna_patterns = list(antenna_patterns)

    return mismatch_from_strains(
        h1,
        h2,
        fmin,
        fmax,
        noises=noises,
        antenna_patterns=antenna_patterns,
        num_polarization_shifts=num_polarization_shifts,
        num_time_shifts=num_time_shifts,
        time_shift_start=time_shift_start,
        time_shift_end=time_shift_end,
        force_numba=force_numba,
    )
예제 #5
0
    def test_mismatch(self):

        fmin = 5
        fmax = 15

        # Invalid noise
        with self.assertRaises(TypeError):
            gwm.network_mismatch(self.ts1,
                                 self.ts2,
                                 8,
                                 -70,
                                 "2015-09-14 09:50:45",
                                 noises=1)

        # No noise, three detectors
        antennas = gwu.antenna_responses_from_sky_localization(
            8, -70, "2015-09-14 09:50:45")

        self.assertAlmostEqual(
            gwm.mismatch_from_strains(
                self.ts1,
                self.ts2,
                fmin=fmin,
                fmax=fmax,
                noises=None,
                antenna_patterns=list(antennas),
                num_polarization_shifts=30,
                num_time_shifts=30,
                time_shift_start=-70,
                time_shift_end=70,
            )[0],
            gwm.network_mismatch(
                self.ts1,
                self.ts2,
                8,
                -70,
                "2015-09-14 09:50:45",
                fmin=fmin,
                fmax=fmax,
                noises=None,
                num_polarization_shifts=30,
                num_time_shifts=30,
                time_shift_start=-70,
                time_shift_end=70,
            )[0],
        )

        # Only one active detector

        only_virgo = gwu.Detectors(hanford=-1, livingston=-1, virgo=None)

        self.assertAlmostEqual(
            gwm.mismatch_from_strains(
                self.ts1,
                self.ts2,
                fmin=fmin,
                fmax=fmax,
                noises=None,
                antenna_patterns=[antennas.virgo],
                num_polarization_shifts=30,
                num_time_shifts=30,
                time_shift_start=-70,
                time_shift_end=70,
            )[0],
            gwm.network_mismatch(
                self.ts1,
                self.ts2,
                8,
                -70,
                "2015-09-14 09:50:45",
                fmin=fmin,
                fmax=fmax,
                noises=only_virgo,
                num_polarization_shifts=30,
                num_time_shifts=30,
                time_shift_start=-70,
                time_shift_end=70,
            )[0],
        )

        # Test with a "gw-looking" singal from PyCBC
        #
        # First, we test the overlap by giving num_polarizations,
        # num_time_shifts=1

        try:
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                from pycbc.filter import match, overlap
                from pycbc.types import timeseries as pycbcts
                from pycbc.waveform import get_td_waveform
            fmin_gw = 50
            fmax_gw = 100
            delta_t = 1 / 4096

            hp1, hc1 = get_td_waveform(
                approximant="IMRPhenomPv2",
                mass1=10,
                mass2=10,
                spin1z=0.9,
                delta_t=delta_t,
                f_lower=40,
            )

            hp2, hc2 = get_td_waveform(
                approximant="IMRPhenomPv2",
                mass1=10,
                mass2=25,
                spin1z=-0.5,
                delta_t=delta_t,
                f_lower=40,
            )

            # PyCBC does not work well with series with different length. So, we
            # crop the longer one to the length of the shorter one. For the choice
            # of paramters, it is 2 that is shorter than 1. 1 starts earlier in the
            # past. However, they have the same frequencies, so we can simply crop
            # away the part we are not interested in.

            time_offset = 2  # Manually computed looking at the times
            hp1 = hp1.crop(time_offset, 0)
            hc1 = hc1.crop(time_offset, 0)

            # We apply the "antenna pattern"
            h1_pycbc = pycbcts.TimeSeries(0.33 * hp1 + 0.66 * hc1,
                                          delta_t=hp1.delta_t)
            h2_pycbc = pycbcts.TimeSeries(0.33 * hp2 + 0.66 * hc2,
                                          delta_t=hp2.delta_t)

            overlap_m = overlap(
                h1_pycbc,
                h2_pycbc,
                psd=None,
                low_frequency_cutoff=fmin_gw,
                high_frequency_cutoff=fmax_gw,
            )

            h1_postcac = ts.TimeSeries(h1_pycbc.sample_times, hp1 - 1j * hc1)
            h2_postcac = ts.TimeSeries(h2_pycbc.sample_times, hp2 - 1j * hc2)

            o = gwm.mismatch_from_strains(
                h1_postcac,
                h2_postcac,
                fmin=fmin_gw,
                fmax=fmax_gw,
                noises=None,
                antenna_patterns=[(0.66, 0.33)],
                num_polarization_shifts=1,
                num_time_shifts=1,
                time_shift_start=0,
                time_shift_end=0,
                force_numba=False,
            )

            self.assertAlmostEqual(1 - o[0], overlap_m, places=2)

            # Now we can test the mismatch
            pycbc_m, _ = match(
                h1_pycbc,
                h2_pycbc,
                psd=None,
                low_frequency_cutoff=fmin_gw,
                high_frequency_cutoff=fmax_gw,
            )

            pycbc_m = 1 - pycbc_m

            mat = gwm.mismatch_from_strains(
                h1_postcac,
                h2_postcac,
                fmin=fmin_gw,
                fmax=fmax_gw,
                noises=None,
                antenna_patterns=[(0.66, 0.33)],
                num_polarization_shifts=100,
                num_time_shifts=800,
                time_shift_start=-0.3,
                time_shift_end=0.3,
                force_numba=False,
            )

            self.assertAlmostEqual(mat[0], pycbc_m, places=2)
        except ImportError:  # pragma: no cover
            pass