CMS 3D CMS Logo

runTheMatrix.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 from __future__ import print_function
3 import sys, os
4 
5 from Configuration.PyReleaseValidation.MatrixReader import MatrixReader
6 from Configuration.PyReleaseValidation.MatrixRunner import MatrixRunner
7 from Configuration.PyReleaseValidation.MatrixInjector import MatrixInjector,performInjectionOptionTest
8 
9 # ================================================================================
10 
11 def showRaw(opt):
12 
13  mrd = MatrixReader(opt)
14  mrd.showRaw(opt.useInput, opt.refRel, opt.fromScratch, opt.raw, opt.step1Only, selected=opt.testList)
15 
16  return 0
17 
18 # ================================================================================
19 
20 def runSelected(opt):
21 
22  mrd = MatrixReader(opt)
23  mrd.prepare(opt.useInput, opt.refRel, opt.fromScratch)
24 
25  # test for wrong input workflows
26  if opt.testList:
27  definedWf = [dwf.numId for dwf in mrd.workFlows]
28  definedSet = set(definedWf)
29  testSet = set(opt.testList)
30  undefSet = testSet - definedSet
31  if len(undefSet)>0: raise ValueError('Undefined workflows: '+', '.join(map(str,list(undefSet))))
32  duplicates = [wf for wf in testSet if definedWf.count(wf)>1 ]
33  if len(duplicates)>0: raise ValueError('Duplicated workflows: '+', '.join(map(str,list(duplicates))))
34 
35  ret = 0
36  if opt.show:
37  mrd.show(opt.testList, opt.extended, opt.cafVeto)
38  if opt.testList : print('testListected items:', opt.testList)
39  else:
40  mRunnerHi = MatrixRunner(mrd.workFlows, opt.nProcs, opt.nThreads)
41  ret = mRunnerHi.runTests(opt)
42 
43  if opt.wmcontrol:
44  if ret!=0:
45  print('Cannot go on with wmagent injection with failing workflows')
46  else:
47  wfInjector = MatrixInjector(opt,mode=opt.wmcontrol,options=opt.wmoptions)
48  ret= wfInjector.prepare(mrd,
49  mRunnerHi.runDirs)
50  if ret==0:
51  wfInjector.upload()
52  wfInjector.submit()
53  return ret
54 
55 # ================================================================================
56 
57 if __name__ == '__main__':
58 
59  #this can get out of here
60  predefinedSet={
61  'limited' : [5.1, #FastSim ttbar
62  7.3, #CosmicsSPLoose_UP17
63  8, #BH/Cosmic MC
64  25, #MC ttbar
65  4.22, #cosmic data
66  4.53, #run1 data + miniAOD
67  9.0, #Higgs200 charged taus
68  1000, #data+prompt
69  1001, #data+express
70  101.0, #SingleElectron120E120EHCAL
71  136.731, #2016B Photon data
72  136.7611, #2016E JetHT reMINIAOD from 80X legacy
73  136.8311, #2017F JetHT reMINIAOD from 94X reprocessing
74  136.88811,#2018D JetHT reMINIAOD from UL processing
75  136.793, #2017C DoubleEG
76  136.874, #2018C EGamma
77  138.4, #2021 MinimumBias prompt reco
78  138.5, #2021 MinimumBias express
79  139.001, #2021 MinimumBias offline with HLT step
80  140.53, #2011 HI data
81  140.56, #2018 HI data
82  158.01, #reMiniAOD of 2018 HI MC with pp-like reco
83  312.0, #2021/Run3 HI MC Pyquen_ZeemumuJets_pt10 with pp-like reco
84  1306.0, #SingleMu Pt1 UP15
85  2500.601, #test NanoAOD from existing MINI
86  1330, #Run2 2015/2016 MC Zmm
87  135.4, #Run 2 2015/2016 Zee ttbar fastsim
88  10042.0, #2017 ZMM
89  10024.0, #2017 ttbar
90  10824.0, #2018 ttbar
91  2018.1, #2018 ttbar fastsim
92  11634.911, #2021 DD4hep ttbar reading geometry from XML
93  11634.914, #2021 DDD ttbar reading geometry from the DB
94  11634.0, #2021 ttbar (switching to DD4hep by default)
95  11634.7, #2021 ttbar mkFit
96  11834.0, #2021 ttbar PU
97  13234.0, #2021 ttbar fastsim
98  13434.0, #2021 ttbar PU fastsim
99  12434.0, #2023 ttbar
100  20834.0, #2026D88 ttbar (2022 new baseline)
101  20834.75, #2026D88 ttbar with HLT75e33
102  #20834.911, #2026D88 ttbar DD4hep XML
103  21034.999, #2026D88 ttbar premixing stage1+stage2, PU50
104  20896.0, #CE_E_Front_120um D88
105  20900.0, #CE_H_Coarse_Scint D88
106  23234.0, #2026D94 ttbar (exercise with HFNose)
107  25202.0, #2016 ttbar UP15 PU
108  250202.181, #2018 ttbar stage1 + stage2 premix
109  ],
110  'jetmc': [5.1, 13, 15, 25, 38, 39], #MC
111  'metmc' : [5.1, 15, 25, 37, 38, 39], #MC
112  'muonmc' : [5.1, 124.4, 124.5, 20, 21, 22, 23, 25, 30], #MC
113  }
114 
115 
116  import argparse
117  usage = 'usage: runTheMatrix.py --show -s '
118 
119  parser = argparse.ArgumentParser(usage,formatter_class=argparse.ArgumentDefaultsHelpFormatter)
120 
121  parser.add_argument('-b','--batchName',
122  help='relval batch: suffix to be appended to Campaign name',
123  dest='batchName',
124  default='')
125 
126  parser.add_argument('-m','--memoryOffset',
127  help='memory of the wf for single core',
128  dest='memoryOffset',
129  type=int,
130  default=3000)
131 
132  parser.add_argument('--addMemPerCore',
133  help='increase of memory per each n > 1 core: memory(n_core) = memoryOffset + (n_core-1) * memPerCore',
134  dest='memPerCore',
135  type=int,
136  default=1500)
137 
138  parser.add_argument('-j','--nproc',
139  help='number of processes. 0 Will use 4 processes, not execute anything but create the wfs',
140  dest='nProcs',
141  type=int,
142  default=4)
143 
144  parser.add_argument('-t','--nThreads',
145  help='number of threads per process to use in cmsRun.',
146  dest='nThreads',
147  type=int,
148  default=1)
149 
150  parser.add_argument('--nStreams',
151  help='number of streams to use in cmsRun.',
152  dest='nStreams',
153  type=int,
154  default=0)
155 
156  parser.add_argument('--numberEventsInLuminosityBlock',
157  help='number of events in a luminosity block',
158  dest='numberEventsInLuminosityBlock',
159  type=int,
160  default=-1)
161 
162  parser.add_argument('-n','--showMatrix',
163  help='Only show the worflows. Use --ext to show more',
164  dest='show',
165  default=False,
166  action='store_true')
167 
168  parser.add_argument('-e','--extended',
169  help='Show details of workflows, used with --show',
170  dest='extended',
171  default=False,
172  action='store_true')
173 
174  parser.add_argument('-s','--selected',
175  help='Run a pre-defined selected matrix of wf. Deprecated, please use -l limited',
176  dest='restricted',
177  default=False,
178  action='store_true')
179 
180  parser.add_argument('-l','--list',
181  help='Comma separated list of workflow to be shown or ran. Possible keys are also '+str(predefinedSet.keys())+'. and wild card like muon, or mc',
182  dest='testList',
183  default=None)
184 
185  parser.add_argument('-f','--failed-from',
186  help='Provide a matrix report to specify the workflows to be run again. Augments the -l option if specified already',
187  dest='failed_from',
188  default=None)
189 
190  parser.add_argument('-r','--raw',
191  help='Temporary dump the .txt needed for prodAgent interface. To be discontinued soon. Argument must be the name of the set (standard, pileup,...)',
192  dest='raw')
193 
194  parser.add_argument('-i','--useInput',
195  help='Use recyling where available. Either all, or a comma separated list of wf number.',
196  dest='useInput',
197  type=lambda x: x.split(','),
198  default=None)
199 
200  parser.add_argument('-w','--what',
201  help='Specify the set to be used. Argument must be the name of a set (standard, pileup,...) or multiple sets separated by commas (--what standard,pileup )',
202  dest='what',
203  default='all')
204 
205  parser.add_argument('--step1',
206  help='Used with --raw. Limit the production to step1',
207  dest='step1Only',
208  default=False)
209 
210  parser.add_argument('--maxSteps',
211  help='Only run maximum on maxSteps. Used when we are only interested in first n steps.',
212  dest='maxSteps',
213  default=9999,
214  type=int)
215 
216  parser.add_argument('--fromScratch',
217  help='Comma separated list of wf to be run without recycling. all is not supported as default.',
218  dest='fromScratch',
219  type=lambda x: x.split(','),
220  default=None)
221 
222  parser.add_argument('--refRelease',
223  help='Allow to modify the recycling dataset version',
224  dest='refRel',
225  default=None)
226 
227  parser.add_argument('--wmcontrol',
228  help='Create the workflows for injection to WMAgent. In the WORKING. -wmcontrol init will create the the workflows, -wmcontrol test will dryRun a test, -wmcontrol submit will submit to wmagent',
229  choices=['init','test','submit','force'],
230  dest='wmcontrol',
231  default=None)
232 
233  parser.add_argument('--revertDqmio',
234  help='When submitting workflows to wmcontrol, force DQM outout to use pool and not DQMIO',
235  choices=['yes','no'],
236  dest='revertDqmio',
237  default='no')
238 
239  parser.add_argument('--optionswm',
240  help='Specify a few things for wm injection',
241  default='',
242  dest='wmoptions')
243 
244  parser.add_argument('--keep',
245  help='allow to specify for which comma separated steps the output is needed',
246  default=None)
247 
248  parser.add_argument('--label',
249  help='allow to give a special label to the output dataset name',
250  default='')
251 
252  parser.add_argument('--command',
253  help='provide a way to add additional command to all of the cmsDriver commands in the matrix',
254  dest='command',
255  action='append',
256  default=None)
257 
258  parser.add_argument('--apply',
259  help='allow to use the --command only for 1 comma separeated',
260  dest='apply',
261  default=None)
262 
263  parser.add_argument('--workflow',
264  help='define a workflow to be created or altered from the matrix',
265  action='append',
266  dest='workflow',
267  default=None)
268 
269  parser.add_argument('--dryRun',
270  help='do not run the wf at all',
271  action='store_true',
272  dest='dryRun',
273  default=False)
274 
275  parser.add_argument('--testbed',
276  help='workflow injection to cmswebtest (you need dedicated rqmgr account)',
277  dest='testbed',
278  default=False,
279  action='store_true')
280 
281  parser.add_argument('--noCafVeto',
282  help='Run from any source, ignoring the CAF label',
283  dest='cafVeto',
284  default=True,
285  action='store_false')
286 
287  parser.add_argument('--overWrite',
288  help='Change the content of a step for another. List of pairs.',
289  dest='overWrite',
290  default=None)
291 
292  parser.add_argument('--noRun',
293  help='Remove all run list selection from wfs',
294  dest='noRun',
295  default=False,
296  action='store_true')
297 
298  parser.add_argument('--das-options',
299  help='Options to be passed to dasgoclient.',
300  dest='dasOptions',
301  default="--limit 0",
302  action='store')
303 
304  parser.add_argument('--job-reports',
305  help='Dump framework job reports',
306  dest='jobReports',
307  default=False,
308  action='store_true')
309 
310  parser.add_argument('--ibeos',
311  help='Use IB EOS site configuration',
312  dest='IBEos',
313  default=False,
314  action='store_true')
315 
316  parser.add_argument('--sites',
317  help='Run DAS query to get data from a specific site. Set it to empty string to search all sites.',
318  dest='dasSites',
319  default='T2_CH_CERN',
320  action='store')
321 
322  parser.add_argument('--interactive',
323  help="Open the Matrix interactive shell",
324  action='store_true',
325  default=False)
326 
327  parser.add_argument('--dbs-url',
328  help='Overwrite DbsUrl value in JSON submitted to ReqMgr2',
329  dest='dbsUrl',
330  default=None,
331  action='store')
332 
333  gpugroup = parser.add_argument_group('GPU-related options','These options are only meaningful when --gpu is used, and is not set to forbidden.')
334 
335  gpugroup.add_argument('--gpu','--requires-gpu',
336  help='Enable GPU workflows. Possible options are "forbidden" (default), "required" (implied if no argument is given), or "optional".',
337  dest='gpu',
338  choices=['forbidden', 'optional', 'required'],
339  nargs='?',
340  const='required',
341  default='forbidden',
342  action='store')
343 
344  gpugroup.add_argument('--gpu-memory',
345  help='Specify the minimum amount of GPU memory required by the job, in MB.',
346  dest='GPUMemoryMB',
347  type=int,
348  default=8000)
349 
350  gpugroup.add_argument('--cuda-capabilities',
351  help='Specify a comma-separated list of CUDA "compute capabilities", or GPU hardware architectures, that the job can use.',
352  dest='CUDACapabilities',
353  type=lambda x: x.split(','),
354  default='6.0,6.1,6.2,7.0,7.2,7.5,8.0,8.6')
355 
356  # read the CUDA runtime version included in CMSSW
357  cudart_version = None
358  libcudart = os.path.realpath(os.path.expandvars('$CMSSW_RELEASE_BASE/external/$SCRAM_ARCH/lib/libcudart.so'))
359  if os.path.isfile(libcudart):
360  cudart_basename = os.path.basename(libcudart)
361  cudart_version = '.'.join(cudart_basename.split('.')[2:4])
362  gpugroup.add_argument('--cuda-runtime',
363  help='Specify major and minor version of the CUDA runtime used to build the application.',
364  dest='CUDARuntime',
365  default=cudart_version)
366 
367  gpugroup.add_argument('--force-gpu-name',
368  help='Request a specific GPU model, e.g. "Tesla T4" or "NVIDIA GeForce RTX 2080". The default behaviour is to accept any supported GPU.',
369  dest='GPUName',
370  default='')
371 
372  gpugroup.add_argument('--force-cuda-driver-version',
373  help='Request a specific CUDA driver version, e.g. 470.57.02. The default behaviour is to accept any supported CUDA driver version.',
374  dest='CUDADriverVersion',
375  default='')
376 
377  gpugroup.add_argument('--force-cuda-runtime-version',
378  help='Request a specific CUDA runtime version, e.g. 11.4. The default behaviour is to accept any supported CUDA runtime version.',
379  dest='CUDARuntimeVersion',
380  default='')
381 
382  opt = parser.parse_args()
383  if opt.command: opt.command = ' '.join(opt.command)
384  os.environ["CMSSW_DAS_QUERY_SITES"]=opt.dasSites
385  if opt.failed_from:
386  rerunthese=[]
387  with open(opt.failed_from,'r') as report:
388  for report_line in report:
389  if 'FAILED' in report_line:
390  to_run,_=report_line.split('_',1)
391  rerunthese.append(to_run)
392  if opt.testList:
393  opt.testList+=','.join(['']+rerunthese)
394  else:
395  opt.testList = ','.join(rerunthese)
396 
397  if opt.IBEos:
398  from subprocess import getstatusoutput as run_cmd
399 
400  ibeos_cache = os.path.join(os.getenv("LOCALRT"), "ibeos_cache.txt")
401  if not os.path.exists(ibeos_cache):
402  err, out = run_cmd("curl -L -s -o %s https://raw.githubusercontent.com/cms-sw/cms-sw.github.io/master/das_queries/ibeos.txt" % ibeos_cache)
403  if err:
404  run_cmd("rm -f %s" % ibeos_cache)
405  print("Error: Unable to download ibeos cache information")
406  print(out)
407  sys.exit(err)
408 
409  for cmssw_env in [ "CMSSW_BASE", "CMSSW_RELEASE_BASE" ]:
410  cmssw_base = os.getenv(cmssw_env,None)
411  if not cmssw_base: continue
412  cmssw_base = os.path.join(cmssw_base,"src/Utilities/General/ibeos")
413  if os.path.exists(cmssw_base):
414  os.environ["PATH"]=cmssw_base+":"+os.getenv("PATH")
415  os.environ["CMS_PATH"]="/cvmfs/cms-ib.cern.ch"
416  os.environ["SITECONFIG_PATH"]="/cvmfs/cms-ib.cern.ch/SITECONF/local"
417  os.environ["CMSSW_USE_IBEOS"]="true"
418  print(">> WARNING: You are using SITECONF from /cvmfs/cms-ib.cern.ch")
419  break
420  if opt.restricted:
421  print('Deprecated, please use -l limited')
422  if opt.testList: opt.testList+=',limited'
423  else: opt.testList='limited'
424 
425  def stepOrIndex(s):
426  if s.isdigit():
427  return int(s)
428  else:
429  return s
430  if opt.apply:
431  opt.apply=map(stepOrIndex,opt.apply.split(','))
432  if opt.keep:
433  opt.keep=map(stepOrIndex,opt.keep.split(','))
434 
435  if opt.testList:
436  testList=[]
437  for entry in opt.testList.split(','):
438  if not entry: continue
439  mapped=False
440  for k in predefinedSet:
441  if k.lower().startswith(entry.lower()) or k.lower().endswith(entry.lower()):
442  testList.extend(predefinedSet[k])
443  mapped=True
444  break
445  if not mapped:
446  try:
447  testList.append(float(entry))
448  except:
449  print(entry,'is not a possible selected entry')
450 
451  opt.testList = list(set(testList))
452 
453  if opt.wmcontrol:
455  if opt.overWrite:
456  opt.overWrite=eval(opt.overWrite)
457  if opt.interactive:
458  import cmd
459 
460  class TheMatrix(cmd.Cmd):
461  intro = "Welcome to the Matrix (? for help)"
462  prompt = "matrix> "
463 
464  def __init__(self, opt):
465  cmd.Cmd.__init__(self)
466  self.opt_ = opt
467  self.matrices_ = {}
468  tmp = MatrixReader(self.opt_)
469  for what in tmp.files:
470  what = what.replace('relval_','')
471  self.opt_.what = what
472  self.matrices_[what] = MatrixReader(self.opt_)
473  self.matrices_[what].prepare(self.opt_.useInput, self.opt_.refRel,
474  self.opt_.fromScratch)
475  os.system("clear")
476 
477  def do_clear(self, arg):
478  """Clear the screen, put prompt at the top"""
479  os.system("clear")
480 
481  def do_exit(self, arg):
482  print("Leaving the Matrix")
483  return True
484 
485  def default(self, inp):
486  if inp == 'x' or inp == 'q':
487  return self.do_exit(inp)
488 
489  def help_predefined(self):
490  print("\n".join(["predefined [predef1 [...]]\n",
491  "Run w/o argument, it will print the list of known predefined workflows.",
492  "Run with space-separated predefined workflows, it will print the workflow-ids registered to them"]))
493 
494  def complete_predefined(self, text, line, start_idx, end_idx):
495  if text and len(text) > 0:
496  return [t for t in predefinedSet.keys() if t.startswith(text)]
497  else:
498  return predefinedSet.keys()
499 
500  def do_predefined(self, arg):
501  """Print the list of predefined workflows"""
502  print("List of predefined workflows")
503  if arg:
504  for w in arg.split():
505  if w in predefinedSet.keys():
506  print("Predefined Set: %s" % w)
507  print(predefinedSet[w])
508  else:
509  print("Unknown Set: %s" % w)
510  else:
511  print(predefinedSet.keys())
512 
513  def help_showWorkflow(self):
514  print("\n".join(["showWorkflow [workflow1 [...]]\n",
515  "Run w/o arguments, it will print the list of registered macro-workflows.",
516  "Run with space-separated workflows, it will print the full list of workflow-ids registered to them"]))
517 
518  def complete_showWorkflow(self, text, line, start_idx, end_idx):
519  if text and len(text) > 0:
520  return [t for t in self.matrices_.keys() if t.startswith(text)]
521  else:
522  return self.matrices_.keys()
523 
524  def do_showWorkflow(self, arg):
525  if arg == '':
526  print("Available workflows:")
527  for k in self.matrices_.keys():
528  print(k)
529  else:
530  selected = arg.split()
531  for k in selected:
532  if k not in self.matrices_.keys():
533  print("Unknown workflow %s: skipping" % k)
534  else:
535  for wfl in self.matrices_[k].workFlows:
536  wfName, stepNames = wfl.nameId.split('+',1)
537  print("%s %s %s" % (wfl.numId, wfName, stepNames))
538  print("%s contains %d workflows" % (k, len(self.matrices_[k].workFlows)))
539 
541  print("\n".join(["searchInWorkflow wfl_name search_regexp\n",
542  "This command will search for a match within all workflows registered to wfl_name.",
543  "The search is done on both the workflow name and the names of steps registered to it."]))
544 
545  def complete_searchInWorkflow(self, text, line, start_idx, end_idx):
546  if text and len(text) > 0:
547  return [t for t in self.matrices_.keys() if t.startswith(text)]
548  else:
549  return self.matrices_.keys()
550 
551  def do_searchInWorkflow(self, arg):
552  args = arg.split()
553  if len(args) < 2:
554  print("searchInWorkflow name regexp")
555  return
556  if args[0] not in self.matrices_.keys():
557  print("Unknown workflow")
558  return
559  import re
560  pattern = None
561  try:
562  pattern = re.compile(args[1])
563  except:
564  print("Failed to compile regexp %s" % args[1])
565  return
566  counter = 0
567  for wfl in self.matrices_[args[0]].workFlows:
568  wfName, stepNames = wfl.nameId.split('+',1)
569  if re.match(pattern, wfName) or re.match(pattern, stepNames):
570  print("%s %s %s" % (wfl.numId, wfName, stepNames))
571  counter += 1
572  print("Found %d compatible workflows inside %s" % (counter, args[0]))
573 
574  def help_search(self):
575  print("\n".join(["search search_regexp\n",
576  "This command will search for a match within all workflows registered.",
577  "The search is done on both the workflow name and the names of steps registered to it."]))
578 
579  def do_search(self, arg):
580  args = arg.split()
581  if len(args) < 1:
582  print("search regexp")
583  return
584  for wfl in self.matrices_.keys():
585  self.do_searchInWorkflow(' '.join([wfl, args[0]]))
586 
588  print("\n".join(["dumpWorkflowId [wfl-id1 [...]]\n",
589  "Dumps the details (cmsDriver commands for all steps) of the space-separated workflow-ids in input."]))
590 
591  def do_dumpWorkflowId(self, arg):
592  wflids = arg.split()
593  if len(wflids) == 0:
594  print("dumpWorkflowId [wfl-id1 [...]]")
595  return
596 
597  fmt = "[%d]: %s\n"
598  maxLen = 100
599  for wflid in wflids:
600  dump = True
601  for key, mrd in self.matrices_.items():
602  for wfl in mrd.workFlows:
603  if wfl.numId == float(wflid):
604  wfName, stepNames = wfl.nameId.split('+',1)
605  if dump:
606  dump = False
607  print(wfl.numId, stepNames)
608  for i,s in enumerate(wfl.cmds):
609  print(fmt % (i+1, (str(s)+' ')))
610  print("\nWorkflow found in %s." % key)
611  else:
612  print("Workflow also found in %s." % key)
613 
614  do_EOF = do_exit
615 
616  TheMatrix(opt).cmdloop()
617  sys.exit(0)
618 
619  if opt.raw and opt.show:
620  ret = showRaw(opt)
621  else:
622  ret = runSelected(opt)
623 
624 
625  sys.exit(ret)
def performInjectionOptionTest(opt)
def runSelected(opt)
Definition: runTheMatrix.py:20
def do_predefined(self, arg)
def do_showWorkflow(self, arg)
def complete_showWorkflow(self, text, line, start_idx, end_idx)
def do_search(self, arg)
def showRaw(opt)
Definition: runTheMatrix.py:11
def do_clear(self, arg)
def help_dumpWorkflowId(self)
def complete_predefined(self, text, line, start_idx, end_idx)
def help_searchInWorkflow(self)
def do_dumpWorkflowId(self, arg)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def __init__(self, opt)
static std::string join(char **cmd)
Definition: RemoteFile.cc:19
def do_exit(self, arg)
def default(self, inp)
def complete_searchInWorkflow(self, text, line, start_idx, end_idx)
def stepOrIndex(s)
#define str(s)
def do_searchInWorkflow(self, arg)