示例#1
0
    def _get_coorbital_frame_spins_at_idx(self, chiA, chiB, omega, lNhat, phi, \
            idx):
        """ Computes PN spins and dynamics at a given idx.

        Inputs:
            chiA:       Dimless spin evolution of BhA in inertial frame.
            chiB:       Dimless spin evolution of BhB in inertial frame.
            omega:      Orbital frequency evolution in dimless units.
            lNhat:      Orbital angular momentum direction evolution.
            phi:        Orbital phase evolution.
            idx:        Index for output.

        Outputs (all are time series):
            chiA_at_idx_coorb:   Spin of BhA at idx, in coorbital frame.
            chiB_at_idx_coorb:   Spin of BhB at idx, in coorbital frame.
            quat_copr_at_idx:   Coprecessing frame quaternion at idx.
            phi_at_idx:         Orbital phase in the coprecessing frame at idx.
            omega_at_idx        Dimensionless orbital frequency at idx.

        The inertial frame is assumed to be aligned to the coorbital frame at
        the first index.
        """

        # Compute omega, inertial spins, angular momentum direction and orbital
        # phase at idx
        omega_at_idx = omega[idx]
        chiA_at_idx = chiA[idx]
        chiB_at_idx = chiB[idx]
        lNhat_at_idx = lNhat[idx]
        phi_at_idx = phi[idx]

        # Align the z-direction along orbital angular momentum direction
        # at idx. This moves us in to the coprecessing frame.
        quat_copr_at_idx = utils.alignVec_quat(lNhat_at_idx)
        chiA_at_idx_copr = utils.transformTimeDependentVector(
            np.array([quat_copr_at_idx]).T,
            np.array([chiA_at_idx]).T,
            inverse=1).T[0]
        chiB_at_idx_copr = utils.transformTimeDependentVector(
            np.array([quat_copr_at_idx]).T,
            np.array([chiB_at_idx]).T,
            inverse=1).T[0]

        # get coorbital frame spins at idx
        chiA_at_idx_coorb = utils.rotate_in_plane(chiA_at_idx_copr, phi_at_idx)
        chiB_at_idx_coorb = utils.rotate_in_plane(chiB_at_idx_copr, phi_at_idx)

        return chiA_at_idx_coorb, chiB_at_idx_coorb, quat_copr_at_idx, \
            phi_at_idx, omega_at_idx
示例#2
0
    def _evolve_spins(self, q, chiA0, chiB0, omega0, \
            return_spin_evolution=False, **kwargs):
        """ Evolves spins of the component BHs from an initial orbital
        frequency = omega0 until t=-100 M from the peak of the waveform.  If
        omega0 < omega_switch_IG, use PN to evolve the spins until
        t=t_sur_switch. Then evolves further with the NRSur7dq4 waveform model
        until t=-100M from the peak.

        Returns spins in the coorbital frame at t=-100M, as well as the
        coprecessing frame quaternion and orbital phase in the coprecessing
        frame at this time.

        If return_spin_evolution is given, also returns the PN and surrogate
        spin times series.
        """

        PN_approximant = kwargs.pop('PN_approximant', 'SpinTaylorT4')
        PN_dt = kwargs.pop('PN_dt', 0.1)
        PN_spin_order = kwargs.pop('PN_spin_order', 7)
        PN_phase_order = kwargs.pop('PN_phase_order', 7)
        # Initial guess for surrogate omega0, this should be large enough for
        # all q=6 cases
        omega_switch_IG = kwargs.pop('omega_switch_IG', 0.03)
        # The surrogate begins at -4300, use -4100 to be safe
        t_sur_switch = kwargs.pop('t_sur_switch', -4100)
        self._check_unused_kwargs(kwargs)

        # Load NRSur7dq4 if not previously loaded
        if self.nrsur is None:
            self._load_NRSur7dq4()

        # If omega0 is below the NRSur7dq4 initial guess frequency, we use PN
        # to evolve the spins. We get the initial spins and omega_init_sur such
        # that should go into the surrogate such that the inital time is
        # t_sur_switch.
        if omega0 < omega_switch_IG:
            chiA0_nrsur_coorb, chiB0_nrsur_coorb, quat0_nrsur_copr, \
                phi0_nrsur, omega_init_sur, chiA_PN, chiB_PN, omega_PN \
                = self._get_PN_spins_at_surrogate_start(PN_approximant, q, \
                omega0, chiA0, chiB0, PN_dt, PN_spin_order, PN_phase_order, \
                omega_switch_IG, t_sur_switch)

        # If omega0 >= omega_switch_IG, we evolve spins directly with NRSur7dq4
        # waveform model. We set the coprecessing frame quaternion to identity
        # and orbital phase to 0 at omega=omega0, hence the coprecessing frame
        # is the same as the inertial frame here.
        else:
            # Note that here we set omega_init_sur to omega0
            chiA0_nrsur_coorb, chiB0_nrsur_coorb, quat0_nrsur_copr, \
                phi0_nrsur, omega_init_sur, chiA_PN, chiB_PN, omega_PN \
                = chiA0, chiB0, [1,0,0,0], 0, omega0, None, None, None

        # Now evaluate the surrogate dynamics using PN spins at omega_init_sur
        quat_sur, orbphase_sur, chiA_copr_sur, chiB_copr_sur \
            = self.nrsur._sur_dimless.get_dynamics(q, chiA0_nrsur_coorb, \
            chiB0_nrsur_coorb, init_quat=quat0_nrsur_copr,
            init_orbphase=phi0_nrsur, omega_ref=omega_init_sur)

        # get data at time node where remnant fits are done
        dyn_times = self.nrsur._sur_dimless.tds
        nodeIdx = np.argmin(np.abs(dyn_times - self.fitnode_time))
        quat_fitnode = quat_sur.T[nodeIdx]
        orbphase_fitnode = orbphase_sur[nodeIdx]

        # get coorbital frame spins at the time node
        chiA_coorb_fitnode = utils.rotate_in_plane(chiA_copr_sur[nodeIdx],
                                                   orbphase_fitnode)
        chiB_coorb_fitnode = utils.rotate_in_plane(chiB_copr_sur[nodeIdx],
                                                   orbphase_fitnode)

        if return_spin_evolution:
            # Transform spins to the reference inertial frame
            chiA_inertial_sur = utils.transformTimeDependentVector(quat_sur, \
                    chiA_copr_sur.T).T
            chiB_inertial_sur = utils.transformTimeDependentVector(quat_sur, \
                    chiB_copr_sur.T).T
            spin_evolution = {
                't_sur': dyn_times,
                'chiA_sur': chiA_inertial_sur,
                'chiB_sur': chiB_inertial_sur,
                'orbphase_sur': orbphase_sur,
                'quat_sur': quat_sur,
                'omega_PN': omega_PN,
                'chiA_PN': chiA_PN,
                'chiB_PN': chiB_PN,
                'omega_init_sur': omega_init_sur,
            }
        else:
            spin_evolution = None

        return chiA_coorb_fitnode, chiB_coorb_fitnode, quat_fitnode, \
            orbphase_fitnode, spin_evolution
