ATLAS Offline Software
Loading...
Searching...
No Matches
CPGridRun.CPGridRun Class Reference
Collaboration diagram for CPGridRun.CPGridRun:

Public Member Functions

 __init__ (self)
 inputList (self)
 outputFilesParsing (self)
 printHelp (self)
 getParser (self)
 configureSubmission (self)
 configureSubmissionSingleSample (self, input)
 printInputDetails (self)
 hasPyami (self)
bool checkInputInPyami (self)
 outputDSFormatter (self, name)
 execFormatter (self)
 outputsFormatter (self)
bool hasPrun (self)
 submit (self)
 printDelayedErrorCollection (self)
 checkExternalTools (self)
 askSubmission (self)

Static Public Member Functions

 isAtlasProductionFormat (name)
 rucioCustomNameParser (filename)
 atlasProductionNameParser (filename)

Public Attributes

dict cmd = {}
 gridParser = self._parseGridArguments()
dict prunArgsDict = self._createPrunArgsDict()
 args
 unknown_args = parser.parse_known_args()
 output_files = output_files
 inputList

Protected Member Functions

 _initRunscript (self)
 _parseGridArguments (self)
dict _createPrunArgsDict (self)
dict _unknownArgsDict (self)
 _checkPrunArgs (self, argDict)
 _prepareAmiQueryFromInputList (self)
bool _analyzeAmiResults (self, results, datasetPtag)
 _filesChangedOrTarballNotCreated (self)
 _hasCompressedTarball (self)
 _outputDSFormatter (self, name)
 _customOutputDSFormatter (self, name)
 _suffixFormatter (self)
 _filesChanged (self)
 _buildDir (self)
 _sourceDir (self)
 _checkYamlExists (self, runscriptArgs)

Static Protected Member Functions

 _parseInputFileList (path)

Protected Attributes

dict _errorCollector = {}
 _runscript = None
str _tarfile = 'cpgrid.tar.gz'
bool _isFirstRun = True
bool _tarballRecreated = False
list _inputList = None
 _yamlPath = None

Detailed Description

Definition at line 11 of file CPGridRun.py.

Constructor & Destructor Documentation

◆ __init__()

CPGridRun.CPGridRun.__init__ ( self)

Definition at line 12 of file CPGridRun.py.

12 def __init__(self):
13 self._errorCollector = {} # Delay the error collection until the end of the script for better user experience
14 self._runscript = None
15 self._tarfile = 'cpgrid.tar.gz'
16 self._isFirstRun = True
17 self._tarballRecreated = False
18 self._inputList = None
19 self._yamlPath = None
20 self.cmd = {} # sample name -> command
21
22 self.gridParser = self._parseGridArguments()
23 self.prunArgsDict = self._createPrunArgsDict()
24
25 if self.args.help:
26 self._initRunscript()
27 self.printHelp()
28 sys.exit(0)
29

Member Function Documentation

◆ _analyzeAmiResults()

bool CPGridRun.CPGridRun._analyzeAmiResults ( self,
results,
datasetPtag )
protected

Definition at line 286 of file CPGridRun.py.

286 def _analyzeAmiResults(self, results, datasetPtag) -> bool:
287 import re
288 regex = re.compile("_p[0-9]+")
289 results = [r['ldn'] for r in results]
290 notFound = []
291 latestPtag = {}
292
293 for datasetName in self.cmd:
294 if datasetName not in results:
295 notFound.append(datasetName)
296
297 base = regex.sub("_p%", datasetName)
298 matching = [r for r in results if r.startswith(base.replace("_p%", ""))]
299 for m in matching:
300 mParsed = CPGridRun.atlasProductionNameParser(m)
301 try:
302 mPtagInt = int(mParsed.get('ptag', 'p0')[1:])
303 currentPtagInt = int(datasetPtag.get(datasetName, 'p0')[1:])
304 if mPtagInt > currentPtagInt:
305 latestPtag[datasetName] = f"p{mPtagInt}"
306 except (ValueError, TypeError):
307 continue
308
309 if latestPtag:
310 logCPGridRun.info("Newer version of datasets found in AMI:")
311 for name, ptag in latestPtag.items():
312 logCPGridRun.info(f"{name} -> ptag: {ptag}")
313
314 if notFound:
315 logCPGridRun.error("Some input datasets are not available in AMI, missing datasets are likely to fail on the grid:")
316 logCPGridRun.error(", ".join(notFound))
317 return False
318
319 return True
320

◆ _buildDir()

CPGridRun.CPGridRun._buildDir ( self)
protected

Definition at line 404 of file CPGridRun.py.

404 def _buildDir(self):
405 buildDir = os.environ["CMAKE_PREFIX_PATH"]
406 buildDir = os.path.dirname(buildDir.split(":")[0])
407 return buildDir
408

◆ _checkPrunArgs()

CPGridRun.CPGridRun._checkPrunArgs ( self,
argDict )
protected
check the arguments against the prun script to ensure they are valid
See https://github.com/PanDAWMS/panda-client/blob/master/pandaclient/PrunScript.py

Definition at line 208 of file CPGridRun.py.

