ATLAS Offline Software
Loading...
Searching...
No Matches
python.GENtoEVGEN_Skeleton Namespace Reference

Functions

 _mk_symlink (srcfile, dstfile)
 _find_unique_file (pattern)
 _merge_lhe_files (listOfFiles, outputFile)
 _handle_input_files (generators, flags)
 _validate_sample_properties (sample)
 setupSample (runArgs, flags)
 checkBlackList (cache, generatorName, checkType)
 fromRunArgs (runArgs)

Variables

 jobPropertiesDisallowed
 evgenLog = logging.getLogger("Gen_tf")

Detailed Description

Functionality core of the Gen_tf transform

Function Documentation

◆ _find_unique_file()

python.GENtoEVGEN_Skeleton._find_unique_file ( pattern)
protected

Definition at line 38 of file GENtoEVGEN_Skeleton.py.

38def _find_unique_file(pattern):
39 import glob
40 files = glob.glob(pattern)
41 # Check that there is exactly 1 match
42 if not files:
43 raise RuntimeError(f"No {pattern} file found")
44 elif len(files) > 1:
45 raise RuntimeError(f"More than one {pattern} file found")
46 return files[0]
47
48
49# This function merges a list of input LHE files into one output file.
50# The header is taken from the first file, but the number of events is
51# updated to equal the total number of events in all input files.

◆ _handle_input_files()

python.GENtoEVGEN_Skeleton._handle_input_files ( generators,
flags )
protected

Definition at line 99 of file GENtoEVGEN_Skeleton.py.

99def _handle_input_files(generators, flags):
100 from GeneratorConfig.GenConfigHelpers import gens_lhef
101
102 # Name of event files produced by various generators.
103 events_file_map = {
104 "Alpgen": "alpgen.unw_events",
105 "Protos": "protos.events",
106 "ProtosLHEF": "protoslhef.events",
107 "BeamHaloGenerator": "beamhalogen.events",
108 "HepMCAscii": "events.hepmc",
109 "ReadMcAscii": "events.hepmc",
110 }
111 eventsFile = None
112 for gen_name, out_file in events_file_map.items():
113 if gen_name in generators:
114 eventsFile = out_file
115 break
116 if eventsFile is None:
117 if gens_lhef(generators):
118 eventsFile = "events.lhe"
119 else:
120 raise RuntimeError(f"Unknown type of ME generator: {generators}")
121
122 genInputFiles = [f.strip() for f in flags.Generator.inputGeneratorFile.split(",") if f.strip()]
123 if not genInputFiles:
124 raise RuntimeError("Generator.inputGeneratorFile is empty while input handling is requested")
125
126 def _input_root(path, keep_suffix_after_underscore=False):
127 fname = os.path.basename(path)
128 if any(ext in fname for ext in (".tar.", ".tgz", ".gz")):
129 return re.split(r"\.tar\.|\.tgz|\.gz", fname, maxsplit=1)[0]
130 parts = fname.split("._", 1)
131 if keep_suffix_after_underscore and len(parts) > 1:
132 return parts[0] + "._" + parts[1].split(".", 1)[0]
133 return parts[0]
134
135 # If there is a single file, make a symlink. If multiple files, merge them into one output eventsFile.
136 if len(genInputFiles) == 1:
137 inputroot = _input_root(genInputFiles[0], keep_suffix_after_underscore=False)
138 if inputroot.endswith(".events"):
139 inputroot = inputroot[:-7]
140 realEventsFile = _find_unique_file(f"*{inputroot}.*ev*ts")
141 _mk_symlink(realEventsFile, eventsFile)
142 return
143
144 allFiles = []
145 for file in genInputFiles:
146 # Since we can have multiple files from the same task, include more of the filename
147 # to make the lookup unique in the plain-file case.
148 inputroot = _input_root(file, keep_suffix_after_underscore=True)
149 evgenLog.info("inputroot = %s", inputroot)
150 realEventsFile = _find_unique_file(f"*{inputroot}.*ev*ts")
151 # The only input format where merging is permitted is LHE.
152 with open(realEventsFile, "r") as f:
153 first_line = f.readline()
154 if "LesHouche" not in first_line:
155 raise RuntimeError(f"{realEventsFile} is NOT a LesHouche file")
156 allFiles.append(realEventsFile)
157 _merge_lhe_files(allFiles, eventsFile)
158
159 # counting the number of events in LHE input
160 eventsInLHE = 0
161 with open(eventsFile) as f:
162 for line in f:
163 eventsInLHE += line.count('/event')
164 return eventsInLHE
165
166
167# Helper function to validate and set sample properties
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179

◆ _merge_lhe_files()

python.GENtoEVGEN_Skeleton._merge_lhe_files ( listOfFiles,
outputFile )
protected

Definition at line 52 of file GENtoEVGEN_Skeleton.py.

52def _merge_lhe_files(listOfFiles, outputFile):
53 if os.path.exists(outputFile):
54 print("outputFile", outputFile, "already exists. Will rename to", outputFile + ".OLD")
55 os.rename(outputFile, outputFile + ".OLD")
56
57 total_events = 0
58 for file in listOfFiles:
59 with open(file, "r") as f:
60 total_events += sum(1 for line in f if "</event>" in line)
61
62 wrote_header = False
63 with open(outputFile, "w") as output:
64 for file in listOfFiles:
65 inHeader = True
66 header = ""
67 print("*** Starting file", file)
68 with open(file, "r") as infile:
69 for line in infile:
70 # Reading first event signals that we are done with all header information.
71 if "<event" in line and inHeader:
72 inHeader = False
73 if not wrote_header:
74 wrote_header = True
75 output.write(header)
76 output.write(line)
77 # Each input file ends with "</LesHouchesEvents>". We only write it once at the end.
78 elif not inHeader and "</LesHouchesEvents>" not in line:
79 output.write(line)
80
81 if inHeader:
82 # Format for storing number of events differs in MG and Powheg.
83 if "nevents" in line:
84 # MG5 format is "n = nevents".
85 parts = line.split("=")
86 if parts:
87 line = line.replace(parts[0], str(total_events), 1)
88 elif "numevts" in line:
89 # Powheg format is "numevts n".
90 parts = line.split()
91 if len(parts) > 1:
92 line = line.replace(parts[1], str(total_events), 1)
93 header += line
94
95 output.write("</LesHouchesEvents>\n")
96
97
98# Helper for handling input files
void print(char *figname, TCanvas *c1)

◆ _mk_symlink()

python.GENtoEVGEN_Skeleton._mk_symlink ( srcfile,
dstfile )
protected

Definition at line 25 of file GENtoEVGEN_Skeleton.py.

25def _mk_symlink(srcfile, dstfile):
26 if dstfile:
27 if os.path.exists(dstfile) and not os.path.samefile(dstfile, srcfile):
28 os.remove(dstfile)
29 if not os.path.exists(dstfile):
30 evgenLog.info(f"Symlinking {srcfile} to {dstfile}")
31 print (f"Symlinking {srcfile} to {dstfile}")
32 os.symlink(srcfile, dstfile)
33 else:
34 evgenLog.debug(f"Symlinking: {dstfile} is already the same as {srcfile}")
35
36
37# Helper functions for finding input file

◆ _validate_sample_properties()

python.GENtoEVGEN_Skeleton._validate_sample_properties ( sample)
protected

Definition at line 168 of file GENtoEVGEN_Skeleton.py.

168def _validate_sample_properties(sample):
169 # Required fields with lightweight, explicit validators.
170 required_rules = {
171 "keywords": lambda v: isinstance(v, list) and len(v) > 0,
172 "contact": lambda v: isinstance(v, list) and len(v) > 0,
173 "nEventsPerJob": lambda v: v is not None,
174 }
175 for field, validator in required_rules.items():
176 value = getattr(sample, field, None)
177 if not validator(value):
178 raise RuntimeError(f"self.{field} should be set in Sample(EvgenConfig)")
179
180 input_files_per_job = getattr(sample, "inputFilesPerJob", 0)
181 me_generator = getattr(sample, "MEgenerator", None)
182
183 if input_files_per_job < 0:
184 raise RuntimeError("self.inputFilesPerJob should be >= 0 in Sample(EvgenConfig)")
185 if input_files_per_job > 0 and not me_generator:
186 raise RuntimeError("self.MEgenerator should be set when self.inputFilesPerJob > 0 in Sample(EvgenConfig)")
187 if input_files_per_job == 0 and me_generator:
188 raise RuntimeError("self.MEgenerator should be empty when self.inputFilesPerJob == 0 in Sample(EvgenConfig)")
189
190
191# Function that reads the jO and returns an instance of Sample(EvgenCAConfig)

