// ----- local function definitions ---------- static int XLALComputeFstatDemod ( FstatResults* Fstats, const FstatCommon *common, void *method_data ) { // Check input XLAL_CHECK(Fstats != NULL, XLAL_EFAULT); XLAL_CHECK(common != NULL, XLAL_EFAULT); XLAL_CHECK(method_data != NULL, XLAL_EFAULT); DemodMethodData *demod = (DemodMethodData*) method_data; // get internal timing info DemodTimingInfo *ti = &(demod->timingInfo); REAL8 tic = 0, toc = 0; // Get which F-statistic quantities to compute const FstatQuantities whatToCompute = Fstats->whatWasComputed; // handy shortcuts BOOLEAN returnAtoms = (whatToCompute & FSTATQ_ATOMS_PER_DET); PulsarDopplerParams thisPoint = Fstats->doppler; const REAL8 fStart = thisPoint.fkdot[0]; const MultiSFTVector *multiSFTs = demod->multiSFTs; const MultiNoiseWeights *multiWeights = common->multiNoiseWeights; const MultiDetectorStateSeries *multiDetStates = common->multiDetectorStates; UINT4 numDetectors = multiSFTs->length; XLAL_CHECK ( multiDetStates->length == numDetectors, XLAL_EINVAL ); XLAL_CHECK ( multiWeights==NULL || (multiWeights->length == numDetectors), XLAL_EINVAL ); UINT4 numSFTs = 0; for ( UINT4 X = 0; X < numDetectors; X ++ ) { numSFTs += multiDetStates->data[X]->length; } // initialize timing info struct if ( ti->collectTiming ) { XLAL_INIT_MEM ( (*ti) ); ti->collectTiming = 1; ti->numDetectors = numDetectors; ti->numFreqBins = Fstats->numFreqBins; ti->numSFTs = numSFTs; tic = XLALGetCPUTime(); } MultiSSBtimes *multiSSB = NULL; MultiAMCoeffs *multiAMcoef = NULL; // ----- check if we have buffered SSB+AMcoef for current sky-position if ( (demod->prevAlpha == thisPoint.Alpha) && (demod->prevDelta == thisPoint.Delta ) && (demod->prevMultiSSBtimes != NULL) && ( XLALGPSDiff(&demod->prevRefTime, &thisPoint.refTime) == 0 ) && // have SSB times for same reftime? (demod->prevMultiAMcoef != NULL) ) { // if yes ==> reuse multiSSB = demod->prevMultiSSBtimes; multiAMcoef = demod->prevMultiAMcoef; } else { // if not, compute SSB + AMcoef for this skyposition SkyPosition skypos; skypos.system = COORDINATESYSTEM_EQUATORIAL; skypos.longitude = thisPoint.Alpha; skypos.latitude = thisPoint.Delta; XLAL_CHECK ( (multiSSB = XLALGetMultiSSBtimes ( multiDetStates, skypos, thisPoint.refTime, common->SSBprec )) != NULL, XLAL_EFUNC ); XLAL_CHECK ( (multiAMcoef = XLALComputeMultiAMCoeffs ( multiDetStates, multiWeights, skypos )) != NULL, XLAL_EFUNC ); // store these for possible later re-use in buffer XLALDestroyMultiSSBtimes ( demod->prevMultiSSBtimes ); demod->prevMultiSSBtimes = multiSSB; demod->prevRefTime = thisPoint.refTime; XLALDestroyMultiAMCoeffs ( demod->prevMultiAMcoef ); demod->prevMultiAMcoef = multiAMcoef; demod->prevAlpha = thisPoint.Alpha; demod->prevDelta = thisPoint.Delta; } // if could not reuse previously buffered quantites MultiSSBtimes *multiBinary = NULL; MultiSSBtimes *multiSSBTotal = NULL; // handle binary-orbital timing corrections, if applicable if ( thisPoint.asini > 0 ) { // compute binary time corrections to the SSB time delays and SSB time derivitive XLAL_CHECK ( XLALAddMultiBinaryTimes ( &multiBinary, multiSSB, &thisPoint ) == XLAL_SUCCESS, XLAL_EFUNC ); multiSSBTotal = multiBinary; } else { multiSSBTotal = multiSSB; } if ( ti->collectTiming ) { toc = XLALGetCPUTime(); ti->tauBary = (toc - tic); } // ----- compute final Fstatistic-value ----- REAL4 Ad = multiAMcoef->Mmunu.Ad; REAL4 Bd = multiAMcoef->Mmunu.Bd; REAL4 Cd = multiAMcoef->Mmunu.Cd; REAL4 Ed = multiAMcoef->Mmunu.Ed;; REAL4 Dd_inv = 1.0 / multiAMcoef->Mmunu.Dd; // ---------- Compute F-stat for each frequency bin ---------- for ( UINT4 k = 0; k < Fstats->numFreqBins; k++ ) { // Set frequency to search at thisPoint.fkdot[0] = fStart + k * Fstats->dFreq; COMPLEX8 Fa = 0; // complex amplitude Fa COMPLEX8 Fb = 0; // complex amplitude Fb MultiFstatAtomVector *multiFstatAtoms = NULL; // per-IFO, per-SFT arrays of F-stat 'atoms', ie quantities required to compute F-stat // prepare return of 'FstatAtoms' if requested if ( returnAtoms ) { XLAL_CHECK ( (multiFstatAtoms = XLALMalloc ( sizeof(*multiFstatAtoms) )) != NULL, XLAL_ENOMEM ); multiFstatAtoms->length = numDetectors; XLAL_CHECK ( (multiFstatAtoms->data = XLALMalloc ( numDetectors * sizeof(*multiFstatAtoms->data) )) != NULL, XLAL_ENOMEM ); } // if returnAtoms // loop over detectors and compute all detector-specific quantities for ( UINT4 X=0; X < numDetectors; X ++) { COMPLEX8 FaX, FbX; FstatAtomVector *FstatAtoms = NULL; FstatAtomVector **FstatAtoms_p = returnAtoms ? (&FstatAtoms) : NULL; // call XLALComputeFaFb_...() function for the user-requested hotloop variant XLAL_CHECK ( (demod->computefafb_func) ( &FaX, &FbX, FstatAtoms_p, multiSFTs->data[X], thisPoint.fkdot, multiSSBTotal->data[X], multiAMcoef->data[X], demod->Dterms ) == XLAL_SUCCESS, XLAL_EFUNC ); if ( returnAtoms ) { multiFstatAtoms->data[X] = FstatAtoms; // copy pointer to IFO-specific Fstat-atoms 'contents' } XLAL_CHECK ( isfinite(creal(FaX)) && isfinite(cimag(FaX)) && isfinite(creal(FbX)) && isfinite(cimag(FbX)), XLAL_EFPOVRFLW ); if ( whatToCompute & FSTATQ_FAFB_PER_DET ) { Fstats->FaPerDet[X][k] = FaX; Fstats->FbPerDet[X][k] = FbX; } // compute single-IFO F-stats, if requested if ( whatToCompute & FSTATQ_2F_PER_DET ) { REAL4 AdX = multiAMcoef->data[X]->A; REAL4 BdX = multiAMcoef->data[X]->B; REAL4 CdX = multiAMcoef->data[X]->C; REAL4 EdX = 0; REAL4 DdX_inv = 1.0 / multiAMcoef->data[X]->D; // compute final single-IFO F-stat Fstats->twoFPerDet[X][k] = XLALComputeFstatFromFaFb ( FaX, FbX, AdX, BdX, CdX, EdX, DdX_inv ); } // if FSTATQ_2F_PER_DET /* Fa = sum_X Fa_X */ Fa += FaX; /* Fb = sum_X Fb_X */ Fb += FbX; } // for X < numDetectors if ( whatToCompute & FSTATQ_2F ) { Fstats->twoF[k] = XLALComputeFstatFromFaFb ( Fa, Fb, Ad, Bd, Cd, Ed, Dd_inv ); } // Return multi-detector Fa & Fb if ( whatToCompute & FSTATQ_FAFB ) { Fstats->Fa[k] = Fa; Fstats->Fb[k] = Fb; } // Return F-atoms per detector if ( whatToCompute & FSTATQ_ATOMS_PER_DET ) { XLALDestroyMultiFstatAtomVector ( Fstats->multiFatoms[k] ); Fstats->multiFatoms[k] = multiFstatAtoms; } } // for k < Fstats->numFreqBins // this needs to be free'ed, as it's currently not buffered XLALDestroyMultiSSBtimes ( multiBinary ); // Return amplitude modulation coefficients Fstats->Mmunu = demod->prevMultiAMcoef->Mmunu; // return per-detector antenna-pattern matrices for ( UINT4 X=0; X < numDetectors; X ++ ) { Fstats->MmunuX[X].Ad = multiAMcoef->data[X]->A; Fstats->MmunuX[X].Bd = multiAMcoef->data[X]->B; Fstats->MmunuX[X].Cd = multiAMcoef->data[X]->C; Fstats->MmunuX[X].Dd = multiAMcoef->data[X]->D; Fstats->MmunuX[X].Ed = 0; } if ( ti->collectTiming ) { toc = XLALGetCPUTime(); ti->tauTotal = (toc - tic); ti->tauF1NoBuf = ti->tauTotal / ( Fstats->numFreqBins * numDetectors ); ti->tauF1Buf = (ti->tauTotal - ti->tauBary) / ( Fstats->numFreqBins * numDetectors ); } return XLAL_SUCCESS; } // XLALComputeFstatDemod()
/** * Function to compute transient-window "F-statistic map" over start-time and timescale {t0, tau}. * Returns a 2D matrix F_mn, with m = index over start-times t0, and n = index over timescales tau, * in steps of dt0 in [t0, t0+t0Band], and dtau in [tau, tau+tauBand] as defined in transientWindowRange * * Note: if window->type == none, we compute a single rectangular window covering all the data. * * Note2: if the experimental switch useFReg is true, returns FReg=F - log(D) instead of F. This option is of * little practical interest, except for demonstrating that marginalizing (1/D)e^F is *less* sensitive * than marginalizing e^F (see transient methods-paper [in prepartion]) * */ transientFstatMap_t * XLALComputeTransientFstatMap ( const MultiFstatAtomVector *multiFstatAtoms, /**< [in] multi-IFO F-statistic atoms */ transientWindowRange_t windowRange, /**< [in] type and parameters specifying transient window range to search */ BOOLEAN useFReg /**< [in] experimental switch: compute FReg = F - log(D) instead of F */ ) { /* check input consistency */ if ( !multiFstatAtoms || !multiFstatAtoms->data || !multiFstatAtoms->data[0]) { XLALPrintError ("%s: invalid NULL input.\n", __func__ ); XLAL_ERROR_NULL ( XLAL_EINVAL ); } if ( windowRange.type >= TRANSIENT_LAST ) { XLALPrintError ("%s: unknown window-type (%d) passes as input. Allowed are [0,%d].\n", __func__, windowRange.type, TRANSIENT_LAST-1); XLAL_ERROR_NULL ( XLAL_EINVAL ); } /* ----- pepare return container ----- */ transientFstatMap_t *ret; if ( (ret = XLALCalloc ( 1, sizeof(*ret) )) == NULL ) { XLALPrintError ("%s: XLALCalloc(1,%zu) failed.\n", __func__, sizeof(*ret) ); XLAL_ERROR_NULL ( XLAL_ENOMEM ); } /* ----- first combine all multi-atoms into a single atoms-vector with *unique* timestamps */ FstatAtomVector *atoms; UINT4 TAtom = multiFstatAtoms->data[0]->TAtom; UINT4 TAtomHalf = TAtom/2; /* integer division */ if ( (atoms = XLALmergeMultiFstatAtomsBinned ( multiFstatAtoms, TAtom )) == NULL ) { XLALPrintError ("%s: XLALmergeMultiFstatAtomsSorted() failed with code %d\n", __func__, xlalErrno ); XLAL_ERROR_NULL ( XLAL_EFUNC ); } UINT4 numAtoms = atoms->length; /* actual data spans [t0_data, t0_data + numAtoms * TAtom] in steps of TAtom */ UINT4 t0_data = atoms->data[0].timestamp; UINT4 t1_data = atoms->data[numAtoms-1].timestamp + TAtom; /* ----- special treatment of window_type = none ==> replace by rectangular window spanning all the data */ if ( windowRange.type == TRANSIENT_NONE ) { windowRange.type = TRANSIENT_RECTANGULAR; windowRange.t0 = t0_data; windowRange.t0Band = 0; windowRange.dt0 = TAtom; /* irrelevant */ windowRange.tau = numAtoms * TAtom; windowRange.tauBand = 0; windowRange.dtau = TAtom; /* irrelevant */ } /* NOTE: indices {i,j} enumerate *actual* atoms and their timestamps t_i, while the * indices {m,n} enumerate the full grid of values in [t0_min, t0_max]x[Tcoh_min, Tcoh_max] in * steps of deltaT. This allows us to deal with gaps in the data in a transparent way. * * NOTE2: we operate on the 'binned' atoms returned from XLALmergeMultiFstatAtomsBinned(), * which means we can safely assume all atoms to be lined up perfectly on a 'deltaT' binned grid. * * The mapping used will therefore be {i,j} -> {m,n}: * m = offs_i / deltaT = start-time offset from t0_min measured in deltaT * n = Tcoh_ij / deltaT = duration Tcoh_ij measured in deltaT, * * where * offs_i = t_i - t0_min * Tcoh_ij = t_j - t_i + deltaT * */ /* We allocate a matrix {m x n} = t0Range * TcohRange elements * covering the full timerange the transient window-range [t0,t0+t0Band]x[tau,tau+tauBand] */ UINT4 N_t0Range = (UINT4) floor ( windowRange.t0Band / windowRange.dt0 ) + 1; UINT4 N_tauRange = (UINT4) floor ( windowRange.tauBand / windowRange.dtau ) + 1; if ( ( ret->F_mn = gsl_matrix_calloc ( N_t0Range, N_tauRange )) == NULL ) { XLALPrintError ("%s: failed ret->F_mn = gsl_matrix_calloc ( %d, %d )\n", __func__, N_tauRange, N_t0Range ); XLAL_ERROR_NULL ( XLAL_ENOMEM ); } transientWindow_t win_mn; win_mn.type = windowRange.type; ret->maxF = -1.0; // keep track of loudest F-stat point. Initializing to a negative value ensures that we always update at least once and hence return sane t0_d_ML, tau_d_ML even if there is only a single bin where F=0 happens. UINT4 m, n; /* ----- OUTER loop over start-times [t0,t0+t0Band] ---------- */ for ( m = 0; m < N_t0Range; m ++ ) /* m enumerates 'binned' t0 start-time indices */ { /* compute Fstat-atom index i_t0 in [0, numAtoms) */ win_mn.t0 = windowRange.t0 + m * windowRange.dt0; INT4 i_tmp = ( win_mn.t0 - t0_data + TAtomHalf ) / TAtom; // integer round: floor(x+0.5) if ( i_tmp < 0 ) i_tmp = 0; UINT4 i_t0 = (UINT4)i_tmp; if ( i_t0 >= numAtoms ) i_t0 = numAtoms - 1; /* ----- INNER loop over timescale-parameter tau ---------- */ REAL4 Ad=0, Bd=0, Cd=0; COMPLEX8 Fa=0, Fb=0; UINT4 i_t1_last = i_t0; for ( n = 0; n < N_tauRange; n ++ ) { /* translate n into an atoms end-index for this search interval [t0, t0+Tcoh], * giving the index range of atoms to sum over */ win_mn.tau = windowRange.tau + n * windowRange.dtau; /* get end-time t1 of this transient-window search */ UINT4 t0, t1; if ( XLALGetTransientWindowTimespan ( &t0, &t1, win_mn ) != XLAL_SUCCESS ) { XLALPrintError ("%s: XLALGetTransientWindowTimespan() failed.\n", __func__ ); XLAL_ERROR_NULL ( XLAL_EFUNC ); } /* compute window end-time Fstat-atom index i_t1 in [0, numAtoms) */ i_tmp = ( t1 - t0_data + TAtomHalf ) / TAtom - 1; // integer round: floor(x+0.5) if ( i_tmp < 0 ) i_tmp = 0; UINT4 i_t1 = (UINT4)i_tmp; if ( i_t1 >= numAtoms ) i_t1 = numAtoms - 1; /* protection against degenerate 1-atom case: (this implies D=0 and therefore F->inf) */ if ( i_t1 == i_t0 ) { XLALPrintError ("%s: encountered a single-atom Fstat-calculation. This is degenerate and cannot be computed!\n", __func__ ); XLALPrintError ("Window-values m=%d (t0=%d=t0_data + %d), n=%d (tau=%d) ==> t1_data - t0 = %d\n", m, win_mn.t0, i_t0 * TAtom, n, win_mn.tau, t1_data - win_mn.t0 ); XLALPrintError ("The most likely cause is that your t0-range covered all of your data: t0 must stay away *at least* 2*TAtom from the end of the data!\n"); XLAL_ERROR_NULL ( XLAL_EDOM ); } /* now we have two valid atoms-indices [i_t0, i_t1] spanning our Fstat-window to sum over, * using weights according to the window-type */ switch ( windowRange.type ) { case TRANSIENT_RECTANGULAR: #if 0 /* 'vanilla' unoptimized method, for sanity checks with 'optimized' method */ Ad=0; Bd=0; Cd=0; Fa=0; Fb=0; for ( UINT4 i = i_t0; i <= i_t1; i ++ ) #else /* special optimiziation in the rectangular-window case: just add on to previous tau values * ie re-use the sum over [i_t0, i_t1_last] from the pevious tau-loop iteration */ for ( UINT4 i = i_t1_last; i <= i_t1; i ++ ) #endif { FstatAtom *thisAtom_i = &atoms->data[i]; /* now add on top of previous values, summed from [i_t0, i_t1_last] */ Ad += thisAtom_i->a2_alpha; Bd += thisAtom_i->b2_alpha; Cd += thisAtom_i->ab_alpha; Fa += thisAtom_i->Fa_alpha; Fb += thisAtom_i->Fb_alpha; } /* for i = i_t1_last : i_t1 */ i_t1_last = i_t1 + 1; /* keep track of up to where we summed for the next iteration */ break; case TRANSIENT_EXPONENTIAL: /* reset all values */ Ad=0; Bd=0; Cd=0; Fa=0; Fb=0; for ( UINT4 i = i_t0; i <= i_t1; i ++ ) { FstatAtom *thisAtom_i = &atoms->data[i]; UINT4 t_i = thisAtom_i->timestamp; REAL8 win_i; win_i = XLALGetExponentialTransientWindowValue ( t_i, t0, t1, win_mn.tau ); REAL8 win2_i = win_i * win_i; Ad += thisAtom_i->a2_alpha * win2_i; Bd += thisAtom_i->b2_alpha * win2_i; Cd += thisAtom_i->ab_alpha * win2_i; Fa += thisAtom_i->Fa_alpha * win_i; Fb += thisAtom_i->Fb_alpha * win_i; } /* for i in [i_t0, i_t1] */ break; default: XLALPrintError ("%s: invalid transient window type %d not in [%d, %d].\n", __func__, windowRange.type, TRANSIENT_NONE, TRANSIENT_LAST -1 ); XLAL_ERROR_NULL ( XLAL_EINVAL ); break; } /* switch window.type */ /* generic F-stat calculation from A,B,C, Fa, Fb */ REAL4 Dd = ( Ad * Bd - Cd * Cd ); REAL4 DdInv = 0; if ( Dd > 0 ) { /* safety catch as in XLALWeightMultiAMCoeffs(): make it so that in the end F=0 instead of -nan */ DdInv = 1.0f / Dd; } REAL4 twoF = XLALComputeFstatFromFaFb ( Fa, Fb, Ad, Bd, Cd, 0, DdInv ); REAL4 F = 0.5 * twoF; /* keep track of loudest F-stat value encountered over the m x n matrix */ if ( F > ret->maxF ) { ret->maxF = F; ret->t0_ML = win_mn.t0; /* start-time t0 corresponding to Fmax */ ret->tau_ML = win_mn.tau; /* timescale tau corresponding to Fmax */ } /* if requested: use 'regularized' F-stat: log ( 1/D * e^F ) = F + log(1/D) */ if ( useFReg ) F += log( DdInv ); /* and store this in Fstat-matrix as element {m,n} */ gsl_matrix_set ( ret->F_mn, m, n, F ); } /* for n in n[tau] : n[tau+tauBand] */ } /* for m in m[t0] : m[t0+t0Band] */ /* free internal mem */ XLALDestroyFstatAtomVector ( atoms ); /* return end product: F-stat map */ return ret; } /* XLALComputeTransientFstatMap() */