Example #1
0
int XLALCOMPLEX16FreqTimeFFT(
    COMPLEX16TimeSeries            *time,
    const COMPLEX16FrequencySeries *freq,
    const COMPLEX16FFTPlan         *plan
    )
{
  COMPLEX16Vector *tmp;
  UINT4 k;

  if ( ! freq || ! time || ! plan )
    XLAL_ERROR( XLAL_EFAULT );
  if ( freq->deltaF <= 0.0 )
    XLAL_ERROR( XLAL_EINVAL );
  if ( plan->sign != 1 )
    XLAL_ERROR( XLAL_EINVAL );

  /* create temporary workspace */
  tmp = XLALCreateCOMPLEX16Vector( freq->data->length );
  if ( ! tmp )
    XLAL_ERROR( XLAL_EFUNC );

  /* pack the frequency series and multiply by deltaF */
  for ( k = 0; k < freq->data->length / 2; ++k )
    tmp->data[k+(freq->data->length+1)/2] = freq->deltaF * freq->data->data[k];
  for ( k = freq->data->length / 2; k < freq->data->length; ++k )
    tmp->data[k-freq->data->length/2] = freq->deltaF * freq->data->data[k];

  /* perform transform */
  if ( XLALCOMPLEX16VectorFFT( time->data, tmp, plan ) == XLAL_FAILURE )
  {
    int saveErrno = xlalErrno;
    xlalErrno = 0;
    XLALDestroyCOMPLEX16Vector( tmp );
    xlalErrno = saveErrno;
    XLAL_ERROR( XLAL_EFUNC );
  }

  /* destroy temporary workspace */
  XLALDestroyCOMPLEX16Vector( tmp );
  if ( xlalErrno )
    XLAL_ERROR( XLAL_EFUNC );

  /* adjust the units */
  if ( ! XLALUnitMultiply( &time->sampleUnits, &freq->sampleUnits, &lalHertzUnit ) )
    XLAL_ERROR( XLAL_EFUNC );

  /* remaining fields */
  time->f0     = freq->f0 + freq->deltaF * floor( freq->data->length / 2 );
  time->epoch  = freq->epoch;
  time->deltaT = 1.0 / ( freq->deltaF * freq->data->length );

  return 0;
}
Example #2
0
/**
 * \brief Chops and remerges data into stationary segments
 *
 * This function finds segments of data that appear to be stationary (have the same standard deviation).
 *
 * The function first attempts to chop up the data into as many stationary segments as possible. The splitting may not
 * be optimal, so it then tries remerging consecutive segments to see if the merged segments show more evidence of
 * stationarity. <b>[NOTE: Remerging is currently turned off and will make very little difference to the algorithm]</b>.
 * It then, if necessary, chops the segments again to make sure there are none greater than the required \c chunkMax.
 * The default \c chunkMax is 0, so this rechopping will not normally happen.
 *
 * This is all performed on data that has had a running median subtracted, to try and removed any underlying trends in
 * the data (e.g. those caused by a strong signal), which might affect the calculations (which assume the data is
 * Gaussian with zero mean).
 *
 * If the \c outputchunks is non-zero then a list of the segments will be output to a file called \c data_segment_list.txt,
 * with a prefix of the detector name.
 *
 * \param data [in] A data structure
 * \param chunkMin [in] The minimum length of a segment
 * \param chunkMax [in] The maximum length of a segment
 * \param outputchunks [in] A flag to check whether to output the segments
 *
 * \return A vector of segment/chunk lengths
 *
 * \sa subtract_running_median
 * \sa chop_data
 * \sa merge_data
 * \sa rechop_data
 */
