def test_physical_ephem_model(self):
        """Test physical ephemeris model"""

        if isinstance(self.psr, enterprise.pulsar.Tempo2Pulsar):
            # define signals with and without epoch TOAs
            eph1 = deterministic_signals.PhysicalEphemerisSignal(
                inc_saturn_orb=True)
            eph2 = deterministic_signals.PhysicalEphemerisSignal(
                inc_saturn_orb=True, use_epoch_toas=False)

            # initialize signals
            e1, e2 = eph1(self.psr), eph2(self.psr)

            # set parameters
            params = {'d_jupiter_mass': -8.561198198000628e-12,
                      'd_neptune_mass': 1.0251757860647059e-11,
                      'd_saturn_mass': 6.22114376130324e-12,
                      'd_uranus_mass': -2.1157536169469958e-10,
                      'frame_drift_rate': 2.874659280396648e-10,
                      'jup_orb_elements': np.array([0.04140015, -0.03422412,
                                                    0.01165894, -0.03525219,
                                                    -0.00406852, 0.0421522]),
                      'sat_orb_elements': np.array([-0.39701798, -0.13322608,
                                                    -0.05025925, 0.36331171,
                                                    -0.17080321,0.25093799])
                      }

            # test against waveform and compare non-epoch and epoch TOA results
            d1 = e1.get_delay(params=params)
            d2 = e2.get_delay(params=params)

            jup_mjd, jup_orbelxyz, sat_mjd, sat_orbelxyz = (
                utils.get_planet_orbital_elements())
            d3 = utils.physical_ephem_delay(
                self.psr.toas, self.psr.planetssb, self.psr.pos_t,
                jup_mjd=jup_mjd, jup_orbelxyz=jup_orbelxyz, sat_mjd=sat_mjd,
                sat_orbelxyz=sat_orbelxyz, inc_jupiter_orb=True,
                inc_saturn_orb=True, **params)

            msg1 = 'Signal delay does not match function delay'
            assert np.allclose(d1, d3, rtol=1e-10), msg1
            msg2 = 'epoch-TOA delay does not match full TOA delay'
            assert np.allclose(d1, d2, rtol=1e-10), msg2

            # test against pre-computed wafeform
            eph_wf = np.load(datadir + '/phys_ephem_1855_test.npy')
            msg = 'Ephemeris delay does not match pre-computed values'
            assert np.allclose(d1, eph_wf, rtol=1e-10), msg

        # test PINT exception raising
        elif isinstance(self.psr, enterprise.pulsar.PintPulsar):
            with self.assertRaises(NotImplementedError) as context:
                eph1 = deterministic_signals.PhysicalEphemerisSignal(
                    inc_saturn_orb=True)
                e1 = eph1(self.psr)

                msg = 'Physical Ephemeris model is not compatible with PINT '
                msg += 'at this time.'
                self.assertEqual(msg, str(context.exception))
示例#2
0
    def test_physical_ephem_model_setIII(self):
        """Test physical ephemeris model"""

        # define signals with and without epoch TOAs
        eph1 = deterministic_signals.PhysicalEphemerisSignal(
            sat_orb_elements=True, model="setIII")
        eph2 = deterministic_signals.PhysicalEphemerisSignal(
            sat_orb_elements=True, use_epoch_toas=False, model="setIII")

        # initialize signals
        e1, e2 = eph1(self.psr), eph2(self.psr)

        # set parameters
        params = {
            "d_jupiter_mass":
            -8.561198198000628e-12,
            "d_neptune_mass":
            1.0251757860647059e-11,
            "d_saturn_mass":
            6.22114376130324e-12,
            "d_uranus_mass":
            -2.1157536169469958e-10,
            "frame_drift_rate":
            2.874659280396648e-10,
            "jup_orb_elements":
            np.array([
                0.04140015, -0.03422412, 0.01165894, -0.03525219, -0.00406852,
                0.0421522
            ]),
            "sat_orb_elements":
            np.array([
                -0.39701798, -0.13322608, -0.05025925, 0.36331171, -0.17080321,
                0.25093799
            ]),
        }

        # test against waveform and compare non-epoch and epoch TOA results
        d1 = e1.get_delay(params=params)
        d2 = e2.get_delay(params=params)

        (jup_mjd, jup_orbel,
         sat_orbel) = utils.get_planet_orbital_elements("setIII")

        d3 = utils.physical_ephem_delay(
            self.psr.toas,
            self.psr.planetssb,
            self.psr.pos_t,
            times=jup_mjd,
            jup_orbit=jup_orbel,
            sat_orbit=sat_orbel,
            **params,
        )

        msg1 = "Signal delay does not match function delay"
        assert np.allclose(d1, d3, rtol=1e-10), msg1
        msg2 = "epoch-TOA delay does not match full TOA delay"
        assert np.allclose(d1, d2, rtol=1e-10), msg2
