1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23  import os, sys, re, operator, math 
 24  from StringIO import StringIO 
 25  from glue import segments 
 26  from glue.ligolw import ligolw, lsctables, table, utils 
 27  from glue.ligolw.utils import segments as ligolw_segments 
 28  from glue.segmentdb import query_engine, segmentdb_utils 
 29  from glue.ligolw.utils import process as ligolw_process 
 30  from pylal.dq.dqTriggerUtils import def_get_time 
 31   
 32  from scipy.stats import poisson 
 33   
 34  LIGOTimeGPS = lsctables.LIGOTimeGPS 
 35   
 36   
 37  import copy_reg 
 38  copy_reg.pickle(type(segments.segment(0, 1)), \ 
 39                  lambda x:(segments.segment, (x[0], x[1]))) 
 40  copy_reg.pickle(type(segments.segmentlist([])),  
 41                  lambda x:(segments.segmentlist, ([y for y in x], ))) 
 42   
 43  from glue import git_version 
 44   
 45  __author__  = "Andrew P Lundgren <andrew.lundgren@ligo.org>, Duncan Macleod <duncan.macleod@ligo.org>" 
 46  __version__ = "git id %s" % git_version.id 
 47  __date__    = git_version.date 
 48   
 49  """ 
 50  This module provides useful segment and veto tools for data quality investigations. 
 51  """ 
 52   
 53   
 54   
 55   
 56   
 58   
 59    """ 
 60      Read a glue.segments.segmentlist from the file object file containing an 
 61      xml segment table. 
 62   
 63      Arguments: 
 64   
 65        file : file object 
 66          file object for segment xml file 
 67   
 68      Keyword Arguments: 
 69   
 70        dict : [ True | False ] 
 71          returns a glue.segments.segmentlistdict containing coalesced 
 72          glue.segments.segmentlists keyed by seg_def.name for each entry in the 
 73          contained segment_def_table. Default False 
 74        id : int 
 75          returns a glue.segments.segmentlist object containing only those 
 76          segments matching the given segment_def_id integer 
 77           
 78    """ 
 79   
 80     
 81    xmldoc, digest = utils.load_fileobj(file, gz=file.name.endswith(".gz"), contenthandler = lsctables.use_in(ligolw.LIGOLWContentHandler)) 
 82    seg_def_table  = lsctables.SegmentDefTable.get_table(xmldoc) 
 83    seg_table      = lsctables.SegmentTable.get_table(xmldoc) 
 84   
 85    if dict: 
 86      segs = segments.segmentlistdict() 
 87    else: 
 88      segs = segments.segmentlist() 
 89   
 90    seg_id = {} 
 91    for seg_def in seg_def_table: 
 92      seg_id[int(seg_def.segment_def_id)] = str(seg_def.name) 
 93      if dict: 
 94        segs[str(seg_def.name)] = segments.segmentlist() 
 95   
 96    for seg in seg_table: 
 97      if dict: 
 98        segs[seg_id[int(seg.segment_def_id)]]\ 
 99            .append(segments.segment(seg.start_time, seg.end_time)) 
100        continue 
101      if id and int(seg.segment_def_id)==id: 
102        segs.append(segments.segment(seg.start_time, seg.end_time)) 
103        continue 
104      segs.append(segments.segment(seg.start_time, seg.end_time)) 
105   
106    if dict: 
107     for seg_name in seg_id.values(): 
108       segs[seg_name] = segs[seg_name].coalesce() 
109    else: 
110      segs = segs.coalesce() 
111   
112    xmldoc.unlink() 
113   
114    return segs 
 115   
116   
117   
118   
119   
121   
122    """ 
123      Write the glue.segments.segmentlist object segs to file object file in xml 
124      format with appropriate tables. 
125    """ 
126   
127     
128    xmldoc = ligolw.Document() 
129    xmldoc.appendChild(ligolw.LIGO_LW()) 
130    xmldoc.childNodes[-1].appendChild(lsctables.New(lsctables.ProcessTable)) 
131    xmldoc.childNodes[-1].appendChild(lsctables.New(lsctables.ProcessParamsTable)) 
132   
133     
134    process = ligolw_process.append_process(xmldoc,\ 
135                                    program='pylal.dq.dqSegmentUtils',\ 
136                                    version=__version__,\ 
137                                    cvs_repository='lscsoft',\ 
138                                    cvs_entry_time=__date__) 
139   
140    gpssegs = segments.segmentlist() 
141    for seg in segs: 
142      gpssegs.append(segments.segment(LIGOTimeGPS(seg[0]), LIGOTimeGPS(seg[1]))) 
143   
144     
145    segments_tables = ligolw_segments.LigolwSegments(xmldoc) 
146    segments_tables.add(ligolw_segments.LigolwSegmentList(active=gpssegs)) 
147     
148    segments_tables.coalesce() 
149    segments_tables.optimize() 
150    segments_tables.finalize(process) 
151    ligolw_process.set_process_end_time(process) 
152   
153     
154    with utils.SignalsTrap(): 
155      utils.write_fileobj(xmldoc, file, gz=False) 
 156   
