Belle II Software  release-08-01-10
iov.py
1 #!/usr/bin/env python3
2 
3 
10 
11 """
12 conditions_db.iov
13 -----------------
14 
15 This module contains classes to work with validity intervals. There's a class
16 for a single interval, `IntervalOfValidity` and a class to manage a set of
17 validities, `IoVSet`, which can be used to manipulate iov ranges
18 """
19 
20 import math
21 from itertools import product
22 
23 
25  """
26  Interval of validity class to support set operations like union and
27  intersection.
28 
29  An interval of validity is a set of runs for which something is valid. An
30  IntervalOfValidity consists of a `first` valid run and a `final` valid run.
31 
32  Warning:
33  The `final` run is inclusive so the the validity is including the final run.
34 
35  Each run is identified by a experiment number and a run number. Accessing
36  `first` or `final` will return a tuple ``(experiment, run)`` but the
37  elements can also be accessed separately with `first_exp`, `first_exp`,
38  `final_exp` and `final_run`.
39 
40  For `final` there's a special case where either the run or both, the run and
41  the experiment number are infinite. This means the validity extends to all
42  values. If only the run number is infinite then it's valid for all further
43  runs in this experiment. If both are infinite the validity extends to everything.
44 
45  For simplicity ``-1`` can be passed in instead of infinity when creating objects.
46  """
47 
48  def __init__(self, *iov):
49  """Create a new object.
50 
51  It can be either instantiated by providing four values or one tuple/list
52  with four values for first_exp, first_run, final_exp, final_run
53  """
54  if len(iov) == 1 and isinstance(iov[0], (list, tuple)):
55  iov = iov[0]
56  if len(iov) != 4:
57  raise ValueError("A iov should have four values")
58 
59  self.__first__first = tuple(iov[:2])
60 
61  self.__final__final = tuple(math.inf if x == -1 else x for x in iov[2:])
62  if math.isinf(self.__final__final[0]) and not math.isinf(self.__final__final[1]):
63  raise ValueError(f"Unlimited final experiment but not unlimited run: {self}")
64  if self.__first__first[0] > self.__final__final[0]:
65  raise ValueError(f"First exp larger than final exp: {self}")
66  if self.__first__first[0] == self.__final__final[0] and self.__first__first[1] > self.__final__final[1]:
67  raise ValueError(f"First run larger than final run: {self}")
68  if self.__first__first[0] < 0 or self.__first__first[1] < 0:
69  raise ValueError(f"Negative first exp or run: {self}")
70 
71  @staticmethod
72  def always():
73  """Return an iov that is valid everywhere
74 
75  >>> IntervalOfValidity.always()
76  (0, 0, inf, inf)
77  """
78  return IntervalOfValidity(0, 0, -1, -1)
79 
80  @property
81  def first(self):
82  """Return the first valid experiment,run"""
83  return self.__first__first
84 
85  @property
86  def first_exp(self):
87  """Return the first valid experiment"""
88  return self.__first__first[0]
89 
90  @property
91  def first_run(self):
92  """Return the first valid run"""
93  return self.__first__first[1]
94 
95  @property
96  def final(self):
97  """Return the final valid experiment,run"""
98  return self.__final__final
99 
100  @property
101  def final_exp(self):
102  """Return the final valid experiment"""
103  return self.__final__final[0]
104 
105  @property
106  def final_run(self):
107  """Return the final valid run"""
108  return self.__final__final[1]
109 
110  def __repr__(self):
111  """Return a printable representation"""
112  return str(self.__first__first + self.__final__final)
113 
114  def __eq__(self, other):
115  """Check for equality"""
116  if not isinstance(other, IntervalOfValidity):
117  return NotImplemented
118  return (self.__first__first, self.__final__final) == (other.__first, other.__final)
119 
120  def __lt__(self, other):
121  """Sort by run values"""
122  if not isinstance(other, IntervalOfValidity):
123  return NotImplemented
124  return (self.__first__first, self.__final__final) < (other.__first, other.__final)
125 
126  def __and__(self, other):
127  """Intersection between iovs. Will return None if the payloads don't overlap"""
128  if not isinstance(other, IntervalOfValidity):
129  return NotImplemented
130  return self.intersectintersect(other)
131 
132  def __or__(self, other):
133  """Union between iovs. Will return None if the iovs don't overlap or
134  connect to each other"""
135  if not isinstance(other, IntervalOfValidity):
136  return NotImplemented
137  return self.unionunion(other, False)
138 
139  def __sub__(self, other):
140  """Difference between iovs. Will return None if nothing is left over"""
141  if not isinstance(other, IntervalOfValidity):
142  return NotImplemented
143  return self.subtractsubtract(other)
144 
145  def __hash__(self):
146  """Make object hashable"""
147  return hash((self.__first__first, self.__final__final))
148 
149  def subtract(self, other):
150  """Return a new iov with the validity of the other removed.
151  Will return None if everything is removed.
152 
153  Warning:
154  If the other iov is in the middle of the validity we will return a
155  tuple of two new iovs
156 
157  >>> iov1 = IntervalOfValidity(0,0,10,-1)
158  >>> iov2 = IntervalOfValidity(5,0,5,-1)
159  >>> iov1 - iov2
160  ((0, 0, 4, inf), (6, 0, 10, inf))
161  """
162  if other.first <= self.firstfirst and other.final >= self.finalfinalfinal:
163  # full overlap
164  return None
165  if other.first > self.firstfirst and other.final < self.finalfinalfinal:
166  # the one we want to remove is in the middle, return a pair of iovs
167  # by subtracting two extended once
168  iov1 = self.subtractsubtract(IntervalOfValidity(other.first + (-1, -1)))
169  iov2 = self.subtractsubtract(IntervalOfValidity((0, 0) + other.final))
170  return (iov1, iov2)
171  if other.first <= self.finalfinalfinal and other.final >= self.firstfirst:
172  # one sided overlap, figure out which side and calculate the remainder
173  if self.firstfirst < other.first:
174  end_run = other.first_run - 1
175  end_exp = other.first_exp if end_run >= 0 else other.first_exp - 1
176  return IntervalOfValidity(self.firstfirst + (end_exp, end_run))
177  else:
178  start_run = other.final_run + 1
179  start_exp = other.final_exp
180  if math.isinf(other.final_run):
181  start_exp += 1
182  start_run = 0
183  return IntervalOfValidity((start_exp, start_run) + self.finalfinalfinal)
184  # no overlap so return unchanged
185  return self
186 
187  def intersect(self, other):
188  """Intersection with another iov.
189 
190  Will return None if the payloads don't overlap
191 
192  >>> iov1 = IntervalOfValidity(1,0,2,5)
193  >>> iov2 = IntervalOfValidity(2,0,2,-1)
194  >>> iov3 = IntervalOfValidity(2,10,5,-1)
195  >>> iov1.intersect(iov2)
196  (2, 0, 2, 5)
197  >>> iov2.intersect(iov3)
198  (2, 10, 2, inf)
199  >>> iov3.intersect(iov1) is None
200  True
201 
202  """
203  if other.first <= self.finalfinalfinal and other.final >= self.firstfirst:
204  return IntervalOfValidity(*(max(self.firstfirst, other.first) + min(self.finalfinalfinal, other.final)))
205  return None
206 
207  def union(self, other, allow_startone=False):
208  """
209  Return the union with another iov.
210 
211  >>> iov1 = IntervalOfValidity(1,0,1,-1)
212  >>> iov2 = IntervalOfValidity(2,0,2,-1)
213  >>> iov3 = IntervalOfValidity(2,10,5,-1)
214  >>> iov1.union(iov2)
215  (1, 0, 2, inf)
216  >>> iov2.union(iov3)
217  (2, 0, 5, inf)
218  >>> iov3.union(iov1) is None
219  True
220 
221  Warning:
222  This method will return None if the iovs don't overlap or connect to
223  each other as no union can be formed.
224 
225  Parameters:
226  other (IntervalOfValidity): IoV to calculate the union with
227  allow_startone (bool): If True we will consider run 0 and run 1 the
228  first run in an experiment. This means that if one of the iovs has
229  un unlimited final run it can be joined with the other iov if the
230  experiment number increases and the iov starts at run 0 and 1. If
231  this is False just run 0 is considered the next run.
232 
233  >>> iov1 = IntervalOfValidity(0,0,0,-1)
234  >>> iov2 = IntervalOfValidity(1,1,1,-1)
235  >>> iov1.union(iov2, False) is None
236  True
237  >>> iov1.union(iov2, True)
238  (0, 0, 1, inf)
239 
240  """
241  # check the trivial case of overlapping
242  if other.first <= self.finalfinalfinal and other.final >= self.firstfirst:
243  return IntervalOfValidity(min(self.firstfirst, other.first) + max(self.finalfinalfinal, other.final))
244  # ok, let's do the less simple case where they don't overlap but join directly
245  for i1, i2 in (self, other), (other, self):
246  if (i1.first == (i2.final_exp, i2.final_run + 1) or
247  (math.isinf(i2.final_run) and (i1.first_exp == i2.final_exp + 1) and
248  (i1.first_run == 0 or allow_startone and i1.first_run == 1))):
249  return IntervalOfValidity(i2.first + i1.final)
250  # no union possible: not directly connected and not overlapping
251  return None
252 
253  def contains(self, exp, run):
254  """Check if a run is part of the validity"""
255  return self.firstfirst <= (exp, run) <= self.finalfinalfinal
256 
257  @property
258  def is_open(self):
259  """Check whether the iov is valid until infinity"""
260 
261  return self.finalfinalfinal == (math.inf, math.inf)
262 
263  @property
264  def tuple(self):
265  """Return the iov as a tuple with experiment/run numbers replaced with -1
266 
267  This is mostly helpful where infinity is not supported and is how the
268  intervals are represented in the database.
269 
270  >>> a = IntervalOfValidity.always()
271  >>> a
272  (0, 0, inf, inf)
273  >>> a.tuple
274  (0, 0, -1, -1)
275  """
276  return self.__first__first + tuple(-1 if math.isinf(x) else x for x in self.__final__final)
277 
278 
279 class IoVSet:
280  """A set of iovs.
281 
282  This class allows to combine iovs into a set. New iovs can be added with
283  `add()` and will be combined with existing iovs if possible.
284 
285  The final, minimal number of iovs can be obtained with the `iovs` property
286 
287  >>> a = IoVSet()
288  >>> a.add((0,0,0,2))
289  >>> a.add((0,3,0,5))
290  >>> a.add((0,8,0,9))
291  >>> a
292  {(0, 0, 0, 5), (0, 8, 0, 9)}
293  """
294 
295  def __init__(self, iterable=None, *, allow_overlaps=False, allow_startone=False):
296  """Create a new set.
297 
298  >>> a = IoVSet([IntervalOfValidity(3,6,3,-1), (0,0,3,5)])
299  >>> a
300  {(0, 0, 3, inf)}
301 
302  Parameters:
303  iterable: if not None it should be an iterable of IntervalOfValidity
304  objects or anything that can be converted to an IntervalOfValidity.
305  allow_overlaps (bool): If False adding which overlaps with any
306  existing iov in the set will raise a ValueError.
307  allow_startone (bool): If True also join iovs if one covers the
308  whole experiment and the next one starts at run 1 in the next
309  experiment. If False they will only be joined if the next one
310  starts at run 0.
311  """
312 
313  self.__iovs__iovs = set()
314 
315  self.__allow_overlaps__allow_overlaps = allow_overlaps
316 
318  self.__allow_startone__allow_startone = allow_startone
319  if iterable is not None:
320  for element in iterable:
321  self.addadd(element)
322 
323  def add(self, iov, allow_overlaps=None):
324  """
325  Add a new iov to the set.
326 
327  The new iov be combined with existing iovs if possible. After the
328  operation the set will contain the minimal amount of separate iovs
329  possible to represent all added iovs
330 
331  >>> a = IoVSet()
332  >>> a.add((0, 0, 0, 2))
333  >>> a.add((0, 3, 0, 5))
334  >>> a.add((0, 8, 0, 9))
335  >>> a
336  {(0, 0, 0, 5), (0, 8, 0, 9)}
337  >>> a.add(IoVSet([(10, 0, 10, 1), (10, 2, 10, -1)]))
338  >>> a
339  {(0, 0, 0, 5), (0, 8, 0, 9), (10, 0, 10, inf)}
340 
341  Be aware, by default it's not possible to add overlapping iovs to the set.
342  This can be changed either on construction or per `add` call using
343  ``allow_overlap``
344 
345  >>> a.add((0, 2, 0, 3))
346  Traceback (most recent call last):
347  ...
348  ValueError: Overlap between (0, 0, 0, 5) and (0, 2, 0, 3)
349  >>> a.add((0, 2, 0, 3), allow_overlaps=True)
350  >>> a
351  {(0, 0, 0, 5), (0, 8, 0, 9), (10, 0, 10, inf)}
352 
353  Parameters:
354  iov (Union[IoVSet, IntervalOfValidity, tuple(int)]): IoV or
355  set of IoVs to add to this set
356  allow_overlaps (bool): Can be used to override global overlap setting
357  of this set to allow/restrict overlaps for a single insertion
358  operation
359 
360  Warning:
361  This method modifies the set in place
362  """
363  # check whether we override overlap settings
364  if allow_overlaps is None:
365  allow_overlaps = self.__allow_overlaps__allow_overlaps
366  # we can add a set to a set :D
367  if isinstance(iov, IoVSet):
368  for element in iov:
369  self.addadd(element, allow_overlaps)
370  return
371  # make sure it's actually an IoV, this will raise an error on failure
372  if not isinstance(iov, IntervalOfValidity):
373  iov = IntervalOfValidity(iov)
374  # and now check for all existing iovs ... (but use a copy since we modify the set)
375  for existing in list(self.__iovs__iovs):
376  # if there's an overlap to the new iov
377  if (not allow_overlaps) and (existing & iov):
378  raise ValueError(f"Overlap between {existing} and {iov}")
379  # and if they can be combined to a bigger iov
380  combined = existing.union(iov, self.__allow_startone__allow_startone)
381  # if we now have a combined iov, remove the one that we were able to
382  # combine it with from the existing iovs because we now check
383  # against the combined one. Since the only way to add a new iov is
384  # this loop we know all previous existing iovs we checked before
385  # didn't have a union with this new iov or any other existing iovs
386  # so if the just check the remaining iovs against the new combined
387  # one we can cascade combine all iovs in one go.
388  if combined is not None:
389  self.__iovs__iovs.remove(existing)
390  iov = combined
391  # done, we now have a new iov which combines all existing iovs it had an
392  # overlap with and we removed the existing iovs so nothing else to do
393  # but add the iov back in the list
394  self.__iovs__iovs.add(iov)
395 
396  def remove(self, iov):
397  """Remove an iov or a set of iovs from this set
398 
399  After this operation the set will not be valid for the given iov or set
400  of iovs:
401 
402  >>> a = IoVSet()
403  >>> a.add((0,0,10,-1))
404  >>> a.remove((1,0,1,-1))
405  >>> a.remove((5,0,8,5))
406  >>> a
407  {(0, 0, 0, inf), (2, 0, 4, inf), (8, 6, 10, inf)}
408  >>> a.remove(IoVSet([(3,0,3,10), (3,11,3,-1)]))
409  >>> a
410  {(0, 0, 0, inf), (2, 0, 2, inf), (4, 0, 4, inf), (8, 6, 10, inf)}
411 
412  Parameters:
413  iov (Union[IoVSet, IntervalOfValidity, tuple(int)]): IoV or
414  set of IoVs to remove from this set
415 
416  Warning:
417  This method modifies the set in place
418  """
419  # we can remove a set from a set :D
420  if isinstance(iov, IoVSet):
421  for element in iov:
422  self.removeremove(element)
423  return
424  # make sure it's actually an IoV, this will raise an error on failure
425  if not isinstance(iov, IntervalOfValidity):
426  iov = IntervalOfValidity(iov)
427  # and subtract the iov from all existing iovs
428  for existing in list(self.__iovs__iovs):
429  delta = existing - iov
430  if delta != existing:
431  self.__iovs__iovs.remove(existing)
432  if isinstance(delta, tuple):
433  # got two new iovs, apparently we split the old one
434  for new in delta:
435  self.__iovs__iovs.add(new)
436  elif delta is not None:
437  self.__iovs__iovs.add(delta)
438 
439  def intersect(self, iov):
440  """Intersect this set with another set and return a new set
441  which is valid exactly where both sets have been valid before
442 
443  >>> a = IoVSet()
444  >>> a.add((0,0,10,-1))
445  >>> a.intersect((5,0,20,-1))
446  {(5, 0, 10, inf)}
447  >>> a.intersect(IoVSet([(0,0,3,-1), (9,0,20,-1)]))
448  {(0, 0, 3, inf), (9, 0, 10, inf)}
449 
450  Parameters:
451  iov (Union[IoVSet, IntervalOfValidity, tuple(int)]): IoV or
452  set of IoVs to intersect with this set
453  """
454  if not isinstance(iov, (IoVSet, IntervalOfValidity)):
455  iov = IntervalOfValidity(iov)
456  if isinstance(iov, IntervalOfValidity):
457  iov = IoVSet([iov])
458 
459  # ok for all combinations a,b from set1 and set2 check the intersection
460  # and if not empty add to the result
461  result = IoVSet()
462  for a, b in product(self.iovsiovs, iov.iovs):
463  c = a & b
464  if c:
465  result.add(c)
466  return result
467 
468  def contains(self, iov):
469  """
470  Check if an iov is fully covered by the set
471 
472  >>> a = IoVSet([(0,0,2,-1), (5,0,5,-1)])
473  >>> a.contains((0,0,1,-1))
474  True
475  >>> a.contains(IntervalOfValidity(0,0,3,2))
476  False
477  >>> a.contains(IoVSet([(0,1,1,23), (5,0,5,23)]))
478  True
479  >>> a.contains(IoVSet([(0,1,1,23), (5,0,6,23)]))
480  False
481  >>> a.contains((3,0,4,-1))
482  False
483 
484  Parameters:
485  iov (Union[IoVSet, IntervalOfValidity, tuple(int)]): IoV or
486  set of IoVs to be checked
487 
488  Returns:
489  True if the full iov or all the iovs in the given set are fully
490  present in this set
491  """
492  # check if the whole set is in this set: all iovs need to be in here
493  if isinstance(iov, IoVSet):
494  return all(e in self for e in iov)
495  # make sure it's actually an IoV, this will raise an error on failure
496  if not isinstance(iov, IntervalOfValidity):
497  iov = IntervalOfValidity(iov)
498  # and then check all iovs in the set if they cover it
499  for existing in self.__iovs__iovs:
500  if iov - existing is None:
501  return True
502  return False
503 
504  def overlaps(self, iov):
505  """Check if the given iov overlaps with this set.
506 
507  In contrast to `contains` this doesn't require the given iov to be fully
508  covered. It's enough if the any run covered by the iov is also covered
509  by this set.
510 
511  >>> a = IoVSet([(0,0,2,-1), (5,0,5,-1)])
512  >>> a.overlaps((0,0,1,-1))
513  True
514  >>> a.overlaps(IntervalOfValidity(0,0,3,2))
515  True
516  >>> a.overlaps(IoVSet([(0,1,1,23), (5,0,5,23)]))
517  True
518  >>> a.overlaps(IoVSet([(0,1,1,23), (5,0,6,23)]))
519  True
520  >>> a.overlaps((3,0,4,-1))
521  False
522 
523  Parameters:
524  iov (Union[IoVSet, IntervalOfValidity, tuple(int)]): IoV or
525  set of IoVs to be checked
526 
527  Returns:
528  True if the iov or any of the iovs in the given set overlap with any
529  iov in this set
530  """
531  if not isinstance(iov, (IoVSet, IntervalOfValidity)):
532  iov = IntervalOfValidity(iov)
533  if isinstance(iov, IntervalOfValidity):
534  iov = IoVSet([iov])
535 
536  for a, b in product(self.iovsiovs, iov.iovs):
537  c = a & b
538  if c:
539  return True
540  return False
541 
542  def copy(self):
543  """Return a copy of this set"""
544  copy = IoVSet(allow_overlaps=self.__allow_overlaps__allow_overlaps, allow_startone=self.__allow_startone__allow_startone)
545  copy.__iovs = set(self.__iovs__iovs)
546  return copy
547 
548  def clear(self):
549  """Clear all iovs from this set"""
550  self.__iovs__iovs = {}
551 
552  @property
553  def iovs(self):
554  """Return the set of valid iovs"""
555  return self.__iovs__iovs
556 
557  @property
558  def first(self):
559  """Return the first run covered by this iov set
560 
561  >>> a = IoVSet([(3,0,3,10), (10,11,10,23), (0,0,2,-1), (5,0,5,-1)])
562  >>> a.first
563  (0, 0)
564  """
565  if not self.__iovs__iovs:
566  return None
567  return min(self.iovsiovs).first
568 
569  @property
570  def final(self):
571  """Return the final run covered by this iov set
572 
573  >>> a = IoVSet([(3,0,3,10), (10,11,10,23), (0,0,2,-1), (5,0,5,-1)])
574  >>> a.final
575  (10, 23)
576  """
577  if not self.__iovs__iovs:
578  return None
579  return max(self.iovsiovs).final
580 
581  @property
582  def gaps(self):
583  """Return the gaps in the set. Any area not covered between the first
584  point of validity and the last
585 
586  >>> a = IoVSet([(0,0,2,-1)])
587  >>> a.gaps
588  {}
589  >>> b = IoVSet([(0,0,2,-1), (5,0,5,-1)])
590  >>> b.gaps
591  {(3, 0, 4, inf)}
592  >>> c = IoVSet([(0,0,2,-1), (5,0,5,-1), (10,3,10,6)])
593  >>> c.gaps
594  {(3, 0, 4, inf), (6, 0, 10, 2)}
595  """
596  if len(self.__iovs__iovs) < 2:
597  return IoVSet()
598 
599  full_range = IoVSet([self.firstfirst + self.finalfinal])
600  return full_range - self
601 
602  def __bool__(self):
603  """Return True if the set is not empty
604 
605 
606  >>> a = IoVSet()
607  >>> a.add((0,0,1,-1))
608  >>> bool(a)
609  True
610  >>> a.clear()
611  >>> a
612  {}
613  >>> bool(a)
614  False
615  """
616  return len(self.__iovs__iovs) > 0
617 
618  def __contains__(self, iov):
619  """Check if an iov is fully covered by the set"""
620  return self.containscontains(iov)
621 
622  def __and__(self, other):
623  """Return a new set that is the intersection between two sets
624 
625  >>> a = IoVSet([(0,0,1,-1)])
626  >>> a & (1,0,2,-1)
627  {(1, 0, 1, inf)}
628  """
629  return self.intersectintersect(other)
630 
631  def __or__(self, other):
632  """
633  Return a new set that is the combination of two sets: The new set will
634  be valid everywhere any of the two sets were valid.
635 
636  No check for overlaps will be performed but the result will inherit the
637  settings for further additions from the first set
638 
639  >>> a = IoVSet([(0,0,1,-1)])
640  >>> a | (1,0,2,-1)
641  {(0, 0, 2, inf)}
642  >>> a | (3,0,3,-1)
643  {(0, 0, 1, inf), (3, 0, 3, inf)}
644  """
645  copy = self.copycopy()
646  copy.add(other, allow_overlaps=True)
647  return copy
648 
649  def __sub__(self, other):
650  """
651  Return a new set which is only valid for where a is valid but not b.
652 
653  See `remove` but this will not modify the set in place
654 
655  >>> a = IoVSet([(0,0,-1,-1)])
656  >>> a - (1,0,2,-1)
657  {(0, 0, 0, inf), (3, 0, inf, inf)}
658  >>> a - (0,0,3,-1) - (10,0,-1,-1)
659  {(4, 0, 9, inf)}
660  >>> IoVSet([(0,0,1,-1)]) - (2,0,2,-1)
661  {(0, 0, 1, inf)}
662  """
663  copy = self.copycopy()
664  copy.remove(other)
665  return copy
666 
667  def __iter__(self):
668  """Loop over the set of iovs"""
669  return iter(self.__iovs__iovs)
670 
671  def __len__(self):
672  """Return the number of validity intervals in this set"""
673  return len(self.__iovs__iovs)
674 
675  def __repr__(self):
676  """Return a printable representation"""
677  return '{' + ', '.join(str(e) for e in sorted(self.__iovs__iovs)) + '}'
def contains(self, exp, run)
Definition: iov.py:253
def subtract(self, other)
Definition: iov.py:149
def intersect(self, other)
Definition: iov.py:187
final
Doxygen complains without this string.
Definition: iov.py:261
def union(self, other, allow_startone=False)
Definition: iov.py:207
def __init__(self, *iov)
Definition: iov.py:48
__first
tuple with the first valid exp, run
Definition: iov.py:59
def __and__(self, other)
Definition: iov.py:126
def __sub__(self, other)
Definition: iov.py:139
__final
tuple with the final valid exp, run
Definition: iov.py:61
def __contains__(self, iov)
Definition: iov.py:618
def __init__(self, iterable=None, *allow_overlaps=False, allow_startone=False)
Definition: iov.py:295
__iovs
The set of iovs.
Definition: iov.py:313
def contains(self, iov)
Definition: iov.py:468
def add(self, iov, allow_overlaps=None)
Definition: iov.py:323
def __bool__(self)
Definition: iov.py:602
def __iter__(self)
Definition: iov.py:667
def first(self)
Definition: iov.py:558
def remove(self, iov)
Definition: iov.py:396
def __or__(self, other)
Definition: iov.py:631
def final(self)
Definition: iov.py:570
def iovs(self)
Definition: iov.py:553
def gaps(self)
Definition: iov.py:582
def __len__(self)
Definition: iov.py:671
def __repr__(self)
Definition: iov.py:675
__allow_startone
Whether or not run 1 will be also considered the first run when combining iovs between experiments.
Definition: iov.py:318
def __and__(self, other)
Definition: iov.py:622
def clear(self)
Definition: iov.py:548
__allow_overlaps
Whether or not we raise an error on overlaps.
Definition: iov.py:315
def __sub__(self, other)
Definition: iov.py:649
def intersect(self, iov)
Definition: iov.py:439
def copy(self)
Definition: iov.py:542
def overlaps(self, iov)
Definition: iov.py:504