Package madgraph :: Package loop :: Module loop_exporters
[hide private]
[frames] | no frames]

Source Code for Module madgraph.loop.loop_exporters

   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  """Methods and classes to export matrix elements to v4 format.""" 
  16   
  17  from __future__ import absolute_import 
  18  import copy 
  19  import fractions 
  20  import glob 
  21  import logging 
  22  import os 
  23  import stat 
  24  import sys 
  25  import re 
  26  import shutil 
  27  import subprocess 
  28  import itertools 
  29  import time 
  30  import datetime 
  31   
  32   
  33  import aloha 
  34   
  35  import madgraph.core.base_objects as base_objects 
  36  import madgraph.core.color_algebra as color 
  37  import madgraph.core.helas_objects as helas_objects 
  38  import madgraph.loop.loop_helas_objects as loop_helas_objects 
  39  import madgraph.iolibs.drawing_eps as draw 
  40  import madgraph.iolibs.files as files 
  41  import madgraph.iolibs.group_subprocs as group_subprocs 
  42  import madgraph.various.banner as banner_mod 
  43  import madgraph.various.misc as misc 
  44  import madgraph.various.q_polynomial as q_polynomial 
  45  import madgraph.iolibs.file_writers as writers 
  46  import madgraph.iolibs.gen_infohtml as gen_infohtml 
  47  import madgraph.iolibs.template_files as template_files 
  48  import madgraph.iolibs.ufo_expression_parsers as parsers 
  49  import madgraph.iolibs.export_v4 as export_v4 
  50  import madgraph.various.diagram_symmetry as diagram_symmetry 
  51  import madgraph.various.process_checks as process_checks 
  52  import madgraph.various.progressbar as pbar 
  53  import madgraph.various.q_polynomial as q_polynomial 
  54  import madgraph.core.color_amp as color_amp 
  55  import madgraph.iolibs.helas_call_writers as helas_call_writers 
  56  import models.check_param_card as check_param_card 
  57  from madgraph.loop.loop_base_objects import LoopDiagram 
  58  from madgraph.loop.MadLoopBannerStyles import MadLoopBannerStyles 
  59  from six.moves import range 
  60  from six.moves import zip 
  61   
  62   
  63   
  64  pjoin = os.path.join 
  65   
  66  import aloha.create_aloha as create_aloha 
  67  import models.write_param_card as param_writer 
  68  from madgraph import MadGraph5Error, MG5DIR, InvalidCmd 
  69  from madgraph.iolibs.files import cp, ln, mv 
  70  pjoin = os.path.join 
  71  _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/' 
  72  logger = logging.getLogger('madgraph.loop_exporter') 
  73   
  74  #=============================================================================== 
  75  # LoopExporterFortran 
  76  #=============================================================================== 
