ATLAS Offline Software
Loading...
Searching...
No Matches
hancoolmod.py
Go to the documentation of this file.
1# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2
3
4import os
5
6import glob
7
8# Needed to correct ROOT behavior; see below
9CWD = os.getcwd()
10# Importing gSystem may change the current directory to one of the
11# command-line arguments; chdir to original directory to have
12# predictable behavior
13from ROOT import gSystem
14
15# Import the ROOT library for reading han results
16gSystem.Load('libDataQualityUtils')
17from ROOT import dqutils
18
19import collections
20import logging
21
22defect_val = collections.namedtuple('defect_val',
23 'defect, comment, recoverable')
24defect_iov = collections.namedtuple('defect_iov',
25 'defect, comment, recoverable, since, until')
26
27logger = logging.getLogger('hancoolmod')
28logger.addHandler(logging.NullHandler())
29
30os.chdir(CWD)
31
32
33# LumiBlock length (in minutes)
34LBlength = 1.0
35
36
37# Mapping different interval types
38intervalType = {
39 0: "_minutes30_",
40 1: "_minutes10_",
41 2: "_medStat_",
42 3: "_lowStat_",
43 4: "ERROR"
44}
45
46# Generates LB from filename
47def getLimits(name):
48 try:
49 import re
50 from . import detmaskmod
51 runNumber = re.match(r'run_(\d+)_.*han.root', name).group(1)
52 max_hi_limit = detmaskmod.getNumLumiBlocks(int(runNumber))+1
53 if (name.find('minutes10_') > -1):
54 t = name.split('minutes10_')
55 digit = float(((t[len(t)-1]).split('_'))[0])
56 low_limit = int((digit-1.0)*10.0/LBlength+1)-1
57 hi_limit = int(digit*10.0/LBlength)
58 elif (name.find('minutes30_') > -1):
59 t = name.split('minutes30_')
60 digit = float(((t[len(t)-1]).split('_'))[0])
61 low_limit = int((digit-1.0)*30.0/LBlength+1)-1
62 hi_limit = int(digit*30.0/LBlength)
63 elif 'lowStat_' in name:
64 t = name.split('lowStat_LB')[-1]
65 t = t.split('_han.root')[0]
66 digits = t.split('-')
67 low_limit = int(digits[0])
68 hi_limit = min(int(digits[1])+1, max_hi_limit)
69 elif 'medStat_' in name:
70 t = name.split('medStat_LB')[-1]
71 t = t.split('_han.root')[0]
72 digits = t.split('-')
73 low_limit = int(digits[0])
74 hi_limit = min(int(digits[1])+1, max_hi_limit)
75 else:
76 low_limit = 1
77 hi_limit = max_hi_limit
78 except Exception as e:
79 logging.warning('Could not determine limits because: %s', e)
80 low_limit = 1
81 hi_limit = 4294967295
82
83 return (low_limit, hi_limit)
84
85# Looks up the result in the HanOutputFile
86
87
88def stringGetResult(file, rootFolder):
89 rootFolder = file+":"+rootFolder
90 of = dqutils.HanOutputFile(file)
91# result = of.getStatus(rootFolder) #until DataQualityUtils-02-02-00
92 result = of.getStringName(rootFolder)
93 return result
94
95# /afs/cern.ch/user/a/atlasdqm/dqmdisk/han_results/tier0/FDR2/NoStream/run_3070_han.root
96
97# path="/afs/cern.ch/user/a/atlasdqm/dqmdisk/han_results/tier0/physics_HLT_Cosmic_AllTeIDSelected/"
98# path="/afs/cern.ch/user/a/atlasdqm/dqmdisk/han_results/tier0/FDR2/NoStream/"
99
100# -------------------------------------------------------------
101
102
103def hancool(runNumber=3070,
104 filePath="/afs/cern.ch/user/a/atlasdqm/dqmdisk/han_results/tier0/FDR2/NoStream/",
105 dbConnection="sqlite://;schema=MyCOOL.db;dbname=CONDBR2", isESn=True):
106
107 logger.info('====> Running hancool_defects')
108 hancool_defects(runNumber, filePath, dbConnection, isESn)
109 logger.info('<==== Done with hancool_defects')
110
111
112def detmask_defects(runNumber):
113 from . import detmaskmod
114 blacks = detmaskmod.decodeBlack(detmaskmod.getRunMask(runNumber),
115 defects=True)
116 nlbs = detmaskmod.getNumLumiBlocks(runNumber)
117 toinsert = []
118 for defect in blacks:
119 toinsert.append(defect_iov(since=1,
120 until=nlbs+1,
121 defect=defect,
122 recoverable=False,
123 comment='Automatically added by hancool')
124 )
125 return toinsert
126
127
128def ctp_defects(d, i, runNumber):
129 mapping = {1: 'TRIG_L1_CTP_CTP_ROD_bcid',
130 2: 'TRIG_L1_CTP_bcid',
131 3: 'TRIG_L1_CTP_CTP_MuCTPI_bcid',
132 4: 'TRIG_L1_CTP_candnumber',
133 5: 'TRIG_L1_CTP_multpt',
134 6: 'TRIG_L1_CTP_roiNum',
135 7: 'TRIG_L1_CTP_roiCand',
136 8: 'TRIG_L1_CTP_bcidrange',
137 9: 'TRIG_L1_CTP_lumiblockrange',
138 10: 'TRIG_L1_CTP_lumiblocktime',
139 11: 'TRIG_L1_CTP_nanosectime',
140 12: 'TRIG_L1_CTP_TAPnoTBP',
141 13: 'TRIG_L1_CTP_TAVnoTAP',
142 14: 'TRIG_L1_CTP_CTPsim',
143 15: 'TRIG_L1_CTP_incompletefragment',
144 # TODO: add missing-orbit bin here, eventually (i.e. 15: 'TRIG_L1_CTP_missingorbit')
145 }
146
147 rv = []
148 when = d.Get('CentralTrigger/ErrorSummary/errorSummaryPerLumiBlock')
149 if not when:
150 return None
151 bad_lbs = {}
152 overflow_bad_lbs = {}
153 for key in mapping:
154 bad_lbs[key] = []
155
156 # loop over error bin numbers defined in dict above
157 for errorBin in mapping:
158 bad_lbs[errorBin] = [bin for bin in range(1, when.GetNbinsX(
159 )+1) if when.GetBinContent(bin, errorBin) > 0] # fix this line, slicing in Y? is that it?
160 overflow_bad_lbs[errorBin] = (
161 when.GetBinContent(when.GetNbinsX()+1, errorBin) > 0)
162
163 message = 'Automatically set'
164
165 for defect in mapping:
166 for lb in bad_lbs[defect]:
167 rv.append(defect_iov(mapping[defect], message, False, lb, lb+1))
168 if overflow_bad_lbs[defect]:
169 message += '; defect occurred past end of monitoring histogram, marking end of run as bad'
170 from . import detmaskmod # ugly: could happen for more than one defect - should be cheap though
171 nlbs = detmaskmod.getNumLumiBlocks(runNumber)
172 rv.append(defect_iov(defect, message,
173 False, when.GetNbinsX(), nlbs+1))
174 # print "The following defects were extracted: " # TODO: remove this line?
175 # print rv # TODO: remove this line?
176 return rv
177
178
179def sct_lowstat_defect(d, i, runNumber):
180 histogram = d.Get('InnerDetector/SCT/Summary/tracksPerRegion')
181 if not histogram:
182 return None
183 if histogram.GetEntries() < 200:
184 return [defect_val('SCT_GLOBAL_LOWSTAT', 'Low statistics', False)]
185 else:
186 return []
187
188
189def sct_conf_defects(d, i, runNumber):
190 def sct_conf_defects_core(d, i, runNumber, histname, mapping):
191 rv = []
192 histogram = d.Get(histname)
193 if not histogram:
194 return None
195 for bin in mapping:
196 threshold = 40
197 if mapping[bin] == 'SCT_MOD_OUT_GT40':
198 threshold = 80
199 if histogram.GetBinContent(bin) > threshold:
200 rv.append(defect_val(
201 mapping[bin], '%.1d modules affected' % histogram.GetBinContent(bin), False))
202 return rv
203
204 rv1 = sct_conf_defects_core(d, i, runNumber, 'InnerDetector/SCT/Summary/SCTConfOutM',
205 {1: 'SCT_MOD_OUT_GT40'})
206 rv2 = sct_conf_defects_core(d, i, runNumber, 'InnerDetector/SCT/Summary/SCTConfNew',
207 {3: 'SCT_MOD_ERR_GT40',
208 5: 'SCT_MOD_NOISE_GT40'})
209 if rv1 is None and rv2 is None:
210 return None
211 else:
212 return (rv1 if rv1 is not None else [])+(rv2 if rv2 is not None else [])
213
214
215def sct_perlb_defects(d, i, runNumber):
216 pairs = [('InnerDetector/SCT/Summary/SCT_LinksWithLinkLevelErrorsVsLbs',
217 'SCT_PERIOD_ERR_GT40', lambda _: _ > 80),
218 ('InnerDetector/SCT/Summary/SCT_LinksWithRODLevelErrorsVsLbs',
219 'SCT_ROD_OUT', lambda _: _ >= 1)]
220
221 rv = []
222 bad_lbs = {}
223 overflow_bad_lbs = {}
224 message = 'Automatically set'
225 foundany = False
226
227 for hname, dname, policy in pairs:
228 when = d.Get(hname)
229 if not when:
230 continue
231 foundany = True
232
233 # extract bad bins
234 bad_lbs[dname] = [bin for bin in range(
235 1, when.GetNbinsX()+1) if policy(when.GetBinContent(bin))]
236 overflow_bad_lbs[dname] = policy(
237 when.GetBinContent(when.GetNbinsX()+1))
238
239 for lb in bad_lbs[dname]:
240 rv.append(defect_iov(dname, message, False, lb, lb+1))
241 if overflow_bad_lbs[dname]:
242 message += '; defect occurred past end of monitoring histogram, marking end of run as bad'
243 from . import detmaskmod # ugly: could happen for more than one defect - should be cheap though
244 nlbs = detmaskmod.getNumLumiBlocks(runNumber)
245 rv.append(defect_iov(dname, message,
246 False, when.GetNbinsX(), nlbs+1))
247 if foundany:
248 return rv
249 else:
250 return None
251
252
254 l.sort(key=lambda x: x.since)
255 rl = []
256 i = 0
257 previous = None
258 until = -1
259 while i < len(l):
260 if not previous:
261 previous = l[i]
262 until = previous.until
263 else:
264 if l[i].since != until or previous.comment != l[i].comment:
265 # we have no more to merge for this range
266 rl.append(previous._replace(until=until))
267 previous = l[i]
268 until = previous.until
269 else:
270 until = l[i].until
271 i += 1
272 if previous:
273 rl.append(previous._replace(until=until))
274 return rl
275
276
277def sct_eff_defect(d, i, runNumber):
278 h1 = d.Get('InnerDetector/SCT/Summary/SctTotalEffBCID_/Results/Status')
279 h2 = d.Get('InnerDetector/SCT/Summary/SctTotalEff_/Results/Status')
280 if not h1 or not h2:
281 return None
282 badstatuses = {'Yellow', 'Red'}
283 statuscheck = []
284 for h in h1, h2:
285 status = set(x.GetName() for x in h.GetListOfKeys())
286 if len(badstatuses & status) > 0:
287 assert len(
288 status) == 1, 'Status must be length one or the file is corrupt'
289 statuscheck.append(True)
290 else:
291 statuscheck.append(False)
292 if all(statuscheck):
293 return [defect_val('SCT_EFF_LT99', 'Automatically set for whole run', False)]
294 else:
295 return []
296
297
298def dqmf_node_defect(node, defect, badstatuses=['Red']):
299 badstatuses = set(badstatuses)
300
301 def dqmf_node_defect_core(d, i, runNumber):
302 filenode = d.Get(node + '_/Results/Status')
303 if not filenode:
304 return None
305 status = set(x.GetName() for x in filenode.GetListOfKeys())
306 if len(badstatuses & status) > 0:
307 assert len(
308 status) == 1, 'Status must be length one or the file is corrupt'
309 return [defect_val(defect, node + ' DQMF color ' + status.pop(), False)]
310 else:
311 return []
312 return dqmf_node_defect_core
313
314
315def hancool_defects(runNumber, filePath="./", dbConnection="", isESn=True):
316 from . import pix_defect
317 analyzers = []
318 if isESn:
319 # CTP
320 analyzers += [ctp_defects]
321 # SCT
322 analyzers += [sct_eff_defect,
323 sct_lowstat_defect,
324 sct_conf_defects,
325 sct_perlb_defects,
326 ]
327
328 if (len(filePath) == 0 or filePath[-1] != '/'):
329 filePath += "/"
330 if (len(dbConnection) < 1):
331 dbConnection = "/afs/cern.ch/user/a/atlasdqm/dqmdisk1/cherrypy-devel/defectstest.db/COMP200"
332
333 import ROOT
334 # Conflict logic: shorter intervals override longer ones
335 defects_by_function = {}
336
337 # empty list for missing high stat, reserved for future use
338
339 fnames = ([[filePath+"run_"+str(runNumber)+"_han.root"], []]
340 + [glob.glob(os.path.join(filePath, 'run_%s%s*_han.root' % (runNumber, intervalType[i]))) for i in [2, 3]])
341
342 for i, itype in enumerate(fnames):
343 ldefects_by_function = {}
344 for globname in itype:
345 filename = os.path.basename(globname)
346
347 since, until = getLimits(filename)
348 default_iov = defect_iov(*([0]*5))
349 default_iov = default_iov._replace(since=since, until=until)
350 #print filename + ':', since, until
351 fobj = ROOT.TFile.Open(globname)
352 for func in analyzers:
353 rv = func(fobj, i, runNumber)
354 if rv is not None:
355 rvt = [default_iov._replace(**(i._asdict())) for i in rv]
356 if func not in ldefects_by_function:
357 ldefects_by_function[func] = rvt
358 else:
359 ldefects_by_function[func] += rvt
360 defects_by_function.update(ldefects_by_function)
361 defects = sum(defects_by_function.values(), [])
362
363 if isESn:
364 globname = fnames[0][0]
365 filename = os.path.basename(globname)
366 since, until = getLimits(filename)
367 try:
368 defects += pix_defect.execute(runNumber, globname, until-1)
369 except Exception as e:
370 logging.warning('Unable to execute pixel hancool code')
371 logging.warning('--> %s: %s', type(e).__name__, e)
372
373 from DQDefects import DefectsDB, DEFECT_IOV
374 from DQUtils.sugar import RunLumi
375 import json
376 ddb = DefectsDB(dbConnection, read_only=False)
377 if isESn:
378 logging.info('Running detmask_defects')
379 dm_defects = detmask_defects(runNumber)
380
381 defectlist = []
382 for defect in dm_defects + iovs_merge(defects):
383 logger.debug('Working with %s', defect)
384 defectlist.append(DEFECT_IOV(RunLumi(runNumber, defect.since),
385 RunLumi(runNumber, defect.until),
386 channel=defect.defect,
387 comment=defect.comment,
388 present=True,
389 recoverable=defect.recoverable,
390 user='sys:hancool'))
391 secret_path=os.environ.get('COOLFLASK_SECRET', '/afs/cern.ch/user/a/atlasdqm/private/coolflask_secret/coolflask_secret.json')
392 auth = json.loads(open(secret_path).read())
393 logger.debug('Flask upload')
394 ddb.insert_multiple(defectlist, use_flask=('sqlite' not in dbConnection),
395 flask_auth=auth)
#define min(a, b)
Definition cfImp.cxx:40
STL class.
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
detmask_defects(runNumber)
sct_eff_defect(d, i, runNumber)
ctp_defects(d, i, runNumber)
sct_perlb_defects(d, i, runNumber)
sct_lowstat_defect(d, i, runNumber)
stringGetResult(file, rootFolder)
Definition hancoolmod.py:88
sct_conf_defects(d, i, runNumber)
dqmf_node_defect(node, defect, badstatuses=['Red'])
hancool_defects(runNumber, filePath="./", dbConnection="", isESn=True)
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)