Package madgraph :: Package interface :: Module loop_interface
[hide private]
[frames] | no frames]

Source Code for Module madgraph.interface.loop_interface

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15  """A user friendly command line interface to access all MadGraph5_aMC@NLO features. 
  16     Uses the cmd package for command interpretation and tab completion. 
  17  """ 
  18   
  19  import os 
  20  import shutil 
  21  import time 
  22  import logging 
  23  import re 
  24   
  25  import madgraph 
  26  from madgraph import MG4DIR, MG5DIR, MadGraph5Error 
  27  import madgraph.interface.madgraph_interface as mg_interface 
  28  import madgraph.interface.extended_cmd as cmd 
  29  import madgraph.interface.launch_ext_program as launch_ext 
  30  import madgraph.interface.extended_cmd as extended_cmd 
  31  import madgraph.core.base_objects as base_objects 
  32  import madgraph.core.diagram_generation as diagram_generation 
  33  import madgraph.loop.loop_diagram_generation as loop_diagram_generation 
  34  import madgraph.loop.loop_base_objects as loop_base_objects 
  35  import madgraph.loop.loop_helas_objects as loop_helas_objects 
  36  import madgraph.core.helas_objects as helas_objects 
  37  import madgraph.iolibs.export_v4 as export_v4 
  38  import madgraph.iolibs.helas_call_writers as helas_call_writers 
  39  import madgraph.iolibs.file_writers as writers 
  40  import madgraph.interface.launch_ext_program as launch_ext 
  41  import madgraph.various.misc as misc 
  42  import madgraph.fks.fks_base as fks_base 
  43  import aloha 
  44   
  45  # Special logger for the Cmd Interface 
  46  logger = logging.getLogger('cmdprint') 
  47   
  48  #useful shortcut 
  49  pjoin = os.path.join 
