ATLAS Offline Software
Loading...
Searching...
No Matches
MadGraphConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
4from GeneratorConfig.Sequences import EvgenSequence, EvgenSequenceFactory
5from GeneratorConfig.GeneratorInfoSvcConfig import GeneratorInfoSvcCfg
6
7import os
8
9from MadGraphControl.MadGraphPDFSettings import MadGraphPDFSets, get_pdf_set
10
11
12def _get_nevents(flags, safety):
13 """Helper function to determing number of events to be generated
14 in MadGraph, based on MaxEvents or nEventsPerJob and a user-provided
15 safety factor (the latter defaults to 1.1 to allow for failures
16 in showering stage)."""
17 try:
18 sf = float(safety)
19 except (TypeError, ValueError) as exc:
20 raise RuntimeError(f"safety must be numeric, got {safety}.") from exc
21 if sf <= 0:
22 raise RuntimeError(f"safety must be > 0, got {safety}.")
23
24 base_events = (
25 flags.Exec.MaxEvents
26 if flags.Exec.MaxEvents > 0
27 else flags.Generator.nEventsPerJob
28 )
29
30 return int(base_events * sf)
31
32
33def _prepare_lhe_for_shower(produced_output, lhe_file):
34 # The supported lhe file formats are .lhe, .lhe.gz, .tar.gz, and .tgz.
35 # .tar.gz and .tgz are tarballs that contain a single .lhe file
36 # .gz files can be read directly by Pythia so we don't want to unzip them.
37 if (produced_output and produced_output.endswith(".gz")
38 and not produced_output.endswith((".tar.gz", ".tgz"))):
39 compressed_lhe_file = (
40 lhe_file if lhe_file.endswith(".gz") else f"{lhe_file}.gz"
41 )
43 compressed_lhe_file, [produced_output], overwrite=True):
44 return
45 raise RuntimeError(
46 "Could not prepare compressed LHE file for showering. "
47 f"Expected: {produced_output}"
48 )
49
50 primary_output = None
51 if produced_output:
52 if produced_output.endswith(".tar.gz"):
53 root = produced_output[:-7]
54 elif produced_output.endswith(".tgz"):
55 root = produced_output[:-4]
56 else:
57 root, _ = os.path.splitext(produced_output)
58 primary_output = f"{root}.events"
59
60 # If the transform requested a specific TXT output name, symlink the
61 # produced output to the filename that the transform expects
62 candidates = [candidate
63 for candidate in (primary_output,
64 "tmp_LHE_events.events",
65 "events.events")
66 if candidate]
67 if _symlink_first_existing(lhe_file, candidates, overwrite=True):
68 return
69
70 raise RuntimeError(
71 "Could not prepare LHE file for showering. "
72 f"Expected one of: {', '.join(candidates)}"
73 )
74
75
76def _symlink_first_existing(link_name, candidates, overwrite=False):
77 """
78 Helper function to symlink the first existing file in candidates to link_name.
79 """
80 if os.path.exists(link_name) and not overwrite:
81 return True
82
83 for candidate in candidates:
84 if not candidate or not os.path.exists(candidate):
85 continue
86 if os.path.abspath(candidate) == os.path.abspath(link_name):
87 return True
88 if os.path.lexists(link_name):
89 os.remove(link_name)
90 os.symlink(os.path.abspath(candidate), link_name)
91 return True
92
93 return False
94
95
96def MadGraphBaseCfg(flags, **kwargs):
97 """Base MadGraph CA fragment. It returns a CA object
98 that contains the generator metadata and registers
99 default values for steering the MGC object
100 (to be created by the top-level config)."""
101 from MadGraphControl.MGC import (
102 MADGRAPH_CATCH_ERRORS,
103 MADGRAPH_DEVICES,
104 MADGRAPH_PDFSETTING,
105 )
106
107 # Default values for MGC. Use MGC defaults for now, but these
108 # can be declared here in the future.
109 defaults = {
110 "safety": 1.1,
111 "pdf_setting": MADGRAPH_PDFSETTING,
112 "devices": MADGRAPH_DEVICES,
113 "catch_errors": MADGRAPH_CATCH_ERRORS,
114 "lhe_version": 3,
115 "saveProcDir": False,
116 "keepJpegs": False,
117 "usePMGSettings": False,
118 }
119
120 # Create a dictionary with default settings.
121 # This can be used in top-level configs.
122 cfg = {**defaults, **{k: v for k, v in kwargs.items() if v is not None}}
123
124 # Create the CA object adding the generator metadata
125 ca = ComponentAccumulator(EvgenSequenceFactory(EvgenSequence.Generator))
126 ca.merge(
127 GeneratorInfoSvcCfg(flags, Generators=["MadGraph"]),
128 sequenceName=EvgenSequence.Generator.value,
129 )
130
131 return ca, cfg
132
133
135 flags,
136 process_definition,
137 *,
138 safety=None,
139 run_card_settings=None,
140 param_card_settings=None,
141 pdf_setting=None,
142 devices=None,
143 catch_errors=None,
144 lhe_version=None,
145 saveProcDir=None,
146 plugin=None,
147 keepJpegs=None,
148 usePMGSettings=None,
149 prepare_lhe_for_shower=False,
150 lhe_file="events.lhe",
151):
152 """
153 Fragment for configuring a LHE generation step.
154
155 This starts from MadGraphBaseCfg and creates a MGC instance
156 that is later used to call the MadGraphUtils functions that steer
157 the event generation.
158
159 All arguments after * are keyword-only to avoid confusion
160 between MadGraphControl settings and CA configuration options.
161 Set prepare_lhe_for_shower=True when the same job should feed the
162 produced LHE file into a shower generator.
163
164 process_definition is required, the rest is optional.
165
166 run_card_settings maps run_card.dat settings to their requested values.
167 param_card_settings maps param_card.dat settings to dictionaries of
168 parameter indices and values.
169
170 If prepare_lhe_for_shower is True, the produced LHE file will be
171 symlinked to lhe_file (default: events.lhe)
172 for later use in the showering step.
173 """
174
175 from MadGraphControl.MGC import MGControl
176 import MadGraphControl.MadGraphUtils as MadGraphUtils
177
178 if isinstance(pdf_setting, MadGraphPDFSets):
179 pdf_setting = get_pdf_set(pdf_setting)
180
181 # TODO: implement deduplication of settings as done in Pythia8Config
182 ca, cfg = MadGraphBaseCfg(
183 flags,
184 safety=safety,
185 pdf_setting=pdf_setting,
186 devices=devices,
187 catch_errors=catch_errors,
188 lhe_version=lhe_version,
189 saveProcDir=saveProcDir,
190 keepJpegs=keepJpegs,
191 usePMGSettings=usePMGSettings,
192 )
193
194 run_card_settings = {} if run_card_settings is None else dict(run_card_settings)
195 param_card_settings = {} if param_card_settings is None else dict(param_card_settings)
196
197 # Overwrite the number of events in the run_card_settings with the value
198 # determined from the flags and safety factor.
199 run_card_settings["nevents"] = _get_nevents(flags, cfg["safety"])
200
201 # Create the MGC instance
202 mgc = MGControl(
203 process=process_definition,
204 plugin=plugin,
205 keepJpegs=cfg["keepJpegs"],
206 usePMGSettings=cfg["usePMGSettings"],
207 pdf_setting=cfg["pdf_setting"],
208 devices=cfg["devices"],
209 catch_errors=cfg["catch_errors"],
210 )
211
212 # Bind the MGC instance to the MadGraphUtils module,
213 # so that it can be used in the calls to the MadGraphUtils functions
214 # This is not following the CA logic completely, but it avoids
215 # having to pass the MGC instance through multiple function calls.
216 MadGraphUtils.my_MGC_instance = mgc
217
218 # Create the process directory
219 process_dir = mgc.process_dir
220
221 # Modify the run_card settings in the process directory before generating events.
222 mgc.runCardDict.update(run_card_settings)
223
224 # Modify the parameter_card settings in the process directory before generating events.
225 mgc.paramCard.modify_paramCardDict(
226 params=param_card_settings
227 )
228
229 # Generate events
230 MadGraphUtils.generate(process_dir=process_dir, flags=flags, pdf_setting=cfg["pdf_setting"])
231 produced_output = MadGraphUtils.arrange_output(
232 process_dir=process_dir,
233 flags=flags,
234 lhe_version=cfg["lhe_version"],
235 saveProcDir=cfg["saveProcDir"],
236 pdf_setting=cfg["pdf_setting"],
237 )
238
239 # If requested, prepare the produced LHE file for showering
240 # by symlinking it to the filename that pythia expects,
241 # by default "events.lhe".
242 if prepare_lhe_for_shower:
243 _prepare_lhe_for_shower(produced_output, lhe_file)
244
245 # If the transform requested a specific TXT output name, symlink the
246 # produced output to the filename that the transform expects
247 # (only if the file does not exist).
248 requested_output = flags.Output.TXTFileName
249 if requested_output and not os.path.exists(requested_output):
250 root, _ = os.path.splitext(requested_output)
251 candidates = [candidate for candidate in (produced_output, f"{root}.events", "events.events") if candidate]
252 _symlink_first_existing(requested_output, candidates, overwrite=True)
253
254 return ca
MadGraphBaseCfg(flags, **kwargs)
MadGraphCfg(flags, process_definition, *, safety=None, run_card_settings=None, param_card_settings=None, pdf_setting=None, devices=None, catch_errors=None, lhe_version=None, saveProcDir=None, plugin=None, keepJpegs=None, usePMGSettings=None, prepare_lhe_for_shower=False, lhe_file="events.lhe")
_prepare_lhe_for_shower(produced_output, lhe_file)
_get_nevents(flags, safety)
_symlink_first_existing(link_name, candidates, overwrite=False)