gwBOB.BOB_utils module

class gwBOB.BOB_utils.BOB[source]

Bases: object

Construct Backwards-One-Body waveforms.

Typical workflow:

bob = BOB() bob.initialize_with_sxs_data(“SXS:BBH:0305”) # or initialize_with_cce_data / initialize_with_NR_mode bob.what_should_BOB_create = “news” # or “psi4”, “strain_using_news”, etc. (see valid_choices()) bob.optimize_Omega0 = True # optional t, y = bob.construct_BOB()

Internally, state is grouped into six dataclasses (see gwBOB/_state.py) accessed transparently via property delegation:

  • _remnant : physics constants (mf, chif, Omega_ISCO, Omega_QNM, w_r, tau, l, m, …)

  • _data : NR waveform timeseries (psi4_data, news_data, strain_data, …)

  • _wf_config : waveform construction knobs (what_to_create, t0, Phi_0, minf_t0, …)

  • _fit_config : fit/optimization knobs (optimize_Omega0, optimize_t0, start_fit_before_tpeak, …)

  • _runtime : derived state recomputed on every mode switch (data, tp, Ap, t, t_tp_tau, …)

  • _fit_result : fit outputs (fitted_t0, fitted_Omega0, fit_failed)

Every documented public attribute (e.g. bob.tp, bob.Omega_0, bob.optimize_Omega0, bob.fitted_Omega0) is reachable via a @property that reads/writes through to the relevant container. __slots__ is enabled so attribute typos raise AttributeError instead of silently creating a new attribute.

Claude Code: See DESIGN_state_split.md for the architectural rationale.

property Ap
property M_tot
property NR_based_on_BOB_ts
property Omega_0
property Omega_ISCO
property Omega_QNM
property Phi_0
property auto_switch_to_numerical_integration
property chif
property chif_with_sign
construct_BOB(N=2)[source]

Construct the BOB timeseries.

Dispatches to construct_BOB_minf_t0 (when minf_t0 is True) or construct_BOB_finite_t0 (otherwise), then performs a phase alignment against the NR data and stores the resampled NR result in self.NR_based_on_BOB_ts for plotting / comparison.

Parameters:

N (int) – Number of terms in the series expansion used for “strain_using_news” / “strain_using_psi4” integration. Ignored for direct (non-integrated) modes. Be careful: very large N can cause memory pressure.

Returns:

(t, y) where t is the time array and y is the complex BOB waveform array.

Return type:

tuple of (np.ndarray, np.ndarray)

construct_BOB_current_quadrupole_naturally(perform_phase_alignment_first=False, lm_Omega0=None, lmm_Omega0=None)[source]

Construct the current quadrupole wave

I_lm = (i / sqrt(2)) * (h_lm - (-1)^|m| * conj(h_l,-m))

by building the (l, +m) and (l, -m) modes with BOB first, then combining.

Parameters:
  • perform_phase_alignment_first (bool) – Whether to phase-align the (l, ±m) BOB modes against NR before combining (vs. aligning only the final current wave). Currently unused — see body.

  • lm_Omega0 (float, optional) – Override the initial-condition frequency for the (l, +m) BOB construction.

  • lmm_Omega0 (float, optional) – Override the initial-condition frequency for the (l, -m) BOB construction.

Returns:

(union_ts, BOB_current_wave) — the time grid (union of the two mode grids) and the complex current-quadrupole waveform on it.

Return type:

tuple of (np.ndarray, np.ndarray)

construct_BOB_finite_t0(N)[source]

This function is used to construct the BOB for a finite t0 value.

Parameters:

N (int) – Number of terms to use in the series if “strain_using_news” is used

Returns:

BOB timeseries

Return type:

Kuibit Timeseries

construct_BOB_mass_quadrupole_naturally(perform_phase_alignment_first=False, lm_Omega0=None, lmm_Omega0=None)[source]

Construct the mass quadrupole wave

I_lm = (1 / sqrt(2)) * (h_lm + (-1)^|m| * conj(h_l,-m))

by building the (l, +m) and (l, -m) modes with BOB first, then combining.

Parameters:
  • perform_phase_alignment_first (bool) – Whether to phase-align the (l, ±m) BOB modes against NR before combining (vs. aligning only the final mass wave). Currently unused — see body.

  • lm_Omega0 (float, optional) – Override the initial-condition frequency for the (l, +m) BOB construction.

  • lmm_Omega0 (float, optional) – Override the initial-condition frequency for the (l, -m) BOB construction.

Returns:

(union_ts, BOB_mass_wave) — the time grid (union of the two mode grids) and the complex mass-quadrupole waveform on it.

Return type:

tuple of (np.ndarray, np.ndarray)

construct_BOB_minf_t0(N)[source]

This function is used to construct BOB taking t0 to be -infinity.

Parameters:

N (int) – Number of terms to use in the series if “strain_using_news” is used

Returns:

BOB timeseries

Return type:

Kuibit Timeseries

construct_NR_mass_and_current_quadrupole(what_to_create)[source]

Build the NR mass and current quadrupole waveforms from the loaded (l, ±m) modes via:

I_current = (i / sqrt(2)) * (h_lm - (-1)^|m| * conj(h_l,-m)) I_mass = (1 / sqrt(2)) * (h_lm + (-1)^|m| * conj(h_l,-m))

Parameters:

what_to_create (str) – Which NR data to combine — one of “psi4”, “news”, “strain”.

Returns:

(NR_current, NR_mass) — note the order: current first, mass second.

Return type:

tuple of (kuibit_ts, kuibit_ts)

property current_quadrupole_data
property data
property end_fit_after_tpeak
fit_Omega0()[source]

Optimize the initial angular frequency Omega_0 by fitting BOB’s analytic frequency to the NR frequency over the configured fit window. Only valid for the t0 = -infinity flavor.

On success, writes the optimized value to self.Omega_0. On failure (any exception during curve_fit), sets self.fit_failed = True and writes self.Omega_0 = self.Omega_ISCO.

Side effect: if end_fit_after_tpeak exceeds end_after_tpeak, the former is silently lowered to match. (Claude Code: See code_review §2 P5 E9 — flagged for future cleanup.)

Raises:

ValueError – If minf_t0 is False (use fit_t0 for finite t0).

property fit_failed
fit_omega(x, Omega_0)[source]

Optimizer callback for fit_Omega0 — used by scipy.optimize.curve_fit.

Mutates self.Omega_0 to the trial value, evaluates the analytic BOB frequency for the currently selected mode, and returns the frequency array sliced to the fit window. The mutation of self.Omega_0 is a known fragility (Claude Code: see code_review §2 P9 / DESIGN_stage3.md “Deferred work”); for now the caller is expected to overwrite self.Omega_0 with the optimized value after curve_fit returns.

Parameters:
  • x (np.ndarray) – Time samples (passed by curve_fit; not actually used inside, since self.t provides the time grid).

  • Omega_0 (float) – Trial initial angular frequency.

Returns:

BOB frequency evaluated at self.t[start:end] for the configured fit window.

Return type:

np.ndarray

fit_t0()[source]

Optimize the initial time t0 by grid search.

For each candidate t0, Omega_0 is fixed from the NR frequency at that time, and the squared residual against the BOB frequency is computed (via fit_t0_only). On completion, self.t0, self.t0_tp_tau, self.Omega_0, self.fitted_t0, self.fitted_Omega0 are all updated.

Implementation note: a grid search is preferred to a least-squares fit because (1) each t0 is linked to a discrete Omega_0 with a finite timestep, (2) LSQ can get trapped in local minima even with a good initial guess, and (3) since this is a 1-D fit, brute-force is fast.

Use this when minf_t0 is False. For t0 = -infinity, use fit_Omega0 instead.

fit_t0_and_Omega0()[source]

Joint optimization of the initial time (t0) and initial frequency (Omega_0).

Not yet implemented. An experimental differential-evolution + curve_fit approach lived here previously but was unreliable; see git history if you want to revive it.

fit_t0_and_omega(x, t0, Omega_0)[source]

Optimizer callback for joint t0 + Omega_0 fitting via curve_fit.

Currently UNCALLED: its only caller was the body of fit_t0_and_Omega0, which was retired in favor of raise NotImplementedError. Kept for future reuse if joint fitting is reimplemented.

Mutates self.Omega_0, self.t0, self.t0_tp_tau to the trial values. Same impurity caveat as fit_omega.

Parameters:
  • x (np.ndarray) – Time samples (passed by curve_fit).

  • t0 (float) – Trial initial time.

  • Omega_0 (float) – Trial initial angular frequency.

Returns:

BOB frequency evaluated on the fit window. On evaluation failure, returns a flat array of 1e10 to signal a bad residual to the optimizer.

Return type:

np.ndarray

fit_t0_only(t00, freq_data)[source]

Optimizer callback for fit_t0 — used by scipy.optimize.brute.