208 def _checkPrunArgs(self,argDict):
209 '''
210 check the arguments against the prun script to ensure they are valid
211 See https://github.com/PanDAWMS/panda-client/blob/master/pandaclient/PrunScript.py
212 '''
213 import pandaclient.PrunScript
214 # We need to temporarily clear the sys.argv to avoid the parser from PrunScript to fail
215 original_argv = sys.argv
216 sys.argv = ['prun'] # Reset sys.argv to only contain the script name
217 prunArgsDict = {}
218 prunArgsDict = pandaclient.PrunScript.main(get_options=True)
219 sys.argv = original_argv # Restore the original sys.argv
220 nonPrunOrCPGridArgs = []
221 for arg in argDict:
222 if arg not in prunArgsDict:
223 nonPrunOrCPGridArgs.append(arg)
224 if nonPrunOrCPGridArgs:
225 logCPGridRun.error(f"Unknown arguments detected: {nonPrunOrCPGridArgs}. They do not belong to CPGridRun or Panda.")
226 raise ValueError(f"Unknown arguments detected: {nonPrunOrCPGridArgs}. They do not belong to CPGridRun or Panda.")
227

◆ _checkYamlExists()

CPGridRun.CPGridRun._checkYamlExists ( self,
runscriptArgs )
protected

Definition at line 465 of file CPGridRun.py.

465 def _checkYamlExists(self, runscriptArgs):
466 from AnalysisAlgorithmsConfig.CPBaseRunner import CPBaseRunner
467 if not hasattr(runscriptArgs, 'text_config'):
468 self._errorCollector['no yaml'] = "No YAML configuration file is specified in the exec string. Please provide one using --text-config"
469 return
470 yamlPath = getattr(runscriptArgs, 'text_config')
471 self._yamlPath = yamlPath
472 haveLocalYaml = CPBaseRunner.findLocalPathYamlConfig(yamlPath)
473 if haveLocalYaml:
474 logCPGridRun.warning("A path to a local YAML configuration file is found, but it may not be grid-usable.")
475
476 repoYamls, _ = CPBaseRunner.findRepoPathYamlConfig(yamlPath)
477 if repoYamls and len(repoYamls) > 1:
478 self._errorCollector['ambiguous yamls'] = f'Multiple files named \"{yamlPath}\" found in the analysis repository. Please provide a more specific path to the config file.\nMatches found:\n' + '\n'.join(repoYamls)
479 return
480 elif repoYamls and len(repoYamls) == 1:
481 logCPGridRun.info(f"Found a grid-usable YAML configuration file in the analysis repository: {repoYamls[0]}")
482 return
483
484 if haveLocalYaml and self.args.useCentralPackage:
485 logCPGridRun.warning("A path to a local YAML configuration file is found, no custom packages are found, proceed with /cvmfs packages only.")
486
487 if not repoYamls and not self.args.useCentralPackage:
488 self._errorCollector['no usable yaml'] = f"Grid usable YAML configuration file not found: {yamlPath}"
489 if haveLocalYaml:
490 self._errorCollector['have local yaml'] = f"Only a local YAML configuration file is found: {yamlPath}, not usable in the grid.\n" \
491 f"Make sure the YAML file is in build/x86_64-el9-gcc14-opt/data/package_name/config.yaml. You can install the YAML file through CMakeList.txt with `atlas_install_data( data/* )`; use `-t package_name/config.yaml` in the --exec\n"\
492 f"Or if you are only using central packages, please use the `--useCentralPackage` flag."
493

◆ _createPrunArgsDict()

dict CPGridRun.CPGridRun._createPrunArgsDict ( self)
protected
converting unknown args to a dictionary

Definition at line 83 of file CPGridRun.py.

83 def _createPrunArgsDict(self) -> dict:
84 '''
85 converting unknown args to a dictionary
86 '''
87 unknownArgsDict = self._unknownArgsDict()
88 if unknownArgsDict and self.hasPrun():
89 self._checkPrunArgs(unknownArgsDict)
90 logCPGridRun.info(f"Adding prun exclusive arguments: {unknownArgsDict.keys()}")
91 elif unknownArgsDict:
92 logCPGridRun.warning(f"Unknown arguments detected: {unknownArgsDict}. Cannot check the availablility in Prun because Prun is not available / noSubmit is on.")
93 else:
94 pass
95 return unknownArgsDict
96

◆ _customOutputDSFormatter()

CPGridRun.CPGridRun._customOutputDSFormatter ( self,
name )
protected
{group/user}.{username}.{main}.outputDS.{suffix}

Definition at line 350 of file CPGridRun.py.

350 def _customOutputDSFormatter(self, name):
351 '''
352 {group/user}.{username}.{main}.outputDS.{suffix}
353 '''
354 parts = name.split('.')
355 base = 'group' if self.args.groupProduction else 'user'
356 username = self.args.gridUsername
357 main = parts[2]
358 outputDS = 'outputDS'
359 suffix = parts[-1]
360
361 result = [base, username,main, outputDS, suffix]
362 return ".".join(filter(None, result))
363

◆ _filesChanged()

CPGridRun.CPGridRun._filesChanged ( self)
protected

Definition at line 373 of file CPGridRun.py.

