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 args = self.split_arg(line) 807 # Check the validity of the arguments 808 self.check_add(args) 809 perturbation_couplings_pattern = \ 810 re.compile("^(?P<proc>.+)\s*\[\s*((?P<option>\w+)\s*\=)?\s*(?P<pertOrders>(\w+\s*)*)\s*\]\s*(?P<rest>.*)$") 811 perturbation_couplings_re = perturbation_couplings_pattern.match(line) 812 perturbation_couplings="" 813 if perturbation_couplings_re: 814 perturbation_couplings = perturbation_couplings_re.group("pertOrders") 815 QED_found=re.search('QED',perturbation_couplings) 816 if QED_found: 817 self.validate_model(coupling_type='QED') 818 else: 819 self.validate_model() 820 821 loop_filter=None 822 if args[0] == 'process': 823 824 # Extract potential loop_filter 825 for arg in args: 826 if arg.startswith('--loop_filter='): 827 start = arg[14] 828 end = arg[-1] 829 if start == end and start in ["'", '"']: 830 loop_filter = arg[15:-1] 831 else: 832 loop_filter = arg[14:] 833 if not isinstance(self, extended_cmd.CmdShell): 834 raise self.InvalidCmd("loop_filter is not allowed in web mode") 835 args = [a for a in args if not a.startswith('--loop_filter=')] 836 837 # Rejoin line 838 line = ' '.join(args[1:]) 839 840 # store the first process (for the perl script) 841 if not self._generate_info: 842 self._generate_info = line 843 844 # Reset Helas matrix elements 845 self._curr_matrix_elements = helas_objects.HelasMultiProcess() 846 847 # Extract process from process definition 848 myprocdef = self.extract_process(line) 849 # hack for multiprocess: 850 if myprocdef.has_multiparticle_label(): 851 # split it in a loop 852 succes, failed = 0, 0 853 for base_proc in myprocdef: 854 command = "add process %s" % base_proc.nice_string(prefix=False, print_weighted=True) 855 if '@' not in command: 856 command += ' @%s' % base_proc.get('id') 857 try: 858 self.exec_cmd(command) 859 succes += 1 860 except Exception: 861 failed +=1 862 logger.info("%s/%s processes succeeded" % (succes, failed+succes)) 863 if succes == 0: 864 raise 865 else: 866 return 867 868 869 # If it is a process for MadLoop standalone, make sure it has a 870 # unique ID. It is important for building a BLHA library which 871 # contains unique entry point for each process generated. 872 #all_ids = [amp.get('process').get('id') for amp in self._curr_amps] 873 #if myprocdef.get('id') in all_ids: 874 # myprocdef.set('id',max(all_ids)+1) 875 #This is ensure at the output stage! by checking that every output have 876 # a different id => No need here. 877 878 self.proc_validity(myprocdef,'ML5') 879 880 cpu_time1 = time.time() 881 882 # Decide here wether one needs a LoopMultiProcess or a MultiProcess 883 multiprocessclass=None 884 if myprocdef['perturbation_couplings']!=[]: 885 multiprocessclass=loop_diagram_generation.LoopMultiProcess 886 else: 887 multiprocessclass=diagram_generation.MultiProcess 888 889 myproc = multiprocessclass(myprocdef, collect_mirror_procs = False, 890 ignore_six_quark_processes = False, 891 loop_filter = loop_filter) 892 893 for amp in myproc.get('amplitudes'): 894 if amp not in self._curr_amps: 895 self._curr_amps.append(amp) 896 else: 897 warning = "Warning: Already in processes:\n%s" % \ 898 amp.nice_string_processes() 899 logger.warning(warning) 900 901 # Reset _done_export, since we have new process 902 self._done_export = False 903 904 cpu_time2 = time.time() 905 906 ndiags = sum([len(amp.get('loop_diagrams')) for \ 907 amp in myproc.get('amplitudes')]) 908 logger.info("Process generated in %0.3f s" % \ 909 (cpu_time2 - cpu_time1))
910
911 -class LoopInterfaceWeb(mg_interface.CheckValidForCmdWeb, LoopInterface):
912 pass
913
914 915 -class AskLoopInstaller(cmd.OneLinePathCompletion):
916 917 local_installer = ['ninja', 'collier'] 918 required = ['cuttools', 'iregi'] 919 order = ['cuttools', 'iregi', 'ninja', 'collier', 'golem'] 920 bypassed = ['pjfry'] 921 922 @property
923 - def answer(self):
924 return self.code
925 926
927 - def __init__(self, question, *args, **opts):
928 929 import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse 930 try: 931 response=six.moves.urllib.request.urlopen('http://madgraph.phys.ucl.ac.be/F1.html', timeout=3) 932 self.online=True 933 except six.moves.urllib.error.URLError as err: 934 self.online=False 935 936 self.code = {'ninja': 'install', 937 'collier': 'install', 938 'golem': 'off', 939 'cuttools': 'required', 940 'iregi': 'required'} 941 if not self.online: 942 self.code['ninja'] = 'local' 943 self.code['collier'] = 'local' 944 self.code['golem'] = 'fail' 945 if not misc.which('cmake'): 946 self.code['collier'] = 'off' 947 948 #check if some partial installation is already done. 949 if 'mother_interface' in opts: 950 mother = opts['mother_interface'] 951 if 'heptools_install_dir' in mother.options: 952 install_dir1 = mother.options['heptools_install_dir'] 953 install_dir2 = mother.options['heptools_install_dir'] 954 if os.path.exists(pjoin(install_dir1, 'CutTools')): 955 self.code['cuttools'] = mother.options['heptools_install_dir'] 956 if os.path.exists(pjoin(install_dir1, 'IREGI')): 957 self.code['iregi'] = mother.options['heptools_install_dir'] 958 else: 959 install_dir1 = pjoin(MG5DIR, 'HEPTools') 960 install_dir2 = MG5DIR 961 if os.path.exists(pjoin(install_dir1, 'collier')): 962 self.code['collier'] = pjoin(install_dir1, 'collier') 963 if os.path.exists(pjoin(install_dir2, 'golem95')): 964 self.code['glem'] = pjoin(install_dir2, 'golem95') 965 if os.path.exists(pjoin(install_dir1, 'ninja')): 966 self.code['ninja'] = pjoin(install_dir2, 'ninja','lib') 967 968 # 1. create the question 969 question, allowed_answer = self.create_question(first=True) 970 971 opts['allow_arg'] = allowed_answer 972 973 cmd.OneLinePathCompletion.__init__(self, question, *args, **opts)
974 975
976 - def create_question(self, first = False):
977 """ """ 978 979 question = "For loop computations, MadLoop requires dedicated tools to"+\ 980 " perform the reduction of loop Feynman diagrams using OPP-based and/or TIR approaches.\n"+\ 981 "\nWhich one do you want to install? (this needs to be done only once)\n" 982 983 allowed_answer = set(['0','done']) 984 985 descript = {'cuttools': ['cuttools','(OPP)','[0711.3596]'], 986 'iregi': ['iregi','(TIR)','[1405.0301]'], 987 'ninja': ['ninja','(OPP)','[1403.1229]'], 988 'golem': ['golem','(TIR)','[0807.0605]'], 989 'collier': ['collier','(TIR)','[1604.06792]']} 990 991 992 status = {'off': '%(start_red)sdo not install%(stop)s', 993 'install': '%(start_green)swill be installed %(stop)s', 994 'local': '%(start_green)swill be installed %(stop)s(offline installation from local repository)', 995 'fail': 'not available without internet connection', 996 'required': 'will be installed (required)'} 997 998 for i,key in enumerate(self.order,1): 999 if key in self.bypassed and self.code[key] == 'off': 1000 continue 1001 if os.path.sep not in self.code[key]: 1002 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s%s\n' % \ 1003 tuple([i,]+descript[key]+[status[self.code[key]],]+\ 1004 ['(recommended)' if key in ['ninja','collier'] and self.code[key] in ['install'] else '']) 1005 else: 1006 question += '%s. %%(start_blue)s%-9s %-5s %-13s%%(stop)s : %s\n' % tuple([i,]+descript[key]+[self.code[key],]) 1007 if key in self.required: 1008 continue 1009 allowed_answer.update([str(i), key]) 1010 if key in self.local_installer: 1011 allowed_answer.update(['key=local','key=off']) 1012 if self.online: 1013 allowed_answer.update(['key=on','key=install', 'key=off']) 1014 1015 question += "You can:\n -> hit 'enter' to proceed\n -> type a number to cycle its options\n -> enter the following command:\n"+\ 1016 ' %(start_blue)s{tool_name}%(stop)s [%(start_blue)sinstall%(stop)s|%(start_blue)snoinstall%(stop)s|'+\ 1017 '%(start_blue)s{prefixed_installation_path}%(stop)s]\n' 1018 if first: 1019 question += '\n%(start_bold)s%(start_red)sIf you are unsure about what this question means, just type enter to proceed. %(stop)s' 1020 1021 question = question % {'start_green' : '\033[92m', 1022 'start_red' : '\033[91m', 1023 'start_blue' : '\033[34m', 1024 'stop': '\033[0m', 1025 'start_bold':'\033[1m', 1026 } 1027 return question, allowed_answer
1028
1029 - def default(self, line):
1030 """Default action if line is not recognized""" 1031 1032 line = line.strip() 1033 args = line.split() 1034 1035 if line in ['0', 'done','','EOF']: 1036 self.value = 'done' 1037 return self.answer 1038 self.value = 'repeat' 1039 if args: 1040 if len(args) ==1 and '=' in args[0]: 1041 args = args[0].split('=') 1042 args[0] = args[0].lower() 1043 if len(args) == 1: 1044 # loop over the possibility 1045 if args[0].isdigit(): 1046 if len(self.order) < int(args[0]): 1047 logger.warning('Invalid integer %s. Please Retry' % args[0]) 1048 return 1049 args[0] = self.order[int(args[0])-1] 1050 key = args[0] 1051 if key in self.code: 1052 if self.code[key] in ['off']: 1053 if self.online: 1054 self.code[key] = 'install' 1055 elif key in self.local_installer: 1056 self.code[key] = 'local' 1057 elif self.code[key] == 'install': 1058 if key in self.local_installer: 1059 self.code[key] = 'local' 1060 else: 1061 self.code[key] = 'off' 1062 elif self.code[key] == 'local': 1063 self.code[key] = 'off' 1064 else: 1065 logger.warning('Unknown entry \'%s\'. Please retry' % key) 1066 return 1067 elif len(args) == 2: 1068 key = args[0] 1069 if key not in self.code: 1070 logger.warning('unknown %s type of entry. Bypass command.') 1071 return 1072 if os.path.sep not in args[1]: 1073 value = args[1].lower() 1074 if value in ['off', 'not','noinstall']: 1075 self.code[key] = 'off' 1076 elif value in ['on', 'install']: 1077 if self.online: 1078 self.code[key] = 'install' 1079 elif key in self.local_installer: 1080 self.code[key] = 'local' 1081 else: 1082 logger.warning('offline installer not available for %s', key) 1083 self.code[key] = 'off' 1084 elif value in ['local']: 1085 if key in self.local_installer: 1086 self.code[key] = 'local' 1087 else: 1088 logger.warning('offline installer not available for %s', key) 1089 self.code[key] = 'off' 1090 else: 1091 self.code[key] = args[1] 1092 else: 1093 self.value = 0 1094 self.question,self.allow_arg = self.create_question() 1095 return self.answer
1096
1097 - def apply_name(self, name, line):
1098 1099 if line.startswith('='): 1100 line = line[1:] 1101 return self.default('%s %s' % (name,line))
1102 1103 1104 do_ninja = lambda self,line : self.apply_name('ninja', line) 1105 do_collier = lambda self,line : self.apply_name('collier', line) 1106 do_golem = lambda self,line : self.apply_name('golem', line) 1107 do_cuttools = lambda self,line : self.apply_name('cuttools', line) 1108 do_iregi = lambda self,line : self.apply_name('iregi', line) 1109 1110
1111 - def complete_prog(self, text, line, begidx, endidx, formatting=True):
1112 1113 if os.path.sep in line: 1114 args = line[0:begidx].split() 1115 if args[-1].endswith(os.path.sep): 1116 return self.path_completion(text, 1117 pjoin(*[a for a in args if a.endswith(os.path.sep)]), 1118 only_dirs = True) 1119 else: 1120 return self.path_completion(text, '.', only_dirs = True) 1121 else: 1122 return self.list_completion(text, ['install', 'noinstall', 'local'], line)
1123 1124 complete_ninja = complete_prog 1125 complete_collier = complete_prog 1126 complete_golem = complete_prog 1127 complete_cuttools = complete_prog 1128 complete_iregi = complete_prog
1129