Scattering Angles from EOB Data

This tutorial demonstrates how to compute scattering angles from hyperbolic encounters using Effective One Body (EOB) waveforms.

In hyperbolic encounters, two compact objects approach each other with sufficient energy to scatter rather than merge. The scattering angle quantifies the deflection of the trajectory.

Setup

First, we import the necessary modules:

%matplotlib inline
%config InlineBackend.figure_format = 'retina'

import PyART.models.teob as teob
from PyART.analysis.scattering_angle import ScatteringAngle
import matplotlib.pyplot as plt 
from PyART.utils.utils import D1
import numpy as np
WARNING: TEOBResumS not installed.

Define Configurations

We’ll test several different impact parameters (b) to see how they affect the scattering angle. Each configuration has:

  • b: impact parameter

  • chi1, chi2: dimensionless spins

  • E0: initial energy

  • p: dimensionless momentum parameter

configurations = [
    {'b': 9.678, 'chi1':0., 'chi2':0., 'E0':1.0226, 'p':0.11456439, 'eob':None, 'scat':None},
    {'b':10.000, 'chi1':0., 'chi2':0., 'E0':1.0226, 'p':0.11456439, 'eob':None, 'scat':None},
    {'b':11.000, 'chi1':0., 'chi2':0., 'E0':1.0226, 'p':0.11456439, 'eob':None, 'scat':None},
    {'b':12.000, 'chi1':0., 'chi2':0., 'E0':1.0226, 'p':0.11456439, 'eob':None, 'scat':None},
    {'b':13.000, 'chi1':0., 'chi2':0., 'E0':1.0226, 'p':0.11456439, 'eob':None, 'scat':None},
]
n_conf = len(configurations)

# Initial separation and symmetric mass ratio
r0 = 100
nu = 0.25

Generate EOB Data and Compute Scattering Angles

For each configuration, we:

  1. Set up the EOB parameters

  2. Generate the EOB waveform

  3. Compute the scattering angle (if not a capture)

cutoff_min = 25
for i in range(n_conf):
    conf = configurations[i]
    J0 = conf['p']*conf['b']/nu
    
    eobpars = teob.CreateDict(r_hyp=r0, H_hyp=conf['E0'], J_hyp=J0, q=1, 
                             chi1z=conf['chi1'], chi2z=conf['chi2'])
    eob = teob.Waveform_EOB(pars=eobpars)
    conf['eob']  = eob
    E_final = eob.dyn['E'][-1]
    
    print(f"Configuration {i+1}:")
    print(f"  Impact parameter b = {conf['b']:.3f}")
    print(f"  E0, J0 = {conf['E0']:.5f}, {J0:.5f}")
    print(f"  Final energy = {E_final:.5f}")
    
    if E_final > 1:
        # Scattering event (not a capture)
        scat = ScatteringAngle(puncts=eob.dyn, nmin=2, nmax=10, n_extract=None,
                               hypfit=True,
                               r_cutoff_in_low=cutoff_min,  r_cutoff_in_high=eob.dyn['r'][0],
                               r_cutoff_out_low=cutoff_min, r_cutoff_out_high=None, verbose=False)
        conf['scat'] = scat
        print(f"  Scattering angle χ = {scat.chi:.2f}°")
    else:
        print('  Result: Capture!')
    print()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 8
      4 J0 = conf['p']*conf['b']/nu
      6 eobpars = teob.CreateDict(r_hyp=r0, H_hyp=conf['E0'], J_hyp=J0, q=1, 
      7                          chi1z=conf['chi1'], chi2z=conf['chi2'])
----> 8 eob = teob.Waveform_EOB(pars=eobpars)
      9 conf['eob']  = eob
     10 E_final = eob.dyn['E'][-1]

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/PyART/models/teob.py:27, in Waveform_EOB.__init__(self, pars)
     25 self.pars = pars
     26 self._kind = "EOB"
---> 27 self._run_py()
     28 pass

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/PyART/models/teob.py:61, in Waveform_EOB._run_py(self)
     59     self.domain = "Freq"
     60 else:
---> 61     t, hp, hc, hlm, dyn = EOB.EOBRunPy(self.pars)
     62     self._u = t
     63     hlm_conv = convert_hlm(hlm)

NameError: name 'EOB' is not defined

Visualize Results

Now let’s plot the results for all scattering configurations:

  1. Real part of the (2,2) mode strain

  2. The Weyl scalar ψ₄

  3. Radial distance vs time

  4. Trajectories in the x-y plane with scattering angles

plt.figure(figsize=(12,9))

for conf in configurations:
    eob  = conf['eob']
    scat = conf['scat']
    
    if scat is None:
        continue
    
    # Plot waveform
    plt.subplot(2,2,1)
    plt.plot(eob.u, eob.hlm[(2,2)]['real'])
    plt.xlim([-100, 100])
    plt.xlabel('Time (M)')
    plt.ylabel(r'Re[$h_{22}$]')
    plt.title('Waveform (2,2) mode')
    plt.grid(True, alpha=0.3)

    # Plot psi4
    dh   = D1(eob.hlm[(2,2)]['z'], eob.u, 4)
    psi4 = D1(dh, eob.u, 4)
    b      = conf['b']
    chi_BH = conf['chi1']
    
    plt.subplot(2,2,2)
    plt.plot(eob.u, -psi4.real, label=f'b={b:.3f}, χ={chi_BH}')
    plt.xlim([-100, 100])
    plt.xlabel('Time (M)')
    plt.ylabel(r'Re[$-\psi_4$]')
    plt.title('Weyl scalar')
    plt.legend()
    plt.grid(True, alpha=0.3)

    # Plot radial distance
    x = eob.dyn['r']*np.cos(eob.dyn['phi'])
    y = eob.dyn['r']*np.sin(eob.dyn['phi'])
    r = np.sqrt(x**2 + y**2)
    
    plt.subplot(2,2,3)
    plt.plot(eob.dyn['t'], r)
    plt.xlabel('Time (M)')
    plt.ylabel('Radial distance r (M)')
    plt.title('Radial distance vs time')
    plt.grid(True, alpha=0.3)

    # Plot trajectory
    plt.subplot(2,2,4)
    plt.plot(x, y, label=f'χ={scat.chi:.2f}°')
    plt.xlabel('x (M)')
    plt.ylabel('y (M)')
    plt.title('Trajectories with scattering angles')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.axis('equal')

plt.tight_layout()
plt.show()

Summary

This tutorial demonstrated:

  • How to set up hyperbolic encounter parameters

  • How to generate EOB waveforms for scattering scenarios

  • How to compute scattering angles using the ScatteringAngle class

  • How to visualize the waveforms and trajectories

The scattering angle provides important information about the dynamics of hyperbolic encounters and can be used to validate EOB models against numerical relativity simulations.