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")
18 Helper function to format QCD scale value
19 to conform to the ATLAS weight variation
20 naming scheme if possible.
21 Given a scale factor as a float, it returns
26 - or the float rounded to two digits after the decimal point if it is not equal to one of the above
29 return {0.5 :
'0.5', 1.0 :
'1', 2.0 :
'2'}[factor]
31 return '{0:.2f}'.
format(factor)
36 """! Provides PowhegConfig objects which are user-configurable in the jobOptions.
38 All subprocesses inherit from this class.
40 @author James Robinson <james.robinson@cern.ch>
43 def __init__(self, process_name, run_args=None, run_opts=None):
46 @param run_args Generate_tf run arguments
47 @param run_opts athena run options
62 process_kwargs = {
"cores":
int(os.environ.pop(
"ATHENA_PROC_NUMBER", 1))}
64 logger.warning(
"No run arguments found! Using defaults.")
67 if hasattr(run_args,
"ecmEnergy"):
68 process_kwargs[
"beam_energy"] = 0.5 * run_args.ecmEnergy
69 if hasattr(run_args,
"maxEvents")
and run_args.maxEvents > 0:
70 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
71 process_kwargs[
"nEvents"] =
int(1.1 * run_args.maxEvents + 0.5)
73 process_kwargs[
"nEvents"] = run_args.maxEvents
75 if hasattr(run_args,
"outputEVNTFile")
or hasattr(run_args,
"outputYODAFile"):
76 process_kwargs[
"nEvents"] = 11000
78 process_kwargs[
"nEvents"] = 10000
79 if hasattr(run_args,
"randomSeed"):
80 process_kwargs[
"random_seed"] = run_args.randomSeed
81 if hasattr(run_args,
"outputTXTFile"):
82 for tarball_suffix
in [x
for x
in [
".tar.gz",
".tgz"]
if x
in run_args.outputTXTFile]:
84 self.
__output_LHE_file = run_args.outputTXTFile.split(tarball_suffix)[0] +
".events"
90 self.
process = getattr(processes.powheg, process_name)(os.environ[
"POWHEGPATH"].
replace(
"POWHEG-BOX",
""), **process_kwargs)
93 self.
process.check_using_integration_files()
96 for parameter
in self.
process.parameters:
97 if parameter.is_visible:
98 setattr(self, parameter.name, parameter.value)
101 for external
in self.
process.externals.values():
102 for parameter
in external.parameters:
103 if parameter.is_visible:
104 setattr(self, parameter.name, parameter.value)
113 if self.
process.cores == 1
and self.
process.powheg_version !=
"RES":
117 logger.info(
"Configuring this POWHEG-BOX-RES process to run in multistage mode")
120 logger.info(
"This job is running with an athenaMP-like whole-node setup, requesting {} cores".
format(self.
process.cores))
121 if hasattr(run_opts,
"nprocs"):
122 logger.info(
"Re-configuring to keep athena running serially while parallelising POWHEG-BOX generation.")
125 logger.warning(
"Running in multicore mode but no 'nprocs' option was provided!")
128 list(self.
process.parameters_by_name(
"manyseeds"))[0].value = 1
134 logger.info(
"Configured for event generation with: {}".
format(self.
process.executable))
136 def generate(self, create_run_card_only=False, save_integration_grids=True, use_external_run_card=False, remove_oldStyle_rwt_comments=False):
137 """! Run normal event generation.
139 @param create_run_card_only Only generate the run card.
140 @param save_integration_grids Save the integration grids for future reuse.
141 @param use_external_run_card Use a user-provided Powheg run card (powheg.input).
142 @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).
145 self.
process.use_XML_reweighting =
True
147 self.
process.remove_oldStyle_rwt_comments = remove_oldStyle_rwt_comments
150 if save_integration_grids:
154 if not use_external_run_card:
157 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")
158 if not create_run_card_only:
162 """! Initialise runcard with appropriate options."""
164 if (hasattr(self,
"bornsuppfact")
and self.bornsuppfact > 0.0)
and (hasattr(self,
"bornktmin")
and self.bornktmin <= 0.0):
165 logger.warning(
"These settings: bornsuppfact = {} and bornktmin = {} cannot be used to generate events!".
format(self.bornsuppfact, self.bornktmin))
166 logger.warning(
"Only fixed-order distributions can be produced with these settings!")
170 logger.info(
"Preparing to parallelise: running with {} jobs".
format(self.
process.cores))
174 self.
process.validate_parameters()
177 parameters_unsorted =
list(self.
process.parameters)
178 for external
in self.
process.externals.values():
179 parameters_unsorted.extend(external.parameters)
180 parameters_sorted = [x[1]
for x
in sorted(dict((p.name.lower(), p)
for p
in parameters_unsorted).
items(), key=
lambda x: x[0])]
183 logger.info(
"=========================================================================================================")
184 logger.info(
"| User configurable parameters for this process |")
185 logger.info(
"=========================================================================================================")
186 logger.info(
"| Option name | ATLAS default | Description |")
187 logger.info(
"=========================================================================================================")
188 for parameter
in [p
for p
in parameters_sorted
if p.is_visible]:
189 _default_value =
"default" if (parameter.default_value
is None or parameter.default_value ==
"")
else str(parameter.default_value)
190 logger.info(
"| {:<25} | {:>19} | {}".
format(parameter.name, _default_value, parameter.description))
191 logger.info(
"========================================================================================================")
194 parameters_changed = [p
for p
in parameters_sorted
if p.value
is not p.default_value]
195 logger.info(
"In these jobOptions {} parameter(s) have been changed from their default value:".
format(len(parameters_changed)))
196 for idx, parameter
in enumerate(parameters_changed):
197 logger.info(
" {:<3} {:<19} {:>15} => {}".
format(
"{})".
format(idx + 1),
"{}:".
format(parameter.name),
str(parameter.default_value), parameter.value))
200 event_weight_options = []
204 logger.info(
"Writing POWHEG-BOX runcard to {}".
format(run_card_path))
205 with open(run_card_path,
"w")
as f_runcard:
206 for parameter
in sorted(self.
process.parameters, key=
lambda p: p.keyword.lower()):
207 if parameter.name ==
"bornsuppfact" and parameter.value > 0:
208 event_weight_options.append((
"Born-level suppression",
"magnitude"))
209 if parameter.name ==
"withnegweights" and parameter.value > 0:
210 event_weight_options.append((
"negative event weights",
"sign"))
212 if parameter.name ==
"PDF" and isinstance(parameter.value, collections.Iterable):
214 if len(parameter.value) < 2:
215 logger.error(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
216 raise TypeError(
"Use 'PowhegConfig.PDF = {0}' rather than 'PowhegConfig.PDF = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>"))
218 for PDF
in map(int, parameter.value[1:]):
221 if parameter.name
in [
"mu_F",
"mu_R"]
and isinstance(parameter.value, collections.Iterable):
222 pdfs =
list(self.
process.parameters_by_name(
"PDF"))[0].value
223 nominal_pdf = pdfs
if isinstance(pdfs, int)
or isinstance(pdfs, str)
else pdfs[0]
225 mu_Rs =
list(self.
process.parameters_by_name(
"mu_R"))[0].value
226 mu_Fs =
list(self.
process.parameters_by_name(
"mu_F"))[0].value
227 if len(parameter.value) < 2:
228 logger.error(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
229 raise TypeError(
"Use 'PowhegConfig.{1} = {0}' rather than 'PowhegConfig.{1} = [{0}]'".
format(parameter.value[0]
if len(parameter.value) > 0
else "<value>", parameter.name))
230 if not isinstance(mu_Rs, collections.Iterable)
or not isinstance(mu_Fs, collections.Iterable)
or len(mu_Rs)
is not len(mu_Fs):
231 logger.error(
"Number of mu_R and mu_F variations must be the same.")
232 raise ValueError(
"Number of mu_R and mu_F variations must be the same.")
234 for mu_R, mu_F
in zip(map(float, mu_Rs[1:]), map(float, mu_Fs[1:])):
237 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])
238 f_runcard.write(
"{}\n".
format(parameter))
241 if len(event_weight_options) > 0:
243 logger.warning(
"POWHEG-BOX has been configured to run with {}".
format(
" and ".
join([x[0]
for x
in event_weight_options])))
244 logger.warning(
"This means that event weights will vary in {}.".
format(
" and ".
join([x[1]
for x
in event_weight_options])))
245 logger.warning(
"The cross-section passed to the parton shower will be inaccurate.")
246 logger.warning(
"Please use the cross-section printed in the log file before showering begins.")
249 doReweighting =
False
252 elif len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
253 if self.
process.parameters_by_keyword(
"for_reweighting")[0].value == 1:
254 logger.warning (
"No more than the nominal weight is requested, but for_reweighting is set to 1")
255 logger.warning (
"Therefore, reweighting is enabled anyway, otherwise virtual corrections wouldn't be included")
259 __ordered_event_weight_groups_list = []
260 for __key
in [
"scale_variation",
"PDF_variation"]:
264 __ordered_event_weight_groups_list.append(__item)
267 _n_weights = len(event_weight_group) - 3
270 logger.warning(
"Ignoring weight group '{}' as it does not have any variations defined. Check your jobOptions!".
format(group_name))
274 logger.info(
"Adding new weight group '{}' which contains {} weights defined by varying {} parameters".
format(group_name, _n_weights, len(event_weight_group[
"parameter_names"])))
275 for parameter_name
in event_weight_group[
"parameter_names"]:
276 logger.info(
"... {}".
format(parameter_name))
277 if not self.
process.has_parameter(parameter_name):
278 logger.warning(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
279 raise ValueError(
"Parameter '{}' does not exist for this process!".
format(parameter_name))
283 @
timed(
"Powheg LHE event generation")
285 """! Generate events according to the scheduler."""
288 heartbeat.setName(
"heartbeat thread")
289 heartbeat.daemon =
True
293 logger.info(
"Using executable: {}".
format(self.
process.executable))
296 extra_args = {
"quark colour fixer": [self.
process]}
299 for algorithm, external
in self.
process.externals.items():
300 if external.needs_scheduling(self.
process):
301 self.
scheduler.
add(algorithm, external, *extra_args.get(algorithm, []))
304 for algorithm
in self.
process.algorithms:
305 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
307 if len(
list(self.
process.parameters_by_keyword(
"for_reweighting"))) == 1:
308 if list(self.
process.parameters_by_keyword(
"for_reweighting"))[0].value == 1:
309 algorithm =
"LHE file nominal weight updater"
310 self.
scheduler.
add(algorithm, *extra_args.get(algorithm, []))
311 logger.info (
"Since parameter for_reweighting was set to 1, virtual corrections are added at the reweighting stage only.")
312 logger.info (
"Will run LHE file nominal weight updater so that XWGTUP value is updated with value of reweighted nominal weight.")
330 """! Add a new named group of event weights.
332 @exceptions ValueError Raise a ValueError if reweighting is not supported.
334 @param group_name Name of the group of weights.
335 @param parameters_to_vary Names of the parameters to vary.
336 @param combination_method Method for combining the weights.
338 if not self.
process.is_reweightable:
339 logger.warning(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
340 raise ValueError(
"Additional event weights cannot be added by this process! Remove reweighting lines from the jobOptions.")
344 self.
__event_weight_groups[group_name][
"keywords"] = [[p.keyword
for p
in self.
process.parameters_by_name(parameter)]
for parameter
in parameters_to_vary]
347 """! Add a new event weight to an existing group.
349 @param group_name Name of the group of weights that this weight belongs to.
350 @param weight_name Name of this event weight.
351 @param parameter_values Values of the parameters.
354 raise ValueError(
"Weight group '{}' has not been defined.".
format(group_name))
356 if len(parameter_values)
is not n_expected:
357 raise ValueError(
"Expected {} parameter values but only got {}".
format(n_expected, len(parameter_values)))
359 for parameter_name, value
in zip(self.
__event_weight_groups[group_name][
"parameter_names"], parameter_values):
363 """! Override default attribute setting to stop users setting non-existent attributes.
365 @exceptions AttributeError Raise an AttributeError if the interface is frozen
367 @param key Attribute name.
368 @param value Value to set the attribute to.
371 if hasattr(self,
"process"):
373 for parameter
in self.
process.parameters_by_name(key):
374 parameter.ensure_default()
375 parameter.value = value
377 for external
in self.
process.externals.values():
378 for parameter
in external.parameters_by_name(key):
379 parameter.ensure_default()
380 parameter.value = value
383 if not hasattr(self, key):
384 raise AttributeError(
"This POWHEG-BOX process has no option '{}'".
format(key))
385 object.__setattr__(self, key, value)
388 logger.info(
"Setting parameters for the stages : {0}".
format(parameterStageDict))
390 self.
process.parameterStageDict = parameterStageDict