157   
158   
159   
160   
162   
163    """ 
164      Read a glue.segments.segmentlist object from the file object file containin 
165      a comma separated list of segments. 
166    """ 
167   
168    def CSVLineToSeg(line): 
169      tstart, tend = map(LIGOTimeGPS, line.split(',')) 
170      return segments.segment(tstart, tend) 
 171   
172    segs = segments.segmentlist([CSVLineToSeg(line) for line in csvfile]) 
173   
174    return segs.coalesce() 
175   
176   
177   
178   
179   
181   
182    """ 
183      Remove any segments shorter than 2064 seconds from seglist because ihope 
184      won't analyze them. 
185    """ 
186   
187    return segments.segmentlist([seg for seg in seglist if abs(seg) >= 2064]) 
 188   
189   
190   
191   
192   
194   
195    """ 
196      Given a veto segmentlist, start pad, and end pad, pads and coalesces the 
197      segments. Convention is to expand segments with positive padding, contract 
198      with negative. Any segments that are not big enough to be contracted 
199      appropriately are removed. 
200    """ 
201   
202    if not end_pad: end_pad = start_pad 
203   
204    padded = lambda seg: segments.segment(seg[0]-start_pad, seg[1]+end_pad) 
205   
206    seglist = segments.segmentlist([padded(seg) for seg in seglist if \ 
207                                    abs(seg) > start_pad+end_pad]) 
208   
209    return seglist.coalesce() 
 210   
211   
212   
213   
214   
216   
217    """ 
218      Given a segmentlist and time to chop, removes time from the end of each 
219      segment (defaults to 30 seconds). 
220    """ 
221   
222    chopped = lambda seg: segments.segment(seg[0], max(seg[0], seg[1] - end_chop)) 
223    seglist = segments.segmentlist([chopped(seg) for seg in seglist]) 
224    return seglist.coalesce() 
 225   
226   
227   
228   
229   
230 -def grab_segments(start, end, flag,\ 
231                    segment_url='https://segdb.ligo.caltech.edu',\ 
232                    segment_summary=False): 
 233   
234    """ 
235      Returns a segmentlist containing the segments during which the given flag 
236      was active in the given period. 
237   
238      Arguments: 
239   
240        start : int 
241          GPS start time 
242        end : int 
243          GPS end time 
244        flag : string 
245          'IFO:NAME:VERSION' format string 
246   
247      Keyword arguments: 
248   
249        segment_url : string 
250          url of segment database to query, default https://segdb.ligo.caltech.edu 
251        segment_summary : [ True | False ] 
252          also return the glue.segments.segmentlist defining the valid span of the 
253          returned segments 
254    """ 
255   
256     
257    start = int(math.floor(start)) 
258    end   = int(math.ceil(end)) 
259   
260     
261    connection        = segmentdb_utils.setup_database(segment_url) 
262    engine            = query_engine.LdbdQueryEngine(connection) 
263   
264     
265    if isinstance(flag, basestring): 
266      flags = flag.split(',') 
267    else: 
268      flags = flag 
269   
270    segdefs = [] 
271    for f in flags: 
272      spec = f.split(':') 
273      if len(spec) < 2 or len(spec) > 3: 
274        raise AttributeError, "Included segements must be of the form "+\ 
275                              "ifo:name:version or ifo:name:*" 
276   
277      ifo  = spec[0] 
278      name = spec[1] 
279   
280      if len(spec) is 3 and spec[2] is not '*': 
281        version = int(spec[2]) 
282        if version < 1: 
283          raise AttributeError, "Segment version numbers must be greater than zero" 
284      else: 
285        version = '*' 
286   
287       
288      segdefs += segmentdb_utils.expand_version_number(engine, (ifo, name, version, \ 
289                                                              start, end, 0, 0)) 
290   
291     
292    segs = segmentdb_utils.query_segments(engine, 'segment', segdefs) 
293    segs = [s.coalesce() for s in segs] 
294    if segment_summary: 
295      segsums = segmentdb_utils.query_segments(engine, 'segment_summary', segdefs) 
296       
297      segsums = [s.coalesce() for s in segsums] 
298      segsummap = [segments.segmentlist() for f in flags] 
299      for segdef,segsum in zip(segdefs, segsums): 
300        try: 
301          fidx = flags.index(':'.join(map(str, segdef[:3]))) 
302        except ValueError: 
303          fidx = flags.index(':'.join(segdef[:2])) 
304        segsummap[fidx].extend(segsum) 
305      if flag == flags[0]: 
306        return segs[0],segsummap[0] 
307      else: 
308        return segs,segsummap 
309    if flag == flags[0]: 
310      return segs[0] 
311    else: 
312      return segs 
 313   