373 def _filesChanged(self):
374 tarball_mtime = os.path.getmtime(self._tarfile) if os.path.exists(self._tarfile) else 0
375 buildDir = self._buildDir()
376 sourceDir = self._sourceDir()
377
378 # Check for changes in buildDir
379 for root, _, files in os.walk(buildDir):
380 for file in files:
381 file_path = os.path.join(root, file)
382 try:
383 if os.path.getmtime(file_path) > tarball_mtime:
384 logCPGridRun.info(f"File {file_path} is newer than the tarball.")
385 return True
386 except FileNotFoundError:
387 continue
388
389 # Check for changes in sourceDir
390 if sourceDir is None:
391 logCPGridRun.warning("Source directory is not detected, auto-compression is not performed. Use --recreateTar to update the submission")
392 return False
393 for root, _, files in os.walk(sourceDir):
394 for file in files:
395 file_path = os.path.join(root, file)
396 try:
397 if os.path.getmtime(file_path) > tarball_mtime:
398 logCPGridRun.info(f"File {file_path} is newer than the tarball.")
399 return True
400 except FileNotFoundError:
401 continue
402 return False
403

◆ _filesChangedOrTarballNotCreated()

CPGridRun.CPGridRun._filesChangedOrTarballNotCreated ( self)
protected

Definition at line 321 of file CPGridRun.py.

321 def _filesChangedOrTarballNotCreated(self):
322 return not self._tarballRecreated and (self.args.recreateTar or not os.path.exists(self._tarfile) or self._filesChanged())
323

◆ _hasCompressedTarball()

CPGridRun.CPGridRun._hasCompressedTarball ( self)
protected

Definition at line 324 of file CPGridRun.py.

324 def _hasCompressedTarball(self):
325 return os.path.exists(self._tarfile) or self._tarballRecreated
326

◆ _initRunscript()

CPGridRun.CPGridRun._initRunscript ( self)
protected

Definition at line 30 of file CPGridRun.py.

30 def _initRunscript(self):
31 if self._runscript is not None:
32 return self._runscript
33 elif isAthena:
34 from AnalysisAlgorithmsConfig.AthenaCPRunScript import AthenaCPRunScript
35 self._runscript = AthenaCPRunScript()
36 else:
37 from AnalysisAlgorithmsConfig.EventLoopCPRunScript import EventLoopCPRunScript
38 self._runscript = EventLoopCPRunScript()
39 return self._runscript
40

◆ _outputDSFormatter()

CPGridRun.CPGridRun._outputDSFormatter ( self,
name )
protected
{group/user}.{username}.{prefix}.{DSID}.{format}.{tags}.{suffix}

Definition at line 333 of file CPGridRun.py.

333 def _outputDSFormatter(self, name):
334 '''
335 {group/user}.{username}.{prefix}.{DSID}.{format}.{tags}.{suffix}
336 '''
337 nameParser = CPGridRun.atlasProductionNameParser(name)
338 base = 'group' if self.args.groupProduction else 'user'
339 username = self.args.gridUsername
340 dsid = nameParser['DSID']
341 tags = '_'.join(nameParser['tags'])
342 fileFormat = nameParser['format']
343 base = 'group' if self.args.groupProduction else 'user'
344 prefix = self.args.prefix if self.args.prefix else nameParser['main'].split('_')[0] # Dynamically set the prefix, likely to be something like PhPy8Eg
345 suffix = self._suffixFormatter()
346
347 result = [base, username, prefix, dsid, fileFormat, tags, suffix]
348 return ".".join(filter(None, result))
349
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179

◆ _parseGridArguments()

CPGridRun.CPGridRun._parseGridArguments ( self)
protected

Definition at line 41 of file CPGridRun.py.

41 def _parseGridArguments(self):
42 parser = argparse.ArgumentParser(description='CPGrid runscript to submit CPRun.py jobs to the grid. '
43 'This script will submit a job to the grid using files in the input text one by one.'
44 'CPRun.py can handle multiple sources of input and create one output; but not this script',
45 add_help=False,
46 formatter_class=argparse.RawTextHelpFormatter)
47 parser.add_argument('-h', '--help', dest='help', action='store_true', help='Show this help message and continue')
48
49 ioGroup = parser.add_argument_group('Input/Output file configuration')
50 ioGroup.add_argument('-i','--input-list', dest='input_list', help='Path to the text file containing list of containers on the panda grid. Each container will be passed to prun as --inDS and is run individually')
51 ioGroup.add_argument('--output-files', dest='output_files', nargs='+', default=['output.root'],
52 help='The output files of the grid job. Example: --output-files A.root B.txt B.root results in A/A.root, B/B.txt, B/B.root in the output directory. No need to specify if using CPRun.py')
53 ioGroup.add_argument('--destSE', dest='destSE', default='', type=str, help='Destination storage element (PanDA)')
54 ioGroup.add_argument('--mergeType', dest='mergeType', default='Default', type=str, help='Output merging type, [None, Default, xAOD]')
55
56 pandaGroup = parser.add_argument_group('Input/Output naming configuration')
57 pandaGroup.add_argument('--gridUsername', dest='gridUsername', default=os.getenv('USER', ''), type=str, help='Grid username, or the groupname. Default is the current user. Only affect file naming')
58 pandaGroup.add_argument('--prefix', dest='prefix', default='', type=str, help='Prefix for the output directory. Dynamically set with input container if not provided')
59 pandaGroup.add_argument('--suffix', dest='suffix', default='',type=str, help='Suffix for the output directory')
60 pandaGroup.add_argument('--outDS', dest='outDS', default='', type=str,
61 help='Name of an output dataset. outDS will contain all output files (PanDA). If not provided, support dynamic naming if input name is in the Atlas production format or typical user production format')
62
63 cpgridGroup = parser.add_argument_group('CPGrid configuration')
64 cpgridGroup.add_argument('--groupProduction', dest='groupProduction', action='store_true', help='Only use for official production')
65
66 cpgridGroup.add_argument('--exec', dest='exec', type=str,
67 help='Executable line for the CPRun.py or custom script to run on the grid encapsulated in a double quote (PanDA)\n'
68 'Run CPRun.py with preset behavior including streamlined file i/o. E.g, "CPRun.py -t config.yaml --no-systematics".\n'
69 'Run custom script: "customRun.py -i inputs -o output --text-config config.yaml --flagA --flagB"\n'
70 )
71
72 submissionGroup = parser.add_argument_group('Submission configuration')
73 submissionGroup.add_argument('-y', '--agreeAll', dest='agreeAll', action='store_true', help='Agree to all the submission details without asking for confirmation. Use with caution!')
74 submissionGroup.add_argument('--noSubmit', dest='noSubmit', action='store_true', help='Do not submit the job to the grid (PanDA). Useful to inspect the prun command')
75 submissionGroup.add_argument('--testRun', dest='testRun', action='store_true', help='Will submit job to the grid but greatly limit the number of files per job (10) and number of events (300)')
76 submissionGroup.add_argument('--checkInputDS', dest='checkInputDS', action='store_true', help='Check if the input datasets are available on the AMI.')
77 submissionGroup.add_argument('--recreateTar', dest='recreateTar', action='store_true', help='Re-compress the source code. Source code are compressed by default in submission, this is useful when the source code is updated')
78 submissionGroup.add_argument('--useCentralPackage', dest='useCentralPackage', action='store_true', help='Use central package instead of custom packages')
79 self.args, self.unknown_args = parser.parse_known_args()
80 self.outputFilesParsing()
81 return parser
82

