ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Generators
Pepper_i
python
PepperConfig.py
Go to the documentation of this file.
1
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
"""Driver that invokes the LCG-installed `pepper` binary from inside an
3
AthGeneration job.
4
5
This module exists because the pepper_kokkos package as shipped in LCG
6
needs three things wired up correctly before it will run:
7
8
1. The PEPPER_DATA_PATH env var must point at the process-data
9
directory (the binary's compiled-in path is the build server's
10
scratch dir and doesn't exist on cvmfs). The wrapper sets it
11
automatically.
12
13
2. The output filename's extension governs the format. The pepper
14
binary accepts `.lhef[.gz]`, `.hepmc3[.gz]`, `.hdf5`, and `.debug`.
15
AthGeneration's Gen_tf.py post-job validator globs for `*.events*`
16
(MadGraph/Powheg convention). We resolve the tension by accepting
17
`.events.gz` (or `.events`) as the user-facing default: pepper is
18
invoked with the equivalent `.lhef[.gz]` form, then symlinked back
19
to the requested `.events.gz` name.
20
21
3. The binary deadlocks in Kokkos initialisation on CPU-only nodes,
22
waiting for a CUDA device. Jobs MUST run on a GPU-equipped worker.
23
"""
24
25
import
os
26
import
shutil
27
import
subprocess
28
29
from
AthenaCommon.Logging
import
logging
30
31
log = logging.getLogger(
"PepperConfig"
)
32
33
34
# Map of user-facing output extensions to the pepper-CLI extension
35
# we actually invoke pepper with. .events[.gz] is the transform's
36
# expected pattern; pepper itself doesn't recognise it, so we
37
# transparently swap to .lhef[.gz] and symlink afterwards.
38
_EXT_MAP = {
39
".events.gz"
:
".lhef.gz"
,
40
".events"
:
".lhef"
,
41
# Pass-through: pepper recognises these natively.
42
".lhef.gz"
:
".lhef.gz"
,
43
".lhef"
:
".lhef"
,
44
".hepmc3.gz"
:
".hepmc3.gz"
,
45
".hepmc3"
:
".hepmc3"
,
46
".hdf5"
:
".hdf5"
,
47
".debug"
:
".debug"
,
48
}
49
50
51
def
_resolve_pepper_executable
():
52
"""Return the absolute path to the `pepper` binary, or raise."""
53
exe = os.environ.get(
"PEPPER_EXECUTABLE"
)
or
shutil.which(
"pepper"
)
54
if
not
exe
or
not
os.path.isfile(exe):
55
raise
RuntimeError(
56
"Could not locate the `pepper` executable. "
57
"Either set PEPPER_EXECUTABLE, or ensure that an LCG view "
58
"containing pepper_kokkos is on PATH "
59
"(e.g. /cvmfs/sft.cern.ch/lcg/views/LCG_109a/x86_64-el9-gcc13-opt)."
60
)
61
return
exe
62
63
64
def
_resolve_data_path
(exe):
65
"""Return the directory containing the Pepper process CSVs.
66
67
Tries (in order): PEPPER_DATA_PATH env var; the share/pepper/data
68
directory next to the binary (1.8.x layout); the share/pepper_data
69
directory next to the binary (1.1.x layout).
70
"""
71
candidate = os.environ.get(
"PEPPER_DATA_PATH"
)
72
if
candidate
and
os.path.isfile(os.path.join(candidate,
"2j.csv"
)):
73
return
candidate
74
75
install_root = os.path.dirname(os.path.dirname(os.path.realpath(exe)))
76
for
sub
in
(
"share/pepper/data"
,
"share/pepper_data"
):
77
candidate = os.path.join(install_root, sub)
78
if
os.path.isfile(os.path.join(candidate,
"2j.csv"
)):
79
return
candidate
80
81
raise
RuntimeError(
82
"Could not locate Pepper's process-data directory. "
83
"Set PEPPER_DATA_PATH explicitly to the directory containing "
84
"2j.csv, z0j.csv, etc."
85
)
86
87
88
def
_split_ext
(filename):
89
"""Return (stem, longest matching extension) where the extension
90
is one of the keys in _EXT_MAP. Raises if no match."""
91
for
ext
in
sorted(_EXT_MAP.keys(), key=len, reverse=
True
):
92
if
filename.endswith(ext):
93
return
filename[:-len(ext)], ext
94
raise
ValueError(
95
"Pepper output filename %r must end in one of: %s"
96
% (filename,
", "
.join(sorted(_EXT_MAP.keys()))))
97
98
99
def
run_pepper
(process,
100
nevents,
101
seed,
102
ecm,
103
output="pepper.events.gz",
104
batch_size=None,
105
n_batches=None,
106
extra_args=None):
107
"""Invoke `pepper`, produce an event file, and return its path.
108
109
The default output is `.events.gz` to match the convention used by
110
AthGeneration's `Gen_tf.py` for upstream-generator input files
111
(`Pythia8_LHEF` and the transform's post-job validator both look
112
for `*.events*`). Internally we invoke pepper with the
113
corresponding `.lhef[.gz]` extension and create the `.events[.gz]`
114
name as a symlink.
115
116
Parameters
117
----------
118
process : Pepper process shortcut, e.g. "ppjj", "ppz1j", "pptt".
119
nevents : Approximate number of unweighted events wanted.
120
seed : Random seed (typically runArgs.randomSeed).
121
ecm : Centre-of-mass energy in GeV (typically runArgs.ecmEnergy).
122
output : Output filename. Must end in one of .events[.gz],
123
.lhef[.gz], .hepmc3[.gz], .hdf5, or .debug.
124
batch_size : Pepper batch size. Defaults to min(nevents, 5000).
125
n_batches : Number of batches. Defaults so that we usually
126
overshoot the requested nevents after accounting for
127
unweighting efficiency.
128
extra_args : List of additional CLI args to pass through.
129
130
Returns
131
-------
132
The absolute path of the event file in $PWD.
133
"""
134
exe =
_resolve_pepper_executable
()
135
data =
_resolve_data_path
(exe)
136
137
stem, user_ext =
_split_ext
(output)
138
pepper_ext = _EXT_MAP[user_ext]
139
pepper_output = stem + pepper_ext
140
141
if
batch_size
is
None
:
142
batch_size =
min
(nevents, 5000)
143
if
n_batches
is
None
:
144
# Unweighting efficiency is process-dependent; pad by 2x so
145
# we usually overshoot rather than undershoot.
146
n_batches =
max
(1, (2 * nevents + batch_size - 1) // batch_size)
147
148
cmd = [exe,
149
"--process"
, str(process),
150
"--collision-energy"
, str(ecm),
151
"--seed"
, str(seed),
152
"--batch-size"
, str(batch_size),
153
"--n-batches"
, str(n_batches),
154
"--output"
, pepper_output]
155
if
extra_args:
156
cmd += list(extra_args)
157
158
env = os.environ.copy()
159
env[
"PEPPER_DATA_PATH"
] = data
160
# Sensible OpenMP defaults so Kokkos doesn't warn at startup.
161
env.setdefault(
"OMP_PROC_BIND"
,
"spread"
)
162
env.setdefault(
"OMP_PLACES"
,
"threads"
)
163
164
log.info(
"PEPPER_DATA_PATH = %s"
, data)
165
log.info(
"Pepper command: %s"
,
" "
.join(cmd))
166
167
rc = subprocess.call(cmd, env=env)
168
if
rc != 0:
169
raise
RuntimeError(
"pepper exited with rc=%d"
% rc)
170
if
not
os.path.isfile(pepper_output):
171
raise
RuntimeError(
"pepper succeeded but %s is missing"
% pepper_output)
172
173
# If user requested a name different from what pepper produces
174
# (typical: user asked for .events.gz; pepper made .lhef.gz),
175
# link the user-facing name to the actual file.
176
if
output != pepper_output:
177
if
os.path.lexists(output):
178
os.remove(output)
179
os.symlink(os.path.basename(pepper_output), output)
180
log.info(
"Linked %s -> %s"
, output, pepper_output)
181
182
return
os.path.abspath(output)
min
#define min(a, b)
Definition
cfImp.cxx:40
max
#define max(a, b)
Definition
cfImp.cxx:41
PepperConfig.run_pepper
run_pepper(process, nevents, seed, ecm, output="pepper.events.gz", batch_size=None, n_batches=None, extra_args=None)
Definition
PepperConfig.py:106
PepperConfig._split_ext
_split_ext(filename)
Definition
PepperConfig.py:88
PepperConfig._resolve_pepper_executable
_resolve_pepper_executable()
Definition
PepperConfig.py:51
PepperConfig._resolve_data_path
_resolve_data_path(exe)
Definition
PepperConfig.py:64
Generated on
for ATLAS Offline Software by
1.17.0