50 51 -class CheckLoop(mg_interface.CheckValidForCmd):
52
53 - def check_display(self, args):
54 """ Check the arguments of the display diagrams command in the context 55 of the Loop interface.""" 56 57 mg_interface.MadGraphCmd.check_display(self,args) 58 59 if all([not amp['process']['has_born'] for amp in self._curr_amps]): 60 if args[0]=='diagrams' and len(args)>=2 and args[1]=='born': 61 raise self.InvalidCmd("Processes generated do not have born diagrams.") 62 63 if args[0]=='diagrams' and len(args)>=3 and args[1] not in ['born','loop']: 64 raise self.InvalidCmd("Can only display born or loop diagrams, not %s."%args[1])
65
66 - def check_tutorial(self, args):
67 """check the validity of the line""" 68 if len(args) == 0: 69 #this means mg5 tutorial 70 args.append('MadLoop') 71 else: 72 return mg_interface.CheckValidForCmd.check_tutorial(self,args)
73
74 - def check_add(self, args):
75 """ If no model is defined yet, make sure to load the right loop one """ 76 77 if not self._curr_model: 78 pert_coupl_finder = re.compile(r"^(?P<proc>.+)\s*\[\s*((?P<option>\w+)"+ 79 r"\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 80 pert_coupl = pert_coupl_finder.match(' '.join(args)) 81 model_name = 'loop_sm' 82 if pert_coupl: 83 pert_coupls = pert_coupl.group("pertOrders") 84 if "QED" in pert_coupls: 85 model_name = 'loop_qcd_qed_sm' 86 self.do_import('model %s'%model_name) 87 88 mg_interface.MadGraphCmd.check_add(self,args)
89
90 - def check_output(self, args, default='standalone'):
91 """ Check the arguments of the output command in the context 92 of the Loop interface.""" 93 94 mg_interface.MadGraphCmd.check_output(self,args, default=default) 95 96 if self._export_format not in self.supported_ML_format: 97 raise self.InvalidCmd, "not supported format %s" % self._export_format
98 99
100 - def check_launch(self, args, options):
101 """ Further check that only valid options are given to the MadLoop 102 default launcher.""" 103 104 mg_interface.MadGraphCmd.check_launch(self,args,options) 105 if int(options.cluster) != 0 : 106 return self.InvalidCmd, 'MadLoop standalone runs cannot be '+\ 107 'performed on a cluster.' 108 109 if int(options.multicore) != 0 : 110 logger.warning('MadLoop standalone can only run on a single core,'+\ 111 ' so the -m option is ignored.') 112 options.multicore = '0' 113 114 if options.laststep != '' : 115 logger.warning('The -laststep option is only used for Madevent.'+\ 116 'Ignoring this option') 117 options.multicore = '' 118 119 if options.interactive : 120 logger.warning('No interactive mode for MadLoop standalone runs.') 121 options.interactive = False
122
123 -class CheckLoopWeb(mg_interface.CheckValidForCmdWeb, CheckLoop):
124 pass
125
126 -class CompleteLoop(mg_interface.CompleteForCmd):
127
128 - def complete_display(self, text, line, begidx, endidx):
129 "Complete the display command in the context of the Loop interface" 130 131 args = self.split_arg(line[0:begidx]) 132 133 if len(args) == 2 and args[1] == 'diagrams': 134 return self.list_completion(text, ['born', 'loop']) 135 else: 136 return mg_interface.MadGraphCmd.complete_display(self, text, line, 137 begidx, endidx)
138
139 -class HelpLoop(mg_interface.HelpToCmd):
140
141 - def help_display(self):
142 mg_interface.MadGraphCmd.help_display(self) 143 logger.info(" In ML5, after display diagrams, the user can add the option") 144 logger.info(" \"born\" or \"loop\" to display only the corresponding diagrams.")
145
146 147 -class CommonLoopInterface(mg_interface.MadGraphCmd):
148 """ An additional layer between MadGraphInterface and LoopInterface as well 149 as aMCatNLO interface, to put the common feature of these two here.""" 150
151 - def rate_proc_difficulty(self, proc, mode):
152 """ Gives an integer more or less representing the difficulty of the process. 153 For now it is very basic and such that "difficult" processes start at 154 a value of about 35.""" 155 156 def pdg_difficulty(pdg): 157 """ Gives a score from the pdg of a leg to state how it increases the 158 difficulty of the process """ 159 # For now, it is only based on the color charge. One can change that 160 # of course. 161 part=self._curr_model.get_particle(pdg) 162 if abs(part.get_color())==1: 163 return 2 164 elif abs(part.get_color())==3: 165 return 3 166 elif abs(part.get_color())==6: 167 return 4 168 elif abs(part.get_color())==8: 169 return 6
170 171 score = 0 172 for leg in proc.get('legs'): 173 if isinstance(leg,base_objects.MultiLeg): 174 score += max([pdg_difficulty(id) for id in leg['ids']]) 175 # add one if it has more than one particle 176 if len(leg['ids'])>1: 177 score += 1 178 else: 179 score += pdg_difficulty(leg.get('id')) 180 181 # No integration planned right away if only virtual, remove 6 182 if proc['NLO_mode']=='virt': 183 score = score - 6 184 # Only reals, then again remove 6 185 if proc['NLO_mode']=='real': 186 score = score - 6 187 # If tree only then it is easy 188 if proc['NLO_mode']=='tree': 189 return 0 190 return score
191
192 - def do_set(self, line, log=True):
193 """Set the loop optimized output while correctly switching to the 194 Feynman gauge if necessary. 195 """ 196 197 mg_interface.MadGraphCmd.do_set(self,line,log) 198 199 args = self.split_arg(line) 200 self.check_set(args) 201 202 if args[0] == 'gauge' and args[1] == 'unitary' and \ 203 not self.options['gauge']=='unitary' and \ 204 isinstance(self._curr_model,loop_base_objects.LoopModel) and \ 205 not self._curr_model['perturbation_couplings'] in [[],['QCD']]: 206 if log: logger.warning('You will only be able to do tree level and QCD'+\ 207 ' corrections in the unitary gauge.')
208
209 - def proc_validity(self, proc, mode):
210 """ Check that the process or processDefinition describes a process that 211 ML5 can handle. Mode specifies who called the function, 212 typically ML5, ML5_check or aMCatNLO. This allows to relieve some limitation 213 depending on the functionality.""" 214 215 tool = 'MadLoop' if mode.startswith('ML5') else 'aMC@NLO' 216 # The threshold for the triggering of the 'Warning difficult process' 217 # message. 218 difficulty_threshold = 100 219 # Check that we have something 220 if not proc: 221 raise self.InvalidCmd("Empty or wrong format process, please try again.") 222 223 # Check that we have the same number of initial states as 224 # existing processes 225 if self._curr_amps and self._curr_amps[0].get_ninitial() != \ 226 proc.get_ninitial(): 227 raise self.InvalidCmd("Can not mix processes with different number of initial states.") 228 229 # It is partially supported for now if the initial state is not charged 230 # under the gauge group perturbed. 231 # if proc.get_ninitial()==1 and tool=='aMC@NLO': 232 # raise self.InvalidCmd("At this stage %s cannot handle decay process."%tool+\ 233 # "\nIt is however a straight-forward extension which "+\ 234 # "will come out with the next release.") 235 236 # Now all checks should support multi-particle label for loops as well. 237 if isinstance(proc, base_objects.ProcessDefinition) and mode=='ML5': 238 if proc.has_multiparticle_label(): 239 raise self.InvalidCmd( 240 "When running ML5 standalone, multiparticle labels cannot be"+\ 241 " employed.") 242 243 if proc['decay_chains']: 244 raise self.InvalidCmd( 245 "ML5 cannot yet decay a core process including loop corrections.") 246 247 if proc.are_decays_perturbed(): 248 raise self.InvalidCmd( 249 "The processes defining the decay of the core process cannot"+\ 250 " include loop corrections.") 251 252 if not proc['perturbation_couplings'] and mode.startswith('ML5'): 253 raise self.InvalidCmd( 254 "Please perform tree-level generations within default MG5 interface.") 255 if not 'real': 256 if not isinstance(self._curr_model,loop_base_objects.LoopModel) or \ 257 not proc['perturbation_couplings']: 258 raise self.InvalidCmd( 259 "The current model does not allow for loop computations.") 260 261 miss_order = [ p_order for p_order in proc['perturbation_couplings'] \ 262 if p_order not in self._curr_model.get('perturbation_couplings')] 263 if len(miss_order)>0 and not 'real' in mode: 264 raise self.InvalidCmd( 265 "Perturbation orders %s not among"%str(miss_order) + \ 266 " the perturbation orders allowed for by the loop model.") 267 268 if proc['perturbation_couplings'] not in [[],['QCD']]: 269 raise self.InvalidCmd( 270 "The process perturbation coupling orders %s are beyond "+\ 271 "tree level or only QCD corrections. MadLoop can only work"+\ 272 " in the Feynman gauge for these. Please set the gauge to "+\ 273 " Feynman and try again.") 274 275 proc_diff = self.rate_proc_difficulty(proc, mode) 276 logger.debug('Process difficulty estimation: %d'%proc_diff) 277 if proc_diff >= difficulty_threshold: 278 msg = """ 279 The %s you attempt to generate appears to be of challenging difficulty, but it will be tried anyway. If you have successfully studied it with MadGraph5_aMC@NLO, please report it. 280 """ 281 logger.warning(msg%proc.nice_string().replace('Process:','process'))
282
283 - def validate_model(self, loop_type='virtual',coupling_type=['QCD'], stop=True):
284 """ Upgrade the model sm to loop_sm if needed """ 285 286 # Allow to call this function with a string instead of a list of 287 # perturbation orders. 288 if isinstance(coupling_type,str): 289 coupling_type = [coupling_type,] 290 291 if coupling_type!= ['QCD'] and loop_type not in ['virtual','noborn']: 292 c = ' '.join(coupling_type) 293 raise self.InvalidCmd, 'MG5aMC can only handle QCD at NLO accuracy.\n We can however compute loop with [virt=%s].\n We can also compute cross-section for loop-induced processes with [noborn=%s]' % (c,c) 294 295 296 if not isinstance(self._curr_model,loop_base_objects.LoopModel) or \ 297 self._curr_model['perturbation_couplings']==[] or \ 298 any((coupl not in self._curr_model['perturbation_couplings']) \ 299 for coupl in coupling_type): 300 if loop_type.startswith('real') or loop_type == 'LOonly': 301 if loop_type == 'real': 302 logger.info(\ 303 "Beware that real corrections are generated from a tree-level model.") 304 if loop_type == 'real_init' and \ 305 self._curr_model.get('name').split('-')[0]!='sm': 306 logger.info(\ 307 "You are entering aMC@NLO with a model which does not "+\ 308 " support loop corrections.") 309 else: 310 logger.info(\ 311 "The current model %s does not allow to generate"%self._curr_model.get('name')+ 312 " loop corrections of type %s."%str(coupling_type)) 313 model_path = self._curr_model.get('modelpath') 314 model_name = self._curr_model.get('name') 315 if model_name.split('-')[0]=='loop_sm': 316 model_name = model_name[5:] 317 if model_name.split('-')[0]=='sm': 318 # So that we don't load the model twice 319 if not self.options['gauge']=='Feynman' and 'QED' in coupling_type: 320 logger.info('Switch to Feynman gauge because '+\ 321 'model loop_qcd_qed_sm is restricted only to Feynman gauge.') 322 self._curr_model = None 323 mg_interface.MadGraphCmd.do_set(self,'gauge Feynman') 324 if coupling_type == ['QCD',]: 325 add_on = '' 326 elif coupling_type in [['QED'],['QCD','QED']]: 327 add_on = 'qcd_qed_' 328 else: 329 raise MadGraph5Error( 330 "The pertubation coupling cannot be '%s'"\ 331 %str(coupling_type)+" in SM loop processes") 332 333 logger.info("MG5_aMC now loads 'loop_%s%s'."%(add_on,model_name)) 334 335 #import model with correct treatment of the history 336 self.history.move_to_last('generate') 337 last_command = self.history[-1] 338 self.exec_cmd(" import model loop_%s%s" % (add_on,model_name), precmd=True) 339 self.history.append(last_command) 340 elif stop: 341 raise self.InvalidCmd( 342 "The model %s cannot handle loop processes"%model_name) 343 344 if loop_type and not loop_type.startswith('real') and \ 345 not self.options['gauge']=='Feynman' and \ 346 not self._curr_model['perturbation_couplings'] in [[],['QCD']]: 347 if 1 in self._curr_model.get('gauge'): 348 logger.info("Setting gauge to Feynman in order to process all"+\ 349 " possible loop computations available in the model.") 350 mg_interface.MadGraphCmd.do_set(self,'gauge Feynman') 351 else: 352 logger.warning("You will only be able to do tree level and QCD"+\ 353 " corrections with this model because it does not support Feynman gauge.")
354
355 -class LoopInterface(CheckLoop, CompleteLoop, HelpLoop, CommonLoopInterface):
356 357 supported_ML_format = ['standalone', 'standalone_rw', 'matchbox'] 358
359 - def __init__(self, mgme_dir = '', *completekey, **stdin):
360 """ Special init tasks for the Loop Interface """ 361 362 mg_interface.MadGraphCmd.__init__(self, mgme_dir = '', *completekey, **stdin) 363 self.setup()
364
365 - def setup(self):
366 """ Special tasks when switching to this interface """ 367 368 # Refresh all the interface stored value as things like generated 369 # processes and amplitudes are not to be reused in between different 370 # interfaces 371 # Clear history, amplitudes and matrix elements when a model is imported 372 # Remove previous imports, generations and outputs from history 373 self.history.clean(remove_bef_last='import', 374 to_keep=['set','load','import', 'define']) 375 # Reset amplitudes and matrix elements 376 self._done_export=False 377 self._curr_amps = diagram_generation.AmplitudeList() 378 self._curr_matrix_elements = helas_objects.HelasMultiProcess() 379 self._v4_export_formats = [] 380 self._export_formats = [ 'matrix', 'standalone' ] 381 self._nlo_modes_for_completion = ['virt'] 382 self.validate_model() 383 # Set where to look for CutTools installation. 384 # In further versions, it will be set in the same manner as _mgme_dir so that 385 # the user can chose its own CutTools distribution. 386 self._cuttools_dir=str(os.path.join(self._mgme_dir,'vendor','CutTools')) 387 if not os.path.isdir(os.path.join(self._cuttools_dir, 'src','cts')): 388 logger.warning(('Warning: Directory %s is not a valid CutTools directory.'+\ 389 'Using default CutTools instead.') % \ 390 self._cuttools_dir) 391 self._cuttools_dir=str(os.path.join(self._mgme_dir,'vendor','CutTools')) 392 # Set where to look for IREGI installation 393 self._iregi_dir=str(os.path.join(self._mgme_dir,'vendor','IREGI','src')) 394 if not os.path.isdir(self._iregi_dir): 395 logger.warning(('Warning: Directory %s is not a valid IREGI directory.'+\ 396 'Using default IREGI instead.')%\ 397 self._iregi_dir) 398 self._iregi_dir=str(os.path.join(self._mgme_dir,'vendor','IREGI','src'))
399
400 - def do_display(self,line, *argss, **opt):
401 """ Display born or loop diagrams, otherwise refer to the default display 402 command """ 403 404 args = self.split_arg(line) 405 #check the validity of the arguments 406 self.check_display(args) 407 408 if args[0]=='diagrams': 409 if len(args)>=2 and args[1] in ['loop','born']: 410 self.draw(' '.join(args[2:]),args[1]) 411 else: 412 self.draw(' '.join(args[1:]),'all') 413 else: 414 mg_interface.MadGraphCmd.do_display(self,line,*argss,**opt)
415
416 - def do_output(self, line):
417 """Main commands:Initialize a new Template or reinitialize one""" 418 419 args = self.split_arg(line) 420 # Check Argument validity 421 self.check_output(args) 422 423 noclean = '-noclean' in args 424 force = '-f' in args 425 nojpeg = '-nojpeg' in args 426 main_file_name = "" 427 try: 428 main_file_name = args[args.index('-name') + 1] 429 except Exception: 430 pass 431 line_options = dict(arg[2:].split('=') for arg in args if arg.startswith('--') and '=' in arg) 432 433 # Whatever the format we always output the quadruple precision routines 434 # to allow for curing possible unstable points. 435 aloha_original_quad_mode = aloha.mp_precision 436 aloha.mp_precision = True 437 438 if self._export_format not in self.supported_ML_format: 439 raise self.InvalidCmd('ML5 only support "%s" as export format.' % \ 440 ''.join(self.supported_ML_format)) 441 442 if not os.path.isdir(self._export_dir) and self._export_format in ['matrix']: 443 raise self.InvalidCmd('Specified export directory %s does not exist.'\ 444 %str(self._export_dir)) 445 446 if not force and not noclean and os.path.isdir(self._export_dir)\ 447 and self._export_format.startswith('standalone'): 448 # Don't ask if user already specified force or noclean 449 logger.info('INFO: directory %s already exists.' % self._export_dir) 450 logger.info('If you continue this directory will be cleaned') 451 answer = self.ask('Do you want to continue?', 'y', ['y','n']) 452 if answer != 'y': 453 raise self.InvalidCmd('Stopped by user request') 454 else: 455 try: 456 shutil.rmtree(self._export_dir) 457 except OSError: 458 raise self.InvalidCmd('Could not remove directory %s.'\ 459 %str(self._export_dir)) 460 461 if self._export_format.startswith('standalone'): 462 output_type = 'madloop' 463 elif self._export_format == 'matchbox': 464 output_type = 'madloop_matchbox' 465 466 self._curr_exporter = export_v4.ExportV4Factory(self, \ 467 noclean, output_type=output_type, group_subprocesses=False, 468 cmd_options=line_options) 469 470 if self._export_format in ['standalone', 'matchbox']: 471 self._curr_exporter.copy_template(self._curr_model) 472 473 if self._export_format == "standalone_rw": 474 self._export_format = "standalone" 475 self._curr_exporter.copy_template(self._curr_model) 476 self._export_format = "standalone_rw" 477 478 # Reset _done_export, since we have new directory 479 self._done_export = False 480 481 # Perform export and finalize right away 482 self.ML5export(nojpeg, main_file_name) 483 484 # Automatically run finalize 485 self.ML5finalize(nojpeg) 486 487 # Remember that we have done export 488 self._done_export = (self._export_dir, self._export_format) 489 490 # Reset _export_dir, so we don't overwrite by mistake later 491 self._export_dir = None 492 493 # Put aloha back in its original mode. 494 aloha.mp_precision = aloha_original_quad_mode
495 496
497 - def install_reduction_library(self):
498 """Code to install the reduction library if needed""" 499 500 opt = self.options 501 502 # Check if first time: 503 if (opt['ninja'] is None) or (os.path.isfile(pjoin(MG5DIR, opt['ninja'],'libninja.a'))): 504 return 505 506 logger.info("First output using loop matrix-elements has been detected. Now asking for loop reduction:", '$MG:BOLD') 507 to_install = self.ask('install', '0', ask_class=AskLoopInstaller, timeout=300, 508 path_msg=' ') 509 510 511 for key, value in to_install.items(): 512 if key in ['cuttools', 'iregi']: 513 if os.path.sep not in value: 514 continue 515 import madgraph.iolibs.files as files 516 if key == 'cuttools': 517 if os.path.exists(pjoin(value, 'includects')): 518 path = pjoin(value, 'includects') 519 elif os.path.exists(pjoin(value, 'CutTools','includects')): 520 path = pjoin(value, 'CutTools', 'includects') 521 elif os.path.exists(pjoin(value, 'vendor','CutTools','includects')): 522 path = pjoin(value, 'vendor','CutTools', 'includects') 523 else: 524 logger.warning('invalid path for cuttools import') 525 continue 526 527 target = pjoin(MG5DIR,'vendor','CutTools','includects') 528 if not os.path.exists(target): 529 os.mkdir(target) 530 files.cp(pjoin(path,'libcts.a'), target) 531 files.cp(pjoin(path,'mpmodule.mod'), target, log=True) 532 if os.path.exists(pjoin(path,'compiler_version.log')): 533 files.cp(pjoin(path,'compiler_version.log'), target) 534 535 if key == 'iregi': 536 if os.path.exists(pjoin(value, 'src','IREGI4ML5_interface.f90')): 537 path = pjoin(value, 'src') 538 elif os.path.exists(pjoin(value, 'IREGI','src','IREGI4ML5_interface.f90')): 539 path = pjoin(value, 'IREGI', 'src') 540 elif os.path.exists(pjoin(value, 'vendor','IREGI','src','IREGI4ML5_interface.f90')): 541 path = pjoin(value, 'vendor', 'IREGI', 'src') 542 else: 543 logger.warning('invalid path for IREGI import') 544 continue 545 546 target = pjoin(MG5DIR,'vendor','IREGI','src') 547 files.cp(pjoin(path,'libiregi.a'), target, log=True) 548 elif value == 'local': 549 ## LOCAL INSTALLATION OF NINJA/COLLIER 550 logger.info( 551 """MG5aMC will now install the loop reduction tool '%(p)s' from the local offline installer. 552 Use the command 'install $(p)s' if you want to update to the latest online version. 553 This installation can take some time but only needs to be performed once.""" %{'p': key},'$MG:color:GREEN') 554 additional_options = ['--ninja_tarball=%s'%pjoin(MG5DIR,'vendor','%s.tar.gz' % key)] 555 if key == 'ninja': 556 additional_options.append('--oneloop_tarball=%s'%pjoin(MG5DIR,'vendor','oneloop.tar.gz')) 557 558 try: 559 self.do_install(key,paths={'HEPToolsInstaller': 560 pjoin(MG5DIR,'vendor','OfflineHEPToolsInstaller.tar.gz')}, 561 additional_options=additional_options) 562 except self.InvalidCmd: 563 logger.warning( 564 """The offline installation of %(p)s was unsuccessful, and MG5aMC disabled it. 565 In the future, if you want to reactivate Ninja, you can do so by re-attempting 566 its online installation with the command 'install %(p)s' or install it on your 567 own and set the path to its library in the MG5aMC option '%(p)s'.""" % {'p': key}) 568 self.exec_cmd("set %s ''" % key) 569 self.exec_cmd('save options %s' % key) 570 571 # ONLINE INSTALLATION 572 elif value == 'install': 573 prog = {'pjfry': 'PJFry', 'golem': 'Golem95'} 574 if key in prog: 575 self.exec_cmd('install %s' % prog[key]) 576 else: 577 self.exec_cmd('install %s' % key) 578 # Not install 579 elif value == 'off': 580 self.exec_cmd("set %s ''" % key) 581 self.exec_cmd('save options %s' % key) 582 else: 583 self.exec_cmd("set %s %s" % (key,value)) 584 self.exec_cmd('save options %s' % key)
585 586 587 588 # Export a matrix element
589 - def ML5export(self, nojpeg = False, main_file_name = ""):
590 """Export a generated amplitude to file""" 591 592 if not self._curr_helas_model: 593 self._curr_helas_model = helas_call_writers.FortranUFOHelasCallWriter(self._curr_model) 594 def generate_matrix_elements(self): 595 """Helper function to generate the matrix elements before exporting""" 596 597 # Sort amplitudes according to number of diagrams, 598 # to get most efficient multichannel output 599 self._curr_amps.sort(lambda a1, a2: a2.get_number_of_diagrams() - \ 600 a1.get_number_of_diagrams()) 601 602 cpu_time1 = time.time() 603 ndiags = 0 604 if not self._curr_matrix_elements.get_matrix_elements(): 605 self._curr_matrix_elements = \ 606 loop_helas_objects.LoopHelasProcess(self._curr_amps, 607 optimized_output = self.options['loop_optimized_output']) 608 ndiags = sum([len(me.get('diagrams')) for \ 609 me in self._curr_matrix_elements.\ 610 get_matrix_elements()]) 611 612 # assign a unique id number to all process 613 uid = 0 614 id_list = set() # the id needs also to be different to ensure that 615 # all the prefix are different which allows to have 616 # a unique library 617 for me in self._curr_matrix_elements.get_matrix_elements(): 618 uid += 1 # update the identification number 619 me.get('processes')[0].set('uid', uid) 620 if me.get('processes')[0].get('id') in id_list: 621 me.get('processes')[0].set('id', uid) 622 id_list.add(me.get('processes')[0].get('id')) 623 624 cpu_time2 = time.time() 625 return ndiags, cpu_time2 - cpu_time1
626 627 # Start of the actual routine 628 ndiags, cpu_time = generate_matrix_elements(self) 629 630 calls = 0 631 632 path = self._export_dir 633 if self._export_format in self.supported_ML_format: 634 path = pjoin(path, 'SubProcesses') 635 636 cpu_time1 = time.time() 637 638 # Pick out the matrix elements in a list 639 matrix_elements = \ 640 self._curr_matrix_elements.get_matrix_elements() 641 642 # Fortran MadGraph5_aMC@NLO Standalone 643 if self._export_format in self.supported_ML_format: 644 for unique_id, me in enumerate(matrix_elements): 645 calls = calls + \ 646 self._curr_exporter.generate_subprocess_directory(\ 647 me, self._curr_helas_model) 648 # If all ME's do not share the same maximum loop vertex rank and the 649 # same loop maximum wavefunction size, we need to set the maximum 650 # in coef_specs.inc of the HELAS Source. The SubProcesses/P* directory 651 # all link this file, so it should be properly propagated 652 if self.options['loop_optimized_output'] and len(matrix_elements)>1: 653 max_lwfspins = [m.get_max_loop_particle_spin() for m in \ 654 matrix_elements] 655 max_loop_vert_ranks = [me.get_max_loop_vertex_rank() for me in \ 656 matrix_elements] 657 if len(set(max_lwfspins))>1 or len(set(max_loop_vert_ranks))>1: 658 self._curr_exporter.fix_coef_specs(max(max_lwfspins),\ 659 max(max_loop_vert_ranks)) 660 661 # Just the matrix.f files 662 if self._export_format == 'matrix': 663 for me in matrix_elements: 664 filename = pjoin(path, 'matrix_' + \ 665 me.get('processes')[0].shell_string() + ".f") 666 if os.path.isfile(filename): 667 logger.warning("Overwriting existing file %s" % filename) 668 else: 669 logger.info("Creating new file %s" % filename) 670 calls = calls + self._curr_exporter.write_matrix_element_v4(\ 671 writers.FortranWriter(filename),\ 672 me, self._curr_helas_model) 673 674 cpu_time2 = time.time() - cpu_time1 675 676 logger.info(("Generated helas calls for %d subprocesses " + \ 677 "(%d diagrams) in %0.3f s") % \ 678 (len(matrix_elements), 679 ndiags, cpu_time)) 680 681 if calls: 682 if "cpu_time2" in locals(): 683 logger.info("Wrote files for %d OPP calls in %0.3f s" % \ 684 (calls, cpu_time2)) 685 else: 686 logger.info("Wrote files for %d OPP calls" % \ 687 (calls)) 688 689 # Replace the amplitudes with the actual amplitudes from the 690 # matrix elements, which allows proper diagram drawing also of 691 # decay chain processes 692 self._curr_amps = diagram_generation.AmplitudeList(\ 693 [me.get('base_amplitude') for me in \ 694 matrix_elements])
695
696 - def ML5finalize(self, nojpeg, online = False):
697 """Copy necessary sources and output the ps representation of 698 the diagrams, if needed""" 699 700 if self._export_format in self.supported_ML_format: 701 logger.info('Export UFO model to MG4 format') 702 # wanted_lorentz are the lorentz structures which are 703 # actually used in the wavefunctions and amplitudes in 704 # these processes 705 wanted_lorentz = self._curr_matrix_elements.get_used_lorentz() 706 wanted_couplings = self._curr_matrix_elements.get_used_couplings() 707 # For a unique output of multiple type of exporter model information 708 # are save in memory 709 if hasattr(self, 'previous_lorentz'): 710 wanted_lorentz = list(set(self.previous_lorentz + wanted_lorentz)) 711 wanted_couplings = list(set(self.previous_couplings + wanted_couplings)) 712 del self.previous_lorentz 713 del self.previous_couplings 714 715 self._curr_exporter.convert_model(self._curr_model, 716 wanted_lorentz, 717 wanted_couplings) 718 719 if self._export_format in self.supported_ML_format: 720 flags = [] 721 if nojpeg: 722 flags.append('nojpeg') 723 if online: 724 flags.append('online') 725 726 self._curr_exporter.finalize( \ 727 self._curr_matrix_elements, 728 self.history, 729 self.options, 730 flags) 731 732 if self._export_format in self.supported_ML_format: 733 logger.info('Output to directory ' + self._export_dir + ' done.')
734
735 - def do_launch(self, line, *args,**opt):
736 """Main commands: Check that the type of launch is fine before proceeding with the 737 mother function. """ 738 739 args = self.split_arg(line) 740 # check argument validity and normalise argument 741 (options, args) = mg_interface._launch_parser.parse_args(args) 742 743 self.check_launch(args, options) 744 745 if not args[0].startswith('standalone'): 746 raise self.InvalidCmd('ML5 can only launch standalone runs.') 747 748 start_cwd = os.getcwd() 749 options = options.__dict__ 750 # args is now MODE PATH 751 752 ext_program = launch_ext.MadLoopLauncher(self, args[1], \ 753 options=self.options, **options) 754 ext_program.run() 755 os.chdir(start_cwd) #ensure to go to the initial path
756
757 - def do_check(self, line, *args,**opt):
758 """Check a given process or set of processes""" 759 760 argss = self.split_arg(line, *args,**opt) 761 # Check args validity 762 perturbation_couplings_pattern = \ 763 re.compile("^(?P<proc>.+)\s*\[\s*((?P<option>\w+)\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 764 perturbation_couplings_re = perturbation_couplings_pattern.match(line) 765 perturbation_couplings="" 766 if perturbation_couplings_re: 767 perturbation_couplings = perturbation_couplings_re.group("pertOrders") 768 QED_found=re.search("QED",perturbation_couplings) 769 if QED_found: 770 self.validate_model(coupling_type='QED') 771 else: 772 self.validate_model() 773 774 param_card = self.check_check(argss) 775 reuse = argss[1]=="-reuse" 776 argss = argss[:1]+argss[2:] 777 # For the stability check the user can specify the statistics (i.e 778 # number of trial PS points) as a second argument 779 if argss[0] in ['stability', 'profile']: 780 stab_statistics = int(argss[1]) 781 argss = argss[:1]+argss[2:] 782 # Remove the extra options 783 i=-1 784 while argss[i].startswith('--'): 785 i=i-1 786 # Now make sure the process is acceptable 787 proc = " ".join(argss[1:i+1]) 788 myprocdef = self.extract_process(proc) 789 self.proc_validity(myprocdef,'ML5_check_cms' if argss[0]=='cms' else \ 790 'ML5_check') 791 792 return mg_interface.MadGraphCmd.do_check(self, line, *args,**opt)
793
794 - def do_add(self, line, *args,**opt):
795 """Generate an amplitude for a given process and add to 796 existing amplitudes 797 """ 798 args = self.split_arg(line) 799 # Check the validity of the arguments 800 self.check_add(args) 801 perturbation_couplings_pattern = \ 802 re.compile("^(?P<proc>.+)\s*\[\s*((?P<option>\w+)\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 803 perturbation_couplings_re = perturbation_couplings_pattern.match(line) 804 perturbation_couplings="" 805 if perturbation_couplings_re: 806 perturbation_couplings = perturbation_couplings_re.group("pertOrders") 807 QED_found=re.search('QED',perturbation_couplings) 808 if QED_found: 809 self.validate_model(coupling_type='QED') 810 else: 811 self.validate_model() 812 813 loop_filter=None 814 if args[0] == 'process': 815 816 # Extract potential loop_filter 817 for arg in args: 818 if arg.startswith('--loop_filter='): 819 loop_filter = arg[14:] 820 if not isinstance(self, extended_cmd.CmdShell): 821 raise self.InvalidCmd, "loop_filter is not allowed in web mode" 822 args = [a for a in args if not a.startswith('--loop_filter=')] 823 824 # Rejoin line 825 line = ' '.join(args[1:]) 826 827 # store the first process (for the perl script) 828 if not self._generate_info: 829 self._generate_info = line 830 831 # Reset Helas matrix elements 832 self._curr_matrix_elements = helas_objects.HelasMultiProcess() 833 834 # Extract process from process definition 835 myprocdef = self.extract_process(line) 836 # hack for multiprocess: 837 if myprocdef.has_multiparticle_label(): 838 # split it in a loop 839 succes, failed = 0, 0 840 for base_proc in myprocdef: 841 command = "add process %s" % base_proc.nice_string(prefix=False, print_weighted=True) 842 if '@' not in command: 843 command += ' @%s' % base_proc.get('id') 844 try: 845 self.exec_cmd(command) 846 succes += 1 847 except Exception: 848 failed +=1 849 logger.info("%s/%s processes succeeded" % (succes, failed+succes)) 850 if succes == 0: 851 raise 852 else: 853 return 854 855 856 # If it is a process for MadLoop standalone, make sure it has a 857 # unique ID. It is important for building a BLHA library which 858 # contains unique entry point for each process generated. 859 #all_ids = [amp.get('process').get('id') for amp in self._curr_amps] 860 #if myprocdef.get('id') in all_ids: 861 # myprocdef.set('id',max(all_ids)+1) 862 #This is ensure at the output stage! by checking that every output have 863 # a different id => No need here. 864 865 self.proc_validity(myprocdef,'ML5') 866 867 cpu_time1 = time.time() 868 869 # Decide here wether one needs a LoopMultiProcess or a MultiProcess 870 multiprocessclass=None 871 if myprocdef['perturbation_couplings']!=[]: 872 multiprocessclass=loop_diagram_generation.LoopMultiProcess 873 else: 874 multiprocessclass=diagram_generation.MultiProcess 875 876 myproc = multiprocessclass(myprocdef, collect_mirror_procs = False, 877 ignore_six_quark_processes = False, 878 loop_filter = loop_filter) 879 880 for amp in myproc.get('amplitudes'): 881 if amp not in self._curr_amps: 882 self._curr_amps.append(amp) 883 else: 884 warning = "Warning: Already in processes:\n%s" % \ 885 amp.nice_string_processes() 886 logger.warning(warning) 887 888 # Reset _done_export, since we have new process 889 self._done_export = False 890 891 cpu_time2 = time.time() 892 893 ndiags = sum([len(amp.get('loop_diagrams')) for \ 894 amp in myproc.get('amplitudes')]) 895 logger.info("Process generated in %0.3f s" % \ 896 (cpu_time2 - cpu_time1))
897
898 -class LoopInterfaceWeb(mg_interface.CheckValidForCmdWeb, LoopInterface):
899 pass
900
901 902 -class AskLoopInstaller(cmd.OneLinePathCompletion):
903 904 local_installer = ['ninja', 'collier'] 905 required = ['cuttools', 'iregi'] 906 order = ['cuttools', 'iregi', 'ninja', 'collier', 'golem', 'pjfry'] 907 bypassed = ['pjfry'] 908 909 @property
910 - def answer(self):
911 return self.code
912 913
914 - def __init__(self, question, *args, **opts):
915 916 import urllib2 917 try: 918 response=urllib2.urlopen('http://madgraph.phys.ucl.ac.be/F1.html', timeout=3) 919 self.online=True 920 except urllib2.URLError as err: 921 self.online=False 922 923 self.code = {'ninja': 'install', 924 'collier': 'install', 925 'golem': 'off', 926 'pjfry':'off', 927 'cuttools': 'required', 928 'iregi': 'required'} 929 if not self.online: 930 self.code['ninja'] = 'local' 931 self.code['collier'] = 'local' 932 self.code['pjfry'] = 'fail' 933 self.code['golem'] = 'fail' 934 if not misc.which('cmake'): 935 self.code['collier'] = 'off' 936 937 #check if some partial installation is already done. 938 if 'mother_interface' in opts: 939 mother = opts['mother_interface'] 940 if 'heptools_install_dir' in mother.options: 941 install_dir1 = mother.options['heptools_install_dir'] 942 install_dir2 = mother.options['heptools_install_dir'] 943 if os.path.exists(pjoin(install_dir1, 'CutTools')): 944 self.code['cuttools'] = mother.options['heptools_install_dir'] 945 if os.path.exists(pjoin(install_dir1, 'IREGI')): 946 self.code['iregi'] = mother.options['heptools_install_dir'] 947 else: 948 install_dir1 = pjoin(MG5DIR, 'HEPTools') 949 install_dir2 = MG5DIR 950 if os.path.exists(pjoin(install_dir1, 'collier')): 951 self.code['collier'] = pjoin(install_dir1, 'collier') 952 if os.path.exists(pjoin(install_dir2, 'PJFry','bin','qd-config')): 953 self.code['collier'] = pjoin(install_dir2, 'PJFry') 954 if os.path.exists(pjoin(install_dir2, 'golem95')): 955 self.code['collier'] = pjoin(install_dir2, 'golem95') 956 957 # 1. create the question 958 question, allowed_answer = self.create_question(first=True) 959 960 opts['allow_arg'] = allowed_answer 961 962 cmd.OneLinePathCompletion.__init__(self, question, *args, **opts)
963 964
965 - def create_question(self, first = False):
966 """ """ 967 968 question = "For loop computations, MadLoop requires dedicated tools to"+\ 969 " perform the reduction of loop Feynman diagrams using OPP-based and/or TIR approaches.\n"+\ 970 "\nWhich one do you want to install? (this needs to be done only once)\n" 971 972 allowed_answer = set(['0','done']) 973 974 descript = {'cuttools': ['cuttools','(OPP)','[0711.3596]'], 975 'iregi': ['iregi','(TIR)','[1405.0301]'], 976 'ninja': ['ninja','(OPP)','[1403.1229]'], 977 'pjfry': ['pjfry','(TIR)','[1112.0500]'], 978 'golem': ['golem','(TIR)','[0807.0605]'], 979 'collier': ['collier','(TIR)','[1604.06792]']} 980 981 982 status = {'off': '%(start_red)sdo not install%(stop)s', 983 'install': '%(start_green)swill be installed %(stop)s', 984 'local': '%(start_green)swill be installed %(stop)s(offline installation from local repository)', 985 'fail': 'not available without internet connection', 986 'required': 'will be installed (required)'} 987 988 for i,key in enumerate(self.order,1): 989 if key in self.bypassed and self.code[key] == 'off': 990 continue 991 if os.path.sep not in self.code[key]: 992 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s%s\n' % \ 993 tuple([i,]+descript[key]+[status[self.code[key]],]+\ 994 ['(recommended)' if key in ['ninja','collier'] and self.code[key] in ['install'] else '']) 995 else: 996 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s\n' % tuple([i,]+descript[key]+[self.code[key],]) 997 if key in self.required: 998 continue 999 allowed_answer.update([str(i), key]) 1000 if key in self.local_installer: 1001 allowed_answer.update(['key=local','key=off']) 1002 if self.online: 1003 allowed_answer.update(['key=on','key=install', 'key=off']) 1004 1005 question += "You can:\n -> hit 'enter' to proceed\n -> type a number to cycle its options\n -> enter the following command:\n"+\ 1006 ' %(start_blue)s{tool_name}%(stop)s [%(start_blue)sinstall%(stop)s|%(start_blue)snoinstall%(stop)s|'+\ 1007 '%(start_blue)s{prefixed_installation_path}%(stop)s]\n' 1008 if first: 1009 question += '\n%(start_bold)s%(start_red)sIf you are unsure about what this question means, just type enter to proceed. %(stop)s' 1010 1011 question = question % {'start_green' : '\033[92m', 1012 'start_red' : '\033[91m', 1013 'start_blue' : '\033[34m', 1014 'stop': '\033[0m', 1015 'start_bold':'\033[1m', 1016 } 1017 return question, allowed_answer
1018
1019 - def default(self, line):
1020 """Default action if line is not recognized""" 1021 1022 line = line.strip() 1023 args = line.split() 1024 1025 if line in ['0', 'done','','EOF']: 1026 self.value = 'done' 1027 return self.answer 1028 self.value = 'repeat' 1029 if args: 1030 if len(args) ==1 and '=' in args[0]: 1031 args = args[0].split('=') 1032 args[0] = args[0].lower() 1033 if len(args) == 1: 1034 # loop over the possibility 1035 if args[0].isdigit(): 1036 if len(self.order) < int(args[0]): 1037 logger.warning('Invalid integer %s. Please Retry' % args[0]) 1038 return 1039 args[0] = self.order[int(args[0])-1] 1040 key = args[0] 1041 if key in self.code: 1042 if self.code[key] in ['off']: 1043 if self.online: 1044 self.code[key] = 'install' 1045 elif key in self.local_installer: 1046 self.code[key] = 'local' 1047 elif self.code[key] == 'install': 1048 if key in self.local_installer: 1049 self.code[key] = 'local' 1050 else: 1051 self.code[key] = 'off' 1052 elif self.code[key] == 'local': 1053 self.code[key] = 'off' 1054 else: 1055 logger.warning('Unknown entry \'%s\'. Please retry' % key) 1056 return 1057 elif len(args) == 2: 1058 key = args[0] 1059 if key not in self.code: 1060 logger.warning('unknown %s type of entry. Bypass command.') 1061 return 1062 if os.path.sep not in args[1]: 1063 value = args[1].lower() 1064 if value in ['off', 'not','noinstall']: 1065 self.code[key] = 'off' 1066 elif value in ['on', 'install']: 1067 if self.online: 1068 self.code[key] = 'install' 1069 elif key in self.local_installer: 1070 self.code[key] = 'local' 1071 else: 1072 logger.warning('offline installer not available for %s', key) 1073 self.code[key] = 'off' 1074 elif value in ['local']: 1075 if key in self.local_installer: 1076 self.code[key] = 'local' 1077 else: 1078 logger.warning('offline installer not available for %s', key) 1079 self.code[key] = 'off' 1080 else: 1081 self.code[key] = args[1] 1082 else: 1083 self.value = 0 1084 self.question,self.allow_arg = self.create_question() 1085 return self.answer
1086
1087 - def apply_name(self, name, line):
1088 1089 if line.startswith('='): 1090 line = line[1:] 1091 return self.default('%s %s' % (name,line))
1092 1093 1094 do_ninja = lambda self,line : self.apply_name('ninja', line) 1095 do_pjfry = lambda self,line : self.apply_name('pjfry', line) 1096 do_collier = lambda self,line : self.apply_name('collier', line) 1097 do_golem = lambda self,line : self.apply_name('golem', line) 1098 do_cuttools = lambda self,line : self.apply_name('cuttools', line) 1099 do_iregi = lambda self,line : self.apply_name('iregi', line) 1100 1101
1102 - def complete_prog(self, text, line, begidx, endidx, formatting=True):
1103 1104 if os.path.sep in line: 1105 args = line[0:begidx].split() 1106 if args[-1].endswith(os.path.sep): 1107 return self.path_completion(text, 1108 pjoin(*[a for a in args if a.endswith(os.path.sep)]), 1109 only_dirs = True) 1110 else: 1111 return self.path_completion(text, '.', only_dirs = True) 1112 else: 1113 return self.list_completion(text, ['install', 'noinstall', 'local'], line)
1114 1115 complete_ninja = complete_prog 1116 complete_pjfry = complete_prog 1117 complete_collier = complete_prog 1118 complete_golem = complete_prog 1119 complete_cuttools = complete_prog 1120 complete_iregi = complete_prog
1121