Belle II Software development
iov.py
1#!/usr/bin/env python3
2
3
10
11"""
12conditions_db.iov
13-----------------
14
15This module contains classes to work with validity intervals. There's a class
16for a single interval, `IntervalOfValidity` and a class to manage a set of
17validities, `IoVSet`, which can be used to manipulate iov ranges
18"""
19
20import math
21from 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 = tuple(iov[:2])
60
61 self.__final = tuple(math.inf if x == -1 else x for x in iov[2:])
62 if math.isinf(self.__final[0]) and not math.isinf(self.__final[1]):
63 raise ValueError(f"Unlimited final experiment but not unlimited run: {self}")
64 if self.__first[0] > self.__final[0]:
65 raise ValueError(f"First exp larger than final exp: {self}")
66 if self.__first[0] == self.__final[0] and self.__first[1] > self.__final[1]:
67 raise ValueError(f"First run larger than final run: {self}")
68 if self.__first[0] < 0 or self.__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
84
85 @property
86 def first_exp(self):
87 """Return the first valid experiment"""
88 return self.__first[0]
89
90 @property
91 def first_run(self):
92 """Return the first valid run"""
93 return self.__first[1]
94
95 @property
96 def final(self):
97 """Return the final valid experiment,run"""
98 return self.__final
99
100 @property
101 def final_exp(self):
102 """Return the final valid experiment"""
103 return self.__final[0]
104
105 @property
106 def final_run(self):
107 """Return the final valid run"""
108 return self.__final[1]
109
110 def __repr__(self):
111 """Return a printable representation"""
112 return str(self.__first + self.__final)
113
114 def __eq__(self, other):
115 """Check for equality"""
116 if not isinstance(other, IntervalOfValidity):
117 return NotImplemented
118 return (self.__first, self.__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, self.__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.intersect(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.union(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.subtract(other)
144
145 def __hash__(self):
146 """Make object hashable"""
147 return hash((self.__first, self.__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.first and other.final >= self.finalfinal:
163 # full overlap
164 return None
165 if other.first > self.first and other.final < self.finalfinal:
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.subtract(IntervalOfValidity(other.first + (-1, -1)))
169 iov2 = self.subtract(IntervalOfValidity((0, 0) + other.final))
170 return (iov1, iov2)
171 if other.first <= self.finalfinal and other.final >= self.first:
172 # one sided overlap, figure out which side and calculate the remainder
173 if self.first < 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.first + (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.finalfinal)
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.finalfinal and other.final >= self.first:
204 return IntervalOfValidity(*(max(self.first, other.first) + min(self.finalfinal, 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.finalfinal and other.final >= self.first:
243 return IntervalOfValidity(min(self.first, other.first) + max(self.finalfinal, 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.first <= (exp, run) <= self.finalfinal
256
257 @property
258 def is_open(self):
259 """Check whether the iov is valid until infinity"""
260
261 return self.finalfinal == (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 + tuple(-1 if math.isinf(x) else x for x in self.__final)
277
278
279class 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 = set()
314
315 self.__allow_overlaps = allow_overlaps
316
318 self.__allow_startone = allow_startone
319 if iterable is not None:
320 for element in iterable:
321 self.add(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
366 # we can add a set to a set :D
367 if isinstance(iov, IoVSet):
368 for element in iov:
369 self.add(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):
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)
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.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.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.remove(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):
429 delta = existing - iov
430 if delta != existing:
431 self.__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.add(new)
436 elif delta is not None:
437 self.__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.iovs, 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:
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.iovs, 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_startone=self.__allow_startone)
545 copy.__iovs = set(self.__iovs)
546 return copy
547
548 def clear(self):
549 """Clear all iovs from this set"""
550 self.__iovs = {}
551
552 @property
553 def iovs(self):
554 """Return the set of valid iovs"""
555 return self.__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:
566 return None
567 return min(self.iovs).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:
578 return None
579 return max(self.iovs).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) < 2:
597 return IoVSet()
598
599 full_range = IoVSet([self.first + self.final])
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) > 0
617
618 def __contains__(self, iov):
619 """Check if an iov is fully covered by the set"""
620 return self.contains(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.intersect(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.copy()
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.copy()
664 copy.remove(other)
665 return copy
666
667 def __iter__(self):
668 """Loop over the set of iovs"""
669 return iter(self.__iovs)
670
671 def __len__(self):
672 """Return the number of validity intervals in this set"""
673 return len(self.__iovs)
674
675 def __repr__(self):
676 """Return a printable representation"""
677 return '{' + ', '.join(str(e) for e in sorted(self.__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