def convert_to_sngl_inspiral_table(params, proc_id): """ Convert a list of m1,m2,spin1z,spin2z values into a basic sngl_inspiral table with mass and spin parameters populated and event IDs assigned Parameters ----------- params : iterable Each entry in the params iterable should be a sequence of [mass1, mass2, spin1z, spin2z] in that order proc_id : ilwd char Process ID to add to each row of the sngl_inspiral table Returns ---------- SnglInspiralTable Bank of templates in SnglInspiralTable format """ sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) col_names = ["mass1", "mass2", "spin1z", "spin2z"] for values in params: tmplt = return_empty_sngl() tmplt.process_id = proc_id for colname, value in zip(col_names, values): setattr(tmplt, colname, value) tmplt.mtotal, tmplt.eta = pnutils.mass1_mass2_to_mtotal_eta(tmplt.mass1, tmplt.mass2) tmplt.mchirp, junk = pnutils.mass1_mass2_to_mchirp_eta(tmplt.mass1, tmplt.mass2) tmplt.template_duration = 0 # FIXME tmplt.event_id = sngl_inspiral_table.get_next_id() sngl_inspiral_table.append(tmplt) return sngl_inspiral_table
def test_get_phys_cov_masses(self): evecs = self.metricParams.evecs[self.f_upper] evals = self.metricParams.evals[self.f_upper] masses1 = [2.2, 1.8, 0.4, 0.3] masses2 = [2.21, 1.79, 0.41, 0.29] xis1 = pycbc.tmpltbank.get_cov_params(masses1[0], masses1[1], masses1[2], masses1[3], self.metricParams, self.f_upper) xis2 = pycbc.tmpltbank.get_cov_params(masses2[0], masses2[1], masses2[2], masses2[3], self.metricParams, self.f_upper) testXis = [xis1[0], xis1[1]] b_mtot, b_eta = pnutils.mass1_mass2_to_mtotal_eta( masses2[0], masses2[1]) bestMasses = [b_mtot, b_eta, masses2[2], masses2[3]] bestXis = xis2 output = pycbc.tmpltbank.get_physical_covaried_masses(testXis, \ bestMasses, bestXis, 0.0001, self.massRangeParams, \ self.metricParams, self.f_upper) # Test that returned xis are close enough diff = (output[6][0] - testXis[0])**2 diff += (output[6][1] - testXis[1])**2 errMsg = 'pycbc.tmpltbank.get_physical_covaried_masses ' errMsg += 'failed to find a point within the desired limits.' self.assertTrue(diff < 1E-4, msg=errMsg) # Test that returned masses and xis agree massT = output[0] + output[1] etaT = output[0] * output[1] / (massT * massT) spinSetT = pycbc.pnutils.get_beta_sigma_from_aligned_spins(\ etaT, output[2], output[3]) xisT = pycbc.tmpltbank.get_cov_params(output[0], output[1], output[2], output[3], self.metricParams, self.f_upper) errMsg = "Recovered xis do not agree with those expected." self.assertTrue(abs(xisT[0] - output[6][0]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[1] - output[6][1]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[2] - output[6][2]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[3] - output[6][3]) < 1E-5, msg=errMsg) # Test again with nsbh flag on output = pycbc.tmpltbank.get_physical_covaried_masses(testXis, \ bestMasses, bestXis, 0.0001, self.massRangeParams2, \ self.metricParams, self.f_upper) # Test that returned xis are close enough diff = (output[6][0] - testXis[0])**2 diff += (output[6][1] - testXis[1])**2 errMsg = 'pycbc.tmpltbank.get_physical_covaried_masses ' errMsg += 'failed to find a point within the desired limits.' self.assertTrue(diff < 1E-4, msg=errMsg) # Test that returned masses and xis agree xisT = pycbc.tmpltbank.get_cov_params(output[0], output[1], output[2], output[3], self.metricParams, self.f_upper) errMsg = "Recovered xis do not agree with those expected." self.assertTrue(abs(xisT[0] - output[6][0]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[1] - output[6][1]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[2] - output[6][2]) < 1E-5, msg=errMsg) self.assertTrue(abs(xisT[3] - output[6][3]) < 1E-5, msg=errMsg)
def test_get_phys_cov_masses(self): evecs = self.metricParams.evecs[self.f_upper] evals = self.metricParams.evals[self.f_upper] masses1 = [2.2,1.8,0.4,0.3] masses2 = [2.21,1.79,0.41,0.29] xis1 = pycbc.tmpltbank.get_cov_params(masses1[0], masses1[1], masses1[2], masses1[3], self.metricParams, self.f_upper) xis2 = pycbc.tmpltbank.get_cov_params(masses2[0], masses2[1], masses2[2], masses2[3], self.metricParams, self.f_upper) testXis = [xis1[0],xis1[1]] b_mtot, b_eta = pnutils.mass1_mass2_to_mtotal_eta(masses2[0], masses2[1]) bestMasses = [b_mtot, b_eta, masses2[2], masses2[3]] bestXis = xis2 output = pycbc.tmpltbank.get_physical_covaried_masses(testXis, \ bestMasses, bestXis, 0.0001, self.massRangeParams, \ self.metricParams, self.f_upper) # Test that returned xis are close enough diff = (output[6][0] - testXis[0])**2 diff += (output[6][1] - testXis[1])**2 errMsg = 'pycbc.tmpltbank.get_physical_covaried_masses ' errMsg += 'failed to find a point within the desired limits.' self.assertTrue( diff < 1E-4,msg=errMsg) # Test that returned masses and xis agree massT = output[0] + output[1] etaT = output[0]*output[1] / (massT*massT) spinSetT = pycbc.pnutils.get_beta_sigma_from_aligned_spins(\ etaT, output[2], output[3]) xisT = pycbc.tmpltbank.get_cov_params(output[0], output[1], output[2], output[3], self.metricParams, self.f_upper) errMsg = "Recovered xis do not agree with those expected." self.assertTrue( abs(xisT[0] - output[6][0]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[1] - output[6][1]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[2] - output[6][2]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[3] - output[6][3]) < 1E-5, msg=errMsg) # Test again with nsbh flag on output = pycbc.tmpltbank.get_physical_covaried_masses(testXis, \ bestMasses, bestXis, 0.0001, self.massRangeParams2, \ self.metricParams, self.f_upper) # Test that returned xis are close enough diff = (output[6][0] - testXis[0])**2 diff += (output[6][1] - testXis[1])**2 errMsg = 'pycbc.tmpltbank.get_physical_covaried_masses ' errMsg += 'failed to find a point within the desired limits.' self.assertTrue( diff < 1E-4,msg=errMsg) # Test that returned masses and xis agree xisT = pycbc.tmpltbank.get_cov_params(output[0], output[1], output[2], output[3], self.metricParams, self.f_upper) errMsg = "Recovered xis do not agree with those expected." self.assertTrue( abs(xisT[0] - output[6][0]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[1] - output[6][1]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[2] - output[6][2]) < 1E-5, msg=errMsg) self.assertTrue( abs(xisT[3] - output[6][3]) < 1E-5, msg=errMsg)
def test_stack_xi_direction(self): # Just run the function, no checking output evecs = self.metricParams.evecs[self.f_upper] evals = self.metricParams.evals[self.f_upper] masses1 = [2.2,1.8,0.4,0.3] masses2 = [2.21,1.79,0.41,0.29] xis1 = pycbc.tmpltbank.get_cov_params(masses1[0], masses1[1], \ masses1[2], masses1[3], self.metricParams, self.f_upper) xis2 = pycbc.tmpltbank.get_cov_params(masses2[0], masses2[1], \ masses2[2], masses2[3], self.metricParams, self.f_upper) testXis = [xis1[0],xis1[1]] b_mtot, b_eta = pnutils.mass1_mass2_to_mtotal_eta(masses2[0], masses2[1]) bestMasses = [b_mtot, b_eta, masses2[2], masses2[3]] bestXis = xis2 depths = pycbc.tmpltbank.stack_xi_direction_brute(testXis, \ bestMasses, bestXis, 3, 0.03, self.massRangeParams, \ self.metricParams, self.f_upper, numIterations=50)
def test_stack_xi_direction(self): # Just run the function, no checking output evecs = self.metricParams.evecs[self.f_upper] evals = self.metricParams.evals[self.f_upper] masses1 = [2.2, 1.8, 0.4, 0.3] masses2 = [2.21, 1.79, 0.41, 0.29] xis1 = pycbc.tmpltbank.get_cov_params(masses1[0], masses1[1], \ masses1[2], masses1[3], self.metricParams, self.f_upper) xis2 = pycbc.tmpltbank.get_cov_params(masses2[0], masses2[1], \ masses2[2], masses2[3], self.metricParams, self.f_upper) testXis = [xis1[0], xis1[1]] b_mtot, b_eta = pnutils.mass1_mass2_to_mtotal_eta( masses2[0], masses2[1]) bestMasses = [b_mtot, b_eta, masses2[2], masses2[3]] bestXis = xis2 depths = pycbc.tmpltbank.stack_xi_direction_brute(testXis, \ bestMasses, bestXis, 3, 0.03, self.massRangeParams, \ self.metricParams, self.f_upper, numIterations=50)
def convert_to_sngl_inspiral_table(params, proc_id): ''' Convert a list of m1,m2,spin1z,spin2z values into a basic sngl_inspiral table with mass and spin parameters populated and event IDs assigned Parameters ----------- params : iterable Each entry in the params iterable should be a sequence of [mass1, mass2, spin1z, spin2z] in that order proc_id : ilwd char Process ID to add to each row of the sngl_inspiral table Returns ---------- SnglInspiralTable Bank of templates in SnglInspiralTable format ''' sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) col_names = ['mass1', 'mass2', 'spin1z', 'spin2z'] for values in params: tmplt = return_empty_sngl() tmplt.process_id = proc_id for colname, value in zip(col_names, values): setattr(tmplt, colname, value) tmplt.mtotal, tmplt.eta = pnutils.mass1_mass2_to_mtotal_eta( tmplt.mass1, tmplt.mass2) tmplt.mchirp, junk = pnutils.mass1_mass2_to_mchirp_eta( tmplt.mass1, tmplt.mass2) tmplt.template_duration = 0 # FIXME tmplt.event_id = sngl_inspiral_table.get_next_id() sngl_inspiral_table.append(tmplt) return sngl_inspiral_table
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is defined in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. psds: dict of FrequencySeries Dictionary providing PSD estimates for all involved detectors. low_frequency_cutoff: float Minimum valid frequency for the PSD estimates. followup_data: dict of dicts, optional Dictionary providing SNR time series for each detector, to be used in sky localization with BAYESTAR. The format should be `followup_data['H1']['snr_series']`. More detectors can be present than given in `ifos`. If so, the extra detectors will only be used for sky localization. channel_names: dict of strings, optional Strain channel names for each detector. Will be recorded in the sngl_inspiral table. """ self.template_id = coinc_results['foreground/%s/template_id' % ifos[0]] self.coinc_results = coinc_results self.ifos = ifos # remember if this should be marked as HWINJ self.is_hardware_injection = ('HWINJ' in coinc_results and coinc_results['HWINJ']) if 'followup_data' in kwargs: fud = kwargs['followup_data'] assert len({fud[ifo]['snr_series'].delta_t for ifo in fud}) == 1, \ "delta_t for all ifos do not match" self.snr_series = {ifo: fud[ifo]['snr_series'] for ifo in fud} usable_ifos = fud.keys() followup_ifos = list(set(usable_ifos) - set(ifos)) else: self.snr_series = None usable_ifos = ifos followup_ifos = [] # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=usable_ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/'+pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(usable_ifos) coinc_event_row.instruments = ','.join(usable_ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_populated = None network_snrsq = 0 for sngl_id, ifo in enumerate(usable_ifos): sngl = return_empty_sngl(nones=True) sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.process_id = proc_id sngl.ifo = ifo names = [n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n] for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(lal.LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass if sngl.mass1 and sngl.mass2: sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl_populated = sngl if sngl.snr: sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr network_snrsq += sngl.snr ** 2.0 if 'channel_names' in kwargs and ifo in kwargs['channel_names']: sngl.channel = kwargs['channel_names'][ifo] sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) if self.snr_series is not None: snr_series_to_xml(self.snr_series[ifo], outdoc, sngl.event_id) # for subthreshold detectors, respect BAYESTAR's assumptions and checks bayestar_check_fields = ('mass1 mass2 mtotal mchirp eta spin1x ' 'spin1y spin1z spin2x spin2y spin2z').split() subthreshold_sngl_time = numpy.mean( [coinc_results['foreground/{}/end_time'.format(ifo)] for ifo in ifos]) for sngl in sngl_inspiral_table: if sngl.ifo in followup_ifos: for bcf in bayestar_check_fields: setattr(sngl, bcf, getattr(sngl_populated, bcf)) sngl.set_end(lal.LIGOTimeGPS(subthreshold_sngl_time)) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(usable_ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_populated.mchirp coinc_inspiral_row.mass = sngl_populated.mtotal coinc_inspiral_row.end_time = sngl_populated.end_time coinc_inspiral_row.end_time_ns = sngl_populated.end_time_ns coinc_inspiral_row.snr = network_snrsq ** 0.5 far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) # append the PSDs self.psds = kwargs['psds'] psds_lal = {} for ifo in self.psds: psd = self.psds[ifo] kmin = int(kwargs['low_frequency_cutoff'] / psd.delta_f) fseries = lal.CreateREAL8FrequencySeries( "psd", psd.epoch, kwargs['low_frequency_cutoff'], psd.delta_f, lal.StrainUnit**2 / lal.HertzUnit, len(psd) - kmin) fseries.data.data = psd.numpy()[kmin:] / pycbc.DYN_RANGE_FAC ** 2.0 psds_lal[ifo] = fseries make_psd_xmldoc(psds_lal, outdoc) self.outdoc = outdoc self.time = sngl_populated.get_end()
def get_random_mass(numPoints, massRangeParams): """ This function will generate a large set of points within the chosen mass and spin space, and with the desired minimum remnant disk mass (this applies to NS-BH systems only). It will also return the corresponding PN spin coefficients for ease of use later (though these may be removed at some future point). Parameters ---------- numPoints : int Number of systems to simulate massRangeParams : massRangeParameters instance Instance holding all the details of mass ranges and spin ranges. Returns -------- mass1 : float Mass of heavier body. mass2 : float Mass of lighter body. spin1z : float Spin of body 1. spin2z : float Spin of body 2. """ # WARNING: We expect mass1 > mass2 ALWAYS # Check if EM contraints are required, i.e. if the systems must produce # a minimum remnant disk mass. If this is not the case, proceed treating # the systems as point particle binaries if massRangeParams.remnant_mass_threshold is None: mass1, mass2, spin1z, spin2z = \ get_random_mass_point_particles(numPoints, massRangeParams) # otherwise, load EOS dependent data, generate the EM constraint # (i.e. compute the minimum symmetric mass ratio needed to # generate a given remnant disk mass as a function of the NS # mass and the BH spin along z) and then proceed by accepting # only systems that can yield (at least) the desired remnant # disk mass and that pass the mass and spin range cuts. else: ns_sequence, max_ns_g_mass = load_ns_sequence(massRangeParams.ns_eos) # Generate EM constraint surface: minumum eta as a function of BH spin # and NS mass required to produce an EM counterpart if not os.path.isfile('constraint_em_bright.npz'): logging.info("""constraint_em_bright.npz not found. Generating the constraint surface for EM bright binaries in the physical parameter space. One day, this will be made faster, for now be patient and wait a few minutes!""") generate_em_constraint_data(massRangeParams.minMass2, massRangeParams.maxMass2, massRangeParams.delta_ns_mass, \ -1.0, massRangeParams.maxBHSpinMag, massRangeParams.delta_bh_spin, \ massRangeParams.ns_eos, massRangeParams.remnant_mass_threshold, 0.0) constraint_datafile = numpy.load('constraint_em_bright.npz') mNS_pts = constraint_datafile['mNS_pts'] bh_spin_z_pts = constraint_datafile['sBH_pts'] eta_mins = constraint_datafile['eta_mins'] # Empty arrays to store points that pass all cuts mass1_out = [] mass2_out = [] spin1z_out = [] spin2z_out = [] # As the EM cut can remove several randomly generated # binaries, track the number of accepted points that pass # all cuts and stop only once enough of them are generated numPointsFound = 0 while numPointsFound < numPoints: # Generate the random points within the required mass # and spin cuts mass1, mass2, spin1z, spin2z = \ get_random_mass_point_particles(numPoints-numPointsFound, massRangeParams) _, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) # Now proceed with cutting out EM dim systems # Logical mask to clean up points by removing EM dim binaries mask = numpy.ones(len(mass1), dtype=bool) # Commpute the minimum eta to generate a counterpart min_eta_em = min_eta_for_em_bright(spin1z, mass2, mNS_pts, bh_spin_z_pts, eta_mins) # Remove a point if: # 1) eta is smaller than the eta threshold required to have a counterpart; # 2) the primary is a BH (mass1 >= ns_bh_boundary_mass); # 3) the secondary mass does not exceed the maximum NS mass # allowed by the EOS (if the user runs with --use-eos-max-ns-mass # this last condition will always be true, otherwise the user is # implicitly asking to keep binaries in which the secondary may be # a BH). mask[(mass1 >= massRangeParams.ns_bh_boundary_mass) & (mass2 <= max_ns_g_mass) & (eta < min_eta_em)] = False # Keep only binaries that can produce an EM counterpart and add them to # the pile of accpeted points to output mass1_out = numpy.concatenate((mass1_out, mass1[mask])) mass2_out = numpy.concatenate((mass2_out, mass2[mask])) spin1z_out = numpy.concatenate((spin1z_out,spin1z[mask])) spin2z_out = numpy.concatenate((spin2z_out,spin2z[mask])) # Number of points that survived all cuts numPointsFound = len(mass1_out) # Ready to go mass1 = mass1_out mass2 = mass2_out spin1z = spin1z_out spin2z = spin2z_out return mass1, mass2, spin1z, spin2z
def __init__(self, ifos, coinc_results): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is define in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. """ # remember if this should be marked as HWINJ self.is_hardware_injection = False if 'HWINJ' in coinc_results: self.is_hardware_injection = True # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/'+pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_id = 0 for ifo in ifos: names = [n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n] sngl_id += 1 sngl = return_empty_sngl() sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.ifo = ifo for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl.mchirp coinc_inspiral_row.mass = sngl.mtotal coinc_inspiral_row.end_time = sngl.end_time coinc_inspiral_row.end_time_ns = sngl.end_time_ns coinc_inspiral_row.snr = coinc_results['foreground/stat'] far = 1.0 / (YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) self.outdoc = outdoc
def get_random_mass(numPoints, massRangeParams): """ This function will generate a large set of points within the chosen mass and spin space, and with the desired minimum remnant disk mass (this applies to NS-BH systems only). It will also return the corresponding PN spin coefficients for ease of use later (though these may be removed at some future point). Parameters ---------- numPoints : int Number of systems to simulate massRangeParams : massRangeParameters instance Instance holding all the details of mass ranges and spin ranges. Returns -------- mass1 : float Mass of heavier body. mass2 : float Mass of lighter body. spin1z : float Spin of body 1. spin2z : float Spin of body 2. """ # WARNING: We expect mass1 > mass2 ALWAYS # Check if EM contraints are required, i.e. if the systems must produce # a minimum remnant disk mass. If this is not the case, proceed treating # the systems as point particle binaries if massRangeParams.remnant_mass_threshold is None: mass1, mass2, spin1z, spin2z = \ get_random_mass_point_particles(numPoints, massRangeParams) # otherwise, load EOS dependent data, generate the EM constraint # (i.e. compute the minimum symmetric mass ratio needed to # generate a given remnant disk mass as a function of the NS # mass and the BH spin along z) and then proceed by accepting # only systems that can yield (at least) the desired remnant # disk mass and that pass the mass and spin range cuts. else: _, max_ns_g_mass = load_ns_sequence(massRangeParams.ns_eos) # Generate EM constraint surface: minumum eta as a function of BH spin # and NS mass required to produce an EM counterpart if not os.path.isfile('constraint_em_bright.npz'): logging.info("""constraint_em_bright.npz not found. Generating the constraint surface for EM bright binaries in the physical parameter space. One day, this will be made faster, for now be patient and wait a few minutes!""") generate_em_constraint_data(massRangeParams.minMass2, massRangeParams.maxMass2, massRangeParams.delta_ns_mass, \ -1.0, massRangeParams.maxBHSpinMag, massRangeParams.delta_bh_spin, \ massRangeParams.ns_eos, massRangeParams.remnant_mass_threshold, 0.0) constraint_datafile = numpy.load('constraint_em_bright.npz') mNS_pts = constraint_datafile['mNS_pts'] bh_spin_z_pts = constraint_datafile['sBH_pts'] eta_mins = constraint_datafile['eta_mins'] # Empty arrays to store points that pass all cuts mass1_out = [] mass2_out = [] spin1z_out = [] spin2z_out = [] # As the EM cut can remove several randomly generated # binaries, track the number of accepted points that pass # all cuts and stop only once enough of them are generated numPointsFound = 0 while numPointsFound < numPoints: # Generate the random points within the required mass # and spin cuts mass1, mass2, spin1z, spin2z = \ get_random_mass_point_particles(numPoints-numPointsFound, massRangeParams) _, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) # Now proceed with cutting out EM dim systems # Logical mask to clean up points by removing EM dim binaries mask = numpy.ones(len(mass1), dtype=bool) # Commpute the minimum eta to generate a counterpart min_eta_em = min_eta_for_em_bright(spin1z, mass2, mNS_pts, bh_spin_z_pts, eta_mins) # Remove a point if: # 1) eta is smaller than the eta threshold required to have a counterpart; # 2) the primary is a BH (mass1 >= ns_bh_boundary_mass); # 3) the secondary mass does not exceed the maximum NS mass # allowed by the EOS (if the user runs with --use-eos-max-ns-mass # this last condition will always be true, otherwise the user is # implicitly asking to keep binaries in which the secondary may be # a BH). mask[(mass1 >= massRangeParams.ns_bh_boundary_mass) & (mass2 <= max_ns_g_mass) & (eta < min_eta_em)] = False # Keep only binaries that can produce an EM counterpart and add them to # the pile of accpeted points to output mass1_out = numpy.concatenate((mass1_out, mass1[mask])) mass2_out = numpy.concatenate((mass2_out, mass2[mask])) spin1z_out = numpy.concatenate((spin1z_out,spin1z[mask])) spin2z_out = numpy.concatenate((spin2z_out,spin2z[mask])) # Number of points that survived all cuts numPointsFound = len(mass1_out) # Ready to go mass1 = mass1_out mass2 = mass2_out spin1z = spin1z_out spin2z = spin2z_out return mass1, mass2, spin1z, spin2z
def __init__(self, ifos, coinc_results): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is define in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. """ # remember if this should be marked as HWINJ self.is_hardware_injection = False if 'HWINJ' in coinc_results: self.is_hardware_injection = True # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/' + pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_id = 0 for ifo in ifos: names = [ n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n ] sngl_id += 1 sngl = return_empty_sngl() sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.ifo = ifo for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl.mchirp coinc_inspiral_row.mass = sngl.mtotal coinc_inspiral_row.end_time = sngl.end_time coinc_inspiral_row.end_time_ns = sngl.end_time_ns coinc_inspiral_row.snr = coinc_results['foreground/stat'] far = 1.0 / (YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) self.outdoc = outdoc
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos participating in this trigger. coinc_results: dict of values A dictionary of values. The format is defined in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. psds: dict of FrequencySeries Dictionary providing PSD estimates for all involved detectors. low_frequency_cutoff: float Minimum valid frequency for the PSD estimates. high_frequency_cutoff: float, optional Maximum frequency considered for the PSD estimates. Default None. followup_data: dict of dicts, optional Dictionary providing SNR time series for each detector, to be used in sky localization with BAYESTAR. The format should be `followup_data['H1']['snr_series']`. More detectors can be present than given in `ifos`. If so, the extra detectors will only be used for sky localization. channel_names: dict of strings, optional Strain channel names for each detector Will be recorded in the sngl_inspiral table. padata: PAstroData instance, organizes info relevant to p astro. mc_area_args: dict of dicts, optional Dictionary providing arguments to be used in source probability estimation with pycbc/mchirp_area.py """ self.template_id = coinc_results['foreground/%s/template_id' % ifos[0]] self.coinc_results = coinc_results self.ifos = ifos self.basename = None # remember if this should be marked as HWINJ self.is_hardware_injection = ('HWINJ' in coinc_results and coinc_results['HWINJ']) # Check if we need to apply a time offset (this may be permerger) self.time_offset = 0 rtoff = 'foreground/{}/time_offset'.format(ifos[0]) if rtoff in coinc_results: self.time_offset = coinc_results[rtoff] if 'followup_data' in kwargs: fud = kwargs['followup_data'] assert len({fud[ifo]['snr_series'].delta_t for ifo in fud}) == 1, \ "delta_t for all ifos do not match" self.snr_series = {ifo: fud[ifo]['snr_series'] for ifo in fud} usable_ifos = fud.keys() followup_ifos = list(set(usable_ifos) - set(ifos)) for ifo in self.snr_series: self.snr_series[ifo].start_time += self.time_offset else: self.snr_series = None usable_ifos = ifos followup_ifos = [] # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) # FIXME is it safe (in terms of downstream operations) to let # `program_name` default to the actual script name? proc_id = create_process_table(outdoc, program_name='pycbc', detectors=usable_ifos).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(usable_ifos) coinc_event_row.instruments = ','.join(usable_ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_populated = None network_snrsq = 0 for sngl_id, ifo in enumerate(usable_ifos): sngl = return_empty_sngl(nones=True) sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.process_id = proc_id sngl.ifo = ifo names = [ n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n ] for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': val += self.time_offset sngl.end = lal.LIGOTimeGPS(val) else: try: setattr(sngl, name, val) except AttributeError: pass if sngl.mass1 and sngl.mass2: sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl_populated = sngl if sngl.snr: sngl.eff_distance = sngl.sigmasq**0.5 / sngl.snr network_snrsq += sngl.snr**2.0 if 'channel_names' in kwargs and ifo in kwargs['channel_names']: sngl.channel = kwargs['channel_names'][ifo] sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) if self.snr_series is not None: snr_series_to_xml(self.snr_series[ifo], outdoc, sngl.event_id) # Set merger time to the average of the ifo peaks self.merger_time = numpy.mean([ coinc_results['foreground/{}/end_time'.format(ifo)] for ifo in ifos ]) + self.time_offset # For subthreshold detectors, respect BAYESTAR's assumptions and checks bayestar_check_fields = ('mass1 mass2 mtotal mchirp eta spin1x ' 'spin1y spin1z spin2x spin2y spin2z').split() for sngl in sngl_inspiral_table: if sngl.ifo in followup_ifos: for bcf in bayestar_check_fields: setattr(sngl, bcf, getattr(sngl_populated, bcf)) sngl.end = lal.LIGOTimeGPS(self.merger_time) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.instruments = tuple(usable_ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_populated.mchirp coinc_inspiral_row.mass = sngl_populated.mtotal coinc_inspiral_row.end_time = sngl_populated.end_time coinc_inspiral_row.end_time_ns = sngl_populated.end_time_ns coinc_inspiral_row.snr = network_snrsq**0.5 far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) # Append the PSDs self.psds = kwargs['psds'] psds_lal = {} for ifo in self.psds: psd = self.psds[ifo] kmin = int(kwargs['low_frequency_cutoff'] / psd.delta_f) fseries = lal.CreateREAL8FrequencySeries( "psd", psd.epoch, kwargs['low_frequency_cutoff'], psd.delta_f, lal.StrainUnit**2 / lal.HertzUnit, len(psd) - kmin) fseries.data.data = psd.numpy()[kmin:] / pycbc.DYN_RANGE_FAC**2.0 psds_lal[ifo] = fseries make_psd_xmldoc(psds_lal, outdoc) # P astro calculation if 'padata' in kwargs: padata = kwargs['padata'] trigger_data = { 'mass1': sngl_populated.mass1, 'mass2': sngl_populated.mass2, 'spin1z': sngl_populated.spin1z, 'spin2z': sngl_populated.spin2z, 'network_snr': network_snrsq**0.5, 'far': far } horizons = {ifo: self.psds[ifo].dist for ifo in self.psds} self.p_astro, self.p_terr = \ padata.do_pastro_calc(trigger_data, horizons) else: self.p_astro, self.p_terr = None, None # Source probabilities estimation if 'mc_area_args' in kwargs: eff_distances = [sngl.eff_distance for sngl in sngl_inspiral_table] self.probabilities = calc_probabilities(coinc_inspiral_row.mchirp, coinc_inspiral_row.snr, min(eff_distances), kwargs['mc_area_args']) else: self.probabilities = None # Combine p astro and source probs if self.p_astro is not None and self.probabilities is not None: self.astro_probs = { cl: pr * self.p_astro for cl, pr in self.probabilities.items() } self.astro_probs['p_terr'] = self.p_terr else: self.astro_probs = None self.outdoc = outdoc self.time = sngl_populated.end
def to_coinc_xml_object(self, file_name): # FIXME: This function will only work with two ifos!! outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) ifos = [ifo for ifo in self.sngl_files.keys()] proc_id = ligolw_process.register_to_xmldoc( outdoc, "pycbc", {}, ifos=ifos, comment="", version=pycbc_version.git_hash, cvs_repository="pycbc/" + pycbc_version.git_branch, cvs_entry_time=pycbc_version.date, ).process_id search_summ_table = lsctables.New(lsctables.SearchSummaryTable) coinc_h5file = self.coinc_file.h5file start_time = coinc_h5file["segments"]["coinc"]["start"][:].min() end_time = coinc_h5file["segments"]["coinc"]["end"][:].max() num_trigs = len(self.sort_arr) search_summary = return_search_summary(start_time, end_time, num_trigs, ifos) search_summ_table.append(search_summary) outdoc.childNodes[0].appendChild(search_summ_table) sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) time_slide_table = lsctables.New(lsctables.TimeSlideTable) # Set up time_slide table time_slide_id = lsctables.TimeSlideID(0) for ifo in ifos: time_slide_row = lsctables.TimeSlide() time_slide_row.instrument = ifo time_slide_row.time_slide_id = time_slide_id time_slide_row.offset = 0 time_slide_row.process_id = proc_id time_slide_table.append(time_slide_row) # Set up coinc_definer table coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral-sngl_inspiral coincidences" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) bank_col_names = ["mass1", "mass2", "spin1z", "spin2z"] bank_col_vals = {} for name in bank_col_names: bank_col_vals[name] = self.get_bankfile_array(name) coinc_event_names = ["ifar", "time1", "fap", "stat"] coinc_event_vals = {} for name in coinc_event_names: coinc_event_vals[name] = self.get_coincfile_array(name) sngl_col_names = [ "snr", "chisq", "chisq_dof", "bank_chisq", "bank_chisq_dof", "cont_chisq", "cont_chisq_dof", "end_time", "template_duration", "coa_phase", "sigmasq", ] sngl_col_vals = {} for name in sngl_col_names: sngl_col_vals[name] = self.get_snglfile_array_dict(name) for idx, coinc_idx in enumerate(self.sort_arr): # Set up IDs and mapping values curr_tmplt_id = self.template_id[idx] coinc_id = lsctables.CoincID(idx) # Set up sngls # FIXME: As two-ifo is hardcoded loop over all ifos sngl_combined_mchirp = 0 sngl_combined_mtot = 0 for ifo in ifos: sngl_id = self.trig_id[ifo][idx] event_id = lsctables.SnglInspiralID(sngl_id) sngl = return_empty_sngl() sngl.event_id = event_id sngl.ifo = ifo curr_sngl_file = self.sngl_files[ifo].h5file[ifo] for name in sngl_col_names: val = sngl_col_vals[name][ifo][idx] setattr(sngl, name, val) for name in bank_col_names: val = bank_col_vals[name][idx] setattr(sngl, name, val) sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta(sngl.mass1, sngl.mass2) sngl.mchirp, junk = pnutils.mass1_mass2_to_mchirp_eta(sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq) ** 0.5 / sngl.snr sngl_combined_mchirp += sngl.mchirp sngl_combined_mtot += sngl.mtotal sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = "sngl_inspiral" coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = event_id coinc_event_map_table.append(coinc_map_row) sngl_combined_mchirp = sngl_combined_mchirp / len(ifos) sngl_combined_mtot = sngl_combined_mtot / len(ifos) # Set up coinc inspiral and coinc event tables coinc_event_row = lsctables.Coinc() coinc_inspiral_row = lsctables.CoincInspiral() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ",".join(ifos) coinc_inspiral_row.set_ifos(ifos) coinc_event_row.time_slide_id = time_slide_id coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_combined_mchirp coinc_inspiral_row.mass = sngl_combined_mtot coinc_inspiral_row.set_end(LIGOTimeGPS(coinc_event_vals["time1"][idx])) coinc_inspiral_row.snr = coinc_event_vals["stat"][idx] coinc_inspiral_row.false_alarm_rate = coinc_event_vals["fap"][idx] coinc_inspiral_row.combined_far = 1.0 / coinc_event_vals["ifar"][idx] # Transform to Hz coinc_inspiral_row.combined_far = coinc_inspiral_row.combined_far / YRJUL_SI coinc_event_row.likelihood = 0.0 coinc_inspiral_row.minimum_duration = 0.0 coinc_event_table.append(coinc_event_row) coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_def_table) outdoc.childNodes[0].appendChild(coinc_event_table) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(time_slide_table) outdoc.childNodes[0].appendChild(coinc_inspiral_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) ligolw_utils.write_filename(outdoc, file_name)
def get_chirp_params_old(mass1, mass2, spin1z, spin2z, f0, order): """ Take a set of masses and spins and convert to the various lambda coordinates that describe the orbital phase. Accepted PN orders are: {} Parameters ---------- mass1 : float or array Mass1 of input(s). mass2 : float or array Mass2 of input(s). spin1z : float or array Parallel spin component(s) of body 1. spin2z : float or array Parallel spin component(s) of body 2. f0 : float This is an arbitrary scaling factor introduced to avoid the potential for numerical overflow when calculating this. Generally the default value (70) is safe here. **IMPORTANT, if you want to calculate the ethinca metric components later this MUST be set equal to f_low.** This value must also be used consistently (ie. don't change its value when calling different functions!). order : string The Post-Newtonian order that is used to translate from masses and spins to the lambda_i coordinate system. Valid orders given above. Returns -------- lambdas : list of floats or numpy.arrays The lambda coordinates for the input system(s) """ totmass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) beta, sigma, gamma = pnutils.get_beta_sigma_from_aligned_spins(\ eta, spin1z, spin2z) # Convert mass to seconds totmass = totmass * MTSUN_SI pi = numpy.pi mapping = generate_inverse_mapping(order) lambdas = [] for idx in xrange(len(mapping.keys())): if mapping[idx] == 'Lambda0': lambda0 = 3. / (128. * eta * (pi * totmass * f0)**(5./3.)) lambdas.append(lambda0) elif mapping[idx] == 'Lambda2': lambda2 = 5. / (96. * pi * eta * totmass * f0) \ * (743./336. + 11./4. * eta) lambdas.append(lambda2) elif mapping[idx] == 'Lambda3': lambda3 = (-3. * pi**(1./3.))/(8. * eta * (totmass*f0)**(2./3.)) \ * (1. - beta/ (4. * pi)) lambdas.append(lambda3) elif mapping[idx] == 'Lambda4': lambda4 = 15. / (64. * eta * (pi * totmass * f0)**(1./3.)) * \ (3058673./1016064. + 5429./1008. * eta + 617./144. * \ eta**2 - sigma) lambdas.append(lambda4) # No Lambda5 term is present as that would be equivalent to a constant # phase offset, and thus completely degenerate with the initial orbital # phase. elif mapping[idx] == 'LogLambda5': loglambda5 = 3. * (38645.*pi/756. - 65.*pi*eta/9. - gamma) loglambda5 = loglambda5 * (3./(128.*eta)) lambdas.append(loglambda5) elif mapping[idx] == 'Lambda6': lambda6 = 11583231236531./4694215680. - (640.*pi*pi)/3.\ - (6848.*GAMMA)/21. lambda6 -= (6848./21.) * numpy.log(4 * (pi*totmass*f0)**(1./3.)) lambda6 += (-15737765635/3048192. + 2255.*pi*pi/12.)*eta lambda6 += (76055.*eta*eta)/1728. - (127825.*eta*eta*eta)/1296. lambda6 = lambda6 * 3./(128.*eta) * (pi * totmass * f0)**(1/3.) lambdas.append(lambda6) elif mapping[idx] == 'LogLambda6': loglambda6 = -( 6848./21) loglambda6 = loglambda6 * 3./(128.*eta)\ * (pi * totmass * f0)**(1/3.) lambdas.append(loglambda6) elif mapping[idx] == 'Lambda7': lambda7 = (77096675.*pi)/254016. + (378515.*pi*eta)/1512. \ - (74045.*pi*eta*eta)/756. lambda7 = lambda7 * 3./(128.*eta) * (pi * totmass * f0)**(2/3.) lambdas.append(lambda7) else: err_msg = "Do not understand term {}.".format(mapping[idx]) raise ValueError(err_msg) return lambdas
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is defined in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. """ followup_ifos = kwargs.get('followup_ifos') or [] self.template_id = coinc_results['foreground/%s/template_id' % ifos[0]] # remember if this should be marked as HWINJ self.is_hardware_injection = ('HWINJ' in coinc_results) # remember if we want to use a non-standard gracedb server self.gracedb_server = kwargs.get('gracedb_server') # compute SNR time series if needed, and figure out which of # the followup detectors are usable subthreshold_sngl_time = numpy.mean( [coinc_results['foreground/%s/end_time' % ifo] for ifo in ifos]) self.upload_snr_series = kwargs.get('upload_snr_series') usable_ifos = [] if self.upload_snr_series: self.snr_series = {} self.snr_series_psd = {} htilde = kwargs['bank'][self.template_id] for ifo in ifos + followup_ifos: if ifo in ifos: trig_time = coinc_results['foreground/%s/end_time' % ifo] else: trig_time = subthreshold_sngl_time # NOTE we only check the state/DQ of followup IFOs here. # IFOs producing the coincidence are assumed to also # produce valid SNR series. snr_series, snr_series_psd = compute_followup_snr_series( kwargs['data_readers'][ifo], htilde, trig_time, check_state=(ifo in followup_ifos)) if snr_series is not None: self.snr_series[ifo] = snr_series self.snr_series_psd[ifo] = snr_series_psd usable_ifos.append(ifo) else: usable_ifos = ifos # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=usable_ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/'+pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(usable_ifos) coinc_event_row.instruments = ','.join(usable_ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_populated = None for sngl_id, ifo in enumerate(usable_ifos): sngl = return_empty_sngl(nones=True) sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.process_id = proc_id sngl.ifo = ifo names = [n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n] for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(lal.LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass if sngl.mass1 and sngl.mass2: sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl_populated = sngl if sngl.snr: sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) if self.upload_snr_series: snr_series_to_xml(self.snr_series[ifo], outdoc, sngl.event_id) # for subthreshold detectors, respect BAYESTAR's assumptions and checks bayestar_check_fields = ('mass1 mass2 mtotal mchirp eta spin1x ' 'spin1y spin1z spin2x spin2y spin2z').split() for sngl in sngl_inspiral_table: if sngl.ifo in followup_ifos: for bcf in bayestar_check_fields: setattr(sngl, bcf, getattr(sngl_populated, bcf)) sngl.set_end(lal.LIGOTimeGPS(subthreshold_sngl_time)) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(usable_ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_populated.mchirp coinc_inspiral_row.mass = sngl_populated.mtotal coinc_inspiral_row.end_time = sngl_populated.end_time coinc_inspiral_row.end_time_ns = sngl_populated.end_time_ns coinc_inspiral_row.snr = coinc_results['foreground/stat'] far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) self.outdoc = outdoc self.time = sngl_populated.get_end()
def to_coinc_xml_object(self, file_name): # FIXME: This function will only work with two ifos!! outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) ifos = [ifo for ifo in self.sngl_files.keys()] proc_id = ligolw_process.register_to_xmldoc(outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/'+pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id search_summ_table = lsctables.New(lsctables.SearchSummaryTable) coinc_h5file = self.coinc_file.h5file start_time = coinc_h5file['segments']['coinc']['start'][:].min() end_time = coinc_h5file['segments']['coinc']['end'][:].max() num_trigs = len(self.sort_arr) search_summary = return_search_summary(start_time, end_time, num_trigs, ifos) search_summ_table.append(search_summary) outdoc.childNodes[0].appendChild(search_summ_table) sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) time_slide_table = lsctables.New(lsctables.TimeSlideTable) # Set up time_slide table time_slide_id = lsctables.TimeSlideID(0) for ifo in ifos: time_slide_row = lsctables.TimeSlide() time_slide_row.instrument = ifo time_slide_row.time_slide_id = time_slide_id time_slide_row.offset = 0 time_slide_row.process_id = proc_id time_slide_table.append(time_slide_row) # Set up coinc_definer table coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincidences" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) bank_col_names = ['mass1', 'mass2', 'spin1z', 'spin2z'] bank_col_vals = {} for name in bank_col_names: bank_col_vals[name] = self.get_bankfile_array(name) coinc_event_names = ['ifar', 'time1', 'fap', 'stat'] coinc_event_vals = {} for name in coinc_event_names: coinc_event_vals[name] = self.get_coincfile_array(name) sngl_col_names = ['snr', 'chisq', 'chisq_dof', 'bank_chisq', 'bank_chisq_dof', 'cont_chisq', 'cont_chisq_dof', 'end_time', 'template_duration', 'coa_phase', 'sigmasq'] sngl_col_vals = {} for name in sngl_col_names: sngl_col_vals[name] = self.get_snglfile_array_dict(name) for idx in xrange(len(self.sort_arr)): # Set up IDs and mapping values coinc_id = lsctables.CoincID(idx) # Set up sngls # FIXME: As two-ifo is hardcoded loop over all ifos sngl_combined_mchirp = 0 sngl_combined_mtot = 0 for ifo in ifos: sngl_id = self.trig_id[ifo][idx] event_id = lsctables.SnglInspiralID(sngl_id) sngl = return_empty_sngl() sngl.event_id = event_id sngl.ifo = ifo for name in sngl_col_names: val = sngl_col_vals[name][ifo][idx] if name == 'end_time': sngl.set_end(LIGOTimeGPS(val)) else: setattr(sngl, name, val) for name in bank_col_names: val = bank_col_vals[name][idx] setattr(sngl, name, val) sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_combined_mchirp += sngl.mchirp sngl_combined_mtot += sngl.mtotal sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = event_id coinc_event_map_table.append(coinc_map_row) sngl_combined_mchirp = sngl_combined_mchirp / len(ifos) sngl_combined_mtot = sngl_combined_mtot / len(ifos) # Set up coinc inspiral and coinc event tables coinc_event_row = lsctables.Coinc() coinc_inspiral_row = lsctables.CoincInspiral() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_inspiral_row.set_ifos(ifos) coinc_event_row.time_slide_id = time_slide_id coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_combined_mchirp coinc_inspiral_row.mass = sngl_combined_mtot coinc_inspiral_row.set_end(\ LIGOTimeGPS(coinc_event_vals['time1'][idx])) coinc_inspiral_row.snr = coinc_event_vals['stat'][idx] coinc_inspiral_row.false_alarm_rate = coinc_event_vals['fap'][idx] coinc_inspiral_row.combined_far = 1./coinc_event_vals['ifar'][idx] # Transform to Hz coinc_inspiral_row.combined_far = \ coinc_inspiral_row.combined_far / YRJUL_SI coinc_event_row.likelihood = 0. coinc_inspiral_row.minimum_duration = 0. coinc_event_table.append(coinc_event_row) coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_def_table) outdoc.childNodes[0].appendChild(coinc_event_table) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(time_slide_table) outdoc.childNodes[0].appendChild(coinc_inspiral_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) ligolw_utils.write_filename(outdoc, file_name)
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is defined in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. """ self.ifos = ifos followup_ifos = kwargs.get('followup_ifos') or [] self.template_id = coinc_results['foreground/%s/template_id' % self.ifos[0]] # remember if this should be marked as HWINJ self.is_hardware_injection = ('HWINJ' in coinc_results) # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/' + pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # compute SNR time series subthreshold_sngl_time = numpy.mean( [coinc_results['foreground/%s/end_time' % ifo] for ifo in ifos]) self.upload_snr_series = kwargs.get('upload_snr_series') if self.upload_snr_series: self.snr_series = {} self.snr_series_psd = {} htilde = kwargs['bank'][self.template_id] for ifo in ifos + followup_ifos: if ifo in ifos: trig_time = coinc_results['foreground/%s/end_time' % ifo] else: trig_time = subthreshold_sngl_time # NOTE we only check the state/DQ of followup IFOs here. # IFOs producing the coincidence are assumed to also # produce valid SNR series. snr_series, snr_series_psd = compute_followup_snr_series( kwargs['data_readers'][ifo], htilde, trig_time, check_state=(ifo in followup_ifos)) if snr_series is not None: self.snr_series[ifo] = snr_series self.snr_series_psd[ifo] = snr_series_psd # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_populated = None for sngl_id, ifo in enumerate(ifos + followup_ifos): if self.upload_snr_series and ifo not in self.snr_series: # SNR series could not be computed, so skip this continue sngl = return_empty_sngl(nones=True) sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.process_id = proc_id sngl.ifo = ifo names = [ n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n ] for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(lal.LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass if sngl.mass1 and sngl.mass2: sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl_populated = sngl if sngl.snr: sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) if self.upload_snr_series and ifo in self.snr_series: snr_series_to_xml(self.snr_series[ifo], outdoc, sngl.event_id) # for subthreshold detectors, respect BAYESTAR's assumptions and checks bayestar_check_fields = ('mass1 mass2 mtotal mchirp eta spin1x ' 'spin1y spin1z spin2x spin2y spin2z').split() for sngl in sngl_inspiral_table: if sngl.ifo in followup_ifos: for bcf in bayestar_check_fields: setattr(sngl, bcf, getattr(sngl_populated, bcf)) sngl.set_end(lal.LIGOTimeGPS(subthreshold_sngl_time)) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_populated.mchirp coinc_inspiral_row.mass = sngl_populated.mtotal coinc_inspiral_row.end_time = sngl_populated.end_time coinc_inspiral_row.end_time_ns = sngl_populated.end_time_ns coinc_inspiral_row.snr = coinc_results['foreground/stat'] far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) self.outdoc = outdoc self.time = sngl_populated.get_end()
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is defined in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. psds: dict of FrequencySeries Dictionary providing PSD estimates for all involved detectors. low_frequency_cutoff: float Minimum valid frequency for the PSD estimates. followup_data: dict of dicts, optional Dictionary providing SNR time series for each detector, to be used in sky localization with BAYESTAR. The format should be `followup_data['H1']['snr_series']`. More detectors can be present than given in `ifos`. If so, the extra detectors will only be used for sky localization. channel_names: dict of strings, optional Strain channel names for each detector. Will be recorded in the sngl_inspiral table. """ self.template_id = coinc_results['foreground/%s/template_id' % ifos[0]] # remember if this should be marked as HWINJ self.is_hardware_injection = ('HWINJ' in coinc_results and coinc_results['HWINJ']) if 'followup_data' in kwargs: fud = kwargs['followup_data'] assert len({fud[ifo]['snr_series'].delta_t for ifo in fud}) == 1, \ "delta_t for all ifos do not match" self.snr_series = {ifo: fud[ifo]['snr_series'] for ifo in fud} usable_ifos = fud.keys() followup_ifos = list(set(usable_ifos) - set(ifos)) else: self.snr_series = None usable_ifos = ifos followup_ifos = [] # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=usable_ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/'+pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(usable_ifos) coinc_event_row.instruments = ','.join(usable_ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_populated = None network_snrsq = 0 for sngl_id, ifo in enumerate(usable_ifos): sngl = return_empty_sngl(nones=True) sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl.process_id = proc_id sngl.ifo = ifo names = [n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n] for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(lal.LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass if sngl.mass1 and sngl.mass2: sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl_populated = sngl if sngl.snr: sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr network_snrsq += sngl.snr ** 2.0 if 'channel_names' in kwargs and ifo in kwargs['channel_names']: sngl.channel = kwargs['channel_names'][ifo] sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) if self.snr_series is not None: snr_series_to_xml(self.snr_series[ifo], outdoc, sngl.event_id) # for subthreshold detectors, respect BAYESTAR's assumptions and checks bayestar_check_fields = ('mass1 mass2 mtotal mchirp eta spin1x ' 'spin1y spin1z spin2x spin2y spin2z').split() subthreshold_sngl_time = numpy.mean( [coinc_results['foreground/{}/end_time'.format(ifo)] for ifo in ifos]) for sngl in sngl_inspiral_table: if sngl.ifo in followup_ifos: for bcf in bayestar_check_fields: setattr(sngl, bcf, getattr(sngl_populated, bcf)) sngl.set_end(lal.LIGOTimeGPS(subthreshold_sngl_time)) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(usable_ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_populated.mchirp coinc_inspiral_row.mass = sngl_populated.mtotal coinc_inspiral_row.end_time = sngl_populated.end_time coinc_inspiral_row.end_time_ns = sngl_populated.end_time_ns coinc_inspiral_row.snr = network_snrsq ** 0.5 far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) # append the PSDs self.psds = kwargs['psds'] psds_lal = {} for ifo in self.psds: psd = self.psds[ifo] kmin = int(kwargs['low_frequency_cutoff'] / psd.delta_f) fseries = lal.CreateREAL8FrequencySeries( "psd", psd.epoch, kwargs['low_frequency_cutoff'], psd.delta_f, lal.StrainUnit**2 / lal.HertzUnit, len(psd) - kmin) fseries.data.data = psd.numpy()[kmin:] / pycbc.DYN_RANGE_FAC ** 2.0 psds_lal[ifo] = fseries make_psd_xmldoc(psds_lal, outdoc) self.outdoc = outdoc self.time = sngl_populated.get_end()
def to_coinc_xml_object(self, file_name): outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) ifos = list(self.sngl_files.keys()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/' + pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id search_summ_table = lsctables.New(lsctables.SearchSummaryTable) coinc_h5file = self.coinc_file.h5file try: start_time = coinc_h5file['segments']['coinc']['start'][:].min() end_time = coinc_h5file['segments']['coinc']['end'][:].max() except KeyError: start_times = [] end_times = [] for ifo_comb in coinc_h5file['segments']: if ifo_comb == 'foreground_veto': continue seg_group = coinc_h5file['segments'][ifo_comb] start_times.append(seg_group['start'][:].min()) end_times.append(seg_group['end'][:].max()) start_time = min(start_times) end_time = max(end_times) num_trigs = len(self.sort_arr) search_summary = return_search_summary(start_time, end_time, num_trigs, ifos) search_summ_table.append(search_summary) outdoc.childNodes[0].appendChild(search_summ_table) sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) time_slide_table = lsctables.New(lsctables.TimeSlideTable) # Set up time_slide table time_slide_id = lsctables.TimeSlideID(0) for ifo in ifos: time_slide_row = lsctables.TimeSlide() time_slide_row.instrument = ifo time_slide_row.time_slide_id = time_slide_id time_slide_row.offset = 0 time_slide_row.process_id = proc_id time_slide_table.append(time_slide_row) # Set up coinc_definer table coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = \ "sngl_inspiral<-->sngl_inspiral coincidences" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) bank_col_names = ['mass1', 'mass2', 'spin1z', 'spin2z'] bank_col_vals = {} for name in bank_col_names: bank_col_vals[name] = self.get_bankfile_array(name) coinc_event_names = ['ifar', 'time', 'fap', 'stat'] coinc_event_vals = {} for name in coinc_event_names: if name == 'time': coinc_event_vals[name] = self.get_end_time() else: coinc_event_vals[name] = self.get_coincfile_array(name) sngl_col_names = [ 'snr', 'chisq', 'chisq_dof', 'bank_chisq', 'bank_chisq_dof', 'cont_chisq', 'cont_chisq_dof', 'end_time', 'template_duration', 'coa_phase', 'sigmasq' ] sngl_col_vals = {} for name in sngl_col_names: sngl_col_vals[name] = self.get_snglfile_array_dict(name) sngl_event_count = 0 for idx in range(len(self.sort_arr)): # Set up IDs and mapping values coinc_id = lsctables.CoincID(idx) # Set up sngls # FIXME: As two-ifo is hardcoded loop over all ifos sngl_combined_mchirp = 0 sngl_combined_mtot = 0 net_snrsq = 0 for ifo in ifos: # If this ifo is not participating in this coincidence then # ignore it and move on. if not sngl_col_vals['snr'][ifo][1][idx]: continue event_id = lsctables.SnglInspiralID(sngl_event_count) sngl_event_count += 1 sngl = return_empty_sngl() sngl.event_id = event_id sngl.ifo = ifo net_snrsq += sngl_col_vals['snr'][ifo][0][idx]**2 for name in sngl_col_names: val = sngl_col_vals[name][ifo][0][idx] if name == 'end_time': sngl.set_end(LIGOTimeGPS(val)) else: setattr(sngl, name, val) for name in bank_col_names: val = bank_col_vals[name][idx] setattr(sngl, name, val) sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_combined_mchirp += sngl.mchirp sngl_combined_mtot += sngl.mtotal sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = event_id coinc_event_map_table.append(coinc_map_row) sngl_combined_mchirp = sngl_combined_mchirp / len(ifos) sngl_combined_mtot = sngl_combined_mtot / len(ifos) # Set up coinc inspiral and coinc event tables coinc_event_row = lsctables.Coinc() coinc_inspiral_row = lsctables.CoincInspiral() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_inspiral_row.set_ifos(ifos) coinc_event_row.time_slide_id = time_slide_id coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl_combined_mchirp coinc_inspiral_row.mass = sngl_combined_mtot coinc_inspiral_row.set_end( LIGOTimeGPS(coinc_event_vals['time'][idx])) coinc_inspiral_row.snr = net_snrsq**0.5 coinc_inspiral_row.false_alarm_rate = coinc_event_vals['fap'][idx] coinc_inspiral_row.combined_far = 1. / coinc_event_vals['ifar'][idx] # Transform to Hz coinc_inspiral_row.combined_far = \ coinc_inspiral_row.combined_far / YRJUL_SI coinc_event_row.likelihood = coinc_event_vals['stat'][idx] coinc_inspiral_row.minimum_duration = 0. coinc_event_table.append(coinc_event_row) coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_def_table) outdoc.childNodes[0].appendChild(coinc_event_table) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(time_slide_table) outdoc.childNodes[0].appendChild(coinc_inspiral_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) ligolw_utils.write_filename(outdoc, file_name)
def calculate_ethinca_metric_comps(metricParams, ethincaParams, mass1, mass2, spin1z=0., spin2z=0., full_ethinca=True): """ Calculate the Gamma components needed to use the ethinca metric. At present this outputs the standard TaylorF2 metric over the end time and chirp times \tau_0 and \tau_3. A desirable upgrade might be to use the \chi coordinates [defined WHERE?] for metric distance instead of \tau_0 and \tau_3. The lower frequency cutoff is currently hard-coded to be the same as the bank layout options fLow and f0 (which must be the same as each other). Parameters ----------- metricParams : metricParameters instance Structure holding all the options for construction of the metric and the eigenvalues, eigenvectors and covariance matrix needed to manipulate the space. ethincaParams : ethincaParameters instance Structure holding options relevant to the ethinca metric computation. mass1 : float Mass of the heavier body in the considered template. mass2 : float Mass of the lighter body in the considered template. spin1z : float (optional, default=0) Spin of the heavier body in the considered template. spin2z : float (optional, default=0) Spin of the lighter body in the considered template. full_ethinca : boolean (optional, default=True) If True calculate the ethinca components in all 3 directions (mass1, mass2 and time). If False calculate only the time component (which is stored in Gamma0). Returns -------- fMax_theor : float Value of the upper frequency cutoff given by the template parameters and the cutoff formula requested. gammaVals : numpy_array Array holding 6 independent metric components in (end_time, tau_0, tau_3) coordinates to be stored in the Gamma0-5 slots of a SnglInspiral object. """ if (float(spin1z) != 0. or float(spin2z) != 0.) and full_ethinca: raise NotImplementedError("Ethinca cannot at present be calculated " "for nonzero component spins!") f0 = metricParams.f0 if f0 != metricParams.fLow: raise ValueError("If calculating ethinca the bank f0 value must be " "equal to f-low!") if ethincaParams.fLow is not None and ( ethincaParams.fLow != metricParams.fLow): raise NotImplementedError("An ethinca metric f-low different from the" " bank metric f-low is not supported!") twicePNOrder = ethinca_order_from_string(ethincaParams.pnOrder) piFl = PI * f0 totalMass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) totalMass = totalMass * MTSUN_SI v0cube = totalMass*piFl v0 = v0cube**(1./3.) # Get theoretical cutoff frequency and work out the closest # frequency for which moments were calculated fMax_theor = pnutils.frequency_cutoff_from_name( ethincaParams.cutoff, mass1, mass2, spin1z, spin2z) fMaxes = metricParams.moments['J4'].keys() fMaxIdx = abs(numpy.array(fMaxes,dtype=float) - fMax_theor).argmin() fMax = fMaxes[fMaxIdx] # Set the appropriate moments Js = numpy.zeros([18,3],dtype=float) for i in range(18): Js[i,0] = metricParams.moments['J%d'%(i)][fMax] Js[i,1] = metricParams.moments['log%d'%(i)][fMax] Js[i,2] = metricParams.moments['loglog%d'%(i)][fMax] # Compute the time-dependent metric term. two_pi_flower_sq = TWOPI * f0 * TWOPI * f0 gammaVals = numpy.zeros([6],dtype=float) gammaVals[0] = 0.5 * two_pi_flower_sq * \ ( Js[(1,0)] - (Js[(4,0)]*Js[(4,0)]) ) # If mass terms not required stop here if not full_ethinca: return fMax_theor, gammaVals # 3pN is a mess, so split it into pieces a0 = 11583231236531/200286535680 - 5*PI*PI - 107*GAMMA/14 a1 = (-15737765635/130056192 + 2255*PI*PI/512)*eta a2 = (76055/73728)*eta*eta a3 = (-127825/55296)*eta*eta*eta alog = numpy.log(4*v0) # Log terms are tricky - be careful # Get the Psi coefficients Psi = [{},{}] #Psi = numpy.zeros([2,8,2],dtype=float) Psi[0][0,0] = 3/5 Psi[0][2,0] = (743/756 + 11*eta/3)*v0*v0 Psi[0][3,0] = 0. Psi[0][4,0] = (-3058673/508032 + 5429*eta/504 + 617*eta*eta/24)\ *v0cube*v0 Psi[0][5,1] = (-7729*PI/126)*v0cube*v0*v0/3 Psi[0][6,0] = (128/15)*(-3*a0 - a1 + a2 + 3*a3 + 107*(1+3*alog)/14)\ *v0cube*v0cube Psi[0][6,1] = (6848/35)*v0cube*v0cube/3 Psi[0][7,0] = (-15419335/63504 - 75703*eta/756)*PI*v0cube*v0cube*v0 Psi[1][0,0] = 0. Psi[1][2,0] = (3715/12096 - 55*eta/96)/PI/v0; Psi[1][3,0] = -3/2 Psi[1][4,0] = (15293365/4064256 - 27145*eta/16128 - 3085*eta*eta/384)\ *v0/PI Psi[1][5,1] = (193225/8064)*v0*v0/3 Psi[1][6,0] = (4/PI)*(2*a0 + a1/3 - 4*a2/3 - 3*a3 -107*(1+6*alog)/42)\ *v0cube Psi[1][6,1] = (-428/PI/7)*v0cube/3 Psi[1][7,0] = (77096675/1161216 + 378515*eta/24192 + 74045*eta*eta/8064)\ *v0cube*v0 # Set the appropriate moments Js = numpy.zeros([18,3],dtype=float) for i in range(18): Js[i,0] = metricParams.moments['J%d'%(i)][fMax] Js[i,1] = metricParams.moments['log%d'%(i)][fMax] Js[i,2] = metricParams.moments['loglog%d'%(i)][fMax] # Calculate the g matrix PNterms = [(0,0),(2,0),(3,0),(4,0),(5,1),(6,0),(6,1),(7,0)] PNterms = [term for term in PNterms if term[0] <= twicePNOrder] # Now can compute the mass-dependent gamma values for m in [0, 1]: for k in PNterms: gammaVals[1+m] += 0.5 * two_pi_flower_sq * Psi[m][k] * \ ( Js[(9-k[0],k[1])] - Js[(12-k[0],k[1])] * Js[(4,0)] ) g = numpy.zeros([2,2],dtype=float) for (m,n) in [(0,0),(0,1),(1,1)]: for k in PNterms: for l in PNterms: g[m,n] += Psi[m][k] * Psi[n][l] * \ ( Js[(17-k[0]-l[0], k[1]+l[1])] - Js[(12-k[0],k[1])] * Js[(12-l[0],l[1])] ) g[m,n] = 0.5 * two_pi_flower_sq * g[m,n] g[n,m] = g[m,n] gammaVals[3] = g[0,0] gammaVals[4] = g[0,1] gammaVals[5] = g[1,1] return fMax_theor, gammaVals
def calculate_ethinca_metric_comps(metricParams, ethincaParams, mass1, mass2, spin1z=0., spin2z=0., full_ethinca=True): """ Calculate the Gamma components needed to use the ethinca metric. At present this outputs the standard TaylorF2 metric over the end time and chirp times \tau_0 and \tau_3. A desirable upgrade might be to use the \chi coordinates [defined WHERE?] for metric distance instead of \tau_0 and \tau_3. The lower frequency cutoff is currently hard-coded to be the same as the bank layout options fLow and f0 (which must be the same as each other). Parameters ----------- metricParams : metricParameters instance Structure holding all the options for construction of the metric and the eigenvalues, eigenvectors and covariance matrix needed to manipulate the space. ethincaParams : ethincaParameters instance Structure holding options relevant to the ethinca metric computation. mass1 : float Mass of the heavier body in the considered template. mass2 : float Mass of the lighter body in the considered template. spin1z : float (optional, default=0) Spin of the heavier body in the considered template. spin2z : float (optional, default=0) Spin of the lighter body in the considered template. full_ethinca : boolean (optional, default=True) If True calculate the ethinca components in all 3 directions (mass1, mass2 and time). If False calculate only the time component (which is stored in Gamma0). Returns -------- fMax_theor : float Value of the upper frequency cutoff given by the template parameters and the cutoff formula requested. gammaVals : numpy_array Array holding 6 independent metric components in (end_time, tau_0, tau_3) coordinates to be stored in the Gamma0-5 slots of a SnglInspiral object. """ if (float(spin1z) != 0. or float(spin2z) != 0.) and full_ethinca: raise NotImplementedError("Ethinca cannot at present be calculated " "for nonzero component spins!") f0 = metricParams.f0 if f0 != metricParams.fLow: raise ValueError("If calculating ethinca the bank f0 value must be " "equal to f-low!") if ethincaParams.fLow is not None and (ethincaParams.fLow != metricParams.fLow): raise NotImplementedError("An ethinca metric f-low different from the" " bank metric f-low is not supported!") twicePNOrder = ethinca_order_from_string(ethincaParams.pnOrder) piFl = PI * f0 totalMass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) totalMass = totalMass * MTSUN_SI v0cube = totalMass * piFl v0 = v0cube**(1. / 3.) # Get theoretical cutoff frequency and work out the closest # frequency for which moments were calculated fMax_theor = pnutils.frequency_cutoff_from_name(ethincaParams.cutoff, mass1, mass2, spin1z, spin2z) fMaxes = metricParams.moments['J4'].keys() fMaxIdx = abs(numpy.array(fMaxes, dtype=float) - fMax_theor).argmin() fMax = fMaxes[fMaxIdx] # Set the appropriate moments Js = numpy.zeros([18, 3], dtype=float) for i in range(18): Js[i, 0] = metricParams.moments['J%d' % (i)][fMax] Js[i, 1] = metricParams.moments['log%d' % (i)][fMax] Js[i, 2] = metricParams.moments['loglog%d' % (i)][fMax] # Compute the time-dependent metric term. two_pi_flower_sq = TWOPI * f0 * TWOPI * f0 gammaVals = numpy.zeros([6], dtype=float) gammaVals[0] = 0.5 * two_pi_flower_sq * \ ( Js[(1,0)] - (Js[(4,0)]*Js[(4,0)]) ) # If mass terms not required stop here if not full_ethinca: return fMax_theor, gammaVals # 3pN is a mess, so split it into pieces a0 = 11583231236531 / 200286535680 - 5 * PI * PI - 107 * GAMMA / 14 a1 = (-15737765635 / 130056192 + 2255 * PI * PI / 512) * eta a2 = (76055 / 73728) * eta * eta a3 = (-127825 / 55296) * eta * eta * eta alog = numpy.log(4 * v0) # Log terms are tricky - be careful # Get the Psi coefficients Psi = [{}, {}] #Psi = numpy.zeros([2,8,2],dtype=float) Psi[0][0, 0] = 3 / 5 Psi[0][2, 0] = (743 / 756 + 11 * eta / 3) * v0 * v0 Psi[0][3, 0] = 0. Psi[0][4,0] = (-3058673/508032 + 5429*eta/504 + 617*eta*eta/24)\ *v0cube*v0 Psi[0][5, 1] = (-7729 * PI / 126) * v0cube * v0 * v0 / 3 Psi[0][6,0] = (128/15)*(-3*a0 - a1 + a2 + 3*a3 + 107*(1+3*alog)/14)\ *v0cube*v0cube Psi[0][6, 1] = (6848 / 35) * v0cube * v0cube / 3 Psi[0][7, 0] = (-15419335 / 63504 - 75703 * eta / 756) * PI * v0cube * v0cube * v0 Psi[1][0, 0] = 0. Psi[1][2, 0] = (3715 / 12096 - 55 * eta / 96) / PI / v0 Psi[1][3, 0] = -3 / 2 Psi[1][4,0] = (15293365/4064256 - 27145*eta/16128 - 3085*eta*eta/384)\ *v0/PI Psi[1][5, 1] = (193225 / 8064) * v0 * v0 / 3 Psi[1][6,0] = (4/PI)*(2*a0 + a1/3 - 4*a2/3 - 3*a3 -107*(1+6*alog)/42)\ *v0cube Psi[1][6, 1] = (-428 / PI / 7) * v0cube / 3 Psi[1][7,0] = (77096675/1161216 + 378515*eta/24192 + 74045*eta*eta/8064)\ *v0cube*v0 # Set the appropriate moments Js = numpy.zeros([18, 3], dtype=float) for i in range(18): Js[i, 0] = metricParams.moments['J%d' % (i)][fMax] Js[i, 1] = metricParams.moments['log%d' % (i)][fMax] Js[i, 2] = metricParams.moments['loglog%d' % (i)][fMax] # Calculate the g matrix PNterms = [(0, 0), (2, 0), (3, 0), (4, 0), (5, 1), (6, 0), (6, 1), (7, 0)] PNterms = [term for term in PNterms if term[0] <= twicePNOrder] # Now can compute the mass-dependent gamma values for m in [0, 1]: for k in PNterms: gammaVals[1+m] += 0.5 * two_pi_flower_sq * Psi[m][k] * \ ( Js[(9-k[0],k[1])] - Js[(12-k[0],k[1])] * Js[(4,0)] ) g = numpy.zeros([2, 2], dtype=float) for (m, n) in [(0, 0), (0, 1), (1, 1)]: for k in PNterms: for l in PNterms: g[m,n] += Psi[m][k] * Psi[n][l] * \ ( Js[(17-k[0]-l[0], k[1]+l[1])] - Js[(12-k[0],k[1])] * Js[(12-l[0],l[1])] ) g[m, n] = 0.5 * two_pi_flower_sq * g[m, n] g[n, m] = g[m, n] gammaVals[3] = g[0, 0] gammaVals[4] = g[0, 1] gammaVals[5] = g[1, 1] return fMax_theor, gammaVals
def __init__(self, ifos, coinc_results, **kwargs): """Initialize a ligolw xml representation of a zerolag trigger for upload from pycbc live to gracedb. Parameters ---------- ifos: list of strs A list of the ifos pariticipating in this trigger coinc_results: dict of values A dictionary of values. The format is define in pycbc/events/coinc.py and matches the on disk representation in the hdf file for this time. """ self.ifos = ifos self.template_id = coinc_results['foreground/%s/template_id' % self.ifos[0]] # remember if this should be marked as HWINJ self.is_hardware_injection = False if 'HWINJ' in coinc_results: self.is_hardware_injection = True # Set up the bare structure of the xml document outdoc = ligolw.Document() outdoc.appendChild(ligolw.LIGO_LW()) proc_id = ligolw_process.register_to_xmldoc( outdoc, 'pycbc', {}, ifos=ifos, comment='', version=pycbc_version.git_hash, cvs_repository='pycbc/' + pycbc_version.git_branch, cvs_entry_time=pycbc_version.date).process_id # Set up coinc_definer table coinc_def_table = lsctables.New(lsctables.CoincDefTable) coinc_def_id = lsctables.CoincDefID(0) coinc_def_row = lsctables.CoincDef() coinc_def_row.search = "inspiral" coinc_def_row.description = "sngl_inspiral<-->sngl_inspiral coincs" coinc_def_row.coinc_def_id = coinc_def_id coinc_def_row.search_coinc_type = 0 coinc_def_table.append(coinc_def_row) outdoc.childNodes[0].appendChild(coinc_def_table) # Set up coinc inspiral and coinc event tables coinc_id = lsctables.CoincID(0) coinc_event_table = lsctables.New(lsctables.CoincTable) coinc_event_row = lsctables.Coinc() coinc_event_row.coinc_def_id = coinc_def_id coinc_event_row.nevents = len(ifos) coinc_event_row.instruments = ','.join(ifos) coinc_event_row.time_slide_id = lsctables.TimeSlideID(0) coinc_event_row.process_id = proc_id coinc_event_row.coinc_event_id = coinc_id coinc_event_row.likelihood = 0. coinc_event_table.append(coinc_event_row) outdoc.childNodes[0].appendChild(coinc_event_table) # Set up sngls sngl_inspiral_table = lsctables.New(lsctables.SnglInspiralTable) coinc_event_map_table = lsctables.New(lsctables.CoincMapTable) sngl_id = 0 sngl_event_id_map = {} for ifo in ifos: names = [ n.split('/')[-1] for n in coinc_results if 'foreground/%s' % ifo in n ] sngl_id += 1 sngl = return_empty_sngl() sngl.event_id = lsctables.SnglInspiralID(sngl_id) sngl_event_id_map[ifo] = sngl.event_id sngl.ifo = ifo for name in names: val = coinc_results['foreground/%s/%s' % (ifo, name)] if name == 'end_time': sngl.set_end(lal.LIGOTimeGPS(val)) else: try: setattr(sngl, name, val) except AttributeError: pass sngl.mtotal, sngl.eta = pnutils.mass1_mass2_to_mtotal_eta( sngl.mass1, sngl.mass2) sngl.mchirp, _ = pnutils.mass1_mass2_to_mchirp_eta( sngl.mass1, sngl.mass2) sngl.eff_distance = (sngl.sigmasq)**0.5 / sngl.snr sngl_inspiral_table.append(sngl) # Set up coinc_map entry coinc_map_row = lsctables.CoincMap() coinc_map_row.table_name = 'sngl_inspiral' coinc_map_row.coinc_event_id = coinc_id coinc_map_row.event_id = sngl.event_id coinc_event_map_table.append(coinc_map_row) outdoc.childNodes[0].appendChild(coinc_event_map_table) outdoc.childNodes[0].appendChild(sngl_inspiral_table) # Set up the coinc inspiral table coinc_inspiral_table = lsctables.New(lsctables.CoincInspiralTable) coinc_inspiral_row = lsctables.CoincInspiral() # This seems to be used as FAP, which should not be in gracedb coinc_inspiral_row.false_alarm_rate = 0 coinc_inspiral_row.minimum_duration = 0. coinc_inspiral_row.set_ifos(ifos) coinc_inspiral_row.coinc_event_id = coinc_id coinc_inspiral_row.mchirp = sngl.mchirp coinc_inspiral_row.mass = sngl.mtotal coinc_inspiral_row.end_time = sngl.end_time coinc_inspiral_row.end_time_ns = sngl.end_time_ns coinc_inspiral_row.snr = coinc_results['foreground/stat'] far = 1.0 / (lal.YRJUL_SI * coinc_results['foreground/ifar']) coinc_inspiral_row.combined_far = far coinc_inspiral_table.append(coinc_inspiral_row) outdoc.childNodes[0].appendChild(coinc_inspiral_table) self.outdoc = outdoc self.time = sngl.get_end() # compute SNR time series self.upload_snr_series = kwargs['upload_snr_series'] if self.upload_snr_series: data_readers = kwargs['data_readers'] bank = kwargs['bank'] htilde = bank[self.template_id] self.snr_series = {} self.snr_series_psd = {} for ifo in self.ifos: stilde = data_readers[ifo].overwhitened_data(htilde.delta_f) norm = 4.0 * htilde.delta_f / (htilde.sigmasq(stilde.psd)**0.5) qtilde = zeros((len(htilde) - 1) * 2, dtype=htilde.dtype) correlate(htilde, stilde, qtilde) snr = qtilde * 0 ifft(qtilde, snr) valid_end = int(len(qtilde) - data_readers[ifo].trim_padding) valid_start = int(valid_end - data_readers[ifo].blocksize * data_readers[ifo].sample_rate) seg = slice(valid_start, valid_end) snr = snr[seg] snr *= norm delta_t = 1.0 / data_readers[ifo].sample_rate start = data_readers[ifo].start_time snr = TimeSeries(snr, delta_t=delta_t, epoch=start) self.snr_series[ifo] = snr self.snr_series_psd[ifo] = stilde.psd # store the on-source slice of the series into the XML doc snr_onsource_time = coinc_results['foreground/%s/end_time' % ifo] - snr.start_time snr_onsource_dur = lal.REARTH_SI / lal.C_SI onsource_idx = round(snr_onsource_time * snr.sample_rate) onsource_start = onsource_idx - int( snr.sample_rate * snr_onsource_dur / 2) onsource_end = onsource_idx + int( snr.sample_rate * snr_onsource_dur / 2) onsource_slice = slice(onsource_start, onsource_end + 1) snr_lal = snr[onsource_slice].lal() snr_lal.name = 'snr' snr_lal.sampleUnits = '' snr_xml = _build_series(snr_lal, (u'Time', u'Time,Real,Imaginary'), None, 'deltaT', 's') snr_node = outdoc.childNodes[-1].appendChild(snr_xml) eid_param = ligolw_param.new_param( u'event_id', u'ilwd:char', unicode(sngl_event_id_map[ifo])) snr_node.appendChild(eid_param)