ATLAS Offline Software
Loading...
Searching...
No Matches
iovset.py
Go to the documentation of this file.
1# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
2
3
4from collections import defaultdict
5from pickle import load, dump, loads
6from sys import stdout
7
8from ..general import interleave
9from ..utils import pprint_objects
10
11from .runlumi import RunLumi
12from .iovtype import RANGEIOV_VAL, make_iov_type
13
14
15def restore_iovset(cls, name, variables, elements):
16 """
17 Rebuild a pickled IOVSet
18 """
19 iov_type = make_iov_type(name, variables)
20 return cls(map(iov_type._make, elements))
21
22class IOVSet(list):
23 def __init__(self, *args, **kwargs):
24 self.iov_type = kwargs.pop("iov_type", None)
25 self.origin = kwargs.pop("origin", None)
26 self.parent = kwargs.pop("parent", None)
27 from sys import _getframe
28 f = _getframe(1)
29 self.constructed_at = "%s:%i" % (f.f_code.co_filename, f.f_lineno)
30 super(IOVSet, self).__init__(*args, **kwargs)
31 if self.iov_type is None and self:
32 self.iov_type = type(self.first)
33
34 def logical_not(self):
35 from ..events import process_iovs
36 cls = type(self)
37 alliov = cls([RANGEIOV_VAL(RunLumi(0), RunLumi(0xFFFFFFFFFFFFFFFF))])
38 events = process_iovs(alliov, self)
39 return cls(RANGEIOV_VAL(since, until)
40 for since, until, (_, state) in events if not state)
41
42 @classmethod
43 def logical_or(cls, *rhs_iovsets):
44 """
45 Gives the IOV ranges which are present in both this IOVSet and `rhs`
46 """
47 # Import goes here to prevent circular imports
48 from ..events import process_iovs
49 result = cls()
50 for since, until, iovsets in process_iovs(*rhs_iovsets):
51 if any(iovsets):
52 result.add(since, until)
53 return result.solidify(RANGEIOV_VAL)
54
55 @classmethod
56 def logical_and(cls, *rhs_iovsets):
57 """
58 Gives the IOV ranges which are present in both this IOVSet and `rhs`
59 """
60 # Import goes here to prevent circular imports
61 from ..events import process_iovs
62 result = cls()
63 for since, until, iovsets in process_iovs(*rhs_iovsets):
64 if all(iovsets):
65 result.add(since, until)
66 return result.solidify(RANGEIOV_VAL)
67
68 def __or__(self, rhs):
69 # Note: classmethod
70 return self.logical_or(self, rhs)
71
72 def __and__(self, rhs):
73 # Note: classmethod
74 return self.logical_and(self, rhs)
75
76 @classmethod
77 def from_iovsets(cls, iovsets):
78 """
79 Construct one IOVSet from many iovsets
80 """
81 iovsets = list(iovsets)
82 if not iovsets:
83 return cls()
84 types = set(iovset.iov_type for iovset in iovsets)
85 assert len(types) == 1
86 origins = [i.origin for i in iovsets]
87 parents = [i.parent for i in iovsets]
88 iov_gen = (iov for iovset in iovsets for iov in iovset)
89 return cls(iov_gen, iov_type=types.pop(), origin=origins, parent=parents)
90
92 from ..iov_arrangement import connect_adjacent_iovs
93 return connect_adjacent_iovs(self)
94
95 def pprint(self, where=stdout):
96 pprint_objects(self, where)
97
98 @property
99 def ordered(self):
100 prev_until = None
101 for iov in self:
102 if iov.since > iov.until:
103 return False
104 if prev_until is not None and iov.since < prev_until:
105 return False
106 prev_until = iov.until
107 return True
108
109 def to_file(self, filename):
110 with open(filename, "wb") as fd:
111 dump(self, fd)
112
113 @classmethod
114 def from_file(cls, filename):
115 with open(filename, "rb") as fd:
116 if filename.endswith(".bz2"):
117 return loads(fd.read().decode("bz2"))
118 return load(fd)
119
120 @classmethod
121 def from_grl_string(cls, data):
122 from ..grl import load_grl_string
123 return load_grl_string(data, IOVSet_class=cls)
124
125 @classmethod
126 def from_grl(cls, filename):
127 with open(filename, "rb") as fd:
128 return cls.from_grl_string(fd.read())
129
130 def to_grl_string(self, name="unknown", version="unknown"):
131 from ..grl import make_grl
132 return make_grl(self, name, version)
133
134 def to_grl(self, filename, name="unknown", version="unknown"):
135 with open(filename, "w") as fd:
136 fd.write(self.to_grl_string(name, version))
137
138 @classmethod
139 def from_runs(cls, runs):
140 return IOVSet(RANGEIOV_VAL(RunLumi(r, 0), RunLumi(r+1, 0)-1)
141 for r in sorted(runs))
142
143 @property
144 def chans_iovsets(self):
145 """
146 Returns ([channel1, c2, c3, ...], [channel 1 iovs, c2iovs, c3iovs, ...])
147 """
148 if not self:
149 return [], []
150 return zip(*sorted(self.by_channel.items()))
151
152 def empty(self, content=None):
153 """
154 Return an empty IOVSet, but keeping any metadata around. (Such as the
155 IOVType we were holding..)
156 """
157 return self.empty_maker()([] if content is None else content)
158
159 def empty_maker(self):
160 """
161 Indirection is used here in order to create new empty instances on
162 demand without keeping a reference to the original object alive.
163 """
164 iov_type = self.iov_type
165 origin = self.origin
166 def empty(content=None):
167 return type(self)([] if content is None else content,
168 iov_type=iov_type, origin=origin, parent=self)
169 return empty
170
171 def __reduce__(self):
172 """
173 Make an IOVSet pickleable (beware limitations)
174 """
175 types = set(type(x) for x in self)
176 if not types:
177 return IOVSet, ()
178
179 assert len(types) == 1, (
180 "Can only pickle IOVSets with only one type (got types=%r)" % types)
181
182 iov_type = type(self.first)
183 name = iov_type.__name__
184 variables = iov_type._fields[2:]
185
186 return restore_iovset, (type(self), name, variables, map(tuple, self))
187
188 def __hash__(self):
189 return hash(tuple(self))
190
191 def __repr__(self):
192 if self.time_based:
193 args = (len(self), self.duration, len(self.channels),
194 ("%.2f" % (self.duration / len(self.channels)))
195 if self.channels else "N/A")
196 plurals = ["" if x == 1 else "s" for x in args]
197 args = tuple(interleave(args, plurals))
198 return ("<%i IoV%s %.2f hour%s %i channel%s (avg=%s hr%s/c)>"
199 % args)
200
201 args = len(self), self.lb_counts, len(self.runs), len(self.channels)
202 plurals = ["" if x == 1 else "s" for x in args]
203 args = tuple(interleave(args, plurals))
204 return "<%i IoV%s %i lb%s %i run%s %i channel%s>" % args
205
206 def add(self, since, until, *args):
207 args = list(args)
208 if self and self[-1][1] == since and self[-1][2:] == args:
209 # Last until == since and the rest of the thing matches
210 #self[-1] = self[-1]._replace(until=until)
211 self[-1][1] = until
212 else:
213 self.append([since, until] + args)
214
215 def solidify(self, iov_type):
216 """
217 Because mutating lists is faster, once building an IOVSet is complete,
218 it should be 'solidified' into the desired IOV type
219 """
220 self.iov_type = type
221 return IOVSet(map(iov_type._make, self))
222
223 @property
224 def trim_iovs(self):
225 """
226 Ensure all IoVs start on lumiblock 1.
227 """
228 return self.empty(iov.trimmed for iov in self)
229
230 def add_old(self, iov):
231 """
232 Extends this result set by one iov. If it is adjacent (and equal)
233 to the previous IoV, then that IoV is extended instead.
234
235 This was deprecated because it is too slow compared to add() and solidify().
236 """
237 if self and self.last.connected_to(iov):
238 self[-1] = self[-1].merge(iov)
239 else:
240 self.append(iov)
241
242 @property
243 def time_based(self):
244 """
245 Looks at the first IoV to see if it is time based
246 """
247 if not self:
248 return False
249 try:
250 return self.first.is_time_based
251 except AttributeError:
252 return False
253
254 @property
255 def lb_counts(self):
256 """
257 Sum of the LB counts
258 """
259 return sum(iov.length for iov in self)
260
261 @property
262 def duration(self):
263 """
264 Duration of the sum of the time-based IoVs, in hours.
265 """
266 return self.lb_counts / 1e9 / 3600
267
268 def __getslice__(self, *args):
269 """
270 Slicing an IOVSet should return an IOVSet
271 """
272 return self.__class__(super(IOVSet, self).__getslice__(*args))
273
274 @property
275 def runs(self):
276 """
277 The set of runs present in this IOVSet
278 """
279 return set(iov.since.run for iov in self)
280
281 @property
282 def by_run(self):
283 """
284 Return a dictionary representing {run : iovs in run}
285 """
286 result = defaultdict(IOVSet)
287 for iov in self:
288 result[iov.run].append(iov)
289 return dict(result)
290
291 @property
292 def channels(self):
293 """
294 The set of channels this IOVSet represents
295 """
296 if not self or not hasattr(self[0], "channel"):
297 return set()
298 return set(iov.channel for iov in self)
299
300 @property
301 def by_channel(self):
302 """
303 Return a dictionary representing {channels : iovs with channelid}
304 """
305 # Import here to prevent circular imports
306 from ..iov_arrangement import split_by_channel
307 return split_by_channel(self)
308
309 @property
310 def first(self):
311 "The first IoV in the set"
312 assert self, (".first used in an empty IoV set. Is the range you are "
313 "trying to query valid?")
314 return self[0]
315
316 @property
317 def last(self):
318 "The last IoV in the set"
319 assert self, (".last used in an empty IoV set. Is the range you are "
320 "trying to query valid?")
321 return self[-1]
322
323 @property
324 def range_iov(self):
325 """
326 Returns an IoV object which represents the maximum range contained by
327 the IoVs.
328 """
329 since = min(i.since for i in self)
330 until = max(i.until for i in self)
331 return RANGEIOV_VAL(since, until)
332
333 def intersect_range(self, iov_range):
334 """
335 Returns a new IOVSet intersected with `iov_range` (since, until)
336 """
337 since, until = iov_range
338 iov_range = RANGEIOV_VAL(RunLumi(since), RunLumi(until))
339
340 intersected = (iov.intersect(iov_range) for iov in self)
341 return self.empty(iov for iov in intersected if iov)
342
343 def select_runs(self, *selected):
344 """
345 Pick IoVs which are in the set of runs `selected`
346 """
347 selected = set(selected)
348 return self.empty(iov for iov in self
349 if any(run in selected for run in iov.runs))
350
351 def select_channels(self, *selected):
352 """
353 Pick IoVs which are in the set of channels `selected`
354 """
355 selected = set(selected)
356 return self.empty(iov for iov in self if iov.channel in selected)
357
358 def select(self, **what):
359 def selected(iov):
360 return all(getattr(iov, key) == value
361 for key, value in what.iteritems())
362
363 return self.empty(iov for iov in self if selected(iov))
364
365
static const Attributes_t empty
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
STL class.
select_runs(self, *selected)
Definition iovset.py:343
__init__(self, *args, **kwargs)
Definition iovset.py:23
to_file(self, filename)
Definition iovset.py:109
solidify(self, iov_type)
Definition iovset.py:215
empty(self, content=None)
Definition iovset.py:152
to_grl(self, filename, name="unknown", version="unknown")
Definition iovset.py:134
add(self, since, until, *args)
Definition iovset.py:206
select_channels(self, *selected)
Definition iovset.py:351
from_grl_string(cls, data)
Definition iovset.py:121
logical_and(cls, *rhs_iovsets)
Definition iovset.py:56
logical_or(cls, *rhs_iovsets)
Definition iovset.py:43
from_iovsets(cls, iovsets)
Definition iovset.py:77
from_file(cls, filename)
Definition iovset.py:114
from_grl(cls, filename)
Definition iovset.py:126
select(self, **what)
Definition iovset.py:358
to_grl_string(self, name="unknown", version="unknown")
Definition iovset.py:130
pprint(self, where=stdout)
Definition iovset.py:95
intersect_range(self, iov_range)
Definition iovset.py:333
__getslice__(self, *args)
Definition iovset.py:268
STL class.
-event-from-file
Definition merge.py:1
restore_iovset(cls, name, variables, elements)
Definition iovset.py:15