23from typing
import Iterable, Mapping, Union
27from ligo
import segments
28from ligo.segments
import utils
as segutils
29from ligo.lw
import ligolw
30from ligo.lw
import lsctables
31from ligo.lw
import utils
as ligolw_utils
32from lal
import LIGOTimeGPS
35DEFAULT_DQSEGDB_SERVER = os.environ.get(
"DEFAULT_SEGMENT_SERVER",
"https://segments.ligo.org")
43def query_dqsegdb_segments(
44 instruments: Union[str, Iterable],
45 start: Union[int, LIGOTimeGPS],
46 end: Union[int, LIGOTimeGPS],
47 flags: Union[str, Mapping],
48 server: str = DEFAULT_DQSEGDB_SERVER,
49) -> segments.segmentlistdict:
50 """Query DQSegDB for science segments.
54 Union[str, Iterable], the instruments to query segments for
56 Union[int, LIGOTimeGPS], the GPS start time
58 Union[int, LIGOTimeGPS], the GPS end time
60 Union[str, Mapping], the name of the DQ flags used to query
62 str, defaults to main DQSegDB server, the server URL
65 segmentlistdict, the queried segments
68 span = segments.segment(LIGOTimeGPS(start), LIGOTimeGPS(end))
69 if isinstance(flags, str):
70 if not isinstance(instruments, str):
71 raise ValueError(
"if flags is type str, then instruments must also be type str")
72 flags = {instruments: flags}
73 if isinstance(instruments, str):
74 instruments = [instruments]
76 segs = segments.segmentlistdict()
77 for ifo, flag
in flags.items():
78 active = dqsegdb2.query.query_segments(flag, start, end, host=server, coalesce=
True)[
"active"]
79 segs[ifo] = segments.segmentlist([span]) & active
84def query_dqsegdb_veto_segments(
85 instruments: Union[str, Iterable],
86 start: Union[int, LIGOTimeGPS],
87 end: Union[int, LIGOTimeGPS],
88 veto_definer_file: str,
90 cumulative: bool =
True,
91 server: str = DEFAULT_DQSEGDB_SERVER,
92) -> segments.segmentlistdict:
93 """Query DQSegDB for veto segments.
97 Union[str, Iterable], the instruments to query segments for
99 Union[int, LIGOTimeGPS], the GPS start time
101 Union[int, LIGOTimeGPS], the GPS end time
103 str, the veto definer file in which to query veto segments for
105 the veto category to use for vetoes, one of CAT1, CAT2, CAT3
107 whether veto categories are cumulative, e.g. choosing CAT2
108 also includes CAT1 vetoes
110 str, defaults to main DQSegDB server, the server URL
113 segmentlistdict, the queried veto segments
116 if isinstance(instruments, str):
117 instruments = [instruments]
119 if category
not in set((
"CAT1",
"CAT2",
"CAT3")):
120 raise ValueError(
"not valid category")
123 xmldoc = ligolw_utils.load_filename(veto_definer_file, contenthandler=LIGOLWContentHandler)
124 vetoes = lsctables.VetoDefTable.get_table(xmldoc)
127 vetoes[:] = [v
for v
in vetoes
if v.ifo
in set(instruments)]
130 cat_level = int(category[-1])
132 vetoes[:] = [v
for v
in vetoes
if v.category <= cat_level]
134 vetoes[:] = [v
for v
in vetoes
if v.category == cat_level]
137 segs = segments.segmentlistdict()
138 for instrument
in instruments:
139 segs[instrument] = segments.segmentlist()
141 flag = f
"{veto.ifo}:{veto.name}:{veto.version}"
142 segs[veto.ifo] |= dqsegdb2.query.query_segments(flag, start, end, host=server, coalesce=
True)[
"active"]
148def query_gwosc_segments(
149 instruments: Union[str, Iterable],
150 start: Union[int, LIGOTimeGPS],
151 end: Union[int, LIGOTimeGPS],
152 verify_certs: bool =
True,
153) -> segments.segmentlistdict:
154 """Query GWOSC for science segments.
158 Union[str, Iterable], the instruments to query segments for
160 Union[int, LIGOTimeGPS], the GPS start time
162 Union[int, LIGOTimeGPS], the GPS end time
164 bool, default True, whether to verify SSL certificates when querying GWOSC.
167 segmentlistdict, the queried segments
170 if isinstance(instruments, str):
171 instruments = [instruments]
174 context = ssl.create_default_context()
176 context.check_hostname =
False
177 context.verify_mode = ssl.CERT_NONE
180 segs = segments.segmentlistdict()
181 for instrument
in instruments:
182 url = _gwosc_segment_url(start, end, f
"{instrument}_DATA")
183 urldata = urllib.request.urlopen(url, context=context).read().decode(
'utf-8')
184 with warnings.catch_warnings():
185 warnings.filterwarnings(
"ignore", category=FutureWarning)
186 segs[instrument] = segutils.fromsegwizard(
187 urldata.splitlines(),
188 coltype=lsctables.LIGOTimeGPS,
195def query_gwosc_veto_segments(
196 instruments: Union[str, Iterable],
197 start: Union[int, LIGOTimeGPS],
198 end: Union[int, LIGOTimeGPS],
200 cumulative: bool =
True,
201 verify_certs: bool =
True,
202) -> segments.segmentlistdict:
203 """Query GWOSC for veto segments.
207 Union[str, Iterable], the instruments to query segments for
209 Union[int, LIGOTimeGPS], the GPS start time
211 Union[int, LIGOTimeGPS], the GPS end time
213 the veto category to use for vetoes, one of CAT1, CAT2, CAT3
215 whether veto categories are cumulative, e.g. choosing CAT2
216 also includes CAT1 vetoes
218 bool, default True, whether to verify SSL certificates when querying GWOSC.
221 segmentlistdict, the queried veto segments
224 span = segments.segment(LIGOTimeGPS(start), LIGOTimeGPS(end))
225 if isinstance(instruments, str):
226 instruments = [instruments]
228 if category
not in set((
"CAT1",
"CAT2",
"CAT3")):
229 raise ValueError(
"not valid category")
232 flags = [f
"CBC_CAT{i}" for i
in range(1, int(category[-1]) + 1)]
234 flags = [f
"CBC_{category}"]
244 flags += hw_inj_flags
247 context = ssl.create_default_context()
249 context.check_hostname =
False
250 context.verify_mode = ssl.CERT_NONE
253 segs = segments.segmentlistdict()
254 for instrument
in instruments:
255 segs[instrument] = segments.segmentlist([span])
257 url = _gwosc_segment_url(start, end, f
"{instrument}_{flag}")
258 urldata = urllib.request.urlopen(url, context=context).read().decode(
'utf-8')
259 with warnings.catch_warnings():
260 warnings.filterwarnings(
"ignore", category=FutureWarning)
261 segs[instrument] &= segutils.fromsegwizard(
262 urldata.splitlines(),
263 coltype=lsctables.LIGOTimeGPS,
268 for instrument
in instruments:
269 segs[instrument] = segments.segmentlist([span]) & ~segs[instrument]
274def analysis_segments(
276 allsegs: segments.segmentlistdict,
277 boundary_seg: segments.segment,
278 start_pad: float = 0.,
280 min_instruments: int = 1,
281 one_ifo_length: float = (3600 * 8.),
282) -> segments.segmentlistdict:
283 """Generate all disjoint detector combination segments for analysis job boundaries.
287 segsdict = segments.segmentlistdict()
291 segment_length =
lambda n_ifo: one_ifo_length / 2 ** (n_ifo - 1)
294 for n
in range(min_instruments, 1 + len(ifos)):
295 for ifo_combos
in itertools.combinations(list(ifos), n):
296 ifo_key = frozenset(ifo_combos)
297 segsdict[ifo_key] = allsegs.intersection(ifo_combos) - allsegs.union(ifos - set(ifo_combos))
298 segsdict[ifo_key] = segsdict[ifo_key].protract(overlap)
299 segsdict[ifo_key] &= segments.segmentlist([boundary_seg])
300 segsdict[ifo_key] = split_segments(segsdict[ifo_key], segment_length(len(ifo_combos)), start_pad)
301 if not segsdict[ifo_key]:
302 del segsdict[ifo_key]
307def split_segments_by_lock(
309 seglistdicts: segments.segmentlistdict,
310 boundary_seg: segments.segment,
311 max_time: float = 10 * 24 * 3600.,
312) -> segments.segmentlist:
313 """Split segments into segments with maximum time and boundaries outside of lock stretches.
316 ifos = set(seglistdicts)
320 doublesegs = segments.segmentlistdict()
322 for ifo2
in ifos - set([ifo1]):
323 if ifo1
in doublesegs:
324 doublesegs[ifo1] |= seglistdicts.intersection((ifo1, ifo2))
326 doublesegs[ifo1] = seglistdicts.intersection((ifo1, ifo2))
329 doublesegsunion = doublesegs.union(doublesegs.keys())
332 segs = seglistdicts.union(seglistdicts.keys())
335 def enoughtime(seglist, start, end):
336 return abs(seglist & segments.segmentlist([segments.segment(start, end)])) > 0.7 * max_time
341 chunks = segments.segmentlist([boundary_seg])
344 for start, end
in doublesegsunion:
345 if all([enoughtime(s, chunks[-1][0], end)
for s
in doublesegs.values()]):
346 chunks[-1] = segments.segment(chunks[-1][0], end)
347 chunks.append(segments.segment(end, boundary_seg[1]))
351 if len(chunks) > 1
and abs(chunks[-1]) < 0.3 * max_time:
352 last_chunk = chunks.pop()
353 chunks[-1] = segments.segmentlist([chunks[-1], last_chunk]).coalesce().extent()
359 seglist: segments.segmentlist,
362) -> segments.segmentlist:
363 """Split a segmentlist into segments of maximum extent.
366 newseglist = segments.segmentlist()
367 for bigseg
in seglist:
368 newseglist.extend(split_segment(bigseg, maxextent, overlap))
372def split_segment(seg: segments.segment, maxextent: float, overlap: float) -> segments.segmentlist:
373 """Split a segment into segments of maximum extent.
377 raise ValueError(
"maxextent must be positive, not %s" % repr(maxextent))
380 if abs(seg) < maxextent:
381 return segments.segmentlist([seg])
384 maxextent = max(int(abs(seg) / (int(abs(seg)) // int(maxextent) + 1)), overlap)
385 maxextent = int(math.ceil(abs(seg) / math.ceil(abs(seg) / maxextent)))
388 seglist = segments.segmentlist()
391 if (seg[0] + maxextent + overlap) < end:
392 seglist.append(segments.segment(seg[0], seg[0] + maxextent + overlap))
393 seg = segments.segment(seglist[-1][1] - overlap, seg[1])
395 seglist.append(segments.segment(seg[0], end))
401def _gwosc_segment_url(start, end, flag):
402 """Returns the GWOSC URL associated with segments.
405 span = segments.segment(LIGOTimeGPS(start), LIGOTimeGPS(end))
408 urlbase =
"https://gw-openscience.org/timeline/segments"
409 if start
in segments.segment(1126051217, 1137254417):
410 query_url = f
"{urlbase}/O1"
411 elif start
in segments.segment(1164556817, 1187733618):
412 query_url = f
"{urlbase}/O2_16KHZ_R1"
413 elif start
in segments.segment(1238166018, 1253977218):
414 query_url = f
"{urlbase}/O3a_16KHZ_R1"
415 elif start
in segments.segment(1256655618, 1269363618):
416 query_url = f
"{urlbase}/O3b_16KHZ_R1"
418 raise ValueError(
"GPS times requested not in GWOSC")
420 return f
"{query_url}/{flag}/{span[0]}/{abs(span)}"