示例#3
0
def Dropout_PhysicalEphemerisSignal(
        frame_drift_rate=parameter.Uniform(-1e-9, 1e-9)('frame_drift_rate'),
        d_jupiter_mass=parameter.Normal(0, 1.54976690e-11)('d_jupiter_mass'),
        d_saturn_mass=parameter.Normal(0, 8.17306184e-12)('d_saturn_mass'),
        d_uranus_mass=parameter.Normal(0, 5.71923361e-11)('d_uranus_mass'),
        d_neptune_mass=parameter.Normal(0, 7.96103855e-11)('d_neptune_mass'),
        jup_orb_elements=parameter.Uniform(-0.05, 0.05,
                                           size=6)('jup_orb_elements'),
        sat_orb_elements=parameter.Uniform(-0.5, 0.5,
                                           size=6)('sat_orb_elements'),
        inc_jupiter_orb=True,
        inc_saturn_orb=False,
        use_epoch_toas=True,
        k_drop=parameter.Uniform(0.0, 1.0),
        k_threshold=0.5,
        name=''):
    """ Class factory for dropout physical ephemeris model signal."""

    # turn off saturn orbital element parameters if not including in signal
    if not inc_saturn_orb:
        sat_orb_elements = np.zeros(6)

    # define waveform
    jup_mjd, jup_orbelxyz, sat_mjd, sat_orbelxyz = (
        utils.get_planet_orbital_elements())
    wf = dropout_physical_ephem_delay(frame_drift_rate=frame_drift_rate,
                                      d_jupiter_mass=d_jupiter_mass,
                                      d_saturn_mass=d_saturn_mass,
                                      d_uranus_mass=d_uranus_mass,
                                      d_neptune_mass=d_neptune_mass,
                                      jup_orb_elements=jup_orb_elements,
                                      sat_orb_elements=sat_orb_elements,
                                      inc_jupiter_orb=inc_jupiter_orb,
                                      jup_orbelxyz=jup_orbelxyz,
                                      jup_mjd=jup_mjd,
                                      inc_saturn_orb=inc_saturn_orb,
                                      sat_orbelxyz=sat_orbelxyz,
                                      sat_mjd=sat_mjd,
                                      k_drop=k_drop,
                                      k_threshold=k_threshold)

    BaseClass = deterministic_signals.Deterministic(wf, name=name)

    class Dropout_PhysicalEphemerisSignal(BaseClass):
        signal_name = 'phys_ephem'
        signal_id = 'phys_ephem_' + name if name else 'phys_ephem'

        def __init__(self, psr):

            # not available for PINT yet
            if isinstance(psr, enterprise.pulsar.PintPulsar):
                msg = 'Physical Ephemeris model is not compatible with PINT '
                msg += 'at this time.'
                raise NotImplementedError(msg)

            super(Dropout_PhysicalEphemerisSignal, self).__init__(psr)

            if use_epoch_toas:
                # get quantization matrix and calculate daily average TOAs
                U, _ = utils.create_quantization_matrix(psr.toas, nmin=1)
                self.uinds = utils.quant2ind(U)
                avetoas = np.array([psr.toas[sc].mean() for sc in self.uinds])
                self._wf[''].add_kwarg(toas=avetoas)

                # interpolate ssb planet position vectors to avetoas
                planetssb = np.zeros((len(avetoas), 9, 3))
                for jj in range(9):
                    planetssb[:, jj, :] = np.array([
                        np.interp(avetoas, psr.toas, psr.planetssb[:, jj, aa])
                        for aa in range(3)
                    ]).T
                self._wf[''].add_kwarg(planetssb=planetssb)

                # Inteprolating the pulsar position vectors onto epoch TOAs
                pos_t = np.array([
                    np.interp(avetoas, psr.toas, psr.pos_t[:, aa])
                    for aa in range(3)
                ]).T
                self._wf[''].add_kwarg(pos_t=pos_t)

            # initialize delay
            self._delay = np.zeros(len(psr.toas))

        @signal_base.cache_call('delay_params')
        def get_delay(self, params):
            delay = self._wf[''](params=params)
            if use_epoch_toas:
                for slc, val in zip(self.uinds, delay):
                    self._delay[slc] = val
                return self._delay
            else:
                return delay

    return Dropout_PhysicalEphemerisSignal
    def test_physical_ephem_model(self):
        """Test physical ephemeris model"""

        if isinstance(self.psr, enterprise.pulsar.Tempo2Pulsar):
            # define signals with and without epoch TOAs
            eph1 = deterministic_signals.PhysicalEphemerisSignal(
                sat_orb_elements=True, model="orbel")
            eph2 = deterministic_signals.PhysicalEphemerisSignal(
                sat_orb_elements=True, use_epoch_toas=False, model="orbel")

            # initialize signals
            e1, e2 = eph1(self.psr), eph2(self.psr)

            # set parameters
            params = {
                "d_jupiter_mass":
                -8.561198198000628e-12,
                "d_neptune_mass":
                1.0251757860647059e-11,
                "d_saturn_mass":
                6.22114376130324e-12,
                "d_uranus_mass":
                -2.1157536169469958e-10,
                "frame_drift_rate":
                2.874659280396648e-10,
                "jup_orb_elements":
                np.array([
                    0.04140015, -0.03422412, 0.01165894, -0.03525219,
                    -0.00406852, 0.0421522
                ]),
                "sat_orb_elements":
                np.array([
                    -0.39701798, -0.13322608, -0.05025925, 0.36331171,
                    -0.17080321, 0.25093799
                ]),
            }

            # test against waveform and compare non-epoch and epoch TOA results
            d1 = e1.get_delay(params=params)
            d2 = e2.get_delay(params=params)

            (jup_mjd, jup_orbel,
             sat_orbel) = utils.get_planet_orbital_elements("orbel")

            d3 = utils.physical_ephem_delay(
                self.psr.toas,
                self.psr.planetssb,
                self.psr.pos_t,
                times=jup_mjd,
                jup_orbit=jup_orbel,
                sat_orbit=sat_orbel,
                **params,
            )

            msg1 = "Signal delay does not match function delay"
            assert np.allclose(d1, d3, rtol=1e-10), msg1
            msg2 = "epoch-TOA delay does not match full TOA delay"
            assert np.allclose(d1, d2, rtol=1e-10), msg2

            # test against pre-computed wafeform
            eph_wf = np.load(datadir + "/phys_ephem_1855_test.npy")
            msg = "Ephemeris delay does not match pre-computed values"
            assert np.allclose(d1, eph_wf, rtol=1e-10), msg

        # test PINT exception raising
        elif isinstance(self.psr, enterprise.pulsar.PintPulsar):
            with self.assertRaises(NotImplementedError) as context:
                eph1 = deterministic_signals.PhysicalEphemerisSignal(
                    sat_orb_elements=True)
                e1 = eph1(self.psr)

                msg = "Physical Ephemeris model is not compatible with PINT "
                msg += "at this time."
                self.assertEqual(msg, str(context.exception))
