ATLAS Offline Software
Loading...
Searching...
No Matches
CaloCondTools.py
Go to the documentation of this file.
1#!/bin/env python
2
3# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
4# CaloCondTools.py
5# Nils Gollub <nils.gollub@cern.ch>, 2007-11-23
6# Carlos Solans <carlos.solans@cern.ch>, 2012-11-07
7"""
8Python helper module for managing COOL DB connections and CaloCondBlobs.
9"""
10
11import os
12import cppyy
13from PyCool import cool
14import time, re
15g = cppyy.gbl
16
17
18#=== get a logger
19from CaloCondBlobAlgs.CaloCondLogger import CaloCondLogger, getLogger
20log = getLogger("CaloCondTools")
21
22#=== useful constants
23MINRUN = 0
24MINLBK = 0
25MAXRUN = cool.Int32Max
26MAXLBK = cool.UInt32Max
27UNIX2COOL = 1000000000
28UNIXTMAX = cool.Int32Max
29
30#
31#______________________________________________________________________
33 """
34 Returns the common Calo prefix used for all COOL folders
35 """
36 return "/CALO/"
37
38#
39#______________________________________________________________________
41 """
42 Returns the run-lumi type folder description needed to read back the folder content
43 as a CondAttrListCollection in Athena.
44 """
45 desc ='<timeStamp>run-lumi</timeStamp>'
46 desc+='<addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader>'
47 desc+='<typeName>CondAttrListCollection</typeName>'
48 return desc
49
50#
51#______________________________________________________________________
52def getAthenaFolderType(folderDescr):
53 type = re.compile(".*<timeStamp>(.*)</timeStamp>.*").search(folderDescr)
54 if not type:
55 raise Exception("No folder type info found in \'%s\'" % folderDescr)
56 return type.groups()[0]
57
58#
59#______________________________________________________________________
60def openDb(db, instance, mode="READONLY", schema="COOLOFL_CALO", sqlfn="caloSqlite.db"):
61 """
62 Opens a COOL db connection.
63 - db: The DB type. The following names are recognized:
64 * SQLITE: Opens file mentioned in sqlfn parameter
65 * ORACLE or FRONTIER: Opens ORACLE DB, forces READONLY
66 - instance: One of valid instances - CONDBR2 OFLP200 COMP200 CMCP200
67 - mode: Can be READONLY (default), RECREATE or UPDATE
68 - schema: One of valid schemas - COOLONL_CALO COOLOFL_CALO COOLONL_LAR COOLOFL_LAR COOLONL_TILE COOLOFL_TILE
69 - sqlfn: Name of sqlite file if db is SQLITE
70 """
71 #=== check for valid db names
72 if db is not None:
73 validDb = ["SQLITE", "ORACLE", "FRONTIER"]
74 if db not in validDb:
75 raise Exception( "DB not valid: %s, valid DBs are: %s" % (db,validDb) )
76 elif db == "ORACLE" or db == "FRONTIER":
77 mode = "READONLY"
78
79 #=== check for valid instance names
80 validInstance = ["COMP200", "CONDBR2", "CMCP200", "OFLP200"]
81 if instance not in validInstance:
82 raise Exception( "Instance not valid: %s, valid instance are: %s" % (instance,validInstance) )
83
84 #=== check for valid schema names
85 validSchema = ["COOLONL_CALO","COOLOFL_CALO","COOLONL_LAR","COOLOFL_LAR","COOLONL_TILE","COOLOFL_TILE"]
86 if schema not in validSchema:
87 raise Exception( "Schema not valid: %s, valid schemas are: %s" % (schema,validSchema) )
88
89 #=== construct connection string
90 connStr = ""
91 if db=='SQLITE':
92 if mode=="READONLY" and not os.path.exists(sqlfn):
93 raise Exception( "Sqlite file %s does not exist" % (sqlfn) )
94 if (mode=="RECREATE" or mode=="UPDATE") and not os.path.exists(os.path.dirname(sqlfn)):
95 dirn=os.path.dirname(sqlfn)
96 if dirn:
97 os.makedirs(dirn)
98 connStr="sqlite://X;schema=%s;dbname=%s" % (sqlfn,instance)
99 elif db=='FRONTIER':
100 connStr='frontier://ATLF/()/;schema=ATLAS_%s;dbname=%s' % (schema,instance)
101 elif db=='ORACLE':
102 connStr='oracle://%s;schema=ATLAS_%s;dbname=%s' % ('ATLAS_COOLPROD',schema,instance)
103 else:
104 connStr=schema+'/'+instance
105
106 return openDbConn(connStr, mode)
107
108#
109#______________________________________________________________________
110def openDbConn(connStr, mode="READONLY"):
111 """
112 Opens a COOL db connection.
113 - connStr: The DB connection string
114 - mode: Can be READONLY (default), RECREATE or UPDATE
115 or ORACLE or FRONTIER if connStr is only short name of the database
116 """
117
118 #=== split the name into schema and dbinstance
119 splitname=connStr.split('/')
120 if (len(splitname)!=2): # string connection ready to be used as it is
121 connStr_new=connStr
122 else: # construct connection string
123 schema=splitname[0]
124 instance=splitname[1]
125 if mode=="ORACLE":
126 connStr_new='oracle://%s;schema=ATLAS_%s;dbname=%s' % ('ATLAS_COOLPROD',schema,instance)
127 else:
128 connStr_new='frontier://ATLF/()/;schema=ATLAS_%s;dbname=%s' % (schema,instance)
129
130 #=== get dbSvc and print header info
131 dbSvc = cool.DatabaseSvcFactory.databaseService()
132 log.info( "---------------------------------------------------------------------------------" )
133 log.info( "-------------------------- CaloCondTools.openDbConn -----------------------------" )
134 log.info( "- using COOL version %s", dbSvc.serviceVersion() )
135 log.info( "- opening CaloDb: %s",connStr_new )
136 log.info( "- mode: %s", mode )
137 log.info( "---------------------------------------------------------------------------------" )
138
139 #=== action depends on mode
140 if mode in ["READONLY","ORACLE","FRONTIER","",None]:
141 #=== open read only
142 try:
143 db=dbSvc.openDatabase(connStr_new,True)
144 except Exception as e:
145 log.debug( e )
146 log.critical("Could not connect to %s" % connStr_new )
147 return None
148 return db
149 elif mode=="RECREATE":
150 #=== recreating database
151 dbSvc.dropDatabase(connStr_new)
152 try:
153 db = dbSvc.createDatabase(connStr_new)
154 except Exception as e:
155 log.debug( e )
156 log.critical( "Could not create database, giving up..." )
157 return None
158 return db
159 elif mode=="UPDATE":
160 #=== update database
161 try:
162 db=dbSvc.openDatabase(connStr_new,False)
163 except Exception as e:
164 log.debug( e )
165 log.warning( "Could not connect to \'%s\', trying to create it....", connStr_new )
166 try:
167 db=dbSvc.createDatabase(connStr_new)
168 except Exception as e:
169 log.debug( e )
170 log.critical( "Could not create database, giving up..." )
171 return None
172 return db
173 else:
174 log.error("Mode \"%s\" not recognized", mode )
175 return None
176
177#
178#______________________________________________________________________
179def iovFromRunLumi(runNum, lbkNum):
180 """
181 Returns COOL timeStamp build from run and lumi block numbers
182 """
183 return (int(runNum)<<32) + int(lbkNum)
184
185#
186#____________________________________________________________________
188 """
189 Returns run and lumi block numbers from COOL timeStamp
190 """
191 return (int(iov >> 32), int(iov & 0xFFFFFFFF))
192
193#
194#______________________________________________________________________
195def decodeTimeString(timeString):
196 """
197 Returns UNIX time stamp given an input time string
198 """
199 return int(time.mktime(time.strptime(timeString,"%Y-%m-%d %H:%M:%S")))
200
201#
202#______________________________________________________________________
203def getCoolValidityKey(pointInTime, isSince=True):
204 """
205 The interpretation of pointInTime depends on their type:
206 - tuple(int,int) : run and lbk number
207 - integer : Values are interpreted as unix time stamps
208 - string : time stamp of format 'yyyy-mm-dd hh:mm:ss'
209 """
210
211 validityKey = None
212
213 #=== string: convert to unix time and treat as latter
214 if isinstance(pointInTime, str):
215 pointInTime = decodeTimeString(pointInTime)
216
217 #=== integer: unix time stamp
218 if isinstance(pointInTime, int):
219 if pointInTime >=0:
220 validityKey = pointInTime * UNIX2COOL
221 else:
222 if isSince:
223 validityKey = int(time.time()) * UNIX2COOL
224 else :
225 validityKey = cool.ValidityKeyMax
226 #=== run-lumi tuple
227 elif isinstance(pointInTime, tuple):
228 validityKey = iovFromRunLumi(pointInTime[0],pointInTime[1])
229 #=== catch other types
230 else:
231 raise Exception("Unrecognized pointInTime type=%s" % type(pointInTime))
232 return cool.ValidityKey(validityKey)
233
234#
235#______________________________________________________________________
236def getCellHash(detectorId,part,module,sample,tower):
237 """
238 Returns cell subHash given partition,module,sample, and tower (TILE only)
239 """
240 if detectorId != 48:
241 raise Exception('getCellHash only available for TileCells.')
242 section = 0 # 1=LB* 2=EB* 3=ITC
243 side = 0 # 1=A-side -1=C-side
244 if part == 1 or part == 2:
245 section = 1
246 elif part == 3 or part == 4:
247 if sample == 3 or (sample==1 and tower==9) or (sample==2 and tower==8): # Gap and ITC
248 section = 3
249 else:
250 section = 2
251 if part%2 ==0:
252 side = -1
253 else:
254 side = 1
255 if section==0 or side==0 or module>63 or sample>3 or tower>15:
256 raise Exception('Non-physical cell specification')
257
258 # hash array for mod0 of entire TileCal. Use to determine cell hash for any cell
259 hash = [0, 1, -1, -1, 2, 3, -1, -1, 4, 5, 6, -1, 7, 8, -1, -1, 9, 10, 11, -1, 12, 13, -1, -1, 14, 15, 16, -1, 17, 18, -1, -1, 19, 20, 4416, -1, 21, 4417, -1, -1, -1, 2880, 2881, 4418, 2882, 2883, -1, 4419, 2884, 2885, 2886, -1, 2887, 2888, -1, 4420, 2889, 2890, -1, -1, 2891, -1, -1, 4421, 1408, 1409, 1410, -1, 1411, 1412, -1, -1, 1413, 1414, 1415, -1, 1416, 1417, -1, -1, 1418, 1419, 1420, -1, 1421, 1422, -1, -1, 1423, 1424, 1425, -1, 1426, 1427, -1, -1, 1428, 1429, 4800, -1, 1430, 4801, -1, -1, -1, 3648, 3649, 4802, 3650, 3651, -1, 4803, 3652, 3653, 3654, -1, 3655, 3656, -1, 4804, 3657, 3658, -1, -1, 3659, -1, -1, 4805]
260
261 # Section specific offset to be added to cell hash for module different from 0
262 # [LBC,LBA,EBC,EBA,ITC-C,ITC-A]
263 modOffset = [22,23,12,12,6,6]
264 if side==1:
265 sideOffset=1
266 else:
267 sideOffset=0
268
269 return hash[sideOffset*16*4+tower*4+sample]+modOffset[sideOffset+2*(section-1)]*module
270
271#======================================================================
272#===
273#=== CaloBlobReader
274#===
275#======================================================================
276
277#
278#______________________________________________________________________
279class CaloBlobReader(CaloCondLogger):
280 """
281 CaloBlobReader is a helper class, managing the details of COOL interactions
282 """
283
284 #____________________________________________________________________
285 def __init__(self, db, folder, tag=""):
286 """
287 Input:
288 - db : db should be an open database connection
289 - folder: full folder path
290 - tag : The folder tag, e.g. \"000-00\"
291 """
292 #=== initialize base class
293 CaloCondLogger.__init__(self,"CaloBlobReader")
294
295 #=== try to open db
296 try:
297 self.__db = db
298 self.__folder = self.__db.getFolder(folder)
299 except Exception as e:
300 self.log().critical( e )
301 raise
302
303 #=== determine if "run-lumi" or "time" folder
304 validFolderTypes = ("run-lumi","time")
305 folderDescr = self.__folder.description()
307 if self.__folderType not in validFolderTypes:
308 raise Exception("Invalid folder type found: \'%s\'" % self.__folderType)
309
310 #=== use camelized full folder path only if tag is given
311 self.__tag = tag
312
313 #=== initialize dictionary to keep reference to DB object
314 #=== and timestamp, so they do not go out of scope
315 self.__objDict = {}
316
317 #____________________________________________________________________
318 def getCells(self, systemId, pointInTime):
319 """
320 Returns a CaloCondBlob object for the given system.
321 """
322
323 validityKey = getCoolValidityKey(pointInTime)
324 self.log().debug("Validity key is %s", validityKey)
325 try:
326 #=== Have we retrieved data previously?
327 key = (systemId,validityKey)
328 obj = self.__objDict.get(key)
329 #=== ... if not, get it from DB
330 if not obj:
331 channelId = cool.ChannelId(systemId)
332 obj = self.__folder.findObject(validityKey, channelId, self.__tag)
333 self.log().debug("Fetching from DB: %s", obj)
334 blob = obj.payload()[0]
335 self.log().debug("blob size: %d", blob.size())
336 #=== store object in dictionary
337 self.__objDict[key] = obj
338 #=== get blob
339 blob = obj.payload()[0]
340 self.log().debug("blob size: %d", blob.size())
341
342 #=== create CaloCondBlob object
343 flt = g.CaloCondBlobFlt.getInstance(blob)
344 return flt
345 except Exception as e:
346 self.log().error("Fetching of systemId=%i failed with exception %s",systemId,e)
347 return None
348
349 #____________________________________________________________________
350 def getDBobjsWithinRange(self, chan, point1inTime=(0,0), point2inTime=(2147483647,4294967295), printError=True):
351 """
352 Returns all DB objects for the given COOL channel, within given validity range -- default: [0-Infinity)
353 """
354
355 validityKey1 = getCoolValidityKey(point1inTime,True)
356 validityKey2 = getCoolValidityKey(point2inTime,False)
357
358 #print "Validity keys range is %s - %s" % (validityKey1, validityKey2)
359 self.log().debug("Validity key range is %s - %s", validityKey1,validityKey2)
360
361 objs = None
362 try:
363 dbChanSel = cool.ChannelSelection(chan)
364 #self.log().debug("Fetching blobs from DB: %s" % obj)
365 objs = self.__folder.browseObjects(validityKey1,validityKey2,dbChanSel,self.__tag)
366 except Exception as e:
367 if printError:
368 self.log().error("CaloCondTools.getDBobjsWithinRange(): Fetching of COOL_chan=%i failed with exception %s", chan,e)
369
370 return objs
371
372 #____________________________________________________________________
374 """
375 Returns true if MultiVersion folder is connected
376 """
377 if self.__folder.versioningMode()==cool.FolderVersioning.MULTI_VERSION:
378 return True
379 else:
380 return False
381
382
383#======================================================================
384#===
385#=== CaloBlobWriter
386#===
387#======================================================================
388
389#
390#______________________________________________________________________
391class CaloBlobWriter(CaloCondLogger):
392 """
393 Helper class that enables writing to Calo DB
394 """
395
396 #____________________________________________________________________
397 def __init__(self, db, folderPath, caloBlobType=None,
398 isMultiVersionFolder=True, isRunLumiTimeStamp=True):
399 """
400 Input:
401 - db : db should be an open database connection
402 - folderPath: full folder path to create or update
403 """
404
405 #=== initialize base class
406 CaloCondLogger.__init__(self,"CaloBlobWriter")
407
408 #=== store db
409 self.__db = db
410
411 #=== determine folder mode
412 folderMode = cool.FolderVersioning.MULTI_VERSION
413 if not isMultiVersionFolder:
414 folderMode = cool.FolderVersioning.SINGLE_VERSION
415
416 #=== determine folder description
417 folderDescr = getAthenaFolderDescr()
418
419 #=== access folder in db
420 try:
421 #=== create folder if it does not exist
422 if self.__db.existsFolder(folderPath):
423 self.__folder = self.__db.getFolder(folderPath)
424 #=== check folder mode
425 modeInCool = self.__folder.versioningMode()
426 if modeInCool!=folderMode:
427 str = "Incompatible folder mode detected, COOL folder has type "
428 if modeInCool==cool.FolderVersioning.MULTI_VERSION:
429 str += "MULTI"
430 else:
431 str += "SINGLE"
432 raise Exception(str)
433 else:
434 #=== create folder if it does not exist
435 payloadSpec = cool.RecordSpecification()
436 payloadSpec.extend( 'CaloCondBlob16M', cool.StorageType.Blob16M )
437 folderSpec = cool.FolderSpecification(folderMode, payloadSpec)
438 self.__folder = db.createFolder(folderPath, folderSpec, folderDescr, True)
439 except Exception as e:
440 self.log().critical( e )
441 raise
442
443 #=== initialize channel dictionaries
444 self.__chanDictRecord = {} # <int, cool.Record >
445 self.__chanDictCells = {} # <int, CaloCondBlobFlt>
446
447 #____________________________________________________________________
448 def register(self, since=(MINRUN,MINLBK), until=(MAXRUN,MAXLBK), tag=""):
449 """
450 Registers the folder in the database.
451 - since: lower limit of IOV
452 - until: upper limit of IOV
453 - tag : The cool folder tag to write to
454
455 The interpretation of the 'since' and 'until' inputs depends on their type:
456 - tuple(int,int) : run and lbk number
457 - integer : Values are interpreted as unix time stamps
458 If since<0, current time is assumed
459 If until<0, infinity is assumed
460 - string : time stamp of format 'yyyy-mm-dd hh:mm:ss'
461 """
462
463 #=== check for inconsistent input
464 if type(since)!=type(until):
465 raise Exception("Inconsistent types: since=%s, until=%s" % (type(since),type(until)))
466
467 #=== write to user tag only if multiversion mode
468 userTagOnly = True
469 if self.__folder.versioningMode()==cool.FolderVersioning.SINGLE_VERSION:
470 userTagOnly = False
471 #=== no folder Tag allowed for singleversion
472 if tag!="":
473 self.log().warning( "Trying to store with tag \"%s\" to SINGLE_VERSION folder", tag )
474 self.log().warning( "... resetting tag to \"\"!" )
475 tag=""
476
477 #=== get IOV limits
478 sinceCool = getCoolValidityKey(since, True )
479 untilCool = getCoolValidityKey(until, False)
480 if untilCool <= sinceCool:
481 raise Exception("Until(%i) <= Since(%i)" % (untilCool,sinceCool))
482
483 #=== build IOV string
484 iovString = ""
485 if isinstance(since, tuple):
486 iovString = "[%i,%i] - [%i,%i]" % (since[0],since[1],until[0],until[1])
487 else:
488 sinceInfo = time.localtime( sinceCool//UNIX2COOL )
489 untilInfo = time.localtime(min(UNIXTMAX, (untilCool//UNIX2COOL)))
490 untilStr = "<infinity>"
491 if untilCool<cool.ValidityKeyMax:
492 untilStr = time.asctime(untilInfo)
493 if (untilCool//UNIX2COOL)>UNIXTMAX:
494 untilStr = " > "+untilStr
495 iovString = "[%s] - [%s]" % (time.asctime(sinceInfo), untilStr)
496
497 #=== build tag
498 folderTag=tag
499
500 #=== print info
501 self.log().info( "Registering folder %s with tag \"%s\"", self.__folder.fullPath(),folderTag)
502 self.log().info( "... with IOV : %s", iovString )
503 #self.log().info( "... with comment field: \"%s\"", self.__chanDictDrawer[48].getComment() )
504
505 #=== register all channels by increasing channel number
506 chanList = sorted(self.__chanDictRecord.keys())
507 for chanNum in chanList:
508 data = self.__chanDictRecord[chanNum]
509 strout = "cool channel=%4i" % chanNum
510 self.log().debug("Registering %s %s", strout, data)
511 channelId = cool.ChannelId(chanNum)
512 self.__folder.storeObject(sinceCool, untilCool, data, channelId, folderTag, userTagOnly)
513
514 #____________________________________________________________________
515 def getCells(self, systemId):
516 """
517 Returns a CaloCondBlob object of given system Id.
518 """
519
520 #try:
521 chanNum = cool.ChannelId(systemId)
522 flt = self.__chanDictCells.get(chanNum,None)
523 #=== ... if not, get it from DB
524 if not flt:
525 #=== create new blob
526 spec = self.__folder.payloadSpecification()
527 data = cool.Record( spec )
528 self.__chanDictRecord[chanNum] = data
529 for key in data:
530 blob = data[key]
531 flt = g.CaloCondBlobFlt.getInstance(blob)
532
533 self.__chanDictCells[chanNum] = flt
534 return flt
535
536 #except Exception as e:
537 # self.log().critical( e )
538 # return None
539
540 #____________________________________________________________________
541 #def setComment(self, author, comment):
542 # """
543 # Sets a general comment in the comment channel.
544 # """
545 # try:
546 # chanNum = TileCalibUtils.getCommentChannel()
547 # data = self.__chanDictRecord.get(chanNum)
548 # if not data:
549 # spec = self.__folder.payloadSpecification()
550 # data = cool.Record( spec )
551 # self.__chanDictRecord[chanNum] = data
552 # blob = data['TileCalibBlob']
553 # cmt = TileCalibDrawerCmt.getInstance(blob,author,comment)
554 # except Exception as e:
555 # self.log().critical( e )
556
557
558
559 #____________________________________________________________________
560 def zeroBlob(self, systemId):
561 """
562 Resets blob size to zero
563 """
564 try:
565 chanNum = cool.ChannelId(systemId)
566 data = self.__chanDictRecord.get(systemId)
567 if not data:
568 spec = self.__folder.payloadSpecification()
569 data = cool.Record( spec )
570 self.__chanDictRecord[chanNum] = data
571 blob = data['CaloCondBlob16M']
572 blob.resize(0)
573 except Exception as e:
574 self.log().critical( e )
575 return None
576
const bool debug
#define min(a, b)
Definition cfImp.cxx:40
getCells(self, systemId, pointInTime)
__init__(self, db, folder, tag="")
getDBobjsWithinRange(self, chan, point1inTime=(0, 0), point2inTime=(2147483647, 4294967295), printError=True)
__init__(self, db, folderPath, caloBlobType=None, isMultiVersionFolder=True, isRunLumiTimeStamp=True)
#define register
Definition dictionary.h:21
std::string description
glabal timer - how long have I taken so far?
Definition hcg.cxx:91
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:130
void search(TDirectory *td, const std::string &s, std::string cwd, node *n)
recursive directory search for TH1 and TH2 and TProfiles
Definition hcg.cxx:739
decodeTimeString(timeString)
getCoolValidityKey(pointInTime, isSince=True)
iovFromRunLumi(runNum, lbkNum)
openDbConn(connStr, mode="READONLY")
openDb(db, instance, mode="READONLY", schema="COOLOFL_CALO", sqlfn="caloSqlite.db")
getCellHash(detectorId, part, module, sample, tower)
getAthenaFolderType(folderDescr)