UINT4Vector *chop_n_merge( LALInferenceIFOData *data, UINT4 chunkMin, UINT4 chunkMax, UINT4 outputchunks ){
  UINT4 j = 0;

  UINT4Vector *chunkLengths = NULL;
  UINT4Vector *chunkIndex = NULL;

  COMPLEX16Vector *meddata = NULL;

  /* subtract a running median value from the data to remove any underlying trends (e.g. caused by a string signal) that
   * might affect the chunk calculations (which can assume the data is Gaussian with zero mean). */
  meddata = subtract_running_median( data->compTimeData->data );

  /* pass chop data a gsl_vector_view, so that internally it can use vector views rather than having to create new vectors */
  gsl_vector_complex_view meddatagsl = gsl_vector_complex_view_array((double*)meddata->data, meddata->length);
  chunkIndex = chop_data( &meddatagsl.vector, chunkMin );

  /* DON'T BOTHER WITH THE MERGING AS IT WILL MAKE VERY LITTLE DIFFERENCE */
  /* merge_data( meddata, &chunkIndex ); */

  XLALDestroyCOMPLEX16Vector( meddata ); /* free memory */

  /* if a maximum chunk length is defined then rechop up the data, to segment any chunks longer than this value */
  if ( chunkMax > chunkMin ) { rechop_data( &chunkIndex, chunkMax, chunkMin ); }

  chunkLengths = XLALCreateUINT4Vector( chunkIndex->length );

  /* go through segments and turn into vector of chunk lengths */
  for ( j = 0; j < chunkIndex->length; j++ ){
    if ( j == 0 ) { chunkLengths->data[j] = chunkIndex->data[j]; }
    else { chunkLengths->data[j] = chunkIndex->data[j] - chunkIndex->data[j-1]; }
  }

  /* if required output the chunk end indices and lengths to a file */
  if ( outputchunks ){
    FILE *fpsegs = NULL;

    /* set detector name as prefix */
    CHAR *outfile = NULL;
    outfile = XLALStringDuplicate( data->detector->frDetector.prefix );
    outfile = XLALStringAppend( outfile, "data_segment_list.txt" );

    /* check if file exists, i.e. given mutliple frequency harmonics, and if so open for appending */
    FILE *fpcheck = NULL;
    if ( (fpcheck = fopen(outfile, "r")) != NULL ){
      fclose(fpcheck);
      if ( (fpsegs = fopen(outfile, "a")) == NULL ){
        fprintf(stderr, "Non-fatal error open file to output segment list.\n");
        return chunkLengths;
      }
    }
    else{
      if ( (fpsegs = fopen(outfile, "w")) == NULL ){
        fprintf(stderr, "Non-fatal error open file to output segment list.\n");
        return chunkLengths;
      }
    }
    XLALFree( outfile );

    for ( j = 0; j < chunkIndex->length; j++ ) { fprintf(fpsegs, "%u\t%u\n", chunkIndex->data[j], chunkLengths->data[j]); }

    /* add space at the end so that you can separate lists from different detector data streams */
    fprintf(fpsegs, "\n");

    fclose( fpsegs );
  }

  XLALDestroyUINT4Vector( chunkIndex );

  return chunkLengths;
}
/** \see See \ref BilinearTransform_c for documentation */
int XLALWToZCOMPLEX16ZPGFilter( COMPLEX16ZPGFilter *filter )
{
  INT4 i;        /* A counter. */
  INT4 j;        /* Another counter. */
  INT4 num;      /* The total number of zeros or poles. */
  INT4 numZeros; /* The number of finite zeros. */
  INT4 numPoles; /* The number of finite poles. */
  COMPLEX16 *a;   /* A zero or pole in the w plane. */
  COMPLEX16 *b;   /* A zero or pole in the z plane. */
  COMPLEX16 *g;   /* A gain correction factor. */
  COMPLEX16Vector *z=NULL;   /* Vector of zeros or poles in z plane. */
  COMPLEX16Vector *gain=NULL; /* Vector of gain correction factors. */
  COMPLEX16Vector null;       /* A vector of zero length. */
  INT4Vector *idx=NULL;    /* Index array for sorting absGain. */

  /* Make sure the filter pointer is non-null. */
  if ( ! filter )
    XLAL_ERROR( XLAL_EFAULT );

  /* If the filter->zeros or filter->poles pointers is null, this
     means that there are no zeros or no poles.  For simplicity, we
     set the pointer to point to a vector of zero length. */
  null.length=0;
  null.data=NULL;
  if(!filter->zeros)
    filter->zeros=&null;
  if(!filter->poles)
    filter->poles=&null;

  /* Check that the vector lengths are non-negative, and, if positive,
     that the vector data pointer is non-null. */
  numZeros=filter->zeros->length;
  if (numZeros<0)
    XLAL_ERROR(XLAL_EINVAL);
  if(numZeros>0)
    if (!filter->zeros->data)
      XLAL_ERROR(XLAL_EFAULT);
  numPoles=filter->poles->length;
  if (numPoles<0)
    XLAL_ERROR(XLAL_EINVAL);
  if(numPoles>0)
    if (!filter->poles->data)
      XLAL_ERROR(XLAL_EFAULT);

  /* Compute the total number of zeros and poles in the w-plane,
     including those at w=infinity. */
  num = (numZeros>numPoles) ? numZeros : numPoles;
  numZeros=numPoles=num;

  /* If there are neither zeros nor poles, then there is nothing to
     transform.  (The <0 case should never occur if the ASSERT()
     macros have done their job, but is included for extra safety.) */
  if(num<=0){
    filter->zeros=NULL;
    filter->poles=NULL;
    return 0;
  }

  /* Compute the revised number of zeros and poles in the z-plane,
     excluding those at z=infinity (w=-i). */
  for(i=0,a=filter->zeros->data;i<(INT4)filter->zeros->length;i++,a++)
    if((creal(*a)==0.0)&&(cimag(*a)==-1.0))
      numZeros--;
  for(i=0,a=filter->poles->data;i<(INT4)filter->poles->length;i++,a++)
    if((creal(*a)==0.0)&&(cimag(*a)==-1.0))
      numPoles--;

  /* Create the vector of gain correction factors. */
  /* Create the new vector of zeros. */
  gain=XLALCreateCOMPLEX16Vector(filter->zeros->length+filter->poles->length);
  z=XLALCreateCOMPLEX16Vector(numZeros);
  if (!gain||!z)
  {
    XLALDestroyCOMPLEX16Vector(gain);
    XLALDestroyCOMPLEX16Vector(z);
    XLAL_ERROR(XLAL_EFUNC);
  }
  g=gain->data;
  b=z->data;

  /* Transform existing zeros from w to z, except for those at w=-i,
     which are mapped to z=infinity.  At the same time, compute the
     gain correction factors. */
  for(i=0,j=0,a=filter->zeros->data;i<(INT4)filter->zeros->length;
      i++,a++,g++){
    REAL8 ar=creal(*a);
    REAL8 ai=cimag(*a);
    if(ar==0.0){
      if(ai==-1.0){
	/* w=-i is mapped to z=infinity. */
	*g=2.0*I;
      }else{
	/* w=i*y is mapped to z=(1-y)/(1+y). */
	*b=(1.0-ai)/(1.0+ai);
	*g=-(1.0+ai)*I;
	b++;
	j++;
      }
    }else if(fabs(1.0+ai)>fabs(ar)){
      REAL8 ratio = -ar/(1.0+ai);
      REAL8 denom = 1.0+ai - ratio*ar;

      *b = (1.0-ai + ratio*ar)/denom;
      *b += I*(ar - ratio*(1.0-ai))/denom;
      *g = -ar;
      *g += -(1.0+ai)*I;
      b++;
      j++;
    }else{
      REAL8 ratio = -(1.0+ai)/ar;
      REAL8 denom = -ar + ratio*(1.0+ai);

      *b = ((1.0-ai)*ratio + ar)/denom;
      *b += I*(ar*ratio - 1.0+ai)/denom;
      *g = -ar;
      *g += -(1.0+ai)*I;
      b++;
      j++;
    }
  }
  /* Transform any remaining zeros at w=infinity to z=-1. */
  for(;j<numZeros;b++,j++){
    *b = -1.0;
  }
  /* Replace the old filter zeros with the new ones. */
  if(filter->zeros->length>0)
    XLALDestroyCOMPLEX16Vector(filter->zeros);
  filter->zeros=z;
  z=NULL;

  /* Create the new vector of poles. */
  z=XLALCreateCOMPLEX16Vector(numPoles);
  if (!gain||!z)
  {
    XLALDestroyCOMPLEX16Vector(gain);
    XLAL_ERROR(XLAL_EFUNC);
  }
  b=z->data;
  /* Transform existing poles from w to z, except for those at w=-i,
     which are mapped to z=infinity.  At the same time, compute the
     gain correction factors. */
  for(i=0,j=0,a=filter->poles->data;i<(INT4)filter->poles->length;
      i++,a++,g++){
    REAL8 ar=creal(*a);
    REAL8 ai=cimag(*a);
    if(ar==0.0){
      if(ai==-1.0){
	/* w=-i is mapped to z=infinity. */
	*g=-0.5*I;
      }else{
	/* w=i*y is mapped to z=(1-y)/(1+y). */
	*b=(1.0-ai)/(1.0+ai);
	*g=I*1.0/(1.0+ai);
	b++;
	j++;
      }
    }else if(fabs(1.0+ai)>fabs(ar)){
      REAL8 ratio = -ar/(1.0+ai);
      REAL8 denom = 1.0+ai - ratio*ar;

      *b = (1.0-ai + ratio*ar)/denom;
      *b += I*(ar - ratio*(1.0-ai))/denom;
      *g = ratio/denom;
      *g += I*1.0/denom;
      b++;
      j++;
    }else{
      REAL8 ratio = -(1.0+ai)/ar;
      REAL8 denom = -ar + ratio*(1.0+ai);

      *b = ((1.0-ai)*ratio + ar)/denom;
      *b += I*(ar*ratio - 1.0+ai)/denom;
      *g = ratio/denom;
      *g += I*1.0/denom;
      b++;
      j++;
    }
  }
  /* Transform any remaining poles at w=infinity to z=-1. */
  for(;j<numPoles;b++,j++){
    *b = -1.0;
  }
  /* Replace the old filter poles with the new ones. */
  if(filter->poles->length>0)
    XLALDestroyCOMPLEX16Vector(filter->poles);
  filter->poles=z;
  z=NULL;

  /* To avoid numerical overflow when applying the gain correction
     factors, we should multiply alternately by large and small
     factors.  Create an idx vector that indexes the magnitudes
     from small to large. */
  idx=XLALCreateINT4Vector(gain->length);
  if(!idx||XLALHeapIndex(idx->data,gain->data,gain->length,sizeof(*gain->data),NULL,CompareCOMPLEX16Abs)<0)
  {
    XLALDestroyCOMPLEX16Vector(gain);
    XLALDestroyINT4Vector(idx);
    XLAL_ERROR(XLAL_EFUNC);
  }

  /* Now multiply the gain alternately by small and large correction
     factors. */
  for(i=0,j=gain->length-1;i<j;i++,j--){
    /* Multiply the small and largest factors together. */
    /* Multiply the gain by the combined factor. */
    filter->gain *= gain->data[idx->data[i]] * gain->data[idx->data[j]];
  }
  if(i==j){
    /* Multiply by the remaining odd factor. */
    filter->gain *= gain->data[idx->data[i]];
  }

  /* Free remaining temporary vectors, and exit. */
  XLALDestroyCOMPLEX16Vector(gain);
  XLALDestroyINT4Vector(idx);
  return 0;
}
/**
 * The main workhorse function for performing the ringdown attachment for EOB
 * models EOBNRv2 and SEOBNRv1. This is the function which gets called by the
 * code generating the full IMR waveform once generation of the inspiral part
 * has been completed.
 * The ringdown is attached using the hybrid comb matching detailed in
 * The method is describe in Sec. II C of Pan et al. PRD 84, 124052 (2011),
 * specifically Eqs. 30 - 32.. Further details of the
 * implementation of the found in the DCC document T1100433.
 * In SEOBNRv1, the last physical overtone is replace by a pseudoQNM. See
 * Taracchini et al. PRD 86, 024011 (2012) for details.
 * STEP 1) Get mass and spin of the final black hole and the complex ringdown frequencies
 * STEP 2) Based on least-damped-mode decay time, allocate memory for rigndown waveform
 * STEP 3) Get values and derivatives of inspiral waveforms at matching comb points
 * STEP 4) Solve QNM coefficients and generate ringdown waveforms
 * STEP 5) Stitch inspiral and ringdown waveoforms
 */
static INT4 XLALSimIMREOBHybridAttachRingdown(
  REAL8Vector *signal1,    /**<< OUTPUT, Real of inspiral waveform to which we attach ringdown */
  REAL8Vector *signal2,    /**<< OUTPUT, Imag of inspiral waveform to which we attach ringdown */
  const INT4   l,          /**<< Current mode l */
  const INT4   m,          /**<< Current mode m */
  const REAL8  dt,         /**<< Sample time step (in seconds) */
  const REAL8  mass1,      /**<< First component mass (in Solar masses) */
  const REAL8  mass2,      /**<< Second component mass (in Solar masses) */
  const REAL8  spin1x,     /**<<The spin of the first object; only needed for spin waveforms */
  const REAL8  spin1y,     /**<<The spin of the first object; only needed for spin waveforms */
  const REAL8  spin1z,     /**<<The spin of the first object; only needed for spin waveforms */
  const REAL8  spin2x,     /**<<The spin of the second object; only needed for spin waveforms */
  const REAL8  spin2y,     /**<<The spin of the second object; only needed for spin waveforms */
  const REAL8  spin2z,     /**<<The spin of the second object; only needed for spin waveforms */
  REAL8Vector *timeVec,    /**<< Vector containing the time values */
  REAL8Vector *matchrange, /**<< Time values chosen as points for performing comb matching */
  Approximant  approximant /**<<The waveform approximant being used */
  )
{

      COMPLEX16Vector *modefreqs;
      UINT4 Nrdwave;
      UINT4 j;

      UINT4 nmodes;
      REAL8Vector		*rdwave1;
      REAL8Vector		*rdwave2;
      REAL8Vector		*rinspwave;
      REAL8Vector		*dinspwave;
      REAL8Vector		*ddinspwave;
      REAL8VectorSequence	*inspwaves1;
      REAL8VectorSequence	*inspwaves2;
      REAL8 eta, a, NRPeakOmega22; /* To generate pQNM frequency */
      REAL8 mTot; /* In geometric units */
      REAL8 spin1[3] = { spin1x, spin1y, spin1z };
      REAL8 spin2[3] = { spin2x, spin2y, spin2z };
      REAL8 finalMass, finalSpin;

      mTot  = (mass1 + mass2) * LAL_MTSUN_SI;
      eta       = mass1 * mass2 / ( (mass1 + mass2) * (mass1 + mass2) );

      /*
       * STEP 1) Get mass and spin of the final black hole and the complex ringdown frequencies
       */

      /* Create memory for the QNM frequencies */
      nmodes = 8;
      modefreqs = XLALCreateCOMPLEX16Vector( nmodes );
      if ( !modefreqs )
      {
        XLAL_ERROR( XLAL_ENOMEM );
      }

      if ( XLALSimIMREOBGenerateQNMFreqV2( modefreqs, mass1, mass2, spin1, spin2, l, m, nmodes, approximant ) == XLAL_FAILURE )
      {
        XLALDestroyCOMPLEX16Vector( modefreqs );
        XLAL_ERROR( XLAL_EFUNC );
      }

      /* Call XLALSimIMREOBFinalMassSpin() to get mass and spin of the final black hole */
      if ( XLALSimIMREOBFinalMassSpin(&finalMass, &finalSpin, mass1, mass2, spin1, spin2, approximant) == XLAL_FAILURE )
      {
        XLAL_ERROR( XLAL_EFUNC );
      }

      if ( approximant == SEOBNRv1 )
      {
          /* Replace the last QNM with pQNM */
          /* We assume aligned/antialigned spins here */
          a  = (spin1[2] + spin2[2]) / 2. * (1.0 - 2.0 * eta) + (spin1[2] - spin2[2]) / 2. * (mass1 - mass2) / (mass1 + mass2);
          NRPeakOmega22 = GetNRSpinPeakOmega( l, m, eta, a ) / mTot;
          /*printf("a and NRomega in QNM freq: %.16e %.16e %.16e %.16e %.16e\n",spin1[2],spin2[2],
                 mTot/LAL_MTSUN_SI,a,NRPeakOmega22*mTot);*/
          modefreqs->data[7] = (NRPeakOmega22/finalMass + creal(modefreqs->data[0])) / 2.;
          modefreqs->data[7] += I * 10./3. * cimag(modefreqs->data[0]);
      }

      /*for (j = 0; j < nmodes; j++)
      {
        printf("QNM frequencies: %d %d %d %e %e\n",l,m,j,modefreqs->data[j].re*mTot,1./modefreqs->data[j].im/mTot);
      }*/

      /* Ringdown signal length: 10 times the decay time of the n=0 mode */
      Nrdwave = (INT4) (EOB_RD_EFOLDS / cimag(modefreqs->data[0]) / dt);

      /* Check the value of attpos, to prevent memory access problems later */
      if ( matchrange->data[0] * mTot / dt < 5 || matchrange->data[1]*mTot/dt > matchrange->data[2] *mTot/dt - 2 )
      {
        XLALPrintError( "More inspiral points needed for ringdown matching.\n" );
        //printf("%.16e,%.16e,%.16e\n",matchrange->data[0] * mTot / dt, matchrange->data[1]*mTot/dt, matchrange->data[2] *mTot/dt - 2);
        XLALDestroyCOMPLEX16Vector( modefreqs );
        XLAL_ERROR( XLAL_EFAILED );
      }

      /*
       * STEP 2) Based on least-damped-mode decay time, allocate memory for rigndown waveform
       */

      /* Create memory for the ring-down and full waveforms, and derivatives of inspirals */

      rdwave1 = XLALCreateREAL8Vector( Nrdwave );
      rdwave2 = XLALCreateREAL8Vector( Nrdwave );
      rinspwave = XLALCreateREAL8Vector( 6 );
      dinspwave = XLALCreateREAL8Vector( 6 );
      ddinspwave = XLALCreateREAL8Vector( 6 );
      inspwaves1 = XLALCreateREAL8VectorSequence( 3, 6 );
      inspwaves2 = XLALCreateREAL8VectorSequence( 3, 6 );

      /* Check memory was allocated */
      if ( !rdwave1 || !rdwave2 || !rinspwave || !dinspwave 
	   || !ddinspwave || !inspwaves1 || !inspwaves2 )
      {
        XLALDestroyCOMPLEX16Vector( modefreqs );
        if (rdwave1)    XLALDestroyREAL8Vector( rdwave1 );
        if (rdwave2)    XLALDestroyREAL8Vector( rdwave2 );
        if (rinspwave)  XLALDestroyREAL8Vector( rinspwave );
        if (dinspwave)  XLALDestroyREAL8Vector( dinspwave );
        if (ddinspwave) XLALDestroyREAL8Vector( ddinspwave );
        if (inspwaves1) XLALDestroyREAL8VectorSequence( inspwaves1 );
        if (inspwaves2) XLALDestroyREAL8VectorSequence( inspwaves2 );
        XLAL_ERROR( XLAL_ENOMEM );
      }

      memset( rdwave1->data, 0, rdwave1->length * sizeof( REAL8 ) );
      memset( rdwave2->data, 0, rdwave2->length * sizeof( REAL8 ) );

      /*
       * STEP 3) Get values and derivatives of inspiral waveforms at matching comb points
       */

      /* Generate derivatives of the last part of inspiral waves */
      /* Get derivatives of signal1 */
      if ( XLALGenerateHybridWaveDerivatives( rinspwave, dinspwave, ddinspwave, timeVec, signal1, 
			matchrange, dt, mass1, mass2 ) == XLAL_FAILURE )
      {
        XLALDestroyCOMPLEX16Vector( modefreqs );
        XLALDestroyREAL8Vector( rdwave1 );
        XLALDestroyREAL8Vector( rdwave2 );
        XLALDestroyREAL8Vector( rinspwave );
        XLALDestroyREAL8Vector( dinspwave );
        XLALDestroyREAL8Vector( ddinspwave );
        XLALDestroyREAL8VectorSequence( inspwaves1 );
        XLALDestroyREAL8VectorSequence( inspwaves2 );
        XLAL_ERROR( XLAL_EFUNC );
      }
      for (j = 0; j < 6; j++)
      {
	    inspwaves1->data[j] = rinspwave->data[j];
	    inspwaves1->data[j + 6] = dinspwave->data[j];
	    inspwaves1->data[j + 12] = ddinspwave->data[j];
      }

      /* Get derivatives of signal2 */
      if ( XLALGenerateHybridWaveDerivatives( rinspwave, dinspwave, ddinspwave, timeVec, signal2, 
			matchrange, dt, mass1, mass2 ) == XLAL_FAILURE )
      {
        XLALDestroyCOMPLEX16Vector( modefreqs );
        XLALDestroyREAL8Vector( rdwave1 );
        XLALDestroyREAL8Vector( rdwave2 );
        XLALDestroyREAL8Vector( rinspwave );
        XLALDestroyREAL8Vector( dinspwave );
        XLALDestroyREAL8Vector( ddinspwave );
        XLALDestroyREAL8VectorSequence( inspwaves1 );
        XLALDestroyREAL8VectorSequence( inspwaves2 );
        XLAL_ERROR( XLAL_EFUNC );
      }
      for (j = 0; j < 6; j++)
      {
	    inspwaves2->data[j] = rinspwave->data[j];
	    inspwaves2->data[j + 6] = dinspwave->data[j];
	    inspwaves2->data[j + 12] = ddinspwave->data[j];
      }


      /*
       * STEP 4) Solve QNM coefficients and generate ringdown waveforms
       */

      /* Generate ring-down waveforms */
      if ( XLALSimIMREOBHybridRingdownWave( rdwave1, rdwave2, dt, mass1, mass2, inspwaves1, inspwaves2,
			  modefreqs, matchrange ) == XLAL_FAILURE )
      {
        XLALDestroyCOMPLEX16Vector( modefreqs );
        XLALDestroyREAL8Vector( rdwave1 );
        XLALDestroyREAL8Vector( rdwave2 );
        XLALDestroyREAL8Vector( rinspwave );
        XLALDestroyREAL8Vector( dinspwave );
        XLALDestroyREAL8Vector( ddinspwave );
        XLALDestroyREAL8VectorSequence( inspwaves1 );
        XLALDestroyREAL8VectorSequence( inspwaves2 );
        XLAL_ERROR( XLAL_EFUNC );
      }

      /*
       * STEP 5) Stitch inspiral and ringdown waveoforms
       */

      /* Generate full waveforms, by stitching inspiral and ring-down waveforms */
      UINT4 attachIdx = matchrange->data[1] * mTot / dt;
      for (j = 1; j < Nrdwave; ++j)
      {
	    signal1->data[j + attachIdx] = rdwave1->data[j];
	    signal2->data[j + attachIdx] = rdwave2->data[j];
      }

      memset( signal1->data+Nrdwave+attachIdx, 0, (signal1->length - Nrdwave - attachIdx)*sizeof(REAL8) );
      memset( signal2->data+Nrdwave+attachIdx, 0, (signal2->length - Nrdwave - attachIdx)*sizeof(REAL8) );

      /* Free memory */
      XLALDestroyCOMPLEX16Vector( modefreqs );
      XLALDestroyREAL8Vector( rdwave1 );
      XLALDestroyREAL8Vector( rdwave2 );
      XLALDestroyREAL8Vector( rinspwave );
      XLALDestroyREAL8Vector( dinspwave );
      XLALDestroyREAL8Vector( ddinspwave );
      XLALDestroyREAL8VectorSequence( inspwaves1 );
      XLALDestroyREAL8VectorSequence( inspwaves2 );

      return XLAL_SUCCESS;
}