示例#3
0
    def _evolve_spins(self, q, chiA0, chiB0, omega0, PN_approximant, PN_dt,
                      PN_spin0, PN_phase0, omega0_nrsur):
        """ Evolves spins of the component BHs from an initial orbital
        frequency = omega0 until t=-100 M from the peak of the waveform.
        If omega0 < omega0_nrsur, use PN to evolve the spins until
        orbital frequency = omega0. Then evolves further with the NRSur7dq2
        waveform model until t=-100M from the peak.

        Assumes chiA0 and chiB0 are defined in the inertial frame defined
        at orbital frequency = omega0 as:
            The z-axis is along the Newtonian orbital angular momentum when the
                PN orbital frequency = omega0.
            The x-axis is along the line of separation from the smaller BH to
                the larger BH at this frequency.
            The y-axis completes the triad.

        Returns spins in the coorbital frame at t=-100M, as well as the
        coprecessing frame quaternion and orbital phase in the coprecessing
        frame at this time.
        """

        if omega0 < omega0_nrsur:
            # If omega0 is below the NRSur7dq2 start frequency, we use PN
            # to evolve the spins until orbital frequency = omega0_nrsur.

            # Note that we update omega0_nrsur here with the PN
            # frequency that was closest to the input omega0_nrsur.
            chiA0_nrsur_copr, chiB0_nrsur_copr, quat0_nrsur_copr, \
                phi0_nrsur, omega0_nrsur \
                = evolve_pn_spins(q, chiA0, chiB0, omega0,
                    omega0_nrsur, approximant=PN_approximant,
                    dt=PN_dt, spinO=PN_spin0,
                    phaseO=PN_phase0)
        else:
            # If omega0>= omega0_nrsur, we evolve spins directly with NRSur7dq2
            # waveform model. We set the coprecessing frame quaternion to
            # identity and orbital phase to 0 at omega=omega0, hence the
            # coprecessing frame is the same as the inertial frame here.

            # Note that we update omega0_nrsur here and set it to omega0
            chiA0_nrsur_copr, chiB0_nrsur_copr, quat0_nrsur_copr, \
                phi0_nrsur, omega0_nrsur \
                = chiA0, chiB0, [1,0,0,0], 0, omega0

        # Load NRSur7dq2 if needed
        if self.nrsur is None:
            self._load_NRSur7dq2()

        # evaluate NRSur7dq2 dynamics
        # We set allow_extrapolation=True always since we test param limits
        # independently
        quat, orbphase, chiA_copr, chiB_copr = self.nrsur.get_dynamics(
            q,
            chiA0_nrsur_copr,
            chiB0_nrsur_copr,
            init_quat=quat0_nrsur_copr,
            init_phase=phi0_nrsur,
            omega_ref=omega0_nrsur,
            allow_extrapolation=True)

        # get data at time node where remnant fits are done
        fitnode_time = -100
        nodeIdx = np.argmin(np.abs(self.nrsur.tds - fitnode_time))
        quat_fitnode = quat.T[nodeIdx]
        orbphase_fitnode = orbphase[nodeIdx]

        # get coorbital frame spins at the time node
        chiA_coorb_fitnode = utils.rotate_in_plane(chiA_copr[nodeIdx],
                                                   orbphase_fitnode)
        chiB_coorb_fitnode = utils.rotate_in_plane(chiB_copr[nodeIdx],
                                                   orbphase_fitnode)

        return chiA_coorb_fitnode, chiB_coorb_fitnode, quat_fitnode, \
            orbphase_fitnode