Hybridisation: starting below the artifact's reach

The artifact carries a finite stretch of inspiral. A start frequency below it is served by extending the inspiral with a post-Newtonian description joined onto the artifact's own content. Written 2026-09-02; every number on this page was measured that day through the consumer route on the released NRHJSur3dq8_AA_v3 unless it says otherwise.

The knob and its three states

f_low on the geometric methods (dimensionless M f of the (2, 2) mode) and f_min on the physical ones (Hz) select where the waveform starts. None or exactly 0 is the full span; in reach it trims; below reach it extends. hybridization is the policy:

state in reach below reach f_low absent
"extend_if_below_reach" (default) trims, bit for bit extends full span
"never" trims raises, naming the point's own start full span
"require" raises: the arm would not engage extends raises: no target

Every cell was exercised. The trim at f_low = 0.01 returned 4453 samples that are a bitwise slice of the untrimmed call. "never" at f_low = 0.004 raised f_low=0.004 is below this point's native start (M f = 0.006311) and hybridization='never' guarantees NR-only content. "require" at f_low = 0.01 raised the PN extension arm would not engage there, and with no f_low raised there is no target frequency, the PN extension arm cannot extend to 0 Hz. Both keywords at once, or an unknown state, raise. The old hybridize="auto"/False/True spellings map one for one and warn with FutureWarning (nrhjsurrogate/driver/hybridization_states.py).

Only timing_mode="aa" honours the policy. The derived routes carry their own extension gate: timing_mode="aa-int" with f_low = 0.004 extended on its own to 55379 samples, and with hybridization="never" raised hybridization is honoured on timing_mode='aa' only (nrhjsurrogate/driver/aa_api.py:1450-1455 and :1498-1505).

Where the reach is

The reach is decided in the action, by one function every caller shares: physics.reach.reaches compares the 4PN angular momentum at the requested frequency against the artifact's J at its first break, with a relative margin of 1e-12 tolerant toward the native side (nrhjsurrogate/physics/reach.py:211, REACH_REL_MARGIN). It used to be decided twice, from 1/tau' at the edge and from the x variable, and the two disagreed by up to 4.6e-4, so an f_low in that band raised instead of returning a waveform (nrhjsurrogate/physics/action_extend.py:1414-1424).

The artifact holds about 18.07 orbits of numerical-relativity content at every point, because the inspiral is fitted on the common phase domain of its 260 training simulations and the shortest one sets it. Measured (2, 2) start frequencies:

(mass_ratio, spin1z, spin2z) native start M f at 20 solar masses at 100 solar masses
(1, 0, 0) 0.004941 50.2 Hz 10.0 Hz
(1, 0.8, 0.8) 0.005756 58.4 Hz 11.7 Hz
(4, 0.3, -0.2) 0.006311 64.1 Hz 12.8 Hz
(8, -0.5, 0.6) 0.006393 64.9 Hz 13.0 Hz
(2.5, 0.61, 0.13) 0.006018 61.1 Hz 12.2 Hz

So a 20 Hz analysis of a 60 solar mass system is served natively, and one at 20 solar masses draws its first two thirds from the extension. Each mode (l, m) reaches its own content at m/2 times the (2, 2) start, so an analysis that begins exactly at the (2, 2) reach still takes its (3, 3), (4, 4) and (5, 5) from the extension until the (2, 2) frequency has grown by that factor (measured 2026-08-20, memory seam-depth-nr-length-requirement).

What the extension does, from the code

nrhjsurrogate/physics/action_extend.py:extend_action (line 1391), reached through driver/reconstruct.py:370-414:

  1. The dynamics. The fitted action J(Phi) is continued below the edge along the 4PN trajectory, positioned by value continuity of J at an anchor DEFAULT_ANCHOR_ORBITS = 4.0 orbits INSIDE the artifact (physics/pn_extend.py:75), not at the edge. Anchoring at the edge measured 5.5 rad of secular drift against 0.1 rad four orbits in (action_extend.py:240-248). The orbital frequency, the energy and the clock derive from the first law along that trajectory; the report calls this anchor = 'J-action' and at (4, 0.3, -0.2) the anchor sits at Phi = -90.92 against the edge -113.65.
  2. The modes. Below the edge each mode is R_lm * h^PN_lm, the fitted ratio route taken to its post-Newtonian limit. R_lm starts from the artifact's edge ratio, taken as the median over a window one to five orbits interior (r_edge = 'median-window'), and decays to exactly 1 on a per-mode power law in v fitted over the artifact's own span (NRHJ_PN_RATIO_POWERLAW, default on, action_extend.py:48-104). The fitted exponents at this point are 10.8 on (2, 2) and 4.6 to 6.4 on the others; on (2, 1) the guard declined and the 4-orbit cosine roll served instead (ratio_decay_p = None in the report).
  3. The closure. E is a FITTED variable of the artifact, served into the join as closure_mode = 'measured-E', not derived from J. The acceptance diagnostic is the first-law closure |dE/dJ / omega - 1|: 7.2e-7 at most on the extension and 3.1e-4 median, 6.9e-4 maximum on the numerical-relativity side of the seam.

