ATLAS Offline Software
hancoolmod.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2 
3 
4 import os
5 
6 import glob
7 
8 # Needed to correct ROOT behavior; see below
9 CWD = 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
13 from ROOT import gSystem
14 
15 # Import the ROOT library for reading han results
16 gSystem.Load('libDataQualityUtils')
17 from ROOT import dqutils
18 
19 import collections
20 import logging
21 
22 defect_val = collections.namedtuple('defect_val',
23  'defect, comment, recoverable')
24 defect_iov = collections.namedtuple('defect_iov',
25  'defect, comment, recoverable, since, until')
26 
27 logger = logging.getLogger('hancoolmod')
28 logger.addHandler(logging.NullHandler())
29 
30 os.chdir(CWD)
31 
32 
33 # LumiBlock length (in minutes)
34 LBlength = 1.0
35 
36 
37 # Mapping different interval types
38 intervalType = {
39  0: "_minutes30_",
40  1: "_minutes10_",
41  2: "_medStat_",
42  3: "_lowStat_",
43  4: "ERROR"
44 }
45 
46 # Generates LB from filename
47 def 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 
88 def 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 
103 def 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 
112 def 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 
128 def 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 
179 def 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 
189 def 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 
215 def 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 
253 def iovs_merge(l):
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 
277 def 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 
298 def 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 
315 def 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)
python.hancoolmod.sct_perlb_defects
def sct_perlb_defects(d, i, runNumber)
Definition: hancoolmod.py:215
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition: openCoraCool.cxx:569
python.hancoolmod.hancool_defects
def hancool_defects(runNumber, filePath="./", dbConnection="", isESn=True)
Definition: hancoolmod.py:315
python.hancoolmod.sct_lowstat_defect
def sct_lowstat_defect(d, i, runNumber)
Definition: hancoolmod.py:179
python.hancoolmod.ctp_defects
def ctp_defects(d, i, runNumber)
Definition: hancoolmod.py:128
python.hancoolmod.iovs_merge
def iovs_merge(l)
Definition: hancoolmod.py:253
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.hancoolmod.hancool
def hancool(runNumber=3070, filePath="/afs/cern.ch/user/a/atlasdqm/dqmdisk/han_results/tier0/FDR2/NoStream/", dbConnection="sqlite://;schema=MyCOOL.db;dbname=CONDBR2", isESn=True)
Definition: hancoolmod.py:103
python.constants.DEFECT_IOV
def DEFECT_IOV(channel, present, recoverable, user, comment)
Definition: DataQuality/DQDefects/python/constants.py:18
python.hancoolmod.dqmf_node_defect
def dqmf_node_defect(node, defect, badstatuses=['Red'])
Definition: hancoolmod.py:298
python.hancoolmod.sct_eff_defect
def sct_eff_defect(d, i, runNumber)
Definition: hancoolmod.py:277
python.hancoolmod.stringGetResult
def stringGetResult(file, rootFolder)
Definition: hancoolmod.py:88
python.sugar.runlumi.RunLumi
RunLumi
Definition: runlumi.py:131
python.db.DefectsDB
Definition: DQDefects/python/db.py:42
dqutils::HanOutputFile
Definition: HanOutputFile.h:32
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
python.hancoolmod.getLimits
def getLimits(name)
Definition: hancoolmod.py:47
python.hancoolmod.defect_iov
defect_iov
Definition: hancoolmod.py:24
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
python.hancoolmod.sct_conf_defects
def sct_conf_defects(d, i, runNumber)
Definition: hancoolmod.py:189
python.hancoolmod.defect_val
defect_val
Definition: hancoolmod.py:22
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:224
min
#define min(a, b)
Definition: cfImp.cxx:40
Trk::open
@ open
Definition: BinningType.h:40
Cut::all
@ all
Definition: SUSYToolsAlg.cxx:64
CaloLCW_tf.group
group
Definition: CaloLCW_tf.py:28
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
str
Definition: BTagTrackIpAccessor.cxx:11
readCCLHist.float
float
Definition: readCCLHist.py:83
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.hancoolmod.detmask_defects
def detmask_defects(runNumber)
Definition: hancoolmod.py:112