1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from __future__ import division
17
18 __prog__ = "followup_trigger.py"
19
20 import os
21 import sys
22 import copy
23 from math import sqrt, pi
24 import subprocess
25 import tempfile
26
27 import pylab
28 import numpy
29
30 from pylal import SnglInspiralUtils
31 from pylal import InspiralUtils
32 from pylal import SimInspiralUtils
33 from pylal import CoincInspiralUtils
34 from pylal import SearchSummaryUtils
35 from pylal import git_version
36 from pylal import grbsummary
37 from pylal import viz
38 from pylal import tools
39 from glue import lal
40 from glue import markup
41 from glue import pipeline
42 from glue import segments
43 from glue import segmentsUtils
44 from glue import iterutils
45 from glue.markup import oneliner as extra
46 from glue.ligolw import table
47 from glue.ligolw import lsctables
48 from glue.ligolw import utils
49 from glue.ligolw import ligolw
50 from glue.ligolw.utils import ligolw_add
51
52
53
54 -class ContentHandler(ligolw.LIGOLWContentHandler):
56 lsctables.use_in(ContentHandler)
57
58
59
61 """
62 This defines a class for following up a trigger and to create
63 the timeseries of found triggers in each stage of the pipeline.
64
65 Usage:
66
67 # first need to initialize the class
68 followup = followup_trigger.FollowupTrigger(cache, opts)
69
70 # later, one can do a followup of different objects, like an injection,
71 # a coinc. trigger or a single trigger:
72 htmlname1 = followup.from_coinc(coinc)
73 htmlname2 = followup.from_sngl(sngl_inspiral)
74 htmlname3 = followup.from_missed(missed_inj)
75 htmlname4 = followup.from_found(found_inj)
76 htmlname5 = followup.from_new_coinc(new_coinc,[sngls])
77 htmlname6 = followup.from_new_slide_coinc(new_coinc,[sngls],slideDict,segs)
78
79 # In each case the path to the created html file is returned.
80 # In the first call a CoincInspirals table is expected, a SnglInspiral
81 # table in the second case and a SimInspiral table in the two last.
82 """
83
84
85
86 - def __init__(self, cache, opts, use_injections = True,do_slides=False):
87 """
88 Initialize this class and sets up all the cache files.
89 @param cache: The cache of all files
90 @param opts: The 'opts' structure from the main code
91 @param use_injections: Specifying if injections are being used
92 (if no injections are used and this is set to
93 False, it speeds up the whole initalization...)
94 """
95 pylab.rcParams.update({'text.usetex': False})
96
97
98 if not hasattr(opts, 'followup_exttrig'):
99
100 opts.followup_exttrig = False
101 if not hasattr(opts, 'followup_time_window'):
102
103 opts.followup_time_window = 10.0
104 if not hasattr(opts, 'followup_tag'):
105
106 opts.followup_tag = None
107 if not hasattr(opts, 'followup_sned'):
108
109
110 opts.followup_sned = None
111
112 option_list = ['verbose','followup_exttrig','output_path',\
113 'followup_time_window','prefix',\
114 'suffix','figure_resolution','user_tag',\
115 'followup_tag','followup_sned']
116 for option in option_list:
117 if not hasattr(opts, option):
118 raise "Error: The following parameter is required in the "\
119 "opts structure for followup_trigger: ", option
120
121
122
123 self.colors = {'H1':'r','H2':'b','L1':'g','V1':'m','G1':'c'}
124 self.stageLabels = [('MATCHEDFILTER',['INSPIRAL'])]
125 if do_slides:
126 self.stageLabels.append(('COINCIDENCE-SLID',['THINCA_1','THINCA_SLIDES']))
127 else:
128 self.stageLabels.append(('COINCIDENCE',['THINCA_0','THINCA_ZEROLAG']))
129
130 self.orderLabels = ['MATCHEDFILTER']
131 if do_slides:
132 self.orderLabels.extend( [ 'COINCIDENCE-SLID_CAT_1',\
133 'COINCIDENCE-SLID_CAT_2', 'COINCIDENCE-SLID_CAT_3',\
134 'COINCIDENCE-SLID_CAT_4', 'COINCIDENCE-SLID_CAT_5'] )
135 else:
136 self.orderLabels.extend( [ 'COINCIDENCE_CAT_1','COINCIDENCE_CAT_2', \
137 'COINCIDENCE_CAT_3','COINCIDENCE_CAT_4', \
138 'COINCIDENCE_CAT_5'] )
139
140
141 self.opts = opts
142 self.tag = opts.user_tag
143 self.verbose = opts.verbose
144 self.exttrig = opts.followup_exttrig
145 self.output_path = opts.output_path
146 self.time_window = opts.followup_time_window
147 self.sned = opts.followup_sned
148
149
150
151
152 if not hasattr(opts, "old_document"):
153 self.old_document = False
154 else:
155 self.old_document = opts.old_document
156
157
158 if opts.followup_tag is None:
159 if opts.verbose:
160 print "WARNING: All injection files are considered. Is that desired?"
161 print " Or do you want to select an injection run with --followup-tag INJRUN ?"
162 self.cache = cache
163 else:
164 if opts.verbose:
165 print "WARNING: Only a selected injection run is taken into account: ", opts.followup_tag
166 print " Is that the desired behaviour? You might reconsider removing --followup-tag INJRUN "
167 self.cache = cache.sieve(description = opts.followup_tag)
168
169
170
171 self.get_injection_window()
172
173
174 self.fname_list = []
175
176
177 self.number = 0
178
179
180 self.followup_time = None
181 self.injection_id = None
182 self.flag_followup = False
183
184
185
186
187 self.followup_dict = {'inj':lsctables.New(lsctables.SimInspiralTable), 'dist':[], 'type':[]}
188 self.vetoed_injections = lsctables.New(lsctables.SimInspiralTable)
189
190 if self.verbose:
191 print "\nStarting initializing the Followup class..."
192
193
194 self.trigger_cache = {}
195 for stageName, stagePatterns in self.stageLabels:
196 sievedFiles = []
197 for pattern in stagePatterns:
198 sievedFiles.extend(self.cache.sieve(description=pattern))
199 self.trigger_cache[stageName] = sievedFiles
200 if self.opts.verbose:
201 print "%d files found for stage %s" %\
202 (len(self.trigger_cache[stageName]), stageName)
203
204
205
206 self.injections = dict()
207 if use_injections:
208 if self.verbose:
209 print "Creating the injection cache..."
210
211 self.injection_cache = self.cache.sieve(description = "INJECTION").\
212 sieve(ifos='HL')
213
214 for file, entry in zip(self.injection_cache.pfnlist(), \
215 self.injection_cache):
216 injection_id = self.get_injection_id(cache_entry = entry)
217 self.injections[injection_id] = SimInspiralUtils.\
218 ReadSimInspiralFromFiles( \
219 [file], verbose=False )
220
221
222 self.read_veto_files()
223
224 if self.verbose:
225 print "Initializing the Followup class done..."
226
227
229 """
230 Sets the sned flag to 'flag'
231 @param flag: the executable used to calculate the sned
232 """
233 self.sned = executable
234
235
237 """
238 Makes an external call to lalapps_sned to recalculate
239 the effective distances
240 @param injections: the list of injections to be converted
241 @return: the recalculated injection
242 """
243
244 def fix_numrel_columns(sims):
245 """
246 Due to a mismatch between our LAL tag and the LAL HEAD, against which
247 we compile pylal, there are missing columns that cannot be missing.
248 Just put in nonsense values so that glue.ligolw code won't barf.
249 """
250 for sim in sims:
251 sim.numrel_data="nan"
252 sim.numrel_mode_max = 0
253 sim.numrel_mode_min = 0
254
255 file1 = '.followup_trigger.output.xml'
256 file2 = '.followup_trigger.input.xml'
257
258
259 fix_numrel_columns(injections)
260 grbsummary.write_rows(injections, lsctables.SimInspiralTable, file1)
261
262
263
264 command = self.sned+' --f-lower 40.0 --inj-file '+file1+\
265 ' --output '+file2
266 if self.verbose:
267 print "Executing command '%s'" % command
268 subprocess.call(command.split())
269
270
271 doc = ligolw_add.ligolw_add(ligolw.Document(), [file2])
272
273 return lsctables.SimInspiralTable.get_table(doc)
274
275
277 """
278 Setting a tag (non-conformal naming because of backwards compatibality)
279 @param tag: the tag to be set
280 """
281 self.set_tag(tag)
282
284 """
285 Setting a tag
286 @param tag: well, its the tag!
287 """
288 self.tag = tag
289
290
291
293 """
294 Extracting the length of the used injection_window from
295 any 'FOUND' file in the cache.
296 """
297
298
299
300
301
302
303 found_cache = self.cache.sieve(description = "FOUND")
304 if len(found_cache)==0:
305 self.injection_window = 0.1
306 print "INFO: No FOUND files found, so setting the injection window"\
307 " to default value."
308 return
309
310 coire_file = found_cache.checkfilesexist()[0].pfnlist()[0]
311 try:
312 doc = SearchSummaryUtils.ReadTablesFromFiles([coire_file],\
313 [lsctables.ProcessParamsTable])
314 process_params = lsctables.ProcessParamsTable.get_table(doc)
315 except IOError:
316 sys.stderr.write("ERROR (IOError) while reading process_params table from"\
317 " file %s. Does this file exist and does it contain"\
318 " a search_summary table?\n" %(coire_file))
319 raise
320 except:
321 raise "Error while reading process_params table from file: ", coire_file
322
323
324 found_flag = False
325 for tab in process_params:
326 if tab.param=='--injection-window':
327 found_flag = True
328 self.injection_window = float(tab.value)/1000.0
329 if not found_flag:
330 sys.stderr.write("WARNING: No entry '--injection-window' found in file %s"\
331 "Value used is %.1f ms. If incorrect, please change file at %s\n" %\
332 (coire_file, 1000.0*self.injection_window, __file__))
333
334
335 if self.verbose:
336 print "Injection-window set to %.0f ms" % (1000*self.injection_window)
337
338
339
341 """
342 Extracting the injection ID from the filename, using
343 the mechanism as used in lalapps_path2cache. You can specify the filename
344 itself, the url or the cache entry. You must not specify more than one input!
345 The injection-ID is calculated in the following way (exttrig only):
346
347 The code expects the INSPIRAL and THINCA files in the following scheme (example):
348 PREFIX-TAGPART_injections32_77-GPS-DURATION.xml
349 The number of the injection run is extracted (INJRUN) as well as the following
350 number (INJNUMBER). The injection ID is then calculated as:
351 INJID = 100000*INJRUN + INJNUMBER
352 so for this example the injectionj ID is 3200077.
353
354 @param filename: filename from which the injection ID is extracted
355 @param url: url from which the injection ID is extracted
356 @param cache_entry: cache entry from which the injection ID is extracted
357 """
358
359
360 if cache_entry:
361 if filename and url:
362 raise "Error in function get_injection_id: Only one input should be "\
363 "specified. Now 'cache_entry' and another variable is specified. Check the code."
364
365 if cache_entry is None:
366 if filename and url:
367 raise "Error in function get_injection_id: Only one input should be "\
368 "specified. Now 'filename' and 'url' is specified. Check the code."
369
370 if filename:
371 path, filename = os.path.split(filename.strip())
372 url = "file://localhost%s" % os.path.abspath(os.path.join(path, filename))
373
374 try:
375 cache_entry = lal.CacheEntry.from_T050017(url)
376 except ValueError, e:
377 raise "Error while extracting injection ID from file ", filename
378
379
380 pieces = cache_entry.description.split('_')
381 if self.exttrig:
382
383
384 injection_id = pieces[-2]+'_'+pieces[-1]
385 else:
386
387
388 index = 0
389 for ind, piece in enumerate(pieces):
390 if 'CAT' in piece:
391 index = ind
392 injection_id = pieces[index-1]
393
394 return injection_id
395
397 """
398 The above relies on a very specific naming convention, here we check if
399 the injection tag is present in the files' description.
400 """
401 if tag in cache_entry.description:
402 return True
403 else:
404 return False
405
406
408 """
409 Find the injection-ID corresponding to this particular injection.
410 @param injection: the injection object
411 @return: the injection ID
412 """
413
414 if self.injections:
415
416
417 for injection_id, group_inj in self.injections.iteritems():
418 for inj in group_inj:
419 if injection.geocent_end_time==inj.geocent_end_time and \
420 injection.geocent_end_time_ns==inj.geocent_end_time_ns:
421 return injection_id
422
423 raise "No injection ID found for the above particular missed Injection "
424
425 else:
426
427
428 if self.verbose:
429 print "INFO: Searching for the injection at time %d.%d" % \
430 (injection.geocent_end_time, injection.geocent_end_time_ns)
431 injection_cache = self.cache.sieve(description = "INJECTION").\
432 sieve(ifos='HL')
433
434 for file, entry in zip(injection_cache.pfnlist(), injection_cache):
435 injection_id = self.get_injection_id(cache_entry = entry)
436 sims = SimInspiralUtils.ReadSimInspiralFromFiles( [file], verbose=False )
437
438 for sim in sims:
439
440 if injection.geocent_end_time==sim.geocent_end_time and \
441 injection.geocent_end_time_ns==sim.geocent_end_time_ns:
442 if self.verbose:
443 print "... found it: injection_id = ", injection_id
444 return injection_id
445
446
447 return None
448
449
451 """
452 Reads the veto segments given by veto-files (if any)
453 """
454 self.vetodict = dict()
455
456
457 for ifoName in self.colors.keys():
458
459 self.vetodict[ifoName]=None
460
461 attributeName = 'followup_vetofile_'+ifoName.lower()
462 if hasattr( self.opts, attributeName):
463
464
465 filename = getattr( self.opts, attributeName )
466 if filename:
467 self.vetodict[ifoName] = segmentsUtils.fromsegwizard(open(filename))
468
469
471 """
472 Resets the counting number for the time-series plots generated.
473 """
474 self.number=0
475
476
478 """
479 Print some useful informations to the screen.
480 @param inj: the current missed injection
481 @param injID: the injection ID (used for exttrig only)
482 """
483
484 if self.exttrig:
485 print "\nInjection details for injection %d with injID %s: " %\
486 (self.number, injID)
487 else:
488 print "\nInjection details for injection %d:" % (self.number)
489
490 print "m1: %.2f m2:%.2f | end_time: %d.%d | "\
491 "distance: %.2f eff_dist_h: %.2f eff_dist_l: %.2f" % \
492 ( inj.mass1, inj.mass2, inj.geocent_end_time, inj.geocent_end_time_ns,\
493 inj.distance, inj.eff_dist_h, inj.eff_dist_l )
494
495
497 """
498 Saves the plots and store them in a seperate fname_list.
499 @param stage: the stage this plot belongs to (e.g. INSPIRAL, THINCA,...)
500 """
501 fname = 'Images/'+self.opts.prefix + "_"+self.tag+"_map-"+\
502 stage+"-"+str(self.number) +self.opts.suffix+'.png'
503 fname_thumb = InspiralUtils.\
504 savefig_pylal( filename = self.output_path+fname,\
505 doThumb = True,
506 dpi_thumb = self.opts.figure_resolution)
507
508 self.fname_list.append( fname )
509 return fname
510
511
512
514 """
515 This is a helper function to return a GPS time as one float number
516 @param trig: a sngl_inspiral table entry
517 """
518 return float(trig.end_time) + float(trig.end_time_ns) * 1.0e-9
519
520
521
523 if trig.chisq>0:
524 return trig.get_effective_snr(fac=fac)
525 else:
526 return 0.0
527
528
531
532
534 """
535 This is a helper function to return a GPS time as one float number
536 for a certain IFO. If no IFO is specified the injected geocentric
537 time is returned.
538 @param sim: a sim_inspiral table entry
539 @param ifo: the IFO for which we want the sim time
540 """
541
542 time=0
543 nano=0
544
545 if not ifo:
546 time = sim.geocent_end_time
547 nano = sim.geocent_end_time_ns
548 if ifo:
549 time = getattr(sim, ifo[0].lower()+'_end_time' )
550 nano = getattr(sim, ifo[0].lower()+'_end_time_ns' )
551
552 return float(time) + float(nano) * 1.0e-9
553
554
555
556 - def is_veto(self, time_trigger, ifo):
557 """
558 This function checks if there is a veto at the time 'timeTrigger'
559 for the IFO 'ifo'.
560 @param time_trigger: The time to be investigated
561 @param ifo: The name of the IFO to be investigated
562 """
563 if self.vetodict[ifo] is None:
564 return False
565
566 return iterutils.any(time_trigger in seg for seg in self.vetodict[ifo])
567
568
569 - def put_text(self, text):
570 """
571 Puts some text into an otherwise empty plot.
572 @param text: text to put in the empty plot
573 """
574
575 newText = ''
576 for i in range( int(len(text)/60.0)+1):
577 newText+=text[60*i:60*i+60]+'\n'
578 pylab.figtext(0.15,0.15, newText)
579
580
583 """
584 Investigate inspiral triggers and create a time-series
585 of the SNRs around the injected time
586 @param trigger_files: List of files containing the inspiral triggers
587 @param stage: the name of the stage (INSPIRAL_FIRST, THINCA_0_CAT_2)
588 @param number: the consecutive number for this inspiral followup
589 @param slideDict: A dictionary of ifo keyed slide times if using slides
590 """
591
592 seg_small = segments.segment(self.followup_time - self.injection_window, \
593 self.followup_time + self.injection_window)
594 seg_large = segments.segment(self.followup_time - self.time_window, \
595 self.followup_time + self.time_window)
596 if abs(seg_small) > abs(seg_large):
597 err_msg = "Injection window must be smaller than time_window."
598 err_msg = "Got injection window = %f and time window = %g." \
599 %(self.injection_window,self.time_window)
600 raise ValueError(err_msg)
601
602
603 if self.verbose:
604 print "Processing INSPIRAL triggers from files ", trigger_files
605
606
607
608 sngls = lsctables.New(lsctables.SnglInspiralTable, \
609 columns=lsctables.SnglInspiralTable.loadcolumns)
610
611 for file in trigger_files:
612 xmldoc = utils.load_filename(file, verbose=self.verbose,
613 contenthandler=ContentHandler)
614 try:
615 sngl_table = lsctables.SnglInspiralTable.get_table(xmldoc)
616 except ValueError:
617 xmldoc.unlink()
618 continue
619 if slideDict:
620 for event in sngl_table:
621 event.set_end( event.get_end() + slideDict[event.ifo] )
622
623 sngl_table = sngl_table.vetoed(seg_large)
624
625
626 if sngl_table:
627 sngls.extend(sngl_table)
628
629 xmldoc.unlink()
630
631
632 fig=pylab.figure()
633 foundSet = set()
634 loudest_details = {}
635 no_triggers_found = True
636
637 if len(sngls) == 0:
638 self.put_text( 'No triggers/coincidences found within time window')
639 else:
640
641 for ifo in self.colors.keys():
642
643 sngls_ifo = sngls.ifocut(ifo)
644
645
646 selected_large = sngls_ifo
647 time_large = [ float(sel.get_end()) - self.followup_time \
648 for sel in selected_large ]
649 selected_small = sngls_ifo.vetoed(seg_small)
650 time_small = [ float(sel.get_end()) - self.followup_time \
651 for sel in selected_small ]
652
653
654 if len(time_large) == 0:
655 continue
656 no_triggers_found = False
657
658
659 if len(time_small)>0:
660 foundSet.add(ifo)
661
662
663 loudest = selected_small[selected_small.get_column('snr').argmax()]
664 loudest_details[ifo] = {}
665 loudest_details[ifo]["snr"] = loudest.snr
666 loudest_details[ifo]["mchirp"] = loudest.mchirp
667 loudest_details[ifo]["eta"] = loudest.eta
668 loudest_details[ifo]["eff_dist"] = loudest.eff_distance
669 loudest_details[ifo]["rchisq"] = 0
670 loudest_details[ifo]["bank_rchisq"] = 0
671 loudest_details[ifo]["auto_rchisq"] = 0
672 if loudest.chisq_dof:
673 loudest_details[ifo]["rchisq"] = loudest.chisq/(2*loudest.chisq_dof-2)
674 if loudest.bank_chisq_dof:
675 loudest_details[ifo]["bank_rchisq"] = loudest.bank_chisq/loudest.bank_chisq_dof
676 if loudest.cont_chisq_dof:
677 loudest_details[ifo]["auto_rchisq"] = loudest.cont_chisq/loudest.cont_chisq_dof
678 loudest_details[ifo]["timeTrigger"] = float(loudest.get_end())
679 loudest_details[ifo]["eff_snr"] = self.get_effective_snr(loudest)
680 loudest_details[ifo]["new_snr"] = self.get_new_snr(loudest)
681 loudest_details[ifo]["end_time"] = loudest.end_time+loudest.end_time_ns*1E-9
682 loudest_details[ifo]["trig"] = loudest
683
684
685 pylab.plot( time_large, selected_large.get_column('snr'),\
686 self.colors[ifo]+'o', label="_nolegend_")
687 pylab.plot( time_small, selected_small.get_column('snr'), \
688 self.colors[ifo]+'s', label=ifo)
689
690
691
692
693
694
695 if no_triggers_found:
696 self.put_text( 'No triggers/coincidences found within time window')
697
698 ylims=pylab.axes().get_ylim()
699 pylab.plot([0,0], ylims, 'g--', label="_nolegend_")
700 pylab.plot([-self.injection_window, -self.injection_window], ylims, 'c:',\
701 label="_nolegend_")
702 pylab.plot([+self.injection_window, +self.injection_window], ylims, 'c:',\
703 label="_nolegend_")
704
705
706 pylab.grid(True)
707 pylab.legend()
708
709 ylims=pylab.axes().get_ylim()
710 pylab.axis([-self.time_window, +self.time_window, ylims[0], ylims[1]])
711 pylab.xlabel('time [s]')
712 pylab.ylabel('SNR')
713 pylab.title(stage+'_'+str(self.number))
714 fname = self.save_plot( stage )
715 pylab.close(fig)
716
717 result = {'filename':fname, 'foundset':foundSet, 'loudest_details':loudest_details}
718 return result
719
720
721
723 """
724 Return a trigger list that contains only files for the choosen category.
725 @param trigger_files : a list of trigger file names
726 @param category: a category number
727 @return: a sub list of filename corresponding to the category requested
728 """
729
730
731
732 cat1 = 'CAT_'+str(category)
733 cat2 = 'CATEGORY_'+str(category)
734
735 if category==1:
736
737
738
739 new_list = [file for file in trigger_files \
740 if 'CAT' not in file or cat1 in file or cat2 in file]
741
742 else:
743 cat = 'CAT_'+str(category)
744 new_list = [file for file in trigger_files if cat1 in file\
745 or cat2 in file]
746
747 return new_list
748
749
750 - def fill_table(self, page, contents,header=False,no_wrapping=False):
751 """
752 Fills contents in a html table
753 @param page: the page object describing a html page
754 @contents: the contents of the next table item
755 """
756
757 page.add('<tr>')
758 for content in contents:
759 if header:
760 tmpString = '<th'
761 else:
762 tmpString = '<td'
763 if no_wrapping:
764 tmpString += ' style="white-space: nowrap;"'
765 tmpString += '>'
766 page.add(tmpString)
767 page.add( str(content) )
768 if header:
769 page.add('</th>')
770 else:
771 page.add('</td>')
772 page.add('</tr>')
773
774
775
777 """
778 Creates the first table containing basic properties
779 of the injection which is followed up.
780 @param inj: an injection table
781 """
782
783 if self.sned:
784 inj_sned = self.execute_sned([inj])[0]
785
786
787
788 page = markup.page()
789 page.h1("Followup injection #"+str(self.number))
790 page.add('<table border="2" >')
791 page.add('<caption><b> Injection parameters </b> </caption>')
792 self.fill_table( page, ['<b>parameter','<b>value'] )
793 self.fill_table( page, ['Number', self.number] )
794 self.fill_table( page, ['inj ID', self.injection_id] )
795 self.fill_table( page, ['mass1', '%.2f' % inj.mass1] )
796 self.fill_table( page, ['mass2', '%.2f' % inj.mass2] )
797 self.fill_table( page, ['mtotal', '%.2f' % (inj.mass1+inj.mass2)] )
798 self.fill_table( page, ['mchirp', '%.2f' % (inj.mchirp)] )
799 self.fill_table( page, ['eta', '%.2f' % (inj.eta)] )
800 self.fill_table( page, ['spin1z', '%.2f' % (inj.spin1z) ] )
801 self.fill_table( page, ['spin2z', '%.2f' % (inj.spin2z) ] )
802 self.fill_table( page, ['end_time', '%010d' % inj.geocent_end_time] )
803 self.fill_table( page, ['end_time_ns', '%09d' % inj.geocent_end_time_ns] )
804 self.fill_table( page, ['distance', '%.1f' % inj.distance] )
805 for ifo_id in ['h','l','v','g']:
806 if self.sned:
807 self.fill_table( page, ['eff_dist_%s' % ifo_id, '%5.1f / %5.1f' % \
808 (eval("inj.eff_dist_%s" % ifo_id), eval("inj_sned.eff_dist_%s" % ifo_id))] )
809 else:
810 self.fill_table( page, ['eff_dist_%s' % ifo_id, '%5.1f' % eval("inj.eff_dist_%s" % ifo_id)] )
811 self.fill_table( page, ['playground','%s' % pipeline.s2play(inj.geocent_end_time)] )
812 page.add('</table></td>')
813
814 return page
815
816
818 """
819 Creates the first table containing basic properties
820 of the trigger which is followed up.
821 @param trig: an sngl_inspiral table
822 """
823
824
825 page = markup.page()
826 page.h1("Followup trigger #"+str(self.number))
827 page.add('<table border="2" >')
828 self.fill_table(page, ['<b>parameter','<b>value'] )
829 self.fill_table(page, ['Number', self.number] )
830 self.fill_table(page, ['inj ID', self.injection_id] )
831 self.fill_table(page, ['mass1', '%.2f'% trig.mass1] )
832 self.fill_table(page, ['mass2', '%.2f'% trig.mass2] )
833 self.fill_table(page, ['mtotal', '%.2f' % (trig.mass1+trig.mass2)] )
834 self.fill_table(page, ['mchirp', '%.2f' % (trig.mchirp)] )
835 self.fill_table(page, ['end_time', '%010d' % trig.end_time] )
836 self.fill_table(page, ['end_time_ns', '%09d' % trig.end_time_ns] )
837 self.fill_table(page, ['snr', trig.snr])
838 if trig.chisq_dof:
839 self.fill_table(page, ['rchisq', trig.chisq/(2*trig.chisq_dof-2)])
840 if trig.bank_chisq_dof:
841 self.fill_table(page, ['bank_rchisq', trig.bank_chisq/trig.bank_chisq_dof])
842 if trig.cont_chisq_dof:
843 self.fill_table(page, ['auto_rchisq', trig.cont_chisq/trig.cont_chisq_dof])
844 self.fill_table(page, ['eff_snr (fac=50)', self.get_effective_snr(trig)])
845 self.fill_table(page, ['new_snr', self.get_new_snr(trig)])
846 self.fill_table(page, ['eff_distance', '%.1f' % trig.eff_distance] )
847 page.add('</table></td><br>')
848 page.hr()
849
850 return page
851
852
855 """
856 Creates the first table containing basic properties
857 of the coincidence which is followed up.
858 @param coinc: an CoincInspiral table
859 """
860
861 if slideDict:
862 timeSlide = True
863 else:
864 timeSlide = False
865
866
867 if not page:
868 page = markup.page()
869 page.h1("Followup trigger #"+str(self.number))
870 page.add('<table border="2">')
871
872 page.add('<caption><b>Coincidence Information</b></caption>')
873 if not snglInspirals:
874 self.fill_table( page, ['Statistic: ', coinc.stat] )
875 else:
876 self.fill_table( page, ['Combined FAR: ', coinc.combined_far] )
877 self.fill_table( page, ['Uncombined FAR: ', coinc.false_alarm_rate] )
878 for i in range(len(snglInspirals)):
879 for j in range(i+1,len(snglInspirals)):
880 sngl1 = snglInspirals[i]
881 sngl2 = snglInspirals[j]
882 ifo1 = sngl1.ifo
883 ifo2 = sngl2.ifo
884 try:
885 ethinca = tools.XLALCalculateEThincaParameter(sngl1,sngl2)
886 except:
887 ethinca = 'Not coincident'
888 Name = 'Ethinca distance between ' + ifo1 + ' and ' + ifo2
889 self.fill_table( page, [Name + ': ', ethinca])
890
891 page.add('</table><br>')
892
893 page.add('<table border="2" >')
894 page.add('<caption><b>Individual IFO Information</b></caption>')
895 for ifo in ['H1','H2','L1','V1','G1']:
896 trig = None
897 if snglInspirals:
898 for sngl in snglInspirals:
899 if sngl.ifo == ifo:
900 trig = sngl
901 if timeSlide:
902 trig2 = copy.deepcopy(trig)
903 trig2.set_end(trig2.get_end() - slideDict[trig2.ifo])
904 elif hasattr(coinc,ifo):
905 trig = getattr(coinc,ifo)
906
907 if trig:
908 page.add('<td><table border="2" >')
909
910 self.fill_table( page, ['parameter', ifo], header=True )
911 self.fill_table( page, ['Number', self.number] )
912 self.fill_table( page, ['inj ID', self.injection_id] )
913 self.fill_table( page, ['SNR', '%.3f' % trig.snr] )
914 self.fill_table( page, ['Effective SNR (fac=50)', '%.3f' % self.get_effective_snr(trig,fac=50.)] )
915 self.fill_table( page, ['New SNR', '%.3f' % self.get_new_snr(trig,index=6.)] )
916 if trig.chisq_dof:
917 self.fill_table( page, ['Chisq/dof', '%.3f' % (trig.chisq/(2*trig.chisq_dof-2))] )
918 if trig.bank_chisq_dof:
919 self.fill_table( page, ['Bank chisq/dof', '%.3f' % (trig.bank_chisq/trig.bank_chisq_dof)] )
920 if trig.cont_chisq_dof:
921 self.fill_table( page, ['Auto chisq/dof', '%.3f' % (trig.cont_chisq/trig.cont_chisq_dof)] )
922 self.fill_table( page, ['Rsq duration (s)', '%.4f' % trig.rsqveto_duration] )
923 self.fill_table( page, ['''Mass1 (M<sub>⨀</sub>)''', '%.2f' % trig.mass1] )
924 self.fill_table( page, ['''Mass2 (M<sub>⨀</sub>)''', '%.2f' % trig.mass2] )
925 self.fill_table( page, ['''Mtotal (M<sub>⨀</sub>)''', '%.2f' % (trig.mass1+trig.mass2)] )
926 self.fill_table( page, ['''Mchirp (M<sub>⨀</sub>)''', '%.3f' % trig.mchirp] )
927 self.fill_table( page, ['Template duration (s)', '%.3f' % trig.template_duration ] )
928 if timeSlide:
929 endTime = trig.end_time + 1E-9*trig.end_time_ns
930 self.fill_table( page, ['Slid GPS end time', '%.4f' % endTime] )
931 slidEndTime = trig2.end_time + 1E-9*trig2.end_time_ns
932 self.fill_table( page, ['Unslid end time', '%.4f' % slidEndTime] )
933 else:
934 endTime = trig.end_time + 1E-9*trig.end_time_ns
935 self.fill_table( page, ['GPS end time', '%.3f' % endTime] )
936 self.fill_table( page, ['Effective distance (Mpc)', '%.1f' % trig.eff_distance] )
937 page.add('</table></td>')
938
939 page.add('</table><br>')
940 page.hr()
941
942 return page
943
944
946 """
947 Creates the first table containing the time
948 of the followup
949 @param trigger_time: well, just the trigger time
950 """
951
952
953 page = markup.page()
954 page.h1("Followup time around GPS "+str(trigger_time) )
955 page.add('<table border="2" >')
956 self.fill_table( page, ['Number', self.number] )
957 self.fill_table( page, ['Time', trigger_time] )
958 page.add('</table></td><br>')
959 page.hr()
960
961 return page
962
963
964
966 """
967 Adds the table containing specific information on the loudest
968 trigger found in the followup region for each IFO.
969 @param page: the html page to which to add the table
970 @param invest_dict: dictionary containing the stage results
971 """
972
973
974 page.add('<table border="2" >')
975 page.add('<caption><b> Parameters of the loudest (by SNR) recovered single ifo triggers at each stage of the pipeline </b> </caption>')
976 self.fill_table( page, ['step', 'F/M', 'SNR', \
977 'Mchirp', 'eta', 'eff_dist', \
978 'rchisq', 'bank_rchisq', 'auto_rchisq', 'eff_snr',\
979 'new_snr', 'end_time', 'ethinca', 'Veto ON/OFF'], header=True )
980
981
982
983 for stage in self.orderLabels:
984 if stage in invest_dict:
985 result = invest_dict[stage]
986
987
988 found_ifo = ''
989 loudest_snr = ''
990 loudest_mchirp = ''
991 loudest_eta = ''
992 loudest_eff_dist = ''
993 loudest_rchisq = ''
994 loudest_bank_rchisq = ''
995 loudest_auto_rchisq = ''
996 loudest_effsnr = ''
997 loudest_newsnr = ''
998 loudest_ethinca = ' '
999 loudest_time = ' '
1000 veto_onoff = ''
1001
1002
1003 result['foundlist'] = list(result['foundset'])
1004 for i in range(len(result['foundlist'])):
1005 ifo = (result['foundlist'])[i]
1006 found_ifo += ifo+' '
1007
1008
1009
1010 loudest_snr += "%s : %.3f <br>" % \
1011 (ifo, result['loudest_details'][ifo]['snr'])
1012 loudest_mchirp += "%s : %.3f <br>" % \
1013 (ifo, result['loudest_details'][ifo]['mchirp'])
1014 loudest_eta += "%s : %.3f <br>" % \
1015 (ifo, result['loudest_details'][ifo]['eta'])
1016 loudest_eff_dist += "%s : %.3f <br>" % \
1017 (ifo, result['loudest_details'][ifo]['eff_dist'])
1018 loudest_rchisq += "%s : %.3f <br>" % \
1019 (ifo, result['loudest_details'][ifo]['rchisq'])
1020 loudest_bank_rchisq += "%s : %.3f <br>" % \
1021 (ifo, result['loudest_details'][ifo]['bank_rchisq'])
1022 loudest_auto_rchisq += "%s : %.3f <br>" % \
1023 (ifo, result['loudest_details'][ifo]['auto_rchisq'])
1024 loudest_effsnr += "%s : %.3f <br>" % \
1025 (ifo, result['loudest_details'][ifo]['eff_snr'])
1026 loudest_newsnr += "%s : %.3f <br>" % \
1027 (ifo, result['loudest_details'][ifo]['new_snr'])
1028 loudest_time += "%s : %.3f <br>" % \
1029 (ifo, result['loudest_details'][ifo]['end_time'])
1030 for j in range(i+1,len(result['foundlist'])):
1031 ifo2 = (result['foundlist'])[j]
1032 try:
1033 ethinca = tools.XLALCalculateEThincaParameter(
1034 result['loudest_details'][ifo]['trig'],
1035 result['loudest_details'][ifo2]['trig'])
1036 loudest_ethinca += "%s and %s: %.3f <br>" % \
1037 (ifo,ifo2,ethinca)
1038 except:
1039 loudest_ethinca += "%s and %s: %s <br>" % \
1040 (ifo,ifo2,'Not coincident')
1041
1042
1043 time_trigger = float(result['loudest_details'][ifo]['timeTrigger'])
1044 if self.vetodict[ifo]:
1045 veto = self.is_veto(time_trigger, ifo)
1046 veto_txt = 'OFF'
1047 if veto:
1048 veto_txt = 'ON'
1049 veto_onoff+=ifo+': '+veto_txt+'<br>'
1050 else:
1051 veto_onoff+=ifo+': No info<br>'
1052
1053
1054 if len(result['foundset'])>0:
1055 self.fill_table( page, [ stage, 'FOUND in <br>'+found_ifo, \
1056 loudest_snr, \
1057 loudest_mchirp, \
1058 loudest_eta, \
1059 loudest_eff_dist,\
1060 loudest_rchisq, \
1061 loudest_bank_rchisq, \
1062 loudest_auto_rchisq, \
1063 loudest_effsnr, \
1064 loudest_newsnr, \
1065 loudest_time, \
1066 loudest_ethinca, \
1067 veto_onoff],no_wrapping=True)
1068 else:
1069 self.fill_table( page, [ stage, '<font color="red">MISSED'])
1070
1071 page.add('</table>')
1072 page.add('</td></tr></table><br><br>')
1073
1074 return page
1075
1076
1077
1078 - def from_coinc(self, coinc, ifo = None, more_infos = False, \
1079 injection_id = None,slideDict=None):
1080 """
1081 Creates a followup page from a coincident trigger.
1082 @param coinc: the coincidence to be followed up
1083 @param ifo: specifies the ifo to be used from the coinc.
1084 @param more_infos: to have some additional informations
1085 @param injection_id: Must be specified for exttrig search
1086 to specify what injection to use
1087 """
1088
1089 if not ifo:
1090 print "WARNING (not bad): No IFO specified, using data from the "\
1091 "first IFO in the coincidence "
1092 for ifo_name in self.colors.keys():
1093 if hasattr(coinc, ifo_name):
1094 ifo = ifo_name
1095 break
1096 sngl = getattr(coinc, ifo)
1097
1098
1099 self.followup_time = float(sngl.get_end())
1100
1101
1102 self.injection_id = injection_id
1103 page = self.create_table_coinc(coinc,slideDict=slideDict)
1104 self.flag_followup = more_infos
1105
1106
1107 return self.followup(page,slideDict=slideDict)
1108
1109
1110 - def from_new_coinc(self, coinc, sngls,\
1111 more_infos = False, injection_id = None):
1112 """
1113 Creates a followup page from a coincident trigger.
1114 @param coinc: the coincidence to be followed up
1115 @param ifo: specifies the ifo to be used from the coinc.
1116 @param more_infos: to have some additional informations
1117 @param injection_id: Must be specified for exttrig search
1118 to specify what injection to use
1119 """
1120
1121 sngl = sngls[0]
1122
1123
1124 self.followup_time = float(sngl.get_end())
1125
1126
1127 self.injection_id = injection_id
1128 page = self.create_table_coinc(coinc,snglInspirals= sngls)
1129 self.flag_followup = more_infos
1130
1131
1132 return self.followup(page)
1133
1134
1135 - def from_new_slide_coinc(self, coinc, sngls,slideDict,\
1136 more_infos = False, injection_id = None):
1137 """
1138 Creates a followup page from a slid coincident trigger. This function
1139 does not yet produce the plots (as I'm not sure how to!) but the
1140 relevant information to do this (the slide dictionary and the segment list)
1141 are provided to this function.
1142 @param coinc: the coincidence to be followed up
1143 @param ifo: specifies the ifo to be used from the coinc.
1144 @param more_infos: to have some additional informations
1145 @param injection_id: Must be specified for exttrig search
1146 to specify what injection to use
1147 """
1148
1149 sngl = sngls[0]
1150
1151
1152 self.followup_time = float(sngl.get_end())
1153
1154
1155 self.injection_id = injection_id
1156 page = self.create_table_coinc(coinc,snglInspirals= sngls,\
1157 slideDict=slideDict)
1158 self.flag_followup = more_infos
1159
1160 return self.followup(page,slideDict=slideDict)
1161
1162
1163 - def from_sngl(self, sngl, ifo = None, more_infos = False, \
1164 injection_id = None):
1165 """
1166 Creates a followup page from a single trigger.
1167 @param sngl: the sngl trigger to be followed up
1168 @param ifo: NOT USED
1169 @param more_infos: to have some additional informations
1170 @param injection_id: Must be specified for exttrig search
1171 to specify what injection to use
1172 """
1173
1174
1175 self.injection_id = injection_id
1176 page = self.create_table_sngl(sngl)
1177 self.flag_followup = more_infos
1178
1179
1180 self.followup_time = float(sngl.get_end())
1181
1182
1183 return self.followup(page)
1184
1185
1186 - def from_missed(self, missed, ifo = None, more_infos = True, \
1187 injection_id = None):
1188 """
1189 Creates a followup page from a missed injection.
1190 @param sngl: the missed injection to be followed up
1191 @param ifo: The ifo whose time is used (geocent if None)
1192 @param more_infos: to have some additional informations
1193 @param injection_id: Must be specified for exttrig search
1194 to specify what injection to use
1195 """
1196
1197 return self.from_injection(missed, ifo = ifo, more_infos = more_infos,\
1198 injection_id = injection_id )
1199
1200
1201 - def from_found(self, found, ifo = None, more_infos = False, \
1202 injection_id = None,coinc = None, sngls = None):
1203 """
1204 Creates a followup page from a found injection.
1205 @param sngl: the found injection to be followed up
1206 @param ifo: The ifo whose time is used (geocent if None)
1207 @param more_infos: to have some additional informations
1208 @param injection_id: Must be specified for exttrig search
1209 to specify what injection to use
1210 """
1211
1212 return self.from_injection(found, ifo = ifo, more_infos = more_infos, \
1213 injection_id = injection_id,coinc=coinc, \
1214 sngls = sngls )
1215
1216
1217 - def from_injection(self, injection, ifo = None, more_infos = True, \
1218 injection_id = None,coinc = None, sngls = None):
1219 """
1220 Creates a followup page from an injection.
1221 @param injection: the injection to be followed up
1222 @param ifo: The ifo whose time is used (geocent if None)
1223 @param more_infos: to have some additional informations
1224 @param injection_id: Must be specified for exttrig search
1225 to specify what injection to use
1226 """
1227
1228
1229 if injection_id:
1230 self.injection_id = injection_id
1231 else:
1232 self.injection_id = self.find_injection_id(injection)
1233
1234
1235 page = self.create_table_inj(injection)
1236
1237 if coinc and sngls:
1238 page = self.create_table_coinc(coinc,snglInspirals= sngls,page=page)
1239
1240 self.flag_followup = more_infos
1241
1242
1243 self.followup_time = self.get_sim_time(injection, ifo)
1244
1245
1246 return self.followup(page)
1247
1248
1249
1250 - def from_time(self, trigger_time, ifo = None, more_infos = False, \
1251 injection_id = None):
1252 """
1253 Creates a followup page from a given time.
1254 @param trigger_time: the time to be followed up
1255 @param ifo: NOT USED
1256 @param injection_id: Must be specified for exttrig search
1257 to specify what injection to use
1258 """
1259
1260 self.flag_followup = more_infos
1261
1262
1263 page = self.create_table_time(trigger_time)
1264
1265
1266 self.followup_time = trigger_time
1267 self.injection_id = injection_id
1268
1269
1270 return self.followup(page)
1271
1272
1273
1274 - def followup(self, page,slideDict = None):
1275 """
1276 Central followup procedure, finding corresponding files,
1277 generating the time-series and creating the output html files
1278 @param page: The head of the html page created with different informations
1279 @param slideDict: A dictionary of ifo keyed slide times if using slides
1280 @return: filename of the created html
1281 """
1282
1283
1284 self.number+=1
1285 page.add('<br>')
1286
1287
1288 invest_dict = {}
1289 for stage, cache in self.trigger_cache.iteritems():
1290
1291
1292 trig_cache = lal.Cache()
1293 for c in cache:
1294
1295
1296
1297 if (self.followup_time in c.segment) or ((self.followup_time-2048) in c.segment) or ((self.followup_time+2048) in c.segment):
1298 if not self.injection_id or \
1299 (self.injection_id and \
1300 self.check_injection_id(c, self.injection_id)):
1301 trig_cache.append( c )
1302
1303
1304 file_list = trig_cache.pfnlist()
1305 if len(file_list)==0:
1306 print >>sys.stderr, "ERROR: No files found for stage %s in the "\
1307 "cache for ID %s and time %d; probably mismatch of a "\
1308 "pattern in the options. " % \
1309 ( stage, self.injection_id, self.followup_time)
1310 continue
1311
1312
1313 if stage in ('COINCIDENCE','COINCIDENCE-SLID'):
1314
1315 for cat in [1,2,3,4,5]:
1316 select_list=self.select_category(file_list, cat)
1317 if len(select_list)==0:
1318 print "WARNING (not that bad): "\
1319 "No THINCA files found for category ", cat
1320 continue
1321 modstage = stage+'_CAT_' + str(cat)
1322 invest_dict[modstage] = self.create_timeseries(select_list,modstage,\
1323 self.number,slideDict)
1324 else:
1325 invest_dict[stage]=self.create_timeseries(file_list, stage, \
1326 self.number,slideDict)
1327
1328
1329
1330 if self.flag_followup:
1331 self.add_table_followup(page, invest_dict)
1332
1333
1334 for stage in self.orderLabels:
1335 if stage in invest_dict:
1336 result = invest_dict[stage]
1337
1338 fname = result['filename']
1339 page.a(extra.img(src=[fname], width=400, \
1340 alt=fname, border="2"), title=fname, href=[fname])
1341
1342
1343
1344 page.add("<hr>")
1345 page.add("Figure(s) and data produced with " + __prog__ + ", version " \
1346 + git_version.verbose_msg)
1347
1348
1349 htmlfilename = self.opts.prefix + "_followup_"+str(self.number) +\
1350 self.opts.suffix+'.html'
1351 file = open(self.opts.output_path+htmlfilename,'w')
1352 file.write(page(False))
1353 file.close()
1354
1355
1356 self.fname_list.append(htmlfilename)
1357 return htmlfilename
1358
1359
1361 """
1362 Find the list of unvetoed injections for this particular
1363 injection (missed or found)
1364 @param inj: the injection (SimInspiral table)
1365 @param ifo_list: the ifo list
1366 """
1367
1368 unvetoed_ifos = []
1369 for ifo in ifo_list:
1370 veto_time = float(getattr(inj, ifo[0].lower()+'_end_time'))
1371 if not self.is_veto(veto_time, ifo):
1372 unvetoed_ifos.append(ifo)
1373
1374 return unvetoed_ifos
1375
1376
1378 """
1379 Find the relevant distance for this injection,
1380 given the list of IFO's
1381 """
1382
1383
1384 list_dist = []
1385 for ifo in ifo_list:
1386 list_dist.append(getattr(inj, 'eff_dist_'+ifo[0].lower()))
1387 list_dist.sort()
1388
1389 return list_dist[1]
1390
1391
1393 """
1394 Find the relevant effective distance for this injection
1395 including any possible vetoes.
1396 """
1397
1398
1399 unvetoed_ifos = self.find_unvetoed_ifos(inj, ifo_list)
1400
1401 if len(unvetoed_ifos)>=2:
1402
1403
1404
1405
1406 type = 'ks'
1407 if len(unvetoed_ifos) == len(ifo_list):
1408
1409 type = 'ro'
1410
1411
1412 rel_dist = self.get_relevant_distance(inj, unvetoed_ifos)
1413
1414
1415 self.followup_dict['inj'].append(inj)
1416 self.followup_dict['dist'].append(rel_dist)
1417 self.followup_dict['type'].append(type)
1418
1419 else:
1420 self.vetoed_injections.append(inj)
1421
1422
1423 - def followup_missed_injections(self, opts, missed_injections, \
1424 found_injections, \
1425 xvalue = 'mtotal', xlog = 'linear', \
1426 ylog = 'log'):
1427 """
1428 This function will do the complete followup on each relevant
1429 missed injection.
1430 """
1431
1432
1433
1434 foundSymbol = {'H1H2':'x', 'H1L1':'^', 'H2L1':'+', 'H1V1':'v', \
1435 'H2V1':'<','L1V1':'>', 'H1H2L1':'s', 'H1H2V1':'D',\
1436 'H2L1V1':'d', 'H1L1V1':'1',\
1437 'H1H2L1V1':'p', 'H1':'x', 'H2':'x', 'L1':'x', 'V1':'x'}
1438
1439
1440 ifo_list = get_ifo_list(opts.ifo_times)
1441 for inj in missed_injections:
1442 self.populate_followup_list(inj, ifo_list)
1443
1444
1445 if opts.followup_number:
1446 index = numpy.argsort(self.followup_dict['dist'])
1447 number_to_followup = min(len(index), opts.followup_number)
1448 else:
1449 raise NotImplementedError,"NotImplemented. You only can specify the "\
1450 "number of injections to be followed up. "
1451
1452
1453
1454 found_dist = {}
1455 found_masses = {}
1456 for type in found_injections.keys():
1457 temp_dist = None
1458
1459
1460 sub_ifo_list = get_ifo_list(type)
1461
1462
1463 found_dist[type] = [self.get_relevant_distance(inj, sub_ifo_list)\
1464 for inj in found_injections[type]]
1465
1466 found_masses[type], legend = get_data_from_table( \
1467 found_injections[type], xvalue)
1468
1469
1470 pylab.clf()
1471
1472
1473 for type in found_injections.keys():
1474
1475
1476 sub_ifo_list = get_ifo_list(type)
1477
1478
1479
1480 col = 'm'
1481 if len(sub_ifo_list) == len(ifo_list):
1482 col = 'b'
1483
1484
1485 pylab.plot( found_masses[type], found_dist[type], col+foundSymbol[type], \
1486 markerfacecolor='None',markeredgecolor=col, \
1487 label = type, markersize=10, markeredgewidth=1)
1488
1489
1490 dist = [self.get_relevant_distance(inj, ifo_list)\
1491 for inj in self.vetoed_injections]
1492 masses = viz.readcol(self.vetoed_injections,"mass1")+\
1493 viz.readcol(self.vetoed_injections,"mass2")
1494 pylab.plot( masses, dist, 'ks', \
1495 markerfacecolor='None',label='vetoed',\
1496 markeredgecolor='k',markersize=10, markeredgewidth=1)
1497
1498
1499 missed_masses, legend = get_data_from_table( self.followup_dict['inj'], xvalue)
1500
1501 pylab.plot( missed_masses, self.followup_dict['dist'], 'ro', \
1502 markerfacecolor='None',label='(prop) missed',\
1503 markeredgecolor='r',markersize=10, markeredgewidth=1)
1504
1505
1506 legy = 'Second smallest eff. distance [Mpc]'
1507 title_text = legend+' vs '+legy
1508 pylab.title( opts.title + ' '+title_text+' in '+opts.ifo_times+\
1509 ' times', size='x-large')
1510 pylab.xlabel(legend, size='x-large')
1511 pylab.ylabel(legy, size='x-large')
1512 pylab.grid(True)
1513 pylab.legend()
1514
1515
1516 mapDict ={'object':'', \
1517 'text':'Click on a filled circle to go to the followup page',\
1518 'xCoords':[], 'yCoords':[], 'links': []}
1519
1520
1521
1522
1523 boundFigX = [100.0,720.0]
1524 boundFigY = [540.0, 60.0]
1525
1526
1527 pylab.gca().set_xscale(xlog)
1528 pylab.gca().set_yscale(ylog)
1529
1530
1531 axes=pylab.gcf().axes[0]
1532 rangeFigX = axes.get_xlim()
1533 rangeFigY = numpy.log10( axes.get_ylim() )
1534
1535
1536 slopeX = (boundFigX[1]-boundFigX[0])/(rangeFigX[1]-rangeFigX[0])
1537 interX = boundFigX[1] - slopeX*rangeFigX[1]
1538 slopeY = (boundFigY[1]-boundFigY[0])/(rangeFigY[1]-rangeFigY[0])
1539 interY = boundFigY[1] - slopeY*rangeFigY[1]
1540
1541
1542
1543 for ind in index[:number_to_followup]:
1544
1545
1546 inj = self.followup_dict['inj'][ind]
1547 type = self.followup_dict['type'][ind]
1548 eff_dist = self.followup_dict['dist'][ind]
1549 eff_dist_log = numpy.log10(eff_dist)
1550 total_mass = inj.mass1+inj.mass2
1551
1552
1553
1554 px = int(interX+slopeX*total_mass)
1555 py = int(interY+slopeY*eff_dist_log)
1556
1557
1558
1559 pylab.plot( [total_mass], [eff_dist], type, markerfacecolor = type[0], \
1560 markersize=10)
1561
1562
1563 followuphtml = self.from_missed(inj)
1564
1565
1566 mapDict['xCoords'].append(px)
1567 mapDict['yCoords'].append(py)
1568 mapDict['links'].append(followuphtml)
1569
1570 return mapDict
1571
1572
1574 """
1575 Retrieves data from a table, including non-table entries
1576 such as 'mtotal' and 'time'
1577 @param table: the table containing the data
1578 @param col_name: the name of the column
1579 @param ifo: the ifo for which the column is returned
1580 """
1581
1582 if col_name=='time':
1583 data = [t for t in viz.timeindays(viz.readcol(table,"end_time", ifo))]
1584 legend = "End time (in days)"
1585 elif col_name == 'mtotal':
1586 data = viz.readcol(table,"mass1")+viz.readcol( table,"mass2")
1587 legend = "Total mass"
1588 else:
1589 data = viz.readcol(table, col_name)
1590 legend = xname
1591
1592 return data, legend
1593
1594
1595 -def getData( table, xname, yname, ifo ):
1596 """
1597 Retrieves data from a table, including non-table entries
1598 such as 'mtotal' and 'time'
1599 @param table: the table with the data
1600 @param xname: the x-value
1601 @param yname: the y-value
1602 @param ifo : the ifo for which the time or distance is returned
1603 """
1604
1605 if xname=='time':
1606 xp= [ t-opts.time_offset \
1607 for t in viz.timeindays(viz.readcol( table,"end_time", ifo)) ]
1608 legx = "End time (in days)"
1609 elif xname == 'mtotal':
1610 xp = viz.readcol( table,"mass1")+viz.readcol( table,"mass2")
1611 legx = "Total mass"
1612 else:
1613 xp = viz.readcol( table,xname)
1614 legx = xname
1615
1616
1617 if yname == 'eff_dist':
1618 yp = viz.readcol( table,"eff_dist", ifo )
1619 legy = "Effective distance"
1620 else:
1621 yp = viz.readcol( table,yname)
1622 legy = yname
1623
1624 return xp, yp, legx, legy
1625
1626
1628 ifo_list = []
1629 for i in range(len(ifo_times)/2):
1630 ifo_list.append(ifo_times[2*i:2*i+2])
1631 return ifo_list
1632