For each candidate t0, fixes Omega_0 from the NR frequency at that time and returns the squared residual. Mutates self.t0, self.t0_tp_tau, self.Omega_0 (same impurity caveat as the other fit callbacks).

Parameters:
  • t00 (np.ndarray) – Single-element array containing the trial t0 (brute passes parameters as arrays).

  • freq_data (kuibit_ts) – NR (orbital) frequency timeseries — already divided by |m| so y is big Omega.

Returns:

Sum of squared residuals between BOB and NR frequencies on the configured fit window.

Return type:

float

property fitted_Omega0
property fitted_t0
property full_psi4_data
property full_strain_data
get_correct_Phi_and_Omega()[source]

Return the BOB phase and frequency arrays for the currently selected mode.

Dispatches to the correct BOB_terms.BOB_<flavor>_phase[_finite_t0] function based on what_should_BOB_create and minf_t0.

Note: even in the X_using_Y cases (e.g., strain_using_news), the analytic news-frequency term is used because the BOB amplitude is constructed to best describe the news; using the strain frequency for Omega_0 optimization on these cases would be unphysical.

Returns:

(Phi, Omega) — phase and angular frequency arrays evaluated on the time grid self.t.

Return type:

tuple of (np.ndarray, np.ndarray)

Raises:

ValueError – If what_should_BOB_create is not one of the supported flavors.

get_news_data(**kwargs)[source]

Return the NR news timeseries (computed as d/dt of the strain) for an arbitrary (l, m) mode.

Same restrictions as get_psi4_data: only the requested (l, m) and (l, -m) modes are available unless load_all_modes=True at init.

kwargs:

l (int): l index (defaults to self.l). m (int): m index (defaults to self.m).

Returns:

(t, y) of the requested news mode.

Return type:

tuple of (np.ndarray, np.ndarray)

get_psi4_data(**kwargs)[source]

Return the NR psi4 timeseries for an arbitrary (l, m) mode.

Defaults to the (l, m) mode specified during initialization. Pass l=... and/or m=... as keyword arguments to retrieve a different mode.

With load_all_modes=False at init (the default), only the requested (l, m) and (l, -m) modes are stored — calls for other modes raise AttributeError. Pass load_all_modes=True to initialize_with_* if you need arbitrary mode access (uses much more memory). After initialize_with_NR_mode, only the single mode passed in is ever available.

kwargs:

l (int): l index (defaults to self.l). m (int): m index (defaults to self.m).

Returns:

(t, y) of the requested psi4 mode.

Return type:

tuple of (np.ndarray, np.ndarray)

get_strain_data(**kwargs)[source]

Return the NR strain timeseries for an arbitrary (l, m) mode.

Same restrictions as get_psi4_data: only the requested (l, m) and (l, -m) modes are available unless load_all_modes=True at init.

kwargs:

l (int): l index (defaults to self.l). m (int): m index (defaults to self.m).

Returns:

(t, y) of the requested strain mode.

Return type:

tuple of (np.ndarray, np.ndarray)

get_t_isco()[source]

This function is used to get the time of the ISCO of the waveform.

Parameters:

None

Returns:

Time of ISCO of the waveform

Return type:

float

property h_L2_norm_tp
hello_world()[source]

Print the BOB welcome banner.

initialize_with_NR_mode(t_NR, y_psi4, y_strain, mf, chif, l=2, m=2, w_r=-1, tau=-1, verbose=False)[source]

This function is used to initialize the BOB with NR data. Currently this function only supports the input of one (s=-2,l,m) mode.

Parameters:
  • t_NR (array) – timeseries of NR psi4 data

  • y_psi4 (array) – values of NR psi4 data

  • y_strain (array) – values of NR strain data

  • mf (float) – final mass of the system

  • chif (array) – final spin of the system

  • l (int) – Mode number

  • m (int) – Mode number

  • w_r (float) – Angular frequency of the mode

  • tau (float) – Damping time of the mode

  • verbose (bool) – Whether to print verbose output

initialize_with_cce_data(cce_id, l=2, m=2, perform_superrest_transformation=False, inertial_to_coprecessing_transformation=False, provide_own_abd=None, resample_dt=0.1, verbose=False, load_all_modes=False)[source]

This function is used to initialize the BOB with CCE data.

