gstlal 1.13.0
Loading...
Searching...
No Matches
datafind.py
1# Copyright (C) 2020 Patrick Godwin (patrick.godwin@ligo.org)
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 2 of the License, or (at your
6# option) any later version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11# Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17
18from collections import defaultdict
19from dataclasses import dataclass, field
20from enum import Enum
21import glob
22import math
23import os
24
25import gwdatafind
26from lal.utils import CacheEntry
27from ligo.segments import segment, segmentlist, segmentlistdict
28
29
30DEFAULT_DATAFIND_SERVER = os.getenv('LIGO_DATAFIND_SERVER', 'ldr.ldas.cit:80')
31
32
34 def description(self, svd_bin=None, subtype=None):
35 # FIXME: sanity check subtype input
36 description = []
37 if svd_bin:
38 description.append(svd_bin)
39 description.append(f"GSTLAL_{self.name}")
40 if subtype:
41 description.append(subtype.upper())
42 return "_".join(description)
43
44 def filename(self, ifos, span=None, svd_bin=None, subtype=None, extension=None):
45 if not span:
46 span = segment(0, 0)
47 if not extension:
48 extension = self.extension
49 return T050017_filename(ifos, self.description(svd_bin, subtype), span, extension)
50
51 def file_pattern(self, svd_bin=None, subtype=None, extension=None):
52 if not extension:
53 extension = self.extension
54 return f"*-{self.description(svd_bin, subtype)}-*-*{extension}"
55
56 def directory(self, root=None, start=None):
57 path = self.name.lower()
58 if root:
59 path = os.path.join(root, path)
60 if start:
61 path = os.path.join(path, gps_directory(start))
62 return path
63
64
66 REFERENCE_PSD = (0, "xml.gz")
67 MEDIAN_PSD = (1, "xml.gz")
68 SMOOTH_PSD = (2, "xml.gz")
69 TRIGGERS = (10, "xml.gz")
70 DIST_STATS = (20, "xml.gz")
71 PRIOR_DIST_STATS = (21, "xml.gz")
72 MARG_DIST_STATS = (22, "xml.gz")
73 DIST_STAT_PDFS = (30, "xml.gz")
74 POST_DIST_STAT_PDFS = (31, "xml.gz")
75 ZEROLAG_DIST_STAT_PDFS = (32, "xml.gz")
76 TEMPLATE_BANK = (40, "xml.gz")
77 SPLIT_BANK = (41, "xml.gz")
78 SVD_BANK = (42, "xml.gz")
79 SVD_MANIFEST = (50, "json")
80 MASS_MODEL = (60, "h5")
81 FRAMES = (70, "gwf")
82 INJECTIONS = (80, "xml")
83 SPLIT_INJECTIONS = (81, "xml")
84 MATCHED_INJECTIONS = (82, "xml")
85 LNLR_SIGNAL_CDF = (90, "pkl")
86
87 def __init__(self, value, extension):
88 self.extension = extension
89
90 def __str__(self):
91 return self.name.upper()
92
93
94@dataclass
96 name: "DataType"
97 cache: list = field(default_factory=list)
98
99 @property
100 def files(self):
101 return [entry.path for entry in self.cache]
102
103 def __len__(self):
104 return len(self.cache)
105
106 def __add__(self, other):
107 assert self.name == other.name, "can't combine two DataCaches with different data types"
108 return DataCache(self.name, self.cache + other.cache)
109
110 def chunked(self, chunk_size):
111 for i in range(0, len(self), chunk_size):
112 yield DataCache(self.name, self.cache[i:i+chunk_size])
113
114 def groupby(self, *group):
115 # determine groupby operation
116 keyfunc = self._groupby_keyfunc(group)
117
118 # return groups of DataCaches keyed by group
119 grouped = defaultdict(list)
120 for entry in self.cache:
121 grouped[keyfunc(entry)].append(entry)
122 return {key: DataCache(self.name, cache) for key, cache in sorted(grouped.items())}
123
124 def groupby_bins(self, bin_type, bins):
125 assert bin_type in set(("time", "segment", "time_bin")), f"bin_type: {bin_type} not supported"
126
127 # return groups of DataCaches keyed by group
128 grouped = defaultdict(list)
129 for bin_ in bins:
130 for entry in self.cache:
131 if entry.segment in bin_:
132 grouped[bin_].append(entry)
133
134 return {key: DataCache(self.name, cache) for key, cache in sorted(grouped.items())}
135
136 def _groupby_keyfunc(self, groups):
137 if isinstance(groups, str):
138 groups = [groups]
139
140 def keyfunc(key):
141 keys = []
142 for group in groups:
143 if group in set(("ifo", "instrument", "observatory")):
144 keys.append(key.observatory)
145 elif group in set(("time", "segment", "time_bin")):
146 keys.append(key.segment)
147 elif group in set(("bin", "svd_bin")):
148 keys.append(key.description.split("_")[0])
149 elif group in set(("subtype", "tag")):
150 keys.append(key.description.rpartition(f"GSTLAL_{self.name.name}")[2].lstrip("_"))
151 elif group in set(("directory", "dirname")):
152 keys.append(os.path.dirname(key.path))
153 else:
154 raise ValueError(f"{group} not a valid groupby operation")
155 if len(keys) > 1:
156 return tuple(keys)
157 else:
158 return keys[0]
159
160 return keyfunc
161
162 def copy(self, root=None):
163 cache_paths = []
164 for entry in self.cache:
165 filedir = self._data_path(self.name, start=entry.segment[0], root=root)
166 filename = os.path.basename(entry.path)
167 cache_paths.append(os.path.join(filedir, filename))
168
169 return DataCache.from_files(self.name, cache_paths)
170
171 @classmethod
172 def generate(
173 cls,
174 name,
175 ifos,
176 time_bins=None,
177 svd_bins=None,
178 subtype=None,
179 extension=None,
180 root=None,
181 create_dirs=True
182 ):
183 # format args
184 if isinstance(ifos, str) or isinstance(ifos, frozenset):
185 ifos = [ifos]
186 if svd_bins and isinstance(svd_bins, str):
187 svd_bins = [svd_bins]
188 if subtype is None or isinstance(subtype, str):
189 subtype = [subtype]
190
191 # format time bins
192 if not time_bins:
193 time_bins = segmentlistdict({ifo: segmentlist([segment(0, 0)]) for ifo in ifos})
194 elif isinstance(time_bins, segment):
195 time_bins = segmentlistdict({ifo: segmentlist([time_bins]) for ifo in ifos})
196 elif isinstance(time_bins, segmentlist):
197 time_bins = segmentlistdict({ifo: time_bins for ifo in ifos})
198 else:
199 time_bins = segmentlistdict({ifo: time_bins[ifo] for ifo in ifos if ifo in time_bins})
200
201 # generate the cache
202 cache = []
203 for ifo, time_bins in time_bins.items():
204 for span in time_bins:
205 path = cls._data_path(name, start=span[0], root=root, create=create_dirs)
206 if svd_bins:
207 for svd_bin in svd_bins:
208 for stype in subtype:
209 filename = name.filename(
210 ifo, span, svd_bin=svd_bin, subtype=stype, extension=extension
211 )
212 cache.append(os.path.join(path, filename))
213 else:
214 for stype in subtype:
215 filename = name.filename(ifo, span, subtype=stype, extension=extension)
216 cache.append(os.path.join(path, filename))
217
218 return cls(name, [CacheEntry.from_T050017(entry) for entry in cache])
219
220 @classmethod
221 def find(cls, name, start=None, end=None, root=None, segments=None, svd_bins=None, extension=None, subtype=None):
222 cache = []
223 if svd_bins:
224 svd_bins = set([svd_bins]) if isinstance(svd_bins, str) else set(svd_bins)
225 else:
226 svd_bins = [None]
227 if subtype is None or isinstance(subtype, str):
228 subtype = [subtype]
229 for svd_bin in svd_bins:
230 for stype in subtype:
231 cache.extend(glob.glob(cls._glob_path(name, root, svd_bin, stype, extension=extension)))
232 cache.extend(glob.glob(cls._glob_path(name, root, svd_bin, stype, extension=extension, gps_dir=False)))
233
234 cache = [CacheEntry.from_T050017(entry) for entry in cache]
235 if segments:
236 cache = [entry for entry in cache if segments.intersects_segment(entry.segment)]
237 return cls(name, cache)
238
239 @classmethod
240 def from_files(cls, name, files):
241 if isinstance(files, str):
242 files = [files]
243 return cls(name, [CacheEntry.from_T050017(entry) for entry in files])
244
245 @staticmethod
246 def _data_path(datatype, start=None, root=None, create=True):
247 path = datatype.directory(start=start, root=root)
248 if create:
249 os.makedirs(path, exist_ok=True)
250 return path
251
252 @staticmethod
253 def _glob_path(name, root=None, svd_bin=None, subtype=None, extension=None, gps_dir=True):
254 if gps_dir:
255 glob_path = os.path.join(str(name).lower(), "*", name.file_pattern(svd_bin, subtype, extension=extension))
256 else:
257 glob_path = os.path.join(str(name).lower(), name.file_pattern(svd_bin, subtype, extension=extension))
258 if root:
259 glob_path = os.path.join(root, glob_path)
260 return glob_path
261
262
263def load_frame_cache(start, end, frame_types, host=None):
264 """
265 Given a span and a set of frame types, loads a frame cache.
266 """
267 if not host:
268 host = DEFAULT_DATAFIND_SERVER
269 cache = []
270 with gwdatafind.Session() as sess:
271 for ifo, frame_type in frame_types.items():
272 urls = gwdatafind.find_urls(ifo[0], frame_type, start, end, host=host, session=sess)
273 cache.extend([CacheEntry.from_T050017(url) for url in urls])
274
275 return cache
276
277
278def gps_directory(gpstime):
279 """
280 Given a gps time, returns the directory name where files corresponding
281 to this time will be written to, e.g. 1234567890 -> '12345'.
282 """
283 return str(int(gpstime))[:5]
284
285
286def T050017_filename(instruments, description, seg, extension, path=None):
287 """
288 A function to generate a T050017 filename.
289 """
290 if not isinstance(instruments, str):
291 instruments = "".join(sorted(list(instruments)))
292 start, end = seg
293 start = int(math.floor(start))
294 try:
295 duration = int(math.ceil(end)) - start
296 # FIXME this is not a good way of handling this...
297 except OverflowError:
298 duration = 2000000000
299 extension = extension.strip('.')
300 if path is not None:
301 return '%s/%s-%s-%d-%d.%s' % (path, instruments, description, start, duration, extension)
302 else:
303 return '%s-%s-%d-%d.%s' % (instruments, description, start, duration, extension)
_data_path(datatype, start=None, root=None, create=True)
Definition datafind.py:246
_glob_path(name, root=None, svd_bin=None, subtype=None, extension=None, gps_dir=True)
Definition datafind.py:253
_groupby_keyfunc(self, groups)
Definition datafind.py:136
description(self, svd_bin=None, subtype=None)
Definition datafind.py:34