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