ATLAS Offline Software
Loading...
Searching...
No Matches
postprocessors/reweighter.py
Go to the documentation of this file.
1# Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
2
3from ... import Logging
4from ...decorators import timed
5from ...utility import FileParser
6from ..generators import multicore_untimed, singlecore_untimed
7import collections
8import os
9import shutil
10import re
11
12
13logger = Logging.logging.getLogger("PowhegControl")
14
15
16WeightTuple = collections.namedtuple("WeightTuple", ["parameter_settings", "keywords", "ID", "name", "combine", "group", "parallel_xml_compatible"])
17
18def default_weight_exists_already(powheg_LHE_output):
19 """! Returns True if the LHE file contains a weight called "nominal".
20
21 The weight must be present before the beginning of the first <event> block,
22 otherwise the function returns False.
23
24 @param powheg_LHE_output Name of LHE file produced by PowhegBox.
25 """
26 search_string = "<weight id='0' >default</weight>"
27 with open(powheg_LHE_output, 'r') as lhe_file:
28 for line in lhe_file:
29 if search_string in line:
30 return True
31 if '<event>' in line:
32 # Important to break the loop here with a return, otherwise the
33 # entire (often very large) file is scanned!
34 return False
35
36
37@timed("reweighting")
38def reweighter(process, weight_groups, powheg_LHE_output):
39 """! Add a series of additional weights to an LHE file.
40
41 @param process PowhegBox process.
42 @param weight_groups OrderedDict containing groups of weights to add.
43 @param powheg_LHE_output Name of LHE file produced by PowhegBox.
44 """
45 logger.info("Starting to run PowhegControl PDF and QCD scale reweighter")
46
47 weight_list = []
48
49
50 non_weight_attributes = ["parameter_names", "combination_method", "keywords"]
51
52
53 xml_kwds = {"renscfact": "renscfact", "facscfact": "facscfact", "lhans1": "lhapdf", "lhans2": "lhapdf", "width_correction" : "width_correction", "run_mode": "run_mode"}
54
55 # Initial values for scale, PDF and other weight groups
56 _idx_scale_start, _idx_PDF_start, _idx_other_start = 1001, 2001, 3001
57
58 if not process.use_XML_reweighting:
59 logger.warning("... XML-style reweighting is not enabled for this process. Will use old (multiple-pass) method.")
60
61 # Construct ordered list of weights
62 for group_name, weight_group in weight_groups.items():
63 logger.info("Preparing weight group: {:<19} with {} weights".format(group_name, len(weight_group) - len(non_weight_attributes)))
64
65 # Name -> keyword dictionary is different if this is an XML reweighting
66 is_parallel_xml_compatible = process.use_XML_reweighting and all([k in xml_kwds.keys() for _kw_set in weight_group["keywords"] for k in _kw_set])
67 if is_parallel_xml_compatible:
68 _keywords = [list(set([xml_kwds[k] for k in _kw_set])) for _kw_set in weight_group["keywords"]]
69 else:
70 if process.use_XML_reweighting:
71 logger.warning("... this weight group is incompatible with XML-style reweighting. Will use old (multiple-pass) method.")
72 _keywords = weight_group["keywords"]
73 keyword_dict = dict((n, k) for n, k in zip(weight_group["parameter_names"], _keywords))
74
75 # Common arguments for all weights
76 tuple_kwargs = {"keywords": keyword_dict, "combine": weight_group["combination_method"], "group": group_name, "parallel_xml_compatible": is_parallel_xml_compatible}
77
78 # Nominal variation: ID=0
79 if group_name == "nominal" and not default_weight_exists_already(powheg_LHE_output):
80 weight_list.append(WeightTuple(parameter_settings=weight_group["nominal"], ID=0, name="nominal", **tuple_kwargs))
81
82 # Scale variations: ID=1001+
83 elif group_name == "scale_variation":
84 for idx, name in enumerate([k for k in weight_group.keys() if k not in non_weight_attributes], start=_idx_scale_start):
85 weight_list.append(WeightTuple(parameter_settings=weight_group[name], ID=idx, name=name, **tuple_kwargs))
86 _idx_scale_start = idx + 1
87
88 # PDF variations: ID=2001+
89 elif group_name == "PDF_variation":
90 for idx, name in enumerate([k for k in weight_group.keys() if k not in non_weight_attributes], start=_idx_PDF_start):
91 weight_list.append(WeightTuple(parameter_settings=weight_group[name], ID=idx, name=name, **tuple_kwargs))
92 _idx_PDF_start = idx + 1
93
94 # Other variations: ID=3001+
95 else:
96 for idx, name in enumerate([k for k in weight_group.keys() if k not in non_weight_attributes], start=_idx_other_start):
97 weight_list.append(WeightTuple(parameter_settings=weight_group[name], ID=idx, name=name, **tuple_kwargs))
98 _idx_other_start = idx + 1
99
100 # Make backup of generation statistics
101 if os.path.isfile("pwgcounters.dat"):
102 shutil.copy("pwgcounters.dat", "pwgcounters.dat.bak")
103
104 # ... the Powheg input card
105 try:
106 shutil.copy("powheg.input", "powheg.input.before_reweighting")
107 except IOError:
108 raise IOError("Powheg input card ('powheg.input') cannot be found at the start of reweighting. In a normal setup, PowhegControl generates this input card. Its absence is probably a sign of problems --- please investigate or contact the PowhegControl experts.")
109
110 # ... and also backup unweighted events
111 try:
112 shutil.copy(powheg_LHE_output, "{}.before_reweighting".format(powheg_LHE_output))
113 except IOError:
114 raise IOError("Nominal LHE file could not be found. Probably POWHEG-BOX crashed during event generation.")
115
116 # Do XML-reweighting
117 if process.use_XML_reweighting:
118 # Add nominal weight if not already present
119 if not default_weight_exists_already(powheg_LHE_output) and not any([weight.group == "nominal" for weight in weight_list]):
120 if process.has_parameter("run_mode") and process.parameters_by_keyword("run_mode")[0].value != 1 and hasattr(process, "reweight_for_MiNNLO") and process.reweight_for_MiNNLO:
121 weight_list = [WeightTuple(ID=0, name="nominal", group="nominal", parallel_xml_compatible=True, parameter_settings=[("run_mode",1)], keywords={"run_mode":["run_mode"]}, combine=None)] + weight_list
122 else:
123 weight_list = [WeightTuple(ID=0, name="nominal", group="nominal", parallel_xml_compatible=True, parameter_settings=[], keywords=None, combine=None)] + weight_list
124
125 FileParser("powheg.input").text_replace("pdfreweight .*", "pdfreweight 0")
126 # Construct xml output
127 xml_lines, serial_xml_weight_list, current_weightgroup = [], [], None
128 for weight in weight_list:
129 # Group elements
130 if weight.parallel_xml_compatible and weight.group != current_weightgroup:
131 xml_lines.append("</weightgroup>")
132 current_weightgroup = weight.group
133 xml_lines.append("<weightgroup name='{}' combine='{}'>".format(weight.group, weight.combine))
134 # Weight elements
135 if weight.parallel_xml_compatible:
136 keyword_pairs_str = " ".join(["{}={}".format(k, v) for p, v in weight.parameter_settings for k in weight.keywords[p]])
137 xml_lines.append("<weight id='{}'> {} </weight>".format(weight.ID, keyword_pairs_str))
138 else:
139 serial_xml_weight_list.append(weight)
140 xml_lines.append(xml_lines.pop(0)) # move the closing tag to the end
141 n_parallel_xml_weights = len(weight_list) - len(serial_xml_weight_list)
142
143 # First iterate over the simultaneous XML weights
144 if n_parallel_xml_weights > 0:
145 # Write xml output
146 with open("reweighting_input.xml", "w") as f_rwgt:
147 f_rwgt.write("<initrwgt>\n")
148 [f_rwgt.write("{}\n".format(xml_line)) for xml_line in xml_lines]
149 f_rwgt.write("</initrwgt>")
150
151 # Add reweighting lines to runcard
152 # TODO: add check that the file contains these keywords, otherwise raise an error about
153 # what is probably a faulty process interface!
154 FileParser("powheg.input").text_replace("rwl_file .*", "rwl_file 'reweighting_input.xml'")
155 FileParser("powheg.input").text_replace("rwl_add .*", "rwl_add 1")
156 FileParser("powheg.input").text_replace("clobberlhe .*", "clobberlhe 1")
157 if process.has_parameter("run_mode") and process.parameters_by_keyword("run_mode")[0].value != 1 and hasattr(process, "reweight_for_MiNNLO") and process.reweight_for_MiNNLO:
158 FileParser("powheg.input").text_replace("run_mode .*", "run_mode 1")
159
160 logger.info("Preparing simultaneous calculation of {} additional weights for generated events.".format(n_parallel_xml_weights))
161
162 # Allow RES processes to do their reweighting with manyseeds enabled, or they will look for the wrong files
163 if list(process.parameters_by_name("manyseeds"))[0].value == 1:
164 if process.powheg_version == "RES":
165 os.system('cp reweighting_input.xml backup_of_reweighting_input.xml')
166 os.system('cp powheg.input powheg.input.for_reweighting')
167 multicore_untimed(process)
168 else:
169 FileParser("powheg.input").text_replace("manyseeds .*", "manyseeds 0")
170 FileParser("powheg.input").text_replace("parallelstage .*", "parallelstage -1")
171 os.system('cp reweighting_input.xml backup_of_reweighting_input.xml')
172 os.system('cp powheg.input powheg.input.for_reweighting')
173 singlecore_untimed(process)
174 else:
175 os.system('cp reweighting_input.xml backup_of_reweighting_input.xml')
176 os.system('cp powheg.input powheg.input.for_reweighting')
177 singlecore_untimed(process)
178
179 # Move the reweighted file back
180 rename_LHE_output(powheg_LHE_output)
181
182 # Iterate over any variations which require non-simultaneous reweighting
183 if len(serial_xml_weight_list) > 0:
184 logger.info("Preparing individual calculation of {} additional weights for generated events.".format(len(serial_xml_weight_list)))
185 shutil.move("reweighting_input.xml", "reweighting_input.nominal")
186 for idx_weight, weight in enumerate(serial_xml_weight_list, start=1):
187 add_single_weight(process, weight, idx_weight, len(serial_xml_weight_list), use_XML=True)
188 rename_LHE_output(powheg_LHE_output)
189 shutil.move("reweighting_input.nominal", "reweighting_input.xml")
190
191 # Do non-XML-reweighting
192 else:
193 # Iterate over any variations which require old-style reweighting
194 logger.info("Preparing individual calculation of {} additional weights for generated events.".format(len(weight_list)))
195 for idx_weight, weight in enumerate(weight_list, start=1):
196 add_single_weight(process, weight, idx_weight, len(weight_list), use_XML=False)
197 rename_LHE_output(powheg_LHE_output)
198
199 if process.use_XML_reweighting:
200 comment_patterns = ["#pdf", "#rwgt", "#new weight", "#matching", " #Random" ]
201 if process.remove_oldStyle_rwt_comments:
202
203 logger.info("Removing comment lines from lhe file - these lines can be added back using the 'remove_oldStyle_rwt_comments=False' argument in generate()")
204 # Remove comment lines, which may cause a crash in Pythia
205 for pattern in comment_patterns:
206 removed = FileParser(powheg_LHE_output).text_remove("^"+pattern)
207 logger.info("{} line(s) starting with '{}' were removed from {}".format(removed, pattern, powheg_LHE_output))
208 else:
209 logger.info("Fixing comment lines from lhe file - these lines can be simply removed using the 'remove_oldStyle_rwt_comments=True' argument in generate()")
210 for pattern in comment_patterns: # no whitespace needed in patterns here
211 repair_comment_lines(powheg_LHE_output, pattern) # the last pattern starts with a space
212
213 # Restore #emscales comment lines needed by PowhegHooksST, regardless of remove_oldStyle_rwt_comments
214 restore_emscales_comments(powheg_LHE_output)
215
216 replacelist = []
217 # Rename all weights
218 for weight in weight_list:
219 replacelist += [[".* id='{}' .*".format(weight.ID), "<weight id='{weight_id}'>{weight_name}</weight>".format(weight_id=weight.ID, weight_name=weight.name), 1]]
220
221 # Correct LHE version identification; otherwise Pythia will treat all files as v1
222 replacelist += [['LesHouchesEvents version="1.0"', 'LesHouchesEvents version="3.0"', 1]]
223 FileParser(powheg_LHE_output).text_replace_multi(replacelist)
224
225 # Restore generation statistics and initial runcard
226 shutil.move("powheg_nominal.input", "powheg.input")
227 if os.path.isfile("pwgcounters.dat.bak"):
228 shutil.move("pwgcounters.dat.bak", "pwgcounters.dat")
229
230def add_single_weight(process, weight, idx_weight, n_total, use_XML):
231 """! Add a single additional weight to an LHE file.
232
233 @param process PowhegBox process.
234 @param weight Tuple with details of the weight to be added.
235 @param idx_weight Which number this weight is.
236 @param n_total Total number of weights being added.
237 @param use_XML Whether to use XML or old-style reweighting.
238 """
239 @timed("weight variation {}/{}".format(idx_weight, n_total))
240 def __timed_inner_fn():
241 logger.info("... weight name is: {}".format(weight.name))
242 logger.info("... weight index ID is: {}".format(weight.ID))
243 shutil.copy("powheg_nominal.input", "powheg.input")
244
245 # Make appropriate runcard changes
246 if use_XML:
247 FileParser("powheg.input").text_replace("rwl_file .*", "rwl_file 'reweighting_input.xml'")
248 FileParser("powheg.input").text_replace("rwl_add .*", "rwl_add 1")
249 FileParser("powheg.input").text_replace("clobberlhe .*", "clobberlhe 1")
250 FileParser("powheg.input").text_replace("pdfreweight .*", "pdfreweight 0")
251 else:
252 # As the nominal process has already been run, turn on compute_rwgt
253 FileParser("powheg.input").text_replace("compute_rwgt 0", "compute_rwgt 1")
254 # Ensure that manyseeds/parallelstage are turned off, as these cause the reweighting to crash
255 FileParser("powheg.input").text_replace("manyseeds .*", "manyseeds 0")
256 FileParser("powheg.input").text_replace("parallelstage .*", "parallelstage -1")
257
258 # Apply weight settings
259 for (parameter, value) in weight.parameter_settings:
260 try:
261 for keyword in weight.keywords[parameter]:
262 FileParser("powheg.input").text_replace("^{} .*".format(keyword), "{} {}".format(keyword, value))
263 logger.info("... setting {} to {}".format(parameter, value))
264 except KeyError:
265 logger.warning("Parameter '{}' not recognised. Cannot reweight!".format(parameter))
266
267 if use_XML:
268 # Create XML reweighting file
269 with open("reweighting_input.xml", "w") as f_rwgt:
270 f_rwgt.write("<initrwgt>\n")
271 f_rwgt.write("<weightgroup name='{}' combine='{}'>\n".format(weight.group, weight.combine))
272 f_rwgt.write("<weight id='{}'> </weight>\n".format(weight.ID))
273 f_rwgt.write("</weightgroup>\n")
274 f_rwgt.write("</initrwgt>")
275 else:
276 # Change reweighting metadata in runcard
277 FileParser("powheg.input").text_replace("lhrwgt_descr .*", "lhrwgt_descr '{}'".format(weight.name))
278 FileParser("powheg.input").text_replace("lhrwgt_id .*", "lhrwgt_id '{}'".format(weight.ID))
279 FileParser("powheg.input").text_replace("lhrwgt_group_combine .*", "lhrwgt_group_combine '{}'".format(weight.combine))
280 FileParser("powheg.input").text_replace("lhrwgt_group_name .*", "lhrwgt_group_name '{}'".format(weight.group))
281
282 # Run the process until termination
283 if list(process.parameters_by_name("manyseeds"))[0].value == 1:
284 if process.powheg_version == "RES":
285 multicore_untimed(process)
286 else:
287 FileParser("powheg.input").text_replace("manyseeds .*", "manyseeds 0")
288 FileParser("powheg.input").text_replace("parallelstage .*", "parallelstage -1")
289 singlecore_untimed(process)
290 else:
291 singlecore_untimed(process)
292
293 # Run single weight variation
294 __timed_inner_fn()
295
296
297def rename_LHE_output(powheg_LHE_output):
298 reweighted_events_file_name = powheg_LHE_output.replace(".lhe", "-rwgt.lhe")
299 try:
300 shutil.move(reweighted_events_file_name, powheg_LHE_output)
301 except IOError:
302 raise IOError("Reweighted LHE file '{filename}' could not be found. Probably POWHEG-BOX crashed during reweighting.".format(filename=reweighted_events_file_name))
303
304
305def repair_comment_lines(lheFile, pattern):
306 if pattern is None:
307 return
308 if not os.path.isfile("{}.before_reweighting".format(lheFile)):
309 logger.error("Impossible to find file {}.before_reweighting".format(lheFile))
310 raise IOError
311
312 # create backup file
313 shutil.move(lheFile, "{}.text_replace_backup".format(lheFile))
314
315 n_found = 0
316 n_events = 0
317 with open("{}.text_replace_backup".format(lheFile), "rb") as f_input:
318 line_in = f_input.readline()
319 while line_in:
320 if re.search("^"+pattern.lstrip(), line_in.decode().lstrip()):
321 n_found += 1
322 elif re.search("^</event>", line_in.decode().lstrip()):
323 n_events += 1
324 line_in = f_input.readline()
325
326 n_found_noWeights = 0
327 n_events_noWeights = 0
328 with open("{}.before_reweighting".format(lheFile), "rb") as f_input:
329 line_in = f_input.readline()
330 while line_in:
331 if re.search("^"+pattern.lstrip(), line_in.decode().lstrip()):
332 n_found_noWeights += 1
333 elif re.search("^</event>", line_in.decode().lstrip()):
334 n_events_noWeights += 1
335 line_in = f_input.readline()
336
337 # in case anything turns bad, will give up fixing
338 impossible_to_fix = False
339
340 # initialise counters to 0
341 n_replaced = 0
342 n_added_back = 0
343 with open(lheFile, "w") as f_output:
344 # first strategy: loop over the file with weights, and replace the relevant lines from the files without weights
345 if n_found == n_found_noWeights:
346 # loop in parallel on the lhe file with weights that we want to fix, and on the lhe file without weights from which we'll take the correct comment lines
347 with open("{}.text_replace_backup".format(lheFile), "rb") as f_input, open("{}.before_reweighting".format(lheFile), "rb") as f_input_noWeights:
348 line_in = f_input.readline()
349 line_in_noWeights = f_input_noWeights.readline()
350 while line_in: # loop on the lines of the output file with weights
351 if re.search("^"+pattern.lstrip(), line_in.decode().lstrip()): # found pattern, this line may need to be replaced (ignoring leading whitespaces)
352 processed = False
353 while line_in_noWeights: # loop on the next lines in the other files
354 if re.search("^"+pattern, line_in_noWeights.decode().lstrip()): # found pattern, using this line as replacement (ignoring leading whitespaces)
355 if (line_in.decode().rstrip().lstrip() != line_in_noWeights.decode().rstrip().lstrip()):
356 f_output.write(line_in_noWeights.decode())
357 n_replaced += 1
358 else:
359 f_output.write(line_in.decode())
360 line_in_noWeights = f_input_noWeights.readline()
361 processed = True
362 break # end loop over the other file for the moment
363 else: # pattern not found in the other file, go to next line
364 line_in_noWeights = f_input_noWeights.readline() # keep trying
365 if processed: # if this line has been processed, no need to do the next line
366 line_in = f_input.readline() # next line in output file
367 else : # line hasn't been processed, it means the other file doesn't have enough appropriate lines
368 impossible_to_fix = True
369 break # end loop over patterns, giving up fixing
370 if impossible_to_fix: # it's pointless to continue
371 break
372 # no pattern not found, we keep the line as-is and go to the next
373 f_output.write(line_in.decode())
374 line_in = f_input.readline()
375
376 # this is a cross-check - both file should have the same number of comment lines
377 while line_in_noWeights: # finish processing the other file - we shouldn't find any more lines with pattern
378 if re.search("^"+pattern, line_in_noWeights.decode()): # found pattern, something is wrong
379 impossible_to_fix = True
380 break
381 if impossible_to_fix: # it's pointless to continue
382 break
383 line_in_noWeights = f_input_noWeights.readline()
384 # alternative strategy: loop over file without weights, and add the comments from it just before the end of event
385 elif n_found == 0 and n_found_noWeights == n_events_noWeights and n_events == n_events_noWeights:
386 # loop in parallel on the lhe file with weights that we want to fix, and on the lhe file without weights from which we'll take the correct comment lines
387 with open("{}.text_replace_backup".format(lheFile), "rb") as f_input, open("{}.before_reweighting".format(lheFile), "rb") as f_input_noWeights:
388 line_in = f_input.readline()
389 line_in_noWeights = f_input_noWeights.readline()
390 while line_in_noWeights:
391 if re.search("^"+pattern, line_in_noWeights.decode().lstrip()):
392 while line_in:
393 if re.search("^</event>", line_in.decode().lstrip()):
394 f_output.write(line_in_noWeights.decode())
395 f_output.write(line_in.decode())
396 n_added_back += 1
397 line_in = f_input.readline()
398 break
399 else:
400 f_output.write(line_in.decode())
401 line_in = f_input.readline()
402 line_in_noWeights = f_input_noWeights.readline()
403 while line_in:
404 f_output.write(line_in.decode())
405 line_in = f_input.readline()
406
407 # processing of files ended, now handling the outcome
408 if n_found == 0 and n_found_noWeights == 0: # no line found with this pattern in the file to be fixed
409 shutil.move("{}.text_replace_backup".format(lheFile), lheFile)
410 logger.info("No line with pattern '{}' was found in lhe file before or after reweighting, so need to fix it".format(pattern))
411 elif impossible_to_fix: # we couldn't fix it, so we get the backup copy back, and remove the problematic lines
412 shutil.move("{}.text_replace_backup".format(lheFile), lheFile)
413 logger.info("Impossible to fix the possibly buggy comment lines with pattern {} in {} using the corresponding lines from {}.before_reweighting".format(pattern, lheFile, lheFile))
414 logger.info("Keeping those lines as they are in '{}".format(lheFile))
415 else:
416 os.remove("{}.text_replace_backup".format(lheFile))
417 if n_replaced != 0:
418 logger.info("{} line(s) starting with '{}' replaced in {} using the corresponding line(s) from {}.before_reweighting".format(n_replaced, pattern, lheFile, lheFile))
419 elif n_found != 0 and n_replaced == 0:
420 logger.info("No line starting with '{}' was replaced in {} ({} were found) since none seems buggy".format(pattern, lheFile, n_found))
421 elif n_added_back !=0:
422 logger.info("{} line(s) starting with '{}' added back in {} using the corresponding line(s) from {}.before_reweighting".format(n_added_back, pattern, lheFile, lheFile))
423
424
426 """! Restore #emscales comment blocks per event from before_reweighting.
427
428 POWHEG XML reweighting omits #emscales comment lines written by POWHEG-BOX-RES.
429 This restores each event's #emscales block exactly before </event>.
430 """
431 before_file = "{}.before_reweighting".format(lheFile)
432 if not os.path.isfile(before_file):
433 return
434
435 emscales_per_event = []
436 current_event_scales = []
437 inside_event = False
438 has_emscales = False
439 with open(before_file, "r") as f_in:
440 for line in f_in:
441 if line.lstrip().startswith("<event"):
442 inside_event = True
443 current_event_scales = []
444 elif line.lstrip().startswith("</event>"):
445 if inside_event:
446 emscales_per_event.append(current_event_scales)
447 inside_event = False
448 elif inside_event and line.lstrip().startswith("#emscales"):
449 has_emscales = True
450 current_event_scales.append(line)
451
452 if not has_emscales or len(emscales_per_event) == 0:
453 return
454
455 backup_file = "{}.emscales_backup".format(lheFile)
456 shutil.move(lheFile, backup_file)
457 n_restored = 0
458 event_idx = 0
459
460 try:
461 with open(lheFile, "w") as f_out, open(backup_file, "r") as f_in:
462 for line in f_in:
463 if line.lstrip().startswith("#emscales"):
464 # Discard any stale or duplicated #emscales lines emitted during POWHEG XML reweighting
465 continue
466 if line.lstrip().startswith("</event>"):
467 if event_idx < len(emscales_per_event):
468 for scale_line in emscales_per_event[event_idx]:
469 f_out.write(scale_line)
470 n_restored += 1
471 event_idx += 1
472 f_out.write(line)
473
474 # Strict validation: prevent silent event desynchronization
475 if event_idx != len(emscales_per_event):
476 raise RuntimeError(
477 "Event count mismatch while restoring #emscales in {}: "
478 "found {} events in reweighted file but {} in {}".format(
479 lheFile, event_idx, len(emscales_per_event), before_file)
480 )
481
482 if os.path.isfile(backup_file):
483 os.remove(backup_file)
484
485 except Exception:
486 # Atomic rollback on any error
487 if os.path.isfile(backup_file):
488 shutil.move(backup_file, lheFile)
489 raise
490
491 logger.info("{} line(s) starting with '#emscales' added back in {} across {} events using {}".format(
492 n_restored, lheFile, event_idx, before_file))
493
Utility class to perform simple operations on files.
STL class.
default_weight_exists_already(powheg_LHE_output)
Returns True if the LHE file contains a weight called "nominal".
restore_emscales_comments(lheFile)
Restore emscales comment blocks per event from before_reweighting.
WeightTuple
Tuple to contain arbitrary grouped weights.
add_single_weight(process, weight, idx_weight, n_total, use_XML)
Add a single additional weight to an LHE file.