def generator_mchirp_eta_to_mass1_mass2(generator): """Converts mchirp and eta in `current_params`, to mass1 and mass2. """ mchirp = generator.current_params['mchirp'] eta = generator.current_params['eta'] m1, m2 = pnutils.mchirp_eta_to_mass1_mass2(mchirp, eta) generator.current_params['mass1'] = m1 generator.current_params['mass2'] = m2
'font.serif': ['palatino'], 'savefig.dpi': 300 }) ##### f_low = 15. f_high = 2048. # True NR parameters nrs1 = +0.84984534 nrs2 = +0.84976985 nrmc = 29.57934177 nret = 0.22223058 nrmt = nrmc / nret**0.6 nrm1, nrm2 = mchirp_eta_to_mass1_mass2(nrmc, nret) nrq = nrm1 / eta_mass1_to_mass2(nret, nrm1) nrwav = UseNRinDA.nr_waveform( filename= '/home/prayush/research/cita-papers/SEOBtesting/Paper1/plots/InvestigateSimulations/d11.5-q8.5-sA_0_0_0_sB_0_0_0/rhOverM_CcePITT_Asymptotic_GeometricUnits.h5', time_length=8) nrwav.rescale_to_totalmass(nrmt) nrhp = TimeSeries(nrwav.rescaled_hp, epoch=0) nrhc = TimeSeries(nrwav.rescaled_hc, epoch=0) nrh_max_amp_t, _ = nrwav.get_amplitude_peak() nrwav2 = UseNRinDA.nr_waveform( filename= '/home/prayush/research/cita-papers/SEOBtesting/Paper1/plots/InvestigateSimulations/d11.5-q8.5-sA_0_0_0_sB_0_0_0/rhOverM_Asymptotic_GeometricUnits.h5', time_length=8)
def parameterbiases_vs_parameters(self, inkey=None, approx=None, chieff=False, total_mass=False): """ Computes the relative/absolute differences between maximum-overlap parameters and those of the injections themselves. Default behavior is to return biases in - chirp mass, - eta, - spin1, - spin2, but the following input flags alter this : - total_mass = true ==> instead of chirp mass, total mass diffs're returned """ # {{{ if chieff: from pycbc import pnutils else: print("Not using effective spin", file=sys.stdout) for kk in list(self.data.keys()): if inkey in kk: break for app in list(self.data[kk].keys()): if approx in app: break masses = np.array(list(self.data[inkey][app].keys())) masses.sort() ff = np.array([max(self.data[inkey][app][mm][:, -1]) for mm in masses]) sig_mc = np.array([ self.data[inkey][app][mm][np.where( self.data[inkey][app][mm][:, -1] == max(self.data[inkey][app][mm][:, -1]))[0][0], -5] for mm in masses ]) sig_et = np.array([ self.data[inkey][app][mm][np.where( self.data[inkey][app][mm][:, -1] == max(self.data[inkey][app][mm][:, -1]))[0][0], -4] for mm in masses ]) if chieff: sig_m1, sig_m2 = pnutils.mchirp_eta_to_mass1_mass2(sig_mc, sig_et) if total_mass: sig_mt = sig_mc * sig_et**-0.6 sig_s1 = np.array([ self.data[inkey][app][mm][np.where( self.data[inkey][app][mm][:, -1] == max(self.data[inkey][app][mm][:, -1]))[0][0], -3] for mm in masses ]) sig_s2 = np.array([ self.data[inkey][app][mm][np.where( self.data[inkey][app][mm][:, -1] == max(self.data[inkey][app][mm][:, -1]))[0][0], -2] for mm in masses ]) # NR parameters are fixed for a simulation, so they dont need to be # accumulated nr_et = self.data[inkey][app][mm][0, 1] nr_q = (1. + (1. - 4. * nr_et)**0.5 - 2. * nr_et) / (2. * nr_et) nr_q = np.ones(len(ff)) * nr_q # Its constant nr_et = np.ones(len(ff)) * nr_et nr_mc = masses * nr_et**0.6 if chieff: nr_m1, nr_m2 = pnutils.mchirp_eta_to_mass1_mass2(nr_mc, nr_et) nr_s1 = np.ones(len(ff)) * \ self.data[inkey][app][mm][0, 2] # Its constant nr_s2 = np.ones(len(ff)) * \ self.data[inkey][app][mm][0, 3] # Its constant # if chieff: # Compute PN effective spins #nr_seff = spins_to_PNeffective_spin(nr_m1, nr_m2, nr_s1, nr_s2) #sig_seff= spins_to_PNeffective_spin(sig_m1, sig_m2, sig_s1, sig_s2) nr_seff = spins_to_massweighted_spin(nr_m1, nr_m2, nr_s1, nr_s2) sig_seff = spins_to_massweighted_spin(sig_m1, sig_m2, sig_s1, sig_s2) #mc_diff = (nr_mc-sig_mc)/nr_mc #eta_diff = (nr_et-sig_et)/nr_et # # NB: here 'mc_diff' really can contain either mchirp or mtotal differences, # please do not pay attention to the nomenclature 'mc' if total_mass: mc_diff = (sig_mt - masses) / masses else: mc_diff = (sig_mc - nr_mc) / nr_mc eta_diff = (sig_et - nr_et) / nr_et # Handle spins specially for q = 1 if 'q1' not in inkey: if chieff: s1_diff = sig_seff - nr_seff else: s1_diff = sig_s1 - nr_s1 # nr_s1-sig_s1 s2_diff = sig_s2 - nr_s2 # nr_s2-sig_s2 else: s1_diff, s2_diff = np.zeros(len(nr_s1)), np.zeros(len(nr_s2)) s11d, s12d = sig_s1 - nr_s1, sig_s2 - nr_s1 # nr_s1 - sig_s1, nr_s1 - sig_s2 s21d, s22d = sig_s1 - nr_s2, sig_s2 - nr_s2 # nr_s2 - sig_s1, nr_s2 - sig_s2 s1122rms = (s11d**2 + s22d**2)**0.5 s1221rms = (s12d**2 + s21d**2)**0.5 mask = s1122rms < s1221rms s1_diff[mask] = s11d[mask] s2_diff[mask] = s22d[mask] mask = s1122rms >= s1221rms s1_diff[mask] = s12d[mask] s2_diff[mask] = s21d[mask] if chieff: s1_diff = sig_seff - nr_seff return masses, nr_q, nr_s1, nr_s2, mc_diff, eta_diff, s1_diff, s2_diff, ff
def calculate_fitting_factor(m1, m2, s1x=0, s1y=0, s1z=0, s2x=0, s2y=0, s2z=0, tc=0, phic=0, ra=0, dec=0, polarization=0, signal_approx='IMRPhenomD', signal_file=None, tmplt_approx='IMRPhenomC', vary_masses_only=True, vary_masses_and_aligned_spin_only=False, chirp_mass_window=0.1, effective_spin_window=0.5, num_retries=4, f_lower=15.0, sample_rate=4096, signal_duration=256, psd_string='aLIGOZeroDetHighPower', pso_swarm_size=500, pso_omega=0.5, pso_phip=0.5, pso_phig=0.25, pso_minfunc=1e-8, verbose=True, debug=False): """ Calculates the fitting factor for a signal of given physical parameters, as modelled by a given signal approximant, against templates of another approximant. This function uses a particle swarm optimization to maximize the overlaps between signal and templates. Algorithm parameters are tunable, depending on how many dimensions we are optimizing over. IN PROGRESS: Adding facility to use "FromDataFile" waveforms """ # {{{ # 0) OPTION CHECKING if vary_masses_only: print("WARNING: Only component masses are allowed to be varied in templates. Setting the rest to signal values.") if vary_masses_and_aligned_spin_only: print("WARNING: Only component masses and spin components parallel to L allowed to be varied in templates. Setting the rest to signal values.") if vary_masses_only and vary_masses_and_aligned_spin_only: raise IOError( "Inconsistent options: vary_masses_only and vary_masses_and_aligned_spin_only") if (not vary_masses_only) and (not vary_masses_and_aligned_spin_only): print("WARNING: All mass and spin components varied in templates. Sky parameters still fixed to signal values.") # 1) GENERATE FILTERING META-PARAMETERS signal_duration = int(signal_duration) sample_rate = int(sample_rate) filter_N = signal_duration * sample_rate filter_n = filter_N / 2 + 1 delta_t = 1./sample_rate delta_f = 1./signal_duration if verbose: print("signal_duration = %d, sample_rate = %d, filter_N = %d, filter_n = %d" % ( signal_duration, sample_rate, filter_N, filter_n)) print("deltaT = %f, deltaF = %f" % (delta_t, delta_f)) # LIGO Noise PSD psd = from_string(psd_string, filter_n, delta_f, f_lower) # 2) GENERATE THE TARGET SIGNAL # PREPARATORY: Get the signal generator if signal_approx in pywf.fd_approximants(): generator = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_f=delta_f, f_lower=f_lower, approximant=signal_approx) elif signal_approx in pywf.td_approximants(): generator = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_t=delta_t, f_lower=f_lower, approximant=signal_approx) elif 'FromDataFile' in signal_approx: if os.path.getsize(signal_file) == 0: raise RuntimeError( " ERROR:...OOPS. Waveform file %s empty!!" % signal_file) try: _ = np.loadtxt(signal_file) except: raise RuntimeError( " WARNING: FAILURE READING DATA FROM %s.." % signal_file) waveform_params = lsctables.SimInspiral() waveform_params.latitude = 0 waveform_params.longitude = 0 waveform_params.polarization = 0 waveform_params.spin1x = 0 waveform_params.spin1y = 0 waveform_params.spin1z = 0 waveform_params.spin2x = 0 waveform_params.spin2y = 0 waveform_params.spin2z = 0 # try: if True: if verbose: print(".. generating signal waveform ") signal_htilde, _params = get_waveform(signal_approx, -1, -1, -1, waveform_params, f_lower, sample_rate, filter_N, datafile=signal_file) print(".. generated signal waveform ") m1, m2, w_value, _ = _params waveform_params.mass1 = m1 waveform_params.mass2 = m2 signal_h = make_frequency_series(signal_htilde) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) # except: raise IOError("Approximant %s not found.." % signal_approx) else: raise IOError("Approximant %s not found.." % signal_approx) if verbose: print( "\nGenerating signal with masses = %3f, %.3f, spin1 = (%.3f, %.3f, %.3f), and spin2 = (%.3f, %.3f, %.3f)" % (m1, m2, s1x, s1y, s1z, s2x, s2y, s2z)) sys.stdout.flush() # Actually GENERATE THE SIGNAL if signal_approx in pywf.fd_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) signal_h = extend_waveform_FrequencySeries(signal['H1'], filter_n) elif signal_approx in pywf.td_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) signal_h = make_frequency_series(signal['H1']) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) elif 'FromDataFile' in signal_approx: pass else: raise IOError("Approximant %s not found.." % signal_approx) ### # NOW : Set up PSO calculation of the optimal overlap parameter set, i.e. \theta(FF) ### # 3) INITIALIZE THE WAVEFORM GENERATOR FOR TEMPLATES # We allow all intrinsic parameters to vary, and fix them to the signal # values, in case only masses or only mass+aligned-spin components are # requested to be varied. This fixing is done inside the objective function. if tmplt_approx in pywf.fd_approximants(): generator_tmplt = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z' ], detectors=['H1'], coa_phase=phic, tc=tc, ra=ra, dec=dec, polarization=polarization, delta_f=delta_f, f_lower=f_lower, approximant=tmplt_approx) elif tmplt_approx in pywf.td_approximants(): raise IOError( "Time-domain templates not supported yet (TDomainDetFrameGenerator doesn't exist)") generator_tmplt = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z' ], detectors=['H1'], coa_phase=phic, tc=tc, ra=ra, dec=dec, polarization=polarization, delta_t=delta_t, f_lower=f_lower, approximant=tmplt_approx) elif 'FromDataFile' in tmplt_approx: raise RuntimeError( "Using **templates** from data files is not implemented yet") else: raise IOError("Approximant %s not found.." % tmplt_approx) # 4) DEFINE AN OBJECTIVE FUNCTION FOR PSO TO MINIMIZE def objective_function_fitting_factor(x, *args): """ This function is to be minimized if the fitting factor is to be found """ objective_function_fitting_factor.counter += 1 # 1) OBTAIN THE TEMPLATE PARAMETERS FROM X. ASSUME THAT ONLY # THOSE ARE PASSED THAT ARE NEEDED BY THE GENERATOR if len(x) == 2: m1, m2 = x if vary_masses_only: _s1x = _s1y = _s1z = _s2x = _s2y = _s2z = 0 else: _s1x, _s1y, _s1z = s1x, s1y, s1z _s2x, _s2y, _s2z = s2x, s2y, s2z elif len(x) == 4: m1, m2, _s1z, _s2z = x if vary_masses_and_aligned_spin_only: _s1x = _s1y = _s2x = _s2y = 0 else: _s1x, _s1y = s1x, s1y _s2x, _s2y = s2x, s2y elif len(x) == 8: m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = x else: raise IOError( "No of vars %d not supported (should be 2 or 4 or 8)" % len(x)) # 2) CHECK FOR CONSISTENCY if (_s1x**2 + _s1y**2 + _s1z**2) > s_max or (_s2x**2 + _s2y**2 + _s2z**2) > s_max: return 1e99 # 2) ASSUME THAT signal_h, tmplt_generator = args tmplt = tmplt_generator.generate_from_args( m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) tmplt_h = make_frequency_series(tmplt['H1']) if debug: print("IN FF Objective-> for parameters:", m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) if debug: print("IN FF Objective-> Length(tmplt) = %d, making it %d" % (len(tmplt['H1']), filter_n)) # NOTE: SEOBNRv4 has extra high frequency content, it seems.. if 'SEOBNRv4_ROM' in tmplt_approx or 'SEOBNRv2_ROM' in tmplt_approx: tmplt_h = extend_waveform_FrequencySeries( tmplt_h, filter_n, force_fit=True) else: tmplt_h = extend_waveform_FrequencySeries(tmplt_h, filter_n) # 3) COMPUTE MATCH m, _ = match(signal_h, tmplt_h, psd=psd, low_frequency_cutoff=f_lower) if debug: print("MATCH IS %.6f for parameters:" % m, m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) retval = np.log10(1. - m) # We do not want PSO to go berserk, so we stop when FF = 0.999999 if retval <= -6.0: retval = -6.0 return retval objective_function_fitting_factor.counter = 0 # 5) DEFINE A CONSTRAINT FUNCTION FOR PSO TO RESPECT def constraint_function_fitting_factor(x, *args): """ This function implements constraints on the optimization of fitting factors: 1) spin magnitudes on both holes should be <= 1 """ if len(x) == 2: m1, m2 = x s1x = s1y = s1z = s2x = s2y = s2z = 0 elif len(x) == 4: m1, m2, s1z, s2z = x s1x = s1y = s2x = s2y = 0 elif len(x) == 8: m1, m2, s1x, s1y, s1z, s2x, s2y, s2z = x # 1) Constraint on spin magnitudes s1_mag = (s1x**2 + s1y**2 + s1z**2)**0.5 s2_mag = (s2x**2 + s2y**2 + s2z**2)**0.5 ## if (s1_mag > s_max) or (s2_mag > s_max): return -1 # 2) Constraint on effective spin s_eff = (s1z * m1 + s2z * m2) / (m1 + m2) ## if (s_eff > s_eff_max) or (s_eff < s_eff_min): return -1 # FINALLY) DEFAULT return 1 # 6) FINALLY, CALL THE PSO TO COMPUTE THE FITTING FACTOR # 6a) FIRST CONSTRUCT THE FIXED ARGUMENTS FOR THE PSO's OBJECTIVE FUNCTION pso_args = (signal_h, generator_tmplt) # 6b) NOW SET THE RANGE OF PARAMETERS TO BE PROBED mt = m1 + m2 * 1.0 et = m1 * m2 / mt / mt mc = mt * et**0.6 mc_min = mc * (1.0 - chirp_mass_window) mc_max = mc * (1.0 + chirp_mass_window) et_max = 0.25 et_min = 10. / 121. # Lets say we trust waveform models up to q = 10 m1_max, _ = pnutils.mchirp_eta_to_mass1_mass2(mc_max, et_min) m1_min, _ = pnutils.mchirp_eta_to_mass1_mass2(mc_min, et_max) _, m2_max = pnutils.mchirp_eta_to_mass1_mass2(mc_max, et_max) _, m2_min = pnutils.mchirp_eta_to_mass1_mass2(mc_min, et_min) s_min = -0.99 s_max = +0.99 s_eff = (s1z * m1 + s2z * m2) / (m1 + m2) s_eff_min = s_eff - effective_spin_window s_eff_max = s_eff + effective_spin_window if verbose: print(m1, m2, mt, et, mc, mc_min, mc_max, et_min, et_max, m1_min, m1_max, m2_min, m2_max) if vary_masses_only: low_lim = [m1_min, m2_min] high_lim = [m1_max, m2_max] elif vary_masses_and_aligned_spin_only: low_lim = [m1_min, m2_min, s_min, s_min] high_lim = [m1_max, m2_max, s_max, s_max] else: low_lim = [m1_min, m2_min, s_min, s_min, s_min, s_min, s_min, s_min] high_lim = [m1_max, m2_max, s_max, s_max, s_max, s_max, s_max, s_max] # if verbose: print("\nSearching within limits:\n", low_lim, " and \n", high_lim) print("\nCalculating overlap now..") sys.stdout.flush() olap, idx = calculate_faithfulness(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, tc=tc, phic=phic, ra=ra, dec=dec, polarization=polarization, signal_approx=signal_approx, signal_file=signal_file, tmplt_approx=tmplt_approx, tmplt_file=None, aligned_spin_tmplt_only=vary_masses_and_aligned_spin_only, non_spin_tmplt_only=vary_masses_only, f_lower=f_lower, sample_rate=sample_rate, signal_duration=signal_duration, verbose=verbose, debug=debug) # if verbose: print("Overlap with aligned_spin_tmplt_only = ", vary_masses_and_aligned_spin_only, " and non_spin_tmplt_only = ", vary_masses_only, ": ", olap, np.log10( 1. - olap)) sys.stdout.flush() # idx = 1 ff = 0.0 while ff < olap: if idx and idx % 2 == 0: pso_minfunc *= 0.1 pso_phig *= 1.1 if idx > num_retries: print( "WARNING: Failed to improve on overlap in %d iterations. Set ff = olap now" % num_retries) ff = olap break if verbose: print("\nTry %d to compute fitting factor" % idx) sys.stdout.flush() params, ff = pso(objective_function_fitting_factor, low_lim, high_lim, f_ieqcons=constraint_function_fitting_factor, args=pso_args, swarmsize=pso_swarm_size, omega=pso_omega, phip=pso_phip, phig=pso_phig, minfunc=pso_minfunc, maxiter=500, debug=verbose) # Restore fitting factor from 1-ff ff = 1.0 - 10**ff if verbose: print("\nLoop will continue till %.12f < %.12f" % (ff, olap)) sys.stdout.flush() idx += 1 if verbose: print("optimization took %d objective func evals" % objective_function_fitting_factor.counter) sys.stdout.flush() # # 7) RETURN OPTIMIZED PARAMETERS return [params, olap, ff]
def verify_mass_range_options(opts, parser, nonSpin=False): """ Parses the metric calculation options given and verifies that they are correct. Parameters ---------- opts : argparse.Values instance Result of parsing the input options with OptionParser parser : object The OptionParser instance. nonSpin : boolean, optional (default=False) If this is provided the spin-related options will not be checked. """ if not opts.min_mass1: parser.error("Must supply --min-mass1") if not opts.min_mass2: parser.error("Must supply --min-mass2") if not opts.max_mass1: parser.error("Must supply --max-mass1") if not opts.max_mass2: parser.error("Must supply --max-mass2") # Mass1 must be the heavier! if opts.min_mass1 < opts.min_mass2: parser.error("min-mass1 cannot be less than min-mass2!") if opts.max_mass1 < opts.max_mass2: parser.error("max-mass1 cannot be less than max-mass2!") # If given are min/max total mass/chirp mass possible? if opts.min_total_mass: if opts.min_total_mass > opts.max_mass1 + opts.max_mass2: err_msg = "Supplied minimum total mass %f " %(opts.min_total_mass,) err_msg += "greater than the sum of the two max component masses " err_msg += " %f and %f." %(opts.max_mass1,opts.max_mass2) if opts.max_total_mass: if opts.max_total_mass < opts.min_mass1 + opts.min_mass2: err_msg = "Supplied maximum total mass %f " %(opts.max_total_mass,) err_msg += "smaller than the sum of the two min component masses " err_msg += " %f and %f." %(opts.min_mass1,opts.min_mass2) raise ValueError(err_msg) if opts.max_total_mass and opts.min_total_mass: if opts.max_total_mass < opts.min_total_mass: err_msg = "Min total mass must be larger than max total mass." raise ValueError(err_msg) # Assign min/max total mass from mass1, mass2 if not specified if (not opts.min_total_mass) or \ ((opts.min_mass1 + opts.min_mass2) > opts.min_total_mass): opts.min_total_mass = opts.min_mass1 + opts.min_mass2 if (not opts.max_total_mass) or \ ((opts.max_mass1 + opts.max_mass2) < opts.max_total_mass): opts.max_total_mass = opts.max_mass1 + opts.max_mass2 # It is vital that min and max total mass be set correctly. # This is becasue the heavily-used function get_random_mass will place # points first in total mass (to some power), and then in eta. If the total # mass limits are not well known ahead of time it will place unphysical # points and fail. # This test is a bit convoluted as we identify the maximum and minimum # possible total mass from chirp mass and/or eta restrictions. if opts.min_chirp_mass is not None: # Need to get the smallest possible min_tot_mass from this chirp mass # There are 4 possibilities for where the min_tot_mass is found on the # line of min_chirp_mass that interacts with the component mass limits. # Either it is found at max_m2, or at min_m1, or it starts on the equal # mass line within the parameter space, or it doesn't intersect # at all. # First let's get the masses at both of these possible points m1_at_max_m2 = pnutils.mchirp_mass1_to_mass2(opts.min_chirp_mass, opts.max_mass2) if m1_at_max_m2 < opts.max_mass2: # Unphysical, remove m1_at_max_m2 = -1 m2_at_min_m1 = pnutils.mchirp_mass1_to_mass2(opts.min_chirp_mass, opts.min_mass1) if m2_at_min_m1 > opts.min_mass1: # Unphysical, remove m2_at_min_m1 = -1 # Get the values on the equal mass line m1_at_equal_mass, m2_at_equal_mass = pnutils.mchirp_eta_to_mass1_mass2( opts.min_chirp_mass, 0.25) # Are any of these possible? if m1_at_max_m2 <= opts.max_mass1 and m1_at_max_m2 >= opts.min_mass1: min_tot_mass = opts.max_mass2 + m1_at_max_m2 elif m2_at_min_m1 <= opts.max_mass2 and m2_at_min_m1 >= opts.min_mass2: min_tot_mass = opts.min_mass1 + m2_at_min_m1 elif m1_at_equal_mass <= opts.max_mass1 and \ m1_at_equal_mass >= opts.min_mass1 and \ m2_at_equal_mass <= opts.max_mass2 and \ m2_at_equal_mass >= opts.min_mass2: min_tot_mass = m1_at_equal_mass + m2_at_equal_mass # So either the restriction is low enough to be redundant, or is # removing all the parameter space elif m2_at_min_m1 < opts.min_mass2: # This is the redundant case, ignore min_tot_mass = opts.min_total_mass else: # And this is the bad case err_msg = "The minimum chirp mass provided is not possible given " err_msg += "restrictions on component masses." raise ValueError(err_msg) # Is there also an eta restriction? if opts.max_eta: # Get the value of m1,m2 at max_eta, min_chirp_mass max_eta_m1, max_eta_m2 = pnutils.mchirp_eta_to_mass1_mass2( opts.min_chirp_mass, opts.max_eta) max_eta_min_tot_mass = max_eta_m1 + max_eta_m2 if max_eta_min_tot_mass > min_tot_mass: # Okay, eta does restrict this further. Still physical? min_tot_mass = max_eta_min_tot_mass if max_eta_m1 > opts.max_mass1: err_msg = "The combination of component mass, chirp " err_msg += "mass, eta and (possibly) total mass limits " err_msg += "have precluded all systems." raise ValueError(err_msg) # Update min_tot_mass if needed if min_tot_mass > opts.min_total_mass: opts.min_total_mass = float(min_tot_mass) # Then need to do max_chirp_mass and min_eta if opts.max_chirp_mass is not None: # Need to get the largest possible maxn_tot_mass from this chirp mass # There are 3 possibilities for where the max_tot_mass is found on the # line of max_chirp_mass that interacts with the component mass limits. # Either it is found at min_m2, or at max_m1, or it doesn't intersect # at all. # First let's get the masses at both of these possible points m1_at_min_m2 = pnutils.mchirp_mass1_to_mass2(opts.max_chirp_mass, opts.min_mass2) m2_at_max_m1 = pnutils.mchirp_mass1_to_mass2(opts.max_chirp_mass, opts.max_mass1) # Are either of these possible? if m1_at_min_m2 <= opts.max_mass1 and m1_at_min_m2 >= opts.min_mass1: max_tot_mass = opts.min_mass2 + m1_at_min_m2 elif m2_at_max_m1 <= opts.max_mass2 and m2_at_max_m1 >= opts.min_mass2: max_tot_mass = opts.max_mass1 + m2_at_max_m1 # So either the restriction is low enough to be redundant, or is # removing all the paramter space elif m2_at_max_m1 > opts.max_mass2: # This is the redundant case, ignore max_tot_mass = opts.max_total_mass else: # And this is the bad case err_msg = "The maximum chirp mass provided is not possible given " err_msg += "restrictions on component masses." raise ValueError(err_msg) # Is there also an eta restriction? if opts.min_eta: # Get the value of m1,m2 at max_eta, min_chirp_mass min_eta_m1, min_eta_m2 = pnutils.mchirp_eta_to_mass1_mass2( opts.max_chirp_mass, opts.min_eta) min_eta_max_tot_mass = min_eta_m1 + min_eta_m2 if min_eta_max_tot_mass < max_tot_mass: # Okay, eta does restrict this further. Still physical? max_tot_mass = min_eta_max_tot_mass if min_eta_m1 < opts.min_mass1: err_msg = "The combination of component mass, chirp " err_msg += "mass, eta and (possibly) total mass limits " err_msg += "have precluded all systems." raise ValueError(err_msg) # Update min_tot_mass if needed if max_tot_mass < opts.max_total_mass: opts.max_total_mass = float(max_tot_mass) # Need to check max_eta alone for minimum and maximum mass if opts.max_eta: # Similar to above except this can affect both the minimum and maximum # total mass. Need to identify where the line of max_eta intersects # the parameter space, and if it affects mass restrictions. m1_at_min_m2 = pnutils.eta_mass1_to_mass2(opts.max_eta, opts.min_mass2, return_mass_heavier=True) m2_at_min_m1 = pnutils.eta_mass1_to_mass2(opts.max_eta, opts.min_mass1, return_mass_heavier=False) m1_at_max_m2 = pnutils.eta_mass1_to_mass2(opts.max_eta, opts.max_mass2, return_mass_heavier=True) m2_at_max_m1 = pnutils.eta_mass1_to_mass2(opts.max_eta, opts.max_mass1, return_mass_heavier=False) # Check for restrictions on the minimum total mass # Are either of these possible? if m1_at_min_m2 <= opts.max_mass1 and m1_at_min_m2 >= opts.min_mass1: min_tot_mass = opts.min_mass2 + m1_at_min_m2 elif m2_at_min_m1 <= opts.max_mass2 and m2_at_min_m1 >= opts.min_mass2: # This case doesn't change the minimal total mass min_tot_mass = opts.min_total_mass # So either the restriction is low enough to be redundant, or is # removing all the paramter space elif m2_at_min_m1 > opts.max_mass2: # This is the redundant case, ignore min_tot_mass = opts.min_total_mass else: # And this is the bad case err_msg = "The maximum eta provided is not possible given " err_msg += "restrictions on component masses." raise ValueError(err_msg) # Update min_tot_mass if needed if min_tot_mass > opts.min_total_mass: opts.min_total_mass = float(min_tot_mass) # Check for restrictions on the maximum total mass # Are either of these possible? if m2_at_max_m1 <= opts.max_mass2 and m2_at_max_m1 >= opts.min_mass2: max_tot_mass = opts.max_mass1 + m2_at_max_m1 elif m1_at_max_m2 <= opts.max_mass1 and m1_at_max_m2 >= opts.min_mass1: # This case doesn't change the maximal total mass max_tot_mass = opts.max_total_mass # So either the restriction is low enough to be redundant, or is # removing all the paramter space, the latter case is already tested else: # This is the redundant case, ignore max_tot_mass = opts.max_total_mass if max_tot_mass < opts.max_total_mass: opts.max_total_mass = float(max_tot_mass) # Need to check min_eta alone for maximum and minimum total mass if opts.min_eta: # Same as max_eta. # Need to identify where the line of max_eta intersects # the parameter space, and if it affects mass restrictions. m1_at_min_m2 = pnutils.eta_mass1_to_mass2(opts.min_eta, opts.min_mass2, return_mass_heavier=True) m2_at_min_m1 = pnutils.eta_mass1_to_mass2(opts.min_eta, opts.min_mass1, return_mass_heavier=False) m1_at_max_m2 = pnutils.eta_mass1_to_mass2(opts.min_eta, opts.max_mass2, return_mass_heavier=True) m2_at_max_m1 = pnutils.eta_mass1_to_mass2(opts.min_eta, opts.max_mass1, return_mass_heavier=False) # Check for restrictions on the maximum total mass # Are either of these possible? if m1_at_max_m2 <= opts.max_mass1 and m1_at_max_m2 >= opts.min_mass1: max_tot_mass = opts.max_mass2 + m1_at_max_m2 elif m2_at_max_m1 <= opts.max_mass2 and m2_at_max_m1 >= opts.min_mass2: # This case doesn't affect the maximum total mass max_tot_mass = opts.max_total_mass # So either the restriction is low enough to be redundant, or is # removing all the paramter space elif m2_at_max_m1 < opts.min_mass2: # This is the redundant case, ignore max_tot_mass = opts.max_total_mass else: # And this is the bad case err_msg = "The minimum eta provided is not possible given " err_msg += "restrictions on component masses." raise ValueError(err_msg) # Update min_tot_mass if needed if max_tot_mass < opts.max_total_mass: opts.max_total_mass = float(max_tot_mass) # Check for restrictions on the minimum total mass # Are either of these possible? if m2_at_min_m1 <= opts.max_mass2 and m2_at_min_m1 >= opts.min_mass2: min_tot_mass = opts.min_mass1 + m2_at_min_m1 elif m1_at_min_m2 <= opts.max_mass1 and m1_at_min_m2 >= opts.min_mass1: # This case doesn't change the maximal total mass min_tot_mass = opts.min_total_mass # So either the restriction is low enough to be redundant, or is # removing all the paramter space, which is tested above else: # This is the redundant case, ignore min_tot_mass = opts.min_total_mass if min_tot_mass > opts.min_total_mass: opts.min_total_mass = float(min_tot_mass) if opts.max_total_mass < opts.min_total_mass: err_msg = "After including restrictions on chirp mass, component mass, " err_msg += "eta and total mass, no physical systems are possible." raise ValueError(err_msg) if opts.max_eta and opts.min_eta: if opts.max_eta < opts.min_eta: parser.error("--max-eta must be larger than --min-eta.") if nonSpin: return if opts.max_ns_spin_mag is None: if opts.nsbh_flag: parser.error("Must supply --max_ns_spin_mag with --nsbh-flag") # Can ignore this if no NSs will be generated elif opts.min_mass2 < (opts.ns_bh_boundary_mass or massRangeParameters.default_nsbh_boundary_mass): parser.error("Must supply --max-ns-spin-mag for the chosen" " value of --min_mass2") else: opts.max_ns_spin_mag = opts.max_bh_spin_mag if opts.max_bh_spin_mag is None: if opts.nsbh_flag: parser.error("Must supply --max_bh_spin_mag with --nsbh-flag") # Can ignore this if no BHs will be generated if opts.max_mass1 >= (opts.ns_bh_boundary_mass or massRangeParameters.default_nsbh_boundary_mass): parser.error("Must supply --max-bh-spin-mag for the chosen" " value of --max_mass1") else: opts.max_bh_spin_mag = opts.max_ns_spin_mag
theta_a12[s] = sim['theta_a12'] SeffdotL[s] = sim['SeffdotL'] SeffcrossL[s] = sim['SeffcrossL'] SdotL[s] = sim['theta_SdotL'] theta_SdotL[s] = sim['theta_SdotL'] for n in xrange(config.nsampls): chirp_masses[s,n] = masses[s,n] * sim['eta']**(3./5) mass1, mass2 = \ pnutils.mchirp_eta_to_mass1_mass2(np.median(chirp_masses[s,:]), sim['eta']) chieff[s] = pnutils.phenomb_chi(mass1, mass2, sim['spin1z'], sim['spin2z']) median_chirp_masses = np.median(chirp_masses, axis=1) std_chirp_masses = np.std(chirp_masses, axis=1) matchsort = np.argsort(median_matches) print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" print "Summary for %s"%args[0] print "" print " * Highest (median) Match: %f +/- %f"%(median_matches[matchsort][-1], std_matches[matchsort][-1]) print " * Waveform: %s"%( simulations_goodmatch[matchsort][-1]['wavefile'].split('/')[-1]) print " * mass ratio: %f"%(mass_ratios[matchsort][-1])