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 lal_waves(q, total_mass, approximant, f_lower, distance, inclination, coa_phase, **kwargs): """ Compute time-domain waveform from LAL simulation with a given set of paramater. """ m1, m2 = masses_from_q(q, total_mass) spin1x = spin1y = spin1z = spin2x = spin2y = spin2z = 0. long_asc_nodes = 0. eccentricity = 0. mean_per_ano = 0. f_ref = f_lower f_max = 4096. lal_pars = None l = 2 m = 2 aprox = eval('ls.' + str(approximant)) if 'delta_t' and 'delta_f' in kwargs: error("Please provide delta_t or delta_f.") elif 'delta_t' in kwargs and 'delta_f' not in kwargs: generateTD = ls.SimInspiralTDModesFromPolarizations( m1 * lal.MSUN_SI, m2 * lal.MSUN_SI, spin1x, spin1y, spin1z, spin2x, spin2y, spin2z, distance * 1e6 * lal.PC_SI, coa_phase, long_asc_nodes, eccentricity, mean_per_ano, kwargs['delta_t'], f_lower, f_ref, lal_pars, aprox) h22lal = ls.SphHarmTimeSeriesGetMode(generateTD, l, m) h22 = h22lal.data.data / NR_amp_scale(m1 + m2, distance) h2_2 = ((-1)**l) * conj(h22) Ttot = h22lal.data.length * h22lal.deltaT t1 = arange(h22lal.data.length, dtype=float) * h22lal.deltaT t1 = t1 + h22lal.epoch x = t1 - t1[argmax(abs(h22))] elif 'delta_f' in kwargs and 'delta_t' not in kwargs: hp1, hc1 = ls.SimInspiralChooseFDWaveform( lal.MSUN_SI * m1, lal.MSUN_SI * m2, spin1x, spin1y, spin1z, spin2x, spin2y, spin2z, lal.PC_SI * distance * 1e6, inclination, coa_phase, long_asc_nodes, eccentricity, mean_per_ano, kwargs['delta_f'], f_lower, f_max, f_ref, lal_pars, aprox) hp = hp1.data.data hc = hc1.data.data #TODO: change with FD get modes h22 = 0 h2_2 = 0. Ftot = hp1.data.length * hp1.deltaF t1 = arange(hp1.data.length, dtype=float) * hp1.deltaF t1 = t1 + hp1.epoch x = t1 else: error("Please provide delta_t or delta_f.") return x, h22, h2_2
def get_SEOBNRv4HM_modes(q, M, chi1, chi2, f_start, deltaT): """Generate SEOBNRv4HM modes""" m1SI = lal.MSUN_SI * q * M / (1.0 + q) m2SI = lal.MSUN_SI * M / (1.0 + q) nqcCoeffsInput = lal.CreateREAL8Vector(10) 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 sphtseries, dyn, dynHI = ls.SimIMRSpinAlignedEOBModes( deltaT, m1SI, m2SI, f_start, distance, chi1, chi2, 41, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, nqcCoeffsInput, 0, ) # The minus sign in front of the modes takes into account the fact that the polarization basis in EOB # conventions is different wrt the one in LAL hI = {} modes = [(2, 2), (2, 1), (3, 3), (4, 4), (5, 5)] for lm in modes: hI[lm] = np.trim_zeros( -1 * ls.SphHarmTimeSeriesGetMode(sphtseries, lm[0], lm[1]).data.data, "b") / (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
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_SEOBNRv4HM_modes(q, M, chi1, chi2, f_start, distance, deltaT): """Generate SEOBNRv4HM modes""" m1SI = lal.MSUN_SI * q * M / (1.0 + q) m2SI = lal.MSUN_SI * M / (1.0 + q) nqcCoeffsInput = lal.CreateREAL8Vector(10) sphtseries, dyn, dynHI = ls.SimIMRSpinAlignedEOBModes( deltaT, m1SI, m2SI, f_start, distance, chi1, chi2, 41, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, nqcCoeffsInput, 0, ) # The minus sign in front of the modes takes into account the fact that the polarization basis in EOB # conventions is different wrt the one in LAL hI = {} modes = [(2, 2), (2, 1), (3, 3), (4, 4), (5, 5)] for lm in modes: hI[lm] = np.trim_zeros( -1 * ls.SphHarmTimeSeriesGetMode(sphtseries, lm[0], lm[1]).data.data, "b") return 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
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, 1).data.data / amp_prefactor t_grid_lal = np.linspace(0, len(h_22_P_lal) * t_step, len(h_22_P_lal)) t_grid_lal = t_grid_lal - t_grid_lal[np.argmax(h_22_P_lal)] print("Mismatch NP WFs: 22, 21", compute_optimal_mismatch(h_NP_mlgw[0, :, 0], h_22_NP)[0][0], compute_optimal_mismatch(h_NP_mlgw[0, :, 1], h_21_NP)[0][0]) print("Mismatch P WFs: 22, 21", compute_optimal_mismatch(h_P_mlgw[0, :, 0], h_22)[0][0], compute_optimal_mismatch(h_P_mlgw[0, :, 1], h_21)[0][0]) #plotting #ignoring annoying real/imag warnings in plots import warnings
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