◆ _parseInputFileList()

CPGridRun.CPGridRun._parseInputFileList ( path)
staticprotected

Definition at line 640 of file CPGridRun.py.

640 def _parseInputFileList(path):
641 files = []
642 with open(path, 'r') as inputText:
643 for line in inputText.readlines():
644 # skip comments and empty lines
645 if line.startswith('#') or not line.strip():
646 continue
647 files += line.split(',')
648 # remove leading/trailing whitespaces, and \n
649 files = [file.strip() for file in files]
650 return files
651

◆ _prepareAmiQueryFromInputList()

CPGridRun.CPGridRun._prepareAmiQueryFromInputList ( self)
protected
Helper function to prepare a list of queries for the AMI based on the input list.
It will replace the _p### with _p% to match the latest ptag.

Definition at line 271 of file CPGridRun.py.

271 def _prepareAmiQueryFromInputList(self):
272 '''
273 Helper function to prepare a list of queries for the AMI based on the input list.
274 It will replace the _p### with _p% to match the latest ptag.
275 '''
276 import re
277 regex = re.compile("_p[0-9]+")
278 queries = []
279 datasetPtag = {}
280 for datasetName in self.cmd:
281 parsed = CPGridRun.atlasProductionNameParser(datasetName)
282 datasetPtag[datasetName] = parsed.get('ptag')
283 queries.append(regex.sub("_p%", datasetName))
284 return queries, datasetPtag
285

◆ _sourceDir()

CPGridRun.CPGridRun._sourceDir ( self)
protected

Definition at line 409 of file CPGridRun.py.

409 def _sourceDir(self):
410 cmakeCachePath = os.path.join(self._buildDir(), 'CMakeCache.txt')
411 sourceDir = None
412 if not os.path.exists(cmakeCachePath):
413 return sourceDir
414 with open(cmakeCachePath, 'r') as cmakeCache:
415 for line in cmakeCache:
416 if '_SOURCE_DIR:STATIC=' in line:
417 sourceDir = line.split('=')[1].strip()
418 break
419 return sourceDir
420

◆ _suffixFormatter()

CPGridRun.CPGridRun._suffixFormatter ( self)
protected

Definition at line 364 of file CPGridRun.py.

364 def _suffixFormatter(self):
365 if self.args.suffix:
366 return self.args.suffix
367 if self.args.testRun:
368 import uuid
369 return f"test_{uuid.uuid4().hex[:6]}"
370 else:
371 ''
372

◆ _unknownArgsDict()

dict CPGridRun.CPGridRun._unknownArgsDict ( self)
protected
Cleans the unknown args by removing leading dashes and ensuring they are in key-value pairs

Definition at line 192 of file CPGridRun.py.

192 def _unknownArgsDict(self)->dict:
193 '''
194 Cleans the unknown args by removing leading dashes and ensuring they are in key-value pairs
195 '''
196 unknown_args_dict = {}
197 idx = 0
198 while idx < len(self.unknown_args):
199 if self.unknown_args[idx].startswith('-'):
200 if idx + 1 < len(self.unknown_args) and not self.unknown_args[idx + 1].startswith('-'):
201 unknown_args_dict[self.unknown_args[idx].lstrip('-')] = self.unknown_args[idx + 1]
202 idx += 2
203 else:
204 unknown_args_dict[self.unknown_args[idx].lstrip('-')] = True
205 idx += 1
206 return unknown_args_dict
207

◆ askSubmission()

