Merge lp:~maddevelopers/mg5amcnlo/controlswitch into lp:~mg5core1/mg5amcnlo/2.5.6

Proposed by Olivier Mattelaer
Status: Superseded
Proposed branch: lp:~maddevelopers/mg5amcnlo/controlswitch
Merge into: lp:~mg5core1/mg5amcnlo/2.5.6
Diff against target: 1767 lines (+1113/-488)
3 files modified
madgraph/interface/amcatnlo_run_interface.py (+295/-186)
madgraph/interface/extended_cmd.py (+481/-27)
madgraph/interface/madevent_interface.py (+337/-275)
To merge this branch: bzr merge lp:~maddevelopers/mg5amcnlo/controlswitch
Reviewer Review Type Date Requested Status
Rikkert Frederix Pending
marco zaro Pending
Valentin Hirschi Pending
Review via email: mp+329031@code.launchpad.net

This proposal has been superseded by a proposal from 2017-08-17.

Description of the change

Hi,

This branch refactor the code on how to ask to the user which code to run.
The idea is to have dedicated class for that to ease the possibility to edit such crucial
part of the interface via plugin method.
For that reason, this code use highly modular method and factorise the different step in a quite deep way. This makes the new code much longer than the equivalent one that it replaces.

For the user point of view the change are actually quite minimal:
1) Change the description to fit on 80 char shell.
2) Change question presentation @NLO to match the one already present @LO
3) New method for the gestion of conflict:
   - The previous user value is not overwritten directly anymore
   - The conflicted value is now set in red and the "replacement" value is set after an arrow
   - This allows to allow to use the cycle trough options without creating annoying side effects
4) The cycle trough option is now available for all options
5) the user can now set multiple switch with a single line (need to be separated by ";")

This should not be part of 2.5.6/2.6.0 but rather part of 2.6.1, at the same time,
I would like to have it merged in 2.6.1 as soon as possible such that we have plenty of time to discover potential side effects/error in the conflict handling.

To post a comment you must log in.
324. By olivier-mattelaer

remove print statement

Revision history for this message
Rikkert Frederix (frederix) wrote :

Hi Olivier,

Shouldn't the merge target be something different than 2.5.6?

Cheers,
Rik

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Hi Rik,

Yes, but since that branch was not yet released, this was the only branch available in order to have a nice diff. As said in my comment, this was not intended to be merged in 2.5.6.

As soon as the branch 2.6.1 will be pushed, I will change the target.

Cheers,

Olivier

325. By olivier-mattelaer

improve question displayed -- adapt to the screen size--

326. By olivier-mattelaer

improve the display of the conflict with striketrough instead of yellow color

327. By olivier-mattelaer

longer striketrough to increase visibility

328. By olivier-mattelaer

fix problem when some program are not installed + allowed 1=PY8

329. By olivier-mattelaer

fix some displayed problem pointed by Stefano when some program are not available

330. By olivier-mattelaer

fix problem with script and command not accepted due to the missing;

331. By olivier-mattelaer

first change related to Valentin review

332. By olivier-mattelaer

allow to choose the shower before the edition of the card

333. By olivier-mattelaer

improvment associated to the PS choice

334. By olivier-mattelaer

improve the handling of scripting method.

335. By olivier-mattelaer

merge with latest 2.6.1

336. By olivier-mattelaer

fixing problem revealed by test suite

337. By olivier-mattelaer

merge with latest 2.6.1

Unmerged revisions

337. By olivier-mattelaer

merge with latest 2.6.1

336. By olivier-mattelaer

fixing problem revealed by test suite

335. By olivier-mattelaer

merge with latest 2.6.1

334. By olivier-mattelaer

improve the handling of scripting method.

333. By olivier-mattelaer

improvment associated to the PS choice

332. By olivier-mattelaer

allow to choose the shower before the edition of the card

331. By olivier-mattelaer

first change related to Valentin review

330. By olivier-mattelaer

fix problem with script and command not accepted due to the missing;

329. By olivier-mattelaer

fix some displayed problem pointed by Stefano when some program are not available

328. By olivier-mattelaer

