diff --git a/Generators/EvgenJobTransforms/python/EvgenCAConfig.py b/Generators/EvgenJobTransforms/python/EvgenCAConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..79558c99f54e90a1cfcb4fc7f12f9a72327e8696 --- /dev/null +++ b/Generators/EvgenJobTransforms/python/EvgenCAConfig.py @@ -0,0 +1,29 @@ +# Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration + +# Get logger +from AthenaCommon.Logging import logging +evgenLog = logging.getLogger('EvgenConfig') + +class EvgenConfig(): + """The CA-based EvgenConfig class that holds the configuration for a sample to be generated""" + + __slots__ = () + + def __init__(self, flags): + self.generators = [] + self.keywords = [] + self.contact = [] + self.nEventsPerJob = None + + def setupFlags(self, flags): + raise RuntimeError("setupFlags method needs to be implemented in Sample(EvgenConfig)") + + def checkAttributes(self): + for var, value in vars(self).items(): + if not value: + raise RuntimeError("self.{} should be set in Sample(EvgenConfig)".format(var)) + else: + evgenLog.info("{} = {}".format(var, value)) + + def setupProcess(self, flags): + raise RuntimeError("setupProcess method needs to be implemented in Sample(EvgenConfig)") diff --git a/Generators/EvgenJobTransforms/python/GENtoEVGEN_Skeleton.py b/Generators/EvgenJobTransforms/python/GENtoEVGEN_Skeleton.py index 301ba80a8ecffeafa6c10c6a3e4e755e7e9afbe5..a9bc737a5ce10ee78c91a2f77e864b3f2bf7c1e4 100644 --- a/Generators/EvgenJobTransforms/python/GENtoEVGEN_Skeleton.py +++ b/Generators/EvgenJobTransforms/python/GENtoEVGEN_Skeleton.py @@ -2,8 +2,6 @@ # """Functionality core of the Gen_tf transform""" -from PyJobTransforms.CommonRunArgsToFlags import commonRunArgsToFlags - # temporarily force no global config flags from AthenaConfiguration import AllConfigFlags del AllConfigFlags.ConfigFlags @@ -12,32 +10,180 @@ del AllConfigFlags.ConfigFlags from AthenaCommon import JobProperties JobProperties.jobPropertiesDisallowed = True +# Get logger +from AthenaCommon.Logging import logging +evgenLog = logging.getLogger('Gen_tf') + +# Functions for pre/post-include/exec +from PyJobTransforms.TransformUtils import processPreExec, processPreInclude + +# For creating CA instances of algorithms +from AthenaConfiguration.ComponentFactory import CompFactory + +# Other imports that are needed +import sys, os + + +# Function that reads the jO and returns an instance of Sample(EvgenCAConfig) +def setupSample(runArgs, flags): + # Only permit one jobConfig argument for evgen + if len(runArgs.jobConfig) != 1: + evgenLog.info("runArgs.jobConfig = %s", runArgs.jobConfig) + evgenLog.error("You must supply one and only one jobConfig file argument") + sys.exit(1) + + evgenLog.info("Using JOBOPTSEARCHPATH (as seen in skeleton) = {}".format(os.environ["JOBOPTSEARCHPATH"])) + + FIRST_DIR = (os.environ['JOBOPTSEARCHPATH']).split(":")[0] + + # Find jO file + jofiles = [f for f in os.listdir(FIRST_DIR) if (f.startswith('mc') and f.endswith('.py'))] + if len(jofiles) !=1: + evgenLog.error("You must supply one and only one jobOption file in DSID directory") + sys.exit(1) + jofile = jofiles[0] + + # Perform consistency checks on the jO + from GeneratorConfig.GenConfigHelpers import checkJOConsistency + checkJOConsistency(jofile) + + # Import the jO as a module + # We cannot do import BLAH directly since + # 1. the filenames are not python compatible (mc.GEN_blah.py) + # 2. the filenames are different for every jO + import importlib.util + spec = importlib.util.spec_from_file_location( + name="sample", + location=os.path.join(FIRST_DIR,jofile), + ) + jo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(jo) + + # Create instance of Sample(EvgenCAConfig) + sample = jo.Sample(flags) + + # Set up the sample properties + sample.setupFlags(flags) + + # Set the random number seed + # Need to use logic in EvgenJobTransforms.Generate_dsid_ranseed + + # Get DSID + dsid = os.path.basename(runArgs.jobConfig[0]) + + # Update the global flags + if dsid.isdigit(): + flags.Generator.DSID + flags.Input.Files = [] + flags.Input.RunNumbers = [flags.Generator.DSID] + flags.Input.TimeStamps = [0] + flags.Generator.nEventsPerJob = sample.nEventsPerJob + flags.Exec.MaxEvents = runArgs.maxEvents if runArgs.maxEvents != -1 else sample.nEventsPerJob + flags.Output.EVNTFileName = runArgs.outputEVNTFile + + # Check if sample attributes have been properly set + sample.checkAttributes() + + return sample + + +# Function to set up event generation services +def setupEvgenServices(flags, cfg): + + # EventInfoCnvAlg + from xAODEventInfoCnv.xAODEventInfoCnvConfig import EventInfoCnvAlgCfg + cfg.merge(EventInfoCnvAlgCfg(flags, disableBeamSpot=True, xAODKey="TMPEvtInfo")) + + +# Function to set up event generation services +def setupOutputStream(flags, runArgs, cfg, sample): + + # Items to save + ItemList = ["McEventCollection#*"] + + # Configure output stream + from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg + cfg.merge( + OutputStreamCfg( + flags, + "EVNT", + ItemList = ItemList + ) + ) + + # Add in-file MetaData + from xAODMetaDataCnv.InfileMetaDataConfig import SetupMetaDataForStreamCfg + cfg.merge(SetupMetaDataForStreamCfg(flags, "EVNT")) + + # Write AMI tag into in-file MetaData + from PyUtils.AMITagHelperConfig import AMITagCfg + cfg.merge(AMITagCfg(flags, runArgs)) + # Main function def fromRunArgs(runArgs): - from AthenaCommon.Logging import logging - evgenLog = logging.getLogger('Gen_tf') - evgenLog.setLevel(logging.DEBUG if runArgs.VERBOSE else logging.INFO) + # Set logging level + evgenLog.setLevel(logging.DEBUG if runArgs.VERBOSE else logging.INFO) - # Set up sequences - # Announce arg checking - evgenLog.debug("****************** CHECKING EVENT GENERATION ARGS *****************") - evgenLog.debug(runArgs) - - evgenLog.debug('****************** Setting-up configuration flags *****************') + if runArgs.VERBOSE: + evgenLog.debug("****************** CHECKING EVENT GENERATION ARGS *****************") + evgenLog.debug(runArgs) + evgenLog.debug('****************** Setting-up configuration flags *****************') + from AthenaConfiguration.AllConfigFlags import initConfigFlags flags = initConfigFlags() - # Convert run arguments to athena flags - from GeneratorConfig.GeneratorConfigFlags import generatorRunArgsToFlags + # Convert run arguments to global athena flags + from PyJobTransforms.CommonRunArgsToFlags import commonRunArgsToFlags commonRunArgsToFlags(runArgs, flags) + + # Convert generator-specific run arguments to global athena flags + from GeneratorConfig.GeneratorConfigFlags import generatorRunArgsToFlags generatorRunArgsToFlags(runArgs, flags) + + # convert arguments to flags + flags.fillFromArgs() + # Create an instance of the Sample(EvgenCAConfig) and update global flags accordingly + sample = setupSample(runArgs, flags) + + # Process pre-include + processPreInclude(runArgs, flags) + + # Process pre-exec + processPreExec(runArgs, flags) + # Lock flags flags.lock() + + if runArgs.VERBOSE: + evgenLog.debug('****************** Dump configuration flags *****************') + evgenLog.debug(flags.dump()) - # Announce start of job configuration - evgenLog.debug("****************** CONFIGURING EVENT GENERATION *****************") + # Announce start of job configuration + evgenLog.debug("****************** CONFIGURING EVENT GENERATION *****************") - evgenLog.info("Hello from GENtoEVGEN_Skeleton") + # Main object + from AthenaConfiguration.MainServicesConfig import MainEvgenServicesCfg + cfg = MainEvgenServicesCfg(flags) + + # Set up evgen job with all sequences + setupEvgenServices(flags, cfg) + + # Set up the process + AthSequencer = CompFactory.AthSequencer + evgenGenSeq = AthSequencer('EvgenGenSeq') + cfg.addSequence(evgenGenSeq, parentName='AthAllAlgSeq') + cfg.merge(sample.setupProcess(flags), sequenceName="EvgenGenSeq") + + # Set up the output stream + setupOutputStream(flags, runArgs, cfg, sample) + + # Print ComponentAccumulator components + if runArgs.VERBOSE: + evgenLog.debug("****************** DUMPING CA CONFIG *****************") + evgenLog.debug(cfg.printConfig()) + + # Run final ComponentAccumulator + sys.exit(not cfg.run().isSuccess()) diff --git a/Generators/GeneratorConfig/python/GenConfigHelpers.py b/Generators/GeneratorConfig/python/GenConfigHelpers.py index 2d75cb098ebcfed064e671754a188a3efac3aca8..1cccef591606b53ba5eeba255b747265aa84bfda 100644 --- a/Generators/GeneratorConfig/python/GenConfigHelpers.py +++ b/Generators/GeneratorConfig/python/GenConfigHelpers.py @@ -1,5 +1,9 @@ # Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +# Get logger +from AthenaCommon.Logging import logging +evgenLog = logging.getLogger('GenConfigHelpers') + # Generators providing input events via the LHEF format # (used to determine the input file dummy-naming strategy for C++ generators) LHEFGenerators = ["Lhef", # generic name: prefer to use the names below @@ -93,3 +97,35 @@ def gen_sortkey(genname): # Return a tuple return (genstage, genname) + +# Function to perform consistency check on jO +def checkJOConsistency(jofile): + import os, sys, string + + joparts = (os.path.basename(jofile)).split(".") + # Perform some consistency checks + if joparts[0].startswith("mc") and all(c in string.digits for c in joparts[0][2:]): + # Check that there are exactly 4 name parts separated by '.': MCxx, DSID, physicsShort, .py + if len(joparts) != 3: + evgenLog.error(jofile + " name format is wrong: must be of the form mc.<physicsShort>.py: please rename.") + sys.exit(1) + # Check the length limit on the physicsShort portion of the filename + jo_physshortpart = joparts[1] + if len(jo_physshortpart) > 50: + evgenLog.error(jofile + " contains a physicsShort field of more than 60 characters: please rename.") + sys.exit(1) + # There must be at least 2 physicsShort sub-parts separated by '_': gens, (tune)+PDF, and process + jo_physshortparts = jo_physshortpart.split("_") + if len(jo_physshortparts) < 2: + evgenLog.error(jofile + " has too few physicsShort fields separated by '_': should contain <generators>(_<tune+PDF_if_available>)_<process>. Please rename.") + sys.exit(1) + + # NOTE: a further check on physicsShort consistency is done below, after fragment loading + check_jofiles="/cvmfs/atlas.cern.ch/repo/sw/Generators/MC16JobOptions/scripts" + sys.path.append(check_jofiles) + from check_jo_consistency import check_naming + if os.path.exists(check_jofiles): + check_naming(os.path.basename(jofile)) + else: + evgenLog.error("check_jo_consistency.py not found") + sys.exit(1) diff --git a/Generators/GeneratorConfig/python/GeneratorConfigFlags.py b/Generators/GeneratorConfig/python/GeneratorConfigFlags.py index f13c99e613567714ca18869ab5ff4023b2f9421a..ce7d2ad5fc90d8a20c15d913887a06a2d6f58abc 100644 --- a/Generators/GeneratorConfig/python/GeneratorConfigFlags.py +++ b/Generators/GeneratorConfig/python/GeneratorConfigFlags.py @@ -19,8 +19,11 @@ def createGeneratorConfigFlags(): # Events per job gencf.addFlag("Generator.nEventsPerJob", 10000) + # Events per job + gencf.addFlag("Generator.DSID", 999999) + # First event - gencf.addFlag("Generator.firstEvent", 1) + gencf.addFlag("Generator.firstEvent", -1) # Number of HepMC events to print gencf.addFlag("Generator.printEvts", 0) @@ -43,7 +46,7 @@ def generatorRunArgsToFlags(runArgs, flags): flags.Beam.Energy = runArgs.ecmEnergy/2 * GeV else: raise RuntimeError("No center of mass energy provided.") - + if hasattr(runArgs, "ignoreBlackList"): flags.Generator.ignoreBlackList = runArgs.ignoreBlackList