Difference in conventions from bbhx

1. reference phase

The reference phase is a convention used to fix the freedom of an arbitrary constant phase shift in the waveform, which corresponds to an arbitrary rotation of the binary system.

In pespace, we use waveforms generated following the convention of lalsimulation (see Appendix C in 2001.10914 for more details). While in bbhx, waveforms are generated with $\phi_\mathrm{ref}=0$ (see l.521 in bbhx/waveformbuild.py), the rotation is applied latter through the spin-weighted spherical harmonics in the computation of the response (see l.556 in bbhx/waveformbuild.py and l.582-583 in bbhx/cutils/src/Response.cu).

To obtain the same output, using $\phi^\mathrm{bbhx}\mathrm{ref} = \pi/2 - \phi^\mathrm{pespace}\mathrm{ref}$ as the input parameter.

2. Fourier transformation

To ensure the positive azimuthal modes are concentrated in the positive frequency branch, bbhx adopts a Fourier transform convention with the opposite sign to the commonly used one (see appendix A in 1806.10734).

While pespace uses the conventional definition of the Fourier transform. Thus there is a difference of global complex conjugation. It need to be noted that the waveform inputted also need to be conjugated accordingly.

3. Unit vectors of the constellation (potential bug?? # TODO)

The unit vector of each link is computed in l.101-127 in bbhx/cutils/src/Response.cu, where n1 is obtained by orbits->get_normal_unit_vec(t, 12), giving the vector between nodes 1 and 2. While in the computation of terms involving sinc and $\boldsymbol{n}_l \cdot \boldsymbol{H} \cdot \boldsymbol{n}_l$ (l.175-191), n1 is treated as the vector between nodes 2 and 3. Here we rigidly follow the computation in bbhx to verify whether remaining parts are all consistent, and leave this issue for future checks.

import copy

import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline

import taichi as ti
ti.init(
    arch=ti.cpu,
    default_fp=ti.f64,
    cpu_max_num_threads=1,
    offline_cache=False,
    debug=True,
)

from pespace.detector.antenna import InterferometerAntenna, FDResponseModelMarset2018
from pespace.detector.tdi import TDIChannelData, FDMichelsonConstantEqualArm
from pespace.detector.orbit import KaplerianHeliocentric
from tiwave.waveforms import IMRPhenomXAS

from bbhx.response.fastfdresponse import LISATDIResponse
from bbhx.waveformbuild import BBHWaveformFD
from  bbhx.waveforms.phenomhm import PhenomHMAmpPhase
import lal
import bilby
[Taichi] version 1.7.4, llvm 15.0.4, commit b4b956fd, linux, python 3.10.19
[Taichi] Starting on arch=x64
[I 02/05/26 15:53:45.322 196071] [shell.py:_shell_pop_print@23] Graphical python shell detected, using wrapped sys.stdout
No CuPy or GPU response available.
No CuPy
No CuPy or GPU PhenomHM module.
No CuPy or GPU interpolation available.
/tmp/ipykernel_196071/3853856849.py:24: UserWarning: Wswiglal-redir-stdio:

SWIGLAL standard output/error redirection is enabled in IPython.
This may lead to performance penalties. To disable locally, use:

with lal.no_swig_redirect_standard_output_error():
    ...

To disable globally, use:

lal.swig_redirect_standard_output_error(False)

Note however that this will likely lead to error messages from
LAL functions being either misdirected or lost when called from
Jupyter notebooks.

To suppress this warning, use:

import warnings
warnings.filterwarnings("ignore", "Wswiglal-redir-stdio")
import lal

  import lal
f_ref = 1e-4
f_min = 1e-4
f_max = 0.1
t_start = 0.0

channels = ("A", "E", "T")
delta_time = 5
num_tsamples = 2 ** np.ceil(np.log2(4 * lal.DAYJUL_SI / delta_time))
duration = num_tsamples * delta_time
print("sample num: ", num_tsamples)
print("duration: ", duration)

tdi_data = TDIChannelData()
tdi_data.set_fd_data_from_zero(
    channels,
    duration,
    delta_time,
    start_time=t_start,
    minimum_frequency=f_min,
    maximum_frequency=f_max,
)
sample num:  131072.0
duration:  655360.0
params = dict(
    total_mass=3e6,
    mass_ratio=0.6,
    chi_1=0.75,
    chi_2=0.62,
    luminosity_distance=56000.0,
    inclination=0.4,
    reference_phase=1.3,
    ecliptic_longitude=1.375,
    ecliptic_latitude=-1.2108,
    polarization=2.659,
    coalescence_time=0.0,
)
params = bilby.gw.conversion.generate_mass_parameters(params)
print(params)

waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_ref)
waveform_tiw.update_waveform(params)
f_peak_Hz = waveform_tiw.amplitude_coefficients[None].f_peak / waveform_tiw.source_parameters[None].M_sec  # fmt: skip
print("peak around (Hz): ", f_peak_Hz)

waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_peak_Hz)
waveform_tiw.update_waveform(params)
{'total_mass': 3000000.0, 'mass_ratio': 0.6, 'chi_1': 0.75, 'chi_2': 0.62, 'luminosity_distance': 56000.0, 'inclination': 0.4, 'reference_phase': 1.3, 'ecliptic_longitude': 1.375, 'ecliptic_latitude': -1.2108, 'polarization': 2.659, 'coalescence_time': 0.0, 'mass_1': 1875000.0, 'mass_2': 1125000.0, 'chirp_mass': 1256226.717491785, 'symmetric_mass_ratio': np.float64(0.234375)}
/home/nrui/disk_ext/workspace/tiwave/tiwave/waveforms/base_waveform.py:69: UserWarning: check_parameters is disable, make sure all parameters passed in are valid.
  warnings.warn(
peak around (Hz):  0.006847819521032306

make up a fake signal for comparision

amp = np.ones(tdi_data.data_info.frequency_series_length)
phi = np.zeros(tdi_data.data_info.frequency_series_length)
tf = np.ones(tdi_data.data_info.frequency_series_length)
h22 = amp * np.exp(1j * phi)

from tiwave.utils import ti_complex
harm_fac = ti.Struct.field({"plus": ti_complex, "cross": ti_complex}, shape=())
@ti.kernel
def compute_harmonic_factors():
    waveform_tiw.source_parameters[None].update_source_parameters(
        params["mass_1"],
        params["mass_2"],
        params["chi_1"],
        params["chi_2"],
        params["luminosity_distance"],
        params["inclination"],
        params["reference_phase"],
        f_ref,
    )
    waveform_tiw._set_harmonic_factors(harm_fac[None])  # depending the iota
compute_harmonic_factors()
harm_fac_np = harm_fac.to_numpy()

hp = harm_fac_np["plus"].view(np.complex128) * h22
hc = harm_fac_np["cross"].view(np.complex128) * h22
# hp = -1 * 0.125 * np.sqrt(5 / np.pi) * (1 + np.cos(params["inclination"]) ** 2) * h22
# hc = 1j * 0.125 * np.sqrt(5 / np.pi) * (2 * np.cos(params["inclination"])) * h22
hp = hp.view(np.float64).reshape(-1, 2)
hc = hc.view(np.float64).reshape(-1, 2)
wf_np = {
    "plus": hp,
    "cross": hc,
    "tf": tf,
}
wf_ti = ti.Struct.field(
    {
        "plus": ti_complex,
        "cross": ti_complex,
        "tf": ti.f64,
    },
    shape=tdi_data.frequency_samples.shape,
)
wf_ti.from_numpy(wf_np)
import taichi.math as tm

from pespace.utils.utils import (
    get_polarization_tensor_ssb,
    get_gw_propagation_unit_vector,
    sinc,
    ti_complex,
)
from pespace.utils.constants import *


class ResponseModelBBHxConvention(FDResponseModelMarset2018):

    @ti.kernel
    def update_single_link_response(
        self,
        waveform: ti.template(),
        lam: ti.f64,
        beta: ti.f64,
        psi: ti.f64,
        tc: ti.f64,
    ):
        pol_tensor = get_polarization_tensor_ssb(lam, beta, psi)  # matrix: 3*3
        k = get_gw_propagation_unit_vector(lam, beta)  # vector: 3

        for i in self.detector.single_link_response:
            fi = self.detector.tdi_data.frequency_samples[i]
            cexp_tshift = tm.cexp(ti_complex([0.0, -2.0 * PI * fi * tc]))
            hp = tm.cmul(waveform[i].plus, cexp_tshift)
            hc = tm.cmul(waveform[i].cross, cexp_tshift)
            tf = waveform[i].tf + tc
            constellation_vectors = self.detector.orbit_model.get_constellation_vectors(tf)  # fmt: skip

            # n1: unit vector of 2 -> 3
            n1_h_n1 = (
                constellation_vectors.n1
                @ pol_tensor.plus
                @ constellation_vectors.n1
                * hp
                + constellation_vectors.n1
                @ pol_tensor.cross
                @ constellation_vectors.n1
                * hc
            )  # complex number
            # n2: unit vector of 3 -> 1
            n2_h_n2 = (
                constellation_vectors.n2
                @ pol_tensor.plus
                @ constellation_vectors.n2
                * hp
                + constellation_vectors.n2
                @ pol_tensor.cross
                @ constellation_vectors.n2
                * hc
            )  # complex number
            # n3: unit vector of 1 -> 2
            n3_h_n3 = (
                constellation_vectors.n3
                @ pol_tensor.plus
                @ constellation_vectors.n3
                * hp
                + constellation_vectors.n3
                @ pol_tensor.cross
                @ constellation_vectors.n3
                * hc
            )  # complex number

            k_n1 = k @ constellation_vectors.n1  # scalar
            k_n2 = k @ constellation_vectors.n2  # scalar
            k_n3 = k @ constellation_vectors.n3  # scalar

            k_x1_x2 = k @ (
                constellation_vectors.x1 + constellation_vectors.x2
            )  # scalar
            k_x2_x3 = k @ (
                constellation_vectors.x2 + constellation_vectors.x3
            )  # scalar
            k_x3_x1 = k @ (
                constellation_vectors.x3 + constellation_vectors.x1
            )  # scalar

            pi_f_L = PI * fi * self.detector.orbit_model.arm_length_sec  # scalar
            sinc32 = sinc(pi_f_L * (1.0 - k_n1))  # scalar
            sinc23 = sinc(pi_f_L * (1.0 + k_n1))  # scalar
            sinc13 = sinc(pi_f_L * (1.0 - k_n2))  # scalar
            sinc31 = sinc(pi_f_L * (1.0 + k_n2))  # scalar
            sinc21 = sinc(pi_f_L * (1.0 - k_n3))  # scalar
            sinc12 = sinc(pi_f_L * (1.0 + k_n3))  # scalar

            common_exp = -PI * fi * ti_complex([0.0, 1.0])  # ti_complex
            exp12 = tm.cexp(
                common_exp * (self.detector.orbit_model.arm_length_sec + k_x1_x2)
            )  # ti_complex
            exp23 = tm.cexp(
                common_exp * (self.detector.orbit_model.arm_length_sec + k_x2_x3)
            )  # ti_complex
            exp31 = tm.cexp(
                common_exp * (self.detector.orbit_model.arm_length_sec + k_x3_x1)
            )  # ti_complex

            prefactor = -pi_f_L * ti_complex([0.0, 1.0])  # ti_complex

            # self.detector.single_link_response[i].link12 = sinc12 * tm.cmul(
            #     tm.cmul(prefactor, n3_h_n3), exp12
            # )  # ti_complex
            # self.detector.single_link_response[i].link21 = sinc21 * tm.cmul(
            #     tm.cmul(prefactor, n3_h_n3), exp12
            # )  # ti_complex
            # self.detector.single_link_response[i].link23 = sinc23 * tm.cmul(
            #     tm.cmul(prefactor, n1_h_n1), exp23
            # )  # ti_complex
            # self.detector.single_link_response[i].link32 = sinc32 * tm.cmul(
            #     tm.cmul(prefactor, n1_h_n1), exp23
            # )  # ti_complex
            # self.detector.single_link_response[i].link31 = sinc31 * tm.cmul(
            #     tm.cmul(prefactor, n2_h_n2), exp31
            # )  # ti_complex
            # self.detector.single_link_response[i].link13 = sinc13 * tm.cmul(
            #     tm.cmul(prefactor, n2_h_n2), exp31
            # )  # ti_complex

            self.detector.single_link_response[i].link12 = sinc13 * tm.cmul(
                tm.cmul(prefactor, n2_h_n2), exp12
            )  # ti_complex
            self.detector.single_link_response[i].link21 = sinc31 * tm.cmul(
                tm.cmul(prefactor, n2_h_n2), exp12
            )  # ti_complex
            self.detector.single_link_response[i].link23 = sinc21 * tm.cmul(
                tm.cmul(prefactor, n3_h_n3), exp23
            )  # ti_complex
            self.detector.single_link_response[i].link32 = sinc12 * tm.cmul(
                tm.cmul(prefactor, n3_h_n3), exp23
            )  # ti_complex
            self.detector.single_link_response[i].link31 = sinc32 * tm.cmul(
                tm.cmul(prefactor, n1_h_n1), exp31
            )  # ti_complex
            self.detector.single_link_response[i].link13 = sinc23 * tm.cmul(
                tm.cmul(prefactor, n1_h_n1), exp31
            )  # ti_complex
response_model = ResponseModelBBHxConvention()
tdi_combination = FDMichelsonConstantEqualArm(generation="2.0", orthogonal=True)
orbit_model = KaplerianHeliocentric(2.5e9, 0.0, 0.0)

lisa_bbhx_convention = InterferometerAntenna(
    name="lisa_bbhx_convention",
    tdi_data=tdi_data,
    orbit_model=orbit_model,
    response_model=response_model,
    tdi_combination=tdi_combination,
)

lisa_bbhx_convention.update_detector_response(
    wf_ti,
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    0.0,
    )

chan1_pespace = lisa_bbhx_convention.tdi_response_numpy["A"]
chan2_pespace = lisa_bbhx_convention.tdi_response_numpy["E"]
chan3_pespace = lisa_bbhx_convention.tdi_response_numpy["T"]
freqs = copy.deepcopy(tdi_data.data_info.frequency_samples_array)
response_kwargs = dict(TDItag="AET", tdi2=True)
response_bbhx = LISATDIResponse(**response_kwargs)

# due to the definition of FT, the phase differs by a conjugation
response_bbhx(
    freqs,
    params["inclination"],
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    np.pi / 2,
    int(len(freqs)),
    modes=[(2, 2)],
    phase=-phi,
    tf=tf,
)
chan1_bbhx = response_bbhx.transferL1[0][0] * h22.conjugate()
chan2_bbhx = response_bbhx.transferL2[0][0] * h22.conjugate()
chan3_bbhx = response_bbhx.transferL3[0][0] * h22.conjugate()
# abs
plt.figure()
plt.loglog(freqs, np.abs(chan1_pespace), label="A (pespace)")
plt.loglog(freqs, np.abs(chan1_bbhx), linestyle='dashed', label="chan1 (bbhx)")
plt.title("abs(chan1)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan2_pespace), label="E (pespace)")
plt.loglog(freqs, np.abs(chan2_bbhx), linestyle='dashed', label="chan2 (bbhx)")
plt.title("abs(chan2)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan3_pespace), label="T (pespace)")
plt.loglog(freqs, np.abs(chan3_bbhx), linestyle='dashed', label="chan3 (bbhx)")
plt.title("abs(chan3)")
plt.legend()

# real part
plt.figure()
plt.semilogx(freqs, chan1_pespace.real, label="A.real (pespace)")
plt.semilogx(freqs, chan1_bbhx.real, linestyle='dashed', label="chan1.real (bbhx)")
plt.title("chan1 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan2_pespace.real, label="E.real (pespace)")
plt.semilogx(freqs, chan2_bbhx.real, linestyle='dashed', label="chan2.real (bbhx)")
plt.title("chan2 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan3_pespace.real, label="T.real (pespace)")
plt.semilogx(freqs, chan3_bbhx.real, linestyle='dashed', label="chan3.real (bbhx)")
plt.title("chan3 real")
plt.legend()

# imag part
plt.figure()
plt.semilogx(freqs, -chan1_pespace.imag, label="A.imag (pespace)")
plt.semilogx(freqs, chan1_bbhx.imag, linestyle='dashed', label="chan1.imag (bbhx)")
plt.title("chan1 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, -chan2_pespace.imag, label="E.imag (pespace)")
plt.semilogx(freqs, chan2_bbhx.imag, linestyle='dashed', label="chan2.imag (bbhx)")
plt.title("chan2 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, -chan3_pespace.imag, label="T.imag (pespace)")
plt.semilogx(freqs, chan3_bbhx.imag, linestyle='dashed', label="chan3.imag (bbhx)")
plt.title("chan3 imag")
plt.legend()
<matplotlib.legend.Legend at 0x7f81d9782b90>
../_images/12284f55168b9bc3540cfd117af62a689fa2b9998ba58cf16925a4246d2331cd.png ../_images/cacffd9cfa03158f9604073c381adf1ea2b4c6ba108062356be4fb8a90c51009.png ../_images/b61f224753d6e0dee848b580ba47913e0224617a2f1e1b13d577232c97816d9c.png ../_images/321f37e15c3b401c5212531f5d178c0cb1063aae9f94c165639a9196f538e126.png ../_images/3fdabc8e561413f7c07e97ec1d37497b87c2418baf062e9ef21102c2b1826644.png ../_images/afb7f7696f7098717d66710110d90e6b04d1a437394dc472adbf3f8899a8e23a.png ../_images/477185f6109b795b09f5760a32dcd25c97b31d5ce353ce69e1b4a4dee73372f0.png ../_images/925301fa72c9de1a15de525ad6c53cae947630b3de006f07f70a26d7263b5fd0.png ../_images/c8626bd7f118afe3ac36d691b68ca66c608d3434c766f46e66aacaa5ff1d7105.png

use a signal of GW from CBC

# waveform from bbhx
wf_gen_bbhx = PhenomHMAmpPhase(run_phenomd=True)
wf_gen_bbhx(
    params['mass_1'],
    params['mass_2'],
    params['chi_1'],
    params['chi_2'],
    params['luminosity_distance']*1e6*lal.PC_SI,
    0.0,
    f_peak_Hz,
    0.0,
    len(freqs),
    freqs=freqs,
    modes=[(2,2)], 
    direct=True,
)
# waveform from tiwave
params['reference_phase'] = 0.0
print(params)
waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_peak_Hz, return_form='amplitude_phase')
waveform_tiw.update_waveform(params)
{'total_mass': 3000000.0, 'mass_ratio': 0.6, 'chi_1': 0.75, 'chi_2': 0.62, 'luminosity_distance': 56000.0, 'inclination': 0.4, 'reference_phase': 0.0, 'ecliptic_longitude': 1.375, 'ecliptic_latitude': -1.2108, 'polarization': 2.659, 'coalescence_time': 0.0, 'mass_1': 1875000.0, 'mass_2': 1125000.0, 'chirp_mass': 1256226.717491785, 'symmetric_mass_ratio': np.float64(0.234375)}
plt.figure()
plt.loglog(freqs, wf_gen_bbhx.amp[0][0], label='amp (bbhx)')
plt.loglog(freqs, waveform_tiw.waveform_container_numpy["amplitude"], linestyle='dashed', label='amp (tiwave)')
plt.legend()

plt.figure()
plt.semilogx(freqs, wf_gen_bbhx.phase[0][0], label='phase (bbhx)')
plt.semilogx(freqs, -waveform_tiw.waveform_container_numpy["phase"], linestyle='dashed', label='phase (tiwave)')
plt.legend()

plt.figure()
plt.semilogx(freqs, wf_gen_bbhx.tf[0][0], label='tf (bbhx)')
plt.semilogx(freqs, waveform_tiw.waveform_container_numpy["tf"], linestyle='dashed', label='tf (tiwave)')
plt.legend()
<matplotlib.legend.Legend at 0x7f81d7f17e80>
../_images/84de234578307884ca20dba35ee1958d36f933830c57f4939dccf4d58da993e1.png ../_images/43b93ec779b2c8d9e6adb22002b109d8d3925f5303971eca8086370c102932f6.png ../_images/36c355cdcd8c1f109ebd683c7478a1034c731d532a71cebd4c20f9eb016d1f9f.png
params['reference_phase'] = 1.3

response_bbhx = LISATDIResponse(TDItag="AET", tdi2=True)
# Note the phase differs by a conjugation due to the definition of FT
amp_phase = waveform_tiw.waveform_container_numpy

response_bbhx(
    freqs,
    params["inclination"],
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    np.pi / 2-params['reference_phase'],
    len(freqs),
    modes=[(2, 2)],
    phase=-amp_phase['phase'],
    tf=amp_phase['tf'],
    direct=True,
)
chan1_bbhx = response_bbhx.transferL1[0][0] * amp_phase['amplitude'] * np.exp(-1j*amp_phase['phase'])
chan2_bbhx = response_bbhx.transferL2[0][0] * amp_phase['amplitude'] * np.exp(-1j*amp_phase['phase'])
chan3_bbhx = response_bbhx.transferL3[0][0] * amp_phase['amplitude'] * np.exp(-1j*amp_phase['phase'])
waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_peak_Hz)
waveform_tiw.update_waveform(params)
lisa_bbhx_convention.update_detector_response(
    waveform_tiw.waveform_container,
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    0.0,
    )
# abs
plt.figure()
plt.loglog(freqs, np.abs(chan1_bbhx), label="chan1 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["A"]), linestyle='dashed', label="A (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan1)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan2_bbhx), label="chan2 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["E"]), linestyle='dashed', label="E (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan2)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan3_bbhx), label="chan3 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["T"]), linestyle='dashed', label="T (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan3)")
plt.legend()

# real part
plt.figure()
plt.semilogx(freqs, chan1_bbhx.real, label="chan1.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["A"].real, linestyle='dashed', label="A.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan1 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan2_bbhx.real, label="chan2.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["E"].real, linestyle='dashed', label="E.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan2 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan3_bbhx.real, label="chan3.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["T"].real, linestyle='dashed', label="T.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan3 real")
plt.legend()

# imag part
plt.figure()
plt.semilogx(freqs, chan1_bbhx.imag, label="chan1.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["A"].imag, linestyle='dashed', label="A.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan1 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan2_bbhx.imag, label="chan2.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["E"].imag, linestyle='dashed', label="E.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan2 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan3_bbhx.imag, label="chan3.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["T"].imag, linestyle='dashed', label="T.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan3 imag")
plt.legend()
<matplotlib.legend.Legend at 0x7f81e01543a0>
../_images/0545a11ed499fa6676007a6719daf83c9139a0df9fbfd6d69e775a7b4504b1e2.png ../_images/f6061fe5103dcf6a66b7e4044da99eaabea0e64e41bc57089388b489ceb03f45.png ../_images/d0ccd159fed189e23f76b5a850994e942b27397343e4bf0cbbd283eefee6ea5b.png ../_images/02bc1185dffa0cc0b1f3a8c96fe9b35d48128c2fe05e19287bc66e228a3799e5.png ../_images/4493a5bbea06f74c903a5d6e37edce2ae7ab1e69543a0b95f70839a32197921e.png ../_images/999af529e001c10c12eb22b52d0545ab6df90cb18345335f7a5aeea9d9a48897.png ../_images/e49c0b5673a2e4bdb5c1fd2885e982f747835ea3e3b5b85e7a5c1cb28de0fe49.png ../_images/a933a5bef39e9f9c2f9380a550bce3b34aef8cb4c9c8e7c3e0e709c7a0bd29f8.png ../_images/aeccbc624e57dca5ee3fb336d693ba64b449582cf6c9a6e0d350406f1b051e1a.png

check the setting of coalescence time

before_tc = 0.8 * duration
after_tc = 0.2 * duration
tc = t_start + before_tc
print("tc: ", tc)
params['coalescence_time'] = tc

# waveform from bbhx
wf_gen_bbhx = PhenomHMAmpPhase(run_phenomd=True)
wf_gen_bbhx(
    params['mass_1'],
    params['mass_2'],
    params['chi_1'],
    params['chi_2'],
    params['luminosity_distance']*1e6*lal.PC_SI,
    0.0,
    f_peak_Hz,
    params['coalescence_time'],
    len(freqs),
    freqs=freqs,
    modes=[(2,2)], 
    direct=True,
)

# waveform from tiwave
params['reference_phase'] = 0.0
print(params)
waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_peak_Hz, return_form='amplitude_phase')
waveform_tiw.update_waveform(params)

# Note the phase differs by a conjugation due to the definition of FT
amp_phase = copy.deepcopy(waveform_tiw.waveform_container_numpy)
amp_phase['phase'] = -(amp_phase['phase'] - 2*np.pi*freqs*tc)
amp_phase['tf'] = amp_phase['tf'] + tc

plt.figure()
plt.loglog(freqs, wf_gen_bbhx.amp[0][0], label='amp (bbhx)')
plt.loglog(freqs, amp_phase['amplitude'], linestyle='dashed', label='amp (tiwave)')
plt.legend()

plt.figure()
plt.semilogx(freqs, wf_gen_bbhx.phase[0][0], label='phase (bbhx)')
plt.semilogx(freqs, amp_phase['phase'], linestyle='dashed', label='phase (tiwave)')
plt.legend()

plt.figure()
plt.semilogx(freqs, wf_gen_bbhx.tf[0][0], label='tf (bbhx)')
plt.semilogx(freqs, amp_phase['tf'], linestyle='dashed', label='tf (tiwave)')
plt.legend()
tc:  524288.0
{'total_mass': 3000000.0, 'mass_ratio': 0.6, 'chi_1': 0.75, 'chi_2': 0.62, 'luminosity_distance': 56000.0, 'inclination': 0.4, 'reference_phase': 0.0, 'ecliptic_longitude': 1.375, 'ecliptic_latitude': -1.2108, 'polarization': 2.659, 'coalescence_time': np.float64(524288.0), 'mass_1': 1875000.0, 'mass_2': 1125000.0, 'chirp_mass': 1256226.717491785, 'symmetric_mass_ratio': np.float64(0.234375)}
<matplotlib.legend.Legend at 0x7f81d992c460>
../_images/84de234578307884ca20dba35ee1958d36f933830c57f4939dccf4d58da993e1.png ../_images/e26441b2da54316f093a5d7a9f9bf5fb7203338296a29bdbf50bbd8e6a956bb3.png ../_images/76005fae010742dcb75f6a0eb2c1c1361c99cd862f86e751aa0d9548b5e9fd40.png
params['reference_phase'] = 1.3

response_bbhx = LISATDIResponse(TDItag="AET", tdi2=True)
response_bbhx(
    freqs,
    params["inclination"],
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    np.pi/2-params['reference_phase'],
    len(freqs),
    modes=[(2, 2)],
    phase=amp_phase['phase'],
    tf=amp_phase['tf'],
    direct=True,
    adjust_phase=False, # Note the input phase will be adjusted in-place by default!
)
chan1_bbhx = response_bbhx.transferL1[0][0] * amp_phase['amplitude'] * np.exp(1j*amp_phase['phase'])
chan2_bbhx = response_bbhx.transferL2[0][0] * amp_phase['amplitude'] * np.exp(1j*amp_phase['phase'])
chan3_bbhx = response_bbhx.transferL3[0][0] * amp_phase['amplitude'] * np.exp(1j*amp_phase['phase'])

waveform_tiw = IMRPhenomXAS(tdi_data.frequency_samples, f_peak_Hz)
waveform_tiw.update_waveform(params)
lisa_bbhx_convention.update_detector_response(
    waveform_tiw.waveform_container,
    params["ecliptic_longitude"],
    params["ecliptic_latitude"],
    params["polarization"],
    params['coalescence_time'],
    )
# abs
plt.figure()
plt.loglog(freqs, np.abs(chan1_bbhx), label="chan1 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["A"]), linestyle='dashed', label="A (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan1)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan2_bbhx), label="chan2 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["E"]), linestyle='dashed', label="E (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan2)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(chan3_bbhx), label="chan3 (bbhx)")
plt.loglog(freqs, np.abs(lisa_bbhx_convention.tdi_response_numpy["T"]), linestyle='dashed', label="T (pespace)")
plt.xlim(f_min, f_max)
plt.title("abs(chan3)")
plt.legend()

# real part
plt.figure()
plt.semilogx(freqs, chan1_bbhx.real, label="chan1.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["A"].real, linestyle='dashed', label="A.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan1 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan2_bbhx.real, label="chan2.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["E"].real, linestyle='dashed', label="E.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan2 real")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan3_bbhx.real, label="chan3.real (bbhx)")
plt.semilogx(freqs, lisa_bbhx_convention.tdi_response_numpy["T"].real, linestyle='dashed', label="T.real (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan3 real")
plt.legend()

# imag part
plt.figure()
plt.semilogx(freqs, chan1_bbhx.imag, label="chan1.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["A"].imag, linestyle='dashed', label="A.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan1 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan2_bbhx.imag, label="chan2.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["E"].imag, linestyle='dashed', label="E.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan2 imag")
plt.legend()

plt.figure()
plt.semilogx(freqs, chan3_bbhx.imag, label="chan3.imag (bbhx)")
plt.semilogx(freqs, -lisa_bbhx_convention.tdi_response_numpy["T"].imag, linestyle='dashed', label="T.imag (pespace)")
plt.xlim(f_min, f_max)
plt.title("chan3 imag")
plt.legend()
<matplotlib.legend.Legend at 0x7f81dfb52a70>
../_images/9c340c7c1e9e83a75a9fb404e2ec2333582aa19ee38d72b52a8ec437959552b5.png ../_images/93e2ad69f3a1b0a07fabe423b1c1c254fff45678407a555e0c0557e3b9a3ff6f.png ../_images/18732f213ac970fd4b531975d5f67360a6eee93221e1ff5710456eb101f0fc36.png ../_images/5ec9c3de90db7984eeb708628c799d8ee380e3415a4e4acb462e253a0d0ccc6b.png ../_images/3ba0f950fe8dced13f63da71e4ace79df47a8aefce85f263ee580182ad70a55c.png ../_images/bc2c595086058887a038ddfefcfe8e91e51f6cf5bddc15fdbff059058176657f.png ../_images/a9ecea548211a2e93dbd87f5cdd9ca1ee696642c79139f1f3b77c5b1a972ebc2.png ../_images/ce677233b40d1ae934623446a0301a70cabb5c71a085a990211b0e185941e420.png ../_images/3ac14913cc101121793ec1f807d83a79538617e1962a074cad88863b5b229451.png
# abs
plt.figure()
plt.loglog(freqs, np.abs(np.conj(chan1_bbhx) - lisa_bbhx_convention.tdi_response_numpy["A"])/np.abs(chan1_bbhx), label="A rel_diff")
plt.xlim(f_min, f_max)
plt.title("abs(chan1)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(np.conj(chan2_bbhx) - lisa_bbhx_convention.tdi_response_numpy["E"])/np.abs(chan2_bbhx), label="E rel_diff")
plt.xlim(f_min, f_max)
plt.title("abs(chan2)")
plt.legend()

plt.figure()
plt.loglog(freqs, np.abs(np.conj(chan3_bbhx) - lisa_bbhx_convention.tdi_response_numpy["T"])/np.abs(chan3_bbhx), label="T rel_diff")
plt.xlim(f_min, f_max)
plt.title("abs(chan3)")
plt.legend()
/tmp/ipykernel_196071/3241018105.py:3: RuntimeWarning: divide by zero encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan1_bbhx) - lisa_bbhx_convention.tdi_response_numpy["A"])/np.abs(chan1_bbhx), label="A rel_diff")
/tmp/ipykernel_196071/3241018105.py:3: RuntimeWarning: invalid value encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan1_bbhx) - lisa_bbhx_convention.tdi_response_numpy["A"])/np.abs(chan1_bbhx), label="A rel_diff")
/tmp/ipykernel_196071/3241018105.py:9: RuntimeWarning: divide by zero encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan2_bbhx) - lisa_bbhx_convention.tdi_response_numpy["E"])/np.abs(chan2_bbhx), label="E rel_diff")
/tmp/ipykernel_196071/3241018105.py:9: RuntimeWarning: invalid value encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan2_bbhx) - lisa_bbhx_convention.tdi_response_numpy["E"])/np.abs(chan2_bbhx), label="E rel_diff")
/tmp/ipykernel_196071/3241018105.py:15: RuntimeWarning: divide by zero encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan3_bbhx) - lisa_bbhx_convention.tdi_response_numpy["T"])/np.abs(chan3_bbhx), label="T rel_diff")
/tmp/ipykernel_196071/3241018105.py:15: RuntimeWarning: invalid value encountered in divide
  plt.loglog(freqs, np.abs(np.conj(chan3_bbhx) - lisa_bbhx_convention.tdi_response_numpy["T"])/np.abs(chan3_bbhx), label="T rel_diff")
<matplotlib.legend.Legend at 0x7f81dc013df0>
../_images/eeb7028ba84833dba3534ff8474be587774fa14aa91ea9bf86dd0085df63ef2b.png ../_images/ed480f3f0b0fbc3ee9a62527a3273d9e9a3578db02b7b3a9187d9d3bd20a52e9.png ../_images/a2bd3ef96b065f3de271ee9ee9438ef1bc0cb823b08eef19f6be3d9609e93b3c.png