◆ checkBlackList()

python.GENtoEVGEN_Skeleton.checkBlackList ( cache,
generatorName,
checkType )

Definition at line 276 of file GENtoEVGEN_Skeleton.py.

276def checkBlackList(cache, generatorName, checkType) :
277 isError = None
278 fileName = "BlackList_caches.txt" if checkType == "black" else "PurpleList_generators.txt"
279 with open(f"/cvmfs/atlas.cern.ch/repo/sw/Generators/MC16JobOptions/common/{fileName}") as bfile:
280 for line in bfile.readlines():
281 if not line.strip():
282 continue
283 # Bad caches
284 badCache=line.split(',')[1].strip()
285 # Bad generators
286 badGens=line.split(',')[2].strip()
287
288 used_gens = ','.join(generatorName)
289 # Match Generator and release cache
290 if cache==badCache and re.search(badGens,used_gens) is not None:
291 if badGens=="": badGens="all generators"
292 isError=f"{cache} is {checkType}-listed for {badGens}"
293 return isError
294 return isError
295
296
297# Main function

◆ fromRunArgs()

python.GENtoEVGEN_Skeleton.fromRunArgs ( runArgs)

Definition at line 298 of file GENtoEVGEN_Skeleton.py.

298def fromRunArgs(runArgs):
299 # print release information
300 d = release_metadata()
301 evgenLog.info("using release [%(project name)s-%(release)s] [%(platform)s] [%(nightly name)s/%(nightly release)s] -- built on [%(date)s]", d)
302 athenaRel = d["release"]
303
304 evgenLog.info("****************** STARTING EVENT GENERATION *****************")
305
306 evgenLog.info("**** Transformation run arguments")
307 evgenLog.info(runArgs)
308
309 evgenLog.info("**** Setting-up configuration flags")
310
311 from AthenaConfiguration.AllConfigFlags import initConfigFlags
312 flags = initConfigFlags()
313
314 from AthenaConfiguration.Enums import ProductionStep
315 flags.Common.ProductionStep = ProductionStep.Generation
316
317 # Convert run arguments to global athena flags
318 from PyJobTransforms.CommonRunArgsToFlags import commonRunArgsToFlags
319 commonRunArgsToFlags(runArgs, flags)
320
321 # Convert generator-specific run arguments to global athena flags
322 from GeneratorConfig.GeneratorConfigFlags import generatorRunArgsToFlags
323 generatorRunArgsToFlags(runArgs, flags)
324
325 # convert arguments to flags
326 flags.fillFromArgs()
327
328 # Create an instance of the Sample(EvgenCAConfig) and update global flags accordingly
329 sample = setupSample(runArgs, flags)
330
331 # Setup the main flags
332 flags.Exec.FirstEvent = runArgs.firstEvent
333 # Max events should be not set, job stopping is handled by CountHepMC
334 # using the RequestedOutput property
335 flags.Exec.MaxEvents = -1
336
337 if hasattr(runArgs, "inputEVNT_PreFile"):
338 flags.Input.Files = runArgs.inputEVNT_PreFile
339 else:
340 flags.Input.Files = []
341 flags.Input.RunNumbers = [flags.Generator.DSID]
342 flags.Input.TimeStamps = [0]
343
344 flags.Output.EVNTFileName = runArgs.outputEVNTFile
345
346 flags.Beam.Energy = runArgs.ecmEnergy / 2 * GeV
347
348 flags.PerfMon.doFastMonMT = True
349 flags.PerfMon.doFullMonMT = True
350
351 # Process pre-include
352 processPreInclude(runArgs, flags)
353
354 # Process pre-exec
355 processPreExec(runArgs, flags)
356
357 # Lock flags
358 flags.lock()
359
360 evgenLog.info("**** Configuration flags")
361 if runArgs.VERBOSE:
362 flags.dump()
363 else:
364 flags.dump("Generator.*")
365
366 # Print various stuff
367 evgenLog.info(".transform = Gen_tf")
368 evgenLog.info(".platform = " + str(os.environ["BINARY_TAG"]))
369
370 # Announce start of job configuration
371 evgenLog.info("**** Configuring event generation")
372
373 # Main object
374 from AthenaConfiguration.MainServicesConfig import MainEvgenServicesCfg
375 cfg = MainEvgenServicesCfg(flags, withSequences=True)
376
377 # Input file handling (if needed)
378 if flags.Input.Files:
379 from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
380 cfg.merge(PoolReadCfg(flags))
381
382 # EventInfoCnvAlg
383 from xAODEventInfoCnv.xAODEventInfoCnvConfig import EventInfoCnvAlgCfg
384 cfg.merge(EventInfoCnvAlgCfg(flags, disableBeamSpot=True, xAODKey="TMPEvtInfo"),
385 sequenceName=EvgenSequence.Generator.value)
386
387 # Set up the process
388 cfg.merge(sample.setupProcess(flags))
389
390 # Sort the list of generator names into standard form
391 from GeneratorConfig.GenConfigHelpers import gen_sortkey
392 from GeneratorConfig.Versioning import generatorsGetInitialVersionedDictionary, generatorsVersionedStringList
393 generators = sorted(cfg.getService("GeneratorInfoSvc").Generators, key=gen_sortkey)
394 gendict = generatorsGetInitialVersionedDictionary(generators)
395 generatorsWithVersion = generatorsVersionedStringList(gendict)
396
397 # Check if the setup requires steering
398 from GeneratorConfig.GenConfigHelpers import gen_require_steering
399 if gen_require_steering(generators):
400 if hasattr(runArgs, "outputEVNTFile") and not hasattr(runArgs, "outputEVNT_PreFile"):
401 raise RuntimeError("'EvtGen' found in job options name, please set '--steering=afterburn'")
402
403 # LHE input handling
404 if flags.Generator.inputFilesPerJob > 0:
405 if not flags.Generator.inputGeneratorFile:
406 raise RuntimeError(f"Sample sets inputFilesPerJob = {flags.Generator.inputFilesPerJob} but Gen_tf run without inputGeneratorFile")
407 else:
408 nEventsLHE = _handle_input_files(generators, flags)
409
410 # Check black-list and purple-list
411 blError = checkBlackList(athenaRel, generators, "black")
412 plError = checkBlackList(athenaRel, generators, "purple")
413 if blError is not None:
414 raise RuntimeError(blError)
415 if plError is not None:
416 evgenLog.warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
417 evgenLog.warning(f"!!! WARNING {plError} !!!")
418 evgenLog.warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
419
420 # Fix non-standard event features
421 if not flags.Input.Files:
422 from EvgenProdTools.EvgenProdToolsConfig import FixHepMCCfg
423 from GeneratorConfig.GenConfigHelpers import gens_purgenoendvtx
424 generatorsList = generators.copy()
425 if "Pythia8" in generatorsList:
426 pythia8Alg = cfg.getEventAlgo("Pythia8_i")
427 if pythia8Alg.Beam1 != "PROTON" or pythia8Alg.Beam2 != "PROTON":
428 # generator name is still "Pythia8", even when colliding nuclei
429 generatorsList.append("Pythia8-Angantyr")
430 cfg.merge(FixHepMCCfg(flags,
431 PurgeUnstableWithoutEndVtx=gens_purgenoendvtx(generatorsList)))
432
433 # Sanity check the event record (not appropriate for all generators)
434 from GeneratorConfig.GenConfigHelpers import gens_testhepmc
435 if gens_testhepmc(generators):
436 from EvgenProdTools.EvgenProdToolsConfig import TestHepMCCfg
437 cfg.merge(TestHepMCCfg(flags))
438
439 # Copy the event weight from HepMC to the Athena EventInfo class
440 from EvgenProdTools.EvgenProdToolsConfig import CopyEventWeightCfg
441 cfg.merge(CopyEventWeightCfg(flags))
442
443 from EvgenProdTools.EvgenProdToolsConfig import FillFilterValuesCfg
444 cfg.merge(FillFilterValuesCfg(flags))
445
446 # Configure the event counting (AFTER all filters)
447 from EvgenProdTools.EvgenProdToolsConfig import CountHepMCCfg
448 cfg.merge(CountHepMCCfg(flags,
449 RequestedOutput=sample.nEventsPerJob if runArgs.maxEvents == -1
450 else runArgs.maxEvents))
451 evgenLog.info("Requested output events = %d", cfg.getEventAlgo("CountHepMC").RequestedOutput)
452
453 # Print out the contents of the first 5 events (after filtering)
454 if hasattr(runArgs, "printEvts") and runArgs.printEvts > 0:
455 from TruthIO.TruthIOConfig import PrintMCCfg
456 cfg.merge(PrintMCCfg(flags,
457 LastEvent=runArgs.printEvts))
458
459 # PerfMon
460 from PerfMonComps.PerfMonCompsConfig import PerfMonMTSvcCfg
461 cfg.merge(PerfMonMTSvcCfg(flags), sequenceName=EvgenSequence.Post.value)
462
463 # Estimate time needed for Simulation
464 from EvgenProdTools.EvgenProdToolsConfig import SimTimeEstimateCfg
465 cfg.merge(SimTimeEstimateCfg(flags))
466
467 # TODO: Rivet
468
469 # Extra metadata
470 from EventInfoMgt.TagInfoMgrConfig import TagInfoMgrCfg
471 from GeneratorConfig.GenConfigHelpers import gen_lhef
472 metadata = {
473 "project_name": "IS_SIMULATION",
474 f"AtlasRelease_{runArgs.trfSubstepName}": flags.Input.Release or "n/a",
475 "beam_energy": str(int(flags.Beam.Energy)),
476 "beam_type": flags.Beam.Type.value,
477 "generators": '+'.join(generatorsWithVersion),
478 "tune": cfg.getService("GeneratorInfoSvc").Tune,
479 "hepmc_version": f"HepMC{os.environ['HEPMCVER']}",
480 "keywords": ", ".join(sample.keywords).lower(),
481 "lhefGenerator": '+'.join(filter(gen_lhef, generators)),
482 "mc_channel_number": str(flags.Generator.DSID),
483 }
484 if hasattr(sample, "process"): metadata.update({"evgenProcess": sample.process})
485 if hasattr(sample, "specialConfig"): metadata.update({"specialConfiguration": sample.specialConfig})
486 if hasattr(sample, "hardPDF"): metadata.update({"hardPDF": sample.hardPDF})
487 if hasattr(sample, "softPDF"): metadata.update({"softPDF": sample.softPDF})
488 if hasattr(sample, "randomSeed"): metadata.update({"randomSeed": str(runArgs.randomSeed)})
489 cfg.merge(TagInfoMgrCfg(flags, tagValuePairs=metadata))
490
491 # Print metadata in the log
492 evgenLog.info(f"HepMC version {os.environ['HEPMCVER']}")
493 evgenLog.info(f"MetaData: generatorTune = {cfg.getService('GeneratorInfoSvc').Tune}")
494 evgenLog.info("MetaData: generatorName = {}".format(generatorsWithVersion))
495 if flags.Generator.inputGeneratorFile:
496 print(f"MetaData: Number of input LHE events = {nEventsLHE}")
497
498 # Configure output stream
499 from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
500 cfg.merge(OutputStreamCfg(flags, "EVNT", ["McEventCollection#*"]))
501
502 # Add in-file MetaData
503 from xAODMetaDataCnv.InfileMetaDataConfig import SetupMetaDataForStreamCfg
504 cfg.merge(SetupMetaDataForStreamCfg(flags, "EVNT"))
505
506 # Post-include
507 processPostInclude(runArgs, flags, cfg)
508
509 # Post-exec
510 processPostExec(runArgs, flags, cfg)
511
512 # Write AMI tag into in-file MetaData
513 from PyUtils.AMITagHelperConfig import AMITagCfg
514 cfg.merge(AMITagCfg(flags, runArgs))
515
516 # Print ComponentAccumulator components
517 cfg.printConfig(prefix="Gen_tf", printSequenceTreeOnly=not runArgs.VERBOSE)
518
519 # Run final ComponentAccumulator
520 sys.exit(not cfg.run().isSuccess())

◆ setupSample()

python.GENtoEVGEN_Skeleton.setupSample ( runArgs,
flags )

Definition at line 192 of file GENtoEVGEN_Skeleton.py.

192def setupSample(runArgs, flags):
193 # Only permit one jobConfig argument for evgen
194 if len(runArgs.jobConfig) != 1:
195 raise RuntimeError("You must supply one and only one jobConfig file argument")
196
197 evgenLog.info("Using JOBOPTSEARCHPATH (as seen in skeleton) = {}".format(os.environ["JOBOPTSEARCHPATH"]))
198
199 FIRST_DIR = (os.environ["JOBOPTSEARCHPATH"]).split(":")[0]
200
201 # Find jO file
202 jofiles = [f for f in os.listdir(FIRST_DIR) if (f.startswith("mc") and f.endswith(".py"))]
203 if len(jofiles) !=1:
204 raise RuntimeError("You must supply one and only one jobOption file in DSID directory")
205 jofile = jofiles[0]
206
207 # Perform consistency checks on the jO
208 from GeneratorConfig.GenConfigHelpers import checkNaming, checkNEventsPerJob, checkKeywords, checkCategories
209 checkNaming(jofile)
210
211 # Import the jO as a module
212 # We cannot do import BLAH directly since
213 # 1. the filenames are not python compatible (mc.GEN_blah.py)
214 # 2. the filenames are different for every jO
215 import importlib.util
216 spec = importlib.util.spec_from_file_location(
217 name="sample",
218 location=os.path.join(FIRST_DIR,jofile),
219 )
220 jo = importlib.util.module_from_spec(spec)
221 spec.loader.exec_module(jo)
222 evgenLog.info("including file %s", jofile)
223
224 # Create instance of Sample(EvgenCAConfig)
225 sample = jo.Sample(flags)
226
227 # Set up the sample properties
228 sample.setupFlags(flags)
229
230 # Set the random number seed
231 # Need to use logic in EvgenJobTransforms.Generate_dsid_ranseed
232
233 # Get DSID
234 dsid = os.path.basename(runArgs.jobConfig[0])
235 if dsid.startswith("Test"):
236 dsid = dsid.split("Test")[-1]
237
238 # Update the global flags
239 if dsid.isdigit():
240 flags.Generator.DSID = int(dsid)
241
242 # Set nEventsPerJob
243 if not sample.nEventsPerJob:
244 evgenLog.info("#############################################################")
245 evgenLog.info(" !!!! no sample.nEventsPerJob set !!!")
246 evgenLog.info("#############################################################")
247 # We don't need to set the global flag because its default is 10000
248 else:
249 checkNEventsPerJob(sample)
250 evgenLog.info(" nEventsPerJob = " + str(sample.nEventsPerJob))
251 flags.Generator.nEventsPerJob = sample.nEventsPerJob
252
253 # Validate all required/conditional sample metadata with explicit rules.
254 _validate_sample_properties(sample)
255
256 # Propagate optional sample values to global flags.
257 flags.Generator.inputFilesPerJob = sample.inputFilesPerJob
258 flags.Generator.MEgenerator = sample.MEgenerator or ""
259
260 # Print sample metadata in the log.
261 for var, value in vars(sample).items():
262 evgenLog.info("MetaData: {} = {}".format(var, value))
263
264 # Keywords check
265 if hasattr(sample, "keywords"):
266 checkKeywords(sample, evgenLog)
267
268 # L1, L2 categories check
269 if hasattr(sample, "categories"):
270 checkCategories(sample, evgenLog)
271
272 return sample
273
274
275# Function to check black-listed releases

Variable Documentation

◆ evgenLog

python.GENtoEVGEN_Skeleton.evgenLog = logging.getLogger("Gen_tf")

Definition at line 11 of file GENtoEVGEN_Skeleton.py.

◆ jobPropertiesDisallowed

python.GENtoEVGEN_Skeleton.jobPropertiesDisallowed

Definition at line 7 of file GENtoEVGEN_Skeleton.py.