Every quantity in that list is in result["hybrid_report"], a dictionary of scalars: Phi_start, Phi_edge, Phi_anchor, orbits_added, t_added, f22_start, f22_edge, roll_orbits, per-mode edge_step_rel, R_edge and ratio_decay_p, the J and E edge steps and the closures. At f_low = 0.004 the extension added 24.25 orbits and 10035.5 M; at 0.002, 125.57 orbits and 87971 M. With dynamics=True the result also carries result["dynamics"] = {Phi, J, E, omega, t} over the whole extended domain (1354 points at 0.004). "hybrid_report" is None when the extension did not engage; "dynamics" is then the native-span dynamics under the same key names (since 1.1), see dynamics-and-remnant.md.

What it does to the content you already had

The extension is not a prepend, and the numbers below are the size of that statement. Evaluated on the native grid by passing times=, the extended call at f_low = 0.004 differs from the native call on 15256 of 15775 samples:

| where | |ext - native| / peak, (2, 2) | | --- | --- | | first 2.7 orbits after the edge, the roll window | up to 2.0e-4 (8.1e-4 on (4, 4)) | | the rest of the inspiral, to -20 M | 1e-8 to 7e-7, median 1e-9 | | the join window, -20 M to -5 M | 2.6e-7 | | from -5.1 M through ringdown | 0, bitwise |

The roll re-shapes the ratio in the window where R_lm is taken to 1; the rest of the inspiral is re-sampled through the extension's own phase grid (PN_EXTENSION_N_PER_ORBIT, action_extend.py:313) rather than evaluated on the spectral route the native call uses; and everything past the end of the inspiral to merger-ringdown join is untouched. This is what "never" guarantees you against: not only the PN content below the edge but the re-sampling above it.

Two different f_low values are not bitwise on their overlap either. On a shared grid, 0.002 against 0.0015 differ by 2.6e-5 of peak on (2, 2), 2.1e-5 on (3, 3) and 1.0e-4 on (4, 4). The paper review measured 1.37e-5 for a different pair and refuted the paper's "bitwise across starting frequencies" claim. Only a repeated identical call is bitwise, which was also measured.

Note also that the extended grid is np.arange from the new start, so it contains none of the native sample times; a like-for-like comparison needs times=.

Accuracy against longer numerical relativity, recorded elsewhere

Measured 2026-08-20 on four training simulations (so in sample, and the floor is optimistic; only the rise above it is the seam's cost). Per-mode agreement of the artifact with its PN normaliser at the seam, median |R_lm - 1| over 260: (2, 2) 2.7e-3, (2, 1) 6.7e-3, (3, 3) 5.7e-3, (3, 2) 1.0e-2, (4, 3) 2.2e-2, (4, 4) 4.2e-2, (5, 5) 7.3e-2. The (2, 2) arrives converged and its residual is phase; the corner modes are 2 to 7 percent off and the roll forces that to zero. Detector verdict at mismatch below D / (2 rho^2), D = 4: with aLIGO at 20 solar masses from 20 Hz, an analysis at signal-to-noise 25 needs about 13 to 14 more orbits of numerical relativity than the artifact carries; at 100 the count is 46 to 56. The practical rule from the collapse onto f_seam / f_low is to start at about twice the seam frequency. This behaviour is shown in the hybridisation figures accompanying the model paper.

Cross-machine reproducibility of a DEEP extension is bounded at about 1e-5 relative by libm's exp, through the conditioning of the anchor contraction; the native span is far less sensitive (ACCURACY.md section 2).

Cost, measured

NumPy backend, OMP_NUM_THREADS=1, one waveform, best of three: native 86 ms; f_low = 0.004 186 ms (55881 samples); 0.002 355 ms (367642); 0.0015 657 ms (796583). On the kokkos backend the same f_low = 0.004 call returns the same 55881 samples, agreeing with NumPy to 2.0e-10 of peak on (2, 2); the extension pack is built and synthesised in the compiled layer (nrhjsurrogate/execution/batched_evaluator.py module docstring). The compiled route's cost model on the v1-era build was 29.4 ms fixed plus 3.03 us per manufactured sample, with the fixed term dominating any extension shallower than about 10 Hz at 60 solar masses. An earlier per-stage cost ledger was superseded by its own correction, and the whole-call model is what survives.

Limits, next to the capability

Where the code is

what where
named states and the legacy spellings nrhjsurrogate/driver/hybridization_states.py
the reach test nrhjsurrogate/physics/reach.py:211
the extension nrhjsurrogate/physics/action_extend.py:1391, geometry at :1263
dispatch and the refusals nrhjsurrogate/driver/aa_api.py:1324-1713, driver/reconstruct.py:370-414
compiled extension pack nrhjsurrogate/execution/batched_evaluator.py, cpp/pack.cpp
user documentation docs/USAGE.md section 4; examples/usage/03_hybridisation.py