314   
315   
316   
317   
318 -def dump_flags(ifos=None, segment_url=None, match=None, unmatch=None,\ 
319                 latest=False): 
 320   
321    """ 
322      Returns the list of all flags defined in the database. 
323   
324      Keyword rguments: 
325        ifo : [ str | list ] 
326          list of ifos to query, or str for single ifo 
327        segment_url : str  
328          url of segment database, defaults to contents of S6_SEGMENT_SERVER 
329          environment variable 
330        match : [ str | regular pattern ] 
331          regular expression to search against returned flag names, e.g, 'UPV' 
332        unmatch : [ str | regular pattern ] 
333          regular expression to negatively search against returned flag names 
334    """ 
335   
336    if isinstance(ifos, str): 
337      ifos = [ifos] 
338   
339     
340    if not segment_url: 
341      segment_url = os.getenv('S6_SEGMENT_SERVER') 
342   
343     
344    myClient = segmentdb_utils.setup_database(segment_url) 
345   
346    reply = StringIO(myClient.query(squery)) 
347    xmldoc, digest = utils.load_fileobj(reply) 
348    seg_def_table = lsctables.SegmentDefTable.get_table(xmldoc) 
349   
350     
351    seg_def_table.sort(key=lambda flag: (flag.ifos[0], flag.name, \ 
352                                         flag.version), reverse=True) 
353   
354    flags = lsctables.New(type(seg_def_table)) 
355   
356    for row in seg_def_table: 
357   
358       
359      if match and not re.search(match, row.name):  continue 
360   
361       
362      if unmatch and re.search(unmatch, row.name):  continue 
363   
364       
365      flatest=True 
366      if latest: 
367         
368        vflags = [f for f in flags if row.name==f.name and\ 
369                  row.get_ifos()==f.get_ifos()] 
370         
371        for f in vflags: 
372          if f.version>=row.version: 
373            flatest=False 
374            break 
375      if not flatest: 
376        continue 
377   
378       
379      for ifo in ifos: 
380        if ifo in row.get_ifos(): 
381          flags.append(row) 
382          break 
383   
384    return flags 
 385   
386   
387   
388   
389   
391   
392    """ 
393      Return a tuple containing the number of vetoed injections, the number 
394      expected, and the Poisson safety probability based on the number of 
395      injections vetoed relative to random chance according to Poisson statistics. 
396   
397      Arguments: 
398   
399        segs : glue.segments.segmentlist 
400          list of segments to be tested 
401        injTable : glue.ligolw.table.Table 
402          table of injections 
403        livetime : [ float ] 
404          livetime of search 
405    """ 
406   
407    deadtime = segs.__abs__() 
408   
409    get_time = def_get_time(injTable.tableName) 
410    injvetoed = len([ inj for inj in injTable if get_time(inj) in segs ]) 
411   
412    injexp = len(injTable)*float(deadtime) / float(livetime) 
413   
414    prob = 1 - poisson.cdf(injvetoed-1, injexp) 
415   
416    return injvetoed, injexp, prob 
 417   
418   
419   
420   
421   
423    """ 
424      Convert integer bit mask into binary bits. Returns a list of 0s or 1s 
425      from 2^0 up to 2^n. 
426   
427      Example: 
428       
429      >>> _bits(295, n=8) 
430      [1, 1, 1, 0, 0, 1, 0, 0] 
431    """ 
432    return [(0, 1)[int(i)>>j & 1] for j in xrange(n)] 
 433   
435   
436    """ 
437      Returns a glue.segments.segmentlistdict of active segments for each bit 
438      in a dq_key. 
439    """ 
440   
441    segdict = segments.segmentlistdict() 
442    for key in dq_key: 
443      segdict[key] = segments.segmentlist() 
444   
445     
446    for i in xrange(len(data)): 
447      binary = _bits(data[i], len(dq_key)) 
448      seg = segments.segment(time[i], time[i]-1) 
449      for j, key in enumerate(dq_key): 
450        if binary[j] == 1: 
451          segdict[key].append(seg) 
452   
453    segdict = segdict.coalesce() 
454   
455    return segdict 
 456