Parameters:
  • cce_id (str) – CCE id of the simulation (https://data.black-holes.org/waveforms/extcce_catalog.html)

  • l (int) – Mode number

  • m (int) – Mode number

  • perform_superrest_transformation (bool) – Whether to perform a superrest transformation

  • inertial_to_coprecessing_transformation (bool) – Whether to perform an inertial to coprecessing transformation

  • provide_own_abd (scri.Abd) – Use a user passed in scri abd object (maybe useful if the user has specific pre-processing requirements)

  • resample_dt (float) – Resampling time step

  • verbose (bool) – Whether to print verbose output

  • load_all_modes (bool) – If True, retain the full multi-mode interpolated strain and psi4 arrays so that get_*_data(l, m) can return arbitrary modes after init. Default is False (memory-efficient): only the requested (l, m) and (l, -m) modes are retained. Claude Code: See MEMORY.md for measurements.

initialize_with_sxs_data(sxs_id, l=2, m=2, download=True, resample_dt=0.1, verbose=False, inertial_to_coprecessing_transformation=False, load_all_modes=False)[source]

This function is used to initialize the BOB with SXS data.

Memory: by default, this method extracts the requested (l, m) and (l, -m) modes from the un-interpolated waveform first and resamples only those two modes to the dense uniform grid. This drops the init peak from ~1.2 GB to ~700 MB on SXS:BBH:2325. The original slow path (interpolate all ~77 modes, then slice) is still used when load_all_modes=True or inertial_to_coprecessing_transformation=True, since both require the full multi-mode object. Claude Code: see peak_memory_fix.md for the rollout history.

Parameters:
  • sxs_id (str) – SXS id of the simulation

  • l (int) – Mode number

  • m (int) – Mode number

  • download (bool) – Whether to download the data

  • resample_dt (float) – Resampling time step

  • verbose (bool) – Whether to print verbose output

  • inertial_to_coprecessing_transformation (bool) – Whether to perform inertial to coprecessing transformation. Forces the slow init path (rotation is a multi-mode operation).

  • load_all_modes (bool) – If True, retain the full multi-mode interpolated strain and psi4 arrays so that get_psi4_data(l, m) / get_news_data(l, m) / get_strain_data(l, m) can return arbitrary modes after init. Default is False (memory-efficient): only the requested (l, m) and (l, -m) modes are retained, dropping ~110 MB / BOB instance for SXS:BBH:2325. load_all_modes=True also forces the slow init path. Claude Code: See MEMORY.md for measured costs and parallel-init implications.

property l
property m
property mass_quadrupole_data
meet_the_creator()[source]

Print an ASCII portrait of Sean (the creator of BOB).

property metadata
property mf
property minf_t0
property news_Ap
property news_data
property news_mm_data
property news_tp
property optimize_Omega0
property optimize_t0

Return whether t0 optimization is requested.

property optimize_t0_and_Omega0

Return whether joint t0 + Omega_0 optimization is requested.

property perform_final_amplitude_rescaling
property perform_final_time_alignment
property psi4_Ap
property psi4_data
property psi4_mm_data
property psi4_tp
property resample_dt
residual_t0_and_omega(p, t_freq, y_freq)[source]

Optimizer callback for joint t0 + Omega_0 fitting via scipy.optimize.differential_evolution.

Currently UNCALLED: its only caller was the body of fit_t0_and_Omega0, which was retired in favor of raise NotImplementedError. Kept for future reuse if joint fitting is reimplemented.

Mutates self.Omega_0, self.t0, self.t0_tp_tau to the trial values. Same impurity caveat as the other fit callbacks.

Parameters:
  • p (tuple of (float, float)) – Trial parameters (t0, Omega_0).

  • t_freq (np.ndarray) – Time array of the NR frequency reference.

  • y_freq (np.ndarray) – NR frequency values aligned with t_freq.

Returns:

Sum of squared residuals between the BOB-predicted and NR frequencies over the configured fit window.

Return type:

float

property set_end_after_tpeak

Return the configured end time relative to tpeak.

property set_initial_time

Return the configured initial time t0 (in code units relative to peak).

property set_start_before_tpeak

Return the configured start time relative to tpeak.

property start_fit_before_tpeak
property strain_Ap
property strain_data
property strain_mm_data
property strain_scri_wm
property strain_tp
property strain_wm
property sxs_id
property t
property t0
property t0_tp_tau
property t_tp_tau
property tau
property tp
property use_strain_for_Omega0_optimization
property use_strain_for_t0_optimization
valid_choices()[source]

Print the valid choices for what_should_BOB_create.

Derived from the dispatch tables (_SIMPLE_MODES, _QUADRUPOLE_MODES) so this listing and the setter cannot drift apart.

property w_r
property what_is_BOB_building
property what_should_BOB_create

Returns what BOB should create