77 -class LoopExporterFortran(object):
78 """ Class to define general helper functions to the different 79 loop fortran exporters (ME, SA, MEGroup, etc..) which will inherit both 80 from this class AND from the corresponding ProcessExporterFortran(ME,SA,...). 81 It plays the same role as ProcessExporterFrotran and simply defines here 82 loop-specific helpers functions necessary for all loop exporters. 83 Notice that we do not have LoopExporterFortran inheriting from 84 ProcessExporterFortran but give access to arguments like dir_path and 85 clean using options. This avoids method resolution object ambiguity""" 86 87 default_opt = dict(export_v4.ProcessExporterFortran.default_opt) 88 default_opt.update({'clean': False, 'complex_mass':False, 89 'export_format':'madloop', 'mp':True, 90 'loop_dir':'', 'cuttools_dir':'', 91 'fortran_compiler':'gfortran', 92 'SubProc_prefix': 'P', 93 'output_dependencies': 'external', 94 'compute_color_flows': False, 95 'mode':''}) 96 97 include_names = {'ninja' : 'mninja.mod', 98 'golem' : 'generic_function_1p.mod', 99 'samurai':'msamurai.mod', 100 'collier': 'collier.mod'} 101
102 - def __init__(self, dir_path = "", opt=None):
103 """Initiate the LoopExporterFortran with directory information on where 104 to find all the loop-related source files, like CutTools""" 105 106 107 self.opt = dict(self.default_opt) 108 if opt: 109 self.opt.update(opt) 110 111 self.SubProc_prefix = self.opt['SubProc_prefix'] 112 self.loop_dir = self.opt['loop_dir'] 113 self.cuttools_dir = self.opt['cuttools_dir'] 114 self.fortran_compiler = self.opt['fortran_compiler'] 115 self.dependencies = self.opt['output_dependencies'] 116 self.compute_color_flows = self.opt['compute_color_flows'] 117 118 super(LoopExporterFortran,self).__init__(dir_path, self.opt)
119 120 194
195 - def get_aloha_model(self, model):
196 """ Caches the aloha model created here as an attribute of the loop 197 exporter so that it can later be used in the LoopHelasMatrixElement 198 in the function compute_all_analytic_information for recycling aloha 199 computations across different LoopHelasMatrixElements steered by the 200 same loop exporter. 201 """ 202 if not hasattr(self, 'aloha_model'): 203 self.aloha_model = create_aloha.AbstractALOHAModel(model.get('modelpath')) 204 205 missing_lor = [] 206 for lor in model.get('lorentz'): 207 if not hasattr(self.aloha_model.model.lorentz, lor.name): 208 missing_lor.append(lor) 209 if missing_lor: 210 logger.debug("adding in aloha model %s lorentz struct" % len(missing_lor)) 211 self.aloha_model.add_Lorentz_object(missing_lor) 212 213 return self.aloha_model
214 215 #=========================================================================== 216 # write the multiple-precision header files 217 #===========================================================================
218 - def write_mp_files(self, writer_mprec, writer_mpc):
219 """Write the cts_mprec.h and cts_mpc.h""" 220 221 file = open(os.path.join(self.cuttools_dir, 'src/cts/cts_mprec.h')).read() 222 writer_mprec.writelines(file) 223 224 file = open(os.path.join(self.cuttools_dir, 'src/cts/cts_mpc.h')).read() 225 file = file.replace('&','') 226 writer_mpc.writelines(file) 227 228 return True
229 230 #=============================================================================== 231 # LoopProcessExporterFortranSA 232 #===============================================================================
233 -class LoopProcessExporterFortranSA(LoopExporterFortran, 234 export_v4.ProcessExporterFortranSA):
235 236 """Class to take care of exporting a set of loop matrix elements in the 237 Fortran format.""" 238 239 template_dir=os.path.join(_file_path,'iolibs/template_files/loop') 240 madloop_makefile_name = 'makefile' 241 242 MadLoop_banner = MadLoopBannerStyles.get_MadLoop_Banner( 243 style='classic2', color='green', 244 top_frame_char = '=', bottom_frame_char = '=', 245 left_frame_char = '{',right_frame_char = '}', 246 print_frame=True, side_margin = 7, up_margin = 1) 247
248 - def __init__(self, *args, **opts):
249 super(LoopProcessExporterFortranSA,self).__init__(*args,**opts) 250 self.unique_id=0 # to allow collier to distinguish the various loop subprocesses 251 self.has_loop_induced = False
252
253 - def copy_template(self, model):
254 """Additional actions needed to setup the Template. 255 """ 256 super(LoopProcessExporterFortranSA, self).copy_template(model) 257 258 self.loop_additional_template_setup()
259
260 - def finalize(self, matrix_element, cmdhistory, MG5options, outputflag):
261 """create the global information for loops""" 262 263 super(LoopProcessExporterFortranSA,self).finalize(matrix_element, 264 cmdhistory, MG5options, outputflag) 265 266 267 MLCard = banner_mod.MadLoopParam(pjoin(self.dir_path, 'Cards', 'MadLoopParams.dat')) 268 # For loop-induced processes and *only* when summing over all helicity configurations 269 # (which is the default for standalone usage), COLLIER is faster than Ninja. 270 if self.has_loop_induced: 271 MLCard['MLReductionLib'] = "7|6|1" 272 # Computing the poles with COLLIER also unnecessarily slows down the code 273 # It should only be set to True for checks and it's acceptable to remove them 274 # here because for loop-induced processes they should be zero anyway. 275 # We keep it active for non-loop induced processes because COLLIER is not the 276 # main reduction tool in that case, and the poles wouldn't be zero then 277 MLCard['COLLIERComputeUVpoles'] = False 278 MLCard['COLLIERComputeIRpoles'] = False 279 280 MLCard.write(pjoin(self.dir_path, 'Cards', 'MadLoopParams_default.dat')) 281 MLCard.write(pjoin(self.dir_path, 'Cards', 'MadLoopParams.dat'))
282
283 - def write_f2py_makefile(self):
284 return
285
286 - def write_f2py_check_sa(self, matrix_element, output_path):
287 """ Write the general check_sa.py in SubProcesses that calls all processes successively.""" 288 289 # No need to further edit this file for now. 290 file = open(os.path.join(self.template_dir,\ 291 'check_sa_all.py.inc')).read() 292 open(output_path,'w').writelines(file) 293 # Make it executable 294 os.chmod(output_path, os.stat(output_path).st_mode | stat.S_IEXEC)
295 296
297 - def write_f2py_splitter(self):
298 """write a function to call the correct matrix element""" 299 300 template = """ 301 %(python_information)s 302 303 SUBROUTINE INITIALISE(PATH) 304 C ROUTINE FOR F2PY to read the benchmark point. 305 IMPLICIT NONE 306 CHARACTER*512 PATH 307 CF2PY INTENT(IN) :: PATH 308 CALL SETPARA(PATH) !first call to setup the paramaters 309 RETURN 310 END 311 312 subroutine CHANGE_PARA(name, value) 313 implicit none 314 CF2PY intent(in) :: name 315 CF2PY intent(in) :: value 316 317 character*512 name 318 double precision value 319 320 include '../Source/MODEL/input.inc' 321 include '../Source/MODEL/coupl.inc' 322 include '../Source/MODEL/mp_coupl.inc' 323 include '../Source/MODEL/mp_input.inc' 324 325 SELECT CASE (name) 326 %(parameter_setup)s 327 CASE DEFAULT 328 write(*,*) 'no parameter matching', name 329 END SELECT 330 331 return 332 end 333 334 subroutine update_all_coup() 335 implicit none 336 call coup() 337 call printout() 338 return 339 end 340 341 342 SUBROUTINE SET_MADLOOP_PATH(PATH) 343 C Routine to set the path of the folder 'MadLoop5_resources' to MadLoop 344 CHARACTER(512) PATH 345 CF2PY intent(in)::path 346 CALL SETMADLOOPPATH(PATH) 347 END 348 349 subroutine smatrixhel(pdgs, procid, npdg, p, ALPHAS, SCALES2, nhel, ANS, RETURNCODE) 350 IMPLICIT NONE 351 352 CF2PY double precision, intent(in), dimension(0:3,npdg) :: p 353 CF2PY integer, intent(in), dimension(npdg) :: pdgs 354 CF2PY integer, intent(in):: procid 355 CF2PY integer, intent(in) :: npdg 356 CF2PY double precision, intent(out) :: ANS 357 CF2PY integer, intent(out) :: RETURNCODE 358 CF2PY double precision, intent(in) :: ALPHAS 359 CF2PY double precision, intent(in) :: SCALES2 360 361 integer pdgs(*) 362 integer npdg, nhel, RETURNCODE, procid 363 double precision p(*) 364 double precision ANS, ALPHAS, PI,SCALES2 365 1 continue 366 %(smatrixhel)s 367 368 return 369 end 370 371 subroutine get_pdg_order(OUT, ALLPROC) 372 IMPLICIT NONE 373 CF2PY INTEGER, intent(out) :: OUT(%(nb_me)i,%(maxpart)i) 374 CF2PY INTEGER, intent(out) :: ALLPROC(%(nb_me)i) 375 INTEGER OUT(%(nb_me)i,%(maxpart)i), PDGS(%(nb_me)i,%(maxpart)i) 376 INTEGER ALLPROC(%(nb_me)i),PIDs(%(nb_me)i) 377 DATA PDGS/ %(pdgs)s / 378 DATA PIDS/ %(pids)s / 379 OUT=PDGS 380 ALLPROC = PIDS 381 RETURN 382 END 383 384 subroutine get_prefix(PREFIX) 385 IMPLICIT NONE 386 CF2PY CHARACTER*20, intent(out) :: PREFIX(%(nb_me)i) 387 character*20 PREFIX(%(nb_me)i),PREF(%(nb_me)i) 388 DATA PREF / '%(prefix)s'/ 389 PREFIX = PREF 390 RETURN 391 END 392 393 """ 394 395 allids = list(self.prefix_info.keys()) 396 allprefix = [self.prefix_info[key][0] for key in allids] 397 min_nexternal = min([len(ids[0]) for ids in allids]) 398 max_nexternal = max([len(ids[0]) for ids in allids]) 399 400 info = [] 401 for (key,pid), (prefix, tag) in self.prefix_info.items(): 402 info.append('#PY %s : %s # %s %s' % (tag, key, prefix, pid)) 403 404 405 text = [] 406 for n_ext in range(min_nexternal, max_nexternal+1): 407 current_id = [ids[0] for ids in allids if len(ids[0])==n_ext] 408 current_pid = [ids[1] for ids in allids if len(ids[0])==n_ext] 409 if not current_id: 410 continue 411 if min_nexternal != max_nexternal: 412 if n_ext == min_nexternal: 413 text.append(' if (npdg.eq.%i)then' % n_ext) 414 else: 415 text.append(' else if (npdg.eq.%i)then' % n_ext) 416 for ii,pdgs in enumerate(current_id): 417 pid = current_pid[ii] 418 condition = '.and.'.join(['%i.eq.pdgs(%i)' %(pdg, i+1) for i, pdg in enumerate(pdgs)]) 419 if ii==0: 420 text.append( ' if(%s.and.(procid.le.0.or.procid.eq.%d)) then ! %i' % (condition, pid, len(pdgs))) 421 else: 422 text.append( ' else if(%s.and.(procid.le.0.or.procid.eq.%d)) then ! %i' % (condition,pid,len(pdgs))) 423 text.append(' call %sget_me(p, ALPHAS, DSQRT(SCALES2), NHEL, ANS, RETURNCODE)' % self.prefix_info[(pdgs,pid)][0]) 424 text.append( ' else if(procid.gt.0) then !') 425 text.append( ' procid = -1' ) 426 text.append( ' goto 1' ) 427 428 text.append(' endif') 429 #close the function 430 if min_nexternal != max_nexternal: 431 text.append('endif') 432 433 params = self.get_model_parameter(self.model) 434 parameter_setup =[] 435 for key, var in params.items(): 436 parameter_setup.append(' CASE ("%s")\n %s = value\n MP__%s = value' 437 % (key, var, var)) 438 439 440 441 formatting = {'python_information':'\n'.join(info), 442 'smatrixhel': '\n'.join(text), 443 'maxpart': max_nexternal, 444 'nb_me': len(allids), 445 'pdgs': ','.join([str(pdg[i]) if i<len(pdg) else '0' 446 for i in range(max_nexternal) \ 447 for (pdg,pid) in allids]), 448 'prefix':'\',\''.join(allprefix), 449 'parameter_setup': '\n'.join(parameter_setup), 450 'pids': ','.join(str(pid) for (pdg,pid) in allids), 451 } 452 453 454 text = template % formatting 455 fsock = writers.FortranWriter(pjoin(self.dir_path, 'SubProcesses', 'all_matrix.f'),'w') 456 fsock.writelines(text) 457 fsock.close()
458 459 460
461 - def loop_additional_template_setup(self, copy_Source_makefile = True):
462 """ Perform additional actions specific for this class when setting 463 up the template with the copy_template function.""" 464 465 # We must change some files to their version for NLO computations 466 cpfiles= ["Cards/MadLoopParams.dat", 467 "SubProcesses/MadLoopParamReader.f", 468 "SubProcesses/MadLoopParams.inc"] 469 if copy_Source_makefile: 470 cpfiles.append("Source/makefile") 471 472 for file in cpfiles: 473 shutil.copy(os.path.join(self.loop_dir,'StandAlone/', file), 474 os.path.join(self.dir_path, file)) 475 476 cp(pjoin(self.loop_dir,'StandAlone/Cards/MadLoopParams.dat'), 477 pjoin(self.dir_path, 'Cards/MadLoopParams_default.dat')) 478 479 ln(pjoin(self.dir_path, 'Cards','MadLoopParams.dat'), pjoin(self.dir_path,'SubProcesses')) 480 481 # We might need to give a different name to the MadLoop makefile 482 shutil.copy(pjoin(self.loop_dir,'StandAlone','SubProcesses','makefile'), 483 pjoin(self.dir_path, 'SubProcesses',self.madloop_makefile_name)) 484 485 # Write SubProcesses/MadLoop_makefile_definitions with dummy variables 486 # for the non-optimized output 487 link_tir_libs=[] 488 tir_libs=[] 489 490 filePath = pjoin(self.dir_path, 'SubProcesses', 491 'MadLoop_makefile_definitions') 492 calls = self.write_loop_makefile_definitions( 493 writers.MakefileWriter(filePath),link_tir_libs,tir_libs) 494 495 # We need minimal editing of MadLoopCommons.f 496 # For the optimized output, this file will be overwritten once the 497 # availability of COLLIER has been determined. 498 MadLoopCommon = open(os.path.join(self.loop_dir,'StandAlone', 499 "SubProcesses","MadLoopCommons.inc")).read() 500 writer = writers.FortranWriter(os.path.join(self.dir_path, 501 "SubProcesses","MadLoopCommons.f")) 502 writer.writelines(MadLoopCommon%{ 503 'print_banner_commands':self.MadLoop_banner}, context={ 504 'collier_available':False}) 505 writer.close() 506 507 # Copy the whole MadLoop5_resources directory (empty at this stage) 508 if not os.path.exists(pjoin(self.dir_path,'SubProcesses', 509 'MadLoop5_resources')): 510 cp(pjoin(self.loop_dir,'StandAlone','SubProcesses', 511 'MadLoop5_resources'),pjoin(self.dir_path,'SubProcesses')) 512 513 # Link relevant cards from Cards inside the MadLoop5_resources 514 ln(pjoin(self.dir_path,'SubProcesses','MadLoopParams.dat'), 515 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 516 ln(pjoin(self.dir_path,'Cards','param_card.dat'), 517 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 518 ln(pjoin(self.dir_path,'Cards','ident_card.dat'), 519 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 520 521 # And remove check_sa in the SubProcess folder since now there is a 522 # check_sa tailored to each subprocess. 523 if os.path.isfile(pjoin(self.dir_path,'SubProcesses','check_sa.f')): 524 os.remove(pjoin(self.dir_path,'SubProcesses','check_sa.f')) 525 526 cwd = os.getcwd() 527 dirpath = os.path.join(self.dir_path, 'SubProcesses') 528 try: 529 os.chdir(dirpath) 530 except os.error: 531 logger.error('Could not cd to directory %s' % dirpath) 532 return 0 533 534 # Write the cts_mpc.h and cts_mprec.h files imported from CutTools 535 self.write_mp_files(writers.FortranWriter('cts_mprec.h'),\ 536 writers.FortranWriter('cts_mpc.h')) 537 538 # Return to original PWD 539 os.chdir(cwd) 540 541 # We must link the CutTools to the Library folder of the active Template 542 super(LoopProcessExporterFortranSA, self).link_CutTools(self.dir_path)
543 544 # This function is placed here and not in optimized exporterd, 545 # because the same makefile.inc should be used in all cases.
546 - def write_loop_makefile_definitions(self, writer, link_tir_libs, 547 tir_libs,tir_include=[]):
548 """ Create the file makefile which links to the TIR libraries.""" 549 550 file = open(os.path.join(self.loop_dir,'StandAlone', 551 'SubProcesses','MadLoop_makefile_definitions.inc')).read() 552 replace_dict={} 553 replace_dict['link_tir_libs']=' '.join(link_tir_libs) 554 replace_dict['tir_libs']=' '.join(tir_libs) 555 replace_dict['dotf']='%.f' 556 replace_dict['prefix']= self.SubProc_prefix 557 replace_dict['doto']='%.o' 558 replace_dict['tir_include']=' '.join(tir_include) 559 file=file%replace_dict 560 if writer: 561 writer.writelines(file) 562 else: 563 return file
564
565 - def convert_model(self, model, wanted_lorentz = [], 566 wanted_couplings = []):
567 """ Caches the aloha model created here when writing out the aloha 568 fortran subroutine. 569 """ 570 self.get_aloha_model(model) 571 super(LoopProcessExporterFortranSA, self).convert_model(model, 572 wanted_lorentz = wanted_lorentz, wanted_couplings = wanted_couplings)
573
574 - def get_ME_identifier(self, matrix_element, 575 group_number = None, group_elem_number = None):
576 """ A function returning a string uniquely identifying the matrix 577 element given in argument so that it can be used as a prefix to all 578 MadLoop5 subroutines and common blocks related to it. This allows 579 to compile several processes into one library as requested by the 580 BLHA (Binoth LesHouches Accord) guidelines. 581 The arguments group_number and proc_id are just for the LoopInduced 582 output with MadEvent.""" 583 584 # When disabling the loop grouping in the LoopInduced MadEvent output, 585 # we have only the group_number set and the proc_id set to None. In this 586 # case we don't print the proc_id. 587 if (not group_number is None) and group_elem_number is None: 588 return 'ML5_%d_%s_'%(matrix_element.get('processes')[0].get('id'), 589 group_number) 590 elif group_number is None or group_elem_number is None: 591 return 'ML5_%d_'%matrix_element.get('processes')[0].get('id') 592 else: 593 return 'ML5_%d_%s_%s_'%(matrix_element.get('processes')[0].get('id'), 594 group_number, group_elem_number)
595
596 - def get_SubProc_folder_name(self, process, 597 group_number = None, group_elem_number = None):
598 """Returns the name of the SubProcess directory, which can contain 599 the process goup and group element number for the case of loop-induced 600 integration with MadEvent.""" 601 602 # When disabling the loop grouping in the LoopInduced MadEvent output, 603 # we have only the group_number set and the proc_id set to None. In this 604 # case we don't print the proc_id. 605 if not group_number is None and group_elem_number is None: 606 return "%s%d_%s_%s"%(self.SubProc_prefix, process.get('id'), 607 group_number,process.shell_string(print_id=False)) 608 elif group_number is None or group_elem_number is None: 609 return "%s%s" %(self.SubProc_prefix,process.shell_string()) 610 else: 611 return "%s%d_%s_%s_%s"%(self.SubProc_prefix, process.get('id'), 612 group_number, group_elem_number,process.shell_string(print_id=False))
613 614 #=========================================================================== 615 # Set the compiler to be gfortran for the loop processes. 616 #===========================================================================
617 - def compiler_choice(self, compiler=export_v4.default_compiler):
618 """ Different daughter classes might want different compilers. 619 Here, the gfortran compiler is used throughout the compilation 620 (mandatory for CutTools written in f90) """ 621 if isinstance(compiler, str): 622 fortran_compiler = compiler 623 compiler = export_v4.default_compiler 624 compiler['fortran'] = fortran_compiler 625 626 if not compiler['fortran'] is None and not \ 627 any([name in compiler['fortran'] for name in \ 628 ['gfortran','ifort']]): 629 logger.info('For loop processes, the compiler must be fortran90'+\ 630 'compatible, like gfortran.') 631 compiler['fortran'] = 'gfortran' 632 self.set_compiler(compiler,True) 633 else: 634 self.set_compiler(compiler) 635 636 self.set_cpp_compiler(compiler['cpp'])
637
638 - def turn_to_mp_calls(self, helas_calls_list):
639 # Prepend 'MP_' to all the helas calls in helas_calls_list. 640 # Might look like a brutal unsafe implementation, but it is not as 641 # these calls are built from the properties of the HELAS objects and 642 # whether they are evaluated in double or quad precision is none of 643 # their business but only relevant to the output algorithm. 644 # Also the cast to complex masses DCMPLX(*) must be replaced by 645 # CMPLX(*,KIND=16) 646 MP=re.compile(r"(?P<toSub>^.*CALL\s+)",re.IGNORECASE | re.MULTILINE) 647 648 def replaceWith(match_obj): 649 return match_obj.group('toSub')+'MP_'
650 651 DCMPLX=re.compile(r"DCMPLX\((?P<toSub>([^\)]*))\)",\ 652 re.IGNORECASE | re.MULTILINE) 653 654 for i, helas_call in enumerate(helas_calls_list): 655 new_helas_call=MP.sub(replaceWith,helas_call) 656 helas_calls_list[i]=DCMPLX.sub(r"CMPLX(\g<toSub>,KIND=16)",\ 657 new_helas_call)
658 662 670
671 - def make(self):
672 """ Compiles the additional dependences for loop (such as CutTools).""" 673 super(LoopProcessExporterFortranSA, self).make() 674 675 # make CutTools (only necessary with MG option output_dependencies='internal') 676 libdir = os.path.join(self.dir_path,'lib') 677 sourcedir = os.path.join(self.dir_path,'Source') 678 if self.dependencies=='internal': 679 if not os.path.exists(os.path.realpath(pjoin(libdir, 'libcts.a'))) or \ 680 not os.path.exists(os.path.realpath(pjoin(libdir, 'mpmodule.mod'))): 681 if os.path.exists(pjoin(sourcedir,'CutTools')): 682 logger.info('Compiling CutTools (can take a couple of minutes) ...') 683 misc.compile(['CutTools','-j1'], cwd = sourcedir, nb_core=1) 684 logger.info(' ...done.') 685 else: 686 raise MadGraph5Error('Could not compile CutTools because its'+\ 687 ' source directory could not be found in the SOURCE folder.') 688 if not os.path.exists(os.path.realpath(pjoin(libdir, 'libcts.a'))) or \ 689 not os.path.exists(os.path.realpath(pjoin(libdir, 'mpmodule.mod'))): 690 raise MadGraph5Error('CutTools compilation failed.') 691 692 # Verify compatibility between current compiler and the one which was 693 # used when last compiling CutTools (if specified). 694 compiler_log_path = pjoin(os.path.dirname((os.path.realpath(pjoin( 695 libdir, 'libcts.a')))),'compiler_version.log') 696 if os.path.exists(compiler_log_path): 697 compiler_version_used = open(compiler_log_path,'r').read() 698 if not str(misc.get_gfortran_version(misc.detect_current_compiler(\ 699 pjoin(sourcedir,'make_opts')))) in compiler_version_used: 700 if os.path.exists(pjoin(sourcedir,'CutTools')): 701 logger.info('CutTools was compiled with a different fortran'+\ 702 ' compiler. Re-compiling it now...') 703 misc.compile(['cleanCT'], cwd = sourcedir) 704 misc.compile(['CutTools','-j1'], cwd = sourcedir, nb_core=1) 705 logger.info(' ...done.') 706 else: 707 raise MadGraph5Error("CutTools installation in %s"\ 708 %os.path.realpath(pjoin(libdir, 'libcts.a'))+\ 709 " seems to have been compiled with a different compiler than"+\ 710 " the one specified in MG5_aMC. Please recompile CutTools.")
711
712 - def cat_coeff(self, ff_number, frac, is_imaginary, Nc_power, Nc_value=3):
713 """Concatenate the coefficient information to reduce it to 714 (fraction, is_imaginary) """ 715 716 total_coeff = ff_number * frac * fractions.Fraction(Nc_value) ** Nc_power 717 718 return (total_coeff, is_imaginary)
719
720 - def get_amp_to_jamp_map(self, col_amps, n_amps):
721 """ Returns a list with element 'i' being a list of tuples corresponding 722 to all apparition of amplitude number 'i' in the jamp number 'j' 723 with coeff 'coeff_j'. The format of each tuple describing an apparition 724 is (j, coeff_j). where coeff_j is of the form (Fraction, is_imag).""" 725 726 if(isinstance(col_amps,list)): 727 if(col_amps and isinstance(col_amps[0],list)): 728 color_amplitudes=col_amps 729 else: 730 raise MadGraph5Error("Incorrect col_amps argument passed to get_amp_to_jamp_map") 731 else: 732 raise MadGraph5Error("Incorrect col_amps argument passed to get_amp_to_jamp_map") 733 734 # To store the result 735 res_list = [[] for i in range(n_amps)] 736 for i, coeff_list in enumerate(color_amplitudes): 737 for (coefficient, amp_number) in coeff_list: 738 res_list[amp_number-1].append((i,self.cat_coeff(\ 739 coefficient[0],coefficient[1],coefficient[2],coefficient[3]))) 740 741 return res_list
742
743 - def get_color_matrix(self, matrix_element):
744 """Return the color matrix definition lines. This color matrix is of size 745 NLOOPAMPSxNBORNAMPS and allows for squaring individually each Loop and Born 746 amplitude.""" 747 748 logger.info('Computing diagram color coefficients') 749 750 # The two lists have a list of tuples at element 'i' which correspond 751 # to all apparitions of loop amplitude number 'i' in the jampl number 'j' 752 # with coeff 'coeffj'. The format of each tuple describing an apparition 753 # is (j, coeffj). 754 ampl_to_jampl=self.get_amp_to_jamp_map(\ 755 matrix_element.get_loop_color_amplitudes(), 756 matrix_element.get_number_of_loop_amplitudes()) 757 if matrix_element.get('processes')[0].get('has_born'): 758 ampb_to_jampb=self.get_amp_to_jamp_map(\ 759 matrix_element.get_born_color_amplitudes(), 760 matrix_element.get_number_of_born_amplitudes()) 761 else: 762 ampb_to_jampb=ampl_to_jampl 763 # Below is the original color matrix multiplying the JAMPS 764 if matrix_element.get('color_matrix'): 765 ColorMatrixDenom = \ 766 matrix_element.get('color_matrix').get_line_denominators() 767 ColorMatrixNum = [ matrix_element.get('color_matrix').\ 768 get_line_numerators(index, denominator) for 769 (index, denominator) in enumerate(ColorMatrixDenom) ] 770 else: 771 ColorMatrixDenom= [1] 772 ColorMatrixNum = [[1]] 773 774 # Below is the final color matrix output 775 ColorMatrixNumOutput=[] 776 ColorMatrixDenomOutput=[] 777 778 # Now we construct the color factors between each born and loop amplitude 779 # by scanning their contributions to the different jamps. 780 start = time.time() 781 progress_bar = None 782 time_info = False 783 for i, jampl_list in enumerate(ampl_to_jampl): 784 # This can be pretty long for processes with many color flows. 785 # So, if necessary (i.e. for more than 15s), we tell the user the 786 # estimated time for the processing. 787 if i==5: 788 elapsed_time = time.time()-start 789 t = len(ampl_to_jampl)*(elapsed_time/5.0) 790 if t > 10.0: 791 time_info = True 792 logger.info('The color factors computation will take '+\ 793 ' about %s to run. '%str(datetime.timedelta(seconds=int(t)))+\ 794 'Started on %s.'%datetime.datetime.now().strftime(\ 795 "%d-%m-%Y %H:%M")) 796 if logger.getEffectiveLevel()<logging.WARNING: 797 widgets = ['Color computation:', pbar.Percentage(), ' ', 798 pbar.Bar(),' ', pbar.ETA(), ' '] 799 progress_bar = pbar.ProgressBar(widgets=widgets, 800 maxval=len(ampl_to_jampl), fd=sys.stdout) 801 802 if not progress_bar is None: 803 progress_bar.update(i+1) 804 # Flush to force the printout of the progress_bar to be updated 805 sys.stdout.flush() 806 807 line_num=[] 808 line_denom=[] 809 810 # Treat the special case where this specific amplitude contributes to no 811 # color flow at all. So it is zero because of color but not even due to 812 # an accidental cancellation among color flows, but simply because of its 813 # projection to each individual color flow is zero. In such case, the 814 # corresponding jampl_list is empty and all color coefficients must then 815 # be zero. This happens for example in the Higgs Effective Theory model 816 # for the bubble made of a 4-gluon vertex and the effective ggH vertex. 817 if len(jampl_list)==0: 818 line_num=[0]*len(ampb_to_jampb) 819 line_denom=[1]*len(ampb_to_jampb) 820 ColorMatrixNumOutput.append(line_num) 821 ColorMatrixDenomOutput.append(line_denom) 822 continue 823 824 for jampb_list in ampb_to_jampb: 825 real_num=0 826 imag_num=0 827 common_denom=color_amp.ColorMatrix.lcmm(*[abs(ColorMatrixDenom[jampl]* 828 ampl_coeff[0].denominator*ampb_coeff[0].denominator) for 829 ((jampl, ampl_coeff),(jampb,ampb_coeff)) in 830 itertools.product(jampl_list,jampb_list)]) 831 for ((jampl, ampl_coeff),(jampb, ampb_coeff)) in \ 832 itertools.product(jampl_list,jampb_list): 833 # take the numerator and multiply by lcm/denominator 834 # as we will later divide by the lcm. 835 buff_num=ampl_coeff[0].numerator*\ 836 ampb_coeff[0].numerator*ColorMatrixNum[jampl][jampb]*\ 837 abs(common_denom)/(ampl_coeff[0].denominator*\ 838 ampb_coeff[0].denominator*ColorMatrixDenom[jampl]) 839 # Remember that we must take the complex conjugate of 840 # the born jamp color coefficient because we will compute 841 # the square with 2 Re(LoopAmp x BornAmp*) 842 if ampl_coeff[1] and ampb_coeff[1]: 843 real_num=real_num+buff_num 844 elif not ampl_coeff[1] and not ampb_coeff[1]: 845 real_num=real_num+buff_num 846 elif not ampl_coeff[1] and ampb_coeff[1]: 847 imag_num=imag_num-buff_num 848 else: 849 imag_num=imag_num+buff_num 850 assert not (real_num!=0 and imag_num!=0), "MadGraph5_aMC@NLO found a "+\ 851 "color matrix element which has both a real and imaginary part." 852 if imag_num!=0: 853 assert int(imag_num) == imag_num and int(common_denom) == common_denom 854 res=fractions.Fraction(int(imag_num),int(common_denom)) 855 line_num.append(res.numerator) 856 # Negative denominator means imaginary color coef of the 857 # final color matrix 858 line_denom.append(res.denominator*-1) 859 else: 860 assert int(real_num) == real_num and int(common_denom) == common_denom 861 res=fractions.Fraction(int(real_num),int(common_denom)) 862 line_num.append(res.numerator) 863 # Positive denominator means real color coef of the final color matrix 864 line_denom.append(res.denominator) 865 866 ColorMatrixNumOutput.append(line_num) 867 ColorMatrixDenomOutput.append(line_denom) 868 869 if time_info: 870 logger.info('Finished on %s.'%datetime.datetime.now().strftime(\ 871 "%d-%m-%Y %H:%M")) 872 if progress_bar!=None: 873 progress_bar.finish() 874 875 return (ColorMatrixNumOutput,ColorMatrixDenomOutput)
876
877 - def get_context(self,matrix_element):
878 """ Returns the contextual variables which need to be set when 879 pre-processing the template files.""" 880 881 # The nSquaredSO entry of the general replace dictionary should have 882 # been set in write_loopmatrix prior to the first call to this function 883 # However, for cases where the TIRCaching contextual variable is 884 # irrelevant (like in the default output), this might not be the case 885 # so we set it to 1. 886 try: 887 n_squared_split_orders = matrix_element.rep_dict['nSquaredSO'] 888 except (KeyError, AttributeError): 889 n_squared_split_orders = 1 890 891 LoopInduced = not matrix_element.get('processes')[0].get('has_born') 892 self.has_loop_induced = max(LoopInduced, self.has_loop_induced) 893 # Force the computation of loop color flows for loop_induced processes 894 ComputeColorFlows = self.compute_color_flows or LoopInduced 895 # The variable AmplitudeReduction is just to make the contextual 896 # conditions more readable in the include files. 897 AmplitudeReduction = LoopInduced or ComputeColorFlows 898 # Even when not reducing at the amplitude level, the TIR caching 899 # is useful when there is more than one squared split order config. 900 TIRCaching = AmplitudeReduction or n_squared_split_orders>1 901 MadEventOutput = False 902 return {'LoopInduced': LoopInduced, 903 'ComputeColorFlows': ComputeColorFlows, 904 'AmplitudeReduction': AmplitudeReduction, 905 'TIRCaching': TIRCaching, 906 'MadEventOutput': MadEventOutput}
907 908 909 #=========================================================================== 910 # generate_subprocess_directory 911 #===========================================================================
912 - def generate_loop_subprocess(self, matrix_element, fortran_model, 913 group_number = None, proc_id = None, config_map=None, unique_id=None):
914 """Generate the Pxxxxx directory for a loop subprocess in MG4 standalone, 915 including the necessary loop_matrix.f, born_matrix.f and include files. 916 Notice that this is too different from generate_subprocess_directory 917 so that there is no point reusing this mother function. 918 The 'group_number' and 'proc_id' options are only used for the LoopInduced 919 MadEvent output and only to specify the ME_identifier and the P* 920 SubProcess directory name.""" 921 922 cwd = os.getcwd() 923 proc_dir_name = self.get_SubProc_folder_name( 924 matrix_element.get('processes')[0],group_number,proc_id) 925 dirpath = os.path.join(self.dir_path, 'SubProcesses', proc_dir_name) 926 927 try: 928 os.mkdir(dirpath) 929 except os.error as error: 930 logger.warning(error.strerror + " " + dirpath) 931 932 try: 933 os.chdir(dirpath) 934 except os.error: 935 logger.error('Could not cd to directory %s' % dirpath) 936 return 0 937 938 logger.info('Creating files in directory %s' % dirpath) 939 940 if unique_id is None: 941 raise MadGraph5Error('A unique id must be provided to the function'+\ 942 'generate_loop_subprocess of LoopProcessExporterFortranSA.') 943 # Create an include with the unique consecutive ID assigned 944 open('unique_id.inc','w').write( 945 """ integer UNIQUE_ID 946 parameter(UNIQUE_ID=%d)"""%unique_id) 947 948 # Extract number of external particles 949 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 950 951 calls=self.write_loop_matrix_element_v4(None,matrix_element, 952 fortran_model, group_number = group_number, 953 proc_id = proc_id, config_map = config_map) 954 955 # We assume here that all processes must share the same property of 956 # having a born or not, which must be true anyway since these are two 957 # definite different classes of processes which can never be treated on 958 # the same footing. 959 if matrix_element.get('processes')[0].get('has_born'): 960 filename = 'born_matrix.f' 961 calls = self.write_bornmatrix( 962 writers.FortranWriter(filename), 963 matrix_element, 964 fortran_model) 965 966 filename = 'pmass.inc' 967 self.write_pmass_file(writers.FortranWriter(filename), 968 matrix_element) 969 970 filename = 'ngraphs.inc' 971 self.write_ngraphs_file(writers.FortranWriter(filename), 972 len(matrix_element.get_all_amplitudes())) 973 974 # Do not draw the loop diagrams if they are too many. 975 # The user can always decide to do it manually, if really needed 976 loop_diags = [loop_diag for loop_diag in\ 977 matrix_element.get('base_amplitude').get('loop_diagrams')\ 978 if isinstance(loop_diag,LoopDiagram) and loop_diag.get('type') > 0] 979 if len(loop_diags)>5000: 980 logger.info("There are more than 5000 loop diagrams."+\ 981 "Only the first 5000 are drawn.") 982 filename = "loop_matrix.ps" 983 plot = draw.MultiEpsDiagramDrawer(base_objects.DiagramList( 984 loop_diags[:5000]),filename, 985 model=matrix_element.get('processes')[0].get('model'),amplitude='') 986 logger.info("Drawing loop Feynman diagrams for " + \ 987 matrix_element.get('processes')[0].nice_string()) 988 plot.draw() 989 990 if matrix_element.get('processes')[0].get('has_born'): 991 filename = "born_matrix.ps" 992 plot = draw.MultiEpsDiagramDrawer(matrix_element.get('base_amplitude').\ 993 get('born_diagrams'), 994 filename, 995 model=matrix_element.get('processes')[0].\ 996 get('model'), 997 amplitude='') 998 logger.info("Generating born Feynman diagrams for " + \ 999 matrix_element.get('processes')[0].nice_string(\ 1000 print_weighted=False)) 1001 plot.draw() 1002 1003 self.link_files_from_Subprocesses(self.get_SubProc_folder_name( 1004 matrix_element.get('processes')[0],group_number,proc_id)) 1005 1006 # Return to original PWD 1007 os.chdir(cwd) 1008 1009 if not calls: 1010 calls = 0 1011 return calls
1012 1033
1034 - def generate_general_replace_dict(self,matrix_element, 1035 group_number = None, proc_id = None):
1036 """Generates the entries for the general replacement dictionary used 1037 for the different output codes for this exporter.The arguments 1038 group_number and proc_id are just for the LoopInduced output with MadEvent.""" 1039 1040 dict={} 1041 # A general process prefix which appears in front of all MadLooop 1042 # subroutines and common block so that several processes can be compiled 1043 # together into one library, as necessary to follow BLHA guidelines. 1044 1045 dict['proc_prefix'] = self.get_ME_identifier(matrix_element, 1046 group_number = group_number, group_elem_number = proc_id) 1047 1048 if 'prefix' in self.cmd_options and self.cmd_options['prefix'] in ['int','proc']: 1049 for proc in matrix_element.get('processes'): 1050 ids = [l.get('id') for l in proc.get('legs_with_decays')] 1051 self.prefix_info[tuple(ids),proc.get('id')] = [dict['proc_prefix'], proc.get_tag()] 1052 1053 # The proc_id is used for MadEvent grouping, so none of our concern here 1054 # and it is simply set to an empty string. 1055 dict['proc_id'] = '' 1056 # Extract version number and date from VERSION file 1057 info_lines = self.get_mg5_info_lines() 1058 dict['info_lines'] = info_lines 1059 # Extract process info lines 1060 process_lines = self.get_process_info_lines(matrix_element) 1061 dict['process_lines'] = process_lines 1062 # Extract number of external particles 1063 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 1064 dict['nexternal'] = nexternal 1065 dict['nincoming'] = ninitial 1066 # Extract ncomb 1067 ncomb = matrix_element.get_helicity_combinations() 1068 dict['ncomb'] = ncomb 1069 # Extract nloopamps 1070 nloopamps = matrix_element.get_number_of_loop_amplitudes() 1071 dict['nloopamps'] = nloopamps 1072 # Extract nloopdiags 1073 nloopdiags = len(matrix_element.get('diagrams')) 1074 dict['nloopdiags'] = nloopdiags 1075 # Extract nctamps 1076 nctamps = matrix_element.get_number_of_CT_amplitudes() 1077 dict['nctamps'] = nctamps 1078 # Extract nwavefuncs 1079 nwavefuncs = matrix_element.get_number_of_external_wavefunctions() 1080 dict['nwavefuncs'] = nwavefuncs 1081 # Set format of the double precision 1082 dict['real_dp_format']='real*8' 1083 dict['real_mp_format']='real*16' 1084 # Set format of the complex 1085 dict['complex_dp_format']='complex*16' 1086 dict['complex_mp_format']='complex*32' 1087 # Set format of the masses 1088 dict['mass_dp_format'] = dict['complex_dp_format'] 1089 dict['mass_mp_format'] = dict['complex_mp_format'] 1090 # Fill in default values for the placeholders for the madevent 1091 # loop-induced output 1092 dict['nmultichannels'] = 0 1093 dict['nmultichannel_configs'] = 0 1094 dict['config_map_definition'] = '' 1095 dict['config_index_map_definition'] = '' 1096 # Color matrix size 1097 # For loop induced processes it is NLOOPAMPSxNLOOPAMPS and otherwise 1098 # it is NLOOPAMPSxNBORNAMPS 1099 # Also, how to access the number of Born squared order contributions 1100 1101 if matrix_element.get('processes')[0].get('has_born'): 1102 dict['color_matrix_size'] = 'nbornamps' 1103 dict['get_nsqso_born']=\ 1104 "include 'nsqso_born.inc'" 1105 else: 1106 dict['get_nsqso_born']="""INTEGER NSQSO_BORN 1107 PARAMETER (NSQSO_BORN=0) 1108 """ 1109 dict['color_matrix_size'] = 'nloopamps' 1110 1111 # These placeholders help to have as many common templates for the 1112 # output of the loop induced processes and those with a born 1113 # contribution. 1114 if matrix_element.get('processes')[0].get('has_born'): 1115 # Extract nbornamps 1116 nbornamps = matrix_element.get_number_of_born_amplitudes() 1117 dict['nbornamps'] = nbornamps 1118 dict['ncomb_helas_objs'] = ',ncomb' 1119 dict['nbornamps_decl'] = \ 1120 """INTEGER NBORNAMPS 1121 PARAMETER (NBORNAMPS=%d)"""%nbornamps 1122 dict['nBornAmps'] = nbornamps 1123 1124 else: 1125 dict['ncomb_helas_objs'] = '' 1126 dict['dp_born_amps_decl'] = '' 1127 dict['dp_born_amps_decl_in_mp'] = '' 1128 dict['copy_mp_to_dp_born_amps'] = '' 1129 dict['mp_born_amps_decl'] = '' 1130 dict['nbornamps_decl'] = '' 1131 dict['nbornamps'] = 0 1132 dict['nBornAmps'] = 0 1133 1134 return dict
1135
1136 - def write_loop_matrix_element_v4(self, writer, matrix_element, fortran_model, 1137 group_number = None, proc_id = None, config_map = None):
1138 """ Writes loop_matrix.f, CT_interface.f, loop_num.f and 1139 mp_born_amps_and_wfs. 1140 The arguments group_number and proc_id are just for the LoopInduced 1141 output with MadEvent and only used in get_ME_identifier. 1142 """ 1143 1144 # Create the necessary files for the loop matrix element subroutine 1145 1146 if config_map: 1147 raise MadGraph5Error('The default loop output cannot be used with'+\ 1148 'MadEvent and cannot compute the AMP2 for multi-channeling.') 1149 1150 if not isinstance(fortran_model,\ 1151 helas_call_writers.FortranUFOHelasCallWriter): 1152 raise MadGraph5Error('The loop fortran output can only'+\ 1153 ' work with a UFO Fortran model') 1154 1155 LoopFortranModel = helas_call_writers.FortranUFOHelasCallWriter( 1156 argument=fortran_model.get('model'), 1157 hel_sum=matrix_element.get('processes')[0].get('has_born')) 1158 1159 # Compute the analytical information of the loop wavefunctions in the 1160 # loop helas matrix elements using the cached aloha model to reuse 1161 # as much as possible the aloha computations already performed for 1162 # writing out the aloha fortran subroutines. 1163 matrix_element.compute_all_analytic_information( 1164 self.get_aloha_model(matrix_element.get('processes')[0].get('model'))) 1165 1166 # Initialize a general replacement dictionary with entries common to 1167 # many files generated here. 1168 matrix_element.rep_dict = self.generate_general_replace_dict( 1169 matrix_element, group_number = group_number, proc_id = proc_id) 1170 1171 # Extract max number of loop couplings (specific to this output type) 1172 matrix_element.rep_dict['maxlcouplings']= \ 1173 matrix_element.find_max_loop_coupling() 1174 # The born amp declaration suited for also outputing the loop-induced 1175 # processes as well. 1176 if matrix_element.get('processes')[0].get('has_born'): 1177 matrix_element.rep_dict['dp_born_amps_decl_in_mp'] = \ 1178 matrix_element.rep_dict['complex_dp_format']+" DPAMP(NBORNAMPS,NCOMB)"+\ 1179 "\n common/%sAMPS/DPAMP"%matrix_element.rep_dict['proc_prefix'] 1180 matrix_element.rep_dict['dp_born_amps_decl'] = \ 1181 matrix_element.rep_dict['complex_dp_format']+" AMP(NBORNAMPS,NCOMB)"+\ 1182 "\n common/%sAMPS/AMP"%matrix_element.rep_dict['proc_prefix'] 1183 matrix_element.rep_dict['mp_born_amps_decl'] = \ 1184 matrix_element.rep_dict['complex_mp_format']+" AMP(NBORNAMPS,NCOMB)"+\ 1185 "\n common/%sMP_AMPS/AMP"%matrix_element.rep_dict['proc_prefix'] 1186 matrix_element.rep_dict['copy_mp_to_dp_born_amps'] = \ 1187 '\n'.join(['DO I=1,NBORNAMPS','DPAMP(I,H)=AMP(I,H)','ENDDO']) 1188 1189 if writer: 1190 raise MadGraph5Error('Matrix output mode no longer supported.') 1191 1192 filename = 'loop_matrix.f' 1193 calls = self.write_loopmatrix(writers.FortranWriter(filename), 1194 matrix_element, 1195 LoopFortranModel) 1196 1197 # Write out the proc_prefix in a file, this is quite handy 1198 proc_prefix_writer = writers.FortranWriter('proc_prefix.txt','w') 1199 proc_prefix_writer.write(matrix_element.rep_dict['proc_prefix']) 1200 proc_prefix_writer.close() 1201 1202 filename = 'check_sa.f' 1203 self.write_check_sa(writers.FortranWriter(filename),matrix_element) 1204 1205 filename = 'CT_interface.f' 1206 self.write_CT_interface(writers.FortranWriter(filename),\ 1207 matrix_element) 1208 1209 1210 1211 filename = 'improve_ps.f' 1212 calls = self.write_improve_ps(writers.FortranWriter(filename), 1213 matrix_element) 1214 1215 filename = 'loop_num.f' 1216 self.write_loop_num(writers.FortranWriter(filename),\ 1217 matrix_element,LoopFortranModel) 1218 1219 filename = 'mp_born_amps_and_wfs.f' 1220 self.write_born_amps_and_wfs(writers.FortranWriter(filename),\ 1221 matrix_element,LoopFortranModel) 1222 1223 # Extract number of external particles 1224 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 1225 filename = 'nexternal.inc' 1226 self.write_nexternal_file(writers.FortranWriter(filename), 1227 nexternal, ninitial) 1228 1229 filename = 'process_info.inc' 1230 self.write_process_info_file(writers.FortranWriter(filename), 1231 matrix_element) 1232 return calls
1233
1234 - def write_process_info_file(self, writer, matrix_element):
1235 """A small structural function to write the include file specifying some 1236 process characteristics.""" 1237 1238 model = matrix_element.get('processes')[0].get('model') 1239 process_info = {} 1240 # The maximum spin of any particle connected (or directly running in) 1241 # any loop of this matrix element. This is important because there is 1242 # some limitation in the stability tests that can be performed when this 1243 # maximum spin is above 3 (vectors). Also CutTools has limitations in 1244 # this regard. 1245 process_info['max_spin_connected_to_loop']=\ 1246 matrix_element.get_max_spin_connected_to_loop() 1247 1248 process_info['max_spin_external_particle']= max( 1249 model.get_particle(l.get('id')).get('spin') for l in 1250 matrix_element.get('processes')[0].get('legs')) 1251 1252 proc_include = \ 1253 """ 1254 INTEGER MAX_SPIN_CONNECTED_TO_LOOP 1255 PARAMETER(MAX_SPIN_CONNECTED_TO_LOOP=%(max_spin_connected_to_loop)d) 1256 INTEGER MAX_SPIN_EXTERNAL_PARTICLE 1257 PARAMETER(MAX_SPIN_EXTERNAL_PARTICLE=%(max_spin_external_particle)d) 1258 """%process_info 1259 1260 writer.writelines(proc_include)
1261
1262 - def generate_subprocess_directory(self, matrix_element, fortran_model):
1263 """ To overload the default name for this function such that the correct 1264 function is used when called from the command interface """ 1265 1266 self.unique_id +=1 1267 return self.generate_loop_subprocess(matrix_element,fortran_model, 1268 unique_id=self.unique_id)
1269
1270 - def write_check_sa(self, writer, matrix_element):
1271 """Writes out the steering code check_sa. In the optimized output mode, 1272 All the necessary entries in the replace_dictionary have already been 1273 set in write_loopmatrix because it is only there that one has access to 1274 the information about split orders.""" 1275 replace_dict = copy.copy(matrix_element.rep_dict) 1276 for key in ['print_so_born_results','print_so_loop_results', 1277 'write_so_born_results','write_so_loop_results','set_coupling_target']: 1278 if key not in list(replace_dict.keys()): 1279 replace_dict[key]='' 1280 1281 if matrix_element.get('processes')[0].get('has_born'): 1282 file = open(os.path.join(self.template_dir,'check_sa.inc')).read() 1283 else: 1284 file = open(os.path.join(self.template_dir,\ 1285 'check_sa_loop_induced.inc')).read() 1286 file=file%replace_dict 1287 writer.writelines(file) 1288 1289 # We can always write the f2py wrapper if present (in loop optimized mode, it is) 1290 if not os.path.isfile(pjoin(self.template_dir,'check_py.f.inc')): 1291 return 1292 1293 file = open(os.path.join(self.template_dir,\ 1294 'check_py.f.inc')).read() 1295 1296 if 'prefix' in self.cmd_options and self.cmd_options['prefix'] in ['int','proc']: 1297 replace_dict['prefix_routine'] = replace_dict['proc_prefix'] 1298 else: 1299 replace_dict['prefix_routine'] = '' 1300 file=file%replace_dict 1301 new_path = writer.name.replace('check_sa.f', 'f2py_wrapper.f') 1302 new_writer = writer.__class__(new_path, 'w') 1303 new_writer.writelines(file) 1304 1305 file = open(os.path.join(self.template_dir,\ 1306 'check_sa.py.inc')).read() 1307 # For now just put in an empty PS point but in the future, maybe generate 1308 # a valid one already here by default 1309 curr_proc = matrix_element.get('processes')[0] 1310 random_PSpoint_python_formatted = \ 1311 """# Specify your chosen PS point below. If you leave it filled with None, then the script will attempt to read it from the file PS.input. 1312 p= [[None,]*4]*%d"""%len(curr_proc.get('legs')) 1313 1314 process_definition_string = curr_proc.nice_string().replace('Process:','') 1315 file=file.format(random_PSpoint_python_formatted,process_definition_string, 1316 replace_dict['proc_prefix'].lower()) 1317 new_path = writer.name.replace('check_sa.f', 'check_sa.py') 1318 new_writer = open(new_path, 'w') 1319 new_writer.writelines(file) 1320 # Make it executable 1321 os.chmod(new_path, os.stat(new_path).st_mode | stat.S_IEXEC)
1322
1323 - def write_improve_ps(self, writer, matrix_element):
1324 """ Write out the improve_ps subroutines which modify the PS point 1325 given in input and slightly deform it to achieve exact onshellness on 1326 all external particles as well as perfect energy-momentum conservation""" 1327 replace_dict = copy.copy(matrix_element.rep_dict) 1328 1329 (nexternal,ninitial)=matrix_element.get_nexternal_ninitial() 1330 replace_dict['ninitial']=ninitial 1331 mass_list=matrix_element.get_external_masses()[:-2] 1332 mp_variable_prefix = check_param_card.ParamCard.mp_prefix 1333 1334 # Write the quadruple precision version of this routine only. 1335 replace_dict['real_format']=replace_dict['real_mp_format'] 1336 replace_dict['mp_prefix']='MP_' 1337 replace_dict['exp_letter']='e' 1338 replace_dict['mp_specifier']='_16' 1339 replace_dict['coupl_inc_name']='mp_coupl.inc' 1340 replace_dict['masses_def']='\n'.join(['MASSES(%(i)d)=%(prefix)s%(m)s'\ 1341 %{'i':i+1,'m':m, 'prefix':mp_variable_prefix} for \ 1342 i, m in enumerate(mass_list)]) 1343 file_mp = open(os.path.join(self.template_dir,'improve_ps.inc')).read() 1344 file_mp=file_mp%replace_dict 1345 # 1346 writer.writelines(file_mp)
1347
1348 - def write_loop_num(self, writer, matrix_element,fortran_model):
1349 """ Create the file containing the core subroutine called by CutTools 1350 which contains the Helas calls building the loop""" 1351 1352 if not matrix_element.get('processes') or \ 1353 not matrix_element.get('diagrams'): 1354 return 0 1355 1356 # Set lowercase/uppercase Fortran code 1357 writers.FortranWriter.downcase = False 1358 1359 file = open(os.path.join(self.template_dir,'loop_num.inc')).read() 1360 1361 replace_dict = copy.copy(matrix_element.rep_dict) 1362 1363 loop_helas_calls=fortran_model.get_loop_amplitude_helas_calls(matrix_element) 1364 replace_dict['maxlcouplings']=matrix_element.find_max_loop_coupling() 1365 replace_dict['loop_helas_calls'] = "\n".join(loop_helas_calls) 1366 1367 # The squaring is only necessary for the processes with born where the 1368 # sum over helicities is done before sending the numerator to CT. 1369 dp_squaring_lines=['DO I=1,NBORNAMPS', 1370 'CFTOT=DCMPLX(CF_N(AMPLNUM,I)/DBLE(ABS(CF_D(AMPLNUM,I))),0.0d0)', 1371 'IF(CF_D(AMPLNUM,I).LT.0) CFTOT=CFTOT*IMAG1', 1372 'RES=RES+CFTOT*BUFF*DCONJG(AMP(I,H))','ENDDO'] 1373 mp_squaring_lines=['DO I=1,NBORNAMPS', 1374 'CFTOT=CMPLX(CF_N(AMPLNUM,I)/(1.0E0_16*ABS(CF_D(AMPLNUM,I))),0.0E0_16,KIND=16)', 1375 'IF(CF_D(AMPLNUM,I).LT.0) CFTOT=CFTOT*IMAG1', 1376 'QPRES=QPRES+CFTOT*BUFF*CONJG(AMP(I,H))','ENDDO'] 1377 if matrix_element.get('processes')[0].get('has_born'): 1378 replace_dict['dp_squaring']='\n'.join(dp_squaring_lines) 1379 replace_dict['mp_squaring']='\n'.join(mp_squaring_lines) 1380 else: 1381 replace_dict['dp_squaring']='RES=BUFF' 1382 replace_dict['mp_squaring']='QPRES=BUFF' 1383 1384 # Prepend MP_ to all helas calls. 1385 self.turn_to_mp_calls(loop_helas_calls) 1386 replace_dict['mp_loop_helas_calls'] = "\n".join(loop_helas_calls) 1387 1388 file=file%replace_dict 1389 1390 if writer: 1391 writer.writelines(file) 1392 else: 1393 return file
1394
1395 - def write_CT_interface(self, writer, matrix_element, optimized_output=False):
1396 """ Create the file CT_interface.f which contains the subroutine defining 1397 the loop HELAS-like calls along with the general interfacing subroutine. 1398 It is used to interface against any OPP tool, including Samurai and Ninja.""" 1399 1400 files=[] 1401 1402 # First write CT_interface which interfaces MG5 with CutTools. 1403 replace_dict=copy.copy(matrix_element.rep_dict) 1404 1405 # We finalize CT result differently wether we used the built-in 1406 # squaring against the born. 1407 if matrix_element.get('processes')[0].get('has_born'): 1408 replace_dict['finalize_CT']='\n'.join([\ 1409 'RES(%d)=NORMALIZATION*2.0d0*DBLE(RES(%d))'%(i,i) for i in range(1,4)]) 1410 else: 1411 replace_dict['finalize_CT']='\n'.join([\ 1412 'RES(%d)=NORMALIZATION*RES(%d)'%(i,i) for i in range(1,4)]) 1413 1414 file = open(os.path.join(self.template_dir,'CT_interface.inc')).read() 1415 1416 file = file % replace_dict 1417 files.append(file) 1418 1419 # Now collect the different kind of subroutines needed for the 1420 # loop HELAS-like calls. 1421 HelasLoopAmpsCallKeys=matrix_element.get_used_helas_loop_amps() 1422 1423 for callkey in HelasLoopAmpsCallKeys: 1424 replace_dict=copy.copy(matrix_element.rep_dict) 1425 # Add to this dictionary all other attribute common to all 1426 # HELAS-like loop subroutines. 1427 if matrix_element.get('processes')[0].get('has_born'): 1428 replace_dict['validh_or_nothing']=',validh' 1429 else: 1430 replace_dict['validh_or_nothing']='' 1431 # In the optimized output, the number of couplings in the loop is 1432 # not specified so we only treat it here if necessary: 1433 if len(callkey)>2: 1434 replace_dict['ncplsargs']=callkey[2] 1435 cplsargs="".join(["C%d,MP_C%d, "%(i,i) for i in range(1,callkey[2]+1)]) 1436 replace_dict['cplsargs']=cplsargs 1437 cplsdecl="".join(["C%d, "%i for i in range(1,callkey[2]+1)])[:-2] 1438 replace_dict['cplsdecl']=cplsdecl 1439 mp_cplsdecl="".join(["MP_C%d, "%i for i in range(1,callkey[2]+1)])[:-2] 1440 replace_dict['mp_cplsdecl']=mp_cplsdecl 1441 cplset="\n".join(["\n".join(["LC(%d)=C%d"%(i,i),\ 1442 "MP_LC(%d)=MP_C%d"%(i,i)])\ 1443 for i in range(1,callkey[2]+1)]) 1444 replace_dict['cplset']=cplset 1445 1446 replace_dict['nloopline']=callkey[0] 1447 wfsargs="".join(["W%d, "%i for i in range(1,callkey[1]+1)]) 1448 replace_dict['wfsargs']=wfsargs 1449 # We don't pass the multiple precision mass in the optimized_output 1450 if not optimized_output: 1451 margs="".join(["M%d,MP_M%d, "%(i,i) for i in range(1,callkey[0]+1)]) 1452 else: 1453 margs="".join(["M%d, "%i for i in range(1,callkey[0]+1)]) 1454 replace_dict['margs']=margs 1455 wfsargsdecl="".join([("W%d, "%i) for i in range(1,callkey[1]+1)])[:-2] 1456 replace_dict['wfsargsdecl']=wfsargsdecl 1457 margsdecl="".join(["M%d, "%i for i in range(1,callkey[0]+1)])[:-2] 1458 replace_dict['margsdecl']=margsdecl 1459 mp_margsdecl="".join(["MP_M%d, "%i for i in range(1,callkey[0]+1)])[:-2] 1460 replace_dict['mp_margsdecl']=mp_margsdecl 1461 weset="\n".join([("WE("+str(i)+")=W"+str(i)) for \ 1462 i in range(1,callkey[1]+1)]) 1463 replace_dict['weset']=weset 1464 weset="\n".join([("WE(%d)=W%d"%(i,i)) for i in range(1,callkey[1]+1)]) 1465 replace_dict['weset']=weset 1466 msetlines=["M2L(1)=M%d**2"%(callkey[0]),] 1467 mset="\n".join(msetlines+["M2L(%d)=M%d**2"%(i,i-1) for \ 1468 i in range(2,callkey[0]+1)]) 1469 replace_dict['mset']=mset 1470 mset2lines=["ML(1)=M%d"%(callkey[0]),"ML(2)=M%d"%(callkey[0]), 1471 "MP_ML(1)=MP_M%d"%(callkey[0]),"MP_ML(2)=MP_M%d"%(callkey[0])] 1472 mset2="\n".join(mset2lines+["\n".join(["ML(%d)=M%d"%(i,i-2), 1473 "MP_ML(%d)=MP_M%d"%(i,i-2)]) for \ 1474 i in range(3,callkey[0]+3)]) 1475 replace_dict['mset2']=mset2 1476 replace_dict['nwfsargs'] = callkey[1] 1477 if callkey[0]==callkey[1]: 1478 replace_dict['nwfsargs_header'] = "" 1479 replace_dict['pairingargs']="" 1480 replace_dict['pairingdecl']="" 1481 pairingset="""DO I=1,NLOOPLINE 1482 PAIRING(I)=1 1483 ENDDO 1484 """ 1485 replace_dict['pairingset']=pairingset 1486 else: 1487 replace_dict['nwfsargs_header'] = '_%d'%callkey[1] 1488 pairingargs="".join([("P"+str(i)+", ") for i in \ 1489 range(1,callkey[0]+1)]) 1490 replace_dict['pairingargs']=pairingargs 1491 pairingdecl="integer "+"".join([("P"+str(i)+", ") for i in \ 1492 range(1,callkey[0]+1)])[:-2] 1493 replace_dict['pairingdecl']=pairingdecl 1494 pairingset="\n".join([("PAIRING("+str(i)+")=P"+str(i)) for \ 1495 i in range(1,callkey[0]+1)]) 1496 replace_dict['pairingset']=pairingset 1497 1498 file = open(os.path.join(self.template_dir,\ 1499 'helas_loop_amplitude.inc')).read() 1500 file = file % replace_dict 1501 files.append(file) 1502 1503 file="\n".join(files) 1504 1505 if writer: 1506 writer.writelines(file,context=self.get_context(matrix_element)) 1507 else: 1508 return file
1509 1510 # Helper function to split HELAS CALLS in dedicated subroutines placed 1511 # in different files.
1512 - def split_HELASCALLS(self, writer, replace_dict, template_name, masterfile, \ 1513 helas_calls, entry_name, bunch_name,n_helas=2000, 1514 required_so_broadcaster = 'LOOP_REQ_SO_DONE', 1515 continue_label = 1000, momenta_array_name='P', 1516 context={}):
1517 """ Finish the code generation with splitting. 1518 Split the helas calls in the argument helas_calls into bunches of 1519 size n_helas and place them in dedicated subroutine with name 1520 <bunch_name>_i. Also setup the corresponding calls to these subroutine 1521 in the replace_dict dictionary under the entry entry_name. 1522 The context specified will be forwarded to the the fileWriter.""" 1523 helascalls_replace_dict=copy.copy(replace_dict) 1524 helascalls_replace_dict['bunch_name']=bunch_name 1525 helascalls_files=[] 1526 for i, k in enumerate(range(0, len(helas_calls), n_helas)): 1527 helascalls_replace_dict['bunch_number']=i+1 1528 helascalls_replace_dict['helas_calls']=\ 1529 '\n'.join(helas_calls[k:k + n_helas]) 1530 helascalls_replace_dict['required_so_broadcaster']=\ 1531 required_so_broadcaster 1532 helascalls_replace_dict['continue_label']=continue_label 1533 new_helascalls_file = open(os.path.join(self.template_dir,\ 1534 template_name)).read() 1535 new_helascalls_file = new_helascalls_file % helascalls_replace_dict 1536 helascalls_files.append(new_helascalls_file) 1537 # Setup the call to these HELASCALLS subroutines in loop_matrix.f 1538 helascalls_calls = [ "CALL %s%s_%d(%s,NHEL,H,IC)"%\ 1539 (replace_dict['proc_prefix'] ,bunch_name,a+1,momenta_array_name) \ 1540 for a in range(len(helascalls_files))] 1541 replace_dict[entry_name]='\n'.join(helascalls_calls) 1542 if writer: 1543 for i, helascalls_file in enumerate(helascalls_files): 1544 filename = '%s_%d.f'%(bunch_name,i+1) 1545 writers.FortranWriter(filename).writelines(helascalls_file, 1546 context=context) 1547 else: 1548 masterfile='\n'.join([masterfile,]+helascalls_files) 1549 1550 return masterfile
1551
1552 - def write_loopmatrix(self, writer, matrix_element, fortran_model, 1553 noSplit=False):
1554 """Create the loop_matrix.f file.""" 1555 1556 if not matrix_element.get('processes') or \ 1557 not matrix_element.get('diagrams'): 1558 return 0 1559 1560 # Set lowercase/uppercase Fortran code 1561 1562 writers.FortranWriter.downcase = False 1563 1564 replace_dict = copy.copy(matrix_element.rep_dict) 1565 1566 # Extract overall denominator 1567 # Averaging initial state color, spin, and identical FS particles 1568 den_factor_line = self.get_den_factor_line(matrix_element) 1569 replace_dict['den_factor_line'] = den_factor_line 1570 # When the user asks for the polarized matrix element we must 1571 # multiply back by the helicity averaging factor 1572 replace_dict['hel_avg_factor'] = matrix_element.get_hel_avg_factor() 1573 replace_dict['beamone_helavgfactor'], replace_dict['beamtwo_helavgfactor'] =\ 1574 matrix_element.get_beams_hel_avg_factor() 1575 1576 # These entries are specific for the output for loop-induced processes 1577 # Also sets here the details of the squaring of the loop ampltiudes 1578 # with the born or the loop ones. 1579 if not matrix_element.get('processes')[0].get('has_born'): 1580 replace_dict['compute_born']=\ 1581 """C There is of course no born for loop induced processes 1582 ANS(0)=0.0d0 1583 """ 1584 replace_dict['set_reference']='\n'.join([ 1585 'C For loop-induced, the reference for comparison is set later'+\ 1586 ' from the total contribution of the previous PS point considered.', 1587 'C But you can edit here the value to be used for the first PS point.', 1588 'if (NPSPOINTS.eq.0) then','ref=1.0d-50','else', 1589 'ref=nextRef/DBLE(NPSPOINTS)','endif']) 1590 replace_dict['loop_induced_setup'] = '\n'.join([ 1591 'HELPICKED_BU=HELPICKED','HELPICKED=H','MP_DONE=.FALSE.', 1592 'IF(SKIPLOOPEVAL) THEN','GOTO 1227','ENDIF']) 1593 replace_dict['loop_induced_finalize'] = \ 1594 ("""DO I=NCTAMPS+1,NLOOPAMPS 1595 IF((CTMODERUN.NE.-1).AND..NOT.CHECKPHASE.AND.(.NOT.S(I))) THEN 1596 WRITE(*,*) '##W03 WARNING Contribution ',I 1597 WRITE(*,*) ' is unstable for helicity ',H 1598 ENDIF 1599 C IF(.NOT.%(proc_prefix)sISZERO(ABS(AMPL(2,I))+ABS(AMPL(3,I)),REF,-1,H)) THEN 1600 C WRITE(*,*) '##W04 WARNING Contribution ',I,' for helicity ',H,' has a contribution to the poles.' 1601 C WRITE(*,*) 'Finite contribution = ',AMPL(1,I) 1602 C WRITE(*,*) 'single pole contribution = ',AMPL(2,I) 1603 C WRITE(*,*) 'double pole contribution = ',AMPL(3,I) 1604 C ENDIF 1605 ENDDO 1606 1227 CONTINUE 1607 HELPICKED=HELPICKED_BU""")%replace_dict 1608 replace_dict['loop_helas_calls']="" 1609 replace_dict['nctamps_or_nloopamps']='nloopamps' 1610 replace_dict['nbornamps_or_nloopamps']='nloopamps' 1611 replace_dict['squaring']=\ 1612 """ANS(1)=ANS(1)+DBLE(CFTOT*AMPL(1,I)*DCONJG(AMPL(1,J))) 1613 IF (J.EQ.1) THEN 1614 ANS(2)=ANS(2)+DBLE(CFTOT*AMPL(2,I))+DIMAG(CFTOT*AMPL(2,I)) 1615 ANS(3)=ANS(3)+DBLE(CFTOT*AMPL(3,I))+DIMAG(CFTOT*AMPL(3,I)) 1616 ENDIF""" 1617 else: 1618 replace_dict['compute_born']=\ 1619 """C Compute the born, for a specific helicity if asked so. 1620 call %(proc_prefix)ssmatrixhel(P_USER,USERHEL,ANS(0)) 1621 """%matrix_element.rep_dict 1622 replace_dict['set_reference']=\ 1623 """C We chose to use the born evaluation for the reference 1624 call %(proc_prefix)ssmatrix(p,ref)"""%matrix_element.rep_dict 1625 replace_dict['loop_induced_helas_calls'] = "" 1626 replace_dict['loop_induced_finalize'] = "" 1627 replace_dict['loop_induced_setup'] = "" 1628 replace_dict['nctamps_or_nloopamps']='nctamps' 1629 replace_dict['nbornamps_or_nloopamps']='nbornamps' 1630 replace_dict['squaring']='\n'.join(['DO K=1,3', 1631 'ANS(K)=ANS(K)+2.0d0*DBLE(CFTOT*AMPL(K,I)*DCONJG(AMP(J,H)))', 1632 'ENDDO']) 1633 1634 # Write a dummy nsquaredSO.inc which is used in the default 1635 # loop_matrix.f code (even though it does not support split orders evals) 1636 # just to comply with the syntax expected from the external code using MadLoop. 1637 writers.FortranWriter('nsquaredSO.inc').writelines( 1638 """INTEGER NSQUAREDSO 1639 PARAMETER (NSQUAREDSO=0)""") 1640 1641 # Actualize results from the loops computed. Only necessary for 1642 # processes with a born. 1643 actualize_ans=[] 1644 if matrix_element.get('processes')[0].get('has_born'): 1645 actualize_ans.append("DO I=NCTAMPS+1,NLOOPAMPS") 1646 actualize_ans.extend("ANS(%d)=ANS(%d)+AMPL(%d,I)"%(i,i,i) for i \ 1647 in range(1,4)) 1648 actualize_ans.append(\ 1649 "IF((CTMODERUN.NE.-1).AND..NOT.CHECKPHASE.AND.(.NOT.S(I))) THEN") 1650 actualize_ans.append(\ 1651 "WRITE(*,*) '##W03 WARNING Contribution ',I,' is unstable.'") 1652 actualize_ans.extend(["ENDIF","ENDDO"]) 1653 replace_dict['actualize_ans']='\n'.join(actualize_ans) 1654 else: 1655 replace_dict['actualize_ans']=\ 1656 ("""C We add five powers to the reference value to loosen a bit the vanishing pole check. 1657 C IF(.NOT.(CHECKPHASE.OR.(.NOT.HELDOUBLECHECKED)).AND..NOT.%(proc_prefix)sISZERO(ABS(ANS(2))+ABS(ANS(3)),ABS(ANS(1))*(10.0d0**5),-1,H)) THEN 1658 C WRITE(*,*) '##W05 WARNING Found a PS point with a contribution to the single pole.' 1659 C WRITE(*,*) 'Finite contribution = ',ANS(1) 1660 C WRITE(*,*) 'single pole contribution = ',ANS(2) 1661 C WRITE(*,*) 'double pole contribution = ',ANS(3) 1662 C ENDIF""")%replace_dict 1663 1664 # Write out the color matrix 1665 (CMNum,CMDenom) = self.get_color_matrix(matrix_element) 1666 CMWriter=open(pjoin('..','MadLoop5_resources', 1667 '%(proc_prefix)sColorNumFactors.dat'%matrix_element.rep_dict),'w') 1668 for ColorLine in CMNum: 1669 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 1670 CMWriter.close() 1671 CMWriter=open(pjoin('..','MadLoop5_resources', 1672 '%(proc_prefix)sColorDenomFactors.dat'%matrix_element.rep_dict),'w') 1673 for ColorLine in CMDenom: 1674 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 1675 CMWriter.close() 1676 1677 # Write out the helicity configurations 1678 HelConfigs=matrix_element.get_helicity_matrix() 1679 HelConfigWriter=open(pjoin('..','MadLoop5_resources', 1680 '%(proc_prefix)sHelConfigs.dat'%matrix_element.rep_dict),'w') 1681 for HelConfig in HelConfigs: 1682 HelConfigWriter.write(' '.join(['%d'%H for H in HelConfig])+'\n') 1683 HelConfigWriter.close() 1684 1685 # Extract helas calls 1686 loop_amp_helas_calls = fortran_model.get_loop_amp_helas_calls(\ 1687 matrix_element) 1688 # The proc_prefix must be replaced 1689 loop_amp_helas_calls = [lc % matrix_element.rep_dict 1690 for lc in loop_amp_helas_calls] 1691 1692 born_ct_helas_calls, UVCT_helas_calls = \ 1693 fortran_model.get_born_ct_helas_calls(matrix_element) 1694 # In the default output, we do not need to separate these two kind of 1695 # contributions 1696 born_ct_helas_calls = born_ct_helas_calls + UVCT_helas_calls 1697 file = open(os.path.join(self.template_dir,\ 1698 1699 'loop_matrix_standalone.inc')).read() 1700 1701 if matrix_element.get('processes')[0].get('has_born'): 1702 toBeRepaced='loop_helas_calls' 1703 else: 1704 toBeRepaced='loop_induced_helas_calls' 1705 1706 # Decide here wether we need to split the loop_matrix.f file or not. 1707 if (not noSplit and (len(matrix_element.get_all_amplitudes())>1000)): 1708 file=self.split_HELASCALLS(writer,replace_dict,\ 1709 'helas_calls_split.inc',file,born_ct_helas_calls,\ 1710 'born_ct_helas_calls','helas_calls_ampb') 1711 file=self.split_HELASCALLS(writer,replace_dict,\ 1712 'helas_calls_split.inc',file,loop_amp_helas_calls,\ 1713 toBeRepaced,'helas_calls_ampl') 1714 else: 1715 replace_dict['born_ct_helas_calls']='\n'.join(born_ct_helas_calls) 1716 replace_dict[toBeRepaced]='\n'.join(loop_amp_helas_calls) 1717 1718 file = file % replace_dict 1719 1720 loop_calls_finder = re.compile(r'^\s*CALL\S*LOOP\S*') 1721 n_loop_calls = len([call for call in loop_amp_helas_calls if not loop_calls_finder.match(call) is None]) 1722 if writer: 1723 # Write the file 1724 writer.writelines(file) 1725 return n_loop_calls 1726 else: 1727 # Return it to be written along with the others 1728 return n_loop_calls, file
1729
1730 - def write_bornmatrix(self, writer, matrix_element, fortran_model):
1731 """Create the born_matrix.f file for the born process as for a standard 1732 tree-level computation.""" 1733 1734 if not matrix_element.get('processes') or \ 1735 not matrix_element.get('diagrams'): 1736 return 0 1737 1738 if not isinstance(writer, writers.FortranWriter): 1739 raise writers.FortranWriter.FortranWriterError(\ 1740 "writer not FortranWriter") 1741 1742 # For now, we can use the exact same treatment as for tree-level 1743 # computations by redefining here a regular HelasMatrixElementf or the 1744 # born process. 1745 # It is important to make a deepcopy, as we don't want any possible 1746 # treatment on the objects of the bornME to have border effects on 1747 # the content of the LoopHelasMatrixElement object. 1748 bornME = helas_objects.HelasMatrixElement() 1749 for prop in bornME.keys(): 1750 bornME.set(prop,copy.deepcopy(matrix_element.get(prop))) 1751 bornME.set('base_amplitude',None,force=True) 1752 bornME.set('diagrams',copy.deepcopy(\ 1753 matrix_element.get_born_diagrams())) 1754 bornME.set('color_basis',copy.deepcopy(\ 1755 matrix_element.get('born_color_basis'))) 1756 bornME.set('color_matrix',copy.deepcopy(\ 1757 color_amp.ColorMatrix(bornME.get('color_basis')))) 1758 # This is to decide wether once to reuse old wavefunction to store new 1759 # ones (provided they are not used further in the code.) 1760 bornME.optimization = True 1761 return super(LoopProcessExporterFortranSA,self).write_matrix_element_v4( 1762 writer, bornME, fortran_model, 1763 proc_prefix=matrix_element.rep_dict['proc_prefix'])
1764
1765 - def write_born_amps_and_wfs(self, writer, matrix_element, fortran_model, 1766 noSplit=False):
1767 """ Writes out the code for the subroutine MP_BORN_AMPS_AND_WFS which 1768 computes just the external wavefunction and born amplitudes in 1769 multiple precision. """ 1770 1771 if not matrix_element.get('processes') or \ 1772 not matrix_element.get('diagrams'): 1773 return 0 1774 1775 replace_dict = copy.copy(matrix_element.rep_dict) 1776 1777 # For the wavefunction copy, check what suffix is needed for the W array 1778 if matrix_element.get('processes')[0].get('has_born'): 1779 replace_dict['h_w_suffix']=',H' 1780 else: 1781 replace_dict['h_w_suffix']='' 1782 1783 # Extract helas calls 1784 born_amps_and_wfs_calls , uvct_amp_calls = \ 1785 fortran_model.get_born_ct_helas_calls(matrix_element, include_CT=True) 1786 # In the default output, these two kind of contributions do not need to 1787 # be differentiated 1788 born_amps_and_wfs_calls = born_amps_and_wfs_calls + uvct_amp_calls 1789 1790 # Turn these HELAS calls to the multiple-precision version of the HELAS 1791 # subroutines. 1792 self.turn_to_mp_calls(born_amps_and_wfs_calls) 1793 1794 file = open(os.path.join(self.template_dir,\ 1795 'mp_born_amps_and_wfs.inc')).read() 1796 # Decide here wether we need to split the loop_matrix.f file or not. 1797 if (not noSplit and (len(matrix_element.get_all_amplitudes())>2000)): 1798 file=self.split_HELASCALLS(writer,replace_dict,\ 1799 'mp_helas_calls_split.inc',file,\ 1800 born_amps_and_wfs_calls,'born_amps_and_wfs_calls',\ 1801 'mp_helas_calls') 1802 else: 1803 replace_dict['born_amps_and_wfs_calls']=\ 1804 '\n'.join(born_amps_and_wfs_calls) 1805 1806 file = file % replace_dict 1807 if writer: 1808 # Write the file 1809 writer.writelines(file) 1810 else: 1811 # Return it to be written along with the others 1812 return file 1813 1814 #=============================================================================== 1815 # LoopProcessOptimizedExporterFortranSA 1816 #=============================================================================== 1817
1818 -class LoopProcessOptimizedExporterFortranSA(LoopProcessExporterFortranSA):
1819 """Class to take care of exporting a set of loop matrix elements in the 1820 Fortran format which exploits the Pozzorini method of representing 1821 the loop numerators as polynomial to render its evaluations faster.""" 1822 1823 template_dir=os.path.join(_file_path,'iolibs/template_files/loop_optimized') 1824 # The option below controls wether one wants to group together in one single 1825 # CutTools/TIR call the loops with same denominator structure 1826 forbid_loop_grouping = False 1827 1828 # List of potential TIR library one wants to link to. 1829 # Golem and Samurai will typically get obtained from gosam_contrib 1830 # which might also contain a version of ninja. We must therefore 1831 # make sure that ninja appears first in the list of -L because 1832 # it is the tool for which the user is most susceptible of 1833 # using a standalone verison independent of gosam_contrib 1834 all_tir=['pjfry','iregi','ninja','golem','samurai','collier'] 1835
1836 - def __init__(self, dir_path = "", opt=None):
1837 """Initiate the LoopProcessOptimizedExporterFortranSA with directory 1838 information on where to find all the loop-related source files, 1839 like CutTools and TIR""" 1840 1841 super(LoopProcessOptimizedExporterFortranSA,self).__init__(dir_path, opt) 1842 1843 # TIR available ones 1844 self.tir_available_dict={'pjfry':True,'iregi':True,'golem':True, 1845 'samurai':True,'ninja':True,'collier':True} 1846 1847 for tir in self.all_tir: 1848 tir_dir="%s_dir"%tir 1849 if tir_dir in self.opt and not self.opt[tir_dir] is None: 1850 # Make sure to defer the 'local path' to the current MG5aMC root. 1851 tir_path = self.opt[tir_dir].strip() 1852 if tir_path.startswith('.'): 1853 tir_path = os.path.abspath(pjoin(MG5DIR,tir_path)) 1854 setattr(self,tir_dir,tir_path) 1855 else: 1856 setattr(self,tir_dir,'')
1857
1858 - def copy_template(self, model):
1859 """Additional actions needed to setup the Template. 1860 """ 1861 1862 super(LoopProcessOptimizedExporterFortranSA, self).copy_template(model) 1863 1864 self.loop_optimized_additional_template_setup()
1865
1866 - def get_context(self,matrix_element, **opts):
1867 """ Additional contextual information which needs to be created for 1868 the optimized output.""" 1869 1870 context = LoopProcessExporterFortranSA.get_context(self, matrix_element, 1871 **opts) 1872 1873 # For now assume Ninja always supports quadruple precision 1874 try: 1875 context['ninja_supports_quad_prec'] = \ 1876 misc.get_ninja_quad_prec_support(getattr(self,'ninja_dir')) 1877 except AttributeError: 1878 context['ninja_supports_quad_prec'] = False 1879 1880 for tir in self.all_tir: 1881 context['%s_available'%tir]=self.tir_available_dict[tir] 1882 # safety check 1883 if tir not in ['golem','pjfry','iregi','samurai','ninja','collier']: 1884 raise MadGraph5Error("%s was not a TIR currently interfaced."%tir_name) 1885 1886 return context
1887
1889 """ Perform additional actions specific for this class when setting 1890 up the template with the copy_template function.""" 1891 1892 # We must link the TIR to the Library folder of the active Template 1893 link_tir_libs=[] 1894 tir_libs=[] 1895 tir_include=[] 1896 1897 for tir in self.all_tir: 1898 tir_dir="%s_dir"%tir 1899 libpath=getattr(self,tir_dir) 1900 libname="lib%s.a"%tir 1901 tir_name=tir 1902 libpath = self.link_TIR(os.path.join(self.dir_path, 'lib'), 1903 libpath,libname,tir_name=tir_name) 1904 if libpath != "": 1905 if tir in ['ninja','pjfry','golem','samurai','collier']: 1906 # It is cleaner to use the original location of the libraries 1907 link_tir_libs.append('-L%s/ -l%s'%(libpath,tir)) 1908 tir_libs.append('%s/lib%s.$(libext)'%(libpath,tir)) 1909 # For Ninja, we must also link against OneLoop. 1910 if tir in ['ninja']: 1911 if not any(os.path.isfile(pjoin(libpath,'libavh_olo.%s'%ext)) 1912 for ext in ['a','dylib','so']): 1913 raise MadGraph5Error( 1914 "The OneLOop library 'libavh_olo.(a|dylib|so)' could no be found in path '%s'. Please place a symlink to it there."%libpath) 1915 link_tir_libs.append('-L%s/ -l%s'%(libpath,'avh_olo')) 1916 tir_libs.append('%s/lib%s.$(libext)'%(libpath,'avh_olo')) 1917 if tir in ['ninja','golem', 'samurai','collier']: 1918 trgt_path = pjoin(os.path.dirname(libpath),'include') 1919 if os.path.isdir(trgt_path): 1920 to_include = misc.find_includes_path(trgt_path, 1921 self.include_names[tir]) 1922 else: 1923 to_include = None 1924 # Special possible location for collier 1925 if to_include is None and tir=='collier': 1926 to_include = misc.find_includes_path( 1927 pjoin(libpath,'modules'),self.include_names[tir]) 1928 if to_include is None: 1929 logger.error( 1930 'Could not find the include directory for %s, looking in %s.\n' % (tir, str(trgt_path))+ 1931 'Generation carries on but you will need to edit the include path by hand in the makefiles.') 1932 to_include = '<Not_found_define_it_yourself>' 1933 tir_include.append('-I %s'%str(to_include)) 1934 # To be able to easily compile a MadLoop library using 1935 # makefiles built outside of the MG5_aMC framework 1936 # (such as what is done with the Sherpa interface), we 1937 # place here an easy handle on the golem includes 1938 name_map = {'golem':'golem95','samurai':'samurai', 1939 'ninja':'ninja','collier':'collier'} 1940 ln(to_include, starting_dir=pjoin(self.dir_path,'lib'), 1941 name='%s_include'%name_map[tir],abspath=True) 1942 ln(libpath, starting_dir=pjoin(self.dir_path,'lib'), 1943 name='%s_lib'%name_map[tir],abspath=True) 1944 else : 1945 link_tir_libs.append('-l%s'%tir) 1946 tir_libs.append('$(LIBDIR)lib%s.$(libext)'%tir) 1947 1948 MadLoop_makefile_definitions = pjoin(self.dir_path,'SubProcesses', 1949 'MadLoop_makefile_definitions') 1950 if os.path.isfile(MadLoop_makefile_definitions): 1951 os.remove(MadLoop_makefile_definitions) 1952 1953 calls = self.write_loop_makefile_definitions( 1954 writers.MakefileWriter(MadLoop_makefile_definitions), 1955 link_tir_libs,tir_libs, tir_include=tir_include) 1956 1957 # Finally overwrite MadLoopCommons.f now that we know the availibility of 1958 # COLLIER. 1959 MadLoopCommon = open(os.path.join(self.loop_dir,'StandAlone', 1960 "SubProcesses","MadLoopCommons.inc")).read() 1961 writer = writers.FortranWriter(os.path.join(self.dir_path, 1962 "SubProcesses","MadLoopCommons.f")) 1963 writer.writelines(MadLoopCommon%{ 1964 'print_banner_commands':self.MadLoop_banner}, context={ 1965 'collier_available':self.tir_available_dict['collier']}) 1966 writer.close()
1967 1979 1980 2108
2109 - def set_group_loops(self, matrix_element):
2110 """ Decides whether we must group loops or not for this matrix element""" 2111 2112 # Decide if loops sharing same denominator structures have to be grouped 2113 # together or not. 2114 if self.forbid_loop_grouping: 2115 self.group_loops = False 2116 else: 2117 self.group_loops = (not self.get_context(matrix_element)['ComputeColorFlows'])\ 2118 and matrix_element.get('processes')[0].get('has_born') 2119 2120 return self.group_loops
2121
2122 - def finalize(self, matrix_element, cmdhistory, MG5options, outputflag):
2123 """create the global information for loops""" 2124 2125 super(LoopProcessOptimizedExporterFortranSA,self).finalize(matrix_element, 2126 cmdhistory, MG5options, outputflag) 2127 self.write_global_specs(matrix_element)
2128 2129 2130
2131 - def write_loop_matrix_element_v4(self, writer, matrix_element, fortran_model, 2132 group_number = None, proc_id = None, config_map = None):
2133 """ Writes loop_matrix.f, CT_interface.f,TIR_interface.f,GOLEM_inteface.f 2134 and loop_num.f only but with the optimized FortranModel. 2135 The arguments group_number and proc_id are just for the LoopInduced 2136 output with MadEvent and only used in get_ME_identifier.""" 2137 2138 # Warn the user that the 'matrix' output where all relevant code is 2139 # put together in a single file is not supported in this loop output. 2140 if writer: 2141 raise MadGraph5Error('Matrix output mode no longer supported.') 2142 2143 if not isinstance(fortran_model,\ 2144 helas_call_writers.FortranUFOHelasCallWriter): 2145 raise MadGraph5Error('The optimized loop fortran output can only'+\ 2146 ' work with a UFO Fortran model') 2147 OptimizedFortranModel=\ 2148 helas_call_writers.FortranUFOHelasCallWriterOptimized(\ 2149 fortran_model.get('model'),False) 2150 2151 2152 if not matrix_element.get('processes')[0].get('has_born') and \ 2153 not self.compute_color_flows: 2154 logger.debug("Color flows will be employed despite the option"+\ 2155 " 'loop_color_flows' being set to False because it is necessary"+\ 2156 " for optimizations.") 2157 2158 # Compute the analytical information of the loop wavefunctions in the 2159 # loop helas matrix elements using the cached aloha model to reuse 2160 # as much as possible the aloha computations already performed for 2161 # writing out the aloha fortran subroutines. 2162 matrix_element.compute_all_analytic_information( 2163 self.get_aloha_model(matrix_element.get('processes')[0].get('model'))) 2164 2165 self.set_group_loops(matrix_element) 2166 2167 # Initialize a general replacement dictionary with entries common to 2168 # many files generated here. 2169 matrix_element.rep_dict = LoopProcessExporterFortranSA.\ 2170 generate_general_replace_dict(self, matrix_element, 2171 group_number = group_number, proc_id = proc_id) 2172 2173 # and those specific to the optimized output 2174 self.set_optimized_output_specific_replace_dict_entries(matrix_element) 2175 2176 # Create the necessary files for the loop matrix element subroutine 2177 proc_prefix_writer = writers.FortranWriter('proc_prefix.txt','w') 2178 proc_prefix_writer.write(matrix_element.rep_dict['proc_prefix']) 2179 proc_prefix_writer.close() 2180 2181 filename = 'loop_matrix.f' 2182 calls = self.write_loopmatrix(writers.FortranWriter(filename), 2183 matrix_element, 2184 OptimizedFortranModel) 2185 2186 filename = 'check_sa.f' 2187 self.write_check_sa(writers.FortranWriter(filename),matrix_element) 2188 2189 filename = 'polynomial.f' 2190 calls = self.write_polynomial_subroutines( 2191 writers.FortranWriter(filename), 2192 matrix_element) 2193 2194 filename = 'improve_ps.f' 2195 calls = self.write_improve_ps(writers.FortranWriter(filename), 2196 matrix_element) 2197 2198 filename = 'CT_interface.f' 2199 self.write_CT_interface(writers.FortranWriter(filename),\ 2200 matrix_element) 2201 2202 filename = 'TIR_interface.f' 2203 self.write_TIR_interface(writers.FortranWriter(filename), 2204 matrix_element) 2205 2206 if 'golem' in self.tir_available_dict and self.tir_available_dict['golem']: 2207 filename = 'GOLEM_interface.f' 2208 self.write_GOLEM_interface(writers.FortranWriter(filename), 2209 matrix_element) 2210 2211 if 'collier' in self.tir_available_dict and self.tir_available_dict['collier']: 2212 filename = 'COLLIER_interface.f' 2213 self.write_COLLIER_interface(writers.FortranWriter(filename), 2214 matrix_element) 2215 2216 filename = 'loop_num.f' 2217 self.write_loop_num(writers.FortranWriter(filename),\ 2218 matrix_element,OptimizedFortranModel) 2219 2220 filename = 'mp_compute_loop_coefs.f' 2221 self.write_mp_compute_loop_coefs(writers.FortranWriter(filename),\ 2222 matrix_element,OptimizedFortranModel) 2223 2224 if self.get_context(matrix_element)['ComputeColorFlows']: 2225 filename = 'compute_color_flows.f' 2226 self.write_compute_color_flows(writers.FortranWriter(filename), 2227 matrix_element, config_map = config_map) 2228 2229 # Extract number of external particles 2230 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 2231 filename = 'nexternal.inc' 2232 self.write_nexternal_file(writers.FortranWriter(filename), 2233 nexternal, ninitial) 2234 2235 # Write general process information 2236 filename = 'process_info.inc' 2237 self.write_process_info_file(writers.FortranWriter(filename), 2238 matrix_element) 2239 2240 if self.get_context(matrix_element)['TIRCaching']: 2241 filename = 'tir_cache_size.inc' 2242 self.write_tir_cache_size_include(writers.FortranWriter(filename)) 2243 2244 return calls
2245
2246 - def set_optimized_output_specific_replace_dict_entries(self, matrix_element):
2247 """ Specify the entries of the replacement dictionary which are specific 2248 to the optimized output and only relevant to it (the more general entries 2249 are set in the the mother class LoopProcessExporterFortranSA.""" 2250 2251 max_loop_rank=matrix_element.get_max_loop_rank() 2252 matrix_element.rep_dict['maxrank']=max_loop_rank 2253 matrix_element.rep_dict['loop_max_coefs']=\ 2254 q_polynomial.get_number_of_coefs_for_rank(max_loop_rank) 2255 max_loop_vertex_rank=matrix_element.get_max_loop_vertex_rank() 2256 matrix_element.rep_dict['vertex_max_coefs']=\ 2257 q_polynomial.get_number_of_coefs_for_rank(max_loop_vertex_rank) 2258 2259 matrix_element.rep_dict['nloopwavefuncs']=\ 2260 matrix_element.get_number_of_loop_wavefunctions() 2261 max_spin=matrix_element.get_max_loop_particle_spin() 2262 2263 matrix_element.rep_dict['max_lwf_size']= 4 if max_spin <=3 else 16 2264 matrix_element.rep_dict['nloops']=len(\ 2265 [1 for ldiag in matrix_element.get_loop_diagrams() for \ 2266 lamp in ldiag.get_loop_amplitudes()]) 2267 2268 if self.set_group_loops(matrix_element): 2269 matrix_element.rep_dict['nloop_groups']=\ 2270 len(matrix_element.get('loop_groups')) 2271 else: 2272 matrix_element.rep_dict['nloop_groups']=\ 2273 matrix_element.rep_dict['nloops']
2274
2275 - def write_loop_num(self, writer, matrix_element,fortran_model):
2276 """ Create the file containing the core subroutine called by CutTools 2277 which contains the Helas calls building the loop""" 2278 2279 replace_dict=copy.copy(matrix_element.rep_dict) 2280 2281 file = open(os.path.join(self.template_dir,'loop_num.inc')).read() 2282 file = file % replace_dict 2283 writer.writelines(file,context=self.get_context(matrix_element))
2284
2285 - def write_CT_interface(self, writer, matrix_element):
2286 """ We can re-use the mother one for the loop optimized output.""" 2287 LoopProcessExporterFortranSA.write_CT_interface(\ 2288 self, writer, matrix_element,optimized_output=True)
2289
2290 - def write_TIR_interface(self, writer, matrix_element):
2291 """ Create the file TIR_interface.f which does NOT contain the subroutine 2292 defining the loop HELAS-like calls along with the general interfacing 2293 subroutine. """ 2294 2295 # First write TIR_interface which interfaces MG5 with TIR. 2296 replace_dict=copy.copy(matrix_element.rep_dict) 2297 2298 file = open(os.path.join(self.template_dir,'TIR_interface.inc')).read() 2299 2300 # Check which loops have an Higgs effective vertex so as to correctly 2301 # implement CutTools limitation 2302 loop_groups = matrix_element.get('loop_groups') 2303 has_HEFT_vertex = [False]*len(loop_groups) 2304 for i, (denom_structure, loop_amp_list) in enumerate(loop_groups): 2305 for lamp in loop_amp_list: 2306 final_lwf = lamp.get_final_loop_wavefunction() 2307 while not final_lwf is None: 2308 # We define here an HEFT vertex as any vertex built up from 2309 # only massless vectors and scalars (at least one of each) 2310 scalars = len([1 for wf in final_lwf.get('mothers') if 2311 wf.get('spin')==1]) 2312 vectors = len([1 for wf in final_lwf.get('mothers') if 2313 wf.get('spin')==3 and wf.get('mass')=='ZERO']) 2314 if scalars>=1 and vectors>=1 and \ 2315 scalars+vectors == len(final_lwf.get('mothers')): 2316 has_HEFT_vertex[i] = True 2317 break 2318 final_lwf = final_lwf.get_loop_mother() 2319 else: 2320 continue 2321 break 2322 2323 has_HEFT_list = [] 2324 chunk_size = 9 2325 for k in range(0, len(has_HEFT_vertex), chunk_size): 2326 has_HEFT_list.append("DATA (HAS_AN_HEFT_VERTEX(I),I=%6r,%6r) /%s/" % \ 2327 (k + 1, min(k + chunk_size, len(has_HEFT_vertex)), 2328 ','.join(['.TRUE.' if l else '.FALSE.' for l in 2329 has_HEFT_vertex[k:k + chunk_size]]))) 2330 replace_dict['has_HEFT_list'] = '\n'.join(has_HEFT_list) 2331 2332 file = file % replace_dict 2333 2334 FPR = q_polynomial.FortranPolynomialRoutines( 2335 replace_dict['maxrank'],coef_format=replace_dict['complex_dp_format'],\ 2336 sub_prefix=replace_dict['proc_prefix']) 2337 if self.tir_available_dict['pjfry']: 2338 file += '\n\n'+FPR.write_pjfry_mapping() 2339 if self.tir_available_dict['iregi']: 2340 file += '\n\n'+FPR.write_iregi_mapping() 2341 2342 if writer: 2343 writer.writelines(file,context=self.get_context(matrix_element)) 2344 else: 2345 return file
2346
2347 - def write_COLLIER_interface(self, writer, matrix_element):
2348 """ Create the file COLLIER_interface.f""" 2349 2350 # First write GOLEM_interface which interfaces MG5 with TIR. 2351 replace_dict=copy.copy(matrix_element.rep_dict) 2352 2353 file = open(os.path.join(self.template_dir,'COLLIER_interface.inc')).read() 2354 2355 FPR = q_polynomial.FortranPolynomialRoutines(replace_dict['maxrank'],\ 2356 coef_format=replace_dict['complex_dp_format'],\ 2357 sub_prefix=replace_dict['proc_prefix']) 2358 map_definition = [] 2359 collier_map = FPR.get_COLLIER_mapping() 2360 2361 chunk_size = 10 2362 for map_name, indices_list in \ 2363 [('COEFMAP_ZERO',[c[0] for c in collier_map]), 2364 ('COEFMAP_ONE',[c[1] for c in collier_map]), 2365 ('COEFMAP_TWO',[c[2] for c in collier_map]), 2366 ('COEFMAP_THREE',[c[3] for c in collier_map])]: 2367 for k in range(0, len(indices_list), chunk_size): 2368 map_definition.append("DATA (%s(I),I=%3r,%3r) /%s/" % \ 2369 (map_name,k, min(k + chunk_size, len(indices_list))-1, 2370 ','.join('%2r'%ind for ind in indices_list[k:k + chunk_size]))) 2371 2372 replace_dict['collier_coefmap'] = '\n'.join(map_definition) 2373 2374 file = file % replace_dict 2375 2376 if writer: 2377 writer.writelines(file,context=self.get_context(matrix_element)) 2378 else: 2379 return file
2380
2381 - def write_GOLEM_interface(self, writer, matrix_element):
2382 """ Create the file GOLEM_interface.f which does NOT contain the subroutine 2383 defining the loop HELAS-like calls along with the general interfacing 2384 subroutine. """ 2385 2386 # First write GOLEM_interface which interfaces MG5 with TIR. 2387 replace_dict=copy.copy(matrix_element.rep_dict) 2388 2389 # We finalize TIR result differently wether we used the built-in 2390 # squaring against the born. 2391 if not self.get_context(matrix_element)['AmplitudeReduction']: 2392 replace_dict['loop_induced_sqsoindex']=',SQSOINDEX' 2393 else: 2394 replace_dict['loop_induced_sqsoindex']='' 2395 2396 file = open(os.path.join(self.template_dir,'GOLEM_interface.inc')).read() 2397 2398 file = file % replace_dict 2399 2400 FPR = q_polynomial.FortranPolynomialRoutines(replace_dict['maxrank'],\ 2401 coef_format=replace_dict['complex_dp_format'],\ 2402 sub_prefix=replace_dict['proc_prefix']) 2403 2404 file += '\n\n'+FPR.write_golem95_mapping() 2405 2406 if writer: 2407 writer.writelines(file,context=self.get_context(matrix_element)) 2408 else: 2409 return file
2410
2411 - def write_polynomial_subroutines(self,writer,matrix_element):
2412 """ Subroutine to create all the subroutines relevant for handling 2413 the polynomials representing the loop numerator """ 2414 2415 # First create 'loop_max_coefs.inc' 2416 IncWriter=writers.FortranWriter('loop_max_coefs.inc','w') 2417 IncWriter.writelines("""INTEGER LOOPMAXCOEFS 2418 PARAMETER (LOOPMAXCOEFS=%(loop_max_coefs)d)""" 2419 %matrix_element.rep_dict) 2420 2421 # Then coef_specs directly in DHELAS if it does not exist already 2422 # 'coef_specs.inc'. If several processes exported different files there, 2423 # it is fine because the overall maximum value will overwrite it in the 2424 # end 2425 coef_specs_path = pjoin(self.dir_path, 'Source','DHELAS','coef_specs.inc') 2426 if not os.path.isfile(coef_specs_path): 2427 IncWriter=writers.FortranWriter(coef_specs_path,'w') 2428 IncWriter.writelines("""INTEGER MAXLWFSIZE 2429 PARAMETER (MAXLWFSIZE=%(max_lwf_size)d) 2430 INTEGER VERTEXMAXCOEFS 2431 PARAMETER (VERTEXMAXCOEFS=%(vertex_max_coefs)d)"""\ 2432 %matrix_element.rep_dict) 2433 IncWriter.close() 2434 2435 # List of all subroutines to place there 2436 subroutines=[] 2437 2438 # Start from the routine in the template 2439 replace_dict = copy.copy(matrix_element.rep_dict) 2440 2441 dp_routine = open(os.path.join(self.template_dir,'polynomial.inc')).read() 2442 mp_routine = open(os.path.join(self.template_dir,'polynomial.inc')).read() 2443 # The double precision version of the basic polynomial routines, such as 2444 # create_loop_coefs 2445 replace_dict['complex_format'] = replace_dict['complex_dp_format'] 2446 replace_dict['real_format'] = replace_dict['real_dp_format'] 2447 replace_dict['mp_prefix'] = '' 2448 replace_dict['kind'] = 8 2449 replace_dict['zero_def'] = '0.0d0' 2450 replace_dict['one_def'] = '1.0d0' 2451 dp_routine = dp_routine % replace_dict 2452 # The quadruple precision version of the basic polynomial routines 2453 replace_dict['complex_format'] = replace_dict['complex_mp_format'] 2454 replace_dict['real_format'] = replace_dict['real_mp_format'] 2455 replace_dict['mp_prefix'] = 'MP_' 2456 replace_dict['kind'] = 16 2457 replace_dict['zero_def'] = '0.0e0_16' 2458 replace_dict['one_def'] = '1.0e0_16' 2459 mp_routine = mp_routine % replace_dict 2460 subroutines.append(dp_routine) 2461 subroutines.append(mp_routine) 2462 2463 # Initialize the polynomial routine writer 2464 poly_writer=q_polynomial.FortranPolynomialRoutines( 2465 matrix_element.get_max_loop_rank(), 2466 updater_max_rank = matrix_element.get_max_loop_vertex_rank(), 2467 sub_prefix=replace_dict['proc_prefix'], 2468 proc_prefix=replace_dict['proc_prefix'], 2469 mp_prefix='') 2470 # Write the polynomial constant module common to all 2471 writer.writelines(poly_writer.write_polynomial_constant_module()+'\n') 2472 2473 mp_poly_writer=q_polynomial.FortranPolynomialRoutines( 2474 matrix_element.get_max_loop_rank(), 2475 updater_max_rank = matrix_element.get_max_loop_vertex_rank(), 2476 coef_format='complex*32', sub_prefix='MP_'+replace_dict['proc_prefix'], 2477 proc_prefix=replace_dict['proc_prefix'], mp_prefix='MP_') 2478 # The eval subroutine 2479 subroutines.append(poly_writer.write_polynomial_evaluator()) 2480 subroutines.append(mp_poly_writer.write_polynomial_evaluator()) 2481 # The add coefs subroutine 2482 subroutines.append(poly_writer.write_add_coefs()) 2483 subroutines.append(mp_poly_writer.write_add_coefs()) 2484 # The merging one for creating the loop coefficients 2485 subroutines.append(poly_writer.write_wl_merger()) 2486 subroutines.append(mp_poly_writer.write_wl_merger()) 2487 for wl_update in matrix_element.get_used_wl_updates(): 2488 # We pick here the most appropriate way of computing the 2489 # tensor product depending on the rank of the two tensors. 2490 # The various choices below come out from a careful comparison of 2491 # the different methods using the valgrind profiler 2492 if wl_update[0]==wl_update[1]==1 or wl_update[0]==0 or wl_update[1]==0: 2493 # If any of the rank is 0, or if they are both equal to 1, 2494 # then we are better off using the full expanded polynomial, 2495 # and let the compiler optimize it. 2496 subroutines.append(poly_writer.write_expanded_wl_updater(\ 2497 wl_update[0],wl_update[1])) 2498 subroutines.append(mp_poly_writer.write_expanded_wl_updater(\ 2499 wl_update[0],wl_update[1])) 2500 elif wl_update[0] >= wl_update[1]: 2501 # If the loop polynomial is larger then we will filter and loop 2502 # over the vertex coefficients first. The smallest product for 2503 # which the routines below could be used is then 2504 # loop_rank_2 x vertex_rank_1 2505 subroutines.append(poly_writer.write_compact_wl_updater(\ 2506 wl_update[0],wl_update[1],loop_over_vertex_coefs_first=True)) 2507 subroutines.append(mp_poly_writer.write_compact_wl_updater(\ 2508 wl_update[0],wl_update[1],loop_over_vertex_coefs_first=True)) 2509 else: 2510 # This happens only when the rank of the updater (vertex coef) 2511 # is larger than the one of the loop coef and none of them is 2512 # zero. This never happens in renormalizable theories but it 2513 # can happen in the HEFT ones or other effective ones. In this 2514 # case the typicaly use of this routine if for the product 2515 # loop_rank_1 x vertex_rank_2 2516 subroutines.append(poly_writer.write_compact_wl_updater(\ 2517 wl_update[0],wl_update[1],loop_over_vertex_coefs_first=False)) 2518 subroutines.append(mp_poly_writer.write_compact_wl_updater(\ 2519 wl_update[0],wl_update[1],loop_over_vertex_coefs_first=False)) 2520 2521 writer.writelines('\n\n'.join(subroutines), 2522 context=self.get_context(matrix_element))
2523
2524 - def write_mp_compute_loop_coefs(self, writer, matrix_element, fortran_model):
2525 """Create the write_mp_compute_loop_coefs.f file.""" 2526 2527 if not matrix_element.get('processes') or \ 2528 not matrix_element.get('diagrams'): 2529 return 0 2530 2531 # Set lowercase/uppercase Fortran code 2532 2533 writers.FortranWriter.downcase = False 2534 2535 replace_dict = copy.copy(matrix_element.rep_dict) 2536 2537 # Extract helas calls 2538 squared_orders = matrix_element.get_squared_order_contribs() 2539 split_orders = matrix_element.get('processes')[0].get('split_orders') 2540 2541 born_ct_helas_calls , uvct_helas_calls = \ 2542 fortran_model.get_born_ct_helas_calls(matrix_element, 2543 squared_orders=squared_orders, split_orders=split_orders) 2544 self.turn_to_mp_calls(born_ct_helas_calls) 2545 self.turn_to_mp_calls(uvct_helas_calls) 2546 coef_construction, coef_merging = fortran_model.get_coef_construction_calls(\ 2547 matrix_element,group_loops=self.group_loops, 2548 squared_orders=squared_orders,split_orders=split_orders) 2549 # The proc_prefix must be replaced 2550 coef_construction = [c % matrix_element.rep_dict for c 2551 in coef_construction] 2552 self.turn_to_mp_calls(coef_construction) 2553 self.turn_to_mp_calls(coef_merging) 2554 2555 file = open(os.path.join(self.template_dir,\ 2556 'mp_compute_loop_coefs.inc')).read() 2557 2558 # Setup the contextual environment which is used in the splitting 2559 # functions below 2560 context = self.get_context(matrix_element) 2561 file=self.split_HELASCALLS(writer,replace_dict,\ 2562 'mp_helas_calls_split.inc',file,born_ct_helas_calls,\ 2563 'mp_born_ct_helas_calls','mp_helas_calls_ampb', 2564 required_so_broadcaster = 'MP_CT_REQ_SO_DONE', 2565 continue_label = 2000, 2566 momenta_array_name = 'MP_P', 2567 context=context) 2568 file=self.split_HELASCALLS(writer,replace_dict,\ 2569 'mp_helas_calls_split.inc',file,uvct_helas_calls,\ 2570 'mp_uvct_helas_calls','mp_helas_calls_uvct', 2571 required_so_broadcaster = 'MP_UVCT_REQ_SO_DONE', 2572 continue_label = 3000, 2573 momenta_array_name = 'MP_P', 2574 context=context) 2575 file=self.split_HELASCALLS(writer,replace_dict,\ 2576 'mp_helas_calls_split.inc',file,coef_construction,\ 2577 'mp_coef_construction','mp_coef_construction', 2578 required_so_broadcaster = 'MP_LOOP_REQ_SO_DONE', 2579 continue_label = 4000, 2580 momenta_array_name = 'MP_P', 2581 context=context) 2582 2583 replace_dict['mp_coef_merging']='\n'.join(coef_merging) 2584 2585 file = file % replace_dict 2586 2587 # Write the file 2588 writer.writelines(file,context=context)
2589
2590 - def write_color_matrix_data_file(self, writer, col_matrix):
2591 """Writes out the files (Loop|Born)ColorFlowMatrix.dat corresponding 2592 to the color coefficients for JAMP(L|B)*JAMP(L|B).""" 2593 2594 res = [] 2595 for line in range(len(col_matrix._col_basis1)): 2596 numerators = [] 2597 denominators = [] 2598 for row in range(len(col_matrix._col_basis2)): 2599 coeff = col_matrix.col_matrix_fixed_Nc[(line,row)] 2600 numerators.append('%6r'%coeff[0].numerator) 2601 denominators.append('%6r'%( 2602 coeff[0].denominator*(-1 if coeff[1] else 1))) 2603 res.append(' '.join(numerators)) 2604 res.append(' '.join(denominators)) 2605 2606 res.append('EOF') 2607 2608 writer.writelines('\n'.join(res))
2609
2610 - def write_color_flow_coefs_data_file(self, writer, color_amplitudes, 2611 color_basis):
2612 """ Writes the file '(Loop|Born)ColorFlowCoefs.dat using the coefficients 2613 list of the color_amplitudes in the argument of this function.""" 2614 2615 my_cs = color.ColorString() 2616 2617 res = [] 2618 2619 for jamp_number, coeff_list in enumerate(color_amplitudes): 2620 my_cs.from_immutable(sorted(color_basis.keys())[jamp_number]) 2621 # Order the ColorString so that its ordering is canonical. 2622 ordered_cs = color.ColorFactor([my_cs]).full_simplify()[0] 2623 res.append('%d # Coefficient for flow number %d with expr. %s'\ 2624 %(len(coeff_list), jamp_number+1, repr(ordered_cs))) 2625 # A line element is a tuple (numerator, denominator, amplitude_id) 2626 line_element = [] 2627 2628 for (coefficient, amp_number) in coeff_list: 2629 coef = self.cat_coeff(\ 2630 coefficient[0],coefficient[1],coefficient[2],coefficient[3]) 2631 line_element.append((coef[0].numerator, 2632 coef[0].denominator*(-1 if coef[1] else 1),amp_number)) 2633 # Sort them by growing amplitude number 2634 line_element.sort(key=lambda el:el[2]) 2635 2636 for i in range(3): 2637 res.append(' '.join('%6r'%elem[i] for elem in line_element)) 2638 2639 res.append('EOF') 2640 writer.writelines('\n'.join(res))
2641
2642 - def write_compute_color_flows(self, writer, matrix_element, config_map):
2643 """Writes the file compute_color_flows.f which uses the AMPL results 2644 from a common block to project them onto the color flow space so as 2645 to compute the JAMP quantities. For loop induced processes, this file 2646 will also contain a subroutine computing AMPL**2 for madevent 2647 multichanneling.""" 2648 2649 loop_col_amps = matrix_element.get_loop_color_amplitudes() 2650 matrix_element.rep_dict['nLoopFlows'] = len(loop_col_amps) 2651 2652 dat_writer = open(pjoin('..','MadLoop5_resources', 2653 '%(proc_prefix)sLoopColorFlowCoefs.dat' 2654 %matrix_element.rep_dict),'w') 2655 self.write_color_flow_coefs_data_file(dat_writer, 2656 loop_col_amps, matrix_element.get('loop_color_basis')) 2657 dat_writer.close() 2658 2659 dat_writer = open(pjoin('..','MadLoop5_resources', 2660 '%(proc_prefix)sLoopColorFlowMatrix.dat' 2661 %matrix_element.rep_dict),'w') 2662 self.write_color_matrix_data_file(dat_writer, 2663 matrix_element.get('color_matrix')) 2664 dat_writer.close() 2665 2666 if matrix_element.get('processes')[0].get('has_born'): 2667 born_col_amps = matrix_element.get_born_color_amplitudes() 2668 matrix_element.rep_dict['nBornFlows'] = len(born_col_amps) 2669 dat_writer = open(pjoin('..','MadLoop5_resources', 2670 '%(proc_prefix)sBornColorFlowCoefs.dat' 2671 %matrix_element.rep_dict),'w') 2672 self.write_color_flow_coefs_data_file(dat_writer, 2673 born_col_amps, matrix_element.get('born_color_basis')) 2674 dat_writer.close() 2675 2676 dat_writer = open(pjoin('..','MadLoop5_resources', 2677 '%(proc_prefix)sBornColorFlowMatrix.dat' 2678 %matrix_element.rep_dict),'w') 2679 self.write_color_matrix_data_file(dat_writer, 2680 color_amp.ColorMatrix(matrix_element.get('born_color_basis'))) 2681 dat_writer.close() 2682 else: 2683 matrix_element.rep_dict['nBornFlows'] = 0 2684 2685 replace_dict = copy.copy(matrix_element.rep_dict) 2686 2687 # The following variables only have to be defined for the LoopInduced 2688 # output for madevent. 2689 if self.get_context(matrix_element)['MadEventOutput']: 2690 self.get_amp2_lines(matrix_element, replace_dict, config_map) 2691 else: 2692 replace_dict['config_map_definition'] = '' 2693 replace_dict['config_index_map_definition'] = '' 2694 replace_dict['nmultichannels'] = 0 2695 replace_dict['nmultichannel_configs'] = 0 2696 2697 # The nmultichannels entry will be used in the matrix<i> wrappers as 2698 # well, so we add it to the general_replace_dict too. 2699 matrix_element.rep_dict['nmultichannels'] = \ 2700 replace_dict['nmultichannels'] 2701 matrix_element.rep_dict['nmultichannel_configs'] = \ 2702 replace_dict['nmultichannel_configs'] 2703 2704 2705 file = open(os.path.join(self.template_dir,\ 2706 'compute_color_flows.inc')).read()%replace_dict 2707 2708 writer.writelines(file,context=self.get_context(matrix_element))
2709
2710 - def write_global_specs(self, matrix_element_list, output_path=None):
2711 """ From the list of matrix element, or the single matrix element, derive 2712 the global quantities to write in global_coef_specs.inc""" 2713 2714 if isinstance(matrix_element_list, (group_subprocs.SubProcessGroupList, 2715 loop_helas_objects.LoopHelasProcess)): 2716 matrix_element_list = matrix_element_list.get_matrix_elements() 2717 2718 if isinstance(matrix_element_list, list): 2719 me_list = matrix_element_list 2720 else: 2721 me_list = [matrix_element_list] 2722 2723 if output_path is None: 2724 out_path = pjoin(self.dir_path,'SubProcesses','global_specs.inc') 2725 else: 2726 out_path = output_path 2727 2728 open(out_path,'w').write( 2729 """ integer MAXNEXTERNAL 2730 parameter(MAXNEXTERNAL=%d) 2731 integer OVERALLMAXRANK 2732 parameter(OVERALLMAXRANK=%d) 2733 integer NPROCS 2734 parameter(NPROCS=%d)"""%( 2735 max(me.get_nexternal_ninitial()[0] for me in me_list), 2736 max(me.get_max_loop_rank() for me in me_list), 2737 len(me_list)))
2738 2739
2740 - def fix_coef_specs(self, overall_max_lwf_spin, overall_max_loop_vert_rank):
2741 """ If processes with different maximum loop wavefunction size or 2742 different maximum loop vertex rank have to be output together, then 2743 the file 'coef.inc' in the HELAS Source folder must contain the overall 2744 maximum of these quantities. It is not safe though, and the user has 2745 been appropriatly warned at the output stage """ 2746 2747 # Remove the existing link 2748 coef_specs_path=os.path.join(self.dir_path,'Source','DHELAS',\ 2749 'coef_specs.inc') 2750 os.remove(coef_specs_path) 2751 2752 spin_to_wf_size = {1:4,2:4,3:4,4:16,5:16} 2753 overall_max_lwf_size = spin_to_wf_size[overall_max_lwf_spin] 2754 overall_max_loop_vert_coefs = q_polynomial.get_number_of_coefs_for_rank( 2755 overall_max_loop_vert_rank) 2756 # Replace it by the appropriate value 2757 IncWriter=writers.FortranWriter(coef_specs_path,'w') 2758 IncWriter.writelines("""INTEGER MAXLWFSIZE 2759 PARAMETER (MAXLWFSIZE=%(max_lwf_size)d) 2760 INTEGER VERTEXMAXCOEFS 2761 PARAMETER (VERTEXMAXCOEFS=%(vertex_max_coefs)d)"""\ 2762 %{'max_lwf_size':overall_max_lwf_size, 2763 'vertex_max_coefs':overall_max_loop_vert_coefs}) 2764 IncWriter.close()
2765
2766 - def setup_check_sa_replacement_dictionary(self, matrix_element, \ 2767 split_orders,squared_orders,amps_orders):
2768 """ Sets up the replacement dictionary for the writeout of the steering 2769 file check_sa.f""" 2770 if len(squared_orders)<1: 2771 matrix_element.rep_dict['print_so_loop_results']=\ 2772 "write(*,*) 'No split orders defined.'" 2773 elif len(squared_orders)==1: 2774 matrix_element.rep_dict['set_coupling_target']='' 2775 matrix_element.rep_dict['print_so_loop_results']=\ 2776 "write(*,*) 'All loop contributions are of split orders (%s)'"%( 2777 ' '.join(['%s=%d'%(split_orders[i],squared_orders[0][i]) \ 2778 for i in range(len(split_orders))])) 2779 else: 2780 matrix_element.rep_dict['set_coupling_target']='\n'.join([ 2781 '# Here we leave the default target squared split order to -1, meaning that we'+ 2782 ' aim at computing all individual contributions. You can choose otherwise.', 2783 'call %(proc_prefix)sSET_COUPLINGORDERS_TARGET(-1)'%matrix_element.rep_dict]) 2784 matrix_element.rep_dict['print_so_loop_results'] = '\n'.join([ 2785 '\n'.join(["write(*,*) '%dL) Loop ME for orders (%s) :'"%((j+1),(' '.join( 2786 ['%s=%d'%(split_orders[i],so[i]) for i in range(len(split_orders))]))), 2787 "IF (PREC_FOUND(%d).NE.-1.0d0) THEN"%(j+1), 2788 "write(*,*) ' > accuracy = ',PREC_FOUND(%d)"%(j+1), 2789 "ELSE", 2790 "write(*,*) ' > accuracy = NA'", 2791 "ENDIF", 2792 "write(*,*) ' > finite = ',MATELEM(1,%d)"%(j+1), 2793 "write(*,*) ' > 1eps = ',MATELEM(2,%d)"%(j+1), 2794 "write(*,*) ' > 2eps = ',MATELEM(3,%d)"%(j+1) 2795 ]) for j, so in enumerate(squared_orders)]) 2796 matrix_element.rep_dict['write_so_loop_results'] = '\n'.join( 2797 ["write (69,*) 'Split_Orders_Names %s'"%(' '.join(split_orders))]+ 2798 ['\n'.join([ 2799 "write (69,*) 'Loop_SO_Results %s'"%(' '.join( 2800 ['%d'%so_value for so_value in so])), 2801 "write (69,*) 'SO_Loop ACC ',PREC_FOUND(%d)"%(j+1), 2802 "write (69,*) 'SO_Loop FIN ',MATELEM(1,%d)"%(j+1), 2803 "write (69,*) 'SO_Loop 1EPS ',MATELEM(2,%d)"%(j+1), 2804 "write (69,*) 'SO_Loop 2EPS ',MATELEM(3,%d)"%(j+1), 2805 ]) for j, so in enumerate(squared_orders)]) 2806 2807 # We must reconstruct here the born squared orders. 2808 squared_born_so_orders = [] 2809 for i, amp_order in enumerate(amps_orders['born_amp_orders']): 2810 for j in range(0,i+1): 2811 key = tuple([ord1 + ord2 for ord1,ord2 in \ 2812 zip(amp_order[0],amps_orders['born_amp_orders'][j][0])]) 2813 if not key in squared_born_so_orders: 2814 squared_born_so_orders.append(key) 2815 if len(squared_born_so_orders)<1: 2816 matrix_element.rep_dict['print_so_born_results'] = '' 2817 elif len(squared_born_so_orders)==1: 2818 matrix_element.rep_dict['print_so_born_results'] = \ 2819 "write(*,*) 'All Born contributions are of split orders (%s)'"%( 2820 ' '.join(['%s=%d'%(split_orders[i],squared_born_so_orders[0][i]) 2821 for i in range(len(split_orders))])) 2822 else: 2823 matrix_element.rep_dict['print_so_born_results'] = '\n'.join([ 2824 "write(*,*) '%dB) Born ME for orders (%s) = ',MATELEM(0,%d)"%(j+1,' '.join( 2825 ['%s=%d'%(split_orders[i],so[i]) for i in range(len(split_orders))]),j+1) 2826 for j, so in enumerate(squared_born_so_orders)]) 2827 matrix_element.rep_dict['write_so_born_results'] = '\n'.join( 2828 ['\n'.join([ 2829 "write (69,*) 'Born_SO_Results %s'"%(' '.join( 2830 ['%d'%so_value for so_value in so])), 2831 "write (69,*) 'SO_Born BORN ',MATELEM(0,%d)"%(j+1), 2832 ]) for j, so in enumerate(squared_born_so_orders)]) 2833 2834 # Add a bottom bar to both print_so_[loop|born]_results 2835 matrix_element.rep_dict['print_so_born_results'] += \ 2836 '\nwrite (*,*) "---------------------------------"' 2837 matrix_element.rep_dict['print_so_loop_results'] += \ 2838 '\nwrite (*,*) "---------------------------------"'
2839
2840 - def write_tir_cache_size_include(self, writer):
2841 """Write the file 'tir_cache_size.inc' which sets the size of the TIR 2842 cache the the user wishes to employ and the default value for it. 2843 This can have an impact on MadLoop speed when using stability checks 2844 but also impacts in a non-negligible way MadLoop's memory footprint. 2845 It is therefore important that the user can chose its size.""" 2846 2847 # For the standalone optimized output, a size of one is necessary. 2848 # The MadLoop+MadEvent output sets it to 2 because it can gain further 2849 # speed increase with a TIR cache of size 2 due to the structure of the 2850 # calls to MadLoop there. 2851 tir_cach_size = "parameter(TIR_CACHE_SIZE=1)" 2852 writer.writelines(tir_cach_size)
2853
2854 - def write_loopmatrix(self, writer, matrix_element, fortran_model, \ 2855 write_auxiliary_files=True,):
2856 """Create the loop_matrix.f file.""" 2857 2858 if not matrix_element.get('processes') or \ 2859 not matrix_element.get('diagrams'): 2860 return 0 2861 2862 # Set lowercase/uppercase Fortran code 2863 writers.FortranWriter.downcase = False 2864 2865 # Starting off with the treatment of the split_orders since some 2866 # of the information extracted there will come into the 2867 # general_replace_dict. Split orders are abbreviated SO in all the 2868 # keys of the replacement dictionaries. 2869 2870 # Take care of the split_orders 2871 squared_orders, amps_orders = matrix_element.get_split_orders_mapping() 2872 # Creating here a temporary list containing only the information of 2873 # what are the different squared split orders contributing 2874 # (i.e. not using max_contrib_amp_number and max_contrib_ref_amp_number) 2875 sqso_contribs = [sqso[0] for sqso in squared_orders] 2876 split_orders = matrix_element.get('processes')[0].get('split_orders') 2877 # The entries set in the function below are only for check_sa written 2878 # out in write_loop__matrix_element_v4 (it is however placed here because the 2879 # split order information is only available here). 2880 self.setup_check_sa_replacement_dictionary(matrix_element, 2881 split_orders,sqso_contribs,amps_orders) 2882 2883 # Now recast the split order basis for the loop, born and counterterm 2884 # amplitude into one single splitorderbasis. 2885 overall_so_basis = list(set( 2886 [born_so[0] for born_so in amps_orders['born_amp_orders']]+ 2887 [born_so[0] for born_so in amps_orders['loop_amp_orders']])) 2888 # We must re-sort it to make sure it follows an increasing WEIGHT order 2889 order_hierarchy = matrix_element.get('processes')[0]\ 2890 .get('model').get('order_hierarchy') 2891 if set(order_hierarchy.keys()).union(set(split_orders))==\ 2892 set(order_hierarchy.keys()): 2893 overall_so_basis.sort(key= lambda so: 2894 sum([order_hierarchy[split_orders[i]]*order_power for \ 2895 i, order_power in enumerate(so)])) 2896 2897 # Those are additional entries used throughout the different files of 2898 # MadLoop5 2899 matrix_element.rep_dict['split_order_str_list'] = str(split_orders) 2900 matrix_element.rep_dict['nSO'] = len(split_orders) 2901 matrix_element.rep_dict['nSquaredSO'] = len(sqso_contribs) 2902 matrix_element.rep_dict['nAmpSO'] = len(overall_so_basis) 2903 2904 writers.FortranWriter('nsquaredSO.inc').writelines( 2905 """INTEGER NSQUAREDSO 2906 PARAMETER (NSQUAREDSO=%d)"""%matrix_element.rep_dict['nSquaredSO']) 2907 2908 replace_dict = copy.copy(matrix_element.rep_dict) 2909 # Build the general array mapping the split orders indices to their 2910 # definition 2911 replace_dict['ampsplitorders'] = '\n'.join(self.get_split_orders_lines(\ 2912 overall_so_basis,'AMPSPLITORDERS')) 2913 replace_dict['SquaredSO'] = '\n'.join(self.get_split_orders_lines(\ 2914 sqso_contribs,'SQPLITORDERS')) 2915 2916 # Specify what are the squared split orders selected by the proc def. 2917 replace_dict['chosen_so_configs'] = self.set_chosen_SO_index( 2918 matrix_element.get('processes')[0],sqso_contribs) 2919 2920 # Now we build the different arrays storing the split_orders ID of each 2921 # amp. 2922 ampSO_list=[-1]*sum(len(el[1]) for el in amps_orders['loop_amp_orders']) 2923 for SO in amps_orders['loop_amp_orders']: 2924 for amp_number in SO[1]: 2925 ampSO_list[amp_number-1]=overall_so_basis.index(SO[0])+1 2926 2927 replace_dict['loopAmpSO'] = '\n'.join(self.format_integer_list( 2928 ampSO_list,'LOOPAMPORDERS')) 2929 ampSO_list=[-1]*sum(len(el[1]) for el in amps_orders['born_amp_orders']) 2930 for SO in amps_orders['born_amp_orders']: 2931 for amp_number in SO[1]: 2932 ampSO_list[amp_number-1]=overall_so_basis.index(SO[0])+1 2933 replace_dict['BornAmpSO'] = '\n'.join(self.format_integer_list( 2934 ampSO_list,'BORNAMPORDERS')) 2935 2936 # We then go to the TIR setup 2937 # The first entry is the CutTools, we make sure it is available 2938 looplibs_av=['.TRUE.'] 2939 # one should be careful about the order in the following as it must match 2940 # the ordering in MadLoopParamsCard. 2941 for tir_lib in ['pjfry','iregi','golem','samurai','ninja','collier']: 2942 looplibs_av.append('.TRUE.' if tir_lib in self.all_tir and \ 2943 self.tir_available_dict[tir_lib] else '.FALSE.') 2944 replace_dict['data_looplibs_av']=','.join(looplibs_av) 2945 2946 # Helicity offset convention 2947 # For a given helicity, the attached integer 'i' means 2948 # 'i' in ]-inf;-HELOFFSET[ -> Helicity is equal, up to a sign, 2949 # to helicity number abs(i+HELOFFSET) 2950 # 'i' == -HELOFFSET -> Helicity is analytically zero 2951 # 'i' in ]-HELOFFSET,inf[ -> Helicity is contributing with weight 'i'. 2952 # If it is zero, it is skipped. 2953 # Typically, the hel_offset is 10000 2954 replace_dict['hel_offset'] = 10000 2955 2956 # Extract overall denominator 2957 # Averaging initial state color, spin, and identical FS particles 2958 den_factor_line = self.get_den_factor_line(matrix_element) 2959 replace_dict['den_factor_line'] = den_factor_line 2960 2961 # When the user asks for the polarized matrix element we must 2962 # multiply back by the helicity averaging factor 2963 replace_dict['hel_avg_factor'] = matrix_element.get_hel_avg_factor() 2964 replace_dict['beamone_helavgfactor'], replace_dict['beamtwo_helavgfactor'] =\ 2965 matrix_element.get_beams_hel_avg_factor() 2966 2967 if write_auxiliary_files: 2968 # Write out the color matrix 2969 (CMNum,CMDenom) = self.get_color_matrix(matrix_element) 2970 CMWriter=open(pjoin('..','MadLoop5_resources', 2971 '%(proc_prefix)sColorNumFactors.dat'%matrix_element.rep_dict),'w') 2972 for ColorLine in CMNum: 2973 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 2974 CMWriter.close() 2975 CMWriter=open(pjoin('..','MadLoop5_resources', 2976 '%(proc_prefix)sColorDenomFactors.dat'%matrix_element.rep_dict),'w') 2977 for ColorLine in CMDenom: 2978 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 2979 CMWriter.close() 2980 2981 # Write out the helicity configurations 2982 HelConfigs=matrix_element.get_helicity_matrix() 2983 HelConfigWriter=open(pjoin('..','MadLoop5_resources', 2984 '%(proc_prefix)sHelConfigs.dat'%matrix_element.rep_dict),'w') 2985 for HelConfig in HelConfigs: 2986 HelConfigWriter.write(' '.join(['%d'%H for H in HelConfig])+'\n') 2987 HelConfigWriter.close() 2988 2989 # Extract helas calls 2990 born_ct_helas_calls, uvct_helas_calls = \ 2991 fortran_model.get_born_ct_helas_calls(matrix_element, 2992 squared_orders=squared_orders,split_orders=split_orders) 2993 coef_construction, coef_merging = fortran_model.get_coef_construction_calls(\ 2994 matrix_element,group_loops=self.group_loops, 2995 squared_orders=squared_orders,split_orders=split_orders) 2996 2997 loop_CT_calls = fortran_model.get_loop_CT_calls(matrix_element,\ 2998 group_loops=self.group_loops, 2999 squared_orders=squared_orders, split_orders=split_orders) 3000 # The proc_prefix must be replaced 3001 coef_construction = [c % matrix_element.rep_dict for c 3002 in coef_construction] 3003 loop_CT_calls = [lc % matrix_element.rep_dict for lc in loop_CT_calls] 3004 3005 file = open(os.path.join(self.template_dir,\ 3006 'loop_matrix_standalone.inc')).read() 3007 3008 # Setup the contextual environment which is used in the splitting 3009 # functions below 3010 context = self.get_context(matrix_element) 3011 file=self.split_HELASCALLS(writer,replace_dict,\ 3012 'helas_calls_split.inc',file,born_ct_helas_calls,\ 3013 'born_ct_helas_calls','helas_calls_ampb', 3014 required_so_broadcaster = 'CT_REQ_SO_DONE', 3015 continue_label = 2000, context = context) 3016 file=self.split_HELASCALLS(writer,replace_dict,\ 3017 'helas_calls_split.inc',file,uvct_helas_calls,\ 3018 'uvct_helas_calls','helas_calls_uvct', 3019 required_so_broadcaster = 'UVCT_REQ_SO_DONE', 3020 continue_label = 3000, context=context) 3021 file=self.split_HELASCALLS(writer,replace_dict,\ 3022 'helas_calls_split.inc',file,coef_construction,\ 3023 'coef_construction','coef_construction', 3024 required_so_broadcaster = 'LOOP_REQ_SO_DONE', 3025 continue_label = 4000, context=context) 3026 file=self.split_HELASCALLS(writer,replace_dict,\ 3027 'helas_calls_split.inc',file,loop_CT_calls,\ 3028 'loop_CT_calls','loop_CT_calls', 3029 required_so_broadcaster = 'CTCALL_REQ_SO_DONE', 3030 continue_label = 5000, context=context) 3031 3032 # Add the entries above to the general_replace_dict so that it can be 3033 # used by write_mp_compute_loop_coefs later 3034 matrix_element.rep_dict['loop_CT_calls']=replace_dict['loop_CT_calls'] 3035 matrix_element.rep_dict['born_ct_helas_calls']=replace_dict['born_ct_helas_calls'] 3036 matrix_element.rep_dict['uvct_helas_calls']=replace_dict['uvct_helas_calls'] 3037 matrix_element.rep_dict['coef_construction']=replace_dict['coef_construction'] 3038 3039 replace_dict['coef_merging']='\n'.join(coef_merging) 3040 file = file % replace_dict 3041 number_of_calls = len([call for call in loop_CT_calls if call.find('CALL LOOP') != 0]) 3042 if writer: 3043 # Write the file 3044 writer.writelines(file,context=context) 3045 return number_of_calls 3046 else: 3047 # Return it to be written along with the others 3048 return number_of_calls, file
3049 3050 #=============================================================================== 3051 # LoopProcessExporterFortranSA 3052 #===============================================================================
3053 -class LoopProcessExporterFortranMatchBox(LoopProcessOptimizedExporterFortranSA, 3054 export_v4.ProcessExporterFortranMatchBox):
3055 """Class to take care of exporting a set of loop matrix elements in the 3056 Fortran format.""" 3057 3058 default_opt = {'clean': False, 'complex_mass':False, 3059 'export_format':'madloop_matchbox', 'mp':True, 3060 'loop_dir':'', 'cuttools_dir':'', 3061 'fortran_compiler':'gfortran', 3062 'output_dependencies':'external', 3063 'sa_symmetry':True} 3064 3065 3066
3067 - def get_color_string_lines(self, matrix_element):
3068 """Return the color matrix definition lines for this matrix element. Split 3069 rows in chunks of size n.""" 3070 3071 return export_v4.ProcessExporterFortranMatchBox.get_color_string_lines(matrix_element)
3072 3073
3074 - def get_JAMP_lines(self, *args, **opts):
3075 """Adding leading color part of the colorflow""" 3076 3077 return export_v4.ProcessExporterFortranMatchBox.get_JAMP_lines(self, *args, **opts)
3078
3079 - def get_ME_identifier(self, matrix_element, group_number = None, group_elem_number = None):
3080 """ To not mix notations between borns and virtuals we call it here also MG5 """ 3081 return 'MG5_%d_'%matrix_element.get('processes')[0].get('id')
3082 3083 3084 #=============================================================================== 3085 # LoopInducedExporter 3086 #===============================================================================
3087 -class LoopInducedExporterME(LoopProcessOptimizedExporterFortranSA):
3088 """ A class to specify all the functions common to LoopInducedExporterMEGroup 3089 and LoopInducedExporterMENoGroup (but not relevant for the original 3090 Madevent exporters)""" 3091 3092 madloop_makefile_name = 'makefile_MadLoop' 3093 3094
3095 - def __init__(self, *args, **opts):
3096 """ Initialize the process, setting the proc characteristics.""" 3097 super(LoopInducedExporterME, self).__init__(*args, **opts) 3098 self.proc_characteristic['loop_induced'] = True
3099
3100 - def get_context(self,*args,**opts):
3101 """ Make sure that the contextual variable MadEventOutput is set to 3102 True for this exporter""" 3103 3104 context = super(LoopInducedExporterME,self).get_context(*args,**opts) 3105 context['MadEventOutput'] = True 3106 return context
3107 3108 #=========================================================================== 3109 # write a procdef_mg5 (an equivalent of the MG4 proc_card.dat) 3110 #===========================================================================
3111 - def write_procdef_mg5(self, file_pos, modelname, process_str):
3112 """ write an equivalent of the MG4 proc_card in order that all the Madevent 3113 Perl script of MadEvent4 are still working properly for pure MG5 run. 3114 Not needed for StandAlone so we need to call the correct one 3115 """ 3116 3117 return export_v4.ProcessExporterFortran.write_procdef_mg5( 3118 self, file_pos, modelname, process_str)
3119
3120 - def get_source_libraries_list(self):
3121 """ Returns the list of libraries to be compiling when compiling the 3122 SOURCE directory. It is different for loop_induced processes and 3123 also depends on the value of the 'output_dependencies' option""" 3124 3125 libraries_list = super(LoopInducedExporterME,self).\ 3126 get_source_libraries_list() 3127 3128 if self.dependencies=='internal': 3129 libraries_list.append('$(LIBDIR)libcts.$(libext)') 3130 libraries_list.append('$(LIBDIR)libiregi.$(libext)') 3131 3132 return libraries_list
3133 3140
3141 - def copy_template(self, *args, **opts):
3142 """Pick the right mother functions 3143 """ 3144 # Call specifically the necessary building functions for the mixed 3145 # template setup for both MadEvent and MadLoop standalone 3146 LoopProcessExporterFortranSA.loop_additional_template_setup(self, 3147 copy_Source_makefile=False) 3148 3149 LoopProcessOptimizedExporterFortranSA.\ 3150 loop_optimized_additional_template_setup(self)
3151 3152 3153 #=========================================================================== 3154 # Create jpeg diagrams, html pages,proc_card_mg5.dat and madevent.tar.gz 3155 #===========================================================================
3156 - def finalize(self, matrix_elements, history, mg5options, flaglist):
3157 """Function to finalize v4 directory, for inheritance. 3158 """ 3159 3160 self.proc_characteristic['loop_induced'] = True 3161 3162 # This can be uncommented if one desires to have the MadLoop 3163 # initialization performed at the end of the output phase. 3164 # Alternatively, one can simply execute the command 'initMadLoop' in 3165 # the madevent interactive interface after the output. 3166 # from madgraph.interface.madevent_interface import MadLoopInitializer 3167 # MadLoopInitializer.init_MadLoop(self.dir_path, 3168 # subproc_prefix=self.SubProc_prefix, MG_options=None) 3169 3170 self.write_global_specs(matrix_elements)
3171
3172 - def write_tir_cache_size_include(self, writer):
3173 """Write the file 'tir_cache_size.inc' which sets the size of the TIR 3174 cache the the user wishes to employ and the default value for it. 3175 This can have an impact on MadLoop speed when using stability checks 3176 but also impacts in a non-negligible way MadLoop's memory footprint. 3177 It is therefore important that the user can chose its size.""" 3178 3179 # In this case of MadLoop+MadEvent output, we set it to 2 because we 3180 # gain further speed increase with a TIR cache of size 2 due to the 3181 # the fact that we call MadLoop once per helicity configuration in this 3182 # case. 3183 tir_cach_size = "parameter(TIR_CACHE_SIZE=2)" 3184 writer.writelines(tir_cach_size)
3185
3186 - def write_matrix_element_v4(self, writer, matrix_element, fortran_model, 3187 proc_id = None, config_map = [], subproc_number = None):
3188 """ Write it the wrapper to call the ML5 subroutine in the library.""" 3189 3190 # Generating the MadEvent wrapping ME's routines 3191 if not matrix_element.get('processes') or \ 3192 not matrix_element.get('diagrams'): 3193 return 0 3194 3195 if not isinstance(writer, writers.FortranWriter): 3196 raise writers.FortranWriter.FortranWriterError(\ 3197 "writer not FortranWriter") 3198 3199 replace_dict = copy.copy(matrix_element.rep_dict) 3200 3201 # Extract version number and date from VERSION file 3202 info_lines = self.get_mg5_info_lines() 3203 replace_dict['info_lines'] = info_lines 3204 3205 # Extract process info lines 3206 process_lines = self.get_process_info_lines(matrix_element) 3207 replace_dict['process_lines'] = process_lines 3208 3209 # Set proc_id 3210 # It can be set to None when write_matrix_element_v4 is called without 3211 # grouping. In this case the subroutine SMATRIX should take an empty 3212 # suffix. 3213 if proc_id is None: 3214 replace_dict['proc_id'] = '' 3215 else: 3216 replace_dict['proc_id'] = proc_id 3217 3218 #set the average over the number of initial helicities 3219 replace_dict['hel_avg_factor'] = matrix_element.get_hel_avg_factor() 3220 replace_dict['beamone_helavgfactor'], replace_dict['beamtwo_helavgfactor'] =\ 3221 matrix_element.get_beams_hel_avg_factor() 3222 3223 # Extract helicity lines 3224 helicity_lines = self.get_helicity_lines(matrix_element) 3225 replace_dict['helicity_lines'] = helicity_lines 3226 3227 3228 # Extract ndiags 3229 ndiags = len(matrix_element.get('diagrams')) 3230 replace_dict['ndiags'] = ndiags 3231 3232 # Set define_iconfigs_lines 3233 replace_dict['define_iconfigs_lines'] = \ 3234 """INTEGER MAPCONFIG(0:LMAXCONFIGS), ICONFIG 3235 COMMON/TO_MCONFIGS/MAPCONFIG, ICONFIG""" 3236 3237 if proc_id: 3238 # Set lines for subprocess group version 3239 # Set define_iconfigs_lines 3240 replace_dict['define_iconfigs_lines'] += \ 3241 """\nINTEGER SUBDIAG(MAXSPROC),IB(2) 3242 COMMON/TO_SUB_DIAG/SUBDIAG,IB""" 3243 # Set set_amp2_line 3244 replace_dict['configID_in_matrix'] = "SUBDIAG(%s)"%proc_id 3245 else: 3246 # Standard running 3247 # Set set_amp2_line 3248 replace_dict['configID_in_matrix'] = "MAPCONFIG(ICONFIG)" 3249 3250 # If group_numer 3251 replace_dict['ml_prefix'] = \ 3252 self.get_ME_identifier(matrix_element, subproc_number, proc_id) 3253 3254 # Extract ncolor 3255 ncolor = max(1, len(matrix_element.get('color_basis'))) 3256 replace_dict['ncolor'] = ncolor 3257 3258 n_tot_diags = len(matrix_element.get_loop_diagrams()) 3259 replace_dict['n_tot_diags'] = n_tot_diags 3260 3261 file = open(pjoin(_file_path, \ 3262 'iolibs/template_files/%s' % self.matrix_file)).read() 3263 file = file % replace_dict 3264 3265 # Write the file 3266 writer.writelines(file) 3267 3268 return 0, ncolor
3269
3270 - def get_amp2_lines(self, *args, **opts):
3271 """Make sure the function is implemented in the daughters""" 3272 3273 raise NotImplemented('The function get_amp2_lines must be called in '+\ 3274 ' the daugthers of LoopInducedExporterME')
3275 3276 #=============================================================================== 3277 # LoopInducedExporterMEGroup 3278 #===============================================================================
3279 -class LoopInducedExporterMEGroup(LoopInducedExporterME, 3280 export_v4.ProcessExporterFortranMEGroup):
3281 """Class to take care of exporting a set of grouped loop induced matrix 3282 elements""" 3283 3284 matrix_file = "matrix_loop_induced_madevent_group.inc" 3285 3291
3292 - def write_source_makefile(self, *args, **opts):
3293 """Pick the correct write_source_makefile function from 3294 ProcessExporterFortranMEGroup""" 3295 3296 export_v4.ProcessExporterFortranMEGroup.write_source_makefile(self, 3297 *args, **opts)
3298
3299 - def copy_template(self, *args, **opts):
3300 """Pick the right mother functions 3301 """ 3302 # Call specifically the necessary building functions for the mixed 3303 # template setup for both MadEvent and MadLoop standalone 3304 3305 # Start witht the MadEvent one 3306 export_v4.ProcessExporterFortranMEGroup.copy_template(self,*args,**opts) 3307 3308 # Then the MadLoop-standalone related one 3309 LoopInducedExporterME.copy_template(self, *args, **opts)
3310
3311 - def finalize(self, *args, **opts):
3312 """Pick the right mother functions 3313 """ 3314 # Call specifically what finalize_v4_directory must be used, so that the 3315 # MRO doesn't interfere. 3316 3317 self.proc_characteristic['loop_induced'] = True 3318 3319 export_v4.ProcessExporterFortranMEGroup.finalize(self,*args,**opts) 3320 3321 # And the finilize from LoopInducedExporterME which essentially takes 3322 # care of MadLoop virtuals initialization 3323 LoopInducedExporterME.finalize(self,*args,**opts)
3324
3325 - def generate_subprocess_directory(self, subproc_group, 3326 fortran_model,group_number):
3327 """Generate the Pn directory for a subprocess group in MadEvent, 3328 including the necessary matrix_N.f files, configs.inc and various 3329 other helper files""" 3330 3331 # Generate the MadLoop files 3332 calls = 0 3333 matrix_elements = subproc_group.get('matrix_elements') 3334 for ime, matrix_element in enumerate(matrix_elements): 3335 self.unique_id +=1 3336 calls += self.generate_loop_subprocess(matrix_element,fortran_model, 3337 group_number = group_number, proc_id = str(ime+1), 3338 # group_number = str(subproc_group.get('number')), proc_id = str(ime+1), 3339 config_map = subproc_group.get('diagram_maps')[ime], 3340 unique_id=self.unique_id) 3341 3342 # Then generate the MadEvent files 3343 export_v4.ProcessExporterFortranMEGroup.generate_subprocess_directory( 3344 self, subproc_group,fortran_model,group_number) 3345 3346 return calls
3347
3348 - def get_amp2_lines(self, matrix_element, replace_dict, config_map):
3349 """Return the various replacement dictionary inputs necessary for the 3350 multichanneling amp2 definition for the loop-induced MadEvent output. 3351 """ 3352 3353 if not config_map: 3354 raise MadGraph5Error('A multi-channeling configuration map is '+\ 3355 ' necessary for the MadEvent Loop-induced output with grouping.') 3356 3357 nexternal, ninitial = matrix_element.get_nexternal_ninitial() 3358 3359 ret_lines = [] 3360 # In this case, we need to sum up all amplitudes that have 3361 # identical topologies, as given by the config_map (which 3362 # gives the topology/config for each of the diagrams 3363 if isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement): 3364 diagrams = matrix_element.get_loop_diagrams() 3365 else: 3366 diagrams = matrix_element.get('diagrams') 3367 3368 # Note that we need to use AMP2 number corresponding to the first 3369 # diagram number used for that AMP2. 3370 # The dictionary below maps the config ID to this corresponding first 3371 # diagram number 3372 config_index_map = {} 3373 # For each diagram number, the dictionary below gives the config_id it 3374 # belongs to or 0 if it doesn't belong to any. 3375 loop_amp_ID_to_config = {} 3376 3377 # Combine the diagrams with identical topologies 3378 config_to_diag_dict = {} 3379 for idiag, diag in enumerate(diagrams): 3380 try: 3381 config_to_diag_dict[config_map[idiag]].append(idiag) 3382 except KeyError: 3383 config_to_diag_dict[config_map[idiag]] = [idiag] 3384 3385 for config in sorted(config_to_diag_dict.keys()): 3386 config_index_map[config] = (config_to_diag_dict[config][0] + 1) 3387 3388 # First add the UV and R2 counterterm amplitudes of each selected 3389 # diagram for the multichannel config 3390 CT_amp_numbers = [a.get('number') for a in \ 3391 sum([diagrams[idiag].get_ct_amplitudes() for \ 3392 idiag in config_to_diag_dict[config]], [])] 3393 3394 for CT_amp_number in CT_amp_numbers: 3395 loop_amp_ID_to_config[CT_amp_number] = config 3396 3397 # Now add here the loop amplitudes. 3398 loop_amp_numbers = [a.get('amplitudes')[0].get('number') 3399 for a in sum([diagrams[idiag].get_loop_amplitudes() for \ 3400 idiag in config_to_diag_dict[config]], [])] 3401 3402 for loop_amp_number in loop_amp_numbers: 3403 loop_amp_ID_to_config[loop_amp_number] = config 3404 3405 # Notice that the config_id's are not necessarily sequential here, so 3406 # the size of the config_index_map array has to be the maximum over all 3407 # config_ids. 3408 # config_index_map should never be empty unless there was no diagram, 3409 # so the expression below is ok. 3410 n_configs = max(config_index_map.keys()) 3411 replace_dict['nmultichannel_configs'] = n_configs 3412 3413 # We must fill the empty entries of the map with the dummy amplitude 3414 # number 0. 3415 conf_list = [(config_index_map[i] if i in config_index_map else 0) \ 3416 for i in range(1,n_configs+1)] 3417 # Now the placeholder 'nmultichannels' refers to the number of 3418 # multi-channels which are contributing, so we must filter out zeros. 3419 replace_dict['nmultichannels'] = len([_ for _ in conf_list if _!=0]) 3420 3421 # Now write the amp2 related inputs in the replacement dictionary 3422 res_list = [] 3423 chunk_size = 6 3424 for k in range(0, len(conf_list), chunk_size): 3425 res_list.append("DATA (config_index_map(i),i=%6r,%6r) /%s/" % \ 3426 (k + 1, min(k + chunk_size, len(conf_list)), 3427 ','.join(["%6r" % i for i in conf_list[k:k + chunk_size]]))) 3428 3429 replace_dict['config_index_map_definition'] = '\n'.join(res_list) 3430 3431 res_list = [] 3432 n_loop_amps = max(loop_amp_ID_to_config.keys()) 3433 amp_list = [loop_amp_ID_to_config[i] for i in \ 3434 sorted(loop_amp_ID_to_config.keys()) if i!=0] 3435 chunk_size = 6 3436 for k in range(0, len(amp_list), chunk_size): 3437 res_list.append("DATA (CONFIG_MAP(i),i=%6r,%6r) /%s/" % \ 3438 (k + 1, min(k + chunk_size, len(amp_list)), 3439 ','.join(["%6r" % i for i in amp_list[k:k + chunk_size]]))) 3440 3441 replace_dict['config_map_definition'] = '\n'.join(res_list) 3442 3443 return
3444 3445 #=============================================================================== 3446 # LoopInducedExporterMENoGroup 3447 #===============================================================================
3448 -class LoopInducedExporterMENoGroup(LoopInducedExporterME, 3449 export_v4.ProcessExporterFortranME):
3450 """Class to take care of exporting a set of individual loop induced matrix 3451 elements""" 3452 3453 matrix_file = "matrix_loop_induced_madevent.inc" 3454 3460
3461 - def write_source_makefile(self, *args, **opts):
3462 """Pick the correct write_source_makefile function from 3463 ProcessExporterFortran""" 3464 3465 super(export_v4.ProcessExporterFortranME,self).\ 3466 write_source_makefile(*args, **opts)
3467
3468 - def copy_template(self, *args, **opts):
3469 """Pick the right mother functions 3470 """ 3471 # Call specifically the necessary building functions for the mixed 3472 # template setup for both MadEvent and MadLoop standalone 3473 3474 # Start witht the MadEvent one 3475 export_v4.ProcessExporterFortranME.copy_template(self,*args,**opts) 3476 3477 # Then the MadLoop-standalone related one 3478 LoopInducedExporterME.copy_template(self, *args, **opts)
3479
3480 - def finalize(self, *args, **opts):
3481 """Pick the right mother functions 3482 """ 3483 3484 self.proc_characteristic['loop_induced'] = True 3485 # Call specifically what finalize must be used, so that the 3486 # MRO doesn't interfere. 3487 export_v4.ProcessExporterFortranME.finalize(self, *args, **opts) 3488 3489 # And the finilize_v4 from LoopInducedExporterME which essentially takes 3490 # care of MadLoop virtuals initialization 3491 LoopInducedExporterME.finalize(self, *args, **opts)
3492
3493 - def generate_subprocess_directory(self, matrix_element, fortran_model, me_number):
3494 """Generate the Pn directory for a subprocess group in MadEvent, 3495 including the necessary matrix_N.f files, configs.inc and various 3496 other helper files""" 3497 3498 self.unique_id += 1 3499 # Then generate the MadLoop files 3500 calls = self.generate_loop_subprocess(matrix_element,fortran_model, 3501 group_number = me_number, 3502 unique_id=self.unique_id) 3503 3504 3505 # First generate the MadEvent files 3506 calls += export_v4.ProcessExporterFortranME.generate_subprocess_directory( 3507 self, matrix_element, fortran_model, me_number) 3508 return calls
3509
3510 - def get_amp2_lines(self, matrix_element, replace_dict, config_map):
3511 """Return the amp2(i) = sum(amp for diag(i))^2 lines""" 3512 3513 if config_map: 3514 raise MadGraph5Error('A configuration map should not be specified'+\ 3515 ' for the Loop induced exporter without grouping.') 3516 3517 nexternal, ninitial = matrix_element.get_nexternal_ninitial() 3518 # Get minimum legs in a vertex 3519 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in \ 3520 matrix_element.get('diagrams') if diag.get_vertex_leg_numbers()!=[]] 3521 minvert = min(vert_list) if vert_list!=[] else 0 3522 3523 # Note that we need to use AMP2 number corresponding to the first 3524 # diagram number used for that AMP2. 3525 # The dictionary below maps the config ID to this corresponding first 3526 # diagram number 3527 config_index_map = {} 3528 # For each diagram number, the dictionary below gives the config_id it 3529 # belongs to or 0 if it doesn't belong to any. 3530 loop_amp_ID_to_config = {} 3531 3532 n_configs = 0 3533 for idiag, diag in enumerate(matrix_element.get('diagrams')): 3534 # Ignore any diagrams with 4-particle vertices. 3535 use_for_multichanneling = True 3536 if diag.get_vertex_leg_numbers()!=[] and max(diag.get_vertex_leg_numbers()) > minvert: 3537 use_for_multichanneling = False 3538 curr_config = 0 3539 else: 3540 n_configs += 1 3541 curr_config = n_configs 3542 3543 if not use_for_multichanneling: 3544 if 0 not in config_index_map: 3545 config_index_map[0] = idiag + 1 3546 else: 3547 config_index_map[curr_config] = idiag + 1 3548 3549 CT_amps = [ a.get('number') for a in diag.get_ct_amplitudes()] 3550 for CT_amp in CT_amps: 3551 loop_amp_ID_to_config[CT_amp] = curr_config 3552 3553 Loop_amps = [a.get('amplitudes')[0].get('number') 3554 for a in diag.get_loop_amplitudes()] 3555 for Loop_amp in Loop_amps: 3556 loop_amp_ID_to_config[Loop_amp] = curr_config 3557 3558 # Now write the amp2 related inputs in the replacement dictionary 3559 n_configs = len([k for k in config_index_map.keys() if k!=0]) 3560 replace_dict['nmultichannel_configs'] = n_configs 3561 # Now the placeholder 'nmultichannels' refers to the number of 3562 # multi-channels which are contributing which, in the non-grouped case 3563 # is always equal to the total number of multi-channels. 3564 replace_dict['nmultichannels'] = n_configs 3565 3566 res_list = [] 3567 conf_list = [config_index_map[i] for i in sorted(config_index_map.keys()) 3568 if i!=0] 3569 chunk_size = 6 3570 for k in range(0, len(conf_list), chunk_size): 3571 res_list.append("DATA (config_index_map(i),i=%6r,%6r) /%s/" % \ 3572 (k + 1, min(k + chunk_size, len(conf_list)), 3573 ','.join(["%6r" % i for i in conf_list[k:k + chunk_size]]))) 3574 3575 replace_dict['config_index_map_definition'] = '\n'.join(res_list) 3576 3577 res_list = [] 3578 n_loop_amps = max(loop_amp_ID_to_config.keys()) 3579 amp_list = [loop_amp_ID_to_config[i] for i in \ 3580 sorted(loop_amp_ID_to_config.keys()) if i!=0] 3581 chunk_size = 6 3582 for k in range(0, len(amp_list), chunk_size): 3583 res_list.append("DATA (CONFIG_MAP(i),i=%6r,%6r) /%s/" % \ 3584 (k + 1, min(k + chunk_size, len(amp_list)), 3585 ','.join(["%6r" % i for i in amp_list[k:k + chunk_size]]))) 3586 3587 replace_dict['config_map_definition'] = '\n'.join(res_list)
3588