示例#5
0
    def test_physical_ephem_model(self):
        """Tests physical ephemeris model (which is implemented as a deterministic signal)
        four ways:

        - computed directly with :func:`enterprise.signals.utils.physical_ephem_delay`;
        - computed with :meth:`enterprise.signals.deterministic_signals.PhysicalEphemerisSignal.get_delay`
          with `use_epoch_toas=True` (the default), which reduces computation by evaluating ephemeris corrections
          once per measurement epoch, and then interpolating to the full `toas` vector;
        - computed with :meth:`enterprise.signals.deterministic_signals.PhysicalEphemerisSignal.get_delay`,
          setting `use_epoch_toas=False`;
        - loaded from a golden copy.
        """

        if isinstance(self.psr, enterprise.pulsar.Tempo2Pulsar):
            # define signals with and without epoch TOAs
            eph1 = deterministic_signals.PhysicalEphemerisSignal(
                sat_orb_elements=True, model="orbel")
            eph2 = deterministic_signals.PhysicalEphemerisSignal(
                sat_orb_elements=True, use_epoch_toas=False, model="orbel")

            # initialize signals
            e1, e2 = eph1(self.psr), eph2(self.psr)

            # set parameters
            params = {
                "d_jupiter_mass":
                -8.561198198000628e-12,
                "d_neptune_mass":
                1.0251757860647059e-11,
                "d_saturn_mass":
                6.22114376130324e-12,
                "d_uranus_mass":
                -2.1157536169469958e-10,
                "frame_drift_rate":
                2.874659280396648e-10,
                "jup_orb_elements":
                np.array([
                    0.04140015, -0.03422412, 0.01165894, -0.03525219,
                    -0.00406852, 0.0421522
                ]),
                "sat_orb_elements":
                np.array([
                    -0.39701798, -0.13322608, -0.05025925, 0.36331171,
                    -0.17080321, 0.25093799
                ]),
            }

            # test against waveform and compare non-epoch and epoch TOA results
            d1 = e1.get_delay(params=params)
            d2 = e2.get_delay(params=params)

            (jup_mjd, jup_orbel,
             sat_orbel) = utils.get_planet_orbital_elements("orbel")

            d3 = utils.physical_ephem_delay(
                self.psr.toas,
                self.psr.planetssb,
                self.psr.pos_t,
                times=jup_mjd,
                jup_orbit=jup_orbel,
                sat_orbit=sat_orbel,
                **params,
            )

            msg1 = "Signal delay does not match function delay"
            assert np.allclose(d1, d3, rtol=1e-10), msg1
            msg2 = "epoch-TOA delay does not match full TOA delay"
            assert np.allclose(d1, d2, rtol=1e-10), msg2

            # test against pre-computed wafeform
            eph_wf = np.load(datadir + "/phys_ephem_1855_test.npy")
            msg = "Ephemeris delay does not match pre-computed values"
            assert np.allclose(d1, eph_wf, rtol=1e-10), msg
