UNUSED static REAL8 XLALSimInspiralNRWaveformCheckFRef( UNUSED LALH5File* file, UNUSED REAL8 fRef ) { gsl_vector *omega_w_vec = NULL; LALH5File *omega_group = NULL; REAL8 min_fref, lowest_arr_fref; if (fRef > 0) { /* This is the declared f_lower (using 1MSUN total mass) */ XLALH5FileQueryScalarAttributeValue(&min_fref, file, "f_lower_at_1MSUN"); /* This is the lowest f_lower in the Omega-vs-time array */ omega_group = XLALH5GroupOpen(file, "Omega-vs-time"); ReadHDF5RealVectorDataset(omega_group, "Y", &omega_w_vec); lowest_arr_fref = gsl_vector_get(omega_w_vec, 0); lowest_arr_fref = lowest_arr_fref / (LAL_MTSUN_SI * LAL_PI); gsl_vector_free(omega_w_vec); /* First, is fRef smaller than min_fref? * Allow some float-precision slop */ if (fRef < 0.9999 * min_fref) { XLAL_ERROR(XLAL_EINVAL, "The supplied reference frequency is lower than the start frequency of the waveform. Start frequency (scaled by total mass) is %e and supplied fRef (scaled by total mass) is %e.\n", min_fref, fRef); } /* The Omega array may not quite extend to the minimum fRef, if that * happens, fall back on the attribute values. */ if (fRef < lowest_arr_fref) { fRef = -1; } } return fRef; }
/* Everything needs to be declared as unused in case HDF is not enabled. */ UNUSED static UINT4 XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer( UNUSED REAL8 *S1x, /**< [out] Dimensionless spin1x in LAL frame */ UNUSED REAL8 *S1y, /**< [out] Dimensionless spin1y in LAL frame */ UNUSED REAL8 *S1z, /**< [out] Dimensionless spin1z in LAL frame */ UNUSED REAL8 *S2x, /**< [out] Dimensionless spin2x in LAL frame */ UNUSED REAL8 *S2y, /**< [out] Dimensionless spin2y in LAL frame */ UNUSED REAL8 *S2z, /**< [out] Dimensionless spin2z in LAL frame */ UNUSED LALH5File* file /**< Pointer to HDF5 file */ ) { #ifndef LAL_HDF5_ENABLED XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled"); #else REAL8 nrSpin1x, nrSpin1y, nrSpin1z, nrSpin2x, nrSpin2y, nrSpin2z; REAL8 ln_hat_x, ln_hat_y, ln_hat_z, n_hat_x, n_hat_y, n_hat_z; XLALH5FileQueryScalarAttributeValue(&nrSpin1x, file, "spin1x"); XLALH5FileQueryScalarAttributeValue(&nrSpin1y, file, "spin1y"); XLALH5FileQueryScalarAttributeValue(&nrSpin1z, file, "spin1z"); XLALH5FileQueryScalarAttributeValue(&nrSpin2x, file, "spin2x"); XLALH5FileQueryScalarAttributeValue(&nrSpin2y, file, "spin2y"); XLALH5FileQueryScalarAttributeValue(&nrSpin2z, file, "spin2z"); XLALH5FileQueryScalarAttributeValue(&ln_hat_x , file, "LNhatx"); XLALH5FileQueryScalarAttributeValue(&ln_hat_y , file, "LNhaty"); XLALH5FileQueryScalarAttributeValue(&ln_hat_z , file, "LNhatz"); XLALH5FileQueryScalarAttributeValue(&n_hat_x , file, "nhatx"); XLALH5FileQueryScalarAttributeValue(&n_hat_y , file, "nhaty"); XLALH5FileQueryScalarAttributeValue(&n_hat_z , file, "nhatz"); *S1x = nrSpin1x * n_hat_x + nrSpin1y * n_hat_y + nrSpin1z * n_hat_z; *S1y = nrSpin1x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z) + nrSpin1y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z) + nrSpin1z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y) ; *S1z = nrSpin1x * ln_hat_x + nrSpin1y * ln_hat_y + nrSpin1z * ln_hat_z; *S2x = nrSpin2x * n_hat_x + nrSpin2y * n_hat_y + nrSpin2z * n_hat_z; *S2y = nrSpin2x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z) + nrSpin2y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z) + nrSpin2z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y); *S2z = nrSpin2x * ln_hat_x + nrSpin2y * ln_hat_y + nrSpin2z * ln_hat_z; return XLAL_SUCCESS; #endif }
/* Everything needs to be declared as unused in case HDF is not enabled. */ int XLALSimInspiralNRWaveformGetHplusHcross( UNUSED REAL8TimeSeries **hplus, /**< Output h_+ vector */ UNUSED REAL8TimeSeries **hcross, /**< Output h_x vector */ UNUSED REAL8 phiRef, /**< orbital phase at reference pt. */ UNUSED REAL8 inclination, /**< inclination angle */ UNUSED REAL8 deltaT, /**< sampling interval (s) */ UNUSED REAL8 m1, /**< mass of companion 1 (kg) */ UNUSED REAL8 m2, /**< mass of companion 2 (kg) */ UNUSED REAL8 r, /**< distance of source (m) */ UNUSED REAL8 fStart, /**< start GW frequency (Hz) */ UNUSED REAL8 fRef, /**< reference GW frequency (Hz) */ UNUSED REAL8 s1x, /**< initial value of S1x */ UNUSED REAL8 s1y, /**< initial value of S1y */ UNUSED REAL8 s1z, /**< initial value of S1z */ UNUSED REAL8 s2x, /**< initial value of S2x */ UNUSED REAL8 s2y, /**< initial value of S2y */ UNUSED REAL8 s2z, /**< initial value of S2z */ UNUSED const char *NRDataFile /**< Location of NR HDF file */ ) { #ifndef LAL_HDF5_ENABLED XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled"); #else /* Declarations */ UINT4 curr_idx; INT4 model, modem; size_t array_length; REAL8 nrEta; REAL8 S1x, S1y, S1z, S2x, S2y, S2z; REAL8 Mflower, time_start_M, time_start_s, time_end_M, time_end_s; REAL8 chi, est_start_time, curr_h_real, curr_h_imag; REAL8 theta, psi, calpha, salpha; REAL8 distance_scale_fac; COMPLEX16 curr_ylm; REAL8TimeSeries *hplus_corr; REAL8TimeSeries *hcross_corr; /* These keys follow a strict formulation and cannot be longer than 11 * characters */ char amp_key[20]; char phase_key[20]; gsl_vector *tmpVector=NULL; LALH5File *file, *group; LIGOTimeGPS tmpEpoch = LIGOTIMEGPSZERO; REAL8Vector *curr_amp, *curr_phase; /* Use solar masses for units. NR files will use * solar masses as well, so easier for that conversion */ m1 = m1 / LAL_MSUN_SI; m2 = m2 / LAL_MSUN_SI; file = XLALH5FileOpen(NRDataFile, "r"); /* Sanity checks on physical parameters passed to waveform * generator to guarantee consistency with NR data file. */ XLALH5FileQueryScalarAttributeValue(&nrEta, file, "eta"); if (fabs((m1 * m2) / pow((m1 + m2),2.0) - nrEta) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "MASSES ARE INCONSISTENT WITH THE MASS RATIO OF THE NR SIMULATION.\n"); } /* Read spin metadata, L_hat, n_hat from HDF5 metadata and make sure * the ChooseTDWaveform() input values are consistent with the data * recorded in the metadata of the HDF5 file. * PS: This assumes that the input spins are in the LAL frame! */ XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer(&S1x, &S1y, &S1z, &S2x, &S2y, &S2z, file); if (fabs(S1x - s1x) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1X IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S1y - s1y) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1Y IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S1z - s1z) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1Z IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2x - s2x) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2X IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2y - s2y) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2Y IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2z - s2z) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2Z IS INCONSISTENT WITH THE NR SIMULATION.\n"); } /* First estimate the length of time series that is needed. * Demand that 22 mode that is present and use that to figure this out */ XLALH5FileQueryScalarAttributeValue(&Mflower, file, "f_lower_at_1MSUN"); /* Figure out start time of data */ group = XLALH5GroupOpen(file, "amp_l2_m2"); ReadHDF5RealVectorDataset(group, "knots", &tmpVector); time_start_M = (REAL8)(gsl_vector_get(tmpVector, 0)); time_end_M = (REAL8)(gsl_vector_get(tmpVector, tmpVector->size - 1)); gsl_vector_free(tmpVector); time_start_s = time_start_M * (m1 + m2) * LAL_MTSUN_SI; time_end_s = time_end_M * (m1 + m2) * LAL_MTSUN_SI; /* We don't want to return the *entire* waveform if it will be much longer * than the specified f_lower. Therefore guess waveform length using * the SEOBNR_ROM function and add 10% for safety. * FIXME: Is this correct for precessing waveforms? */ chi = XLALSimIMRPhenomBComputeChi(m1, m2, s1z, s2z); est_start_time = XLALSimIMRSEOBNRv2ChirpTimeSingleSpin(m1 * LAL_MSUN_SI, m2 * LAL_MSUN_SI, chi, fStart); est_start_time = (-est_start_time) * 1.1; if (est_start_time > time_start_s) { /* Restrict start time of waveform */ time_start_s = est_start_time; time_start_M = time_start_s / ((m1 + m2) * LAL_MTSUN_SI); } else if (fStart < Mflower / (m1 + m2) ) { XLAL_ERROR(XLAL_EDOM, "WAVEFORM IS NOT LONG ENOUGH TO REACH f_low. %e %e %e", fStart, Mflower, Mflower / (m1 + m2)); } array_length = (UINT4)(ceil( (time_end_s - time_start_s) / deltaT)); /* Compute correct angles for hplus and hcross following LAL convention. */ theta = psi = calpha = salpha = 0.; XLALSimInspiralNRWaveformGetRotationAnglesFromH5File(&theta, &psi, &calpha, &salpha, file, inclination, phiRef); /* Create the return time series, use arbitrary epoch here. We set this * properly later. */ XLALGPSAdd(&tmpEpoch, time_start_s); *hplus = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); *hcross = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); hplus_corr = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); hcross_corr = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); for (curr_idx = 0; curr_idx < array_length; curr_idx++) { hplus_corr->data->data[curr_idx] = 0.0; hcross_corr->data->data[curr_idx] = 0.0; } /* Create the distance scale factor */ distance_scale_fac = (m1 + m2) * LAL_MRSUN_SI / r; /* Generate the waveform */ for (model=2; model < 9 ; model++) { for (modem=-model; modem < (model+1); modem++) { snprintf(amp_key, sizeof(amp_key), "amp_l%d_m%d", model, modem); snprintf(phase_key, sizeof(phase_key), "phase_l%d_m%d", model, modem); /* Check that both groups exist */ if (XLALH5CheckGroupExists(file, amp_key) == 0) { continue; } if (XLALH5CheckGroupExists(file, phase_key) == 0) { continue; } /* Get amplitude and phase from file */ XLALSimInspiralNRWaveformGetDataFromHDF5File(&curr_amp, file, (m1 + m2), time_start_s, array_length, deltaT, amp_key); XLALSimInspiralNRWaveformGetDataFromHDF5File(&curr_phase, file, (m1 + m2), time_start_s, array_length, deltaT, phase_key); curr_ylm = XLALSpinWeightedSphericalHarmonic(theta, psi, -2, model, modem); for (curr_idx = 0; curr_idx < array_length; curr_idx++) { curr_h_real = curr_amp->data[curr_idx] * cos(curr_phase->data[curr_idx]) * distance_scale_fac; curr_h_imag = curr_amp->data[curr_idx] * sin(curr_phase->data[curr_idx]) * distance_scale_fac; hplus_corr->data->data[curr_idx] = hplus_corr->data->data[curr_idx] + curr_h_real * creal(curr_ylm) - curr_h_imag * cimag(curr_ylm); hcross_corr->data->data[curr_idx] = hcross_corr->data->data[curr_idx] - curr_h_real * cimag(curr_ylm) - curr_h_imag * creal(curr_ylm); } XLALDestroyREAL8Vector(curr_amp); XLALDestroyREAL8Vector(curr_phase); } } /* Correct for the "alpha" angle as given in T1600045 to translate * from the NR wave frame to LAL wave-frame * Helper time series needed. */ for (curr_idx = 0; curr_idx < array_length; curr_idx++) { (*hplus)->data->data[curr_idx] = (calpha*calpha - salpha*salpha) * hplus_corr->data->data[curr_idx] - 2.0*calpha*salpha * hcross_corr->data->data[curr_idx]; (*hcross)->data->data[curr_idx] = + 2.0*calpha*salpha * hplus_corr->data->data[curr_idx] + (calpha*calpha - salpha*salpha) * hcross_corr->data->data[curr_idx]; } XLALDestroyREAL8TimeSeries(hplus_corr); XLALDestroyREAL8TimeSeries(hcross_corr); XLALH5FileClose(file); return XLAL_SUCCESS; #endif }
UNUSED static UINT4 XLALSimInspiralNRWaveformGetRotationAnglesFromH5File( UNUSED REAL8 *theta, /**< Returned inclination angle of source */ UNUSED REAL8 *psi, /**< Returned azimuth angle of source */ UNUSED REAL8 *calpha, /**< Returned cosine of the polarisation angle */ UNUSED REAL8 *salpha, /**< Returned sine of the polarisation angle */ UNUSED LALH5File* filepointer, /**< Pointer to NR HDF5 file */ UNUSED const REAL8 inclination, /**< Inclination of source */ UNUSED const REAL8 phi_ref /**< Orbital reference phase*/ ) { #ifndef LAL_HDF5_ENABLED XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled"); #else /* Compute the angles necessary to rotate from the intrinsic NR source frame * into the LAL frame. See DCC-T1600045 for details. */ /* Attribute declarations go in here */ /* Declarations */ REAL8 orb_phase, ln_hat_x, ln_hat_y, ln_hat_z, ln_hat_norm; REAL8 n_hat_x, n_hat_y, n_hat_z, n_hat_norm; REAL8 corb_phase, sorb_phase, sinclination, cinclination; REAL8 ln_cross_n_x, ln_cross_n_y, ln_cross_n_z; REAL8 z_wave_x, z_wave_y, z_wave_z; REAL8 stheta, ctheta, spsi, cpsi, theta_hat_x, theta_hat_y, theta_hat_z; REAL8 psi_hat_x, psi_hat_y, psi_hat_z; REAL8 n_dot_theta, ln_cross_n_dot_theta, n_dot_psi, ln_cross_n_dot_psi; REAL8 y_val; /* Following section IV of DCC-T1600045 * Step 1: Define Phi = phiref/2 ... I'm ignoring this, I think it is wrong */ orb_phase = phi_ref; /* Step 2: Compute Zref * 2.1: Compute LN_hat from file. LN_hat = direction of orbital ang. mom. */ XLALH5FileQueryScalarAttributeValue(&ln_hat_x , filepointer, "LNhatx"); XLALH5FileQueryScalarAttributeValue(&ln_hat_y , filepointer, "LNhaty"); XLALH5FileQueryScalarAttributeValue(&ln_hat_z , filepointer, "LNhatz"); ln_hat_norm = sqrt(ln_hat_x * ln_hat_x + ln_hat_y * ln_hat_y + ln_hat_z * ln_hat_z); ln_hat_x = ln_hat_x / ln_hat_norm; ln_hat_y = ln_hat_y / ln_hat_norm; ln_hat_z = ln_hat_z / ln_hat_norm; /* 2.2: Compute n_hat from file. * n_hat = direction from object 2 to object 1 */ XLALH5FileQueryScalarAttributeValue(&n_hat_x , filepointer, "nhatx"); XLALH5FileQueryScalarAttributeValue(&n_hat_y , filepointer, "nhaty"); XLALH5FileQueryScalarAttributeValue(&n_hat_z , filepointer, "nhatz"); n_hat_norm = sqrt(n_hat_x * n_hat_x + n_hat_y * n_hat_y + n_hat_z * n_hat_z); n_hat_x = n_hat_x / n_hat_norm; n_hat_y = n_hat_y / n_hat_norm; n_hat_z = n_hat_z / n_hat_norm; /* 2.3: Compute Z in the lal wave frame */ corb_phase = cos(orb_phase); sorb_phase = sin(orb_phase); sinclination = sin(inclination); cinclination = cos(inclination); ln_cross_n_x = ln_hat_y * n_hat_z - ln_hat_z * n_hat_y; ln_cross_n_y = ln_hat_z * n_hat_x - ln_hat_x * n_hat_z; ln_cross_n_z = ln_hat_x * n_hat_y - ln_hat_y * n_hat_x; z_wave_x = sinclination * (sorb_phase * n_hat_x + corb_phase * ln_cross_n_x); z_wave_y = sinclination * (sorb_phase * n_hat_y + corb_phase * ln_cross_n_y); z_wave_z = sinclination * (sorb_phase * n_hat_z + corb_phase * ln_cross_n_z); z_wave_x += cinclination * ln_hat_x; z_wave_y += cinclination * ln_hat_y; z_wave_z += cinclination * ln_hat_z; /* Step 3.1: Extract theta and psi from Z in the lal wave frame * NOTE: Theta can only run between 0 and pi, so no problem with arccos here */ *theta = acos(z_wave_z); /* Degenerate if Z_wave[2] == 1. In this case just choose psi randomly, * the choice will be cancelled out by alpha correction (I hope!) */ if(fabs(z_wave_z - 1.0 ) < 0.000001) { *psi = 0.5; } else { /* psi can run between 0 and 2pi, but only one solution works for x and y */ /* Possible numerical issues if z_wave_x = sin(theta) */ if(fabs(z_wave_x / sin(*theta)) > 1.) { if(fabs(z_wave_x / sin(*theta)) < 1.00001) { if((z_wave_x * sin(*theta)) < 0.) { *psi = LAL_PI; } else { *psi = 0.; } } } else { *psi = acos(z_wave_x / sin(*theta)); } y_val = sin(*psi) * sin(*theta); /* If z_wave[1] is negative, flip psi so that sin(psi) goes negative * while preserving cos(psi) */ if( z_wave_y < 0.) { *psi = 2 * LAL_PI - *psi; y_val = sin(*psi) * sin(*theta); } if( fabs(y_val - z_wave_y) > 0.0001) { XLAL_ERROR(XLAL_EDOM, "Something's wrong in Ian's math. Tell him he's an idiot!"); } } /* 3.2: Compute the vectors theta_hat and psi_hat */ stheta = sin(*theta); ctheta = cos(*theta); spsi = sin(*psi); cpsi = cos(*psi); theta_hat_x = cpsi * ctheta; theta_hat_y = spsi * ctheta; theta_hat_z = - stheta; psi_hat_x = -spsi; psi_hat_y = cpsi; psi_hat_z = 0.0; /* Step 4: Compute sin(alpha) and cos(alpha) */ n_dot_theta = n_hat_x * theta_hat_x + n_hat_y * theta_hat_y + n_hat_z * theta_hat_z; ln_cross_n_dot_theta = ln_cross_n_x * theta_hat_x + ln_cross_n_y * theta_hat_y + ln_cross_n_z * theta_hat_z; n_dot_psi = n_hat_x * psi_hat_x + n_hat_y * psi_hat_y + n_hat_z * psi_hat_z; ln_cross_n_dot_psi = ln_cross_n_x * psi_hat_x + ln_cross_n_y * psi_hat_y + ln_cross_n_z * psi_hat_z; *salpha = corb_phase * n_dot_theta - sorb_phase * ln_cross_n_dot_theta; *calpha = corb_phase * n_dot_psi - sorb_phase * ln_cross_n_dot_psi; /* Step 5: Also useful to keep the source frame vectors as defined in * equation 16 of Harald's document. */ /* * x_source_hat[0] = corb_phase * n_hat_x - sorb_phase * ln_cross_n_x; * x_source_hat[1] = corb_phase * n_hat_y - sorb_phase * ln_cross_n_y; * x_source_hat[2] = corb_phase * n_hat_z - sorb_phase * ln_cross_n_z; * y_source_hat[0] = sorb_phase * n_hat_x + corb_phase * ln_cross_n_x; * y_source_hat[1] = sorb_phase * n_hat_y + corb_phase * ln_cross_n_y; * y_source_hat[2] = sorb_phase * n_hat_z + corb_phase * ln_cross_n_z; * z_source_hat[0] = ln_hat_x; * z_source_hat[1] = ln_hat_y; * z_source_hat[2] = ln_hat_z; */ return XLAL_SUCCESS; #endif }
/* Everything needs to be declared as unused in case HDF is not enabled. */ int XLALSimInspiralNRWaveformGetHplusHcross( UNUSED REAL8TimeSeries **hplus, /**< Output h_+ vector */ UNUSED REAL8TimeSeries **hcross, /**< Output h_x vector */ UNUSED REAL8 phiRef, /**< orbital phase at reference pt. */ UNUSED REAL8 inclination, /**< inclination angle */ UNUSED REAL8 deltaT, /**< sampling interval (s) */ UNUSED REAL8 m1, /**< mass of companion 1 (kg) */ UNUSED REAL8 m2, /**< mass of companion 2 (kg) */ UNUSED REAL8 r, /**< distance of source (m) */ UNUSED REAL8 fStart, /**< start GW frequency (Hz) */ UNUSED REAL8 fRef, /**< reference GW frequency (Hz) */ UNUSED REAL8 s1x, /**< initial value of S1x */ UNUSED REAL8 s1y, /**< initial value of S1y */ UNUSED REAL8 s1z, /**< initial value of S1z */ UNUSED REAL8 s2x, /**< initial value of S2x */ UNUSED REAL8 s2y, /**< initial value of S2y */ UNUSED REAL8 s2z, /**< initial value of S2z */ UNUSED const char *NRDataFile, /**< Location of NR HDF file */ UNUSED LALValue* ModeArray /**< Container for the ell and m modes to generate. To generate all available modes pass NULL */ ) { #ifndef LAL_HDF5_ENABLED XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled"); #else /* Declarations */ UINT4 curr_idx, nr_file_format; INT4 model, modem; size_t array_length; REAL8 nrEta; REAL8 S1x, S1y, S1z, S2x, S2y, S2z; REAL8 Mflower, time_start_M, time_start_s, time_end_M, time_end_s; REAL8 est_start_time, curr_h_real, curr_h_imag; REAL8 theta, psi, calpha, salpha; REAL8 distance_scale_fac; COMPLEX16 curr_ylm; REAL8TimeSeries *hplus_corr; REAL8TimeSeries *hcross_corr; /* These keys follow a strict formulation and cannot be longer than 11 * characters */ char amp_key[20]; char phase_key[20]; gsl_vector *tmpVector=NULL; LALH5File *file, *group; LIGOTimeGPS tmpEpoch = LIGOTIMEGPSZERO; REAL8Vector *curr_amp, *curr_phase; /* Use solar masses for units. NR files will use * solar masses as well, so easier for that conversion */ m1 = m1 / LAL_MSUN_SI; m2 = m2 / LAL_MSUN_SI; file = XLALH5FileOpen(NRDataFile, "r"); if (file == NULL) { XLAL_ERROR(XLAL_EIO, "NR SIMULATION DATA FILE %s NOT FOUND.\n", NRDataFile); } /* Sanity checks on physical parameters passed to waveform * generator to guarantee consistency with NR data file. */ XLALH5FileQueryScalarAttributeValue(&nrEta, file, "eta"); if (fabs((m1 * m2) / pow((m1 + m2),2.0) - nrEta) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "MASSES (%e and %e) ARE INCONSISTENT WITH THE MASS RATIO OF THE NR SIMULATION (eta=%e).\n", m1, m2, nrEta); } /* Read spin metadata, L_hat, n_hat from HDF5 metadata and make sure * the ChooseTDWaveform() input values are consistent with the data * recorded in the metadata of the HDF5 file. * PS: This assumes that the input spins are in the LAL frame! */ XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format"); if (nr_file_format < 2) { XLALPrintInfo("This NR file is format %d. Only formats 2 and above support the use of reference frequency. For formats < 2 the reference frequency always corresponds to the start of the waveform.", nr_file_format); fRef = -1; } XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer(&S1x, &S1y, &S1z, &S2x, &S2y, &S2z, fRef, m1+m2, file); if (fabs(S1x - s1x) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1X IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S1y - s1y) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1Y IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S1z - s1z) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN1Z IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2x - s2x) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2X IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2y - s2y) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2Y IS INCONSISTENT WITH THE NR SIMULATION.\n"); } if (fabs(S2z - s2z) > 1E-3) { XLAL_ERROR(XLAL_EDOM, "SPIN2Z IS INCONSISTENT WITH THE NR SIMULATION.\n"); } /* First estimate the length of time series that is needed. * Demand that 22 mode that is present and use that to figure this out */ XLALH5FileQueryScalarAttributeValue(&Mflower, file, "f_lower_at_1MSUN"); /* Figure out start time of data */ group = XLALH5GroupOpen(file, "amp_l2_m2"); ReadHDF5RealVectorDataset(group, "X", &tmpVector); time_start_M = (REAL8)(gsl_vector_get(tmpVector, 0)); time_end_M = (REAL8)(gsl_vector_get(tmpVector, tmpVector->size - 1)); gsl_vector_free(tmpVector); time_start_s = time_start_M * (m1 + m2) * LAL_MTSUN_SI; time_end_s = time_end_M * (m1 + m2) * LAL_MTSUN_SI; /* We don't want to return the *entire* waveform if it will be much longer * than the specified f_lower. Therefore guess waveform length using * the SEOBNR_ROM function and add 10% for safety. * FIXME: Is this correct for precessing waveforms? */ if (fStart < Mflower / (m1 + m2) ) { XLAL_ERROR(XLAL_EDOM, "WAVEFORM IS NOT LONG ENOUGH TO REACH f_low. %e %e %e", fStart, Mflower, Mflower / (m1 + m2)); } XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format"); if (nr_file_format > 1) { if (XLALSimInspiralNRWaveformCheckFRef(file, fStart * (m1+m2)) > 0) { /* Can use Omega array to get start time */ est_start_time = XLALSimInspiralNRWaveformGetRefTimeFromRefFreq(file, fStart * (m1+m2)) * (m1 + m2) * LAL_MTSUN_SI; } else { /* This is the potential weird case where Omega-vs-time does not start * at precisely the same time as flower_at_1MSUN. This gap should be * small, so just use the full waveform here. */ est_start_time = time_start_s; } } else { /* Fall back on SEOBNR chirp time estimate */ XLALSimIMRSEOBNRv4ROMTimeOfFrequency(&est_start_time, fStart, m1 * LAL_MSUN_SI, m2 * LAL_MSUN_SI, s1z, s2z); est_start_time = (-est_start_time) * 1.1; } if (est_start_time > time_start_s) { /* Restrict start time of waveform */ time_start_s = est_start_time; time_start_M = time_start_s / ((m1 + m2) * LAL_MTSUN_SI); } array_length = (UINT4)(ceil( (time_end_s - time_start_s) / deltaT)); /* Compute correct angles for hplus and hcross following LAL convention. */ theta = psi = calpha = salpha = 0.; XLALSimInspiralNRWaveformGetRotationAnglesFromH5File(&theta, &psi, &calpha, &salpha, file, inclination, phiRef, fRef*(m1+m2)); /* Create the return time series, use arbitrary epoch here. We set this * properly later. */ XLALGPSAdd(&tmpEpoch, time_start_s); *hplus = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); *hcross = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); hplus_corr = XLALCreateREAL8TimeSeries("H_PLUS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); hcross_corr = XLALCreateREAL8TimeSeries("H_CROSS", &tmpEpoch, 0.0, deltaT, &lalStrainUnit, array_length ); for (curr_idx = 0; curr_idx < array_length; curr_idx++) { hplus_corr->data->data[curr_idx] = 0.0; hcross_corr->data->data[curr_idx] = 0.0; } /* Create the distance scale factor */ distance_scale_fac = (m1 + m2) * LAL_MRSUN_SI / r; /* Generate the waveform */ /* NOTE: We assume that for a given ell mode, all m modes are present */ INT4 NRLmax; XLALH5FileQueryScalarAttributeValue(&NRLmax, file, "Lmax"); if ( ModeArray == NULL ) {/* Default behaviour: Generate all modes upto NRLmax */ ModeArray = XLALSimInspiralCreateModeArray(); for (int ell=2; ell<=NRLmax; ell++) { XLALSimInspiralModeArrayActivateAllModesAtL(ModeArray, ell); } } /* else Use the ModeArray given */ for (model=2; model < (NRLmax + 1) ; model++) { for (modem=-model; modem < (model+1); modem++) { /* first check if (l,m) mode is 'activated' in the ModeArray */ /* if activated then generate the mode, else skip this mode. */ if (XLALSimInspiralModeArrayIsModeActive(ModeArray, model, modem) != 1) { XLAL_PRINT_INFO("SKIPPING model = %i modem = %i\n", model, modem); continue; } XLAL_PRINT_INFO("generateing model = %i modem = %i\n", model, modem); snprintf(amp_key, sizeof(amp_key), "amp_l%d_m%d", model, modem); snprintf(phase_key, sizeof(phase_key), "phase_l%d_m%d", model, modem); /* Check that both groups exist */ if (XLALH5FileCheckGroupExists(file, amp_key) == 0) { continue; } if (XLALH5FileCheckGroupExists(file, phase_key) == 0) { continue; } /* Get amplitude and phase from file */ XLALSimInspiralNRWaveformGetDataFromHDF5File(&curr_amp, file, (m1 + m2), time_start_s, array_length, deltaT, amp_key); XLALSimInspiralNRWaveformGetDataFromHDF5File(&curr_phase, file, (m1 + m2), time_start_s, array_length, deltaT, phase_key); curr_ylm = XLALSpinWeightedSphericalHarmonic(theta, psi, -2, model, modem); for (curr_idx = 0; curr_idx < array_length; curr_idx++) { curr_h_real = curr_amp->data[curr_idx] * cos(curr_phase->data[curr_idx]) * distance_scale_fac; curr_h_imag = curr_amp->data[curr_idx] * sin(curr_phase->data[curr_idx]) * distance_scale_fac; hplus_corr->data->data[curr_idx] = hplus_corr->data->data[curr_idx] + curr_h_real * creal(curr_ylm) - curr_h_imag * cimag(curr_ylm); hcross_corr->data->data[curr_idx] = hcross_corr->data->data[curr_idx] - curr_h_real * cimag(curr_ylm) - curr_h_imag * creal(curr_ylm); } XLALDestroyREAL8Vector(curr_amp); XLALDestroyREAL8Vector(curr_phase); } } /* Correct for the "alpha" angle as given in T1600045 to translate * from the NR wave frame to LAL wave-frame * Helper time series needed. */ for (curr_idx = 0; curr_idx < array_length; curr_idx++) { (*hplus)->data->data[curr_idx] = (calpha*calpha - salpha*salpha) * hplus_corr->data->data[curr_idx] - 2.0*calpha*salpha * hcross_corr->data->data[curr_idx]; (*hcross)->data->data[curr_idx] = + 2.0*calpha*salpha * hplus_corr->data->data[curr_idx] + (calpha*calpha - salpha*salpha) * hcross_corr->data->data[curr_idx]; } XLALDestroyREAL8TimeSeries(hplus_corr); XLALDestroyREAL8TimeSeries(hcross_corr); XLALH5FileClose(file); XLALDestroyValue(ModeArray); return XLAL_SUCCESS; #endif }
/* Everything needs to be declared as unused in case HDF is not enabled. */ UNUSED static UINT4 XLALSimInspiralNRWaveformGetSpinsFromHDF5FilePointer( UNUSED REAL8 *S1x, /**< [out] Dimensionless spin1x in LAL frame */ UNUSED REAL8 *S1y, /**< [out] Dimensionless spin1y in LAL frame */ UNUSED REAL8 *S1z, /**< [out] Dimensionless spin1z in LAL frame */ UNUSED REAL8 *S2x, /**< [out] Dimensionless spin2x in LAL frame */ UNUSED REAL8 *S2y, /**< [out] Dimensionless spin2y in LAL frame */ UNUSED REAL8 *S2z, /**< [out] Dimensionless spin2z in LAL frame */ UNUSED REAL8 fRef, /**< Reference frequency */ UNUSED REAL8 mTot, /**< Total mass */ UNUSED LALH5File* file /**< Pointer to HDF5 file */ ) { #ifndef LAL_HDF5_ENABLED XLAL_ERROR(XLAL_EFAILED, "HDF5 support not enabled"); #else REAL8 nrSpin1x, nrSpin1y, nrSpin1z, nrSpin2x, nrSpin2y, nrSpin2z; REAL8 ln_hat_x, ln_hat_y, ln_hat_z, n_hat_x, n_hat_y, n_hat_z, n_norm; REAL8 pos1x, pos1y, pos1z, pos2x, pos2y, pos2z; REAL8 ref_time; INT8 nr_file_format; /* We'll work by always using mTot = 1 */ fRef = fRef * mTot; XLALH5FileQueryScalarAttributeValue(&nr_file_format, file, "Format"); if ((nr_file_format < 2) && (fRef > 0)) { /* Supplying a fRef > 0 indicates that the user actually wants to use * fRef. If this is not possible, because the file format is < 2, then * the code will fail. */ XLAL_ERROR(XLAL_EINVAL, "This NR file is format 1. Only formats 2 and above support the use of reference frequency. For formats < 2 give the reference frequency as 0 or -1 to indicate that the start of the waveform should be used as the reference."); } /* Check if fRef is possible given inputs */ fRef = XLALSimInspiralNRWaveformCheckFRef(file, fRef); if (fRef <= 0) { XLALH5FileQueryScalarAttributeValue(&nrSpin1x, file, "spin1x"); XLALH5FileQueryScalarAttributeValue(&nrSpin1y, file, "spin1y"); XLALH5FileQueryScalarAttributeValue(&nrSpin1z, file, "spin1z"); XLALH5FileQueryScalarAttributeValue(&nrSpin2x, file, "spin2x"); XLALH5FileQueryScalarAttributeValue(&nrSpin2y, file, "spin2y"); XLALH5FileQueryScalarAttributeValue(&nrSpin2z, file, "spin2z"); XLALH5FileQueryScalarAttributeValue(&ln_hat_x , file, "LNhatx"); XLALH5FileQueryScalarAttributeValue(&ln_hat_y , file, "LNhaty"); XLALH5FileQueryScalarAttributeValue(&ln_hat_z , file, "LNhatz"); XLALH5FileQueryScalarAttributeValue(&n_hat_x , file, "nhatx"); XLALH5FileQueryScalarAttributeValue(&n_hat_y , file, "nhaty"); XLALH5FileQueryScalarAttributeValue(&n_hat_z , file, "nhatz"); } else { /* This will be real slow if done many times! * One speed issue is we create the spline for *all* points, but only * evaluate at a single point. */ /* First interpolate between time and freq to get a reference time */ ref_time = XLALSimInspiralNRWaveformGetRefTimeFromRefFreq(file, fRef); /* Now use ref_time to get spins, LN and nhat */ nrSpin1x = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin1x-vs-time", ref_time); nrSpin1y = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin1y-vs-time", ref_time); nrSpin1z = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin1z-vs-time", ref_time); nrSpin2x = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin2x-vs-time", ref_time); nrSpin2y = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin2y-vs-time", ref_time); nrSpin2z = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "spin2z-vs-time", ref_time); ln_hat_x = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "LNhatx-vs-time", ref_time); ln_hat_y = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "LNhaty-vs-time", ref_time); ln_hat_z = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "LNhatz-vs-time", ref_time); /* Would have been easier if we could store n_hat directly!! */ pos1x = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position1x-vs-time", ref_time); pos1y = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position1y-vs-time", ref_time); pos1z = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position1z-vs-time", ref_time); pos2x = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position2x-vs-time", ref_time); pos2y = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position2y-vs-time", ref_time); pos2z = XLALSimInspiralNRWaveformGetInterpValueFromGroupAtPoint (file, "position2z-vs-time", ref_time); n_hat_x = pos2x - pos1x; n_hat_y = pos2y - pos1y; n_hat_z = pos2z - pos1z; n_norm = sqrt(n_hat_x*n_hat_x + n_hat_y*n_hat_y + n_hat_z*n_hat_z); n_hat_x = n_hat_x / n_norm; n_hat_y = n_hat_y / n_norm; n_hat_z = n_hat_z / n_norm; } *S1x = nrSpin1x * n_hat_x + nrSpin1y * n_hat_y + nrSpin1z * n_hat_z; *S1y = nrSpin1x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z) + nrSpin1y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z) + nrSpin1z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y) ; *S1z = nrSpin1x * ln_hat_x + nrSpin1y * ln_hat_y + nrSpin1z * ln_hat_z; *S2x = nrSpin2x * n_hat_x + nrSpin2y * n_hat_y + nrSpin2z * n_hat_z; *S2y = nrSpin2x * (-ln_hat_z * n_hat_y + ln_hat_y * n_hat_z) + nrSpin2y * (ln_hat_z * n_hat_x - ln_hat_x * n_hat_z) + nrSpin2z * (-ln_hat_y * n_hat_x + ln_hat_x * n_hat_y); *S2z = nrSpin2x * ln_hat_x + nrSpin2y * ln_hat_y + nrSpin2z * ln_hat_z; return XLAL_SUCCESS; #endif }