CPGridRun.CPGridRun.askSubmission ( self)

Definition at line 666 of file CPGridRun.py.

666 def askSubmission(self):
667 if self.args.agreeAll:
668 logCPGridRun.info("You have agreed to all the submission details. Jobs will be submitted without confirmation.")
669 self.submit()
670 return
671 answer = input("Please confirm ALL the submission details are correct before submitting [y/n]: ")
672 if answer.lower() == 'y':
673 self.submit()
674 elif answer.lower() == 'n':
675 logCPGridRun.info("Feel free to report any unexpected behavior to the CPAlgorithms team!")
676 else:
677 logCPGridRun.error("Invalid input. Please enter 'y' or 'n'. Jobs are not submitted.")
678

◆ atlasProductionNameParser()

CPGridRun.CPGridRun.atlasProductionNameParser ( filename)
static
Parsing file name into a dictionary, an example is given here
mc20_13TeV.410470.PhPy8EG_A14_ttbar_hdamp258p75_nonallhad.deriv.DAOD_PHYS.e6337_s3681_r13167_p5855/DAOD_PHYS.34865530._000740.pool.root.1
For the first part
datasetName: mc20_13TeV.410470.PhPy8EG_A14_ttbar_hdamp258p75_nonallhad.deriv.DAOD_PHYS.e6337_s3681_r13167_p5855
projectName: mc20_13TeV
campaign: mc20
energy: 13 #(TeV)
DSID: 410470
main: PhPy8EG_A14_ttbar_hdamp258p75_nonallhad
TODO  generator: PhPy8Eg
TODO  tune: A14 # For Pythia8
TODO  process: ttbar
TODO  hdamp: 258p75 # For Powheg
TODO  decayType: nonallhad
step: deriv
format: DAOD_PHYS
tags: e###_s###_r###_p###_a###_t###_b#
etag: e6337 # EVNT (EVGEN) production and merging
stag: s3681 # Geant4 simulation to produce HITS and merging!
rtag: r13167 # Digitisation and reconstruction, as well as AOD merging
ptag: p5855 # Production of NTUP_PILEUP format and merging
atag: aXXX: atlfast configuration (both simulation and digit/recon)
ttag: tXXX: tag production configuration
btag: bXXX: bytestream production configuration

For the second part
JeditaskID: 34865530
fileNumber: 000740
version: 1

Definition at line 544 of file CPGridRun.py.

544 def atlasProductionNameParser(filename):
545 '''
546 Parsing file name into a dictionary, an example is given here
547 mc20_13TeV.410470.PhPy8EG_A14_ttbar_hdamp258p75_nonallhad.deriv.DAOD_PHYS.e6337_s3681_r13167_p5855/DAOD_PHYS.34865530._000740.pool.root.1
548 For the first part
549 datasetName: mc20_13TeV.410470.PhPy8EG_A14_ttbar_hdamp258p75_nonallhad.deriv.DAOD_PHYS.e6337_s3681_r13167_p5855
550 projectName: mc20_13TeV
551 campaign: mc20
552 energy: 13 #(TeV)
553 DSID: 410470
554 main: PhPy8EG_A14_ttbar_hdamp258p75_nonallhad
555 TODO generator: PhPy8Eg
556 TODO tune: A14 # For Pythia8
557 TODO process: ttbar
558 TODO hdamp: 258p75 # For Powheg
559 TODO decayType: nonallhad
560 step: deriv
561 format: DAOD_PHYS
562 tags: e###_s###_r###_p###_a###_t###_b#
563 etag: e6337 # EVNT (EVGEN) production and merging
564 stag: s3681 # Geant4 simulation to produce HITS and merging!
565 rtag: r13167 # Digitisation and reconstruction, as well as AOD merging
566 ptag: p5855 # Production of NTUP_PILEUP format and merging
567 atag: aXXX: atlfast configuration (both simulation and digit/recon)
568 ttag: tXXX: tag production configuration
569 btag: bXXX: bytestream production configuration
570
571 For the second part
572 JeditaskID: 34865530
573 fileNumber: 000740
574 version: 1
575
576 '''
577 result = {}
578 #split the / in case
579 # mc20_13TeV.410470.PhPy8EG_A14_ttbar_hdamp258p75_nonallhad.deriv.DAOD_PHYS.e6337_s3681_r13167_p5855
580 # /DAOD_PHYS.34865530._000740.pool.root.1
581 if '/' in filename:
582 datasetPart, filePart = filename.split('/')
583 else:
584 datasetPart = filename
585 filePart = None
586
587 # Remove the scope
588 if ':' in datasetPart:
589 datasetPart = datasetPart.split(':')[1]
590
591 # Do not try to parse user datasets
592 if datasetPart.startswith('user') or datasetPart.startswith('group'):
593 result['datasetName'] = datasetPart
594 return result
595
596 # Split the dataset part by dots
597 datasetParts = datasetPart.split('.')
598 result['datasetName'] = datasetPart
599 # Extract the first part
600 result['projectName'] = datasetParts[0] # is positional
601 # Extract the campaign and energy
602 campaign_energy = result['projectName'].split('_')
603 result['campaign'] = campaign_energy[0]
604 result['energy'] = campaign_energy[1]
605
606 # Extract the DSID, positional
607 result['DSID'] = datasetParts[1]
608 result['main'] = datasetParts[2]
609 result['step'] = datasetParts[3]
610 result['format'] = datasetParts[4]
611
612 # Extract the tags (etag, stag, rtag, ptag)
613 tags = datasetParts[5].split('_')
614 result['tags'] = tags
615 for tag in tags:
616 if tag.startswith('e'):
617 result['etag'] = tag
618 elif tag.startswith('s'):
619 result['stag'] = tag
620 elif tag.startswith('r'):
621 result['rtag'] = tag
622 elif tag.startswith('p'):
623 result['ptag'] = tag
624 elif tag.startswith('a'):
625 result['atag'] = tag
626 elif tag.startswith('t'):
627 result['ttag'] = tag
628 elif tag.startswith('b'):
629 result['btag'] = tag
630
631 # Extract the file part if it exists
632 if filePart:
633 fileParts = filePart.split('.')
634 result['jediTaskID'] = fileParts[1]
635 result['fileNumber'] = fileParts[2]
636 result['version'] = fileParts[-1]
637 return result
638