def PhysicalEphemerisSignal(frame_drift_rate=parameter.Uniform(-1e-9, 1e-9)
                            ('frame_drift_rate'),
                            d_mercury_mass=parameter.Normal(
                                0, 1.66 - 10)('d_mercury_mass'),
                            d_venus_mass=parameter.Normal(
                                0, 2.45e-9)('d_venus_mass'),
                            d_mars_mass=parameter.Normal(
                                0, 3.23e-10)('d_mars_mass'),
                            d_jupiter_mass=parameter.Normal(
                                0, 1.54976690e-11)('d_jupiter_mass'),
                            d_saturn_mass=parameter.Normal(
                                0, 8.17306184e-12)('d_saturn_mass'),
                            d_uranus_mass=parameter.Normal(
                                0, 5.71923361e-11)('d_uranus_mass'),
                            d_neptune_mass=parameter.Normal(
                                0, 7.96103855e-11)('d_neptune_mass'),
                            jup_orb_elements=parameter.Uniform(
                                -0.05, 0.05, size=6)('jup_orb_elements'),
                            sat_orb_elements=parameter.Uniform(
                                -0.5, 0.5, size=6)('sat_orb_elements'),
                            model="setIII",
                            use_epoch_toas=True,
                            name=""):  # noqa: E125,E501
    """
    Class factory for physical ephemeris model signal.

    This function implements a physically motivated ephemeris delay model.
    It is parameterized by an overall ecliptic-z frame drift rate,
    by the masses of gas giants, by the six orbital elements of Jupiter,
    and by the six orbital elements of Saturn. All these contributions
    can be disabled individually (Saturn orbit corrections are disabled
    by default).

    .. note:: This signal is only compatible with a tempo2 Pulsar object.

    The user can implement their own priors (e.g., by setting
    frame_drift_rate = parameter.Uniform(-1e-10,1e-10)('frame_drift_rate')
    but we have set reasonable defaults (see below).

    :param frame_drift_rate:
        ecliptic z-drift rate in units of rad/year referred to offset 1/1/2010.
        Default prior is Uniform(-1e-9, 1e-9).

    :param d_jupiter_mass:
        Mass deviation of Jupiter in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 1.54976690e-11)

    :param d_saturn_mass:
        Mass deviation of Saturn in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 8.17306184e-12)

    :param d_uranus_mass:
        Mass deviation of Uranus in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 5.71923361e-11)

    :param d_neptune_mass:
        Mass deviation of Neptune in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 7.96103855e-11)

    :param jup_orb_elements:
        Jupiter orbital-element perturbation coefficients.
        Default prior is Uniform(-0.05, 0.05) for each element, appropriate
        for standard SVD basis.

    :param sat_orb_elements:
        Saturn orbital-element perturbations coefficient.
        Default prior is Uniform(-0.5, 0.5) for each element, appropriate
        for standard SVD basis.

    :param model:
        Sets the vector basis used by Jupiter and Saturn orbital-element
        perturbations. Currently can be set to 'orbel', 'orbel-v2', 'setIII'.
        Default: 'setIII'

    :param use_epoch_toas:
        Use interpolation from epoch to full TOAs. This option reduces
        computational cost for large multi-channel TOA data sets.
        Default: True
    """

    times, jup_orbit, sat_orbit = utils.get_planet_orbital_elements(model)

    wf = utils.physical_ephem_delay(
        frame_drift_rate=frame_drift_rate,
        d_mercury_mass=d_mercury_mass,
        d_venus_mass=d_venus_mass,
        d_mars_mass=d_mars_mass,
        d_jupiter_mass=d_jupiter_mass,
        d_saturn_mass=d_saturn_mass,
        d_uranus_mass=d_uranus_mass,
        d_neptune_mass=d_neptune_mass,
        jup_orb_elements=jup_orb_elements,
        sat_orb_elements=sat_orb_elements,
        times=times,
        jup_orbit=jup_orbit,
        sat_orbit=sat_orbit,
    )

    BaseClass = Deterministic(wf, name=name)

    class PhysicalEphemerisSignal(BaseClass):
        signal_name = "phys_ephem"
        signal_id = "phys_ephem_" + name if name else "phys_ephem"

        def __init__(self, psr):
            # not available for PINT yet
            if isinstance(psr, pulsar.PintPulsar):
                msg = "Physical Ephemeris model is not compatible with PINT "
                msg += "at this time."
                raise NotImplementedError(msg)

            super(PhysicalEphemerisSignal, self).__init__(psr)

            if use_epoch_toas:
                # get quantization matrix and calculate daily average TOAs
                U, _ = utils.create_quantization_matrix(psr.toas, nmin=1)
                self._uinds = utils.quant2ind(U)

                avetoas = np.array([psr.toas[sc].mean() for sc in self._uinds])
                self._avetoas = avetoas

                # interpolate ssb planet position vectors to avetoas
                planetssb = np.zeros((len(avetoas), 9, 3))
                for jj in range(9):
                    planetssb[:, jj, :] = np.array([
                        np.interp(avetoas, psr.toas, psr.planetssb[:, jj, aa])
                        for aa in range(3)
                    ]).T
                self._planetssb = planetssb

                # Interpolating the pulsar position vectors onto epoch TOAs
                pos_t = np.array([
                    np.interp(avetoas, psr.toas, psr.pos_t[:, aa])
                    for aa in range(3)
                ]).T
                self._pos_t = pos_t

            # initialize delay
            self._delay = np.zeros(len(psr.toas))

        # this defaults to all parameters
        @signal_base.cache_call("delay_params")
        def get_delay(self, params):
            if use_epoch_toas:
                delay = self._wf[""](toas=self._avetoas,
                                     planetssb=self._planetssb,
                                     pos_t=self._pos_t,
                                     params=params)

                for slc, val in zip(self._uinds, delay):
                    self._delay[slc] = val
                return self._delay
            else:
                delay = self._wf[""](params=params)
                return delay

    return PhysicalEphemerisSignal
