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