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