示例#7
0
def PhysicalEphemerisSignal(
        frame_drift_rate=parameter.Uniform(-1e-9, 1e-9)('frame_drift_rate'),
        d_jupiter_mass=parameter.Normal(0, 1.54976690e-11)('d_jupiter_mass'),
        d_saturn_mass=parameter.Normal(0, 8.17306184e-12)('d_saturn_mass'),
        d_uranus_mass=parameter.Normal(0, 5.71923361e-11)('d_uranus_mass'),
        d_neptune_mass=parameter.Normal(0, 7.96103855e-11)('d_neptune_mass'),
        jup_orb_elements=parameter.Uniform(-0.05, 0.05,
                                           size=6)('jup_orb_elements'),
        sat_orb_elements=parameter.Uniform(-0.5, 0.5,
                                           size=6)('sat_orb_elements'),
        inc_jupiter_orb=True,
        inc_saturn_orb=False,
        use_epoch_toas=True,
        name=''):  # noqa: E125,E501
    """
    Class factory for physical ephemeris model signal.

    This function implements a physically motivated ephemeris delay model.
    It is parameterized by an overall frame drift rate, masses of gas giants,
    6 orbital elements of Jupiter (uses a PCA basis), and 6 orbital elements
    of Saturn (uses PCA basis).

    .. note:: This signal is only compatible with a tempo2 Pulsar object.

    The user can implement their own priors but we have set reasonable
    defaults.

    :param frame_drift_rate:
        ecliptic z-drift rate in units of rad/year referred to offset 1/1/2010.
        Default prior is Uniform(-1e-9, 1e-9).

    :param d_jupiter_mass:
        Mass deviation of jupiter in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 1.54976690e-11)

    :param d_saturn_mass:
        Mass deviation of saturn in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 8.17306184e-12)

    :param d_uranus_mass:
        Mass deviation of uranus in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 5.71923361e-11)

    :param d_neptune_mass:
        Mass deviation of neptune in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 7.96103855e-11)

    :param jup_orb_elements:
        Amplitudes of PCA basis of jupiter orbital elements including
        (1) semi-major axis
        (2) eccentricity
        (3) inclination
        (4) longitude of the ascending node
        (5) longitude of perihelion
        (6) mean longitude

        Default prior is Uniform(-0.05, 0.05) for each element.

    :param sat_orb_elements:
        Amplitudes of PCA basis of saturn orbital elements including
        (1) semi-major axis
        (2) eccentricity
        (3) inclination
        (4) longitude of the ascending node
        (5) longitude of perihelion
        (6) mean longitude

        Default prior is Uniform(-0.5, 0.5) for each element.

    :param inc_jupiter_orb:
        Boolean indicating whether or not to include jupiter's orbital
        elements as free parameters in model. Default: True

    :param inc_saturn_orb:
        Boolean indicating whether or not to include saturn's orbital
        elements as free parameters in model. Default: False

    :param use_epoch_toas:
        Use interpolation from epoch to full TOAs. This option reduces
        computational cost for large multi-channel TOA data sets.
        Default: True
    """

    # turn off saturn orbital element parameters if not including in signal
    if not inc_saturn_orb:
        sat_orb_elements = np.zeros(6)

    # define waveform
    jup_mjd, jup_orbelxyz, sat_mjd, sat_orbelxyz = (
        utils.get_planet_orbital_elements())
    wf = utils.physical_ephem_delay(frame_drift_rate=frame_drift_rate,
                                    d_jupiter_mass=d_jupiter_mass,
                                    d_saturn_mass=d_saturn_mass,
                                    d_uranus_mass=d_uranus_mass,
                                    d_neptune_mass=d_neptune_mass,
                                    jup_orb_elements=jup_orb_elements,
                                    sat_orb_elements=sat_orb_elements,
                                    inc_jupiter_orb=inc_jupiter_orb,
                                    jup_orbelxyz=jup_orbelxyz,
                                    jup_mjd=jup_mjd,
                                    inc_saturn_orb=inc_saturn_orb,
                                    sat_orbelxyz=sat_orbelxyz,
                                    sat_mjd=sat_mjd)

    BaseClass = Deterministic(wf, name=name)

    class PhysicalEphemerisSignal(BaseClass):
        signal_name = 'phys_ephem'
        signal_id = 'phys_ephem_' + name if name else 'phys_ephem'

        def __init__(self, psr):

            # not available for PINT yet
            if isinstance(psr, pulsar.PintPulsar):
                msg = 'Physical Ephemeris model is not compatible with PINT '
                msg += 'at this time.'
                raise NotImplementedError(msg)

            super(PhysicalEphemerisSignal, self).__init__(psr)

            if use_epoch_toas:
                # get quantization matrix and calculate daily average TOAs
                U, _ = utils.create_quantization_matrix(psr.toas, nmin=1)
                self._uinds = utils.quant2ind(U)

                avetoas = np.array([psr.toas[sc].mean() for sc in self._uinds])
                self._avetoas = avetoas

                # interpolate ssb planet position vectors to avetoas
                planetssb = np.zeros((len(avetoas), 9, 3))
                for jj in range(9):
                    planetssb[:, jj, :] = np.array([
                        np.interp(avetoas, psr.toas, psr.planetssb[:, jj, aa])
                        for aa in range(3)
                    ]).T
                self._planetssb = planetssb

                # Interpolating the pulsar position vectors onto epoch TOAs
                pos_t = np.array([
                    np.interp(avetoas, psr.toas, psr.pos_t[:, aa])
                    for aa in range(3)
                ]).T
                self._pos_t = pos_t

            # initialize delay
            self._delay = np.zeros(len(psr.toas))

        @signal_base.cache_call('delay_params')
        def get_delay(self, params):
            if use_epoch_toas:
                delay = self._wf[''](toas=self._avetoas,
                                     planetssb=self._planetssb,
                                     pos_t=self._pos_t,
                                     params=params)

                for slc, val in zip(self._uinds, delay):
                    self._delay[slc] = val
                return self._delay
            else:
                delay = self._wf[''](params=params)
                return delay

    return PhysicalEphemerisSignal