◆ checkExternalTools()

CPGridRun.CPGridRun.checkExternalTools ( self)

Definition at line 661 of file CPGridRun.py.

661 def checkExternalTools(self):
662 self.hasPrun()
663 if self.args.checkInputDS:
664 self.checkInputInPyami()
665

◆ checkInputInPyami()

bool CPGridRun.CPGridRun.checkInputInPyami ( self)

Definition at line 254 of file CPGridRun.py.

254 def checkInputInPyami(self) -> bool:
255 if not self.hasPyami():
256 return False
257
258 client = pyAMI.client.Client('atlas')
259 pyAMI.atlas.api.init()
260
261 queries, datasetPtag = self._prepareAmiQueryFromInputList()
262 try:
263 results = pyAMI.atlas.api.list_datasets(client, patterns=queries)
264 except pyAMI.exception.Error:
265 self._errorCollector['no valid certificate'] = (
266 "Cannot query AMI, please run 'voms-proxy-init -voms atlas' and ensure your certificate is valid.")
267 return False
268
269 return self._analyzeAmiResults(results, datasetPtag)
270

◆ configureSubmission()

CPGridRun.CPGridRun.configureSubmission ( self)

Definition at line 133 of file CPGridRun.py.

133 def configureSubmission(self):
134 for input in self.inputList:
135 cmd = self.configureSubmissionSingleSample(input)
136 self.cmd[input] = cmd
137 self._isFirstRun = False
138

◆ configureSubmissionSingleSample()

CPGridRun.CPGridRun.configureSubmissionSingleSample ( self,
input )

Definition at line 139 of file CPGridRun.py.

139 def configureSubmissionSingleSample(self, input):
140 config = {
141 'inDS': input,
142 'outDS': self.args.outDS if self.args.outDS else self.outputDSFormatter(input) ,
143 'cmtConfig': os.environ["CMTCONFIG"],
144 'writeInputToTxt': 'IN:in.txt',
145 'outputs': self.outputsFormatter(),
146 'exec': self.execFormatter(),
147 'memory': "2000", # MB
148 'addNthFieldOfInDSToLFN': '2,3,6',
149 }
150 if self.args.noSubmit:
151 config['noSubmit'] = True
152
153 if self.args.mergeType == 'xAOD':
154 config['mergeScript'] = 'xAODMerge %OUT `echo %IN | sed \'s/,/ /g\'`'
155
156 if self.args.mergeType != 'None':
157 config['mergeOutput'] = True
158
159 # Three types of files sending the grid
160 if self.args.useCentralPackage: # 1. Using central package and have a yaml file only
161 config['extFile'] = self._yamlPath
162 config['noBuild'] = True
163 config['noCompile'] = True
164 config['athenaTag'] = f"AnalysisBase,{os.environ['AnalysisBase_VERSION']}"
165 elif self._filesChangedOrTarballNotCreated(): # 2. Using custom packages and haven't compressed the tarball since the last changes
166 config['outTarBall'] = self._tarfile
167 config['useAthenaPackages'] = True
168 self._tarballRecreated = True
169 elif self._hasCompressedTarball(): # 3. Using custom packages and have compressed the tarball
170 config['inTarBall'] = self._tarfile
171 config['useAthenaPackages'] = True
172
173 if self.args.groupProduction:
174 config['official'] = True
175 config['voms'] = f'atlas:/atlas/{self.args.gridUsername}/Role=production'
176
177 if self.args.destSE:
178 config['destSE'] = self.args.destSE
179
180 if self.args.testRun:
181 config['nEventsPerFile'] = 100
182 config['nFiles'] = 5
183 config.update(self.prunArgsDict)
184 cmd = 'prun \\\n'
185 for k, v in config.items():
186 if isinstance(v, bool) and v:
187 cmd += f'--{k} \\\n'
188 elif v is not None and v != '':
189 cmd += f'--{k} {v} \\\n'
190 return cmd.rstrip(' \\\n')
191

◆ execFormatter()

CPGridRun.CPGridRun.execFormatter ( self)

Definition at line 421 of file CPGridRun.py.

