ATLAS Offline Software
Loading...
Searching...
No Matches
TileBchCrest.py
Go to the documentation of this file.
1#!/bin/env python
2
3# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
4# TileBchCrest.py
5# Sanya Solodkov <Sanya.Solodkov@cern.ch>, 2025-10-22
6
7"""
8Python module for managing TileCal ADC status words.
9
10"""
11
12import cppyy
13
14from PyCool import cool # noqa: F401
15Blob = cppyy.gbl.coral.Blob
16
17#from pycrest.api.crest_api import CrestApi
18
19from TileCalibBlobObjs.Classes import TileBchStatus, TileCalibUtils, \
20 TileBchPrbs, TileBchDecoder
21from TileCalibBlobPython.TileCalibCrest import MINRUN, MINLBK, MAXRUN, MAXLBK, \
22 TileBlobReaderCrest, TileBlobWriterCrest
23from TileCalibBlobPython.TileCalibLogger import TileCalibLogger
24from TileCalibBlobPython.TileCalibTools import TileASCIIParser
25
26#
27#______________________________________________________________________
28class TileBchMgr(TileCalibLogger):
29 """
30 This class manages updates to the Tile Calorimeter bad channel database.
31 The usual mode of operation should start with initializing this manager
32 with a current set of bad channels from an existing database.
33 The status of individual ADCs can then be modified using the setAdcStatus,
34 updateFromFile or updateFromDb methods.
35 In a final step, the changes are commited to the database using the commit
36 method.
37 """
38 #____________________________________________________________________
39 def __init__(self):
40
41 #=== initialize base class
42 TileCalibLogger.__init__(self,"TileBchMgr")
43
44 #=== initialize all channel status to "good"
45 self.__newStat = [ TileBchStatus() for _ in range(self.__getAdcIdx(4, 63, 47, 1) + 1) ]
46 self.__oldStat = [ TileBchStatus() for _ in range(self.__getAdcIdx(4, 63, 47, 1) + 1) ]
47
48 self.__comment = ""
49 self.__mode = 2
50 self.__runLumi = (MAXRUN,MAXLBK-1)
51 self.__db = None
52 self.__folder = None
53 self.__tag = None
54 self.__reader = None
55
56 #____________________________________________________________________
57 def __getAdcIdx(self, ros, drawer, channel, adc):
58 """
59 Private function, calculating the index of a given ADC
60 for the internal cache.
61 """
62 return TileCalibUtils.getAdcIdx(ros,drawer,channel,adc)
63
64 #____________________________________________________________________
65 def __updateFromDb(self, db, folderPath, tag, runLumi, fillTable=1, ros=-1, module=-1):
66 """
67 Updates the internal bad channel cache with the content
68 found in the database. Server connection string or file name (db) has to
69 be provided. Tag and runLumi are used to locate the set of
70 bad channels to be read from the database.
71 """
72
73 if db is None:
74 db = self.__db
75 if folderPath is None:
76 folderPath = self.__folderPath
77 if tag is None:
78 tag = self.__tag
79
80 if db is None:
81 self.log().critical( "Database is not defined" )
82 return
83
84 #=== print status information
85 if db != self.__db or tag != self.__tag or folderPath != self.__folder:
86 self.__reader = TileBlobReaderCrest(db,folderPath,tag,runLumi[0],runLumi[1])
87 self.__db = db
88 self.__tag = tag
89 self.__folder = folderPath
90 else:
91 self.__reader.getIovs(runLumi,(runLumi[0],runLumi[1]+1))
92 if ros==-2:
93 self.__comment = self.__reader.getComment(runLumi)
94 self.log().info("Comment: %s", self.__comment)
95
96 #=== loop over the whole detector
97 rosmin = ros if ros>=0 else 0
98 rosmax = ros+1 if ros>=0 else TileCalibUtils.max_ros()
99 for ros in range(rosmin,rosmax):
100 modmin = module if module>=0 else 0
101 modmax = module+1 if module>=0 else TileCalibUtils.getMaxDrawer(ros)
102 for mod in range(modmin,modmax):
103 bch = self.__reader.getDrawer(ros, mod, runLumi, False)
104 if bch is None:
105 if fillTable>=0:
106 self.log().warning("Missing IOV in condDB: ros=%i mod=%i runLumi=%s", ros,mod,runLumi)
107 continue
108 bchDecoder = TileBchDecoder(bch.getBitPatternVersion())
109 for chn in range(TileCalibUtils.max_chan()):
110 for adc in range(TileCalibUtils.max_gain()):
111 #=== adc bits
112 adcBits = bch.getData(chn,adc,0)
113 #=== channel bits (works always due to default policy)
114 chnBits = bch.getData(chn, 2,0)
115 #=== build status from both adc and channel bits
116 status = TileBchStatus( bchDecoder.decode(chnBits,adcBits) )
117 if fillTable==0:
118 self.__oldStat[self.__getAdcIdx(ros,mod,chn,adc)] = status
119 elif fillTable==1 or fillTable==-1:
120 self.__newStat[self.__getAdcIdx(ros,mod,chn,adc)] = status
121 elif fillTable==2 or fillTable==-2:
122 self.__oldStat[self.__getAdcIdx(ros,mod,chn,adc)] = status
123 self.__newStat[self.__getAdcIdx(ros,mod,chn,adc)] = status
124 else:
125 self.__newStat[self.__getAdcIdx(ros,mod,chn,adc)] = status
126 status1 = TileBchStatus( bchDecoder.decode(chnBits,adcBits) )
127 self.__oldStat[self.__getAdcIdx(ros,mod,chn,adc)] = status1
128
129 #____________________________________________________________________
130 def getComment(self):
131 return self.__comment
132
133 #____________________________________________________________________
134 def getBlobReader(self):
135 return self.__reader
136
137 #____________________________________________________________________
138 def updateFromDb(self, db, folderPath, tag, runLumi, fillTable=1, mode=None, ros=-1, module=-1):
139 if mode:
140 self.__mode = mode
141 self.__updateFromDb(db, folderPath, tag, runLumi, fillTable, ros, module)
142
143 #____________________________________________________________________
144 def initialize(self, db, folderPath, tag="", runLumi=(MAXRUN,MAXLBK-1), mode=None, ros=-1, module=-1):
145 """
146 Initializes the internal bad channel cache. Any changes applied to the
147 cache previous to calling this function are lost. Typically this function
148 is called once in the beginning to initialize the cache with a set of
149 current bad channels.
150 """
151 self.log().info("Initializing from database, resetting all changes!")
152 #=== initialize reference to current status
153 self.__runLumi = runLumi
154 if mode:
155 self.__mode = mode
156 fT = -1
157 if self.__mode<0: # silent mode
158 self.__mode = -self.__mode
159 if self.__mode==2:
160 fT = -3
161 else:
162 fT = -2
163 else:
164 if self.__mode==2:
165 fT = 3
166 else:
167 fT = 2
168
169 self.__updateFromDb(db,folderPath,tag,runLumi,fT,ros,module)
170 #=== update TileBchStatus::isBad() definition from DB
171 self.log().info("Updating TileBchStatus::isBad() definition from DB")
172 status = self.getBadDefinition()
173 if status.isGood():
174 self.log().info("No TileBchStatus::isBad() definition found in DB, using defaults")
175 else:
177
178 #=== update TileBchStatus::isBadTiming() definition from DB
179 self.log().info("Updating TileBchStatus::isBadTiming() definition from DB")
180 status = self.getBadTimingDefinition()
181 if status.isGood():
182 self.log().info("No TileBchStatus::isBadTiming() definition found in DB, using defaults")
183 else:
185
186 #____________________________________________________________________
187 def getAdcStatus(self, ros, drawer, channel, adc):
188 """
189 Get TileBchStatus for a given ADC.
190 """
191 return self.__newStat[self.__getAdcIdx(ros,drawer,channel,adc)]
192
193 #____________________________________________________________________
194 def setAdcStatus(self, ros, drawer, channel, adc, status):
195 """
196 Set TileBchStatus for a given ADC.
197 """
198 self.__newStat[self.__getAdcIdx(ros,drawer,channel,adc)] = status
199
200 #____________________________________________________________________
201 def getAdcProblems(self, ros, drawer, channel, adc):
202 """
203 Returns a dictionary with { problemEnum : 'Description'}
204 """
205 prbDescDict = {}
206 status = self.getAdcStatus(ros,drawer,channel,adc)
207 if not status.isGood():
208 prbs = status.getPrbs()
209 for prb in prbs:
210 prbDescDict[prb] = TileBchPrbs.getDescription(prb)
211 return prbDescDict
212
213 #____________________________________________________________________
214 def setAdcProblems(self, ros, drawer, channel, adc, problems):
215 """
216 Expects a list of TileBchPrbs::PrbS as input
217 """
218 status = TileBchStatus()
219 for prb in problems:
220 status += prb
221 self.setAdcStatus(ros,drawer,channel,adc,status)
222
223 #____________________________________________________________________
224 def addAdcProblem(self, ros, drawer, channel, adc, problem):
225 """
226 Sets a specific problem
227 """
228 status = self.getAdcStatus(ros,drawer,channel,adc)
229 status += problem
230 self.setAdcStatus(ros,drawer,channel,adc,status)
231
232 #____________________________________________________________________
233 def delAdcProblem(self, ros, drawer, channel, adc, problem):
234 """
235 Removes a specific problem
236 """
237 status = self.getAdcStatus(ros,drawer,channel,adc)
238 status -= problem
239 self.setAdcStatus(ros,drawer,channel,adc,status)
240
241 #____________________________________________________________________
242 def decodeModule(self, module):
243 """
244 convert module name to ros,drawer
245 """
246 try:
247 part_dict = {'LBA':1,'LBC':2,'EBA':3,'EBC':4}
248 partname = str(module[0:3])
249 ros = part_dict[partname]
250 drawer = int(module[3:])-1
251 except Exception:
252 drawer = -1
253 if drawer<0 or drawer>63:
254 self.log().critical( "Invalid module name %s" % module )
255 raise SystemExit
256
257 return (ros,drawer)
258
259 #____________________________________________________________________
260 def getADCStatus(self, module, channel, adc):
261 """
262 Get TileBchStatus for a given ADC.
263 """
264 (ros,drawer) = self.decodeModule(module)
265 return self.__newStat[self.__getAdcIdx(ros,drawer,channel,adc)]
266
267 #____________________________________________________________________
268 def setADCStatus(self, module, channel, adc, status):
269 """
270 Set TileBchStatus for a given ADC.
271 """
272 (ros,drawer) = self.decodeModule(module)
273 self.__newStat[self.__getAdcIdx(ros,drawer,channel,adc)] = status
274
275 #____________________________________________________________________
276 def getADCProblems(self, module, channel, adc):
277 """
278 Returns a dictionary with { problemEnum : 'Description'}
279 """
280 (ros,drawer) = self.decodeModule(module)
281 prbDescDict = {}
282 status = self.getAdcStatus(ros,drawer,channel,adc)
283 if not status.isGood():
284 prbs = status.getPrbs()
285 for prb in prbs:
286 prbDescDict[prb] = TileBchPrbs.getDescription(prb)
287 return prbDescDict
288
289 #____________________________________________________________________
290 def setADCProblems(self, module, channel, adc, problems):
291 """
292 Expects a list of TileBchPrbs::PrbS as input
293 """
294 (ros,drawer) = self.decodeModule(module)
295 status = TileBchStatus()
296 for prb in problems:
297 status += prb
298 self.setAdcStatus(ros,drawer,channel,adc,status)
299
300 #____________________________________________________________________
301 def addADCProblem(self, module, channel, adc, problem):
302 """
303 Sets a specific problem
304 """
305 (ros,drawer) = self.decodeModule(module)
306 status = self.getAdcStatus(ros,drawer,channel,adc)
307 status += problem
308 self.setAdcStatus(ros,drawer,channel,adc,status)
309
310 #____________________________________________________________________
311 def delADCProblem(self, module, channel, adc, problem):
312 """
313 Removes a specific problem
314 """
315 (ros,drawer) = self.decodeModule(module)
316 status = self.getAdcStatus(ros,drawer,channel,adc)
317 status -= problem
318 self.setAdcStatus(ros,drawer,channel,adc,status)
319
320 #____________________________________________________________________
321 def listBadAdcs(self, rosBeg=0, modBeg=0, rosEnd=5, modEnd=64):
322 """
323 Print a formatted list of all ADCs with problems.
324 """
325 self.log().info("==============================================================")
326 self.log().info(" Current list of affected ADCs " )
327 self.log().info("==============================================================")
328 for ros in range(rosBeg,rosEnd):
329 for mod in range(modBeg, min(modEnd,TileCalibUtils.getMaxDrawer(ros))):
330 modName = TileCalibUtils.getDrawerString(ros,mod)
331 for chn in range(TileCalibUtils.max_chan()):
332 chnName = "channel %2i" % chn
333 for adc in range(TileCalibUtils.max_gain()):
334 gainName = "LG:"
335 if adc:
336 gainName = "HG:"
337 prbs = self.getAdcProblems(ros,mod,chn,adc)
338 for prbCode in sorted(prbs.keys()):
339 prbDesc = prbs[prbCode]
340 msg = "%s %s %s %2i (%s)" % (modName,chnName,gainName,prbCode,prbDesc)
341 self.log().info( msg )
342 modName = " " * 5
343 chnName = " " * 10
344 gainName = " " * 3
345 self.log().info("==============================================================")
346
347 #____________________________________________________________________
348 def checkModuleForChanges(self, ros, drawer):
349 """
350 Returns:
351 - if nothing changed : 0
352 - if something changed and complete drawer is now good : -1
353 - if something changed but drawer is not completely good: >0
354 """
355 diffCnt = 0
356 allGood = True
357 for chn in range(TileCalibUtils.max_chan()):
358 for adc in range(TileCalibUtils.max_gain()):
359 idx = self.__getAdcIdx(ros,drawer,chn,adc)
360 newStatus = self.__newStat[idx]
361 #=== count differences between old and new
362 if newStatus!=self.__oldStat[idx]:
363 diffCnt+=1
364 #=== invalidate allGood if one ADC is not good
365 if not newStatus.isGood():
366 allGood = False
367 if diffCnt>0 and allGood:
368 return -1
369 return diffCnt
370
371 #____________________________________________________________________
372 def updateFromFile(self, fileName):
373 """
374 Updates the internal bad channel cache with the content
375 found in the file. The layout of the file has to follow the
376 TileConditions ASCII file layout.
377
378 NGO: change this at some point. In a file, not the status word (which
379 depends on the bit pattern version) should be encoded, but the individual problems (enums).
380 For this we need one line per ADC... this requires some modification in the reader.
381 """
382 parser = TileASCIIParser(fileName,'Bch')
383 dict = parser.getDict()
384 self.log().info("Updating dictionary from file with %i entries", len(dict))
385 self.log().info("... filename: %s", fileName )
386 for key, stat in list(dict.items()):
387 ros = key[0]
388 mod = key[1]
389 chn = key[2]
390 for adc in range(2):
391 status = TileBchStatus()
392 status+=self.getAdcStatus(ros,mod,chn,adc)
393 if adc < len(stat):
394 statInt = int(stat[adc])
395 else:
396 statInt=0
397 #=== temporary convention
398 if statInt==0:
399 pass
400 elif statInt==1:
401 status += TileBchPrbs.IgnoredInHlt
402 else:
403 status += int(stat[adc])
404 self.setAdcStatus(ros,mod,chn,adc,status)
405
406 #____________________________________________________________________
407 def commitToDb(self, db, folderPath, tag, bitPatVer, author, comment,
408 since, moduleList=[]):
409 """
410 Commits the differences compared to the set of bad channels read in with the
411 initialze function to the provided database.
412 - author : author name (string)
413 - comment : a comment (string)
414 - sinceRun, sinceLbk : start of IOV for which bad channels are valid
415 """
416
417 writer = TileBlobWriterCrest(db,folderPath,'Bch')
418 if len(comment) or isinstance(author,tuple):
419 writer.setComment(author, comment)
420 nUpdates = 0
421 goodComment = True
422 #=== get latest state from db
423 if moduleList!=['CMT']:
424 if since != (MINRUN,MINLBK):
425 justBefore = list(since)
426 if justBefore[1]>MINLBK:
427 justBefore[1] = justBefore[1]-1
428 else:
429 justBefore[0] = justBefore[0]-1
430 justBefore[1] = MAXLBK
431 justBefore = tuple(justBefore)
432 if self.__mode!=2:
433 self.log().info("Reading db state just before %s, i.e. at %s", since,justBefore)
434 self.__updateFromDb(None, folderPath, tag, justBefore, 0)
435 else:
436 self.log().info("Using previous bad channel list from input DB")
437 self.log().info("And comparing it with new list of bad channels")
438 else:
439 self.log().info("Filling db from %s, resetting old status cache", list(since))
440 self.__oldStat = len(self.__oldStat) * [TileBchStatus()]
441
442 #=== print status information
443 self.log().info("Committing changes to DB \'%s\'", db)
444 self.log().info("... using tag \'%s\' and [run,lumi] [%i,%i]",
445 tag,since[0],since[1])
446 if isinstance(author,tuple) and len(author)==3:
447 self.log().info("... author : \'%s\'", author[0] )
448 self.log().info("... comment: \'%s\'", author[1] )
449 else:
450 self.log().info("... author : \'%s\'", author )
451 self.log().info("... comment: \'%s\'", comment )
452
453 #=== default for drawer initialization
454 loGainDefVec = cppyy.gbl.std.vector('unsigned int')()
455 loGainDefVec.push_back(0)
456 hiGainDefVec = cppyy.gbl.std.vector('unsigned int')()
457 hiGainDefVec.push_back(0)
458 comChnDefVec = cppyy.gbl.std.vector('unsigned int')()
459 comChnDefVec.push_back(0)
460 defVec = cppyy.gbl.std.vector('std::vector<unsigned int>')()
461 defVec.push_back(loGainDefVec)
462 defVec.push_back(hiGainDefVec)
463 defVec.push_back(comChnDefVec)
464
465 #=== loop over the whole detector
466 bchDecoder = TileBchDecoder(bitPatVer)
467 for ros in range(0,TileCalibUtils.max_ros()):
468 for mod in range(TileCalibUtils.getMaxDrawer(ros)):
469 modName = TileCalibUtils.getDrawerString(ros,mod)
470 nChange = self.checkModuleForChanges(ros,mod)
471 if nChange == 0:
472 if (modName in moduleList or 'ALL' in moduleList):
473 #=== write module if it was requested even if nothing changed
474 drawerR = self.__reader.getDrawer(ros, mod, None, False, False)
475 if drawerR:
476 writer.getDrawer(ros,mod,drawerR)
477 else:
478 writer.zeroBlob(ros,mod)
479 continue
480 if nChange==-1:
481 nUpdates += 1
482 self.log().info("Drawer %s reset to GOOD", modName)
483 if modName not in comment and ("ONL" not in folderPath or "syncALL" not in comment):
484 goodComment = False
485 self.log().error("Comment string - '%s' - doesn't contain drawer %s", comment,modName)
486 writer.zeroBlob(ros,mod)
487 else:
488 nUpdates += 1
489 self.log().info("Applying %2i changes to drawer %s", nChange,modName)
490 if modName not in comment and ("ONL" not in folderPath or "syncALL" not in comment):
491 goodComment = False
492 self.log().error("Comment string - '%s' - doesn't contain drawer %s", comment,modName)
493 drawer = writer.getDrawer(ros,mod)
494 drawer.init(defVec,TileCalibUtils.max_chan(),bitPatVer)
495 for chn in range(TileCalibUtils.max_chan()):
496 #=== get low gain bit words
497 wordsLo = bchDecoder.encode(self.getAdcStatus(ros,mod,chn,0))
498 chBits = wordsLo[0]
499 loBits = wordsLo[1]
500 #=== get high gain bit words
501 wordsHi = bchDecoder.encode(self.getAdcStatus(ros,mod,chn,1))
502 chBits = wordsHi[0] | chBits
503 hiBits = wordsHi[1]
504 #=== set low, high and common channel word in calibDrawer
505 drawer.setData(chn,0,0, loBits)
506 drawer.setData(chn,1,0, hiBits)
507 drawer.setData(chn,2,0, chBits)
508 #=== synchronizing channel status in low and high gain
509 if wordsLo[0] != chBits:
510 self.log().info("Drawer %s ch %2d - sync LG status with HG ", modName,chn)
511 status = TileBchStatus( bchDecoder.decode(chBits,loBits) )
512 self.setAdcStatus(ros,mod,chn,0,status)
513 if wordsHi[0] != chBits:
514 self.log().info("Drawer %s ch %2d - sync HG status with LG ", modName,chn)
515 status = TileBchStatus( bchDecoder.decode(chBits,hiBits) )
516 self.setAdcStatus(ros,mod,chn,1,status)
517
518 #=== register
519 if nUpdates>0 or moduleList==['CMT']:
520 if goodComment:
521 self.log().info("Attempting to register %i modified drawers..." , nUpdates)
522 writer.register(since,tag)
523 else:
524 self.log().error("Aborting update due to errors in comment string")
525 else:
526 self.log().warning("No drawer modifications detected, ignoring commit request")
527
528
529 #____________________________________________________________________
531 """
532 Returns bad status definition
533 """
535
537 """
538 Returns bad time status definition
539 """
#define min(a, b)
Definition cfImp.cxx:40
Class providing the association between TileCal problems and status word bits.
static std::string getDescription(const Prb &prb)
Get description of problem.
Class holding bad channel problems.
static void defineBadTiming(const TileBchStatus &status)
static void defineBad(const TileBchStatus &status)
static unsigned int getAdcIdx(unsigned int ros, unsigned int drawer, unsigned int channel, unsigned int adc)
Returns an ADC hash.
static unsigned int max_gain()
Python compatibility function.
static unsigned int badtiming_definition_chan()
Python compatibility function.
static std::string getDrawerString(unsigned int ros, unsigned int drawer)
Return the drawer name, e.g.
static unsigned int max_chan()
Python compatibility function.
static unsigned int max_ros()
Python compatibility function.
static unsigned int getMaxDrawer(unsigned int ros)
Returns the maximal channel number for a given drawer.
static unsigned int definitions_draweridx()
Python compatibility function.
static unsigned int bad_definition_chan()
Python compatibility function.
setAdcProblems(self, ros, drawer, channel, adc, problems)
setADCStatus(self, module, channel, adc, status)
setADCProblems(self, module, channel, adc, problems)
__getAdcIdx(self, ros, drawer, channel, adc)
getAdcProblems(self, ros, drawer, channel, adc)
addAdcProblem(self, ros, drawer, channel, adc, problem)
listBadAdcs(self, rosBeg=0, modBeg=0, rosEnd=5, modEnd=64)
checkModuleForChanges(self, ros, drawer)
delADCProblem(self, module, channel, adc, problem)
delAdcProblem(self, ros, drawer, channel, adc, problem)
addADCProblem(self, module, channel, adc, problem)
getADCProblems(self, module, channel, adc)
updateFromDb(self, db, folderPath, tag, runLumi, fillTable=1, mode=None, ros=-1, module=-1)
getADCStatus(self, module, channel, adc)
__updateFromDb(self, db, folderPath, tag, runLumi, fillTable=1, ros=-1, module=-1)
commitToDb(self, db, folderPath, tag, bitPatVer, author, comment, since, moduleList=[])
getAdcStatus(self, ros, drawer, channel, adc)
setAdcStatus(self, ros, drawer, channel, adc, status)
void initialize()