gstlal-inspiral  0.4.2
 All Classes Namespaces Files Functions Variables Pages
gstlal_ll_inspiral_create_prior_diststats
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2010--2014 Kipp Cannon, Chad Hanna
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #
19 ## @file gstlal_ll_inspiral_create_prior_diststats
20 # A program to create some prior likelihood data to seed an online analysis
21 #
22 # ### Command line interface
23 #
24 # --verbose, action = "store_true", help = "Be verbose."
25 # --num-templates, metavar = "N", default = 1000, type = "int", help = "Set the number of templates per bank. default 1000")
26 # --num-banks, metavar = "N", default = 1, type = "int", help = "Set the number of banks. default 1")
27 # --write-likelihood-basename, metavar = "string", default = "prior.xml.gz", help = "Write likelihood files to disk with this basename: default prior.xml.gz.")
28 # --write-likelihood-cache, metavar = "filename", help = "Write likelihood files to disk and include the names in this cachefile file.")
29 # --segment-and-horizon, action = "append", help = "Append to a list of segments and horizon distances for a given instrument. Argument specified as IFO:start:end:horizon, e.g., H1:1000000000:1000000100:120 ")
30 # --background-prefactors, metavar = "s,e", default = "1.0,20.", help = "Set the range of prefactors on the chi-squared distribution for the background model: default 1.0,20.")
31 # --injection-prefactors, metavar = "s,e", default = "0.01,0.25", help = "Set the range of prefactors on the chi-squared distribution for the signal model: default 0.01,0.25")
32 
33 #
34 # =============================================================================
35 #
36 # Preamble
37 #
38 # =============================================================================
39 #
40 
41 
42 import sys
43 import shutil
44 from gstlal import far
45 from gstlal import inspiral_pipe
46 from glue.ligolw import utils as ligolw_utils
47 from glue.ligolw.utils import process as ligolw_process
48 from glue.ligolw import ligolw
49 from glue.text_progress_bar import ProgressBar
50 from pylal import rate, snglcoinc
51 from glue import segments
52 from glue import iterutils
53 from glue.lal import LIGOTimeGPS, CacheEntry
54 import numpy
55 from optparse import OptionParser
56 
57 
58 def parse_command_line():
59  parser = OptionParser(
60  version = "Name: %%prog\n%s" % "" # FIXME
61  )
62  parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
63  parser.add_option("--num-templates", metavar = "N", default = 1000, type = "int", help = "Set the number of templates per bank. default 1000")
64  parser.add_option("--num-banks", metavar = "N", default = 1, type = "int", help = "Set the number of banks. default 1")
65  parser.add_option("--write-likelihood-basename", metavar = "string", default = "prior.xml.gz", help = "Write likelihood files to disk with this basename: default prior.xml.gz.")
66  parser.add_option("--write-likelihood-cache", metavar = "filename", help = "Write likelihood files to disk and include the names in this cachefile file.")
67  parser.add_option("--segment-and-horizon", action = "append", help = "Append to a list of segments and horizon distances for a given instrument. Argument specified as IFO:start:end:horizon, e.g., H1:1000000000:1000000100:120 ")
68  parser.add_option("--background-prefactors", metavar = "s,e", default = "1.0,20.", help = "Set the range of prefactors on the chi-squared distribution for the background model: default 1.0,20.")
69  parser.add_option("--injection-prefactors", metavar = "s,e", default = "0.01,0.25", help = "Set the range of prefactors on the chi-squared distribution for the signal model: default 0.01,0.25")
70  options, filenames = parser.parse_args()
71 
72  process_params = dict(options.__dict__)
73 
74  def parse_segment_and_horizon(options = options):
75  seglistdict = segments.segmentlistdict()
76  horizon_history = {}
77  for x in options.segment_and_horizon:
78  ifo, start, stop, horizon = x.split(":")
79  seglistdict.setdefault(ifo, segments.segmentlist()).append(segments.segment([LIGOTimeGPS(float(start)), LIGOTimeGPS(float(stop))]))
80  horizon_history.setdefault(ifo, far.NearestLeafTree())[LIGOTimeGPS(float(start))] = float(horizon)
81  horizon_history.setdefault(ifo, far.NearestLeafTree())[LIGOTimeGPS(float(stop))] = float(horizon)
82  return seglistdict, horizon_history
83 
84  seglistdict, horizon_history = parse_segment_and_horizon(options)
85 
86  instruments = frozenset(seglistdict)
87  if len(instruments) < 2:
88  raise ValueError("must specify at least two distinct instrument")
89 
90  bankcachedict = None #inspiral_pipe.parse_cache_str(options.bank_cache)
91 
92  return options, process_params, instruments, seglistdict, horizon_history, bankcachedict
93 
94 
95 options, process_params, instruments, segs, horizon_history, bankcachedict = parse_command_line()
96 
97 if options.verbose:
98  print "Livetime: ", abs(segs)
99  print "Extent: ", segs.extent_all()
100 
101 #
102 # quantities derived from input
103 #
104 
105 #
106 # Number of background events in each detector
107 #
108 # This is calculated assuming the following
109 # 1) There are options.num_templates in the analysis
110 # 2) Each template produces exactly 1 trigger for every second that a given
111 # detector is on according to the user provided segments
112 #
113 
114 n_background = dict(((ifo, float(abs(seg)) * options.num_templates) for ifo, seg in segs.items()))
115 
116 #
117 # Number of zero lag events in each detector
118 #
119 # This is calculated assuming the following
120 # 1) Only coincident events go into the zero_lag histograms
121 # 2) The coincidence rate is 100 times lower than background rate for each detector
122 #
123 
124 n_zerolag = dict(((ifo, float(abs(seg)) * options.num_templates / 100.) for ifo, seg in segs.items()))
125 
126 #
127 # Initialize an empty ThincaCoincParamsDistributions class
128 #
129 
130 diststats = far.ThincaCoincParamsDistributions()
131 
132 #
133 # Add background, zero_lag and injection prior distributions in the SNR and chi-squared plane
134 #
135 
136 diststats.add_background_prior(n = n_background, ba = "background_rates", prefactors_range = tuple(float(x) for x in options.background_prefactors.split(",")), verbose = options.verbose)
137 diststats.add_background_prior(n = n_zerolag, ba = "zero_lag_rates", prefactors_range = tuple(float(x) for x in options.background_prefactors.split(",")), verbose = options.verbose)
138 diststats.add_foreground_snrchi_prior(n = dict(((ifo, 1e6) for ifo, seg in segs.items())), prefactors_range = tuple(float(x) for x in options.injection_prefactors.split(",")), verbose = options.verbose)
139 
140 #
141 # Update the horizon distance history with our fake, user provided horizon history
142 #
143 
144 diststats.horizon_history.update(horizon_history)
145 
146 #
147 # Fake a set of coincident triggers for all possible combinations
148 # FIXME Derive these from the segment lists!!! Use Kipp's code??
149 #
150 
151 for i in range(2, len(instruments) + 1):
152  for ifos in [frozenset(x) for x in iterutils.choices(tuple(instruments), i)]:
153  diststats.zero_lag_rates["instruments"][diststats.instrument_categories.category(ifos),] = min(n_background[ifo] for ifo in ifos) / 100.**(i-1)
154 
155 diststats.add_instrument_combination_counts(segs = segs, verbose = options.verbose)
156 
157 #
158 # joint SNR PDF
159 #
160 
161 horizon_distances = dict(((ifo, numpy.mean(h.values())) for ifo, h in horizon_history.items()))
162 for n in range(2, len(horizon_distances) + 1):
163  for instruments in iterutils.choices(horizon_distances.keys(), n):
164  # Force the SNR pdf to be generated to be at the actual horizon distance values not the quantized ones
165  key = frozenset(instruments), frozenset(diststats.quantize_horizon_distances(horizon_distances).items())
166  if options.verbose:
167  print >>sys.stderr, "For horizon distances %s" % ", ".join("%s = %.4g Mpc" % item for item in sorted(horizon_distances.items()))
168  progressbar = ProgressBar(text = "%s SNR PDF" % ", ".join(sorted(key[0])))
169  else:
170  progressbar = None
171  binnedarray = diststats.joint_pdf_of_snrs(key[0], horizon_distances, progressbar = progressbar)
172  del progressbar
173 
174  lnbinnedarray = binnedarray.copy()
175  with numpy.errstate(divide = "ignore"):
176  lnbinnedarray.array = numpy.log(lnbinnedarray.array)
177  pdf = rate.InterpBinnedArray(lnbinnedarray, fill_value = float("-inf"))
178  diststats.snr_joint_pdf_cache[key] = pdf, binnedarray, 0
179 
180 diststats.populate_prob_of_instruments_given_signal(segs)
181 
182 #
183 # Finished with this class
184 #
185 
186 diststats.finish()
187 
188 #
189 # Prep an output XML file
190 #
191 
192 xmldoc = ligolw.Document()
193 xmldoc.appendChild(ligolw.LIGO_LW())
194 process = ligolw_process.register_to_xmldoc(xmldoc, u"gstlal_ll_inspiral_create_prior_diststats", ifos = instruments, paramdict = process_params)
195 
196 #
197 # Instantiate a new RankingData class from our distribution stats
198 #
199 
200 ranking_data = far.RankingData(diststats, instruments = instruments, process_id = process.process_id, nsamples = 100000, verbose = options.verbose)
201 
202 #
203 # Simulate a measured coincident trigger likelihood histogram by just using the background one with a lower normalized count
204 #
205 
206 for instruments in ranking_data.background_likelihood_rates:
207  if instruments is None:
208  continue
209  else:
210  ranking_data.zero_lag_likelihood_rates[instruments].array[:] = ranking_data.background_likelihood_rates[instruments].array[:] / ranking_data.background_likelihood_rates[instruments].array.sum() * diststats.zero_lag_rates["instruments"][diststats.instrument_categories.category(ifos),]
211 
212 ranking_data._compute_combined_rates()
213 ranking_data.finish()
214 
215 #
216 # record results in output file
217 #
218 
219 far.gen_likelihood_control_doc(xmldoc, process, diststats, ranking_data, segs, comment = u"background and signal priors (no real data)")
220 
221 #
222 # done
223 #
224 
225 ligolw_process.set_process_end_time(process)
226 
227 cachefile = open(options.write_likelihood_cache, "w")
228 
229 for n in range(options.num_banks):
230  this_name = "%04d_%s" % (n, options.write_likelihood_basename)
231  c = CacheEntry("".join(sorted(segs.keys())), "PRIORS", segs.extent_all(), this_name)
232  if n == 0:
233  ligolw_utils.write_filename(xmldoc, this_name, gz = this_name.endswith(".gz"), verbose = options.verbose)
234  original = this_name
235  else:
236  shutil.copyfile(original, this_name)
237  print >> cachefile, str(c)
238 
239 cachefile.close()
240