fix problem when some program are not installed + allowed 1=PY8

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'madgraph/interface/amcatnlo_run_interface.py'
2--- madgraph/interface/amcatnlo_run_interface.py 2017-08-10 13:09:28 +0000
3+++ madgraph/interface/amcatnlo_run_interface.py 2017-08-15 12:03:07 +0000
4@@ -907,6 +907,274 @@
5 class aMCatNLOAlreadyRunning(InvalidCmd):
6 pass
7
8+class AskRunNLO(cmd.ControlSwitch):
9+
10+ to_control = [('order', 'Type of perturbative computation'),
11+ ('fixed_order', 'No MC@[N]LO matching / event generation'),
12+ ('shower', 'Shower the generated events'),
13+ ('madspin', 'Decay onshell particles'),
14+ ('reweight', 'Add weights to events for new hypp.'),
15+ ('madanalysis','Run MadAnalysis5 on the events generated')]
16+
17+ quit_on = cmd.ControlSwitch.quit_on + ['onlyshower']
18+
19+ def __init__(self, question, line_args=[], mode=None, force=False,
20+ *args, **opt):
21+
22+ self.check_available_module(opt['mother_interface'].options)
23+ self.me_dir = opt['mother_interface'].me_dir
24+ self.last_mode = opt['mother_interface'].last_mode
25+ misc.sprint(self.last_mode)
26+ self.proc_characteristics = opt['mother_interface'].proc_characteristics
27+ super(AskRunNLO,self).__init__(self.to_control, opt['mother_interface'],
28+ *args, **opt)
29+
30+ def check_available_module(self, options):
31+
32+ self.available_module = set()
33+ if options['madanalysis5_path']:
34+ self.available_module.add('MA5')
35+ if not aMCatNLO or ('mg5_path' in options and options['mg5_path']):
36+ self.available_module.add('MadSpin')
37+ if misc.has_f2py() or self.mother_interface.options['f2py_compiler']:
38+ self.available_module.add('reweight')
39+#
40+# shorcut
41+#
42+ def ans_lo(self, value):
43+
44+ if value is None:
45+ self.switch['order'] = 'LO'
46+ self.switch['fixed_order'] = 'ON'
47+ self.set_switch('shower', 'OFF')
48+ else:
49+ logger.warning('Invalid command: lo=%s' % value)
50+
51+ def ans_nlo(self, value):
52+ if value is None:
53+ self.switch['order'] = 'NLO'
54+ self.switch['fixed_order'] = 'ON'
55+ self.set_switch('shower', 'OFF')
56+ else:
57+ logger.warning('Invalid command: nlo=%s' % value)
58+
59+ def ans_amc__at__nlo(self, value):
60+ if value is None:
61+ self.switch['order'] = 'NLO'
62+ self.switch['fixed_order'] = 'OFF'
63+ self.set_switch('shower', 'ON')
64+ else:
65+ logger.warning('Invalid command: aMC@NLO=%s' % value)
66+
67+ def ans_amc__at__lo(self, value):
68+ if value is None:
69+ self.switch['order'] = 'LO'
70+ self.switch['fixed_order'] = 'OFF'
71+ self.set_switch('shower', 'ON')
72+ else:
73+ logger.warning('Invalid command: aMC@LO=%s' % value)
74+
75+ def ans_noshower(self, value):
76+ if value is None:
77+ self.switch['order'] = 'NLO'
78+ self.switch['fixed_order'] = 'OFF'
79+ self.set_switch('shower', 'OFF')
80+ else:
81+ logger.warning('Invalid command: noshower=%s' % value)
82+
83+ def ans_onlyshower(self, value):
84+ if value is None:
85+ self.switch['mode'] = 'onlyshower'
86+ self.switch['madspin'] = 'OFF'
87+ self.switch['reweight'] = 'OFF'
88+ else:
89+ logger.warning('Invalid command: onlyshower=%s' % value)
90+
91+ def ans_noshowerlo(self, value):
92+ if value is None:
93+ self.switch['order'] = 'LO'
94+ self.switch['fixed_order'] = 'OFF'
95+ self.set_switch('shower', 'OFF')
96+ else:
97+ logger.warning('Invalid command: noshowerlo=%s' % value)
98+
99+ def ans_madanalysis5(self, value):
100+ """ shortcut madanalysis5 -> madanalysis """
101+
102+ if value is None:
103+ return self.onecmd('madanalysis')
104+ else:
105+ self.set_switch('madanalysis', value)
106+#
107+# ORDER
108+#
109+ def get_allowed_order(self):
110+ return ["LO", "NLO"]
111+
112+ def set_default_order(self):
113+
114+ if self.last_mode in ['LO', 'aMC@L0', 'noshowerLO']:
115+ self.switch['order'] = 'LO'
116+ self.switch['order'] = 'NLO'
117+
118+ def set_switch_off_order(self):
119+ return
120+#
121+# Fix order
122+#
123+ def get_allowed_fixed_order(self):
124+ """ """
125+ if self.proc_characteristics['ninitial'] == 1:
126+ return ['ON']
127+ else:
128+ return ['ON', 'OFF']
129+
130+ def set_default_fixed_order(self):
131+
132+ if self.last_mode in ['LO', 'NLO']:
133+ self.switch['fixed_order'] = 'ON'
134+ self.switch['fixed_order'] = 'OFF'
135+
136+ def color_for_fixed_order(self, switch_value):
137+
138+ if switch_value in ['OFF']:
139+ return self.green % switch_value
140+ else:
141+ return self.red % switch_value
142+
143+ def consistency_fixed_order_shower(self, vfix, vshower):
144+ """ consistency_XX_YY(val_XX, val_YY)
145+ -> XX is the new key set by the user to a new value val_XX
146+ -> YY is another key set by the user.
147+ -> return value should be None or "replace_YY"
148+ """
149+
150+ if vfix == 'ON' and vshower != 'OFF' :
151+ return 'OFF'
152+ return None
153+
154+ consistency_fixed_order_madspin = consistency_fixed_order_shower
155+ consistency_fixed_order_reweight = consistency_fixed_order_shower
156+
157+ def consistency_fixed_order_madanalysis(self, vfix, vma5):
158+
159+ if vfix == 'ON' and vma5 == 'ON' :
160+ return 'OFF'
161+ return None
162+
163+
164+ def consistency_shower_fixed_order(self, vshower, vfix):
165+ """ consistency_XX_YY(val_XX, val_YY)
166+ -> XX is the new key set by the user to a new value val_XX
167+ -> YY is another key set by the user.
168+ -> return value should be None or "replace_YY"
169+ """
170+
171+ if vshower == 'ON' and vfix == 'ON':
172+ return 'OFF'
173+ return None
174+
175+ consistency_madspin_fixed_order = consistency_shower_fixed_order
176+ consistency_reweight_fixed_order = consistency_shower_fixed_order
177+ consistency_madanalysis_fixed_order = consistency_shower_fixed_order
178+
179+#
180+# Shower
181+#
182+ def get_allowed_shower(self):
183+ """ """
184+ if self.proc_characteristics['ninitial'] == 1:
185+ return ['OFF']
186+ else:
187+ return ['ON', 'OFF']
188+
189+ def set_default_shower(self):
190+
191+ if self.last_mode in ['LO', 'NLO', 'noshower', 'noshowerLO']:
192+ self.switch['shower'] = 'OFF'
193+ return
194+
195+ if os.path.exists(pjoin(self.me_dir, 'Cards', 'shower_card.dat')):
196+ self.switch['shower'] = 'ON'
197+ self.switch['fixed_order'] = "OFF"
198+ else:
199+ self.switch['shower'] = 'OFF'
200+
201+ def consistency_shower_madanalysis(self, vshower, vma5):
202+ """ MA5 only possible with (N)LO+PS if shower is run"""
203+
204+ if vshower == 'OFF' and vma5 == 'ON':
205+ return 'OFF'
206+ return None
207+
208+ def consistency_madanalysis_sower(self, vma5, vshower):
209+
210+ if vma5=='ON' and vshower =='OFF':
211+ return 'ON'
212+ return None
213+
214+#
215+# madspin
216+#
217+ def get_allowed_madspin(self):
218+ """ """
219+ if self.proc_characteristics['ninitial'] == 1:
220+ return ['OFF']
221+ else:
222+ return ['ON', 'OFF']
223+
224+ def set_default_madspin(self):
225+
226+ if 'MadSpin' in self.available_module:
227+ if os.path.exists(pjoin(self.me_dir,'Cards','madspin_card.dat')):
228+ self.switch['madspin'] = 'ON'
229+ else:
230+ self.switch['madspin'] = 'OFF'
231+ else:
232+ self.switch['madspin'] = 'Not Avail.'
233+
234+
235+#
236+# reweight
237+#
238+ get_allowed_reweight = get_allowed_madspin
239+
240+ def set_default_reweight(self):
241+ """initialise the switch for reweight"""
242+
243+ if 'reweight' in self.available_module:
244+ if os.path.exists(pjoin(self.me_dir,'Cards','reweight_card.dat')):
245+ self.switch['reweight'] = 'ON'
246+ else:
247+ self.switch['reweight'] = 'OFF'
248+ else:
249+ self.switch['reweight'] = 'Not Avail.'
250+#
251+# MadAnalysis5
252+#
253+ get_allowed_madanalysis = get_allowed_madspin
254+
255+ def set_default_madanalysis(self):
256+ """initialise the switch for reweight"""
257+
258+ if 'MA5' not in self.available_module:
259+ self.switch['madanalysis'] = 'Not Avail.'
260+ elif os.path.exists(pjoin(self.me_dir,'Cards', 'madanalysis5_hadron_card.dat')):
261+ self.switch['madanalysis'] = 'ON'
262+ else:
263+ self.switch['madanalysis'] = 'OFF'
264+
265+ def check_value_madanalysis(self, value):
266+ """check an entry is valid. return the valid entry in case of shortcut"""
267+
268+ if value.upper() in self.get_allowed('madanalysis'):
269+ return True
270+ value = value.lower()
271+ if value == 'hadron':
272+ return 'ON' if 'ON' in self.get_allowed_madanalysis5 else False
273+ else:
274+ return False
275+
276 #===============================================================================
277 # aMCatNLOCmd
278 #===============================================================================
279@@ -4826,7 +5094,7 @@
280 return model
281
282
283-
284+ action_switcher = AskRunNLO
285 ############################################################################
286 def ask_run_configuration(self, mode, options, switch={}):
287 """Ask the question when launching generate_events/multi_run"""
288@@ -4836,194 +5104,35 @@
289 if 'reweightonly' not in options:
290 options['reweightonly'] = False
291
292-
293- void = 'Not installed'
294- switch_order = ['order', 'fixed_order', 'shower','madspin', 'reweight','madanalysis5']
295- switch_default = {'order': 'NLO', 'fixed_order': 'OFF', 'shower': void,
296- 'madspin': void,'reweight':'OFF','madanalysis5':void}
297- if not switch:
298- switch = switch_default
299- else:
300- switch.update(dict((k,value) for k,v in switch_default.items() if k not in switch))
301- default_switch = ['ON', 'OFF']
302-
303-
304- allowed_switch_value = {'order': ['LO', 'NLO'],
305- 'fixed_order': default_switch,
306- 'shower': default_switch,
307- 'madspin': default_switch,
308- 'reweight': default_switch,
309- 'madanalysis5':['OFF','HADRON']}
310-
311- if not os.path.exists(pjoin(self.me_dir, 'Cards',
312- 'madanalysis5_hadron_card_default.dat')):
313- allowed_switch_value['madanalysis5']=[]
314-
315- description = {'order': 'Perturbative order of the calculation:',
316- 'fixed_order': 'Fixed order (no event generation and no MC@[N]LO matching):',
317- 'shower': 'Shower the generated events:',
318- 'madspin': 'Decay particles with the MadSpin module:',
319- 'reweight': 'Add weights to the events based on changing model parameters:',
320- 'madanalysis5':'Run MadAnalysis5 on the events generated:'}
321-
322- force_switch = {('shower', 'ON'): {'fixed_order': 'OFF'},
323- ('madspin', 'ON'): {'fixed_order':'OFF'},
324- ('reweight', 'ON'): {'fixed_order':'OFF'},
325- ('fixed_order', 'ON'): {'shower': 'OFF', 'madspin': 'OFF', 'reweight':'OFF','madanalysis5':'OFF'},
326- ('madanalysis5','HADRON'): {'shower': 'ON','fixed_order':'OFF'},
327- ('shower','OFF'): {'madanalysis5': 'OFF'},
328- }
329- special_values = ['LO', 'NLO', 'aMC@NLO', 'aMC@LO', 'noshower', 'noshowerLO']
330-
331- assign_switch = lambda key, value: switch.__setitem__(key, value if switch[key] != void else void )
332-
333- if self.proc_characteristics['ninitial'] == 1:
334- switch['fixed_order'] = 'ON'
335- switch['shower'] = 'Not available for decay'
336- switch['madspin'] = 'Not available for decay'
337- switch['reweight'] = 'Not available for decay'
338- switch['madanalysis5'] = 'Not available for decay'
339- allowed_switch_value['fixed_order'] = ['ON']
340- allowed_switch_value['shower'] = ['OFF']
341- allowed_switch_value['madspin'] = ['OFF']
342- allowed_switch_value['reweight'] = ['OFF']
343- allowed_switch_value['madanalysis5'] = ['OFF']
344- available_mode = ['0','1']
345- special_values = ['LO', 'NLO']
346- else:
347- # Init the switch value according to the current status
348- available_mode = ['0', '1', '2','3']
349-
350 if mode == 'auto':
351 mode = None
352 if not mode and (options['parton'] or options['reweightonly']):
353- mode = 'noshower'
354-
355-
356- if '3' in available_mode:
357- if os.path.exists(pjoin(self.me_dir, 'Cards', 'shower_card.dat')):
358- switch['shower'] = 'ON'
359- else:
360- switch['shower'] = 'OFF'
361- if os.path.exists(pjoin(self.me_dir, 'Cards', 'madanalysis5_hadron_card_default.dat')):
362- available_mode.append('6')
363- if os.path.exists(pjoin(self.me_dir, 'Cards', 'madanalysis5_hadron_card.dat')):
364- switch['madanalysis5'] = 'HADRON'
365- else:
366- switch['madanalysis5'] = 'OFF'
367-
368- if (not aMCatNLO or self.options['mg5_path']) and '3' in available_mode:
369- available_mode.append('4')
370- if os.path.exists(pjoin(self.me_dir,'Cards','madspin_card.dat')):
371- switch['madspin'] = 'ON'
372- else:
373- switch['madspin'] = 'OFF'
374- if misc.has_f2py() or self.options['f2py_compiler']:
375- available_mode.append('5')
376- if os.path.exists(pjoin(self.me_dir,'Cards','reweight_card.dat')):
377- switch['reweight'] = 'ON'
378- else:
379- switch['reweight'] = 'OFF'
380- else:
381- switch['reweight'] = 'Not available (requires NumPy)'
382-
383- if 'do_reweight' in options and options['do_reweight'] and '3' in available_mode:
384- if switch['reweight'] == "OFF":
385- switch['reweight'] = "ON"
386- elif switch['reweight'] != "ON":
387- logger.critical("Cannot run REWEIGHT: %s" % switch['reweight'])
388+ mode = 'noshower'
389+
390+ passing_cmd = []
391+ for key,value in switch.keys():
392+ passing_cmd.append('%s=%s' % (key,value))
393+
394+ if 'do_reweight' in options and options['do_reweight']:
395+ passing_cmd.append('reweight=ON')
396 if 'do_madspin' in options and options['do_madspin']:
397- if switch['madspin'] == "OFF":
398- switch['madspin'] = 'ON'
399- elif switch['madspin'] != "ON":
400- logger.critical("Cannot run MadSpin module: %s" % switch['reweight'])
401-
402- answers = list(available_mode) + ['auto', 'done']
403- alias = {}
404- for id, key in enumerate(switch_order):
405- if switch[key] != void and switch[key] in allowed_switch_value[key] and \
406- len(allowed_switch_value[key])>1:
407- answers += ['%s=%s' % (key, s) for s in allowed_switch_value[key]]
408- #allow lower case for on/off
409- alias.update(dict(('%s=%s' % (key, s.lower()), '%s=%s' % (key, s))
410- for s in allowed_switch_value[key]))
411- answers += special_values
412-
413- def create_question(switch):
414- switch_format = " %i %-61s %12s=%s\n"
415- question = "The following switches determine which operations are executed:\n"
416- for id, key in enumerate(switch_order):
417- question += switch_format % (id+1, description[key], key, switch[key])
418- question += ' Either type the switch number (1 to %s) to change its default setting,\n' % (id+1)
419- question += ' or set any switch explicitly (e.g. type \'order=LO\' at the prompt)\n'
420- question += ' Type \'0\', \'auto\', \'done\' or just press enter when you are done.\n'
421- return question
422-
423-
424- def modify_switch(mode, answer, switch):
425- if '=' in answer:
426- key, status = answer.split('=')
427- switch[key] = status
428- if (key, status) in force_switch:
429- for key2, status2 in force_switch[(key, status)].items():
430- if switch[key2] not in [status2, void]:
431- logger.info('For coherence \'%s\' is set to \'%s\''
432- % (key2, status2), '$MG:color:BLACK')
433- switch[key2] = status2
434- elif answer in ['0', 'auto', 'done']:
435- return
436- elif answer in special_values:
437- logger.info('Enter mode value: %s. Go to the related mode' % answer, '$MG:color:BLACK')
438- #assign_switch('reweight', 'OFF')
439- #assign_switch('madspin', 'OFF')
440- if answer == 'LO':
441- switch['order'] = 'LO'
442- switch['fixed_order'] = 'ON'
443- assign_switch('shower', 'OFF')
444- elif answer == 'NLO':
445- switch['order'] = 'NLO'
446- switch['fixed_order'] = 'ON'
447- assign_switch('shower', 'OFF')
448- elif answer == 'aMC@NLO':
449- switch['order'] = 'NLO'
450- switch['fixed_order'] = 'OFF'
451- assign_switch('shower', 'ON')
452- elif answer == 'aMC@LO':
453- switch['order'] = 'LO'
454- switch['fixed_order'] = 'OFF'
455- assign_switch('shower', 'ON')
456- elif answer == 'noshower':
457- switch['order'] = 'NLO'
458- switch['fixed_order'] = 'OFF'
459- assign_switch('shower', 'OFF')
460- elif answer == 'noshowerLO':
461- switch['order'] = 'LO'
462- switch['fixed_order'] = 'OFF'
463- assign_switch('shower', 'OFF')
464- if mode:
465- return
466- return switch
467-
468- modify_switch(mode, self.last_mode, switch)
469- if switch['madspin'] == 'OFF' and os.path.exists(pjoin(self.me_dir,'Cards','madspin_card.dat')):
470- assign_switch('madspin', 'ON')
471-
472- if not self.force:
473- answer = ''
474- while answer not in ['0', 'done', 'auto', 'onlyshower']:
475- question = create_question(switch)
476- if mode:
477- answer = mode
478- else:
479- answer = self.ask(question, '0', answers, alias=alias)
480- if answer.isdigit() and answer != '0':
481- key = switch_order[int(answer) - 1]
482- opt1 = allowed_switch_value[key][0]
483- opt2 = allowed_switch_value[key][1]
484- answer = '%s=%s' % (key, opt1 if switch[key] == opt2 else opt2)
485-
486- if not modify_switch(mode, answer, switch):
487- break
488+ passing_cmd.append('madspin=ON')
489+
490+ force = self.force
491+ if mode == 'onlyshower':
492+ passing_cmd.append('onlyshower')
493+ force = True
494+ elif mode:
495+ passing_cmd.append(mode)
496+
497+ switch = self.ask('', '0', [], ask_class = self.action_switcher,
498+ mode=mode, force=force,
499+ first_cmd=passing_cmd)
500+
501+ misc.sprint(switch)
502+
503+ if 'mode' in switch:
504+ mode = switch['mode']
505
506 #assign the mode depending of the switch
507 if not mode or mode == 'auto':
508@@ -5060,7 +5169,7 @@
509 cards.append('madspin_card.dat')
510 if switch['reweight'] == 'ON':
511 cards.append('reweight_card.dat')
512- if switch['madanalysis5'] == 'HADRON':
513+ if switch['madanalysis'] == 'HADRON':
514 cards.append('madanalysis5_hadron_card.dat')
515 if 'aMC@' in mode:
516 cards.append('shower_card.dat')
517
518=== modified file 'madgraph/interface/extended_cmd.py'
519--- madgraph/interface/extended_cmd.py 2017-08-04 20:30:41 +0000
520+++ madgraph/interface/extended_cmd.py 2017-08-15 12:03:07 +0000
521@@ -1041,7 +1041,7 @@
522 #===============================================================================
523 def ask(self, question, default, choices=[], path_msg=None,
524 timeout = True, fct_timeout=None, ask_class=None, alias={},
525- first_cmd=None, text_format='4', **opt):
526+ first_cmd=None, text_format='4', force=False, **opt):
527 """ ask a question with some pre-define possibility
528 path info is
529 """
530@@ -1082,6 +1082,7 @@
531 if alias:
532 choices += alias.keys()
533
534+
535 question_instance = obj(question, allow_arg=choices, default=default,
536 mother_interface=self, **opt)
537
538@@ -1095,26 +1096,30 @@
539 if hasattr(obj, "haspiping"):
540 obj.haspiping = self.haspiping
541
542-
543-
544-
545- answer = self.check_answer_in_input_file(question_instance, default, path_msg)
546- if answer is not None:
547- if answer in alias:
548- answer = alias[answer]
549- if ask_class:
550- line=answer
551- answer = question_instance.default(line)
552- question_instance.postcmd(answer, line)
553- return question_instance.answer
554- if hasattr(question_instance, 'check_answer_consistency'):
555- question_instance.check_answer_consistency()
556- return answer
557+ if not force:
558+ answer = default
559+ else:
560+
561+ answer = self.check_answer_in_input_file(question_instance, default, path_msg)
562+ if answer is not None:
563+ if answer in alias:
564+ answer = alias[answer]
565+ if ask_class:
566+ line=answer
567+ answer = question_instance.default(line)
568+ question_instance.postcmd(answer, line)
569+ return question_instance.answer
570+ if hasattr(question_instance, 'check_answer_consistency'):
571+ question_instance.check_answer_consistency()
572+ return answer
573
574 question = question_instance.question
575- value = Cmd.timed_input(question, default, timeout=timeout,
576+ if not force:
577+ value = Cmd.timed_input(question, default, timeout=timeout,
578 fct=question_instance, fct_timeout=fct_timeout)
579-
580+ else:
581+ value = default
582+
583 try:
584 if value in alias:
585 value = alias[value]
586@@ -1123,6 +1128,7 @@
587
588 if value == default and ask_class:
589 value = question_instance.default(default)
590+
591 return value
592
593 def do_import(self, line):
594@@ -2014,7 +2020,7 @@
595 try:
596 out = {}
597 out[' Options'] = Cmd.list_completion(text, self.allow_arg)
598- out[' Recognized command'] = BasicCmd.completenames(self, text)
599+ out[' Recognized command'] = super(SmartQuestion, self).completenames(text,line, *ignored)
600
601 return self.deal_multiple_categories(out)
602 except Exception, error:
603@@ -2067,10 +2073,7 @@
604 if prev_timer:
605 if pat.search(self.question):
606 timeout = int(pat.search(self.question).groups()[0])
607- else:
608- timeout=20
609- print
610- signal.alarm(timeout)
611+ signal.alarm(timeout)
612 if reprint_opt:
613 if not prev_timer:
614 self.question = pat.sub('',self.question)
615@@ -2139,7 +2142,7 @@
616 return True
617 elif line and hasattr(self, 'do_%s' % line.split()[0]):
618 return self.reask()
619- elif self.value == 'repeat':
620+ elif self.value in ['repeat', 'reask']:
621 return self.reask()
622 elif len(self.allow_arg)==0:
623 return True
624@@ -2202,7 +2205,7 @@
625 out = {}
626 out[' Options'] = Cmd.list_completion(text, self.allow_arg)
627 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False)
628- out[' Recognized command'] = BasicCmd.completenames(self, text)
629+ out[' Recognized command'] = BasicCmd.completenames(self, text, line, begidx, endidx)
630
631 return self.deal_multiple_categories(out, formatting)
632 except Exception, error:
633@@ -2231,8 +2234,9 @@
634
635 return Cmd.path_completion(text,
636 os.path.join('.',*[a for a in args \
637- if a.endswith(os.path.sep)]))
638- self.completenames(line+text)
639+ if a.endswith(os.path.sep)]),
640+ begidx, endidx)
641+ return self.completenames(text, line, begidx, endidx)
642
643
644 def postcmd(self, stop, line):
645@@ -2267,6 +2271,456 @@
646 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default )
647 return obj.cmdloop()
648
649+
650+
651+class ControlSwitch(SmartQuestion):
652+ """A class for asking a question on which program to run.
653+ This is the abstract class
654+
655+ Behavior for each switch can be customize via:
656+ set_default_XXXX() -> set default value
657+ get_allowed_XXXX() -> return list of possible value
658+ check_value_XXXX(value) -> return True/False if the user can set such value
659+ switch_off_XXXXX() -> set if off (called for special mode)
660+ color_for_XXXX(value) -> return the representation on the screen for value
661+
662+ consistency_XX_YY(val_XX, val_YY)
663+ -> XX is the new key set by the user to a new value val_XX
664+ -> YY is another key set by the user.
665+ -> return value should be None or "replace_YY"
666+
667+ consistency_XX(val_XX):
668+ check the consistency of the other switch given the new status of this one.
669+ return a dict {key:replaced_value} or {} if nothing to do
670+
671+ user typing "NAME" will result to a call to self.ans_NAME(None)
672+ user typing "NAME=XX" will result to a call to self.ans_NAME('XX')
673+
674+ Note on case sensitivity:
675+ -------------------------
676+ the XXX is displayed with the case in self.to_control
677+ but ALL functions should use the lower case version.
678+ for key associated to get_allowed_keys(),
679+ if (user) value not in that list.
680+ -> try to find the first entry matching up to the case
681+ for ans_XXX, set the value to lower case, but if case_XXX is set to True
682+ """
683+
684+ line_length = 80
685+ case_sensitive = False
686+ quit_on = ['0','done', 'EOF','','auto']
687+
688+ def __init__(self, to_control, motherinstance, *args, **opts):
689+ """to_control is a list of ('KEY': 'Choose the shower/hadronization program')
690+ """
691+
692+ self.to_control = to_control
693+ self.mother_interface = motherinstance
694+ self.inconsistent_keys = {} #flag parameter which are currently not consistent
695+ # and the value by witch they will be replaced if the
696+ # inconsistency remains.
697+ self.inconsistent_details = {} # flag to list
698+ self.last_changed = [] # keep the order in which the flag have been modified
699+ # to choose the resolution order of conflict
700+ #initialise the main return value
701+ self.switch = {}
702+ for key, _ in to_control:
703+ self.switch[key.lower()] = 'temporary'
704+
705+ self.set_default_switch()
706+ question = self.create_question()
707+
708+ #check all default for auto-completion
709+ allowed_args = [ `i`+';' for i in range(1, 1+len(self.to_control))]
710+ for key in self.switch:
711+ allowed_args += ['%s=%s;' % (key,s) for s in self.get_allowed(key)]
712+ # adding special mode
713+ allowed_args += [key[4:]+';' for key in dir(self) if key.startswith('ans_')]
714+ if 'allow_arg' in opts:
715+ allowed_args += opts['allow_arg']
716+ del opts['allow_arg']
717+
718+ super(ControlSwitch, self).__init__(question, allowed_args, *args, **opts)
719+ self.options = self.mother_interface.options
720+
721+
722+ def set_default_switch(self):
723+
724+ for key,_ in self.to_control:
725+ key = key.lower()
726+ if hasattr(self, 'set_default_%s' % key):
727+ getattr(self, 'set_default_%s' % key)()
728+ else:
729+ self.default_switch_for(key)
730+
731+ def default_switch_for(self, key):
732+ """use this if they are no dedicated function for such key"""
733+
734+ if hasattr(self, 'get_allowed_%s' % key):
735+ return getattr(self, 'get_allowed_%s' % key)()[0]
736+ else:
737+ self.switch[key] = 'OFF'
738+
739+ def set_all_off(self):
740+ """set all valid parameter to OFF --call before special keyword--
741+ """
742+
743+ for key in self.switch:
744+ if hasattr(self, 'switch_off_%s' % key):
745+ getattr(self, 'switch_off_%s' % key)()
746+ elif self.check_value(key, self.switch[key]):
747+ self.switch[key] = 'OFF'
748+ self.inconsistent_details = {}
749+ self.inconsistent_keys = {}
750+
751+
752+ def check_value(self, key, value):
753+ """return True/False if the value is a correct value to be set by the USER.
754+ other value than those can be set by the system --like-- Not available.
755+ This does not check the full consistency of the switch
756+ """
757+
758+ if hasattr(self, 'check_value_%s' % key):
759+ return getattr(self, 'check_value_%s' % key)(value)
760+ elif value in self.get_allowed(key):
761+ return True
762+ else:
763+ return False
764+
765+
766+
767+ def get_allowed(self, key):
768+ """return the list of possible value for key"""
769+
770+ if hasattr(self, 'get_allowed_%s' % key):
771+ return getattr(self, 'get_allowed_%s' % key)()
772+ else:
773+ return ['ON', 'OFF']
774+
775+ def default(self, line):
776+ """Default action if line is not recognized"""
777+
778+ line=line.strip().replace('@', '__at__')
779+ if ';' in line:
780+ for l in line.split(';'):
781+ if l:
782+ out = self.default(l)
783+ return out
784+
785+ if '=' in line:
786+ base, value = line.split('=')
787+ elif ' ' in line:
788+ base, value = line.split(' ', 1)
789+ elif hasattr(self, 'ans_%s' % line.lower()):
790+ base, value = line.lower(), None
791+ elif line.isdigit() and line in [`i` for i in range(1, len(self.switch)+1)]:
792+ # go from one valid option to the next in the get_allowed for that option
793+ base = self.to_control[int(line)-1][0].lower()
794+ return self.default(base) # just recall this function with the associate name
795+ elif line.lower() in self.switch:
796+ # go from one valid option to the next in the get_allowed for that option
797+ base = line.lower()
798+ try:
799+ cur = self.get_allowed(base).index(self.switch[base])
800+ except:
801+ if self.get_allowed(base):
802+ value = self.get_allowed(base)[0]
803+ else:
804+ logger.warning('Can not switch "%s" to another value via number', base)
805+ self.value='reask'
806+ return
807+ else:
808+ try:
809+ value = self.get_allowed(base)[cur+1]
810+ except IndexError:
811+ value = self.get_allowed(base)[0]
812+ elif line in ['', 'done', 'EOF', 'eof','0']:
813+ super(ControlSwitch, self).default(line)
814+ return self.answer
815+ elif line in 'auto':
816+ self.switch['dynamical'] = True
817+ return super(ControlSwitch, self).default(line)
818+ else:
819+ logger.warning('unknow command: %s' % line)
820+ self.value = 'reask'
821+ return
822+
823+ self.value = 'reask'
824+ base = base.lower()
825+ if hasattr(self, 'ans_%s' % base):
826+ if value and not self.is_case_sensitive(base):
827+ value = value.lower()
828+ getattr(self, 'ans_%s' % base)(value)
829+ elif base in self.switch:
830+ self.set_switch(base, value)
831+ else:
832+ logger.warning('Not valid command: %s' % line)
833+
834+ def is_case_sensitive(self, key):
835+ """check if a key is case sensitive"""
836+
837+ case = self.case_sensitive
838+ if hasattr(self, 'case_%s' % key):
839+ case = getattr(self, 'case_%s' % key)
840+ return case
841+
842+ def onecmd(self, line, **opt):
843+ """ensure to rewrite the function if a call is done directly"""
844+ out = super(ControlSwitch, self).onecmd(line, **opt)
845+ self.create_question()
846+ return out
847+
848+ @property
849+ def answer(self):
850+ if not self.inconsistent_keys:
851+ return self.switch
852+ else:
853+ out = dict(self.switch)
854+ out.update(self.inconsistent_keys)
855+ return out
856+
857+ def postcmd(self, stop, line):
858+
859+ line = line.strip()
860+ if ';' in line:
861+ line= [l for l in line.split(';') if l][-1]
862+ if line in self.quit_on:
863+ return True
864+ return self.reask(True)
865+
866+
867+ def set_switch(self, key, value, user=True):
868+ """change a switch to a given value"""
869+
870+ assert key in self.switch
871+
872+
873+
874+ if hasattr(self, 'ans_%s' % key):
875+ if not self.is_case_sensitive(key):
876+ value = value.lower()
877+ return getattr(self, 'ans_%s' % key)(value)
878+
879+ if not self.is_case_sensitive(key) and value not in self.get_allowed(key):
880+ lower = [t.lower() for t in self.get_allowed(key)]
881+ try:
882+ ind = lower.index(value.lower())
883+ except ValueError:
884+ pass # keep the current case, in case check_value accepts it anyway.
885+ else:
886+ value = self.get_allowed(key)[ind]
887+
888+ check = self.check_value(key, value)
889+ if not check:
890+ logger.warning('"%s" not valid option for "%s"', value, key)
891+ return
892+ if isinstance(check, str):
893+ value = check
894+
895+ self.switch[key] = value
896+
897+ if user:
898+ self.check_consistency(key, value)
899+
900+ def remove_inconsistency(self, keys=[]):
901+
902+ if not keys:
903+ self.inconsistent_keys = {}
904+ self.inconsistent_details = {}
905+ elif isinstance(keys, list):
906+ for key in keys:
907+ if key in self.inconsistent_keys:
908+ del self.inconsistent_keys[keys]
909+ del self.inconsistent_details[keys]
910+ else:
911+ if keys in self.inconsistent_keys:
912+ del self.inconsistent_keys[keys]
913+ del self.inconsistent_details[keys]
914+
915+ def check_consistency(self, key, value):
916+ """check the consistency of the new flag with the old ones"""
917+
918+ if key in self.last_changed:
919+ self.last_changed.remove(key)
920+ self.last_changed.append(key)
921+
922+ # this is used to update self.consistency_keys which contains:
923+ # {key: replacement value with solved conflict}
924+ # it is based on self.consistency_details which is a dict
925+ # key: {'orig_value':
926+ # 'changed_key':
927+ # 'new_changed_key_val':
928+ # 'replacement':
929+ # which keeps track of all conflict and of their origin.
930+
931+
932+ # rules is a dict: {keys:None} if the value for that key is consistent.
933+ # {keys:value_to_replace} if that key is inconsistent
934+ if hasattr(self, 'consistency_%s' % key):
935+ rules = dict([(key2, None) for key2 in self.switch])
936+ rules.update(getattr(self, 'consistency_%s' % key)(value, self.switch))
937+ else:
938+ rules = {}
939+ for key2,value2 in self.switch.items():
940+ if hasattr(self, 'consistency_%s_%s' % (key,key2)):
941+ rules[key2] = getattr(self, 'consistency_%s_%s' % (key,key2))(value, value2)
942+ else:
943+ rules[key2] = None
944+
945+ #update the self.inconsisten_details adding new conflict
946+ # start by removing the inconsistency for the newly set parameter
947+ self.remove_inconsistency(key)
948+ # then add the new ones
949+ for key2 in self.switch:
950+ if rules[key2]:
951+ info = {'orig_value': self.switch[key2],
952+ 'changed_key': key,
953+ 'new_changed_key_val': value,
954+ 'replacement': rules[key2]}
955+ if key2 in self.inconsistent_details:
956+ self.inconsistent_details[key2].append(info)
957+ else:
958+ self.inconsistent_details[key2] = [info]
959+
960+ if not self.inconsistent_details:
961+ return
962+
963+ # review the status of all conflict
964+ for key2 in dict(self.inconsistent_details):
965+ for conflict in list(self.inconsistent_details[key2]):
966+ keep_conflict = True
967+ # check that we are still at the current value
968+ if conflict['orig_value'] != self.switch[key2]:
969+ keep_conflict = False
970+ # check if the reason of the conflict still in place
971+ if self.switch[conflict['changed_key']] != conflict['new_changed_key_val']:
972+ keep_conflict = False
973+ if not keep_conflict:
974+ self.inconsistent_details[key2].remove(conflict)
975+ if not self.inconsistent_details[key2]:
976+ del self.inconsistent_details[key2]
977+
978+
979+ # create the valid set of replacement for this current conflict
980+ # start by current status to avoid to keep irrelevant conflict
981+ tmp_switch = dict(self.switch)
982+
983+ # build the order in which we have to check the various conflict reported
984+ to_check = [(c['changed_key'], c['new_changed_key_val']) \
985+ for k in self.inconsistent_details.values() for c in k
986+ if c['changed_key'] != key]
987+
988+ to_check.sort(lambda x, y: -1 if self.last_changed.index(x[0])>self.last_changed.index(y[0]) else 1)
989+
990+ # validate tmp_switch.
991+ to_check = [(key, value)] + to_check
992+
993+ i = 0
994+ while len(to_check) and i < 50:
995+ # check in a iterative way the consistency of the tmp_switch parameter
996+ i +=1
997+ key2, value2 = to_check.pop(0)
998+ if hasattr(self, 'consistency_%s' % key2):
999+ rules2 = dict([(key2, None) for key2 in self.switch])
1000+ rules2.update(getattr(self, 'consistency_%s' % key2)(value, tmp_switch))
1001+ else:
1002+ rules = {}
1003+ for key3,value3 in self.switch.items():
1004+ if hasattr(self, 'consistency_%s_%s' % (key2,key3)):
1005+ rules[key3] = getattr(self, 'consistency_%s_%s' % (key2,key3))(value2, value3)
1006+ else:
1007+ rules[key3] = None
1008+
1009+ for key, replacement in rules.items():
1010+ if replacement:
1011+ tmp_switch[key] = replacement
1012+ to_check.append((key, replacement))
1013+ # avoid situation like
1014+ # to_check = [('fixed_order', 'ON'), ('fixed_order', 'OFF')]
1015+ # always keep the last one
1016+ pos = {}
1017+ for i, (key,value) in enumerate(to_check):
1018+ pos[key] = i
1019+ to_check_new = []
1020+ for i, (key,value) in enumerate(to_check):
1021+ if pos[key] == i:
1022+ to_check_new.append((key,value))
1023+ to_check = to_check_new
1024+
1025+ # Now tmp_switch is to a fully consistent setup for sure.
1026+ # fill self.inconsistent_key
1027+ self.inconsistent_keys = {}
1028+ for key2, value2 in tmp_switch.items():
1029+ if value2 != self.switch[key2]:
1030+ self.inconsistent_keys[key2] = value2
1031+
1032+ #
1033+ # Helper routine for putting questions with correct color
1034+ #
1035+ green = '\x1b[32m%s\x1b[0m'
1036+ bold = '\x1b[33m%s\x1b[0m'
1037+ red = '\x1b[31m%s\x1b[0m'
1038+ def color_for_value(self, key, switch_value):
1039+
1040+ if key in self.inconsistent_keys:
1041+ return self.red % switch_value + '-> ' + self.bold % self.inconsistent_keys[key]
1042+
1043+ if self.check_value(key, switch_value):
1044+ if hasattr(self, 'color_for_%s' % key):
1045+ return getattr(self, 'color_for_%s' % key)(switch_value)
1046+ if switch_value in ['OFF']:
1047+ # inconsistent key are the list of key which are inconsistent with the last change
1048+ return self.red % switch_value
1049+ else:
1050+ return self.green % switch_value
1051+ else:
1052+ if ' ' in switch_value:
1053+ return self.bold % switch_value
1054+ else:
1055+ return self.red % switch_value
1056+
1057+ def create_question(self):
1058+
1059+ text = \
1060+ ["The following switches determine which programs are run:",
1061+ "/%s\\" % ("=" * (self.line_length-2))
1062+ ]
1063+
1064+ format = '| %2d. %{0}s %11s = %11s'.format(self.line_length-34)
1065+
1066+ for i,(key, descrip) in enumerate(self.to_control):
1067+ text.append(format % (i+1, descrip+':', key, self.color_for_value(key,self.switch[key])))
1068+
1069+
1070+ text.append("\\%s/" % ("=" * (self.line_length-2)))
1071+
1072+ # find a good example of switch to set
1073+ example = None
1074+ for key in self.switch:
1075+ if len(self.get_allowed(key)) > 1:
1076+ for val in self.get_allowed(key):
1077+ if val != self.switch[key]:
1078+ example = (key, val)
1079+ break
1080+ else:
1081+ continue
1082+ break
1083+
1084+ if not example:
1085+ example = ('KEY', 'VALUE')
1086+
1087+ text += \
1088+ ["Either type the switch number (1 to %s) to change its setting," % len(self.to_control),
1089+ "Set any switch explicitly (e.g. type '%s=%s' at the prompt)" % example,
1090+ "Type 'help' for the list of all valid option",
1091+ "Type '0', 'auto', 'done' or just press enter when you are done."]
1092+
1093+ self.question = "\n".join(text)
1094+ return self.question
1095+
1096+
1097+
1098+
1099 #===============================================================================
1100 #
1101 #===============================================================================
1102
1103=== modified file 'madgraph/interface/madevent_interface.py'
1104--- madgraph/interface/madevent_interface.py 2017-08-10 13:09:28 +0000
1105+++ madgraph/interface/madevent_interface.py 2017-08-15 12:03:07 +0000
1106@@ -494,6 +494,321 @@
1107 logger.info(" The banner can be remove only if all files are removed first.")
1108
1109
1110+class AskRun(cmd.ControlSwitch):
1111+ """a class for the question on what to do on a madevent run"""
1112+
1113+ to_control = [('shower', 'Choose the shower/hadronization program'),
1114+ ('detector', 'Choose the detector simulation program'),
1115+ ('analysis', 'Choose an analysis package (plot/convert)'),
1116+ ('madspin', 'Decay onshell particles'),
1117+ ('reweight', 'Add weights to events for new hypp.')
1118+ ]
1119+
1120+ def __init__(self, question, line_args=[], mode=None, force=False,
1121+ *args, **opt):
1122+
1123+ self.check_available_module(opt['mother_interface'].options)
1124+ self.me_dir = opt['mother_interface'].me_dir
1125+ super(AskRun,self).__init__(self.to_control, opt['mother_interface'],
1126+ *args, **opt)
1127+
1128+
1129+ def check_available_module(self, options):
1130+
1131+ self.available_module = set()
1132+
1133+ if options['pythia-pgs_path']:
1134+ self.available_module.add('PY6')
1135+ self.available_module.add('PGS')
1136+ if options['pythia8_path']:
1137+ self.available_module.add('PY8')
1138+ if options['madanalysis_path']:
1139+ self.available_module.add('MA4')
1140+ if options['madanalysis5_path']:
1141+ self.available_module.add('MA5')
1142+ if options['exrootanalysis_path']:
1143+ self.available_module.add('ExRoot')
1144+ if options['delphes_path']:
1145+ if 'PY6' in self.available_module or 'PY8' in self.available_module:
1146+ self.available_module.add('Delphes')
1147+ if not MADEVENT or ('mg5_path' in options and options['mg5_path']):
1148+ self.available_module.add('MadSpin')
1149+ if misc.has_f2py() or self.mother_interface.options['f2py_compiler']:
1150+ self.available_module.add('reweight')
1151+
1152+# old mode to activate the shower
1153+ def ans_parton(self, value=None):
1154+ """None: means that the user type 'pythia'
1155+ value: means that the user type pythia=value"""
1156+
1157+ if value is None:
1158+ self.set_all_off()
1159+ else:
1160+ logger.warning('Invalid command: parton=%s' % value)
1161+
1162+
1163+#
1164+# HANDLING SHOWER
1165+#
1166+ def get_allowed_shower(self):
1167+ """return valid entry for the shower switch"""
1168+
1169+ if hasattr(self, 'allowed_shower'):
1170+ return self.allowed_shower
1171+
1172+ self.allowed_shower = []
1173+ if 'PY6' in self.available_module:
1174+ self.allowed_shower.append('Pythia6')
1175+ if 'PY8' in self.available_module:
1176+ self.allowed_shower.append('Pythia8')
1177+ if self.allowed_shower:
1178+ self.allowed_shower.append('OFF')
1179+ return self.allowed_shower
1180+
1181+ def set_default_shower(self):
1182+
1183+ if 'PY6' in self.available_module and\
1184+ os.path.exists(pjoin(self.me_dir,'Cards','pythia_card.dat')):
1185+ self.switch['shower'] = 'Pythia6'
1186+ elif 'PY8' in self.available_module and\
1187+ os.path.exists(pjoin(self.me_dir,'Cards','pythia8_card.dat')):
1188+ self.switch['shower'] = 'Pythia8'
1189+ elif self.get_allowed_shower():
1190+ self.switch['shower'] = 'OFF'
1191+ else:
1192+ self.switch['shower'] = 'Not Avail'
1193+
1194+ def check_shower(self, value):
1195+ """check an entry is valid. return the valid entry in case of shortcut"""
1196+
1197+ if value in self.get_allowed_shower():
1198+ return True
1199+
1200+ value =value.lower()
1201+ if value in ['py6','6','pythia_6']:
1202+ return 'Pythia6'
1203+ elif value in ['py8','8','pythia_8']:
1204+ return 'Pythia8'
1205+ else:
1206+ return False
1207+
1208+
1209+# old mode to activate the shower
1210+ def ans_pythia(self, value=None):
1211+ """None: means that the user type 'pythia'
1212+ value: means that the user type pythia=value"""
1213+
1214+ if 'PY6' not in self.available_module:
1215+ logger.info('pythia-pgs not available. Ignore commmand')
1216+ return
1217+
1218+ if value is None:
1219+ self.set_all_off()
1220+ self.switch['shower'] = 'Pythia6'
1221+ elif value == 'on':
1222+ return self.ans_pythia(None)
1223+ elif value == 'off':
1224+ self.set_switch('shower', 'OFF')
1225+ else:
1226+ logger.warning('Invalid command: pythia=%s' % value)
1227+
1228+
1229+ def consistency_shower_detector(self, vshower, vdetector):
1230+ """consistency_XX_YY(val_XX, val_YY)
1231+ -> XX is the new key set by the user to a new value val_XX
1232+ -> YY is another key
1233+ -> return value should be None or "replace_YY"
1234+ """
1235+
1236+ if vshower == 'OFF':
1237+ if self.check_value('detector', vdetector) and vdetector!= 'OFF':
1238+ return 'OFF'
1239+ if vshower == 'Pythia8' and vdetector == 'PGS':
1240+ return 'OFF'
1241+
1242+ return None
1243+#
1244+# HANDLING DETECTOR
1245+#
1246+ def get_allowed_detector(self):
1247+ """return valid entry for the switch"""
1248+
1249+ if hasattr(self, 'allowed_detector'):
1250+ return self.allowed_detector
1251+
1252+ self.allowed_detector = []
1253+ if 'PGS' in self.available_module:
1254+ self.allowed_detector.append('PGS')
1255+ if 'Delphes' in self.available_module:
1256+ self.allowed_detector.append('Delphes')
1257+
1258+
1259+ if self.allowed_detector:
1260+ self.allowed_detector.append('OFF')
1261+ return self.allowed_detector
1262+
1263+ def set_default_detector(self):
1264+
1265+ self.set_default_shower() #ensure that this one is called first!
1266+
1267+ if 'PGS' in self.available_module and self.switch['shower'] == 'Pythia6':
1268+ self.switch['detector'] = 'PGS'
1269+ elif 'Delphes' in self.available_module and self.switch['shower'] != 'OFF':
1270+ self.switch['detector'] = 'Delphes'
1271+ elif self.get_allowed_detector():
1272+ self.switch['detector'] = 'OFF'
1273+ else:
1274+ self.switch['detector'] = 'missing'
1275+
1276+# old mode to activate pgs
1277+ def ans_pgs(self, value=None):
1278+ """None: means that the user type 'pgs'
1279+ value: means that the user type pgs=value"""
1280+
1281+ if 'PGS' not in self.available_module:
1282+ logger.info('pythia-pgs not available. Ignore commmand')
1283+ return
1284+
1285+ if value is None:
1286+ self.set_all_off()
1287+ self.switch['shower'] = 'Pythia6'
1288+ self.switch['detector'] = 'PGS'
1289+ elif value == 'on':
1290+ self.switch['shower'] = 'Pythia6'
1291+ self.switch['detector'] = 'PGS'
1292+ elif value == 'off':
1293+ self.set_switch('detector', 'OFF')
1294+ else:
1295+ logger.warning('Invalid command: pgs=%s' % value)
1296+
1297+
1298+# old mode to activate Delphes
1299+ def ans_delphes(self, value=None):
1300+ """None: means that the user type 'delphes'
1301+ value: means that the user type delphes=value"""
1302+
1303+ if 'Delphes' not in self.available_module:
1304+ logger.warning('Delphes not available. Ignore commmand')
1305+ return
1306+
1307+ if value is None:
1308+ self.set_all_off()
1309+ if 'PY6' in self.available_module:
1310+ self.switch['shower'] = 'Pythia6'
1311+ else:
1312+ self.switch['shower'] = 'Pythia8'
1313+ self.switch['detector'] = 'Delphes'
1314+ elif value == 'on':
1315+ return self.ans_delphes(None)
1316+ elif value == 'off':
1317+ self.set_switch('detector', 'OFF')
1318+ else:
1319+ logger.warning('Invalid command: pgs=%s' % value)
1320+
1321+ def consistency_detector_shower(self,vdetector, vshower):
1322+ """consistency_XX_YY(val_XX, val_YY)
1323+ -> XX is the new key set by the user to a new value val_XX
1324+ -> YY is another key
1325+ -> return value should be None or "replace_YY"
1326+ """
1327+
1328+ if vdetector == 'PGS' and vshower != 'Pythia6':
1329+ return 'Pythia6'
1330+ if vdetector == 'Delphes' and vshower not in ['Pythia6', 'Pythia8']:
1331+ if 'PY8' in self.available_module:
1332+ return 'Pythia8'
1333+ elif 'PY6' in self.available_module:
1334+ return 'Pythia6'
1335+ else:
1336+ raise Exception
1337+ return None
1338+
1339+
1340+#
1341+# HANDLING ANALYSIS
1342+#
1343+ def get_allowed_analysis(self):
1344+ """return valid entry for the shower switch"""
1345+
1346+ if hasattr(self, 'allowed_analysis'):
1347+ return self.allowed_analysis
1348+
1349+ self.allowed_analysis = []
1350+ if 'ExRoot' in self.available_module:
1351+ self.allowed_analysis.append('ExRoot')
1352+ if 'MA4' in self.available_module:
1353+ self.allowed_analysis.append('MadAnalysis4')
1354+ if 'MA5' in self.available_module:
1355+ self.allowed_analysis.append('MadAnalysis5')
1356+
1357+ if self.allowed_analysis:
1358+ self.allowed_analysis.append('OFF')
1359+
1360+ return self.allowed_analysis
1361+
1362+ def check_analysis(self, value):
1363+ """check an entry is valid. return the valid entry in case of shortcut"""
1364+
1365+ if value in self.get_allowed_analysis():
1366+ return True
1367+ if value.lower() in ['ma4', 'madanalysis4', 'madanalysis_4','4']:
1368+ return 'MadAnalysis4'
1369+ if value.lower() in ['ma5', 'madanalysis5', 'madanalysis_5','5']:
1370+ return 'MadAnalysis5'
1371+ if value.lower() in ['ma', 'madanalysis']:
1372+ if 'MA5' in self.available_module:
1373+ return 'MadAnalysis5'
1374+ elif 'MA4' in self.available_module:
1375+ return 'MadAnalysis4'
1376+ else:
1377+ return False
1378+ else:
1379+ return False
1380+
1381+
1382+ def set_default_analysis(self):
1383+ """initialise the switch for analysis"""
1384+
1385+ if 'MA4' in self.available_module and \
1386+ os.path.exists(pjoin(self.me_dir,'Cards','plot_card.dat')):
1387+ self.switch['analysis'] = 'MadAnalysis4'
1388+ elif 'MA5' in self.available_module and\
1389+ (os.path.exists(pjoin(self.me_dir,'Cards','madanalysis5_parton_card.dat'))\
1390+ or os.path.exists(pjoin(self.me_dir,'Cards', 'madanalysis5_hadron_card.dat'))):
1391+ self.switch['analysis'] = 'MadAnalysis5'
1392+ elif 'ExRoot' in self.available_module:
1393+ self.switch['analysis'] = 'ExRoot'
1394+ elif self.get_allowed_analysis():
1395+ self.switch['analysis'] = 'OFF'
1396+ else:
1397+ self.switch['analysis'] = 'Not Avail.'
1398+
1399+#
1400+# MADSPIN handling
1401+#
1402+ def set_default_madspin(self):
1403+ """initialise the switch for madspin"""
1404+
1405+ if 'MadSpin' in self.available_module:
1406+ if os.path.exists(pjoin(self.me_dir,'Cards','madspin_card.dat')):
1407+ self.switch['madspin'] = 'ON'
1408+ else:
1409+ self.switch['madspin'] = 'OFF'
1410+ else:
1411+ self.switch['madspin'] = 'Not Avail.'
1412+#
1413+# ReWeight handling
1414+#
1415+ def set_default_reweight(self):
1416+ """initialise the switch for reweight"""
1417+
1418+ if 'reweight' in self.available_module:
1419+ if os.path.exists(pjoin(self.me_dir,'Cards','reweight_card.dat')):
1420+ self.switch['reweight'] = 'ON'
1421+ else:
1422+ self.switch['reweight'] = 'OFF'
1423+ else:
1424+ self.switch['reweight'] = 'Not Avail.'
1425
1426 #===============================================================================
1427 # CheckValidForCmd
1428@@ -1724,6 +2039,8 @@
1429 'delphes' : ['generate_events [OPTIONS]', 'multi_run [OPTIONS]']
1430 }
1431
1432+ asking_for_run = AskRun
1433+
1434 ############################################################################
1435 def __init__(self, me_dir = None, options={}, *completekey, **stdin):
1436 """ add information to the cmd """
1437@@ -5760,290 +6077,35 @@
1438 return True
1439
1440
1441-
1442+ action_switcher = AskRun
1443 ############################################################################
1444 def ask_run_configuration(self, mode=None, args=[]):
1445 """Ask the question when launching generate_events/multi_run"""
1446-
1447- available_mode = ['0']
1448- void = 'Not installed'
1449- switch_order = ['shower', 'detector', 'analysis', 'madspin', 'reweight']
1450-
1451- switch = dict((k, void) for k in switch_order)
1452-
1453- description = {'shower': 'Choose the shower/hadronization program:',
1454- 'detector': 'Choose the detector simulation program:',
1455- 'madspin': 'Decay particles with the MadSpin module:',
1456- 'reweight':'Add weights to events for different model hypothesis:',
1457- 'analysis':'Run an analysis package on the events generated:'
1458- }
1459-
1460- force_switch = {('shower', 'OFF'): {'detector': 'OFF'},
1461- ('detector', 'PGS'): {'shower':'PYTHIA6'},
1462- ('detector', 'DELPHES'): {'shower': ['PYTHIA8', 'PYTHIA6']}}
1463-
1464- switch_assign = lambda key, value: switch.__setitem__(key, value if value \
1465- in valid_options[key] else switch[key])
1466-
1467- valid_options = dict((k, ['OFF']) for k in switch_order) # track of all possible input for an entry
1468- options = ['auto', 'done']
1469- options_legacy = []
1470-
1471- # Init the switch value according to the current status
1472- if self.options['pythia-pgs_path']:
1473- available_mode.append('1')
1474- available_mode.append('2')
1475- valid_options['shower'].append('PYTHIA6')
1476- valid_options['detector'].append('PGS')
1477- options_legacy += ['pythia', 'pgs', 'pythia=ON', 'pythia=OFF', 'pgs=ON', 'pgs=OFF']
1478- if os.path.exists(pjoin(self.me_dir,'Cards','pythia_card.dat')):
1479- switch['shower'] = 'PYTHIA6'
1480- else:
1481- switch['shower'] = 'OFF'
1482- if os.path.exists(pjoin(self.me_dir,'Cards','pgs_card.dat')):
1483- switch['detector'] = 'PGS'
1484- else:
1485- switch['detector'] = 'OFF'
1486-
1487- if self.options['pythia8_path']:
1488- available_mode.append('1')
1489- valid_options['shower'].append('PYTHIA8')
1490- if os.path.exists(pjoin(self.me_dir,'Cards','pythia8_card.dat')):
1491- switch['shower'] = 'PYTHIA8'
1492- elif switch['shower'] == void:
1493- switch['shower'] = 'OFF'
1494-
1495- # MadAnalysis4 options
1496- if self.options['madanalysis_path']:
1497- if os.path.exists(pjoin(self.me_dir,'Cards','plot_card_default.dat')):
1498- valid_options['analysis'].insert(0,'MADANALYSIS_4')
1499-
1500- if os.path.exists(pjoin(self.me_dir,'Cards','plot_card.dat')):
1501- switch['analysis'] = 'MADANALYSIS_4'
1502-
1503- # MadAnalysis5 options
1504- if self.options['madanalysis5_path']:
1505- if os.path.exists(pjoin(self.me_dir,'Cards','madanalysis5_parton_card_default.dat')) or \
1506- os.path.exists(pjoin(self.me_dir,'Cards','madanalysis5_hadron_card_default.dat')):
1507- valid_options['analysis'].append('MADANALYSIS_5')
1508-
1509- parton_card_present = os.path.exists(pjoin(self.me_dir,'Cards',
1510- 'madanalysis5_parton_card.dat'))
1511- hadron_card_present = os.path.exists(pjoin(self.me_dir,'Cards',
1512- 'madanalysis5_hadron_card.dat'))
1513- if hadron_card_present or parton_card_present:
1514- switch['analysis'] = 'MADANALYSIS_5'
1515-
1516- # ExRootanalysis
1517- eradir = self.options['exrootanalysis_path']
1518- if eradir and misc.is_executable(pjoin(eradir,'ExRootLHEFConverter')):
1519- valid_options['analysis'].insert(0,'EXROOTANALYSIS')
1520- if switch['analysis'] in ['OFF', void]:
1521- switch['analysis'] = 'EXROOTANALYSIS'
1522-
1523-
1524-
1525- if len(valid_options['analysis'])>1:
1526- available_mode.append('3')
1527- if switch['analysis'] == void:
1528- switch['analysis'] = 'OFF'
1529- else:
1530- switch['analysis'] = 'No analysis tool interfaced to MG5aMC.'
1531-
1532- # Need to allow Delphes only if a shower exists
1533- if self.options['delphes_path']:
1534- if valid_options['shower'] != ['OFF']:
1535- available_mode.append('2')
1536- valid_options['detector'].append('DELPHES')
1537- options += ['delphes', 'delphes=ON', 'delphes=OFF']
1538- if os.path.exists(pjoin(self.me_dir,'Cards','delphes_card.dat')):
1539- switch['detector'] = 'DELPHES'
1540- elif switch['detector'] not in ['PGS']:
1541- switch['detector'] = 'OFF'
1542- elif valid_options['detector'] == ['OFF']:
1543- switch['detector'] = "Requires a shower"
1544-
1545- # Check switch status for MS/reweight
1546- if not MADEVENT or ('mg5_path' in self.options and self.options['mg5_path']):
1547- available_mode.append('4')
1548- valid_options['madspin'] = ['ON', 'OFF']
1549- if os.path.exists(pjoin(self.me_dir,'Cards','madspin_card.dat')):
1550- switch['madspin'] = 'ON'
1551- else:
1552- switch['madspin'] = 'OFF'
1553- if misc.has_f2py() or self.options['f2py_compiler']:
1554- available_mode.append('5')
1555- valid_options['reweight'] = ['ON', 'OFF']
1556- if os.path.exists(pjoin(self.me_dir,'Cards','reweight_card.dat')):
1557- switch['reweight'] = 'ON'
1558- else:
1559- switch['reweight'] = 'OFF'
1560- else:
1561- switch['reweight'] = 'Not available (requires NumPy/f2py)'
1562-
1563+
1564+ passing_cmd = []
1565 if '-R' in args or '--reweight' in args:
1566- if switch['reweight'] == 'OFF':
1567- switch['reweight'] = 'ON'
1568- elif switch['reweight'] != 'ON':
1569- logger.critical("Cannot run reweight: %s", switch['reweight'])
1570+ passing_cmd.append('reweight=ON')
1571 if '-M' in args or '--madspin' in args:
1572- if switch['madspin'] == 'OFF':
1573- switch['madspin'] = 'ON'
1574- elif switch['madspin'] != 'ON':
1575- logger.critical("Cannot run madspin: %s", switch['reweight'])
1576-
1577- for id, key in enumerate(switch_order):
1578- if len(valid_options[key]) >1:
1579- options += ['%s=%s' % (key, s) for s in valid_options[key]]
1580- options.append(key)
1581- else:
1582- options.append('%s=OFF' % (key))
1583-
1584- options += ['parton'] + sorted(list(set(available_mode)))
1585- options += options_legacy
1586- #options += ['pythia=ON', 'pythia=OFF', 'delphes=ON', 'delphes=OFF', 'pgs=ON', 'pgs=OFF']
1587- #ask the question
1588-
1589- def color(switch_value):
1590- green = '\x1b[32m%s\x1b[0m'
1591- bold = '\x1b[33m%s\x1b[0m'
1592- red = '\x1b[31m%s\x1b[0m'
1593- if switch_value in ['OFF',void,'Requires a shower',
1594- 'Not available (requires NumPy)',
1595- 'Not available yet for this output/process']:
1596- return red%switch_value
1597- elif switch_value in ['ON','MADANALYSIS_4','MADANALYSIS_5',
1598- 'PYTHIA8','PYTHIA6','PGS','DELPHES-ATLAS',
1599- 'DELPHES-CMS','DELPHES', 'EXROOTANALYSIS']:
1600- return green%switch_value
1601- else:
1602- return bold%switch_value
1603-
1604- if mode or not self.force:
1605- answer = ''
1606- while answer not in ['0', 'done', 'auto']:
1607- if mode:
1608- answer = mode
1609- else:
1610- switch_format = " \x1b[31m%i\x1b[0m. %-60s %12s = %s"
1611- question = "The following switches determine which programs are run:\n"
1612- question += '/'+'-'*98+'\\\n'
1613- for id, key in enumerate(switch_order):
1614- question += '| %-115s|\n'%(switch_format%(id+1, description[key], key, color(switch[key])))
1615- question += '\\'+'-'*98+'/\n'
1616- question += ' Either type the switch number (1 to %s) to change its setting,\n' % (id+1)
1617- question += ' Set any switch explicitly (e.g. type \'madspin=ON\' at the prompt)\n'
1618- question += ' Type \'help\' for the list of all valid option\n'
1619- question += ' Type \'0\', \'auto\', \'done\' or just press enter when you are done.\n'
1620- answer = self.ask(question, '0', options, casesensitive=False)
1621- if (answer.isdigit() and answer != '0') or answer in ['shower', 'detector']:
1622- if answer.isdigit():
1623- key = switch_order[int(answer) - 1]
1624- else:
1625- key = answer
1626- for i, opt in enumerate(valid_options[key]):
1627- if opt == switch[key]:
1628- break
1629- i +=1
1630- if i == len(valid_options[key]):
1631- i=0
1632- answer = '%s=%s' % (key, valid_options[key][i])
1633-
1634- if '=' in answer:
1635- key, status = answer.split('=')
1636- key, status = key.lower().strip(), status.upper().strip()
1637-
1638- if key not in switch:
1639- # this means use use outdated switch. Use converter to new syntax
1640- logger.warning("Using old syntax. Please check that we run what you expect.")
1641- if key == "pythia" and status == "ON":
1642- key, status = "shower", "PYTHIA6"
1643- elif key == "pythia" and status == "OFF":
1644- key, status = "shower", "OFF"
1645- elif key == "pgs" and status == "ON":
1646- if switch["detector"] in ["OFF", "PGS"] :
1647- key, status = "detector", "PGS"
1648- else:
1649- key, status = "detector", "DELPHES+PGS"
1650- elif key == "delphes" and status == "ON":
1651- if switch["detector"] in ["OFF", "DELPHES"] :
1652- key, status = "detector", "DELPHES"
1653- else:
1654- key, status = "detector", "DELPHES+PGS"
1655- elif key == "pgs" and status == "OFF":
1656- if switch["detector"] in ["OFF", "PGS"] :
1657- key, status = "detector", "OFF"
1658- elif switch["detector"] == "DELPHES+PGS":
1659- key, status = "detector", "DELPHES"
1660- else:
1661- key, status = "detector", switch['detector']
1662- elif key == "delphes" and status == "OFF":
1663- if switch["detector"] in ["OFF", "DELPHES"] :
1664- key, status = "detector", "OFF"
1665- elif switch["detector"] == "DELPHES+PGS":
1666- key, status = "detector", "PGS"
1667- else:
1668- key, status = "detector", switch['detector']
1669-
1670-
1671- switch[key] = status
1672- if (key, status) in force_switch:
1673- for key2, status2 in force_switch[(key, status)].items():
1674- if isinstance(status2, str):
1675- if switch[key2] not in [status2, void]:
1676- logger.info('For coherence \'%s\' is set to \'%s\''
1677- % (key2, status2), '$MG:color:BLACK')
1678- switch[key2] = status2
1679- else:
1680- if switch[key2] not in status2 + [void]:
1681- logger.info('For coherence \'%s\' is set to \'%s\''
1682- % (key2, status2[0]), '$MG:color:BLACK')
1683- switch[key2] = status2[0]
1684- elif answer in ['0', 'auto', 'done']:
1685- continue
1686- elif answer in ['parton', 'pythia','pgs','madspin','reweight', 'delphes']:
1687- logger.info('pass in %s only mode' % answer, '$MG:color:BLACK')
1688- switch_assign('madspin', 'OFF')
1689- switch_assign('reweight', 'OFF')
1690- if answer == 'parton':
1691- switch_assign('shower', 'OFF')
1692- switch_assign('detector', 'OFF')
1693- elif answer == 'pythia':
1694- switch_assign('shower', 'PYTHIA6')
1695- switch_assign('detector', 'OFF')
1696- elif answer == 'pgs':
1697- switch_assign('shower', 'PYTHIA6')
1698- switch_assign('detector', 'PGS')
1699- elif answer == 'delphes':
1700- switch_assign('shower', 'PYTHIA6')
1701- if switch['shower'] == 'OFF':
1702- switch_assign('shower', 'PYTHIA8')
1703- switch_assign('detector', 'DELPHES')
1704- elif answer == 'madspin':
1705- switch_assign('madspin', 'ON')
1706- switch_assign('shower', 'OFF')
1707- switch_assign('detector', 'OFF')
1708- elif answer == 'reweight':
1709- switch_assign('reweight', 'ON')
1710- switch_assign('shower', 'OFF')
1711- switch_assign('detector', 'OFF')
1712-
1713- if mode:
1714- answer = '0' #mode auto didn't pass here (due to the continue)
1715- else:
1716- answer = 'auto'
1717-
1718+ passing_cmd.append('madspin=ON')
1719+
1720+ switch = self.ask('', '0', [], ask_class = self.action_switcher,
1721+ mode=mode, line_args=args, force=self.force,
1722+ first_cmd=passing_cmd)
1723+
1724+ if 'dynamical' in switch:
1725+ mode = 'auto'
1726+
1727 # Now that we know in which mode we are check that all the card
1728 #exists (copy default if needed)
1729
1730 cards = ['param_card.dat', 'run_card.dat']
1731- if switch['shower'] in ['PY6', 'PYTHIA6']:
1732+ if switch['shower'] == 'Pythia6':
1733 cards.append('pythia_card.dat')
1734- if switch['shower'] in ['PY8', 'PYTHIA8']:
1735+ if switch['shower'] == 'Pythia8':
1736 cards.append('pythia8_card.dat')
1737 if switch['detector'] in ['PGS','DELPHES+PGS']:
1738 cards.append('pgs_card.dat')
1739- if switch['detector'] in ['DELPHES', 'DELPHES+PGS']:
1740+ if switch['detector'] in ['Delphes', 'DELPHES+PGS']:
1741 cards.append('delphes_card.dat')
1742 delphes3 = True
1743 if os.path.exists(pjoin(self.options['delphes_path'], 'data')):
1744@@ -6053,11 +6115,11 @@
1745 cards.append('madspin_card.dat')
1746 if switch['reweight'] == 'ON':
1747 cards.append('reweight_card.dat')
1748- if switch['analysis'] in ['MADANALYSIS_5']:
1749+ if switch['analysis'] in ['MADANALYSIS5']:
1750 cards.append('madanalysis5_parton_card.dat')
1751- if switch['analysis'] in ['MADANALYSIS_5'] and not switch['shower']=='OFF':
1752+ if switch['analysis'] in ['MADANALYSIS5'] and not switch['shower']=='OFF':
1753 cards.append('madanalysis5_hadron_card.dat')
1754- if switch['analysis'] in ['MADANALYSIS_4']:
1755+ if switch['analysis'] in ['MADANALYSIS4']:
1756 cards.append('plot_card.dat')
1757
1758 self.keep_cards(cards)
1759@@ -6069,7 +6131,7 @@
1760 self.check_param_card(pjoin(self.me_dir,'Cards','param_card.dat' ))
1761 return switch
1762
1763- if answer == 'auto':
1764+ if 'dynamical' in switch and switch['dynamical']:
1765 self.ask_edit_cards(cards, plot=False, mode='auto')
1766 else:
1767 self.ask_edit_cards(cards, plot=False)

Subscribers

People subscribed via source and target branches

to all changes: