ATLAS Offline Software
Loading...
Searching...
No Matches
Logic.py
Go to the documentation of this file.
1# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
2
3from __future__ import annotations
4
5__all__ = ['Logic', 'Not']
6
7from copy import copy
8from enum import Enum
9
10from AthenaCommon.Logging import logging
11log = logging.getLogger(__name__)
12
13
14class LogicType(Enum):
15 NONE = ('')
16 AND = ('&')
17 OR = ('|')
18 NOT = ('!')
19 THRESHOLD = ('T')
20 INTERNAL = ('I')
21
22 def __init__(self, opSymbol: str):
23 self.opSymbol: str = opSymbol
24
25 def __str__(self):
26 return self.opSymbol
27
28 def __repr__(self):
29 return self.name
30
31
32class Logic:
33
34 __slots__ = ['logicType', 'subConditions']
35
36 @staticmethod
37 def Not(x: Logic) -> Logic:
38 a = Logic()
39 a.logicType = LogicType.NOT
40 a.subConditions.append(x)
41 return a
42
43 @staticmethod
44 def stripBunchGroups(theLogic: Logic) -> tuple[Logic, list[str]]:
45 subConds: list['Logic'] = []
46 bunchGroups: list[str] = []
47 for sc in theLogic.subConditions:
48 if sc.logicType is LogicType.INTERNAL and sc.name().startswith('BGRP'):
49 bunchGroups.append(sc.name())
50 else:
51 subConds.append(sc)
52
53 if len(subConds) > 1:
54 theLogic.subConditions = subConds
55 return (theLogic, bunchGroups)
56
57 if len(subConds) == 1:
58 return (subConds[0], bunchGroups)
59
60 raise RuntimeError("Item with logic '%s' has only internal triggers defined" % theLogic)
61
62 def __init__(self, logicType: LogicType = LogicType.NONE):
63 self.logicType: LogicType = logicType
64 self.subConditions: list[Logic] = [] # holds Logic instances
65
66 def name(self) -> str:
67 """Jut the symbol for the logic type, e.g. '&' for AND, '|' for OR, '!' for NOT
68 Will be overridden in derived classes to return the name of the threshold or internal trigger
69 """
70 return self.logicType.opSymbol
71
72 def __or__(self, x: Logic) -> Logic:
73 newLogic = Logic(logicType=LogicType.OR)
74
75 if self.logicType is LogicType.OR:
76 newLogic.subConditions += copy(self.subConditions)
77 else:
78 newLogic.subConditions += [copy(self)]
79
80 if x.logicType is LogicType.OR:
81 newLogic.subConditions += copy(x.subConditions)
82 else:
83 newLogic.subConditions += [copy(x)]
84
85 return newLogic
86
87 def __and__(self, x: Logic) -> Logic:
88 newLogic = Logic(logicType=LogicType.AND)
89
90 if self.logicType is LogicType.AND:
91 newLogic.subConditions += copy(self.subConditions)
92 else:
93 newLogic.subConditions += [copy(self)]
94
95 if x.logicType is LogicType.AND:
96 newLogic.subConditions += copy(x.subConditions)
97 else:
98 newLogic.subConditions += [copy(x)]
99
100 return newLogic
101
102 def __not__(self, x: Logic) -> Logic:
103 if self.logicType is LogicType.NONE:
104 self.logicType = LogicType.NOT
105 if self.logicType is LogicType.NOT:
106 if len(self.subConditions) == 1:
107 log.debug('not is a unary operator, ignore it')
108 else:
109 self.subConditions.append(x)
110 return self
111
112 def __str__(self):
113 if self.logicType is LogicType.NONE:
114 log.error('LogicType NONE should not have an instance')
115 return ''
116
117 if self.logicType is LogicType.NOT:
118 if len(self.subConditions) == 1:
119 if self.subConditions[0].logicType is LogicType.NONE:
120 return '!' + str(self.subConditions[0])
121 else:
122 return '!(' + str(self.subConditions[0]) + ')'
123 log.error('Logic NOT must have exactly one element but has %i', len(self.subConditions))
124 return ''
125
126 if self.logicType in (LogicType.AND, LogicType.OR):
127 s = ''
128 if len(self.subConditions) <= 1:
129 log.error('Logic AND/OR must have more than one sub element but has %i: %r', len(self.subConditions), self.subConditions)
130 return ''
131 else:
132 for (i, a) in enumerate(self.subConditions):
133 if i > 0:
134 s += ' ' + str(self.logicType) + ' '
135 if a.logicType in (LogicType.THRESHOLD, LogicType.INTERNAL, LogicType.NOT):
136 s += str(a)
137 else:
138 s += '(' + str(a) + ')'
139 return s
140 return ''
141
142 def __lt__(self, other):
143 if not isinstance(other, Logic):
144 return NotImplemented
145 return self.name() < other.name()
146
147 def thresholdNames(self, include_bgrp: bool = False) -> list[str]:
148 """List of names of thresholds in this logic
149
150 Args:
151 include_bgrp (bool, optional): if True,include bunch group names in the list. Defaults to False.
152
153 Returns:
154 list[str]: list of threshold names
155 """
156 names: set[str] = set()
157 for sc in self.subConditions:
158 names.update(sc.thresholdNames(include_bgrp))
159 return sorted(list(names))
160
161 def conditions(self, include_internal: bool = False) -> list[Logic]:
162 """Sorted list of names of conditions in this logic"""
163 cond: set[Logic] = set([])
164 for sc in self.subConditions:
165 cond.update(sc.conditions(include_internal))
166 return sorted(list(cond))
167
168 def normalize(self):
169 if self.logicType in (LogicType.AND, LogicType.OR):
170 mylogic = self.logicType
171 newconditions = []
172 for c in self.subConditions:
173 if c.logicType == mylogic: # X&(A&B) or X|(A|B)
174 # expand it to X&A&B or X|A|B
175 c.normalize()
176 newconditions.extend(c.subConditions)
177 else:
178 newconditions.append(c)
179 self.subConditions = newconditions
180
181
182def Not(x: Logic) -> Logic:
183 return Logic.Not(x)
__init__(self, str opSymbol)
Definition Logic.py:22
list[Logic] conditions(self, bool include_internal=False)
Definition Logic.py:161
__init__(self, LogicType logicType=LogicType.NONE)
Definition Logic.py:62
__lt__(self, other)
Definition Logic.py:142
Logic __or__(self, Logic x)
Definition Logic.py:72
Logic __not__(self, Logic x)
Definition Logic.py:102
Logic __and__(self, Logic x)
Definition Logic.py:87
Logic Not(Logic x)
Definition Logic.py:37
list[str] thresholdNames(self, bool include_bgrp=False)
Definition Logic.py:147
tuple[Logic, list[str]] stripBunchGroups(Logic theLogic)
Definition Logic.py:44
STL class.
Logic Not(Logic x)
Definition Logic.py:182