diff --git a/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py b/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py
index fd1859d4a..db5f791fe 100644
--- a/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py
+++ b/MonteCarloMarginalizeCode/Code/RIFT/lalsimutils.py
@@ -3275,7 +3275,11 @@ def hlmoft(P, Lmax=2,nr_polarization_convention=False, fixed_tapering=False, sil
# but that is very difficult to do because modes of different 'm' and thus typical frequency generally mix
# Note also that unless the segment length is large, this is often surprisingly few frequency bins for tapering
if not(no_condition):
- our_fvals = evaluate_fvals(hlmsdict[(2,2)])
+ # lal_convention=True is REQUIRED here: ChooseFDModes returns the ascending
+ # center-DC grid, and the default (RIFT reversed) convention would shift the
+ # high-pass window by one bin between (l,m) and (l,-m), breaking the
+ # conjugate-pair identity at the percent level. See evaluate_fvals docs.
+ our_fvals = evaluate_fvals(hlmsdict[(2,2)], lal_convention=True)
vectaper_symmetric = np.ones(len(our_fvals))
indx_below = np.logical_and(np.abs(our_fvals)
=P.fmin*fd_standoff_factor)
vectaper_symmetric[indx_below] = 0.5 + 0.5*np.cos(np.pi* (np.abs(our_fvals[indx_below])/P.fmin - 1)/(1-fd_standoff_factor))
@@ -3300,7 +3304,15 @@ def hlmoft(P, Lmax=2,nr_polarization_convention=False, fixed_tapering=False, sil
indx_crit = TDlen - ntaper
for mode in hlmsdict:
+ npts_fd_pre_resize = hlmsdict[mode].data.length
hlmsdict[mode] = lal.ResizeCOMPLEX16FrequencySeries(hlmsdict[mode],0, TDlen)
+ if npts_fd_pre_resize > TDlen:
+ # The resize above truncated the +fNyq bin from the two-sided grid but kept
+ # its -fNyq partner (index 0). Zero it so the truncation commutes with the
+ # conjugate-pair (f -> -f) reflection for models with support at Nyquist.
+ # Tied to the truncation itself, NOT to no_condition: an asymmetric
+ # truncation would reintroduce (l,±m) asymmetry even on the raw path.
+ hlmsdict[mode].data.data[0] = 0
hlmsT[mode] = DataInverseFourier(hlmsdict[mode])
# Phase factors: see crazy conventions in https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiral.c
if True: #P.approx == lalIMRPhenomXHM or P.approx == lalIMRPhenomHM:
@@ -4901,13 +4913,26 @@ def psd_windowing_factor(window_shape, TDlen):
def evaluate_tvals(lal_tseries):
return float(lal_tseries.epoch) +lal_tseries.deltaT*np.arange(lal_tseries.data.length)
-def evaluate_fvals(lal_2sided_fseries):
+def evaluate_fvals(lal_2sided_fseries, lal_convention=False):
r"""
- evaluate_fvals(lal_2sided_fseries)
+ evaluate_fvals(lal_2sided_fseries, lal_convention=False)
Associates frequencies with a 2sided lal complex array. Compare with 'self.longweights' code
Done by HAND in PrecessingOrbitModesOfFrequency
Manually *reverses* convention re sign of \omega used in lal!
+ lal_convention=False (default): RIFT's own two-sided packing (arrays produced by
+ DataFourier/complex_hoff): even length, REVERSED, f[k] = deltaF*(npts/2 - k).
+ WARNING: for odd-length arrays this assigns f on a grid offset by deltaF/2 --
+ it is only correct for the even-length RIFT packing.
+ lal_convention=True: the packing of two-sided series returned directly by LAL
+ generators (e.g. SimInspiralChooseFDModes): ASCENDING [-fNyq, ..., 0, ..., +fNyq]
+ with DC at index npts//2 (odd length TDlen+1; even length after truncation keeps
+ DC at npts//2). f[k] = deltaF*(k - npts//2), exact for both parities of npts.
+ Use this - not the default - on ChooseFDModes output: the default's reversal +
+ half-bin offset shifts any |f|-symmetric window by one bin between (l,m) and
+ (l,-m) modes (which live at opposite signs of f), breaking the conjugate-pair
+ identity h_{l,-m}(f) = (-1)^l conj(h_{lm}(-f)) at the percent level.
+
Notes:
a) XXXFrequencySeries have an f0 and a deltaF, and *logically* they should run from f0....f0+N*df
b) I will always use COMPLEX16FrequencySeries, which should run from -fNyq...fNyq-df
@@ -4939,6 +4964,8 @@ def evaluate_fvals(lal_2sided_fseries):
"""
npts = lal_2sided_fseries.data.length
df = lal_2sided_fseries.deltaF
+ if lal_convention:
+ return df*(np.arange(npts) - npts//2)
fvals = np.zeros(npts)
# https://www.lsc-group.phys.uwm.edu/daswg/projects/lal/nightly/docs/html/group___time_freq_f_f_t__h.html
# https://www.lsc-group.phys.uwm.edu/daswg/projects/lal/nightly/docs/html/_time_freq_f_f_t_8h.html