def PhysicalEphemerisSignal(
    frame_drift_rate=parameter.Uniform(-1e-9, 1e-9)('frame_drift_rate'),
    d_jupiter_mass=parameter.Normal(0, 1.54976690e-11)('d_jupiter_mass'),
    d_saturn_mass=parameter.Normal(0, 8.17306184e-12)('d_saturn_mass'),
    d_uranus_mass=parameter.Normal(0, 5.71923361e-11)('d_uranus_mass'),
    d_neptune_mass=parameter.Normal(0, 7.96103855e-11)('d_neptune_mass'),
    jup_orb_elements=parameter.Uniform(-0.05,0.05,size=6)('jup_orb_elements'),
    sat_orb_elements=parameter.Uniform(-0.5,0.5,size=6)('sat_orb_elements'),
    inc_jupiter_orb=True, inc_saturn_orb=False, use_epoch_toas=True,
    name=''):  # noqa: E125,E501

    """ Class factory for physical ephemeris model signal.

    This function implements a physically motivated ephemeris delay model.
    It is parameterized by an overall frame drift rate, masses of gas giants,
    6 orbital elements of Jupiter (uses a PCA basis), and 6 orbital elements
    of Saturn (uses PCA basis).

    .. note:: This signal is only compatible with a tempo2 Pulsar object.

    The user can implement their own priors but we have set reasonable
    defaults.

    :param frame_drift_rate:
        ecliptic z-drift rate in units of rad/year referred to offset 1/1/2010.
        Default prior is Uniform(-1e-9, 1e-9).

    :param d_jupiter_mass:
        Mass deviation of jupiter in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 1.54976690e-11)

    :param d_saturn_mass:
        Mass deviation of saturn in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 8.17306184e-12)

    :param d_uranus_mass:
        Mass deviation of uranus in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 5.71923361e-11)

    :param d_neptune_mass:
        Mass deviation of neptune in solar masses. Default prior taken from
        IAU mass measurement uncertainty - Normal(0, 7.96103855e-11)

    :param jup_orb_elements:
        Amplitudes of PCA basis of jupiter orbital elements including
        (1) semi-major axis
        (2) eccentricity
        (3) inclination
        (4) longitude of the ascending node
        (5) longitude of perihelion
        (6) mean longitude

        Default prior is Uniform(-0.05, 0.05) for each element.

    :param sat_orb_elements:
        Amplitudes of PCA basis of saturn orbital elements including
        (1) semi-major axis
        (2) eccentricity
        (3) inclination
        (4) longitude of the ascending node
        (5) longitude of perihelion
        (6) mean longitude

        Default prior is Uniform(-0.5, 0.5) for each element.

    :param inc_jupiter_orb:
        Boolean indicating whether or not to include jupiter's orbital
        elements as free parameters in model. Default: True

    :param inc_saturn_orb:
        Boolean indicating whether or not to include saturn's orbital
        elements as free parameters in model. Default: False

    :param use_epoch_toas:
        Use interprolation from epoch to full TOAs. This option reduces
        computational cost for large multi-channel TOA data sets.
        Default: True
    """

    # turn off saturn orbital element parameters if not including in signal
    if not inc_saturn_orb:
        sat_orb_elements = np.zeros(6)

    # define waveform
    jup_mjd, jup_orbelxyz, sat_mjd, sat_orbelxyz = (
        utils.get_planet_orbital_elements())
    wf = utils.physical_ephem_delay(frame_drift_rate=frame_drift_rate,
                                    d_jupiter_mass=d_jupiter_mass,
                                    d_saturn_mass=d_saturn_mass,
                                    d_uranus_mass=d_uranus_mass,
                                    d_neptune_mass=d_neptune_mass,
                                    jup_orb_elements=jup_orb_elements,
                                    sat_orb_elements=sat_orb_elements,
                                    inc_jupiter_orb=inc_jupiter_orb,
                                    jup_orbelxyz=jup_orbelxyz,
                                    jup_mjd=jup_mjd,
                                    inc_saturn_orb=inc_saturn_orb,
                                    sat_orbelxyz=sat_orbelxyz,
                                    sat_mjd=sat_mjd)

    BaseClass = Deterministic(wf, name=name)

    class PhysicalEphemerisSignal(BaseClass):
        signal_name = 'phys_ephem'
        signal_id = 'phys_ephem_' + name if name else 'phys_ephem'

        def __init__(self, psr):

            # not available for PINT yet
            if isinstance(psr, enterprise.pulsar.PintPulsar):
                msg = 'Physical Ephemeris model is not compatible with PINT '
                msg += 'at this time.'
                raise NotImplementedError(msg)

            super(PhysicalEphemerisSignal, self).__init__(psr)

            if use_epoch_toas:
                # get quantization matrix and calculate daily average TOAs
                U, _ = utils.create_quantization_matrix(psr.toas, nmin=1)
                self.uinds = utils.quant2ind(U)
                avetoas = np.array([psr.toas[sc].mean() for sc in self.uinds])
                self._wf[''].add_kwarg(toas=avetoas)

                # interpolate ssb planet position vectors to avetoas
                planetssb = np.zeros((len(avetoas), 9, 3))
                for jj in range(9):
                    planetssb[:, jj, :] = np.array([
                        np.interp(avetoas, psr.toas, psr.planetssb[:,jj,aa])
                        for aa in range(3)]).T
                self._wf[''].add_kwarg(planetssb=planetssb)

                # Inteprolating the pulsar position vectors onto epoch TOAs
                pos_t = np.array([np.interp(avetoas, psr.toas, psr.pos_t[:,aa])
                                  for aa in range(3)]).T
                self._wf[''].add_kwarg(pos_t=pos_t)

            # initialize delay
            self._delay = np.zeros(len(psr.toas))

        @base.cache_call('delay_params')
        def get_delay(self, params):
            delay = self._wf[''](params=params)
            if use_epoch_toas:
                for slc, val in zip(self.uinds, delay):
                    self._delay[slc] = val
                return self._delay
            else:
                return delay

    return PhysicalEphemerisSignal