421 def execFormatter(self):
422 if not self.args.exec:
423 raise ValueError('No exec command provided, use --exec to specify the command to run on the grid')
424
425 # Check if the execution command starts with 'CPRun.py' or '-'
426 isCPRunDefault = self.args.exec.startswith('-') or self.args.exec.startswith('CPRun.py')
427 formatingClause = {
428 'input_list': 'in.txt',
429 'merge_output_files': len(self.args.output_files) == 1,
430 }
431 if not isCPRunDefault:
432 if self._isFirstRun: logCPGridRun.warning("Non-CPRun.py is detected, please ensure the exec string is formatted correctly. Exec string will not be automatically formatted.")
433 return f'"{self.args.exec}"'
434
435 # Parse the exec string using the parser to validate and extract known arguments
436 self._initRunscript()
437 runscriptArgs, unknownArgs = self._runscript.parser.parse_known_args(self.args.exec.split(' '))
438
439 # Throw error if unknownArgs contains any --args
440 unknown_flags = [arg for arg in unknownArgs if arg.startswith('--')]
441 if unknown_flags:
442 logCPGridRun.error(f"Unknown flags detected in the exec string: {unknown_flags}. Please check the exec string.")
443 raise ValueError(f"Unknown arguments detected: {unknown_flags}")
444
445 # Only override if value is None or the parser default
446 for key, value in formatingClause.items():
447 if hasattr(runscriptArgs, key):
448 old_value = getattr(runscriptArgs, key)
449 if old_value is None or old_value == self._runscript.parser.get_default(key):
450 setattr(runscriptArgs, key, value)
451 if self._isFirstRun: logCPGridRun.info(f"Setting '{key}' to '{value}' (CPRun.py default is: '{old_value}')")
452 else:
453 if self._isFirstRun: logCPGridRun.warning(f"Preserving user-defined '{key}': '{old_value}', default formatting '{value}' will not be applied.")
454 else:
455 logCPGridRun.error(f"Formatting clause '{key}' is not recognized in the CPRun.py script. Check CPGridRun.py")
456 raise ValueError(f"Formatting clause '{key}' is not recognized in the CPRun.py script. Check CPGridRun.py")
457 self._checkYamlExists(runscriptArgs)
458 # Return the formatted arguments as a string
459 arg_string = ' '.join(
460 f'--{k.replace("_", "-")}' if isinstance(v, bool) and v else
461 f'--{k.replace("_", "-")} {v}' for k, v in vars(runscriptArgs).items() if v not in [None, False]
462 )
463 return f'"CPRun.py {arg_string}"'
464

◆ getParser()

CPGridRun.CPGridRun.getParser ( self)

Definition at line 128 of file CPGridRun.py.

128 def getParser(self):
129 return self.gridParser
130

◆ hasPrun()

bool CPGridRun.CPGridRun.hasPrun ( self)

Definition at line 498 of file CPGridRun.py.

498 def hasPrun(self) -> bool:
499 import shutil
500 prun_path = shutil.which("prun")
501 if prun_path is None:
502 self._errorCollector['no prun'] = (
503 "The 'prun' command is not found. If you are on lxplus, please run the following commands:\n\n"
504 "```\n"
505 "lsetup panda\n"
506 "voms-proxy-init -voms atlas\n"
507 "```\n"
508 "Make sure you have a valid certificate."
509 )
510 return False
511 return True
512

◆ hasPyami()

CPGridRun.CPGridRun.hasPyami ( self)

Definition at line 238 of file CPGridRun.py.

238 def hasPyami(self):
239 try:
240 global pyAMI
241 import pyAMI.client
242 import pyAMI.atlas.api
243 except ModuleNotFoundError:
244 self._errorCollector['no AMI'] = (
245 "Cannot import pyAMI, please run the following commands:\n\n"
246 "```\n"
247 "lsetup pyami\n"
248 "voms-proxy-init -voms atlas\n"
249 "```\n"
250 "and make sure you have a valid certificate.")
251 return False
252 return True
253

◆ inputList()

CPGridRun.CPGridRun.inputList ( self)

Definition at line 98 of file CPGridRun.py.

98 def inputList(self):
99 if not self.args.input_list:
100 raise ValueError('No input list provided, use --input-list to specify the input containers')
101 if self._inputList is None:
102 if self.args.input_list.endswith('.txt'):
103 self._inputList = CPGridRun._parseInputFileList(self.args.input_list)
104 elif self.args.input_list.endswith('.json'):
105 raise NotImplementedError('JSON input list parsing is not implemented')
106 elif CPGridRun.isAtlasProductionFormat(self.args.input_list):
107 self._inputList = [self.args.input_list]
108 else:
109 raise ValueError(
110 'use --input-list to specify input containers')
111 return self._inputList
112

◆ isAtlasProductionFormat()

CPGridRun.CPGridRun.isAtlasProductionFormat ( name)
static

Definition at line 520 of file CPGridRun.py.

520 def isAtlasProductionFormat(name):
521 if ":" in name:
522 name = name.split(":")[1]
523
524 if name.startswith('mc') or name.startswith('data'):
525 return True
526
527 logCPGridRun.warning("Name is not in the Atlas production format, assuming it is a user production")
528 return False
529

◆ outputDSFormatter()

CPGridRun.CPGridRun.outputDSFormatter ( self,
name )

Definition at line 327 of file CPGridRun.py.

327 def outputDSFormatter(self, name):
328 if CPGridRun.isAtlasProductionFormat(name):
329 return self._outputDSFormatter(name)
330 else:
331 return self._customOutputDSFormatter(name)
332

◆ outputFilesParsing()

