def __init__(self, instruments): self.densities = {} for instrument in instruments: self.densities["%s_snr2_chi2" % instrument] = rate.BinnedLnPDF(rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801)))) for pair in itertools.combinations(sorted(instruments), 2): dt = 0.005 + snglcoinc.light_travel_time(instrument1, instrument2) # seconds self.densities["%s_%s_dt" % pair] = rate.BinnedLnPDF(rate.NDBins((rate.ATanBins(-dt, +dt, 801),))) self.densities["%s_%s_dA" % pair] = rate.BinnedLnPDF(rate.NDBins((rate.ATanBins(-0.5, +0.5, 801),))) self.densities["%s_%s_df" % pair] = rate.BinnedLnPDF(rate.NDBins((rate.ATanBins(-0.2, +0.2, 501),))) # only non-negative rss timing residual bins will be used # but we want a binning that's linear at the origin so # instead of inventing a new one we just use atan bins that # are symmetric about 0 self.densities["instrumentgroup,rss_timing_residual"] = rate.BinnedLnPDF(rate.NDBins((snglcoinc.InstrumentBins(names = instruments), rate.ATanBins(-0.02, +0.02, 1001))))
def dt_binning(instrument1, instrument2): # FIXME: hard-coded for directional search #dt = 0.02 + inject.light_travel_time(instrument1, instrument2) dt = 0.02 return rate.NDBins( (rate.ATanBins(-dt, +dt, 12001), rate.LinearBins(0.0, 2 * math.pi, 61)))
def __init__(self, instruments): self.densities = {} for pair in intertools.combinations(sorted(instruments), 2): # FIXME: hard-coded for directional search #dt = 0.02 + snglcoinc.light_travel_time(*pair) dt = 0.02 self.densities["%s_%s_dt" % pair] = rate.BinnedLnDPF( rate.NDBins((rate.ATanBins(-dt, +dt, 12001), rate.LinearBins(0.0, 2 * math.pi, 61)))) self.densities["%s_%s_dband" % pair] = rate.BinnedLnDPF( rate.NDBins((rate.LinearBins(-2.0, +2.0, 12001), rate.LinearBins(0.0, 2 * math.pi, 61)))) self.densities["%s_%s_ddur" % pair] = rate.BinnedLnDPF( rate.NDBins((rate.LinearBins(-2.0, +2.0, 12001), rate.LinearBins(0.0, 2 * math.pi, 61)))) self.densities["%s_%s_df" % pair] = rate.BinnedLnDPF( rate.NDBins((rate.LinearBins(-2.0, +2.0, 12001), rate.LinearBins(0.0, 2 * math.pi, 61)))) self.densities["%s_%s_dh" % pair] = rate.BinnedLnDPF( rate.NDBins((rate.LinearBins(-2.0, +2.0, 12001), rate.LinearBins(0.0, 2 * math.pi, 61))))
def dt_binning(instrument1, instrument2): dt = 0.005 + snglcoinc.light_travel_time(instrument1, instrument2) # seconds return rate.NDBins((rate.ATanBins(-dt, +dt, 801), ))
class StringCoincParamsDistributions(snglcoinc.CoincParamsDistributions): ligo_lw_name_suffix = u"stringcusp_coincparamsdistributions" binnings = { "H1_snr2_chi2": rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801))), "H2_snr2_chi2": rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801))), "L1_snr2_chi2": rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801))), "V1_snr2_chi2": rate.NDBins((rate.ATanLogarithmicBins(10, 1e7, 801), rate.ATanLogarithmicBins(.1, 1e4, 801))), "H1_H2_dt": dt_binning("H1", "H2"), "H1_L1_dt": dt_binning("H1", "L1"), "H1_V1_dt": dt_binning("H1", "V1"), "H2_L1_dt": dt_binning("H2", "L1"), "H2_V1_dt": dt_binning("H2", "V1"), "L1_V1_dt": dt_binning("L1", "V1"), "H1_H2_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "H1_L1_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "H1_V1_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "H2_L1_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "H2_V1_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "L1_V1_dA": rate.NDBins((rate.ATanBins(-0.5, +0.5, 801), )), "H1_H2_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), "H1_L1_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), "H1_V1_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), "H2_L1_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), "H2_V1_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), "L1_V1_df": rate.NDBins((rate.ATanBins(-0.2, +0.2, 501), )), # only non-negative rss timing residual bins will be used # but we want a binning that's linear at the origin so # instead of inventing a new one we just use atan bins that # are symmetric about 0 "instrumentgroup,rss_timing_residual": rate.NDBins((snglcoinc.InstrumentBins(names=("H1", "H2", "L1", "V1")), rate.ATanBins(-0.02, +0.02, 1001))) } filters = { "H1_snr2_chi2": rate.gaussian_window(11, 11, sigma=20), "H2_snr2_chi2": rate.gaussian_window(11, 11, sigma=20), "L1_snr2_chi2": rate.gaussian_window(11, 11, sigma=20), "V1_snr2_chi2": rate.gaussian_window(11, 11, sigma=20), "H1_H2_dt": rate.gaussian_window(11, sigma=20), "H1_L1_dt": rate.gaussian_window(11, sigma=20), "H1_V1_dt": rate.gaussian_window(11, sigma=20), "H2_L1_dt": rate.gaussian_window(11, sigma=20), "H2_V1_dt": rate.gaussian_window(11, sigma=20), "L1_V1_dt": rate.gaussian_window(11, sigma=20), "H1_H2_dA": rate.gaussian_window(11, sigma=20), "H1_L1_dA": rate.gaussian_window(11, sigma=20), "H1_V1_dA": rate.gaussian_window(11, sigma=20), "H2_L1_dA": rate.gaussian_window(11, sigma=20), "H2_V1_dA": rate.gaussian_window(11, sigma=20), "L1_V1_dA": rate.gaussian_window(11, sigma=20), "H1_H2_df": rate.gaussian_window(11, sigma=20), "H1_L1_df": rate.gaussian_window(11, sigma=20), "H1_V1_df": rate.gaussian_window(11, sigma=20), "H2_L1_df": rate.gaussian_window(11, sigma=20), "H2_V1_df": rate.gaussian_window(11, sigma=20), "L1_V1_df": rate.gaussian_window(11, sigma=20), # instrument group filter is a no-op, should produce a # 1-bin top-hat window. "instrumentgroup,rss_timing_residual": rate.gaussian_window(1e-100, 11, sigma=20) } @staticmethod def coinc_params(events, offsetvector, triangulators): # # check for coincs that have been vetoed entirely # if len(events) < 2: return None # # Initialize the parameter dictionary, sort the events by # instrument name (the multi-instrument parameters are defined for # the instruments in this order and the triangulators are # constructed this way too), and retrieve the sorted instrument # names # params = {} events = tuple(sorted(events, key=lambda event: event.ifo)) instruments = tuple(event.ifo for event in events) # # zero-instrument parameters # ignored, ignored, ignored, rss_timing_residual = triangulators[ instruments](tuple(event.peak + offsetvector[event.ifo] for event in events)) # FIXME: rss_timing_residual is forced to 0 to disable this # feature. all the code to compute it properly is still here and # given suitable initializations, the distribution data is still # two-dimensional and has a suitable filter applied to it, but all # events are forced into the RSS_{\Delta t} = 0 bin, in effect # removing that dimension from the data. We can look at this again # sometime in the future if we're curious why it didn't help. Just # delete the next line and you're back in business. rss_timing_residual = 0.0 params["instrumentgroup,rss_timing_residual"] = ( frozenset(instruments), rss_timing_residual) # # one-instrument parameters # for event in events: prefix = "%s_" % event.ifo params["%ssnr2_chi2" % prefix] = (event.snr**2.0, event.chisq / event.chisq_dof) # # two-instrument parameters. note that events are sorted by # instrument # for event1, event2 in iterutils.choices(events, 2): assert event1.ifo != event2.ifo prefix = "%s_%s_" % (event1.ifo, event2.ifo) dt = float((event1.peak + offsetvector[event1.ifo]) - (event2.peak + offsetvector[event2.ifo])) params["%sdt" % prefix] = (dt, ) dA = math.log10(abs(event1.amplitude / event2.amplitude)) params["%sdA" % prefix] = (dA, ) # f_cut = central_freq + bandwidth/2 f_cut1 = event1.central_freq + event1.bandwidth / 2 f_cut2 = event2.central_freq + event2.bandwidth / 2 df = float((math.log10(f_cut1) - math.log10(f_cut2)) / (math.log10(f_cut1) + math.log10(f_cut2))) params["%sdf" % prefix] = (df, ) # # done # return params def add_slidelessbackground(self, database, experiments, param_func_args=()): # FIXME: this needs to be taught how to not slide H1 and # H2 with respect to each other # segment lists seglists = database.seglists - database.vetoseglists # construct the event list dictionary. remove vetoed # events from the lists and save event peak times so they # can be restored later eventlists = {} orig_peak_times = {} for event in database.sngl_burst_table: if event.peak in seglists[event.ifo]: try: eventlists[event.ifo].append(event) except KeyError: eventlists[event.ifo] = [event] orig_peak_times[event] = event.peak # parse the --thresholds H1,L1=... command-line options from burca delta_t = [ float(threshold.split("=")[-1]) for threshold in ligolw_process.get_process_params( database.xmldoc, "ligolw_burca", "--thresholds") ] if not all(delta_t[0] == threshold for threshold in delta_t[1:]): raise ValueError( "\Delta t is not unique in ligolw_burca arguments") delta_t = delta_t.pop() # construct the coinc generator. note that H1+H2-only # coincs are forbidden, which is affected here by removing # that instrument combination from the object's internal # .rates dictionary coinc_generator = snglcoinc.CoincSynthesizer(eventlists, seglists, delta_t) if frozenset(("H1", "H2")) in coinc_generator.rates: del coinc_generator.rates[frozenset(("H1", "H2"))] # build a dictionary of time-of-arrival generators toa_generator = dict( (instruments, coinc_generator.plausible_toas(instruments)) for instruments in coinc_generator.rates.keys()) # how many coincs? the expected number is obtained by # multiplying the total zero-lag time for which at least # two instruments were on by the sum of the rates for all # coincs to get the mean number of coincs per zero-lag # observation time, and multiplying that by the number of # experiments the background should simulate to get the # mean number of background events to simulate. the actual # number simulated is a Poisson-distributed RV with that # mean. n_coincs, = scipy.stats.poisson.rvs( float(abs(segmentsUtils.vote(seglists.values(), 2))) * sum(coinc_generator.rates.values()) * experiments) # generate synthetic background coincs zero_lag_offset_vector = offsetvector( (instrument, 0.0) for instrument in seglists) for n, events in enumerate( coinc_generator.coincs(lsctables.SnglBurst.get_peak)): # n = 1 on 2nd iteration, so placing this condition # where it is in the loop causes the correct number # of events to be added to the background if n >= n_coincs: break # assign fake peak times toas = toa_generator[frozenset(event.ifo for event in events)].next() for event in events: event.peak = toas[event.ifo] # compute coincidence parameters self.add_background( self.coinc_params(events, zero_lag_offset_vector, *param_func_args)) # restore original peak times for event, peak_time in orig_peak_times.iteritems(): event.peak = peak_time