5 from .
import processes
6 from AthenaCommon
import Logging
7 from .decorators
import timed
8 from .algorithms
import Scheduler
9 from .utility
import HeartbeatTimer
12 logger = Logging.logging.getLogger(
"PowhegControl")
16 Helper function to format QCD scale value
17 to conform to the ATLAS weight variation
18 naming scheme if possible.
19 Given a scale factor as a float, it returns
24 - or the float rounded to two digits after the decimal point if it is not equal to one of the above
27 return {0.5 :
'0.5', 1.0 :
'1', 2.0 :
'2'}[factor]
29 return '{0:.2f}'.
format(factor)
34 """! Provides PowhegConfig objects which are user-configurable in the jobOptions.
36 All subprocesses inherit from this class.
38 @author James Robinson <james.robinson@cern.ch>
41 def __init__(self, process_name, run_args=None, run_opts=None):
44 @param run_args Generate_tf run arguments
45 @param run_opts athena run options
63 process_kwargs = {
"cores":
int(os.environ.pop(
"ATHENA_CORE_NUMBER", 1))}
65 logger.warning(
"No run arguments found! Using defaults.")
68 if hasattr(run_args,
"ecmEnergy"):
69 process_kwargs[
"beam_energy"] = 0.5 * run_args.ecmEnergy
70 if hasattr(run_args,
"maxEvents")
and run_args.maxEvents > 0:
71 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
72 process_kwargs[
"nEvents"] =
int(1.1 * run_args.maxEvents + 0.5)
74 process_kwargs[
"nEvents"] = run_args.maxEvents
76 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
77 process_kwargs[
"nEvents"] = 11000
79 process_kwargs[
"nEvents"] = 10000
80 if hasattr(run_args,
"randomSeed"):
81 process_kwargs[
"random_seed"] = run_args.randomSeed
82 if hasattr(run_args,
"outputTXTFile"):
83 for tarball_suffix
in [x
for x
in [
".tar.gz",
".tgz"]
if x
in run_args.outputTXTFile]:
85 self.
__output_LHE_file = run_args.outputTXTFile.split(tarball_suffix)[0] +
".events"
91 self.
process = getattr(processes.powheg, process_name)(os.environ[
"POWHEGPATH"].
replace(
"POWHEG-BOX",
""), **process_kwargs)
94 self.
process.check_using_integration_files()
97 for parameter
in self.
process.parameters:
98 if parameter.is_visible:
99 setattr(self, parameter.name, parameter.value)
102 for external
in self.
process.externals.values():
103 for parameter
in external.parameters:
104 if parameter.is_visible:
105 setattr(self, parameter.name, parameter.value)
114 if self.
process.cores == 1
and self.
process.powheg_version !=
"RES":
118 logger.info(
"Configuring this POWHEG-BOX-RES process to run in multistage mode")
121 logger.info(
"This job is running with an athenaMP-like whole-node setup, requesting {} cores".
format(self.
process.cores))
122 if hasattr(run_opts,
"nprocs"):
123 logger.info(
"Re-configuring to keep athena running serially while parallelising POWHEG-BOX generation.")
126 logger.warning(
"Running in multicore mode but no 'nprocs' option was provided!")
129 list(self.
process.parameters_by_name(
"manyseeds"))[0].value = 1
135 logger.info(
"Configured for event generation with: {}".
format(self.
process.executable))
137 def generate(self, create_run_card_only=False, save_integration_grids=True, use_external_run_card=False, remove_oldStyle_rwt_comments=False, is_bb4l_semilep=False):
138 """! Run normal event generation.
140 @param create_run_card_only Only generate the run card.
141 @param save_integration_grids Save the integration grids for future reuse.
142 @param use_external_run_card Use a user-provided Powheg run card (powheg.input).
143 @param remove_oldStyle_rwt_comments Removes old-style '#rwgt', '#pdf', '#new weight', '#matching', and ' #Random' comments in lhe files (kept by default despite using xml reweighting).
146 self.
process.use_XML_reweighting =
True
148 self.
process.remove_oldStyle_rwt_comments = remove_oldStyle_rwt_comments
151 if save_integration_grids:
155 if not use_external_run_card:
158 logger.warning(
"Using native Powheg run card (must be located at './powheg.input' in order for Powheg to find it!) to configure event generation, instead of PowhegControl configuration interface")
159 if not create_run_card_only:
163 """! Initialise runcard with appropriate options."""
165 if (hasattr(self,
"bornsuppfact")
and self.bornsuppfact > 0.0)
and (hasattr(self,
"bornktmin")
and self.bornktmin <= 0.0):
166 logger.warning(
"These settings: bornsuppfact = {} and bornktmin = {} cannot be used to generate events!".
format(self.bornsuppfact, self.bornktmin))
167 logger.warning(
"Only fixed-order distributions can be produced with these settings!")
171 logger.info(
"Preparing to parallelise: running with {} jobs".
format(self.
process.cores))
175 self.
process.validate_parameters()
178 parameters_unsorted =
list(self.
process.parameters)
179 for external
in self.
process.externals.values():
180 parameters_unsorted.extend(external.parameters)
181 parameters_sorted = [x[1]
for x
in sorted(dict((p.name.lower(), p)
for p
in parameters_unsorted).
items(), key=
lambda x: x[0])]
184 logger.info(
"=========================================================================================================")
185 logger.info(
"| User configurable parameters for this process |")
186 logger.info(
"=========================================================================================================")
187 logger.info(
"| Option name | ATLAS default | Description |")
188 logger.info(
"=========================================================================================================")
189 for parameter
in [p
for p
in parameters_sorted
if p.is_visible]:
190 _default_value =
"default" if (parameter.default_value
is None or parameter.default_value ==
"")
else str(parameter.default_value)
191 logger.info(
"| {:<25} | {:>19} | {}".
format(parameter.name, _default_value, parameter.description))
192 logger.info(
"========================================================================================================")
195 parameters_changed = [p
for p
in parameters_sorted
if p.value
is not p.default_value]
196 logger.info(
"In these jobOptions {} parameter(s) have been changed from their default value:".
format(len(parameters_changed)))
197 for idx, parameter
in enumerate(parameters_changed):
198 logger.info(
" {:<3} {:<19} {:>15} => {}".
format(
"{})".
format(idx + 1),
"{}:".
format(parameter.name),
str(parameter.default_value), parameter.value))
201 event_weight_options = []
205 logger.info(
"Writing POWHEG-BOX runcard to {}".
format(run_card_path))
206 with open(run_card_path,
"w")
as f_runcard:
207 for parameter
in sorted(self.
process.parameters, key=
lambda p: p.keyword.lower()):
208 if parameter.name ==
"bornsuppfact" and parameter.value > 0:
209 event_weight_options.append((
"Born-level suppression",
"magnitude"))
210 if parameter.name ==
"withnegweights" and parameter.value > 0:
211 event_weight_options.append((
"negative event weights",
"sign"))
213 if parameter.name ==
"PDF" and isinstance(parameter.value, collections.abc.Iterable):
215 if len(parameter.value) < 2:
216 logger.error(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
217 raise TypeError(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
219 for PDF
in map(int, parameter.value[1:]):
222 if parameter.name
in [
"mu_F",
"mu_R"]
and isinstance(parameter.value, collections.abc.Iterable):
223 pdfs =
list(self.
process.parameters_by_name(
"PDF"))[0].value
224 nominal_pdf = pdfs
if isinstance(pdfs, int)
or isinstance(pdfs, str)
else pdfs[0]
226 mu_Rs =
list(self.
process.parameters_by_name(
"mu_R"))[0].value
227 mu_Fs =
list(self.
process.parameters_by_name(
"mu_F"))[0].value
228 if len(parameter.value) < 2:
229 logger.error(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
230 raise TypeError(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
231 if not isinstance(mu_Rs, collections.abc.Iterable)
or not isinstance(mu_Fs, collections.abc.Iterable)
or len(mu_Rs)
is not len(mu_Fs):
232 logger.error(
"Number of mu_R and mu_F variations must be the same.")
233 raise ValueError(
"Number of mu_R and mu_F variations must be the same.")
235 for mu_R, mu_F
in zip(map(float, mu_Rs[1:]), map(float, mu_Fs[1:])):
238 self.
add_weight_to_group(
"scale_variation",
"MUR{mur}_MUF{muf}_PDF{nominal_pdf}".
format(mur=mu_R_text, muf=mu_F_text, nominal_pdf=nominal_pdf), [mu_R, mu_F])
239 f_runcard.write(
"{}\n".
format(parameter))
242 if len(event_weight_options) > 0:
244 logger.warning(
"POWHEG-BOX has been configured to run with {}".
format(
" and ".
join([x[0]
for x
in event_weight_options])))
245 logger.warning(
"This means that event weights will vary in {}.".
format(
" and ".
join([x[1]
for x
in event_weight_options])))
246 logger.warning(
"The cross-section passed to the parton shower will be inaccurate.")
247 logger.warning(
"Please use the cross-section printed in the log file before showering begins.")
250 doReweighting =
False
253 elif len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
254 if self.
process.parameters_by_keyword(
"for_reweighting")[0].value == 1:
255 logger.warning (
"No more than the nominal weight is requested, but for_reweighting is set to 1")
256 logger.warning (
"Therefore, reweighting is enabled anyway, otherwise virtual corrections wouldn't be included")
260 __ordered_event_weight_groups_list = []
261 for __key
in [
"scale_variation",
"PDF_variation"]:
265 __ordered_event_weight_groups_list.append(__item)
268 _n_weights = len(event_weight_group) - 3
271 logger.warning(
"Ignoring weight group '{}' as it does not have any variations defined. Check your jobOptions!".
format(group_name))
275 logger.info(
"Adding new weight group '{}' which contains {} weights defined by varying {} parameters".
format(group_name, _n_weights, len(event_weight_group[
"parameter_names"])))
276 for parameter_name
in event_weight_group[
"parameter_names"]:
277 logger.info(
"... {}".
format(parameter_name))
278 if not self.
process.has_parameter(parameter_name):
279 logger.warning(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
280 raise ValueError(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
284 @
timed(
"Powheg LHE event generation")
286 """! Generate events according to the scheduler."""
289 heartbeat.setName(
"heartbeat thread")
290 heartbeat.daemon =
True
294 logger.info(
"Using executable: {}".
format(self.
process.executable))
297 extra_args = {
"quark colour fixer": [self.
process]}
300 for algorithm, external
in self.
process.externals.items():
301 if external.needs_scheduling(self.
process):
302 self.
scheduler.
add(algorithm, external, *extra_args.get(algorithm, []))
305 for algorithm
in self.
process.algorithms:
306 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
308 updated_xwgtup =
False
309 if len(
list(self.
process.parameters_by_keyword(
"ubexcess_correct"))) == 1:
310 if list(self.
process.parameters_by_keyword(
"ubexcess_correct"))[0].value == 1:
311 algorithm =
"LHE ubexcess_correct weight updater"
312 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
313 algorithm =
"LHE file nominal weight updater"
314 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
315 logger.info (
"Since parameter ubexcess_correct was set to 1, event weights need to be modified by correction factor which is calculated during event generation.")
316 logger.info (
"Will also run LHE file nominal weight updater so that XWGTUP value is updated with value of reweighted nominal weight.")
317 updated_xwgtup =
True
318 if not updated_xwgtup:
319 if len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
320 if list(self.
process.parameters_by_keyword(
"for_reweighting"))[0].value == 1:
321 algorithm =
"LHE file nominal weight updater"
322 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
323 logger.info (
"Since parameter for_reweighting was set to 1, virtual corrections are added at the reweighting stage only.")
324 logger.info (
"Will run LHE file nominal weight updater so that XWGTUP value is updated with value of reweighted nominal weight.")
330 if not is_bb4l_semilep:
343 """! Add a new named group of event weights.
345 @exceptions ValueError Raise a ValueError if reweighting is not supported.
347 @param group_name Name of the group of weights.
348 @param parameters_to_vary Names of the parameters to vary.
349 @param combination_method Method for combining the weights.
351 if not self.
process.is_reweightable:
352 logger.warning(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
353 raise ValueError(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
357 self.
__event_weight_groups[group_name][
"keywords"] = [[p.keyword
for p
in self.
process.parameters_by_name(parameter)]
for parameter
in parameters_to_vary]
360 """! Add a new event weight to an existing group.
362 @param group_name Name of the group of weights that this weight belongs to.
363 @param weight_name Name of this event weight.
364 @param parameter_values Values of the parameters.
367 raise ValueError(
"Weight group '{}' has not been defined.".
format(group_name))
369 if len(parameter_values)
is not n_expected:
370 raise ValueError(
"Expected {} parameter values but only got {}".
format(n_expected, len(parameter_values)))
372 for parameter_name, value
in zip(self.
__event_weight_groups[group_name][
"parameter_names"], parameter_values):
376 """! Override default attribute setting to stop users setting non-existent attributes.
378 @exceptions AttributeError Raise an AttributeError if the interface is frozen
380 @param key Attribute name.
381 @param value Value to set the attribute to.
384 if hasattr(self,
"process"):
386 for parameter
in self.
process.parameters_by_name(key):
387 parameter.ensure_default()
388 parameter.value = value
390 for external
in self.
process.externals.values():
391 for parameter
in external.parameters_by_name(key):
392 parameter.ensure_default()
393 parameter.value = value
396 if not hasattr(self, key):
397 raise AttributeError(
"This POWHEG-BOX process has no option '{}'".
format(key))
398 object.__setattr__(self, key, value)
401 logger.info(
"Setting parameters for the stages : {0}".
format(parameterStageDict))
403 self.
process.parameterStageDict = parameterStageDict