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
60 process_kwargs = {
"cores":
int(os.environ.pop(
"ATHENA_CORE_NUMBER", 1))}
62 logger.warning(
"No run arguments found! Using defaults.")
65 if hasattr(run_args,
"ecmEnergy"):
66 process_kwargs[
"beam_energy"] = 0.5 * run_args.ecmEnergy
67 if hasattr(run_args,
"maxEvents")
and run_args.maxEvents > 0:
68 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
69 process_kwargs[
"nEvents"] =
int(1.1 * run_args.maxEvents + 0.5)
71 process_kwargs[
"nEvents"] = run_args.maxEvents
73 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
74 process_kwargs[
"nEvents"] = 11000
76 process_kwargs[
"nEvents"] = 10000
77 if hasattr(run_args,
"randomSeed"):
78 process_kwargs[
"random_seed"] = run_args.randomSeed
79 if hasattr(run_args,
"outputTXTFile"):
80 for tarball_suffix
in [x
for x
in [
".tar.gz",
".tgz"]
if x
in run_args.outputTXTFile]:
82 self.
__output_LHE_file = run_args.outputTXTFile.split(tarball_suffix)[0] +
".events"
88 self.
process = getattr(processes.powheg, process_name)(os.environ[
"POWHEGPATH"].
replace(
"POWHEG-BOX",
""), **process_kwargs)
91 self.
process.check_using_integration_files()
94 for parameter
in self.
process.parameters:
95 if parameter.is_visible:
96 setattr(self, parameter.name, parameter.value)
99 for external
in self.
process.externals.values():
100 for parameter
in external.parameters:
101 if parameter.is_visible:
102 setattr(self, parameter.name, parameter.value)
111 if self.
process.cores == 1
and self.
process.powheg_version !=
"RES":
115 logger.info(
"Configuring this POWHEG-BOX-RES process to run in multistage mode")
118 logger.info(
"This job is running with an athenaMP-like whole-node setup, requesting {} cores".
format(self.
process.cores))
119 if hasattr(run_opts,
"nprocs"):
120 logger.info(
"Re-configuring to keep athena running serially while parallelising POWHEG-BOX generation.")
123 logger.warning(
"Running in multicore mode but no 'nprocs' option was provided!")
126 list(self.
process.parameters_by_name(
"manyseeds"))[0].value = 1
132 logger.info(
"Configured for event generation with: {}".
format(self.
process.executable))
134 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):
135 """! Run normal event generation.
137 @param create_run_card_only Only generate the run card.
138 @param save_integration_grids Save the integration grids for future reuse.
139 @param use_external_run_card Use a user-provided Powheg run card (powheg.input).
140 @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).
143 self.
process.use_XML_reweighting =
True
145 self.
process.remove_oldStyle_rwt_comments = remove_oldStyle_rwt_comments
148 if save_integration_grids:
152 if not use_external_run_card:
155 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")
156 if not create_run_card_only:
160 """! Initialise runcard with appropriate options."""
162 if (hasattr(self,
"bornsuppfact")
and self.bornsuppfact > 0.0)
and (hasattr(self,
"bornktmin")
and self.bornktmin <= 0.0):
163 logger.warning(
"These settings: bornsuppfact = {} and bornktmin = {} cannot be used to generate events!".
format(self.bornsuppfact, self.bornktmin))
164 logger.warning(
"Only fixed-order distributions can be produced with these settings!")
168 logger.info(
"Preparing to parallelise: running with {} jobs".
format(self.
process.cores))
172 self.
process.validate_parameters()
175 parameters_unsorted =
list(self.
process.parameters)
176 for external
in self.
process.externals.values():
177 parameters_unsorted.extend(external.parameters)
178 parameters_sorted = [x[1]
for x
in sorted(dict((p.name.lower(), p)
for p
in parameters_unsorted).
items(), key=
lambda x: x[0])]
181 logger.info(
"=========================================================================================================")
182 logger.info(
"| User configurable parameters for this process |")
183 logger.info(
"=========================================================================================================")
184 logger.info(
"| Option name | ATLAS default | Description |")
185 logger.info(
"=========================================================================================================")
186 for parameter
in [p
for p
in parameters_sorted
if p.is_visible]:
187 _default_value =
"default" if (parameter.default_value
is None or parameter.default_value ==
"")
else str(parameter.default_value)
188 logger.info(
"| {:<25} | {:>19} | {}".
format(parameter.name, _default_value, parameter.description))
189 logger.info(
"========================================================================================================")
192 parameters_changed = [p
for p
in parameters_sorted
if p.value
is not p.default_value]
193 logger.info(
"In these jobOptions {} parameter(s) have been changed from their default value:".
format(len(parameters_changed)))
194 for idx, parameter
in enumerate(parameters_changed):
195 logger.info(
" {:<3} {:<19} {:>15} => {}".
format(
"{})".
format(idx + 1),
"{}:".
format(parameter.name),
str(parameter.default_value), parameter.value))
198 event_weight_options = []
202 logger.info(
"Writing POWHEG-BOX runcard to {}".
format(run_card_path))
203 with open(run_card_path,
"w")
as f_runcard:
204 for parameter
in sorted(self.
process.parameters, key=
lambda p: p.keyword.lower()):
205 if parameter.name ==
"bornsuppfact" and parameter.value > 0:
206 event_weight_options.append((
"Born-level suppression",
"magnitude"))
207 if parameter.name ==
"withnegweights" and parameter.value > 0:
208 event_weight_options.append((
"negative event weights",
"sign"))
210 if parameter.name ==
"PDF" and isinstance(parameter.value, collections.abc.Iterable):
212 if len(parameter.value) < 2:
213 logger.error(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
214 raise TypeError(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
216 for PDF
in map(int, parameter.value[1:]):
219 if parameter.name
in [
"mu_F",
"mu_R"]
and isinstance(parameter.value, collections.abc.Iterable):
220 pdfs =
list(self.
process.parameters_by_name(
"PDF"))[0].value
221 nominal_pdf = pdfs
if isinstance(pdfs, int)
or isinstance(pdfs, str)
else pdfs[0]
223 mu_Rs =
list(self.
process.parameters_by_name(
"mu_R"))[0].value
224 mu_Fs =
list(self.
process.parameters_by_name(
"mu_F"))[0].value
225 if len(parameter.value) < 2:
226 logger.error(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
227 raise TypeError(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
228 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):
229 logger.error(
"Number of mu_R and mu_F variations must be the same.")
230 raise ValueError(
"Number of mu_R and mu_F variations must be the same.")
232 for mu_R, mu_F
in zip(map(float, mu_Rs[1:]), map(float, mu_Fs[1:])):
235 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])
236 f_runcard.write(
"{}\n".
format(parameter))
239 if len(event_weight_options) > 0:
241 logger.warning(
"POWHEG-BOX has been configured to run with {}".
format(
" and ".
join([x[0]
for x
in event_weight_options])))
242 logger.warning(
"This means that event weights will vary in {}.".
format(
" and ".
join([x[1]
for x
in event_weight_options])))
243 logger.warning(
"The cross-section passed to the parton shower will be inaccurate.")
244 logger.warning(
"Please use the cross-section printed in the log file before showering begins.")
247 doReweighting =
False
250 elif len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
251 if self.
process.parameters_by_keyword(
"for_reweighting")[0].value == 1:
252 logger.warning (
"No more than the nominal weight is requested, but for_reweighting is set to 1")
253 logger.warning (
"Therefore, reweighting is enabled anyway, otherwise virtual corrections wouldn't be included")
257 __ordered_event_weight_groups_list = []
258 for __key
in [
"scale_variation",
"PDF_variation"]:
262 __ordered_event_weight_groups_list.append(__item)
265 _n_weights = len(event_weight_group) - 3
268 logger.warning(
"Ignoring weight group '{}' as it does not have any variations defined. Check your jobOptions!".
format(group_name))
272 logger.info(
"Adding new weight group '{}' which contains {} weights defined by varying {} parameters".
format(group_name, _n_weights, len(event_weight_group[
"parameter_names"])))
273 for parameter_name
in event_weight_group[
"parameter_names"]:
274 logger.info(
"... {}".
format(parameter_name))
275 if not self.
process.has_parameter(parameter_name):
276 logger.warning(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
277 raise ValueError(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
281 @
timed(
"Powheg LHE event generation")
283 """! Generate events according to the scheduler."""
286 heartbeat.setName(
"heartbeat thread")
287 heartbeat.daemon =
True
291 logger.info(
"Using executable: {}".
format(self.
process.executable))
294 extra_args = {
"quark colour fixer": [self.
process]}
297 for algorithm, external
in self.
process.externals.items():
298 if external.needs_scheduling(self.
process):
299 self.
scheduler.
add(algorithm, external, *extra_args.get(algorithm, []))
302 for algorithm
in self.
process.algorithms:
303 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
305 updated_xwgtup =
False
306 if len(
list(self.
process.parameters_by_keyword(
"ubexcess_correct"))) == 1:
307 if list(self.
process.parameters_by_keyword(
"ubexcess_correct"))[0].value == 1:
308 algorithm =
"LHE ubexcess_correct weight updater"
309 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
310 algorithm =
"LHE file nominal weight updater"
311 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
312 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.")
313 logger.info (
"Will also run LHE file nominal weight updater so that XWGTUP value is updated with value of reweighted nominal weight.")
314 updated_xwgtup =
True
315 if not updated_xwgtup:
316 if len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
317 if list(self.
process.parameters_by_keyword(
"for_reweighting"))[0].value == 1:
318 algorithm =
"LHE file nominal weight updater"
319 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
320 logger.info (
"Since parameter for_reweighting was set to 1, virtual corrections are added at the reweighting stage only.")
321 logger.info (
"Will run LHE file nominal weight updater so that XWGTUP value is updated with value of reweighted nominal weight.")
327 if not is_bb4l_semilep:
340 """! Add a new named group of event weights.
342 @exceptions ValueError Raise a ValueError if reweighting is not supported.
344 @param group_name Name of the group of weights.
345 @param parameters_to_vary Names of the parameters to vary.
346 @param combination_method Method for combining the weights.
348 if not self.
process.is_reweightable:
349 logger.warning(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
350 raise ValueError(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
354 self.
__event_weight_groups[group_name][
"keywords"] = [[p.keyword
for p
in self.
process.parameters_by_name(parameter)]
for parameter
in parameters_to_vary]
357 """! Add a new event weight to an existing group.
359 @param group_name Name of the group of weights that this weight belongs to.
360 @param weight_name Name of this event weight.
361 @param parameter_values Values of the parameters.
364 raise ValueError(
"Weight group '{}' has not been defined.".
format(group_name))
366 if len(parameter_values)
is not n_expected:
367 raise ValueError(
"Expected {} parameter values but only got {}".
format(n_expected, len(parameter_values)))
369 for parameter_name, value
in zip(self.
__event_weight_groups[group_name][
"parameter_names"], parameter_values):
373 """! Override default attribute setting to stop users setting non-existent attributes.
375 @exceptions AttributeError Raise an AttributeError if the interface is frozen
377 @param key Attribute name.
378 @param value Value to set the attribute to.
381 if hasattr(self,
"process"):
383 for parameter
in self.
process.parameters_by_name(key):
384 parameter.ensure_default()
385 parameter.value = value
387 for external
in self.
process.externals.values():
388 for parameter
in external.parameters_by_name(key):
389 parameter.ensure_default()
390 parameter.value = value
393 if not hasattr(self, key):
394 raise AttributeError(
"This POWHEG-BOX process has no option '{}'".
format(key))
395 object.__setattr__(self, key, value)
398 logger.info(
"Setting parameters for the stages : {0}".
format(parameterStageDict))
400 self.
process.parameterStageDict = parameterStageDict