CPGridRun.CPGridRun.outputFilesParsing ( self)

Definition at line 113 of file CPGridRun.py.

113 def outputFilesParsing(self):
114 output_files = []
115 for output in self.args.output_files:
116 if ',' in output:
117 output_files.extend(output.split(','))
118 else:
119 output_files.append(output)
120 self.output_files = output_files
121

◆ outputsFormatter()

CPGridRun.CPGridRun.outputsFormatter ( self)

Definition at line 494 of file CPGridRun.py.

494 def outputsFormatter(self):
495 outputs = [f'{output.split(".")[0]}:{output}' if ":" not in output else output for output in self.args.output_files]
496 return ','.join(outputs)
497

◆ printDelayedErrorCollection()

CPGridRun.CPGridRun.printDelayedErrorCollection ( self)

Definition at line 652 of file CPGridRun.py.

652 def printDelayedErrorCollection(self):
653 if self._errorCollector:
654 logCPGridRun.error("Errors were collected during the script execution:")
655
656 for key, value in self._errorCollector.items():
657 logCPGridRun.error(f"{key}: {value}")
658 logCPGridRun.error("Please fix the errors and try again.")
659 sys.exit(1)
660

◆ printHelp()

CPGridRun.CPGridRun.printHelp ( self)

Definition at line 122 of file CPGridRun.py.

122 def printHelp(self):
123 self.gridParser.print_help()
124 logCPGridRun.info("\033[92m\n If you are using CPRun.py, the following flags are for the CPRun.py in this framework\033[0m")
125 self._runscript.parser.usage = argparse.SUPPRESS
126 self._runscript.parser.print_help()
127
void printHelp()

◆ printInputDetails()

CPGridRun.CPGridRun.printInputDetails ( self)

Definition at line 228 of file CPGridRun.py.

228 def printInputDetails(self):
229 for key, cmd in self.cmd.items():
230 parsed_name = CPGridRun.atlasProductionNameParser(key)
231 logCPGridRun.info("\n"
232 f"Input: {key}\n" +
233 "\n".join([f" {k.replace('_', ' ').title()}: {v}" for k, v in parsed_name.items()]))
234 logCPGridRun.info(f"Command: \n{cmd}")
235 print("-" * 70)
236 # Add your submission logic here
237
void print(char *figname, TCanvas *c1)

◆ rucioCustomNameParser()

CPGridRun.CPGridRun.rucioCustomNameParser ( filename)
static
The custom name has many variations, but most of them follow user/group.username.datasetname.suffix

Definition at line 531 of file CPGridRun.py.

531 def rucioCustomNameParser(filename):
532 '''
533 The custom name has many variations, but most of them follow user/group.username.datasetname.suffix
534 '''
535 result = {}
536 parts = filename.split('.')
537 result['userType'] = parts[0]
538 result['username'] = parts[1]
539 result['main'] = parts[2]
540 result['suffix'] = parts[-1]
541 return result
542

◆ submit()

CPGridRun.CPGridRun.submit ( self)

Definition at line 513 of file CPGridRun.py.

513 def submit(self):
514 import subprocess
515 for key, cmd in self.cmd.items():
516 process = subprocess.Popen(cmd, shell=True, stdout=sys.stdout, stderr=sys.stderr)
517 process.communicate()
518
static Status::Enum submit(SH::Sample *const sample, const bool isFirstSample)

Member Data Documentation

◆ _errorCollector

dict CPGridRun.CPGridRun._errorCollector = {}
protected

Definition at line 13 of file CPGridRun.py.

◆ _inputList

list CPGridRun.CPGridRun._inputList = None
protected

Definition at line 18 of file CPGridRun.py.

◆ _isFirstRun

bool CPGridRun.CPGridRun._isFirstRun = True
protected

Definition at line 16 of file CPGridRun.py.

◆ _runscript

CPGridRun.CPGridRun._runscript = None
protected

Definition at line 14 of file CPGridRun.py.

◆ _tarballRecreated

bool CPGridRun.CPGridRun._tarballRecreated = False
protected

Definition at line 17 of file CPGridRun.py.

◆ _tarfile

CPGridRun.CPGridRun._tarfile = 'cpgrid.tar.gz'
protected

Definition at line 15 of file CPGridRun.py.

◆ _yamlPath

CPGridRun.CPGridRun._yamlPath = None
protected

Definition at line 19 of file CPGridRun.py.

◆ args

CPGridRun.CPGridRun.args

Definition at line 79 of file CPGridRun.py.

◆ cmd

dict CPGridRun.CPGridRun.cmd = {}

Definition at line 20 of file CPGridRun.py.

◆ gridParser

CPGridRun.CPGridRun.gridParser = self._parseGridArguments()

Definition at line 22 of file CPGridRun.py.

◆ inputList

CPGridRun.CPGridRun.inputList

Definition at line 134 of file CPGridRun.py.

◆ output_files

CPGridRun.CPGridRun.output_files = output_files

Definition at line 120 of file CPGridRun.py.

◆ prunArgsDict

CPGridRun.CPGridRun.prunArgsDict = self._createPrunArgsDict()

Definition at line 23 of file CPGridRun.py.

◆ unknown_args

CPGridRun.CPGridRun.unknown_args = parser.parse_known_args()

Definition at line 79 of file CPGridRun.py.


The documentation for this class was generated from the following file: