def generate_LAL_modes(approximant, q, chiA0, chiB0, dt, M, \ dist_mpc, f_low, f_ref, phi_ref, ellMax=None): distance = dist_mpc * 1.0e6 * PC_SI approxTag = lalsim.SimInspiralGetApproximantFromString(approximant) # component masses of the binary m1_kg = M * MSUN_SI * q / (1. + q) m2_kg = M * MSUN_SI / (1. + q) dictParams = lal.CreateDict() if ellMax is not None: ma = lalsim.SimInspiralCreateModeArray() for ell in range(2, ellMax + 1): lalsim.SimInspiralModeArrayActivateAllModesAtL(ma, ell) lalsim.SimInspiralWaveformParamsInsertModeArray(dictParams, ma) lmax = 5 # This in unused hmodes = lalsim.SimInspiralChooseTDModes(phi_ref, dt, m1_kg, m2_kg, \ chiA0[0], chiA0[1], chiA0[2], chiB0[0], chiB0[1], chiB0[2], \ f_low, f_ref, distance, dictParams, lmax, approxTag) t = np.arange(len(hmodes.mode.data.data)) * dt mode_dict = {} while hmodes is not None: mode_dict['h_l%dm%d' % (hmodes.l, hmodes.m)] = hmodes.mode.data.data hmodes = hmodes.next return t, mode_dict
def get_SEOBNRv4PHM_modes(q, M, chi1, chi2, f_start, distance, deltaT): """Generate SEOBNRv4PHM modes""" m1SI = lal.MSUN_SI * q * M / (1.0 + q) m2SI = lal.MSUN_SI * M / (1.0 + q) approx = ls.SEOBNRv4PHM hlm = ls.SimInspiralChooseTDModes( 0., deltaT, m1SI, m2SI, chi1[0], chi1[1], chi1[2], chi2[0], chi2[1], chi2[2], f_start, f_start, distance, None, 5, approx, ) hI = {} modes = [(2, 2), (2, 1), (3, 3), (4, 4), (5, 5)] for lm in modes: hI[lm] = ls.SphHarmTimeSeriesGetMode(hlm, lm[0], lm[1]).data.data return hI
def load_lvcnr_data(filepath, mode, M, dt, inclination, phiRef, \ dist_mpc, f_low=0): """ If f_low = 0, uses the entire NR data. The actual f_low will be returned. """ NRh5File = h5py.File(filepath, 'r') # set mode for NR data params_NR = lal.CreateDict() lalsim.SimInspiralWaveformParamsInsertNumRelData(params_NR, filepath) if mode != 'all': params_NR = set_single_mode(params_NR, mode[0], mode[1]) # Metadata parameters masses: m1 = NRh5File.attrs['mass1'] m2 = NRh5File.attrs['mass2'] m1SI = m1 * M / (m1 + m2) * MSUN_SI m2SI = m2 * M / (m1 + m2) * MSUN_SI distance = dist_mpc * 1.0e6 * PC_SI f_ref = f_low spins = lalsim.SimInspiralNRWaveformGetSpinsFromHDF5File(f_ref, M, \ filepath) s1x = spins[0] s1y = spins[1] s1z = spins[2] s2x = spins[3] s2y = spins[4] s2z = spins[5] # If f_low == 0, update it to the start frequency so that the surrogate # gets the right start frequency if f_low == 0: f_low = NRh5File.attrs['f_lower_at_1MSUN'] / M f_ref = f_low f_low = f_ref lmax = 5 # Generating the NR waveform approx = lalsim.NR_hdf5 hmodes = lalsim.SimInspiralChooseTDModes(phiRef, dt, m1SI, m2SI, \ s1x, s1y, s1z, s2x, s2y, s2z, \ f_low, f_ref, distance, params_NR, lmax, approx) t = np.arange(len(hmodes.mode.data.data)) * dt mode_dict = {} while hmodes is not None: mode_dict['h_l%dm%d' % (hmodes.l, hmodes.m)] = hmodes.mode.data.data hmodes = hmodes.next return t, mode_dict, q, s1x, s1y, s1z, s2x, s2y, s2z, f_low, f_ref '''
def get_SEOBNRv4PHM_modes(q, M, chi1, chi2, f_start, deltaT=1. / (4096 * 4.)): #See the paper: https://arxiv.org/pdf/2004.09442.pdf """Generate SEOBNRv4PHM modes""" prefactor = 4.7864188273360336e-20 # G/c^2*(M_sun/Mpc) distance = 1. * 1e6 * lal.PC_SI # 1 Mpc in m amp_prefactor = prefactor * M / 1. # G/c^2 (M / d_L) nu = q / (1 + q)**2 m1SI = lal.MSUN_SI * q * M / (1.0 + q) m2SI = lal.MSUN_SI * M / (1.0 + q) #approx = lalsim.SEOBNRv4PHM approx = lalsim.SimInspiralGetApproximantFromString('SEOBNRv4PHM') hlm = lalsim.SimInspiralChooseTDModes( 0., deltaT, m1SI, m2SI, chi1[0], chi1[1], chi1[2], chi2[0], chi2[1], chi2[2], f_start, #/**< starting GW frequency (Hz) */ #what is this f_start?? f_start, #/**< reference GW frequency (Hz) */ distance, None, 5, approx, ) hI = {} modes = [(2, 2), (2, 1), (2, -1), (3, 3), (4, 4), (5, 5)] for lm in modes: hI[lm] = lalsim.SphHarmTimeSeriesGetMode( hlm, lm[0], lm[1]).data.data / amp_prefactor / nu times = np.linspace(0, len(hI[(2, 2)]) * deltaT, len(hI[(2, 2)])) h_22 = hI[(2, 2)] ph = np.unwrap(np.angle(np.conj(h_22))) omega_22 = (ph[2] - ph[0]) / (2 * deltaT) print("FREQUENCY of THE WF: ", omega_22, omega_22 / (2 * np.pi)) t_max = times[np.argmax(np.abs(h_22))] times = times - t_max return times, hI
def get_SEOBNRv4PHM_modes(q, M, chi1, chi2, f_start, deltaT): """Generate SEOBNRv4PHM modes""" m1SI = lal.MSUN_SI * q * M / (1.0 + q) m2SI = lal.MSUN_SI * M / (1.0 + q) approx = ls.SEOBNRv4PHM prefactor = 4.7864188273360336e-20 # G/c^2*(M_sun/Mpc) amp_prefactor = prefactor * M / 1. # G/c^2 (M / d_L) nu = np.divide(q, np.square(1 + q)) distance = 1. * 1e6 * lal.PC_SI hlm = ls.SimInspiralChooseTDModes(0., deltaT, m1SI, m2SI, chi1[0], chi1[1], chi1[2], chi2[0], chi2[1], chi2[2], f_start, f_start, distance, None, 5, approx) hI = {} modes = [(2, 2), (2, 1), (3, 3), (4, 4), (5, 5)] for lm in modes: hI[lm] = ls.SphHarmTimeSeriesGetMode( hlm, lm[0], lm[1]).data.data / (amp_prefactor * nu) t = np.linspace(0, deltaT * len(hI[(2, 2)]), len(hI[(2, 2)])) t = t - t[np.argmax(np.abs(hI[(2, 2)]))] return t, hI
h_P_mlgw_T = twist_modes(g, h_22_NP, h_21_NP, alpha, beta, gamma) #here mlgw performs only the twist t_grid = t_grid - t_grid[np.argmax(h_22_NP)] #P choose TD modes (in L0 frame) #The change of frame is performed in XLALSimIMRPhenomTPHM_L0Modes (https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimIMRPhenomTPHM.c#L328) hlm = lalsim.SimInspiralChooseTDModes( 0., t_step, theta[0, 0] * lalsim.lal.MSUN_SI, theta[0, 1] * lalsim.lal.MSUN_SI, theta[0, 2], theta[0, 3], theta[0, 4], theta[0, 5], 0., theta[0, 7], f_min, f_min, 1e6 * lalsim.lal.PC_SI, lal.CreateDict(), 5, #lmax lalsim.IMRPhenomTPHM) prefactor = 4.7864188273360336e-20 m1, m2 = theta[0, 0], theta[0, 1] nu = np.divide(m1 / m2, np.square(1 + m1 / m2)) amp_prefactor = prefactor * (m1 + m2) / 1. * nu h_22_P_lal = lalsim.SphHarmTimeSeriesGetMode(hlm, 2, 2).data.data / amp_prefactor h_21_P_lal = lalsim.SphHarmTimeSeriesGetMode(hlm, 2,
def create_dataset_TD(N_data, N_grid, modes, basefilename, t_coal=0.5, q_range=(1., 5.), m2_range=None, s1_range=(-0.8, 0.8), s2_range=(-0.8, 0.8), t_step=1e-5, alpha=0.35, approximant="SEOBNRv2_opt", path_TEOBResumS=None): """ create_dataset_TD ================= Create datasets for training a ML model to fit GW modes in time domain. Each dataset is saved in a different file (basefilename.mode). The dataset consists in 3 parameters theta=(q, spin1z, spin2z) associated to the modes computed in time domain for a grid of N_grid points in the range given by the user. More specifically, data are stored in 3 vectors: theta_vector vector holding source parameters q, spin1, spin2 amp_vector vector holding amplitudes for each source evaluated at some discrete grid points ph_vector vector holding phase for each source evaluated at some discrete grid points This routine add N_data data to filename if one is specified (if file is not empty it must contain data with the same N_grid); otherwise the datasets are returned as np vectors. Values of q and m2 as well as spins are drawn randomly in the range given by the user: it holds m1 = q *m2 M_sun. The waveforms are computed with a time step t_step; starting from a time in reduced grid tau min (s/M_Sun). Waves are given in a rescaled time grid (i.e. t/m_tot) with N_grid points: t=0 occurs when the 22 mode has a peak. A higher density of grid points is placed close to the merger. Dataset is generated either with an implementation of TEOBResumS (a path to a local installation of TEOBResumS should be provided) either with SEOBNRv4HM (lalsuite installation required). It can be given an TD lal approximant with no HM; in this case, only the 22 mode can be generated. Datasets can be loaded with load_dataset. Input: N_data size of dataset N_grid number of grid points to evaluate modes [] list of modes (each a (l,m) tuple) to generate and fill the dataset with basefilename basename of the file to save dataset in (each dataset is saved in basefilename.lm) t_coal time to coalescence to start computation from (measured in reduced grid) q_range tuple with range for random q values. if single value, q is kept fixed at that value m2_range tuple with range for random m2 values. if single value, m2 is kept fixed at that value. If None, m2 will be chosen s.t. m_tot = m1+m2 = 20. M_sun spin_mag_max_1 tuple with range for random spin #1 values. if single value, s1 is kept fixed at that value spin_mag_max_2 tuple with range for random spin #1 values. if single value, s2 is kept fixed at that value t_step time step to generate the wave with approximant string for the approximant model to be used (in lal convention; to be used only if lal is to be used) alpha distorsion factor for time grid. (In range (0,1], when it's close to 0, more grid points are around merger) approximant lal approximant to be used for generating the modes, or "TEOBResumS" (in the latter case a local installation must be provided by the argument path_TEOBResumS) path_TEOBResumS path to a local installation of TEOBResumS with routine 'EOBRun_module' (has effect only if approximant is "TEOBResumS") """ #TODO: create a function get modes, wrapper to ChooseTDModes: you can call it from here to get the modes if isinstance(modes, tuple): modes = [modes] if not isinstance(modes, list): raise TypeError( "Wrong kind of mode {} given. Expected a list [(l,m)]".format( modes)) if approximant == "TEOBResumS": #see https://bitbucket.org/eob_ihes/teobresums/src/development/ for the implementation of TEOBResumS if not isinstance(path_TEOBResumS, str): raise ValueError("Missing path to TEOBResumS: unable to continue") try: import sys sys.path.append( path_TEOBResumS) #path to local installation of TEOBResumS import EOBRun_module except: raise RuntimeError( "No valid imput source for module 'EOBRun_module' for TEOBResumS. Unable to continue." ) else: try: import lal import lalsimulation as lalsim except: raise RuntimeError( "Impossible to load lal and lalsimulation: try pip install lalsuite" ) LALpars = lal.CreateDict() approx = lalsim.SimInspiralGetApproximantFromString(approximant) prefactor = 4.7864188273360336e-20 # G/c^2*(M_sun/Mpc) #checking that all is good with modes if approximant == "SEOBNRv4HM": for mode in modes: if mode not in [(2, 2), (2, 1), (3, 3), (4, 4), (5, 5)]: raise ValueError( "Currently SEOBNRv4HM approximants do not implement the chosen HM" ) elif approximant != "IMRPhenomTPHM": if modes != [(2, 2)]: raise ValueError( "The chosen lal approximant does not implement HMs") #checking if N_grid is fine if not isinstance(N_grid, int): raise TypeError("N_grid is " + str(type(N_grid)) + "! Expected to be a int.") if isinstance(m2_range, tuple): D_theta = 4 #m2 must be included as a feature else: D_theta = 3 ######setting the time grid time_grid_list = [] t_end_list = [] if approximant == "TEOBResumS": modes_to_k = lambda modes: [ int(x[0] * (x[0] - 1) / 2 + x[1] - 2) for x in modes ] # [(l,m)] -> [k] k_modes = modes_to_k(modes) #setting a list of time grids for mode in modes: #ugly setting of t_end in TEOBResumS: required to kill bad features after merger if mode == (2, 2): t_end = 5.2e-4 #estimated maximum time for ringdown: WF will be killed after that time elif mode == (2, 1) or mode == (3, 3): #special treatment for 21 and 33 t_end = 1e-6 else: t_end = 3e-5 #for other modes t_end_list.append(t_end) else: for mode in modes: t_end_list.append(5.2e-4) print("Generating modes: " + str(modes)) #creating time_grid for i, mode in enumerate(modes): time_grid = np.linspace(-np.power(np.abs(t_coal), alpha), np.power(t_end_list[i], alpha), N_grid) time_grid = np.multiply(np.sign(time_grid), np.power(np.abs(time_grid), 1. / alpha)) #adding 0 to time grid index_0 = np.argmin(np.abs(time_grid)) time_grid[index_0] = 0. #0 is alway set in the grid time_grid_list.append(time_grid) #setting t_coal_freq for generating a waves if np.abs(t_coal) < 0.05: t_coal_freq = 0.05 else: t_coal_freq = np.abs(t_coal) #####create a list of buffer to save the WFs buff_list = [] for i, mode in enumerate(modes): filename = basefilename + '.' + str(mode[0]) + str(mode[1]) if not os.path.isfile( filename ): #file doesn't exist: must be created with proper header filebuff = open(filename, 'w') print("New file ", filename, " created") time_header = np.concatenate((np.zeros( (3, )), time_grid_list[i], time_grid_list[i]))[None, :] np.savetxt(filebuff, time_header, header="#Mode:" + str(mode[0]) + str(mode[1]) + "\n# row: theta " + str(D_theta) + " | amp (None," + str(N_grid) + ")| ph (None," + str(N_grid) + ")\n# N_grid = " + str(N_grid) + " | t_coal =" + str(t_coal) + " | t_step =" + str(t_step) + " | q_range = " + str(q_range) + " | m2_range = " + str(m2_range) + " | s1_range = " + str(s1_range) + " | s2_range = " + str(s2_range), newline='\n') else: filebuff = open(filename, 'a') buff_list.append(filebuff) #####creating WFs for n_WF in range(N_data): #setting value for data if isinstance(m2_range, tuple): m2 = np.random.uniform(m2_range[0], m2_range[1]) elif m2_range is not None: m2 = float(m2_range) if isinstance(q_range, tuple): q = np.random.uniform(q_range[0], q_range[1]) else: q = float(q_range) if isinstance(s1_range, tuple): spin1z = np.random.uniform(s1_range[0], s1_range[1]) else: spin1z = float(s1_range) if isinstance(s2_range, tuple): spin2z = np.random.uniform(s2_range[0], s2_range[1]) else: spin2z = float(s2_range) if m2_range is None: m2 = 20. / (1 + q) m1 = q * m2 else: m1 = q * m2 nu = np.divide(q, np.square(1 + q)) #symmetric mass ratio #computing f_min f_min = .9 * ((151 * (t_coal_freq)**(-3. / 8.) * (((1 + q)**2) / q)**(3. / 8.)) / (m1 + m2)) #in () there is the right scaling formula for frequency in order to get always the right reduced time #this should be multiplied by a prefactor (~1) for dealing with some small variation due to spins if isinstance(m2_range, tuple): temp_theta = [m1, m2, spin1z, spin2z] else: temp_theta = [m1 / m2, spin1z, spin2z] #getting the wave #output of the if: #amp_list, ph_list (same order as in modes) #time_full, argpeak amp_list, ph_list = [None for i in range(len(modes)) ], [None for i in range(len(modes))] if approximant == "TEOBResumS": #using TEOBResumS pars = { 'M': m1 + m2, 'q': m1 / m2, 'Lambda1': 0., 'Lambda2': 0., 'chi1': spin1z, 'chi2': spin2z, 'domain': 0, # TD 'arg_out': 1, # Output hlm/hflm. Default = 0 'use_mode_lm': list( set(k_modes + [1]) ), # List of modes to use/output through EOBRunPy (added 22 mode in case there isn't) #'srate_interp' : 1./t_step, # srate at which to interpolate. Default = 4096. 'use_geometric_units': 0, # Output quantities in geometric units. Default = 1 'initial_frequency': f_min, # in Hz if use_geometric_units = 0, else in geometric units 'interp_uniform_grid': 0, # Interpolate mode by mode on a uniform grid. Default = 0 (no interpolation) 'distance': 1., 'inclination': 0., } time_full, h_p, h_c, hlm = EOBRun_module.EOBRunPy(pars) for i, k_mode in enumerate(k_modes): temp_amp = hlm[str(k_mode)][ 0] * nu #TEOBResumS has weird conventions on the modes temp_ph = hlm[str(k_mode)][1] amp_list[i] = temp_amp ph_list[i] = temp_ph argpeak = locate_peak(hlm['1'][0] * nu) #aligned at the peak of the 22 elif approximant == "SEOBNRv4HM:old": #using SEOBNRv4HM nqcCoeffsInput = lal.CreateREAL8Vector(10) sp, dyn, dynHi = lalsim.SimIMRSpinAlignedEOBModes( t_step, m1 * lal.MSUN_SI, m2 * lal.MSUN_SI, f_min, 1e6 * lal.PC_SI, spin1z, spin2z, 41, 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., nqcCoeffsInput, 0) amp_prefactor = prefactor * (m1 + m2) / 1. # G/c^2 (M / d_L) while sp is not None: lm = (sp.l, sp.m) if lm not in modes: #skipping a non-necessary mode continue else: #computing index and saving the mode i = modes.index(lm) hlm = sp.mode.data.data #complex mode vector temp_amp = np.abs( hlm ) / amp_prefactor / nu #scaling with the convention of SEOB temp_ph = np.unwrap(np.angle(hlm)) amp_list[i] = temp_amp ph_list[i] = temp_ph if (sp.l, sp.m) == (2, 2): #get grid amp_22 = np.abs( sp.mode.data.data ) #amp of 22 mode (for time grid alignment) time_full = np.linspace( 0.0, sp.mode.data.length * t_step, sp.mode.data.length ) #time grid at which wave is computed argpeak = locate_peak( amp_22) #aligned at the peak of the 22 sp = sp.next elif approximant == "IMRPhenomTPHM" or approximant == "SEOBNRv4HM": #https://lscsoft.docs.ligo.org/lalsuite/lalsimulation/test___s_e_o_b_n_rv4_p_h_m__vs__4_h_m__ringdown_8py_source.html #approx = lalsim.SEOBNRv4PHM #DEBUG hlm = lalsim.SimInspiralChooseTDModes( 0., t_step, m1 * lalsim.lal.MSUN_SI, m2 * lalsim.lal.MSUN_SI, 0., 0., spin1z, 0., 0., spin2z, f_min, f_min, 1e6 * lalsim.lal.PC_SI, LALpars, 5, #lmax lalsim.IMRPhenomTPHM) amp_prefactor = prefactor * (m1 + m2) / 1. for i, lm in enumerate(modes): temp_hlm = lalsim.SphHarmTimeSeriesGetMode(hlm, lm[0], lm[1]).data.data temp_amp = np.abs( temp_hlm ) / amp_prefactor / nu #check that this conventions are for every lal part temp_ph = np.unwrap(np.angle(temp_hlm)) amp_list[i] = temp_amp ph_list[i] = temp_ph if (lm[0], lm[1]) == (2, 2): #get grid argpeak = locate_peak( temp_amp) #aligned at the peak of the 22 time_full = np.linspace( 0.0, len(temp_amp) * t_step, len(temp_amp)) #time grid at which wave is computed else: #another lal approximant (only 22 mode) hp, hc = lalsim.SimInspiralChooseTDWaveform( #where is its definition and documentation???? m1 * lalsim.lal.MSUN_SI, #m1 m2 * lalsim.lal.MSUN_SI, #m2 0., 0., spin1z, #spin vector 1 0., 0., spin2z, #spin vector 2 1e6 * lalsim.lal.PC_SI, #distance to source 0., #inclination 0., #phi 0., #longAscNodes 0., #eccentricity 0., #meanPerAno t_step, # time incremental step f_min, # lowest value of freq f_min, #some reference value of freq (??) LALpars, #some lal dictionary approx #approx method for the model ) h_p, h_c = hp.data.data, hc.data.data time_full = np.linspace( 0.0, hp.data.length * t_step, hp.data.length) #time grid at which wave is computed amp_prefactor = prefactor * (m1 + m2) / 1. # G/c^2 (M / d_L) temp_amp = np.sqrt(np.square(h_p) + np.square(h_c)) / amp_prefactor / ( 4 * np.sqrt(5 / (64 * np.pi))) temp_ph = np.unwrap(np.arctan2(h_c, h_p)) amp_list = [temp_amp] ph_list = [temp_ph] argpeak = locate_peak(temp_amp) #aligned at the peak of the 22 #setting time grid t_peak = time_full[argpeak] time_full = (time_full - t_peak) / (m1 + m2 ) #grid is scaled to standard grid #computing waves to the chosen std grid and saving to file for i in range(len(amp_list)): temp_amp, temp_ph = amp_list[i], ph_list[i] temp_amp = np.interp(time_grid_list[i], time_full, temp_amp) temp_ph = np.interp(time_grid_list[i], time_full, temp_ph) temp_ph = temp_ph - temp_ph[ 0] #all phases are shifted by a constant to make sure every wave has 0 phase at beginning of grid if False: #TODO: remove this shit!! plt.figure() plt.plot(time_grid_list[i], temp_amp_, 'o', ms=2) plt.plot(time_full, temp_amp) plt.xlim([-0.00015, 0.00015]) plt.show() to_save = np.concatenate( (temp_theta, temp_amp, temp_ph))[None, :] #(1,D) np.savetxt(buff_list[i], to_save) #user communication if n_WF % 50 == 0 and n_WF != 0: #if n_WF%1 == 0 and n_WF != 0: #debug print("Generated WF ", n_WF) if filename is None: return theta_vector, amp_dataset.real, ph_dataset.real, time_grid else: filebuff.close() return None