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