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 432 # Whatever the format we always output the quadruple precision routines 433 # to allow for curing possible unstable points. 434 aloha_original_quad_mode = aloha.mp_precision 435 aloha.mp_precision = True 436 437 if self._export_format not in self.supported_ML_format: 438 raise self.InvalidCmd('ML5 only support "%s" as export format.' % \ 439 ''.join(self.supported_ML_format)) 440 441 if not os.path.isdir(self._export_dir) and self._export_format in ['matrix']: 442 raise self.InvalidCmd('Specified export directory %s does not exist.'\ 443 %str(self._export_dir)) 444 445 if not force and not noclean and os.path.isdir(self._export_dir)\ 446 and self._export_format.startswith('standalone'): 447 # Don't ask if user already specified force or noclean 448 logger.info('INFO: directory %s already exists.' % self._export_dir) 449 logger.info('If you continue this directory will be cleaned') 450 answer = self.ask('Do you want to continue?', 'y', ['y','n']) 451 if answer != 'y': 452 raise self.InvalidCmd('Stopped by user request') 453 else: 454 try: 455 shutil.rmtree(self._export_dir) 456 except OSError: 457 raise self.InvalidCmd('Could not remove directory %s.'\ 458 %str(self._export_dir)) 459 460 if self._export_format.startswith('standalone'): 461 output_type = 'madloop' 462 elif self._export_format == 'matchbox': 463 output_type = 'madloop_matchbox' 464 465 self._curr_exporter = export_v4.ExportV4Factory(self, \ 466 noclean, output_type=output_type, group_subprocesses=False) 467 468 if self._export_format in ['standalone', 'matchbox']: 469 self._curr_exporter.copy_template(self._curr_model) 470 471 if self._export_format == "standalone_rw": 472 self._export_format = "standalone" 473 self._curr_exporter.copy_template(self._curr_model) 474 self._export_format = "standalone_rw" 475 476 # Reset _done_export, since we have new directory 477 self._done_export = False 478 479 # Perform export and finalize right away 480 self.ML5export(nojpeg, main_file_name) 481 482 # Automatically run finalize 483 self.ML5finalize(nojpeg) 484 485 # Remember that we have done export 486 self._done_export = (self._export_dir, self._export_format) 487 488 # Reset _export_dir, so we don't overwrite by mistake later 489 self._export_dir = None 490 491 # Put aloha back in its original mode. 492 aloha.mp_precision = aloha_original_quad_mode
493 494
495 - def install_reduction_library(self):
496 """Code to install the reduction library if needed""" 497 498 opt = self.options 499 500 # Check if first time: 501 if (opt['ninja'] is None) or (os.path.isfile(pjoin(MG5DIR, opt['ninja'],'libninja.a'))): 502 return 503 504 logger.info("First output using loop matrix-elements has been detected. Now asking for loop reduction:", '$MG:color:BLACK') 505 to_install = self.ask('install', '0', ask_class=AskLoopInstaller, timeout=300, 506 path_msg=' ') 507 508 509 for key, value in to_install.items(): 510 if key in ['cuttools', 'iregi']: 511 if os.path.sep not in value: 512 continue 513 import madgraph.iolibs.files as files 514 if key == 'cuttools': 515 if os.path.exists(pjoin(value, 'includects')): 516 path = pjoin(value, 'includects') 517 elif os.path.exists(pjoin(value, 'CutTools','includects')): 518 path = pjoin(value, 'CutTools', 'includects') 519 elif os.path.exists(pjoin(value, 'vendor','CutTools','includects')): 520 path = pjoin(value, 'vendor','CutTools', 'includects') 521 else: 522 logger.warning('invalid path for cuttools import') 523 continue 524 525 target = pjoin(MG5DIR,'vendor','CutTools','includects') 526 if not os.path.exists(target): 527 os.mkdir(target) 528 files.cp(pjoin(path,'libcts.a'), target) 529 files.cp(pjoin(path,'mpmodule.mod'), target, log=True) 530 if os.path.exists(pjoin(path,'compiler_version.log')): 531 files.cp(pjoin(path,'compiler_version.log'), target) 532 533 if key == 'iregi': 534 if os.path.exists(pjoin(value, 'src','IREGI4ML5_interface.f90')): 535 path = pjoin(value, 'src') 536 elif os.path.exists(pjoin(value, 'IREGI','src','IREGI4ML5_interface.f90')): 537 path = pjoin(value, 'IREGI', 'src') 538 elif os.path.exists(pjoin(value, 'vendor','IREGI','src','IREGI4ML5_interface.f90')): 539 path = pjoin(value, 'vendor', 'IREGI', 'src') 540 else: 541 logger.warning('invalid path for IREGI import') 542 continue 543 544 target = pjoin(MG5DIR,'vendor','IREGI','src') 545 files.cp(pjoin(path,'libiregi.a'), target, log=True) 546 elif value == 'local': 547 ## LOCAL INSTALLATION OF NINJA/COLLIER 548 logger.info( 549 """MG5aMC will now install the loop reduction tool '%(p)s' from the local offline installer. 550 Use the command 'install $(p)s' if you want to update to the latest online version. 551 This installation can take some time but only needs to be performed once.""" %{'p': key},'$MG:color:GREEN') 552 additional_options = ['--ninja_tarball=%s'%pjoin(MG5DIR,'vendor','%s.tar.gz' % key)] 553 if key == 'ninja': 554 additional_options.append('--oneloop_tarball=%s'%pjoin(MG5DIR,'vendor','oneloop.tar.gz')) 555 556 try: 557 self.do_install(key,paths={'HEPToolsInstaller': 558 pjoin(MG5DIR,'vendor','OfflineHEPToolsInstaller.tar.gz')}, 559 additional_options=additional_options) 560 except self.InvalidCmd: 561 logger.warning( 562 """The offline installation of %(p)s was unsuccessful, and MG5aMC disabled it. 563 In the future, if you want to reactivate Ninja, you can do so by re-attempting 564 its online installation with the command 'install %(p)s' or install it on your 565 own and set the path to its library in the MG5aMC option '%(p)s'.""" % {'p': key}) 566 self.exec_cmd("set %s ''" % key) 567 self.exec_cmd('save options %s' % key) 568 569 # ONLINE INSTALLATION 570 elif value == 'install': 571 prog = {'pjfry': 'PJFry', 'golem': 'Golem95'} 572 if key in prog: 573 self.exec_cmd('install %s' % prog[key]) 574 else: 575 self.exec_cmd('install %s' % key) 576 # Not install 577 elif value == 'off': 578 self.exec_cmd("set %s ''" % key) 579 self.exec_cmd('save options %s' % key) 580 else: 581 self.exec_cmd("set %s %s" % (key,value)) 582 self.exec_cmd('save options %s' % key)
583 584 585 586 # Export a matrix element
587 - def ML5export(self, nojpeg = False, main_file_name = ""):
588 """Export a generated amplitude to file""" 589 590 if not self._curr_helas_model: 591 self._curr_helas_model = helas_call_writers.FortranUFOHelasCallWriter(self._curr_model) 592 def generate_matrix_elements(self): 593 """Helper function to generate the matrix elements before exporting""" 594 595 # Sort amplitudes according to number of diagrams, 596 # to get most efficient multichannel output 597 self._curr_amps.sort(lambda a1, a2: a2.get_number_of_diagrams() - \ 598 a1.get_number_of_diagrams()) 599 600 cpu_time1 = time.time() 601 ndiags = 0 602 if not self._curr_matrix_elements.get_matrix_elements(): 603 self._curr_matrix_elements = \ 604 loop_helas_objects.LoopHelasProcess(self._curr_amps, 605 optimized_output = self.options['loop_optimized_output']) 606 ndiags = sum([len(me.get('diagrams')) for \ 607 me in self._curr_matrix_elements.\ 608 get_matrix_elements()]) 609 # assign a unique id number to all process 610 uid = 0 611 for me in self._curr_matrix_elements.get_matrix_elements(): 612 uid += 1 # update the identification number 613 me.get('processes')[0].set('uid', uid) 614 615 cpu_time2 = time.time() 616 return ndiags, cpu_time2 - cpu_time1
617 618 # Start of the actual routine 619 ndiags, cpu_time = generate_matrix_elements(self) 620 621 calls = 0 622 623 path = self._export_dir 624 if self._export_format in self.supported_ML_format: 625 path = pjoin(path, 'SubProcesses') 626 627 cpu_time1 = time.time() 628 629 # Pick out the matrix elements in a list 630 matrix_elements = \ 631 self._curr_matrix_elements.get_matrix_elements() 632 633 # Fortran MadGraph5_aMC@NLO Standalone 634 if self._export_format in self.supported_ML_format: 635 for unique_id, me in enumerate(matrix_elements): 636 calls = calls + \ 637 self._curr_exporter.generate_subprocess_directory(\ 638 me, self._curr_helas_model) 639 # If all ME's do not share the same maximum loop vertex rank and the 640 # same loop maximum wavefunction size, we need to set the maximum 641 # in coef_specs.inc of the HELAS Source. The SubProcesses/P* directory 642 # all link this file, so it should be properly propagated 643 if self.options['loop_optimized_output'] and len(matrix_elements)>1: 644 max_lwfspins = [m.get_max_loop_particle_spin() for m in \ 645 matrix_elements] 646 max_loop_vert_ranks = [me.get_max_loop_vertex_rank() for me in \ 647 matrix_elements] 648 if len(set(max_lwfspins))>1 or len(set(max_loop_vert_ranks))>1: 649 self._curr_exporter.fix_coef_specs(max(max_lwfspins),\ 650 max(max_loop_vert_ranks)) 651 652 # Just the matrix.f files 653 if self._export_format == 'matrix': 654 for me in matrix_elements: 655 filename = pjoin(path, 'matrix_' + \ 656 me.get('processes')[0].shell_string() + ".f") 657 if os.path.isfile(filename): 658 logger.warning("Overwriting existing file %s" % filename) 659 else: 660 logger.info("Creating new file %s" % filename) 661 calls = calls + self._curr_exporter.write_matrix_element_v4(\ 662 writers.FortranWriter(filename),\ 663 me, self._curr_helas_model) 664 665 cpu_time2 = time.time() - cpu_time1 666 667 logger.info(("Generated helas calls for %d subprocesses " + \ 668 "(%d diagrams) in %0.3f s") % \ 669 (len(matrix_elements), 670 ndiags, cpu_time)) 671 672 if calls: 673 if "cpu_time2" in locals(): 674 logger.info("Wrote files for %d OPP calls in %0.3f s" % \ 675 (calls, cpu_time2)) 676 else: 677 logger.info("Wrote files for %d OPP calls" % \ 678 (calls)) 679 680 # Replace the amplitudes with the actual amplitudes from the 681 # matrix elements, which allows proper diagram drawing also of 682 # decay chain processes 683 self._curr_amps = diagram_generation.AmplitudeList(\ 684 [me.get('base_amplitude') for me in \ 685 matrix_elements])
686
687 - def ML5finalize(self, nojpeg, online = False):
688 """Copy necessary sources and output the ps representation of 689 the diagrams, if needed""" 690 691 if self._export_format in self.supported_ML_format: 692 logger.info('Export UFO model to MG4 format') 693 # wanted_lorentz are the lorentz structures which are 694 # actually used in the wavefunctions and amplitudes in 695 # these processes 696 wanted_lorentz = self._curr_matrix_elements.get_used_lorentz() 697 wanted_couplings = self._curr_matrix_elements.get_used_couplings() 698 # For a unique output of multiple type of exporter model information 699 # are save in memory 700 if hasattr(self, 'previous_lorentz'): 701 wanted_lorentz = list(set(self.previous_lorentz + wanted_lorentz)) 702 wanted_couplings = list(set(self.previous_couplings + wanted_couplings)) 703 del self.previous_lorentz 704 del self.previous_couplings 705 706 self._curr_exporter.convert_model(self._curr_model, 707 wanted_lorentz, 708 wanted_couplings) 709 710 if self._export_format in self.supported_ML_format: 711 flags = [] 712 if nojpeg: 713 flags.append('nojpeg') 714 if online: 715 flags.append('online') 716 717 self._curr_exporter.finalize( \ 718 self._curr_matrix_elements, 719 self.history, 720 self.options, 721 flags) 722 723 if self._export_format in self.supported_ML_format: 724 logger.info('Output to directory ' + self._export_dir + ' done.')
725
726 - def do_launch(self, line, *args,**opt):
727 """Main commands: Check that the type of launch is fine before proceeding with the 728 mother function. """ 729 730 args = self.split_arg(line) 731 # check argument validity and normalise argument 732 (options, args) = mg_interface._launch_parser.parse_args(args) 733 734 self.check_launch(args, options) 735 736 if not args[0].startswith('standalone'): 737 raise self.InvalidCmd('ML5 can only launch standalone runs.') 738 739 start_cwd = os.getcwd() 740 options = options.__dict__ 741 # args is now MODE PATH 742 743 ext_program = launch_ext.MadLoopLauncher(self, args[1], \ 744 options=self.options, **options) 745 ext_program.run() 746 os.chdir(start_cwd) #ensure to go to the initial path
747
748 - def do_check(self, line, *args,**opt):
749 """Check a given process or set of processes""" 750 751 argss = self.split_arg(line, *args,**opt) 752 # Check args validity 753 perturbation_couplings_pattern = \ 754 re.compile("^(?P<proc>.+)\s*\[\s*((?P<option>\w+)\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 755 perturbation_couplings_re = perturbation_couplings_pattern.match(line) 756 perturbation_couplings="" 757 if perturbation_couplings_re: 758 perturbation_couplings = perturbation_couplings_re.group("pertOrders") 759 QED_found=re.search("QED",perturbation_couplings) 760 if QED_found: 761 self.validate_model(coupling_type='QED') 762 else: 763 self.validate_model() 764 765 param_card = self.check_check(argss) 766 reuse = argss[1]=="-reuse" 767 argss = argss[:1]+argss[2:] 768 # For the stability check the user can specify the statistics (i.e 769 # number of trial PS points) as a second argument 770 if argss[0] in ['stability', 'profile']: 771 stab_statistics = int(argss[1]) 772 argss = argss[:1]+argss[2:] 773 # Remove the extra options 774 i=-1 775 while argss[i].startswith('--'): 776 i=i-1 777 # Now make sure the process is acceptable 778 proc = " ".join(argss[1:i+1]) 779 myprocdef = self.extract_process(proc) 780 self.proc_validity(myprocdef,'ML5_check_cms' if argss[0]=='cms' else \ 781 'ML5_check') 782 783 return mg_interface.MadGraphCmd.do_check(self, line, *args,**opt)
784
785 - def do_add(self, line, *args,**opt):
786 """Generate an amplitude for a given process and add to 787 existing amplitudes 788 """ 789 args = self.split_arg(line) 790 # Check the validity of the arguments 791 self.check_add(args) 792 perturbation_couplings_pattern = \ 793 re.compile("^(?P<proc>.+)\s*\[\s*((?P<option>\w+)\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 794 perturbation_couplings_re = perturbation_couplings_pattern.match(line) 795 perturbation_couplings="" 796 if perturbation_couplings_re: 797 perturbation_couplings = perturbation_couplings_re.group("pertOrders") 798 QED_found=re.search('QED',perturbation_couplings) 799 if QED_found: 800 self.validate_model(coupling_type='QED') 801 else: 802 self.validate_model() 803 804 loop_filter=None 805 if args[0] == 'process': 806 807 # Extract potential loop_filter 808 for arg in args: 809 if arg.startswith('--loop_filter='): 810 loop_filter = arg[14:] 811 if not isinstance(self, extended_cmd.CmdShell): 812 raise InvalidCmd, "loop_filter is not allowed in web mode" 813 args = [a for a in args if not a.startswith('--loop_filter=')] 814 815 # Rejoin line 816 line = ' '.join(args[1:]) 817 818 # store the first process (for the perl script) 819 if not self._generate_info: 820 self._generate_info = line 821 822 # Reset Helas matrix elements 823 self._curr_matrix_elements = helas_objects.HelasMultiProcess() 824 825 # Extract process from process definition 826 myprocdef = self.extract_process(line) 827 # hack for multiprocess: 828 if myprocdef.has_multiparticle_label(): 829 # split it in a loop 830 succes, failed = 0, 0 831 for base_proc in myprocdef: 832 try: 833 self.exec_cmd("add process %s" % base_proc.nice_string(prefix=False, print_weighted=True)) 834 succes += 1 835 except Exception: 836 failed +=1 837 logger.info("%s/%s processes succeeded" % (succes, failed+succes)) 838 if succes == 0: 839 raise 840 else: 841 return 842 843 844 # If it is a process for MadLoop standalone, make sure it has a 845 # unique ID. It is important for building a BLHA library which 846 # contains unique entry point for each process generated. 847 all_ids = [amp.get('process').get('id') for amp in self._curr_amps] 848 if myprocdef.get('id') in all_ids: 849 myprocdef.set('id',max(all_ids)+1) 850 851 self.proc_validity(myprocdef,'ML5') 852 853 cpu_time1 = time.time() 854 855 # Decide here wether one needs a LoopMultiProcess or a MultiProcess 856 multiprocessclass=None 857 if myprocdef['perturbation_couplings']!=[]: 858 multiprocessclass=loop_diagram_generation.LoopMultiProcess 859 else: 860 multiprocessclass=diagram_generation.MultiProcess 861 862 myproc = multiprocessclass(myprocdef, collect_mirror_procs = False, 863 ignore_six_quark_processes = False, 864 loop_filter = loop_filter) 865 866 for amp in myproc.get('amplitudes'): 867 if amp not in self._curr_amps: 868 self._curr_amps.append(amp) 869 else: 870 warning = "Warning: Already in processes:\n%s" % \ 871 amp.nice_string_processes() 872 logger.warning(warning) 873 874 # Reset _done_export, since we have new process 875 self._done_export = False 876 877 cpu_time2 = time.time() 878 879 ndiags = sum([len(amp.get('loop_diagrams')) for \ 880 amp in myproc.get('amplitudes')]) 881 logger.info("Process generated in %0.3f s" % \ 882 (cpu_time2 - cpu_time1))
883
884 -class LoopInterfaceWeb(mg_interface.CheckValidForCmdWeb, LoopInterface):
885 pass
886
887 888 -class AskLoopInstaller(cmd.OneLinePathCompletion):
889 890 local_installer = ['ninja', 'collier'] 891 required = ['cuttools', 'iregi'] 892 order = ['cuttools', 'iregi', 'ninja', 'collier', 'golem', 'pjfry'] 893 894 @property
895 - def answer(self):
896 return self.code
897 898
899 - def __init__(self, question, *args, **opts):
900 901 import urllib2 902 try: 903 response=urllib2.urlopen('http://madgraph.phys.ucl.ac.be/F1.html', timeout=3) 904 self.online=True 905 except urllib2.URLError as err: 906 self.online=False 907 908 self.code = {'ninja': 'install', 909 'collier': 'install', 910 'golem': 'off', 911 'pjfry':'off', 912 'cuttools': 'required', 913 'iregi': 'required'} 914 if not self.online: 915 self.code['ninja'] = 'local' 916 self.code['collier'] = 'local' 917 self.code['pjfry'] = 'fail' 918 self.code['golem'] = 'fail' 919 if not misc.which('cmake'): 920 self.code['collier'] = 'off' 921 922 #check if some partial installation is already done. 923 if 'mother_interface' in opts: 924 mother = opts['mother_interface'] 925 if 'heptools_install_dir' in mother.options: 926 install_dir1 = mother.options['heptools_install_dir'] 927 install_dir2 = mother.options['heptools_install_dir'] 928 if os.path.exists(pjoin(install_dir1, 'CutTools')): 929 self.code['cuttools'] = mother.options['heptools_install_dir'] 930 if os.path.exists(pjoin(install_dir1, 'IREGI')): 931 self.code['iregi'] = mother.options['heptools_install_dir'] 932 else: 933 install_dir1 = pjoin(MG5DIR, 'HEPTools') 934 install_dir2 = MG5DIR 935 if os.path.exists(pjoin(install_dir1, 'collier')): 936 self.code['collier'] = pjoin(install_dir1, 'collier') 937 if os.path.exists(pjoin(install_dir2, 'PJFry','bin','qd-config')): 938 self.code['collier'] = pjoin(install_dir2, 'PJFry') 939 if os.path.exists(pjoin(install_dir2, 'golem95')): 940 self.code['collier'] = pjoin(install_dir2, 'golem95') 941 942 # 1. create the question 943 question, allowed_answer = self.create_question(first=True) 944 945 opts['allow_arg'] = allowed_answer 946 947 cmd.OneLinePathCompletion.__init__(self, question, *args, **opts)
948 949
950 - def create_question(self, first = False):
951 """ """ 952 953 question = "For loop computations, MadLoop requires dedicated tools to"+\ 954 " perform the reduction of loop Feynman diagrams using OPP-based and/or TIR approaches.\n"+\ 955 "\nWhich one do you want to install? (this needs to be done only once)\n" 956 957 allowed_answer = set(['0','done']) 958 959 descript = {'cuttools': ['cuttools','(OPP)','[0711.3596]'], 960 'iregi': ['iregi','(TIR)','[1405.0301]'], 961 'ninja': ['ninja','(OPP)','[1403.1229]'], 962 'pjfry': ['pjfry','(TIR)','[1112.0500]'], 963 'golem': ['golem','(TIR)','[0807.0605]'], 964 'collier': ['collier','(TIR)','[1604.06792]']} 965 966 967 status = {'off': '%(start_red)sdo not install%(stop)s', 968 'install': '%(start_green)swill be installed %(stop)s', 969 'local': '%(start_green)swill be installed %(stop)s(offline installation from local repository)', 970 'fail': 'not available without internet connection', 971 'required': 'will be installed (required)'} 972 973 for i,key in enumerate(self.order,1): 974 if os.path.sep not in self.code[key]: 975 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s%s\n' % \ 976 tuple([i,]+descript[key]+[status[self.code[key]],]+\ 977 ['(recommended)' if key in ['ninja','collier'] and self.code[key] in ['install'] else '']) 978 else: 979 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s\n' % tuple([i,]+descript[key]+[self.code[key],]) 980 if key in self.required: 981 continue 982 allowed_answer.update([str(i), key]) 983 if key in self.local_installer: 984 allowed_answer.update(['key=local','key=off']) 985 if self.online: 986 allowed_answer.update(['key=on','key=install', 'key=off']) 987 988 question += "You can:\n -> hit 'enter' to proceed\n -> type a number to cycle its options\n -> enter the following command:\n"+\ 989 ' %(start_blue)s{tool_name}%(stop)s [%(start_blue)sinstall%(stop)s|%(start_blue)snoinstall%(stop)s|'+\ 990 '%(start_blue)s{prefixed_installation_path}%(stop)s]\n' 991 if first: 992 question += '\n%(start_bold)s%(start_red)sIf you are unsure about what this question means, just type enter to proceed. %(stop)s' 993 994 question = question % {'start_green' : '\033[92m', 995 'start_red' : '\033[91m', 996 'start_blue' : '\033[34m', 997 'stop': '\033[0m', 998 'start_bold':'\033[1m', 999 } 1000 return question, allowed_answer
1001
1002 - def default(self, line):
1003 """Default action if line is not recognized""" 1004 1005 line = line.strip() 1006 args = line.split() 1007 1008 if line in ['0', 'done','','EOF']: 1009 self.value = 'done' 1010 return self.answer 1011 self.value = 'repeat' 1012 if args: 1013 if len(args) ==1 and '=' in args[0]: 1014 args = args[0].split('=') 1015 args[0] = args[0].lower() 1016 if len(args) == 1: 1017 # loop over the possibility 1018 if args[0].isdigit(): 1019 if len(self.order) < int(args[0]): 1020 logger.warning('Invalid integer %s. Please Retry' % args[0]) 1021 return 1022 args[0] = self.order[int(args[0])-1] 1023 key = args[0] 1024 if key in self.code: 1025 if self.code[key] in ['off']: 1026 if self.online: 1027 self.code[key] = 'install' 1028 elif key in self.local_installer: 1029 self.code[key] = 'local' 1030 elif self.code[key] == 'install': 1031 if key in self.local_installer: 1032 self.code[key] = 'local' 1033 else: 1034 self.code[key] = 'off' 1035 elif self.code[key] == 'local': 1036 self.code[key] = 'off' 1037 else: 1038 logger.warning('Unknown entry \'%s\'. Please retry' % key) 1039 return 1040 elif len(args) == 2: 1041 key = args[0] 1042 if key not in self.code: 1043 logger.warning('unknown %s type of entry. Bypass command.') 1044 return 1045 if os.path.sep not in args[1]: 1046 value = args[1].lower() 1047 if value in ['off', 'not','noinstall']: 1048 self.code[key] = 'off' 1049 elif value in ['on', 'install']: 1050 if self.online: 1051 self.code[key] = 'install' 1052 elif key in self.local_installer: 1053 self.code[key] = 'local' 1054 else: 1055 logger.warning('offline installer not available for %s', key) 1056 self.code[key] = 'off' 1057 elif value in ['local']: 1058 if key in self.local_installer: 1059 self.code[key] = 'local' 1060 else: 1061 logger.warning('offline installer not available for %s', key) 1062 self.code[key] = 'off' 1063 else: 1064 self.code[key] = args[1] 1065 else: 1066 self.value = 0 1067 self.question,self.allow_arg = self.create_question() 1068 return self.answer
1069
1070 - def apply_name(self, name, line):
1071 1072 if line.startswith('='): 1073 line = line[1:] 1074 return self.default('%s %s' % (name,line))
1075 1076 1077 do_ninja = lambda self,line : self.apply_name('ninja', line) 1078 do_pjfry = lambda self,line : self.apply_name('pjfry', line) 1079 do_collier = lambda self,line : self.apply_name('collier', line) 1080 do_golem = lambda self,line : self.apply_name('golem', line) 1081 do_cuttools = lambda self,line : self.apply_name('cuttools', line) 1082 do_iregi = lambda self,line : self.apply_name('iregi', line) 1083 1084
1085 - def complete_prog(self, text, line, begidx, endidx, formatting=True):
1086 1087 if os.path.sep in line: 1088 args = line[0:begidx].split() 1089 if args[-1].endswith(os.path.sep): 1090 return self.path_completion(text, 1091 pjoin(*[a for a in args if a.endswith(os.path.sep)]), 1092 only_dirs = True) 1093 else: 1094 return self.path_completion(text, '.', only_dirs = True) 1095 else: 1096 return self.list_completion(text, ['install', 'noinstall', 'local'], line)
1097 1098 complete_ninja = complete_prog 1099 complete_pjfry = complete_prog 1100 complete_collier = complete_prog 1101 complete_golem = complete_prog 1102 complete_cuttools = complete_prog 1103 complete_iregi = complete_prog
1104