ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Generators
Herwig7_i
python
Herwig7ConfigDecoder.py
Go to the documentation of this file.
1
# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
# Author: Lukas Kretschmann (lukas.kretschmann@cern.ch)
3
4
# Import standard Python modules for file handling, subprocess execution, and path manipulation
5
import
subprocess
6
import
os
7
from
pathlib
import
Path
8
9
# Import Athena-specific modules for logging
10
from
AthenaCommon
import
Logging
11
athMsgLog = Logging.logging.getLogger(
'Herwig7ConfigDecoder'
)
12
13
def
extract_herwig_lines
(input_file="Herwig.run", temp_file="HerwigConfigDecoder_RunCardDump.txt", skip_particles=False, skip_decays=False):
14
"""Extract unique lines containing '/Herwig/' (excluding 'cvmfs') from the input file and save to a temporary file.
15
16
Args:
17
input_file (str): Path to the input Herwig run card file (default: 'Herwig.run').
18
temp_file (str): Path to the temporary output file for extracted lines (default: 'HerwigConfigDecoder_RunCardDump.txt').
19
skip_particles (bool): If True, skip lines related to /Herwig/Particles, /Herwig/Masses, and /Herwig/Widths.
20
skip_decays (bool): If True, skip lines related to /Herwig/Decays.
21
22
Returns:
23
list: List of unique Herwig configuration lines extracted.
24
"""
25
try
:
26
# Read the input file and filter lines containing '/Herwig/' while excluding 'cvmfs'
27
with
open(input_file,
'r'
)
as
infile:
28
all_lines = [line.strip()
for
line
in
infile
if
'/Herwig/'
in
line
and
'cvmfs'
not
in
line]
29
herwig_lines = []
30
seen_paths =
set
()
# Track unique paths to avoid duplicates
31
skip_particles_count = 0
32
skip_decays_count = 0
33
for
line
in
all_lines:
34
# Skip particle-related lines if specified
35
if
skip_particles
and
(
'/Herwig/Particles'
in
line
or
'/Herwig/Masses'
in
line
or
'/Herwig/Widths'
in
line):
36
skip_particles_count += 1
37
continue
38
# Skip decay-related lines if specified
39
if
skip_decays
and
'/Herwig/Decays'
in
line:
40
skip_decays_count += 1
41
continue
42
# Skip duplicate lines
43
if
line
in
seen_paths:
44
athMsgLog.debug(f
"Skipped duplicate path: {line}"
)
45
continue
46
seen_paths.add(line)
47
herwig_lines.append(line)
48
# Clean up specific Herwig path formatting issues
49
for
i
in
range(len(herwig_lines)):
50
if
'|}/Herwig'
in
herwig_lines[i]:
51
herwig_lines[i] = herwig_lines[i].
replace
(
'|}/Herwig'
,
'/Herwig'
)
52
elif
'}/Herwig'
in
herwig_lines[i]:
53
herwig_lines[i] = herwig_lines[i].
replace
(
'}/Herwig'
,
'/Herwig'
)
54
55
# Log information about skipped lines
56
skipped_count = len(all_lines) - len(herwig_lines)
57
if
skipped_count > 0:
58
athMsgLog.debug(f
"Skipped {skipped_count} lines (including {len(all_lines) - len(seen_paths) - skip_particles_count - skip_decays_count} duplicates)"
)
59
if
skip_particles_count > 0:
60
athMsgLog.debug(f
"Skipped {skip_particles_count} /Herwig/Particles, /Herwig/Masses and /Herwig/Widths lines due to --skip-particles"
)
61
if
skip_decays_count > 0:
62
athMsgLog.debug(f
"Skipped {skip_decays_count} /Herwig/Decays lines due to --skip-decays"
)
63
64
# Write the filtered unique lines to the temporary file
65
with
open(temp_file,
'w'
)
as
outfile:
66
outfile.writelines(line +
'\n'
for
line
in
herwig_lines)
67
68
athMsgLog.info(f
"Successfully extracted {len(herwig_lines)} unique lines to {temp_file}"
)
69
return
herwig_lines
70
except
FileNotFoundError:
71
athMsgLog.info(f
"Error: Input file '{input_file}' not found"
)
72
return
[]
73
except
Exception
as
e:
74
athMsgLog.info(f
"Error: An error occurred: {str(e)}"
)
75
return
[]
76
77
def
run_herwig_get_commands
(commands, phase="paths"):
78
"""Execute multiple Herwig 'get' commands in a single shell session and parse the output.
79
80
Args:
81
commands (list): List of commands or tuples (for interfaces) to execute.
82
phase (str): Execution phase, either 'paths' for path commands or 'interfaces' for interface commands.
83
84
Returns:
85
list: List of tuples containing the command and its output or error message.
86
"""
87
if
not
commands:
88
return
[]
89
90
# Retrieve Herwig installation path from environment variable
91
herwig_path = os.getenv(
'HERWIG7_PATH'
)
92
if
not
herwig_path:
93
return
[(cmd,
"Error: Environment variable HERWIG7_PATH is not set"
)
for
cmd
in
commands]
94
95
# Construct the Herwig command to read the default repository
96
herwig_cmd = f
"{herwig_path}/bin/Herwig read --repo={herwig_path}/share/Herwig/HerwigDefaults.rpo"
97
98
try
:
99
# Create a temporary script file to store the commands
100
temp_script = f
"temp_herwig_script_{os.getpid()}.in"
101
with
open(temp_script,
'w'
)
as
f:
102
for
cmd
in
commands:
103
if
phase ==
"paths"
:
104
f.write(f
"get {cmd}\n"
)
105
else
:
# interfaces
106
f.write(f
"get {cmd[0]}:{cmd[1]}\n"
)
107
108
# Execute the Herwig command with the temporary script as input
109
cmd = f
"cat {temp_script} | {herwig_cmd}"
110
result = subprocess.run(
111
cmd,
112
shell=
True
,
113
capture_output=
True
,
114
text=
True
,
115
timeout=10 * len(commands)
# Scale timeout based on number of commands
116
)
117
118
# Process the command output
119
output = result.stdout.strip()
120
if
result.stderr:
121
output += f
"\nError: {result.stderr.strip()}"
122
123
# Clean up the temporary script file
124
Path(temp_script).unlink()
125
126
# Split output into individual command responses
127
outputs = output.split(
"Herwig> get "
)[1:]
if
output
else
[]
128
results = []
129
for
i, cmd
in
enumerate(commands):
130
if
i < len(outputs):
131
raw_output = outputs[i].
strip
()
132
# Remove the command itself from the output
133
cmd_str = cmd
if
phase ==
"paths"
else
f
"{cmd[0]}:{cmd[1]}"
134
if
raw_output.startswith(cmd_str):
135
raw_output = raw_output[len(cmd_str):].
strip
()
136
if
phase ==
"interfaces"
:
137
# Apply filtering for interface outputs
138
if
len(raw_output.split(
"\n"
)) == 1:
139
if
raw_output.split(
"\n"
)[0] !=
''
and
raw_output.split(
"\n"
)[0] !=
'Herwig>'
:
140
filtered_output = raw_output.split(
"\n"
)[0]
141
elif
raw_output.split(
"\n"
)[0] !=
'Herwig>'
:
142
filtered_output =
"No response from the interface"
143
else
:
144
filtered_output =
"No response from the interface"
145
elif
len(raw_output.split(
"\n"
)) == 2:
146
filtered_output =
"No response from the interface"
147
elif
len(raw_output.split(
"\n"
)) == 3:
148
if
all(
"Herwig"
in
line
for
line
in
raw_output.split(
"\n"
)):
149
filtered_output =
"No response from the interface"
150
else
:
151
filtered_output = raw_output.split(
"\n"
)[1]
152
else
:
153
filtered_output = raw_output
154
results.append((cmd[0], f
"{cmd[0]}:{cmd[1]}"
, filtered_output))
155
else
:
156
results.append((cmd, raw_output))
157
else
:
158
if
phase ==
"interfaces"
:
159
results.append((cmd[0], f
"{cmd[0]}:{cmd[1]}"
,
"Error: No output received"
))
160
else
:
161
results.append((cmd,
"Error: No output received"
))
162
163
return
results
164
165
except
subprocess.TimeoutExpired:
166
return
[(cmd,
"Error: Command timed out"
)
if
phase ==
"paths"
else
(cmd[0], f
"{cmd[0]}:{cmd[1]}"
,
"Error: Command timed out"
)
for
cmd
in
commands]
167
except
Exception
as
e:
168
return
[(cmd, f
"Error: {str(e)}"
)
if
phase ==
"paths"
else
(cmd[0], f
"{cmd[0]}:{cmd[1]}"
, f
"Error: {str(e)}"
)
for
cmd
in
commands]
169
170
def
execute_herwig_get
(herwig_lines, output_file="HerwigConfigDecoder_InterfaceDump.txt", max_lines=None):
171
"""Execute Herwig 'get' commands for all provided paths in a single shell session and save the results.
172
173
Args:
174
herwig_lines (list): List of Herwig configuration paths to process.
175
output_file (str): Path to the output file for results (default: 'HerwigConfigDecoder_InterfaceDump.txt').
176
max_lines (int, optional): Maximum number of lines to process (default: None, processes all lines).
177
"""
178
try
:
179
# Check if Herwig path is set
180
herwig_path = os.getenv(
'HERWIG7_PATH'
)
181
if
not
herwig_path:
182
athMsgLog.error(
"Error: Environment variable HERWIG7_PATH is not set"
)
183
return
184
185
# Limit the number of lines to process if specified
186
lines_to_process = herwig_lines[:max_lines]
if
max_lines
is
not
None
else
herwig_lines
187
188
# Open the output file and process the Herwig paths
189
with
open(output_file,
'w'
)
as
outfile:
190
total_tasks = len(lines_to_process)
191
athMsgLog.info(f
"Processing {total_tasks} Herwig paths"
)
192
results =
run_herwig_get_commands
(lines_to_process, phase=
"paths"
)
193
194
# Sort results to maintain original order
195
results.sort(key=
lambda
x: lines_to_process.index(x[0])
if
x[0]
in
lines_to_process
else
float(
'inf'
))
196
197
# Write results to the output file
198
for
line, output
in
results:
199
if
not
line:
200
continue
201
outfile.write(f
"{line}\n{output}\n\n"
)
202
athMsgLog.debug(f
"Success! Parsed the options for: {line}"
)
203
204
athMsgLog.info(f
"Results saved to {output_file} (processed {len(lines_to_process)} lines)"
)
205
206
except
Exception
as
e:
207
athMsgLog.info(f
"Error: An error occurred during execution: {str(e)}"
)
208
209
def
process_valid_interfaces
(input_file="HerwigConfigDecoder_InterfaceDump.txt", output_file="HerwigConfigDecoder_ConfigParameters.txt"):
210
"""Process valid interfaces for each /Herwig/ path, execute 'get' commands, and save results.
211
212
Args:
213
input_file (str): Path to the input file containing Herwig paths and interfaces (default: 'HerwigConfigDecoder_InterfaceDump.txt').
214
output_file (str): Path to the output file for interface results (default: 'HerwigConfigDecoder_ConfigParameters.txt').
215
"""
216
try
:
217
# Check if Herwig path is set
218
herwig_path_env = os.getenv(
'HERWIG7_PATH'
)
219
if
not
herwig_path_env:
220
athMsgLog.error(
"Error: Environment variable HERWIG7_PATH is not set"
)
221
return
222
223
# Read the input file and process each block of Herwig paths and interfaces
224
with
open(input_file,
'r'
)
as
infile, open(output_file,
'w'
)
as
outfile:
225
content = infile.read()
226
blocks = content.split(
'\n\n'
)
227
228
all_tasks = []
229
block_paths = []
230
errors = []
# Buffer for "No valid interfaces" errors
231
seen_paths =
set
()
# Track unique paths to avoid duplicates
232
for
block
in
blocks:
233
lines = block.split(
'\n'
)
234
if
not
lines
or
not
lines[0].startswith(
'/Herwig/'
):
235
continue
236
237
herwig_path = lines[0].
strip
()
238
if
herwig_path
in
seen_paths:
239
athMsgLog.debug(f
"Skipped duplicate path in herwig_results.txt: {herwig_path}"
)
240
continue
241
seen_paths.add(herwig_path)
242
block_paths.append(herwig_path)
243
# Extract valid interfaces from the block
244
valid_interfaces = [
245
line.strip()[2:]
for
line
in
lines[1:]
246
if
line.strip().startswith(
'* '
)
247
]
248
249
if
not
valid_interfaces:
250
errors.append((herwig_path, f
"No valid interfaces found for {herwig_path}"
))
251
outfile.write(f
"{herwig_path}\nNo valid interfaces found\n\n"
)
252
continue
253
254
all_tasks.extend([(herwig_path, interface)
for
interface
in
valid_interfaces])
255
256
if
not
all_tasks:
257
athMsgLog.info(
"No interfaces to process"
)
258
# Log any buffered errors before returning
259
for
_, error_msg
in
errors:
260
athMsgLog.debug(error_msg)
261
return
262
263
# Process all interfaces in a single shell session
264
athMsgLog.info(f
"Processing {len(all_tasks)} interfaces"
)
265
results =
run_herwig_get_commands
(all_tasks, phase=
"interfaces"
)
266
267
# Log buffered errors
268
for
_, error_msg
in
errors:
269
athMsgLog.debug(error_msg)
270
271
# Organize results by Herwig path
272
path_results = {}
273
for
herwig_path, get_command, filtered_output
in
results:
274
if
herwig_path
not
in
path_results:
275
path_results[herwig_path] = []
276
path_results[herwig_path].append((get_command, filtered_output))
277
278
# Write results to the output file
279
for
herwig_path
in
block_paths:
280
if
herwig_path
not
in
path_results:
281
continue
282
athMsgLog.debug(f
"Processing interfaces for {herwig_path}:"
)
283
outfile.write(f
"{herwig_path}\n"
)
284
for
get_command, filtered_output
in
path_results[herwig_path]:
285
athMsgLog.debug(f
"{get_command} ::: {filtered_output}"
)
286
outfile.write(f
"{get_command} ::: {filtered_output}\n"
)
287
outfile.write(
"\n"
)
288
289
athMsgLog.info(f
"Interface results saved to {output_file}"
)
290
291
except
FileNotFoundError:
292
athMsgLog.info(f
"Error: Input file '{input_file}' not found"
)
293
except
Exception
as
e:
294
athMsgLog.info(f
"Error: An error occurred during interface processing: {str(e)}"
)
295
296
def
remove_duplicate_blocks
(input_file="HerwigConfigDecoder_ConfigParameters.txt"):
297
"""Remove duplicate blocks in the configuration parameters file, keeping the first occurrence.
298
299
Args:
300
input_file (str): Path to the input file to process (default: 'HerwigConfigDecoder_ConfigParameters.txt').
301
"""
302
try
:
303
# Read the input file and split into blocks
304
with
open(input_file,
'r'
)
as
infile:
305
content = infile.read()
306
blocks = content.rstrip().
split
(
'\n\n'
)
307
308
# Keep the first block for each unique Herwig path
309
path_blocks = {}
310
for
block
in
blocks:
311
lines = block.split(
'\n'
)
312
if
not
lines
or
not
lines[0].startswith(
'/Herwig/'
):
313
continue
314
herwig_path = lines[0].
strip
()
315
if
herwig_path
not
in
path_blocks:
316
path_blocks[herwig_path] = block
317
else
:
318
athMsgLog.debug(f
"Removed duplicate block for {herwig_path}"
)
319
320
# Collect unique blocks in their original order
321
unique_blocks = []
322
seen_paths =
set
()
323
for
block
in
blocks:
324
lines = block.split(
'\n'
)
325
if
not
lines
or
not
lines[0].startswith(
'/Herwig/'
):
326
continue
327
herwig_path = lines[0].
strip
()
328
if
herwig_path
not
in
seen_paths:
329
seen_paths.add(herwig_path)
330
unique_blocks.append(path_blocks[herwig_path])
331
332
# Write the unique blocks back to the file
333
with
open(input_file,
'w'
)
as
outfile:
334
outfile.write(
'\n\n'
.join(unique_blocks) +
'\n'
)
335
336
athMsgLog.info(f
"Removed duplicate blocks in {input_file}"
)
337
athMsgLog.info(f
"Retained {len(unique_blocks)} unique blocks"
)
338
339
except
FileNotFoundError:
340
athMsgLog.info(f
"Error: Input file '{input_file}' not found"
)
341
except
Exception
as
e:
342
athMsgLog.info(f
"Error: An error occurred while removing duplicate blocks: {str(e)}"
)
343
344
def
DecodeRunCard
(input_file='Herwig.run', skip_particles=False, skip_decays=False):
345
"""Main function to decode a Herwig run card by extracting paths, executing commands, and processing interfaces.
346
347
Args:
348
input_file (str): Path to the Herwig run card file (default: 'Herwig.run').
349
skip_particles (bool): If True, skip particle-related lines.
350
skip_decays (bool): If True, skip decay-related lines.
351
"""
352
athMsgLog.info(
"Hello from the config-decoder!"
)
353
354
# Step 1: Extract Herwig configuration lines
355
herwig_lines =
extract_herwig_lines
(
356
input_file=input_file,
357
skip_particles=skip_particles,
358
skip_decays=skip_decays
359
)
360
361
# Step 2: Execute Herwig 'get' commands for the extracted paths
362
if
herwig_lines:
363
execute_herwig_get
(herwig_lines)
364
365
# Step 3: Process valid interfaces for the paths
366
process_valid_interfaces
()
367
368
# Step 4: Remove any duplicate blocks in the final output
369
remove_duplicate_blocks
()
370
371
athMsgLog.info(
"Goodbye from the config-decoder!"
)
SiliconTech::strip
@ strip
Definition
FPGATrackSimTypes.h:25
set
STL class.
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition
hcg.cxx:312
split
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition
hcg.cxx:179
Herwig7ConfigDecoder.extract_herwig_lines
extract_herwig_lines(input_file="Herwig.run", temp_file="HerwigConfigDecoder_RunCardDump.txt", skip_particles=False, skip_decays=False)
Definition
Herwig7ConfigDecoder.py:13
Herwig7ConfigDecoder.run_herwig_get_commands
run_herwig_get_commands(commands, phase="paths")
Definition
Herwig7ConfigDecoder.py:77
Herwig7ConfigDecoder.process_valid_interfaces
process_valid_interfaces(input_file="HerwigConfigDecoder_InterfaceDump.txt", output_file="HerwigConfigDecoder_ConfigParameters.txt")
Definition
Herwig7ConfigDecoder.py:209
Herwig7ConfigDecoder.execute_herwig_get
execute_herwig_get(herwig_lines, output_file="HerwigConfigDecoder_InterfaceDump.txt", max_lines=None)
Definition
Herwig7ConfigDecoder.py:170
Herwig7ConfigDecoder.DecodeRunCard
DecodeRunCard(input_file='Herwig.run', skip_particles=False, skip_decays=False)
Definition
Herwig7ConfigDecoder.py:344
Herwig7ConfigDecoder.remove_duplicate_blocks
remove_duplicate_blocks(input_file="HerwigConfigDecoder_ConfigParameters.txt")
Definition
Herwig7ConfigDecoder.py:296
Generated on
for ATLAS Offline Software by
1.17.0