ATLAS Offline Software
Loading...
Searching...
No Matches
afp.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3import os
4pbeastDefaultServer = 'https://pc-atlas-www.cern.ch' if os.getenv('PBEAST_SERVER_HTTPS_PROXY', '').startswith('atlasgw') else 'https://atlasop.cern.ch'
5pbeastServer = os.getenv('PBEAST_SERVER', pbeastDefaultServer)
6
7import libpbeastpy; pbeast = libpbeastpy.ServerProxy(pbeastServer)
8import logging; log = logging.getLogger("DCSCalculator2.variable")
9
10from bisect import bisect
11from collections.abc import Collection, Iterable, Mapping
12import functools
13from itertools import chain
14
15from DQUtils.events import process_iovs
16from DQUtils.general import timer
17from DQUtils.sugar import IOVSet, RANGEIOV_VAL, RunLumi, TimestampType, define_iov_type, make_iov_type
18
19from DCSCalculator2 import config
20from DCSCalculator2.consts import RED, YELLOW, GREEN, GREY, GOOD, BAD, EMPTY
21from DCSCalculator2.libcore import map_channels
22from DCSCalculator2.subdetector import DCSC_DefectTranslate_Subdetector
23from DCSCalculator2.variable import DefectIOV, GoodIOV, DCSC_Variable
24
25class DCSC_Variable_With_Mapping(DCSC_Variable):
26 def __init__(self, folder, evaluator, *, mapping=None, **kwargs):
27 super().__init__(folder, evaluator, **kwargs)
28 self.mapping = mapping
29
30 def read(self, query_range, folder_base, folder_name):
31 result = super().read(query_range, folder_base, folder_name)
32 if self.mapping is not None:
33 result = map_channels(result, self.mapping, folder_name)
34 return result
35
37 def make_good_iov(self, iov):
38 giov = []
39 for channel, goodness in self.evaluator(iov):
40 current = GoodIOV(iov.since, iov.until, channel, goodness)
41 current._orig_iov = iov
42 giov.append(current)
43 return giov
44
45 def make_good_iovs(self, iovs):
46 results = []
47 for iov in iovs:
48 results.append(self.make_good_iov(iov))
49 return IOVSet(sum(zip(*results), ())) # Sort by channel first
50
51class DCSC_Merged_Variable(DCSC_Variable):
52 def __init__(self, folders, evaluator, *, mapping={}, **kwargs):
53 folder_merge = ','.join(folders)
54
55 super().__init__(folder_merge, evaluator, **kwargs)
56 self.folder_names = folders
57 self.mapping = mapping
58
59 def read(self, query_range, folder_base, folder_names):
60 var_iovs = []
61 folders = folder_names.split(',')
62
63 for folder in folders:
64 iovs = super().read(query_range, folder_base, folder)
65 if folder in self.mapping:
66 iovs = map_channels(iovs, self.mapping[folder], folder)
67 var_iovs.append(iovs)
68
69 iovs = self.merge_input_variables(*var_iovs)
70
71 if config.opts.check_input_time:
72 self.print_time_info(iovs)
73
74 if log.isEnabledFor(logging.INFO):
75 input_hash = hash(iovs)
76 self.input_hashes.append(input_hash)
77 #log.info(" -> Input hash: % 09x (len=%i)", input_hash, len(iovs))
78
79 return iovs
80
81 def merge_input_variables(self, *inputs):
82 type_bases = [iovs.iov_type for iovs in inputs]
83 base_names = [base.__name__[:-4] for base in type_bases]
84 attributes = [base.lower() for base in base_names]
85 clazz = make_iov_type('MERGER_OF_' + '_AND_'.join(base_names), tuple(['channel'] + attributes))
86
87 inputs_by_channel = [iovs.by_channel for iovs in inputs]
88 all_channels = sorted(set(y for x in inputs_by_channel for y in x.keys()))
89
90 result = []
91 for channel in all_channels:
92 c_inputs = [x[channel] for x in inputs_by_channel]
93 result.extend(iov for iov in self.merge_inputs(clazz, channel, *c_inputs))
94
95 return IOVSet(result, iov_type=clazz, origin=self.folder_names)
96
97 def merge_inputs(self, clazz, channel, *inputs):
98 result = [clazz(since, until, channel, *states) for since, until, states in process_iovs(*inputs)]
99 return IOVSet(result, iov_type=clazz, origin=self.folder_names)
100
101@define_iov_type
102def PBeastIOV(channel, value):
103 "Stores the value of an object's attribute queried from pBeast"
104
105class TDAQC_Variable(DCSC_Variable):
106 """
107 A variable which reads data from pBeast.
108 """
109 TIME_RATIO = 1e3
110
111 @staticmethod
112 def timeCOOL2PBeast(timestamp):
113 return int(timestamp/TDAQC_Variable.TIME_RATIO)
114
115 @staticmethod
116 def timePBeast2COOL(timestamp):
117 return TimestampType(timestamp*TDAQC_Variable.TIME_RATIO)
118
119 def __init__(self, query, evaluator, *, regex=False, mapping=dict(), force_mapping=False, empty_value=None):
120 super().__init__(query, evaluator)
121 self.regex = regex
122 self.mapping = mapping
123 self.force_mapping = force_mapping
124 self.empty_value = empty_value
125 self.query = query
126 self.partition, self.className, self.attribute, self.path = query.split('.', 3)
127 self.input_hashes = []
128
129 def __repr__(self):
130 return f"<TDAQCVariable {self.query}>"
131
132 def read(self, query_range, query, *, regex=False):
133 """
134 Read the relevant data from pBeast for this variable, and convert them to COOL-like format
135 """
136 partition, className, attribute, path = query.split('.', 3)
137
138 log.info(f"Querying pBeast object{'s using regex' if regex else ''} {query}")
139
140 since, until = query_range
141 since, until = TDAQC_Variable.timeCOOL2PBeast(since), TDAQC_Variable.timeCOOL2PBeast(until)
142
143 query_data = pbeast.get_data(partition, className, attribute, path, regex, since, until)
144 data = dict.fromkeys(self.mapping.keys()) if self.force_mapping else dict()
145 if query_data:
146 data.update(query_data[0].data)
147
148 def instantiate(since, until, channel, value):
149 if isinstance(value, list):
150 value = tuple(value)
151 return PBeastIOV(TDAQC_Variable.timePBeast2COOL(since), TDAQC_Variable.timePBeast2COOL(until), channel, value)
152
153 iovs = []
154 for channel, entries in data.items():
155 if not entries:
156 iovs.append(PBeastIOV(*query_range, channel, self.empty_value))
157 continue
158 first = entries[0]
159 since = first.ts
160 value = first.value
161 for point in entries:
162 if point.value == value:
163 continue
164 iovs.append(instantiate(since, point.ts, channel, value))
165 since = point.ts
166 value = point.value
167 last = entries[-1]
168 iovs.append(instantiate(since, last.ts, channel, value))
169 iovs = IOVSet(iovs, iov_type=PBeastIOV, origin=query)
170
171 if log.isEnabledFor(logging.INFO):
172 input_hash = hash(iovs)
173 self.input_hashes.append(input_hash)
174 log.info(" -> Input hash: % 09x (len=%i)", input_hash, len(iovs))
175
176 return iovs
177
178 def calculate_good_iovs(self, lbtime, subdetector):
179 """
180 Calculate LB-wise "good" states
181 """
182
183 self.subdetector = subdetector
184
185 since, until = lbtime.first, lbtime.last
187 query_range = RANGEIOV_VAL(since.since, until.until)
188 else:
189 query_range = RANGEIOV_VAL(RunLumi(since.Run, since.LumiBlock),
190 RunLumi(until.Run, until.LumiBlock))
191
192 # Read the database
193 iovs = self.read(query_range, self.query, regex=self.regex)
194 # Decide the states of the input iovs
195 iovs = self.make_good_iovs(iovs)
196 # Apply a mapping for input channels if necessary
197 iovs = self.map_input_channels(iovs)
198
199 if self.timewise_folder and not config.opts.timewise:
200 # we might already know the defect mapping
201 with timer("Quantize %s (%i iovs over %i lbs)" %
202 (self.query, len(iovs), len(lbtime))):
203 # Quantize to luminosity block
204 iovs = self.quantize(lbtime, iovs)
205
206 self.iovs = iovs
207 return self
208
209 def make_good_iov(self, iov):
210 """
211 Determine if one input iov is good.
212 """
213 giov = GoodIOV(iov.since, iov.until, self.mapping.get(iov.channel, iov.channel), self.evaluator(iov))
214 giov._orig_iov = iov
215 return giov
216
218 def make_good_iov(self, iov):
219 """
220 Determine if channels in one input iov are good.
221 """
222 giov = []
223 for channel, goodness in self.evaluator(iov):
224 current = GoodIOV(iov.since, iov.until, channel, goodness)
225 current._orig_iov = iov
226 giov.append(current)
227 return giov
228
229 def make_good_iovs(self, iovs):
230 """
231 Determine whether each iov signifies a good or bad state.
232 """
233 results = []
234 for iov in iovs:
235 results.append(self.make_good_iov(iov))
236 return IOVSet(sum(zip(*results), ())) # Sort by channel first
237
239 def make_good_iov(self, iov):
240 """
241 Determine if channels in one input iov are good.
242 """
243 giov = []
244 for bit, channel in self.mapping.get(iov.channel, dict()).items():
245 iov_value = (((iov.value >> bit) & 1) == 1) if iov.value is not None else None
246 test_iov = PBeastIOV(iov.since, iov.until, channel, iov_value)
247 current = GoodIOV(iov.since, iov.until, channel, self.evaluator(test_iov))
248 current._orig_iov = iov
249 giov.append(current)
250 return giov
251
253 def make_good_iov(self, iov):
254 """
255 Determine if channels in one input iov are good.
256 """
257 giov = []
258 for index, channel in self.mapping.get(iov.channel, dict()).items():
259 iov_value = iov.value[index] if iov.value is not None else None
260 test_iov = PBeastIOV(iov.since, iov.until, channel, iov_value)
261 current = GoodIOV(iov.since, iov.until, channel, self.evaluator(test_iov))
262 current._orig_iov = iov
263 giov.append(current)
264 return giov
265
266def load_current(filename:str, n_modules:int) -> tuple[list[float],list[tuple[float,...]]]:
267 from datetime import datetime, timezone
268 from pkg_resources import resource_string
269 data = resource_string('DCSCalculator2.subdetectors.data', filename).decode().strip().split('\n')
270 result = {}
271 for line in data:
272 line = line.strip()
273 if not line or line[0] == '#': continue
274 line = line.split()
275 current = tuple(float(x) for x in line[1:])
276 if len(current) != n_modules:
277 log.warning(f"Wrong number of modules ({len(current)}) in resource '{filename}' from {line[0]}. Setting thresholds to 0...")
278 current = (0.,) * n_modules
279 time = datetime.fromisoformat(line[0])
280 if time.tzinfo is None: time = time.replace(tzinfo=timezone.utc)
281 time = time.timestamp()
282 result[time] = current
283 keys, values = zip(*sorted(result.items()))
284 if not values: keys, values = [0.], [(0.,) * n_modules]
285 return (list(keys), list(values))
286
287def get_current(data: tuple[list[float],list[tuple[float,...]]], timestamp:float) -> tuple[float, ...]:
288 timestamp = timestamp / 1e9
289 keys, values = data
290 index = max(bisect(keys, timestamp) - 1, 0)
291 return values[index]
292
293get_sit_current = functools.partial(get_current, load_current('afp_sit_current.dat', 16))
294get_tdc_current = functools.partial(get_current, load_current('afp_tdc_current.dat', 4))
295
296# DCS channels
297A_FAR_GARAGE, A_NEAR_GARAGE, C_FAR_GARAGE, C_NEAR_GARAGE = 101, 105, 109, 113
298A_FAR_SIT_HV, A_NEAR_SIT_HV, C_FAR_SIT_HV, C_NEAR_SIT_HV = 1, 5, 9, 13
299A_FAR_SIT_LV, A_NEAR_SIT_LV, C_FAR_SIT_LV, C_NEAR_SIT_LV = 21, 25, 29, 33
300A_FAR_TOF_HV, C_FAR_TOF_HV = 17, 19
301A_FAR_TOF_LV, C_FAR_TOF_LV = 37, 39
302
303# TDAQ channels
304TTC_RESTART = 1000
305STOPLESSLY_REMOVED = 5000
306
307A_FAR_SIT_DISABLED, A_NEAR_SIT_DISABLED, C_FAR_SIT_DISABLED, C_NEAR_SIT_DISABLED = 1001, 1005, 1009, 1013
308A_FAR_TOF_DISABLED, C_FAR_TOF_DISABLED = 1017, 1019
309
310# Channel names
311NAMING = ['FSA0', 'FSA1', 'FSA2', 'FSA3',
312 'NSA0', 'NSA1', 'NSA2', 'NSA3',
313 'FSC0', 'FSC1', 'FSC2', 'FSC3',
314 'NSC0', 'NSC1', 'NSC2', 'NSC3',
315 'TDC-A-1', 'TDC-A-2',
316 'TDC-C-1', 'TDC-C-2']
317
318# Channel groups
319GARAGE = [A_FAR_GARAGE, A_NEAR_GARAGE, C_FAR_GARAGE, C_NEAR_GARAGE]
320SIT_HV = [A_FAR_SIT_HV, A_NEAR_SIT_HV, C_FAR_SIT_HV, C_NEAR_SIT_HV]
321SIT_LV = [A_FAR_SIT_LV, A_NEAR_SIT_LV, C_FAR_SIT_LV, C_NEAR_SIT_LV]
322TOF_HV = [A_FAR_TOF_HV, C_FAR_TOF_HV]
323TOF_LV = [A_FAR_TOF_LV, C_FAR_TOF_LV]
324
325SIT_DISABLED = [A_FAR_SIT_DISABLED, A_NEAR_SIT_DISABLED, C_FAR_SIT_DISABLED, C_NEAR_SIT_DISABLED]
326TOF_DISABLED = [A_FAR_TOF_DISABLED, C_FAR_TOF_DISABLED]
327
328# Thresholds
329SIT_HV_DEAD_BAND = 0.05
330TOF_HV_DEAD_BAND = 0.90
331
332SIT_LV_CURRENT_HIGH = 0.8
333TOF_HV_CURRENT_LOW = 600
334
335def mapChannels(*mapseqArgs: tuple[Collection[int], int]) -> dict[int, int]:
336 return dict(chain(*[zip(channels, range(defectChannel, defectChannel + len(channels))) for channels, defectChannel in mapseqArgs]))
337
338def mapTranslatorCounts(countMap:Mapping[int,Iterable[int]]) -> dict[int, range]:
339 return {channel: range(channel, channel + count) for count, channels in countMap.items() for channel in channels}
340
341def remove_None(value, default):
342 return value if value is not None else default
343
344class AFP(DCSC_DefectTranslate_Subdetector):
345 folder_base = '/AFP/DCS'
346 variables = [
347
350
351 # AFP_(A|C)_(FAR|NEAR)_IN_GARAGE
353 'STATION',
354 lambda iov: iov.inphysics is True,
355 mapping = {1: C_FAR_GARAGE, 2: C_NEAR_GARAGE, 3: A_FAR_GARAGE, 4: A_NEAR_GARAGE}
356 ),
357
358 # AFP_(A|C)_(FAR|NEAR)_SIT_(PARTIALLY|NOT)_OPERATIONAL_LV
360 'SIT/LV',
361 #lambda iov: SIT_LV_CURRENT_LOW <= remove_None(iov.current, 0) <= SIT_LV_CURRENT_HIGH,
362 #lambda iov: SIT_LV_CURRENT_LOW[iov.channel - SIT_LV[0]] <= remove_None(iov.current, 0) <= SIT_LV_CURRENT_HIGH,
363 lambda iov: get_sit_current(iov.since)[iov.channel - SIT_LV[0]] <= remove_None(iov.current, 0) <= SIT_LV_CURRENT_HIGH,
364 mapping = mapChannels(
365 ([ 9, 10, 11, 12], A_FAR_SIT_LV ),
366 ([13, 14, 15, 16], A_NEAR_SIT_LV),
367 ([ 1, 2, 3, 4], C_FAR_SIT_LV ),
368 ([ 5, 6, 7, 8], C_NEAR_SIT_LV)
369 )
370 ),
371
372 # AFP_(A|C)_(FAR|NEAR)_SIT_(PARTIALLY|NOT)_OPERATIONAL_HV
374 ['SIT/HV', 'SIT/HV_VOLTAGE_SET'],
375 lambda iov: -remove_None(iov.hv.voltage, 0) > iov.hv_voltage_set.voltageSet - SIT_HV_DEAD_BAND,
376 mapping = {
377 'SIT/HV': mapChannels(
378 ([ 6, 7, 1, 2], A_FAR_SIT_HV ),
379 ([ 8, 3, 4, 9], A_NEAR_SIT_HV),
380 ([10, 11, 12, 5], C_FAR_SIT_HV ),
381 ([13, 14, 15, 16], C_NEAR_SIT_HV)),
382 'SIT/HV_VOLTAGE_SET': mapChannels(
383 ([ 1, 2, 3, 4], A_FAR_SIT_HV ),
384 ([ 5, 6, 7, 8], A_NEAR_SIT_HV),
385 ([ 9, 10, 11, 12], C_FAR_SIT_HV ),
386 ([13, 14, 15, 16], C_NEAR_SIT_HV))
387 }
388 ),
389
390 # AFP_(A|C)_FAR_TOF_NOT_OPERATIONAL_LV
392 'TOF_TDC_CURRENT',
393 lambda iov: [(iov.channel, get_tdc_current(iov.since)[iov.channel - TOF_LV[0]] <= remove_None(iov.hptdc1_current, 0)),
394 (iov.channel + 1, get_tdc_current(iov.since)[iov.channel + 1 - TOF_LV[0]] <= remove_None(iov.hptdc2_current, 0))],
395 mapping = {1: A_FAR_TOF_LV, 2: C_FAR_TOF_LV}
396 ),
397
398 # AFP_(A|C)_FAR_TOF_NOT_OPERATIONAL_HV
400 ['TOF', 'TOF_PMT_VOLTAGE_SET'],
401 lambda iov: -remove_None(iov.tof.pmt_voltage, 0) > iov.tof_pmt_voltage_set.pmt_voltageSet - TOF_HV_DEAD_BAND and -remove_None(iov.tof.pmt_current, 0) > TOF_HV_CURRENT_LOW,
402 mapping = {
403 'TOF': {1: A_FAR_TOF_HV, 2: C_FAR_TOF_HV},
404 'TOF_PMT_VOLTAGE_SET': {1: A_FAR_TOF_HV, 2: C_FAR_TOF_HV},
405 }
406 ),
407
408
411
412 # AFP_TTC_RESTART
414 'ATLAS.RCStateInfo.state.RunCtrl.AFP',
415 lambda iov: iov.value == 'RUNNING',
416 mapping = {'RunCtrl.AFP': TTC_RESTART}
417 ),
418
419 # AFP_STOPLESSLY_REMOVED
421 'ATLAS.RODBusyIS.BusyEnabled.Monitoring.afp_rodBusy-VLDB/RODBusy',
422 lambda iov: iov.value is not False,
423 mapping = {'Monitoring.afp_rodBusy-VLDB/RODBusy': {6: STOPLESSLY_REMOVED}}
424 ),
425
426 # AFP_(A|C)_(FAR|NEAR)_(PARTIALLY|NOT)_OPERATIONAL_TDAQ
428 'ATLAS.RceMonitoring.DisabledPerm.Monitoring.RceMonitoring_RCE[34]',
429 lambda iov: iov.value is not True,
430 regex = True,
431 mapping = {
432 'Monitoring.RceMonitoring_RCE3': mapChannels(([0, 2, 4, 6], A_NEAR_SIT_DISABLED), ([8, 10, 12, 14], A_FAR_SIT_DISABLED), ([9, 11], A_FAR_TOF_DISABLED)),
433 'Monitoring.RceMonitoring_RCE4': mapChannels(([0, 2, 4, 6], C_NEAR_SIT_DISABLED), ([8, 10, 12, 14], C_FAR_SIT_DISABLED), ([9, 11], C_FAR_TOF_DISABLED)),
434 }
435 ),
436 ]
437
438 equality_breaker = 0.0001
439
440 dead_fraction_caution = 0 + equality_breaker
441 dead_fraction_bad = 0.25 + equality_breaker
442
444 1: [*GARAGE, TTC_RESTART, STOPLESSLY_REMOVED, *TOF_HV],
445 2: [*TOF_DISABLED, *TOF_LV],
446 4: [*SIT_DISABLED, *SIT_LV, *SIT_HV],
447 })
448
449 def merge_variable_states(self, states):
450 """
451 Merge input channel states across variables, taking the worst.
452
453 Ignore configuration variables and variables without channel.
454 """
455
456 # Remove variables without channel
457 states = [state for state in states if state and state.channel is not None]
458
459 # More simplistic way of doing the above, but cannot handle config vars:
460 return min(state.good for state in states) if len(states) > 0 else None
461
462 def calculate_dead_fraction(self, since, until, output_channel, states, state_iovs):
463 """
464 Calculate the dead fraction and the resulting traffic light code.
465 """
466
467 n_total = len(states)
468 n_working = states.count(GOOD)
469 n_bad = states.count(BAD)
470 n_unfilled = states.count(EMPTY)
471
472 assert n_total == len(self.mapping[output_channel])
473 assert n_total - n_working - n_bad - n_unfilled == 0
474
475 n_config = n_total - n_unfilled
476 dead_fraction = 1. - n_working / n_total
477
478 code = GREEN
479 if dead_fraction > self.dead_fraction_caution:
480 code = YELLOW
481 if dead_fraction > self.dead_fraction_bad:
482 code = RED
483
484 if n_unfilled / n_total > self.dead_fraction_caution:
485 code = GREY
486 if n_unfilled and config.opts.mark_unfilled_grey:
487 code = GREY
488
489 return code, dead_fraction, 0., n_config, n_working
490
491 @staticmethod
492 def defect_translator(channel, defect_name, selector, comment):
493 def translator_core(iovs):
494 return [DefectIOV(iov.since, iov.until, defect_name, True, comment=comment(iov))
495 for iov in iovs if iov.channel == channel and not iov._is_empty and selector(iov)]
496 return translator_core
497
498 @staticmethod
499 def defect_combinator(channels, defect_name, selector, naffected, comment):
500 def combinator_core(iovs):
501 result = []
502 channel_iovs = iovs.by_channel
503 defect_iovs = [channel_iovs.get(channel) for channel in channels]
504 for since, until, states in process_iovs(*defect_iovs):
505 matched = [(state, group) for state,group in zip(states,channels) if not state._is_empty and selector(state)]
506 if len(matched) < 2: continue # We need at least two defects
507 bad_channels = {iov.channel - group for state,group in matched for iov in state._orig_iovs if not iov.good}
508 if len(bad_channels) < naffected: continue
509 result.append(DefectIOV(since, until, defect_name, True, comment=comment()))
510 return result
511 return combinator_core
512
513 @staticmethod
514 def color_selector(color):
515 def selector_core(iov):
516 return iov.Code == color
517 return selector_core
518
519 @staticmethod
520 def nbad_selector(nbad):
521 if isinstance(nbad, int): nbad = [nbad]
522 def selector_core(iov):
523 return iov.NConfig - iov.NWorking in nbad
524 return selector_core
525
526 def __init__(self, *args, **kwargs):
527 super(AFP, self).__init__(*args, **kwargs)
528 self.translators = [
529 AFP.defect_translator(*cdsc)
530 for cdsc in [
531
534 (A_FAR_GARAGE, 'AFP_A_FAR_IN_GARAGE', AFP.color_selector(RED), AFP.comment_GARAGE),
535 (A_NEAR_GARAGE, 'AFP_A_NEAR_IN_GARAGE', AFP.color_selector(RED), AFP.comment_GARAGE),
536 (C_FAR_GARAGE, 'AFP_C_FAR_IN_GARAGE', AFP.color_selector(RED), AFP.comment_GARAGE),
537 (C_NEAR_GARAGE, 'AFP_C_NEAR_IN_GARAGE', AFP.color_selector(RED), AFP.comment_GARAGE),
538
539 (A_FAR_SIT_HV, 'AFP_A_FAR_SIT_PARTIALLY_OPERATIONAL_HV', AFP.nbad_selector([1]), AFP.comment_SIT_HV),
540 (A_NEAR_SIT_HV, 'AFP_A_NEAR_SIT_PARTIALLY_OPERATIONAL_HV', AFP.nbad_selector([1]), AFP.comment_SIT_HV),
541 (C_FAR_SIT_HV, 'AFP_C_FAR_SIT_PARTIALLY_OPERATIONAL_HV', AFP.nbad_selector([1]), AFP.comment_SIT_HV),
542 (C_NEAR_SIT_HV, 'AFP_C_NEAR_SIT_PARTIALLY_OPERATIONAL_HV', AFP.nbad_selector([1]), AFP.comment_SIT_HV),
543 (A_FAR_SIT_HV, 'AFP_A_FAR_SIT_NOT_OPERATIONAL_HV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_HV),
544 (A_NEAR_SIT_HV, 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_HV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_HV),
545 (C_FAR_SIT_HV, 'AFP_C_FAR_SIT_NOT_OPERATIONAL_HV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_HV),
546 (C_NEAR_SIT_HV, 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_HV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_HV),
547
548 (A_FAR_SIT_LV, 'AFP_A_FAR_SIT_PARTIALLY_OPERATIONAL_LV', AFP.nbad_selector([1]), AFP.comment_SIT_LV),
549 (A_NEAR_SIT_LV, 'AFP_A_NEAR_SIT_PARTIALLY_OPERATIONAL_LV', AFP.nbad_selector([1]), AFP.comment_SIT_LV),
550 (C_FAR_SIT_LV, 'AFP_C_FAR_SIT_PARTIALLY_OPERATIONAL_LV', AFP.nbad_selector([1]), AFP.comment_SIT_LV),
551 (C_NEAR_SIT_LV, 'AFP_C_NEAR_SIT_PARTIALLY_OPERATIONAL_LV', AFP.nbad_selector([1]), AFP.comment_SIT_LV),
552 (A_FAR_SIT_LV, 'AFP_A_FAR_SIT_NOT_OPERATIONAL_LV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_LV),
553 (A_NEAR_SIT_LV, 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_LV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_LV),
554 (C_FAR_SIT_LV, 'AFP_C_FAR_SIT_NOT_OPERATIONAL_LV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_LV),
555 (C_NEAR_SIT_LV, 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_LV', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_LV),
556 (A_FAR_SIT_LV, 'AFP_A_FAR_SIT_NOT_OPERATIONAL_LV_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_LV),
557 (A_NEAR_SIT_LV, 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_LV_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_LV),
558 (C_FAR_SIT_LV, 'AFP_C_FAR_SIT_NOT_OPERATIONAL_LV_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_LV),
559 (C_NEAR_SIT_LV, 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_LV_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_LV),
560
561 (A_FAR_TOF_LV, 'AFP_A_FAR_TOF_NOT_OPERATIONAL_LV', AFP.color_selector(RED), AFP.comment_TOF_LV),
562 (C_FAR_TOF_LV, 'AFP_C_FAR_TOF_NOT_OPERATIONAL_LV', AFP.color_selector(RED), AFP.comment_TOF_LV),
563
564 (A_FAR_TOF_HV, 'AFP_A_FAR_TOF_NOT_OPERATIONAL_HV', AFP.color_selector(RED), AFP.comment_TOF_HV),
565 (C_FAR_TOF_HV, 'AFP_C_FAR_TOF_NOT_OPERATIONAL_HV', AFP.color_selector(RED), AFP.comment_TOF_HV),
566
567
570 (TTC_RESTART, 'AFP_TTC_RESTART', AFP.color_selector(RED), AFP.comment_TTC_RESTART),
571 (STOPLESSLY_REMOVED, 'AFP_STOPLESSLY_REMOVED', AFP.color_selector(RED), AFP.comment_STOPLESSLY_REMOVED),
572
573 (A_FAR_SIT_DISABLED, 'AFP_A_FAR_SIT_PARTIALLY_OPERATIONAL_TDAQ', AFP.nbad_selector([1]), AFP.comment_SIT_DISABLED),
574 (A_NEAR_SIT_DISABLED, 'AFP_A_NEAR_SIT_PARTIALLY_OPERATIONAL_TDAQ', AFP.nbad_selector([1]), AFP.comment_SIT_DISABLED),
575 (C_FAR_SIT_DISABLED, 'AFP_C_FAR_SIT_PARTIALLY_OPERATIONAL_TDAQ', AFP.nbad_selector([1]), AFP.comment_SIT_DISABLED),
576 (C_NEAR_SIT_DISABLED, 'AFP_C_NEAR_SIT_PARTIALLY_OPERATIONAL_TDAQ', AFP.nbad_selector([1]), AFP.comment_SIT_DISABLED),
577 (A_FAR_SIT_DISABLED, 'AFP_A_FAR_SIT_NOT_OPERATIONAL_TDAQ', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_DISABLED),
578 (A_NEAR_SIT_DISABLED, 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_TDAQ', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_DISABLED),
579 (C_FAR_SIT_DISABLED, 'AFP_C_FAR_SIT_NOT_OPERATIONAL_TDAQ', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_DISABLED),
580 (C_NEAR_SIT_DISABLED, 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_TDAQ', AFP.nbad_selector([2, 3, 4]), AFP.comment_SIT_DISABLED),
581 (A_FAR_SIT_DISABLED, 'AFP_A_FAR_SIT_NOT_OPERATIONAL_TDAQ_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_DISABLED),
582 (A_NEAR_SIT_DISABLED, 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_TDAQ_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_DISABLED),
583 (C_FAR_SIT_DISABLED, 'AFP_C_FAR_SIT_NOT_OPERATIONAL_TDAQ_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_DISABLED),
584 (C_NEAR_SIT_DISABLED, 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_TDAQ_3PLANES', AFP.nbad_selector([3, 4]), AFP.comment_SIT_DISABLED),
585
586 (A_FAR_TOF_DISABLED, 'AFP_A_FAR_TOF_NOT_OPERATIONAL_TDAQ', AFP.color_selector(RED), AFP.comment_TOF_DISABLED),
587 (C_FAR_TOF_DISABLED, 'AFP_C_FAR_TOF_NOT_OPERATIONAL_TDAQ', AFP.color_selector(RED), AFP.comment_TOF_DISABLED),
588 ]
589 ] + [
590 AFP.defect_combinator(*cdsac)
591 for cdsac in [
592 ([A_FAR_SIT_LV, A_FAR_SIT_DISABLED], 'AFP_A_FAR_SIT_NOT_OPERATIONAL', AFP.nbad_selector([1]), 2, AFP.comment_SIT_COMBINATION),
593 ([A_NEAR_SIT_LV, A_NEAR_SIT_DISABLED], 'AFP_A_NEAR_SIT_NOT_OPERATIONAL', AFP.nbad_selector([1]), 2, AFP.comment_SIT_COMBINATION),
594 ([C_FAR_SIT_LV, C_FAR_SIT_DISABLED], 'AFP_C_FAR_SIT_NOT_OPERATIONAL', AFP.nbad_selector([1]), 2, AFP.comment_SIT_COMBINATION),
595 ([C_NEAR_SIT_LV, C_NEAR_SIT_DISABLED], 'AFP_C_NEAR_SIT_NOT_OPERATIONAL', AFP.nbad_selector([1]), 2, AFP.comment_SIT_COMBINATION),
596 ([A_FAR_SIT_LV, A_FAR_SIT_DISABLED], 'AFP_A_FAR_SIT_NOT_OPERATIONAL_3PLANES', AFP.nbad_selector([1, 2]), 3, AFP.comment_SIT_COMBINATION),
597 ([A_NEAR_SIT_LV, A_NEAR_SIT_DISABLED], 'AFP_A_NEAR_SIT_NOT_OPERATIONAL_3PLANES', AFP.nbad_selector([1, 2]), 3, AFP.comment_SIT_COMBINATION),
598 ([C_FAR_SIT_LV, C_FAR_SIT_DISABLED], 'AFP_C_FAR_SIT_NOT_OPERATIONAL_3PLANES', AFP.nbad_selector([1, 2]), 3, AFP.comment_SIT_COMBINATION),
599 ([C_NEAR_SIT_LV, C_NEAR_SIT_DISABLED], 'AFP_C_NEAR_SIT_NOT_OPERATIONAL_3PLANES', AFP.nbad_selector([1, 2]), 3, AFP.comment_SIT_COMBINATION),
600 ]
601 ]
602
603
606
607 @staticmethod
609 return 'Station not in physics'
610
611 @staticmethod
613 return AFP.comment_planes(iov, 'out of nominal current', SIT_LV[0])
614
615 @staticmethod
617 return AFP.comment_planes(iov, 'out of nominal voltage', SIT_HV[0])
618
619 @staticmethod
621 return AFP.comment_tof_tdc(iov, 'with too low current', TOF_LV[0])
622
623 @staticmethod
625 return 'ToF PMT out of nominal voltage'
626
627
630
631 @staticmethod
633 return 'AFP not in the RUNNING state'
634
635 @staticmethod
637 return 'AFP stoplessly removed from the run'
638
639 @staticmethod
641 return AFP.comment_planes(iov, 'removed from readout', SIT_DISABLED[0])
642
643 @staticmethod
645 return AFP.comment_tof_tdc(iov, 'removed from readout', TOF_DISABLED[0])
646
647
650
651 @staticmethod
653 return "Combination of 'partially operational' defects"
654
655
658
659 @staticmethod
660 def comment_planes(iov, message, defect_offset=None, module_tagger=None):
661 return AFP.comment_device(iov, 'SiT plane', message, defect_offset, module_tagger)
662
663 @staticmethod
664 def comment_tof_tdc(iov, message, defect_offset=None, module_tagger=None):
665 if defect_offset is not None: defect_offset -= 16
666 return AFP.comment_device(iov, 'ToF TDC', message, defect_offset, module_tagger)
667
668 @staticmethod
669 def comment_device(iov, device, message, defect_offset=None, module_tagger=None):
670 count = iov.NConfig - iov.NWorking
671 if count != 1:
672 device += 's'
673 comment = f"{count} {device} {message}"
674 if defect_offset is None:
675 return comment
676 iovs = sorted([orig for orig in iov._orig_iovs if orig.good is False], key=lambda x: x.channel)
677 list = [NAMING[orig.channel - defect_offset] for orig in iovs]
678 if module_tagger is not None:
679 list = [f"{module} {module_tagger(orig)}" for module,orig in zip(list,iovs)]
680 return comment + f" ({', '.join(list)})"
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
merge_variable_states(self, states)
Definition afp.py:449
__init__(self, *args, **kwargs)
Definition afp.py:526
comment_TTC_RESTART(iov)
TDAQ defects.
Definition afp.py:632
comment_SIT_COMBINATION()
Combination defects.
Definition afp.py:652
defect_combinator(channels, defect_name, selector, naffected, comment)
Definition afp.py:499
comment_planes(iov, message, defect_offset=None, module_tagger=None)
Comment templates.
Definition afp.py:660
comment_device(iov, device, message, defect_offset=None, module_tagger=None)
Definition afp.py:669
comment_GARAGE(iov)
DCS defects.
Definition afp.py:608
defect_translator(channel, defect_name, selector, comment)
Definition afp.py:492
calculate_dead_fraction(self, since, until, output_channel, states, state_iovs)
Definition afp.py:462
comment_tof_tdc(iov, message, defect_offset=None, module_tagger=None)
Definition afp.py:664
__init__(self, folders, evaluator, *, mapping={}, **kwargs)
Definition afp.py:52
read(self, query_range, folder_base, folder_names)
Definition afp.py:59
merge_inputs(self, clazz, channel, *inputs)
Definition afp.py:97
read(self, query_range, folder_base, folder_name)
Definition afp.py:30
__init__(self, folder, evaluator, *, mapping=None, **kwargs)
Definition afp.py:26
read(self, query_range, query, *, regex=False)
Definition afp.py:132
calculate_good_iovs(self, lbtime, subdetector)
Definition afp.py:178
__init__(self, query, evaluator, *, regex=False, mapping=dict(), force_mapping=False, empty_value=None)
Definition afp.py:119
STL class.
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:130
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
tuple[list[float], list[tuple[float,...]]] load_current(str filename, int n_modules)
Definition afp.py:266
remove_None(value, default)
Definition afp.py:341
tuple[float,...] get_current(tuple[list[float], list[tuple[float,...]]] data, float timestamp)
Definition afp.py:287
dict[int, int] mapChannels(*tuple[Collection[int], int] mapseqArgs)
Definition afp.py:335
PBeastIOV(channel, value)
Definition afp.py:102
dict[int, range] mapTranslatorCounts(Mapping[int, Iterable[int]] countMap)
Definition afp.py:338