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