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  import copy 
  18  import fractions 
  19  import glob 
  20  import logging 
  21  import os 
  22  import sys 
  23  import re 
  24  import shutil 
  25  import subprocess 
  26  import itertools 
  27  import time 
  28  import datetime 
  29   
  30  import aloha 
  31   
  32  import madgraph.core.base_objects as base_objects 
  33  import madgraph.core.color_algebra as color 
  34  import madgraph.core.helas_objects as helas_objects 
  35  import madgraph.iolibs.drawing_eps as draw 
  36  import madgraph.iolibs.files as files 
  37  import madgraph.iolibs.group_subprocs as group_subprocs 
  38  import madgraph.various.misc as misc 
  39  import madgraph.various.q_polynomial as q_polynomial 
  40  import madgraph.iolibs.file_writers as writers 
  41  import madgraph.iolibs.gen_infohtml as gen_infohtml 
  42  import madgraph.iolibs.template_files as template_files 
  43  import madgraph.iolibs.ufo_expression_parsers as parsers 
  44  import madgraph.iolibs.export_v4 as export_v4 
  45  import madgraph.various.diagram_symmetry as diagram_symmetry 
  46  import madgraph.various.process_checks as process_checks 
  47  import madgraph.various.progressbar as pbar 
  48  import madgraph.core.color_amp as color_amp 
  49  import madgraph.iolibs.helas_call_writers as helas_call_writers 
  50  import models.check_param_card as check_param_card 
  51  from madgraph.loop.loop_base_objects import LoopDiagram 
  52  from madgraph.loop.MadLoopBannerStyles import MadLoopBannerStyles 
  53   
  54  pjoin = os.path.join 
  55   
  56  import aloha.create_aloha as create_aloha 
  57  import models.write_param_card as param_writer 
  58  from madgraph import MadGraph5Error, MG5DIR, InvalidCmd 
  59  from madgraph.iolibs.files import cp, ln, mv 
  60  pjoin = os.path.join 
  61  _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/' 
  62  logger = logging.getLogger('madgraph.loop_exporter') 
  63   
  64  #=============================================================================== 
  65  # LoopExporterFortran 
  66  #=============================================================================== 
67 -class LoopExporterFortran(object):
68 """ Class to define general helper functions to the different 69 loop fortran exporters (ME, SA, MEGroup, etc..) which will inherit both 70 from this class AND from the corresponding ProcessExporterFortran(ME,SA,...). 71 It plays the same role as ProcessExporterFrotran and simply defines here 72 loop-specific helpers functions necessary for all loop exporters. 73 Notice that we do not have LoopExporterFortran inheriting from 74 ProcessExporterFortran but give access to arguments like dir_path and 75 clean using options. This avoids method resolution object ambiguity""" 76
77 - def __init__(self, mgme_dir="", dir_path = "", opt=None):
78 """Initiate the LoopExporterFortran with directory information on where 79 to find all the loop-related source files, like CutTools""" 80 81 if opt: 82 self.opt = opt 83 else: 84 self.opt = {'clean': False, 'complex_mass':False, 85 'export_format':'madloop', 'mp':True, 86 'loop_dir':'', 'cuttools_dir':'', 87 'fortran_compiler':'gfortran', 88 'output_dependencies':'external'} 89 90 self.loop_dir = self.opt['loop_dir'] 91 self.cuttools_dir = self.opt['cuttools_dir'] 92 self.fortran_compiler = self.opt['fortran_compiler'] 93 self.dependencies = self.opt['output_dependencies'] 94 95 super(LoopExporterFortran,self).__init__(mgme_dir, dir_path, self.opt)
96 97 159
160 - def get_aloha_model(self, model):
161 """ Caches the aloha model created here as an attribute of the loop 162 exporter so that it can later be used in the LoopHelasMatrixElement 163 in the function compute_all_analytic_information for recycling aloha 164 computations across different LoopHelasMatrixElements steered by the 165 same loop exporter. 166 """ 167 if not hasattr(self, 'aloha_model'): 168 self.aloha_model = create_aloha.AbstractALOHAModel(model.get('name')) 169 return self.aloha_model
170 171 #=========================================================================== 172 # write the multiple-precision header files 173 #===========================================================================
174 - def write_mp_files(self, writer_mprec, writer_mpc):
175 """Write the cts_mprec.h and cts_mpc.h""" 176 177 file = open(os.path.join(self.cuttools_dir, 'src/cts/cts_mprec.h')).read() 178 writer_mprec.writelines(file) 179 180 file = open(os.path.join(self.cuttools_dir, 'src/cts/cts_mpc.h')).read() 181 file = file.replace('&','') 182 writer_mpc.writelines(file) 183 184 return True
185 186 #=============================================================================== 187 # LoopProcessExporterFortranSA 188 #===============================================================================
189 -class LoopProcessExporterFortranSA(LoopExporterFortran, 190 export_v4.ProcessExporterFortranSA):
191 192 """Class to take care of exporting a set of loop matrix elements in the 193 Fortran format.""" 194 195 template_dir=os.path.join(_file_path,'iolibs/template_files/loop') 196 197 MadLoop_banner = MadLoopBannerStyles.get_MadLoop_Banner( 198 style='classic2', color='green', 199 top_frame_char = '=', bottom_frame_char = '=', 200 left_frame_char = '{',right_frame_char = '}', 201 print_frame=True, side_margin = 7, up_margin = 1) 202
203 - def copy_v4template(self, modelname):
204 """Additional actions needed for setup of Template 205 """ 206 super(LoopProcessExporterFortranSA, self).copy_v4template(modelname) 207 208 # We must change some files to their version for NLO computations 209 cpfiles= ["Source/makefile", 210 "Cards/MadLoopParams.dat", 211 "SubProcesses/MadLoopParamReader.f", 212 "SubProcesses/MadLoopParams.inc"] 213 # first write SubProcesses/makefile from SubProcesses/makefile.inc 214 # dummy variables 215 link_tir_libs=[] 216 tir_libs=[] 217 cwd = os.getcwd() 218 dirpath = os.path.join(self.dir_path, 'SubProcesses') 219 try: 220 os.chdir(dirpath) 221 except os.error: 222 logger.error('Could not cd to directory %s' % dirpath) 223 return 0 224 filename = 'makefile' 225 calls = self.write_makefile_TIR(writers.MakefileWriter(filename), 226 link_tir_libs,tir_libs) 227 # Return to original PWD 228 os.chdir(cwd) 229 230 for file in cpfiles: 231 shutil.copy(os.path.join(self.loop_dir,'StandAlone/', file), 232 os.path.join(self.dir_path, file)) 233 234 # We need minimal editing of MadLoopCommons.f 235 MadLoopCommon = open(os.path.join(self.loop_dir,'StandAlone', 236 "SubProcesses","MadLoopCommons.inc")).read() 237 writer = writers.FortranWriter(os.path.join(self.dir_path, 238 "SubProcesses","MadLoopCommons.f")) 239 writer.writelines(MadLoopCommon%{ 240 'print_banner_commands':self.MadLoop_banner}) 241 writer.close() 242 243 244 # Copy the whole MadLoop5_resources directory (empty at this stage) 245 if not os.path.exists(pjoin(self.dir_path,'SubProcesses', 246 'MadLoop5_resources')): 247 cp(pjoin(self.loop_dir,'StandAlone','SubProcesses', 248 'MadLoop5_resources'),pjoin(self.dir_path,'SubProcesses')) 249 250 # Link relevant cards from Cards inside the MadLoop5_resources 251 ln(pjoin(self.dir_path,'Cards','MadLoopParams.dat'), 252 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 253 ln(pjoin(self.dir_path,'Cards','param_card.dat'), 254 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 255 ln(pjoin(self.dir_path,'Cards','ident_card.dat'), 256 pjoin(self.dir_path,'SubProcesses','MadLoop5_resources')) 257 258 # And remove check_sa in the SubProcess folder since now there is a 259 # check_sa tailored to each subprocess. 260 if os.path.isfile(pjoin(self.dir_path,'SubProcesses','check_sa.f')): 261 os.remove(pjoin(self.dir_path,'SubProcesses','check_sa.f')) 262 263 cwd = os.getcwd() 264 dirpath = os.path.join(self.dir_path, 'SubProcesses') 265 try: 266 os.chdir(dirpath) 267 except os.error: 268 logger.error('Could not cd to directory %s' % dirpath) 269 return 0 270 271 # Write the cts_mpc.h and cts_mprec.h files imported from CutTools 272 self.write_mp_files(writers.FortranWriter('cts_mprec.h'),\ 273 writers.FortranWriter('cts_mpc.h')) 274 275 # Return to original PWD 276 os.chdir(cwd) 277 278 # We must link the CutTools to the Library folder of the active Template 279 super(LoopProcessExporterFortranSA, self).link_CutTools(self.dir_path)
280 281 # This function is placed here and not in optimized exporterd, 282 # because the same makefile.inc should be used in all cases.
283 - def write_makefile_TIR(self, writer, link_tir_libs,tir_libs,tir_include=[]):
284 """ Create the file makefile which links to the TIR libraries.""" 285 286 file = open(os.path.join(self.loop_dir,'StandAlone', 287 'SubProcesses','makefile.inc')).read() 288 replace_dict={} 289 replace_dict['link_tir_libs']=' '.join(link_tir_libs) 290 replace_dict['tir_libs']=' '.join(tir_libs) 291 replace_dict['dotf']='%.f' 292 replace_dict['doto']='%.o' 293 replace_dict['tir_include']=' '.join(tir_include) 294 file=file%replace_dict 295 if writer: 296 writer.writelines(file) 297 else: 298 return file
299
300 - def convert_model_to_mg4(self, model, wanted_lorentz = [], 301 wanted_couplings = []):
302 """ Caches the aloha model created here when writing out the aloha 303 fortran subroutine. 304 """ 305 self.get_aloha_model(model) 306 super(LoopProcessExporterFortranSA, self).convert_model_to_mg4(model, 307 wanted_lorentz = wanted_lorentz, wanted_couplings = wanted_couplings)
308 309
310 - def get_ME_identifier(self, matrix_element):
311 """ A function returning a string uniquely identifying the matrix 312 element given in argument so that it can be used as a prefix to all 313 MadLoop5 subroutines and common blocks related to it. This allows 314 to compile several processes into one library as requested by the 315 BLHA (Binoth LesHouches Accord) guidelines. """ 316 317 # return 'ML5_%s_'%matrix_element.get('processes')[0].shell_string() 318 # The version below is shorter 319 return 'ML5_%d_'%matrix_element.get('processes')[0].get('id')
320 321 #=========================================================================== 322 # Set the compiler to be gfortran for the loop processes. 323 #===========================================================================
324 - def compiler_choice(self, compiler):
325 """ Different daughter classes might want different compilers. 326 Here, the gfortran compiler is used throughout the compilation 327 (mandatory for CutTools written in f90) """ 328 if not compiler is None and not any([name in compiler for name in \ 329 ['gfortran','ifort']]): 330 logger.info('For loop processes, the compiler must be fortran90'+\ 331 'compatible, like gfortran.') 332 self.set_compiler('gfortran',True) 333 else: 334 self.set_compiler(compiler)
335
336 - def turn_to_mp_calls(self, helas_calls_list):
337 # Prepend 'MP_' to all the helas calls in helas_calls_list. 338 # Might look like a brutal unsafe implementation, but it is not as 339 # these calls are built from the properties of the HELAS objects and 340 # whether they are evaluated in double or quad precision is none of 341 # their business but only relevant to the output algorithm. 342 # Also the cast to complex masses DCMPLX(*) must be replaced by 343 # CMPLX(*,KIND=16) 344 MP=re.compile(r"(?P<toSub>^.*CALL\s+)",re.IGNORECASE | re.MULTILINE) 345 346 def replaceWith(match_obj): 347 return match_obj.group('toSub')+'MP_'
348 349 DCMPLX=re.compile(r"DCMPLX\((?P<toSub>([^\)]*))\)",\ 350 re.IGNORECASE | re.MULTILINE) 351 352 for i, helas_call in enumerate(helas_calls_list): 353 new_helas_call=MP.sub(replaceWith,helas_call) 354 helas_calls_list[i]=DCMPLX.sub(r"CMPLX(\g<toSub>,KIND=16)",\ 355 new_helas_call)
356 360 368
369 - def make(self):
370 """ Compiles the additional dependences for loop (such as CutTools).""" 371 super(LoopProcessExporterFortranSA, self).make() 372 373 # make CutTools (only necessary with MG option output_dependencies='internal') 374 libdir = os.path.join(self.dir_path,'lib') 375 sourcedir = os.path.join(self.dir_path,'Source') 376 if self.dependencies=='internal': 377 if not os.path.exists(os.path.realpath(pjoin(libdir, 'libcts.a'))) or \ 378 not os.path.exists(os.path.realpath(pjoin(libdir, 'mpmodule.mod'))): 379 if os.path.exists(pjoin(sourcedir,'CutTools')): 380 logger.info('Compiling CutTools (can take a couple of minutes) ...') 381 misc.compile(['CutTools'], cwd = sourcedir) 382 logger.info(' ...done.') 383 else: 384 raise MadGraph5Error('Could not compile CutTools because its'+\ 385 ' source directory could not be found in the SOURCE folder.') 386 if not os.path.exists(os.path.realpath(pjoin(libdir, 'libcts.a'))) or \ 387 not os.path.exists(os.path.realpath(pjoin(libdir, 'mpmodule.mod'))): 388 raise MadGraph5Error('CutTools compilation failed.') 389 390 # Verify compatibility between current compiler and the one which was 391 # used when last compiling CutTools (if specified). 392 compiler_log_path = pjoin(os.path.dirname((os.path.realpath(pjoin( 393 libdir, 'libcts.a')))),'compiler_version.log') 394 if os.path.exists(compiler_log_path): 395 compiler_version_used = open(compiler_log_path,'r').read() 396 if not str(misc.get_gfortran_version(misc.detect_current_compiler(\ 397 pjoin(sourcedir,'make_opts')))) in compiler_version_used: 398 if os.path.exists(pjoin(sourcedir,'CutTools')): 399 logger.info('CutTools was compiled with a different fortran'+\ 400 ' compiler. Re-compiling it now...') 401 misc.compile(['cleanCT'], cwd = sourcedir) 402 misc.compile(['CutTools'], cwd = sourcedir) 403 logger.info(' ...done.') 404 else: 405 raise MadGraph5Error("CutTools installation in %s"\ 406 %os.path.realpath(pjoin(libdir, 'libcts.a'))+\ 407 " seems to have been compiled with a different compiler than"+\ 408 " the one specified in MG5_aMC. Please recompile CutTools.")
409
410 - def cat_coeff(self, ff_number, frac, is_imaginary, Nc_power, Nc_value=3):
411 """Concatenate the coefficient information to reduce it to 412 (fraction, is_imaginary) """ 413 414 total_coeff = ff_number * frac * fractions.Fraction(Nc_value) ** Nc_power 415 416 return (total_coeff, is_imaginary)
417
418 - def get_amp_to_jamp_map(self, col_amps, n_amps):
419 """ Returns a list with element 'i' being a list of tuples corresponding 420 to all apparition of amplitude number 'i' in the jamp number 'j' 421 with coeff 'coeff_j'. The format of each tuple describing an apparition 422 is (j, coeff_j). where coeff_j is of the form (Fraction, is_imag).""" 423 424 if(isinstance(col_amps,list)): 425 if(col_amps and isinstance(col_amps[0],list)): 426 color_amplitudes=col_amps 427 else: 428 raise MadGraph5Error, "Incorrect col_amps argument passed to get_amp_to_jamp_map" 429 else: 430 raise MadGraph5Error, "Incorrect col_amps argument passed to get_amp_to_jamp_map" 431 432 # To store the result 433 res_list = [[] for i in range(n_amps)] 434 for i, coeff_list in enumerate(color_amplitudes): 435 for (coefficient, amp_number) in coeff_list: 436 res_list[amp_number-1].append((i,self.cat_coeff(\ 437 coefficient[0],coefficient[1],coefficient[2],coefficient[3]))) 438 439 return res_list
440
441 - def get_color_matrix(self, matrix_element):
442 """Return the color matrix definition lines. This color matrix is of size 443 NLOOPAMPSxNBORNAMPS and allows for squaring individually each Loop and Born 444 amplitude.""" 445 446 logger.info('Computing diagram color coefficients') 447 448 # The two lists have a list of tuples at element 'i' which correspond 449 # to all apparitions of loop amplitude number 'i' in the jampl number 'j' 450 # with coeff 'coeffj'. The format of each tuple describing an apparition 451 # is (j, coeffj). 452 ampl_to_jampl=self.get_amp_to_jamp_map(\ 453 matrix_element.get_loop_color_amplitudes(), 454 matrix_element.get_number_of_loop_amplitudes()) 455 if matrix_element.get('processes')[0].get('has_born'): 456 ampb_to_jampb=self.get_amp_to_jamp_map(\ 457 matrix_element.get_born_color_amplitudes(), 458 matrix_element.get_number_of_born_amplitudes()) 459 else: 460 ampb_to_jampb=ampl_to_jampl 461 # Below is the original color matrix multiplying the JAMPS 462 if matrix_element.get('color_matrix'): 463 ColorMatrixDenom = \ 464 matrix_element.get('color_matrix').get_line_denominators() 465 ColorMatrixNum = [ matrix_element.get('color_matrix').\ 466 get_line_numerators(index, denominator) for 467 (index, denominator) in enumerate(ColorMatrixDenom) ] 468 else: 469 ColorMatrixDenom= [1] 470 ColorMatrixNum = [[1]] 471 472 # Below is the final color matrix output 473 ColorMatrixNumOutput=[] 474 ColorMatrixDenomOutput=[] 475 476 # Now we construct the color factors between each born and loop amplitude 477 # by scanning their contributions to the different jamps. 478 start = time.time() 479 progress_bar = None 480 time_info = False 481 for i, jampl_list in enumerate(ampl_to_jampl): 482 # This can be pretty long for processes with many color flows. 483 # So, if necessary (i.e. for more than 15s), we tell the user the 484 # estimated time for the processing. 485 if i==10: 486 elapsed_time = time.time()-start 487 t = int(len(ampl_to_jampl)*(elapsed_time/10.0)) 488 if t > 20: 489 time_info = True 490 logger.info('The color factors computation will take '+\ 491 ' about %s to run. '%str(datetime.timedelta(seconds=t))+\ 492 'Started on %s.'%datetime.datetime.now().strftime(\ 493 "%d-%m-%Y %H:%M")) 494 if logger.getEffectiveLevel()<logging.WARNING: 495 widgets = ['Color computation:', pbar.Percentage(), ' ', 496 pbar.Bar(),' ', pbar.ETA(), ' '] 497 progress_bar = pbar.ProgressBar(widgets=widgets, 498 maxval=len(ampl_to_jampl),fd=sys.stdout) 499 if progress_bar!=None: 500 progress_bar.update(i+1) 501 line_num=[] 502 line_denom=[] 503 504 # Treat the special case where this specific amplitude contributes to no 505 # color flow at all. So it is zero because of color but not even due to 506 # an accidental cancellation among color flows, but simply because of its 507 # projection to each individual color flow is zero. In such case, the 508 # corresponding jampl_list is empty and all color coefficients must then 509 # be zero. This happens for example in the Higgs Effective Theory model 510 # for the bubble made of a 4-gluon vertex and the effective ggH vertex. 511 if len(jampl_list)==0: 512 line_num=[0]*len(ampb_to_jampb) 513 line_denom=[1]*len(ampb_to_jampb) 514 ColorMatrixNumOutput.append(line_num) 515 ColorMatrixDenomOutput.append(line_denom) 516 continue 517 518 for jampb_list in ampb_to_jampb: 519 real_num=0 520 imag_num=0 521 common_denom=color_amp.ColorMatrix.lcmm(*[abs(ColorMatrixDenom[jampl]* 522 ampl_coeff[0].denominator*ampb_coeff[0].denominator) for 523 ((jampl, ampl_coeff),(jampb,ampb_coeff)) in 524 itertools.product(jampl_list,jampb_list)]) 525 for ((jampl, ampl_coeff),(jampb, ampb_coeff)) in \ 526 itertools.product(jampl_list,jampb_list): 527 # take the numerator and multiply by lcm/denominator 528 # as we will later divide by the lcm. 529 buff_num=ampl_coeff[0].numerator*\ 530 ampb_coeff[0].numerator*ColorMatrixNum[jampl][jampb]*\ 531 abs(common_denom)/(ampl_coeff[0].denominator*\ 532 ampb_coeff[0].denominator*ColorMatrixDenom[jampl]) 533 # Remember that we must take the complex conjugate of 534 # the born jamp color coefficient because we will compute 535 # the square with 2 Re(LoopAmp x BornAmp*) 536 if ampl_coeff[1] and ampb_coeff[1]: 537 real_num=real_num+buff_num 538 elif not ampl_coeff[1] and not ampb_coeff[1]: 539 real_num=real_num+buff_num 540 elif not ampl_coeff[1] and ampb_coeff[1]: 541 imag_num=imag_num-buff_num 542 else: 543 imag_num=imag_num+buff_num 544 assert not (real_num!=0 and imag_num!=0), "MadGraph5_aMC@NLO found a "+\ 545 "color matrix element which has both a real and imaginary part." 546 if imag_num!=0: 547 res=fractions.Fraction(imag_num,common_denom) 548 line_num.append(res.numerator) 549 # Negative denominator means imaginary color coef of the 550 # final color matrix 551 line_denom.append(res.denominator*-1) 552 else: 553 res=fractions.Fraction(real_num,common_denom) 554 line_num.append(res.numerator) 555 # Positive denominator means real color coef of the final color matrix 556 line_denom.append(res.denominator) 557 558 ColorMatrixNumOutput.append(line_num) 559 ColorMatrixDenomOutput.append(line_denom) 560 561 if time_info: 562 logger.info('Finished on %s.'%datetime.datetime.now().strftime(\ 563 "%d-%m-%Y %H:%M")) 564 if progress_bar!=None: 565 progress_bar.finish() 566 567 return (ColorMatrixNumOutput,ColorMatrixDenomOutput)
568 569 #=========================================================================== 570 # generate_subprocess_directory_v4 571 #=========================================================================== 572
573 - def generate_loop_subprocess(self, matrix_element, fortran_model):
574 """Generate the Pxxxxx directory for a loop subprocess in MG4 standalone, 575 including the necessary loop_matrix.f, born_matrix.f and include files. 576 Notice that this is too different from generate_subprocess_directory_v4 577 so that there is no point reusing this mother function.""" 578 579 cwd = os.getcwd() 580 # Create the directory PN_xx_xxxxx in the specified path 581 dirpath = os.path.join(self.dir_path, 'SubProcesses', \ 582 "P%s" % matrix_element.get('processes')[0].shell_string()) 583 584 try: 585 os.mkdir(dirpath) 586 except os.error as error: 587 logger.warning(error.strerror + " " + dirpath) 588 589 try: 590 os.chdir(dirpath) 591 except os.error: 592 logger.error('Could not cd to directory %s' % dirpath) 593 return 0 594 595 logger.info('Creating files in directory %s' % dirpath) 596 597 # Extract number of external particles 598 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 599 600 calls=self.write_matrix_element_v4(None,matrix_element,fortran_model) 601 602 # We assume here that all processes must share the same property of 603 # having a born or not, which must be true anyway since these are two 604 # definite different classes of processes which can never be treated on 605 # the same footing. 606 if matrix_element.get('processes')[0].get('has_born'): 607 filename = 'born_matrix.f' 608 calls = self.write_bornmatrix( 609 writers.FortranWriter(filename), 610 matrix_element, 611 fortran_model) 612 613 filename = 'nexternal.inc' 614 self.write_nexternal_file(writers.FortranWriter(filename), 615 (nexternal-2), ninitial) 616 617 filename = 'pmass.inc' 618 self.write_pmass_file(writers.FortranWriter(filename), 619 matrix_element) 620 621 filename = 'ngraphs.inc' 622 self.write_ngraphs_file(writers.FortranWriter(filename), 623 len(matrix_element.get_all_amplitudes())) 624 625 # Do not draw the loop diagrams if they are too many. 626 # The user can always decide to do it manually, if really needed 627 loop_diags = [loop_diag for loop_diag in\ 628 matrix_element.get('base_amplitude').get('loop_diagrams')\ 629 if isinstance(loop_diag,LoopDiagram) and \ 630 loop_diag.get('type') > 0] 631 if len(loop_diags)>5000: 632 logger.info("There are more than 5000 loop diagrams."+\ 633 "Only the first 5000 are drawn.") 634 filename = "loop_matrix.ps" 635 plot = draw.MultiEpsDiagramDrawer(base_objects.DiagramList( 636 loop_diags[:5000]),filename, 637 model=matrix_element.get('processes')[0].get('model'),amplitude='') 638 logger.info("Drawing loop Feynman diagrams for " + \ 639 matrix_element.get('processes')[0].nice_string()) 640 plot.draw() 641 642 if matrix_element.get('processes')[0].get('has_born'): 643 filename = "born_matrix.ps" 644 plot = draw.MultiEpsDiagramDrawer(matrix_element.get('base_amplitude').\ 645 get('born_diagrams'), 646 filename, 647 model=matrix_element.get('processes')[0].\ 648 get('model'), 649 amplitude='') 650 logger.info("Generating born Feynman diagrams for " + \ 651 matrix_element.get('processes')[0].nice_string(\ 652 print_weighted=False)) 653 plot.draw() 654 655 self.link_files_from_Subprocesses(proc_name=\ 656 matrix_element.get('processes')[0].shell_string()) 657 658 # Return to original PWD 659 os.chdir(cwd) 660 661 if not calls: 662 calls = 0 663 return calls
664 683
684 - def generate_general_replace_dict(self,matrix_element):
685 """Generates the entries for the general replacement dictionary used 686 for the different output codes for this exporter""" 687 688 dict={} 689 # A general process prefix which appears in front of all MadLooop 690 # subroutines and common block so that several processes can be compiled 691 # together into one library, as necessary to follow BLHA guidelines. 692 dict['proc_prefix'] = self.get_ME_identifier(matrix_element) 693 694 # The proc_id is used for MadEvent grouping, so none of our concern here 695 # and it is simply set to an empty string. 696 dict['proc_id'] = '' 697 # Extract version number and date from VERSION file 698 info_lines = self.get_mg5_info_lines() 699 dict['info_lines'] = info_lines 700 # Extract process info lines 701 process_lines = self.get_process_info_lines(matrix_element) 702 dict['process_lines'] = process_lines 703 # Extract number of external particles 704 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 705 dict['nexternal'] = nexternal-2 706 # Extract ncomb 707 ncomb = matrix_element.get_helicity_combinations() 708 dict['ncomb'] = ncomb 709 # Extract nloopamps 710 nloopamps = matrix_element.get_number_of_loop_amplitudes() 711 dict['nloopamps'] = nloopamps 712 # Extract nctamps 713 nctamps = matrix_element.get_number_of_CT_amplitudes() 714 dict['nctamps'] = nctamps 715 # Extract nwavefuncs 716 nwavefuncs = matrix_element.get_number_of_external_wavefunctions() 717 dict['nwavefuncs'] = nwavefuncs 718 # Set format of the double precision 719 dict['real_dp_format']='real*8' 720 dict['real_mp_format']='real*16' 721 # Set format of the complex 722 dict['complex_dp_format']='complex*16' 723 dict['complex_mp_format']='complex*32' 724 # Set format of the masses 725 dict['mass_dp_format'] = dict['complex_dp_format'] 726 dict['mass_mp_format'] = dict['complex_mp_format'] 727 # Color matrix size 728 # For loop induced processes it is NLOOPAMPSxNLOOPAMPS and otherwise 729 # it is NLOOPAMPSxNBORNAMPS 730 # Also, how to access the number of Born squared order contributions 731 732 if matrix_element.get('processes')[0].get('has_born'): 733 dict['color_matrix_size'] = 'nbornamps' 734 dict['get_nsqso_born']=\ 735 "include 'nsqso_born.inc'" 736 else: 737 dict['get_nsqso_born']="""INTEGER NSQSO_BORN 738 PARAMETER (NSQSO_BORN=0) 739 """ 740 dict['color_matrix_size'] = 'nloopamps' 741 742 # These placeholders help to have as many common templates for the 743 # output of the loop induced processes and those with a born 744 # contribution. 745 if matrix_element.get('processes')[0].get('has_born'): 746 # Extract nbornamps 747 nbornamps = matrix_element.get_number_of_born_amplitudes() 748 dict['nbornamps'] = nbornamps 749 dict['ncomb_helas_objs'] = ',ncomb' 750 dict['nbornamps_decl'] = \ 751 """INTEGER NBORNAMPS 752 PARAMETER (NBORNAMPS=%d)"""%nbornamps 753 dict['nBornAmps'] = nbornamps 754 755 else: 756 dict['ncomb_helas_objs'] = '' 757 dict['dp_born_amps_decl'] = '' 758 dict['dp_born_amps_decl_in_mp'] = '' 759 dict['copy_mp_to_dp_born_amps'] = '' 760 dict['mp_born_amps_decl'] = '' 761 dict['nbornamps_decl'] = '' 762 dict['nBornAmps'] = 0 763 764 return dict
765
766 - def write_matrix_element_v4(self, writer, matrix_element, fortran_model, 767 proc_id = "", config_map = []):
768 """ Writes loop_matrix.f, CT_interface.f and loop_num.f only""" 769 770 # Create the necessary files for the loop matrix element subroutine 771 772 if not isinstance(fortran_model,\ 773 helas_call_writers.FortranUFOHelasCallWriter): 774 raise MadGraph5Error, 'The loop fortran output can only'+\ 775 ' work with a UFO Fortran model' 776 777 LoopFortranModel = helas_call_writers.FortranUFOHelasCallWriter( 778 argument=fortran_model.get('model'), 779 hel_sum=matrix_element.get('processes')[0].get('has_born')) 780 781 # Compute the analytical information of the loop wavefunctions in the 782 # loop helas matrix elements using the cached aloha model to reuse 783 # as much as possible the aloha computations already performed for 784 # writing out the aloha fortran subroutines. 785 matrix_element.compute_all_analytic_information( 786 self.get_aloha_model(matrix_element.get('processes')[0].get('model'))) 787 788 # Initialize a general replacement dictionary with entries common to 789 # many files generated here. 790 self.general_replace_dict=\ 791 self.generate_general_replace_dict(matrix_element) 792 793 # Extract max number of loop couplings (specific to this output type) 794 self.general_replace_dict['maxlcouplings']= \ 795 matrix_element.find_max_loop_coupling() 796 # The born amp declaration suited for also outputing the loop-induced 797 # processes as well. 798 if matrix_element.get('processes')[0].get('has_born'): 799 self.general_replace_dict['dp_born_amps_decl_in_mp'] = \ 800 self.general_replace_dict['complex_dp_format']+" DPAMP(NBORNAMPS,NCOMB)"+\ 801 "\n common/%sAMPS/DPAMP"%self.general_replace_dict['proc_prefix'] 802 self.general_replace_dict['dp_born_amps_decl'] = \ 803 self.general_replace_dict['complex_dp_format']+" AMP(NBORNAMPS,NCOMB)"+\ 804 "\n common/%sAMPS/AMP"%self.general_replace_dict['proc_prefix'] 805 self.general_replace_dict['mp_born_amps_decl'] = \ 806 self.general_replace_dict['complex_mp_format']+" AMP(NBORNAMPS,NCOMB)"+\ 807 "\n common/%sMP_AMPS/AMP"%self.general_replace_dict['proc_prefix'] 808 self.general_replace_dict['copy_mp_to_dp_born_amps'] = \ 809 '\n'.join(['DO I=1,NBORNAMPS','DPAMP(I,H)=AMP(I,H)','ENDDO']) 810 811 if writer: 812 raise MadGraph5Error, 'Matrix output mode no longer supported.' 813 814 else: 815 filename = 'loop_matrix.f' 816 calls = self.write_loopmatrix(writers.FortranWriter(filename), 817 matrix_element, 818 LoopFortranModel) 819 820 # Write out the proc_prefix in a file, this is quite handy 821 proc_prefix_writer = writers.FortranWriter('proc_prefix.txt','w') 822 proc_prefix_writer.write(self.general_replace_dict['proc_prefix']) 823 proc_prefix_writer.close() 824 825 filename = 'check_sa.f' 826 self.write_check_sa(writers.FortranWriter(filename),matrix_element) 827 828 filename = 'CT_interface.f' 829 self.write_CT_interface(writers.FortranWriter(filename),\ 830 matrix_element) 831 832 filename = 'improve_ps.f' 833 calls = self.write_improve_ps(writers.FortranWriter(filename), 834 matrix_element) 835 836 filename = 'loop_num.f' 837 self.write_loop_num(writers.FortranWriter(filename),\ 838 matrix_element,LoopFortranModel) 839 840 filename = 'mp_born_amps_and_wfs.f' 841 self.write_born_amps_and_wfs(writers.FortranWriter(filename),\ 842 matrix_element,LoopFortranModel) 843 844 return calls
845
846 - def generate_subprocess_directory_v4(self, matrix_element, 847 fortran_model):
848 """ To overload the default name for this function such that the correct 849 function is used when called from the command interface """ 850 851 return self.generate_loop_subprocess(matrix_element,fortran_model)
852
853 - def write_check_sa(self, writer, matrix_element):
854 """Writes out the steering code check_sa. In the optimized output mode, 855 All the necessary entries in the replace_dictionary have already been 856 set in write_loopmatrix because it is only there that one has access to 857 the information about split orders.""" 858 replace_dict = copy.copy(self.general_replace_dict) 859 for key in ['print_so_born_results','print_so_loop_results', 860 'write_so_born_results','write_so_loop_results','set_coupling_target']: 861 if key not in replace_dict.keys(): 862 replace_dict[key]='' 863 if matrix_element.get('processes')[0].get('has_born'): 864 file = open(os.path.join(self.template_dir,'check_sa.inc')).read() 865 else: 866 file = open(os.path.join(self.template_dir,\ 867 'check_sa_loop_induced.inc')).read() 868 file=file%replace_dict 869 writer.writelines(file)
870
871 - def write_improve_ps(self, writer, matrix_element):
872 """ Write out the improve_ps subroutines which modify the PS point 873 given in input and slightly deform it to achieve exact onshellness on 874 all external particles as well as perfect energy-momentum conservation""" 875 replace_dict = copy.copy(self.general_replace_dict) 876 877 (nexternal,ninitial)=matrix_element.get_nexternal_ninitial() 878 replace_dict['ninitial']=ninitial 879 mass_list=matrix_element.get_external_masses()[:-2] 880 mp_variable_prefix = check_param_card.ParamCard.mp_prefix 881 882 # Write the quadruple precision version of this routine only. 883 replace_dict['real_format']=replace_dict['real_mp_format'] 884 replace_dict['mp_prefix']='MP_' 885 replace_dict['exp_letter']='e' 886 replace_dict['mp_specifier']='_16' 887 replace_dict['coupl_inc_name']='mp_coupl.inc' 888 replace_dict['masses_def']='\n'.join(['MASSES(%(i)d)=%(prefix)s%(m)s'\ 889 %{'i':i+1,'m':m, 'prefix':mp_variable_prefix} for \ 890 i, m in enumerate(mass_list)]) 891 file_mp = open(os.path.join(self.template_dir,'improve_ps.inc')).read() 892 file_mp=file_mp%replace_dict 893 # 894 writer.writelines(file_mp)
895
896 - def write_loop_num(self, writer, matrix_element,fortran_model):
897 """ Create the file containing the core subroutine called by CutTools 898 which contains the Helas calls building the loop""" 899 900 if not matrix_element.get('processes') or \ 901 not matrix_element.get('diagrams'): 902 return 0 903 904 # Set lowercase/uppercase Fortran code 905 writers.FortranWriter.downcase = False 906 907 file = open(os.path.join(self.template_dir,'loop_num.inc')).read() 908 909 replace_dict = copy.copy(self.general_replace_dict) 910 911 loop_helas_calls=fortran_model.get_loop_amplitude_helas_calls(matrix_element) 912 replace_dict['maxlcouplings']=matrix_element.find_max_loop_coupling() 913 replace_dict['loop_helas_calls'] = "\n".join(loop_helas_calls) 914 915 # The squaring is only necessary for the processes with born where the 916 # sum over helicities is done before sending the numerator to CT. 917 dp_squaring_lines=['DO I=1,NBORNAMPS', 918 'CFTOT=DCMPLX(CF_N(AMPLNUM,I)/DBLE(ABS(CF_D(AMPLNUM,I))),0.0d0)', 919 'IF(CF_D(AMPLNUM,I).LT.0) CFTOT=CFTOT*IMAG1', 920 'RES=RES+CFTOT*BUFF*DCONJG(AMP(I,H))','ENDDO'] 921 mp_squaring_lines=['DO I=1,NBORNAMPS', 922 'CFTOT=CMPLX(CF_N(AMPLNUM,I)/(1.0E0_16*ABS(CF_D(AMPLNUM,I))),0.0E0_16,KIND=16)', 923 'IF(CF_D(AMPLNUM,I).LT.0) CFTOT=CFTOT*IMAG1', 924 'QPRES=QPRES+CFTOT*BUFF*CONJG(AMP(I,H))','ENDDO'] 925 if matrix_element.get('processes')[0].get('has_born'): 926 replace_dict['dp_squaring']='\n'.join(dp_squaring_lines) 927 replace_dict['mp_squaring']='\n'.join(mp_squaring_lines) 928 else: 929 replace_dict['dp_squaring']='RES=BUFF' 930 replace_dict['mp_squaring']='QPRES=BUFF' 931 932 # Prepend MP_ to all helas calls. 933 self.turn_to_mp_calls(loop_helas_calls) 934 replace_dict['mp_loop_helas_calls'] = "\n".join(loop_helas_calls) 935 936 file=file%replace_dict 937 938 if writer: 939 writer.writelines(file) 940 else: 941 return file
942
943 - def write_CT_interface(self, writer, matrix_element, optimized_output=False):
944 """ Create the file CT_interface.f which contains the subroutine defining 945 the loop HELAS-like calls along with the general interfacing subroutine. """ 946 947 files=[] 948 949 # First write CT_interface which interfaces MG5 with CutTools. 950 replace_dict=copy.copy(self.general_replace_dict) 951 952 # We finalize CT result differently wether we used the built-in 953 # squaring against the born. 954 if matrix_element.get('processes')[0].get('has_born'): 955 replace_dict['finalize_CT']='\n'.join([\ 956 'RES(%d)=NORMALIZATION*2.0d0*DBLE(RES(%d))'%(i,i) for i in range(1,4)]) 957 else: 958 replace_dict['finalize_CT']='\n'.join([\ 959 'RES(%d)=NORMALIZATION*RES(%d)'%(i,i) for i in range(1,4)]) 960 961 file = open(os.path.join(self.template_dir,'CT_interface.inc')).read() 962 963 file = file % replace_dict 964 files.append(file) 965 966 # Now collect the different kind of subroutines needed for the 967 # loop HELAS-like calls. 968 HelasLoopAmpsCallKeys=matrix_element.get_used_helas_loop_amps() 969 970 for callkey in HelasLoopAmpsCallKeys: 971 replace_dict=copy.copy(self.general_replace_dict) 972 # Add to this dictionary all other attribute common to all 973 # HELAS-like loop subroutines. 974 if matrix_element.get('processes')[0].get('has_born'): 975 replace_dict['validh_or_nothing']=',validh' 976 else: 977 replace_dict['validh_or_nothing']='' 978 # In the optimized output, the number of couplings in the loop is 979 # not specified so we only treat it here if necessary: 980 if len(callkey)>2: 981 replace_dict['ncplsargs']=callkey[2] 982 cplsargs="".join(["C%d,MP_C%d, "%(i,i) for i in range(1,callkey[2]+1)]) 983 replace_dict['cplsargs']=cplsargs 984 cplsdecl="".join(["C%d, "%i for i in range(1,callkey[2]+1)])[:-2] 985 replace_dict['cplsdecl']=cplsdecl 986 mp_cplsdecl="".join(["MP_C%d, "%i for i in range(1,callkey[2]+1)])[:-2] 987 replace_dict['mp_cplsdecl']=mp_cplsdecl 988 cplset="\n".join(["\n".join(["LC(%d)=C%d"%(i,i),\ 989 "MP_LC(%d)=MP_C%d"%(i,i)])\ 990 for i in range(1,callkey[2]+1)]) 991 replace_dict['cplset']=cplset 992 993 replace_dict['nloopline']=callkey[0] 994 wfsargs="".join(["W%d, "%i for i in range(1,callkey[1]+1)]) 995 replace_dict['wfsargs']=wfsargs 996 # We don't pass the multiple precision mass in the optimized_output 997 if not optimized_output: 998 margs="".join(["M%d,MP_M%d, "%(i,i) for i in range(1,callkey[0]+1)]) 999 else: 1000 margs="".join(["M%d, "%i for i in range(1,callkey[0]+1)]) 1001 replace_dict['margs']=margs 1002 wfsargsdecl="".join([("W%d, "%i) for i in range(1,callkey[1]+1)])[:-2] 1003 replace_dict['wfsargsdecl']=wfsargsdecl 1004 margsdecl="".join(["M%d, "%i for i in range(1,callkey[0]+1)])[:-2] 1005 replace_dict['margsdecl']=margsdecl 1006 mp_margsdecl="".join(["MP_M%d, "%i for i in range(1,callkey[0]+1)])[:-2] 1007 replace_dict['mp_margsdecl']=mp_margsdecl 1008 weset="\n".join([("WE("+str(i)+")=W"+str(i)) for \ 1009 i in range(1,callkey[1]+1)]) 1010 replace_dict['weset']=weset 1011 weset="\n".join([("WE(%d)=W%d"%(i,i)) for i in range(1,callkey[1]+1)]) 1012 replace_dict['weset']=weset 1013 msetlines=["M2L(1)=M%d**2"%(callkey[0]),] 1014 mset="\n".join(msetlines+["M2L(%d)=M%d**2"%(i,i-1) for \ 1015 i in range(2,callkey[0]+1)]) 1016 replace_dict['mset']=mset 1017 mset2lines=["ML(1)=M%d"%(callkey[0]),"ML(2)=M%d"%(callkey[0]), 1018 "MP_ML(1)=MP_M%d"%(callkey[0]),"MP_ML(2)=MP_M%d"%(callkey[0])] 1019 mset2="\n".join(mset2lines+["\n".join(["ML(%d)=M%d"%(i,i-2), 1020 "MP_ML(%d)=MP_M%d"%(i,i-2)]) for \ 1021 i in range(3,callkey[0]+3)]) 1022 replace_dict['mset2']=mset2 1023 replace_dict['nwfsargs'] = callkey[1] 1024 if callkey[0]==callkey[1]: 1025 replace_dict['nwfsargs_header'] = "" 1026 replace_dict['pairingargs']="" 1027 replace_dict['pairingdecl']="" 1028 pairingset="""DO I=1,NLOOPLINE 1029 PAIRING(I)=1 1030 ENDDO 1031 """ 1032 replace_dict['pairingset']=pairingset 1033 else: 1034 replace_dict['nwfsargs_header'] = '_%d'%callkey[1] 1035 pairingargs="".join([("P"+str(i)+", ") for i in \ 1036 range(1,callkey[0]+1)]) 1037 replace_dict['pairingargs']=pairingargs 1038 pairingdecl="integer "+"".join([("P"+str(i)+", ") for i in \ 1039 range(1,callkey[0]+1)])[:-2] 1040 replace_dict['pairingdecl']=pairingdecl 1041 pairingset="\n".join([("PAIRING("+str(i)+")=P"+str(i)) for \ 1042 i in range(1,callkey[0]+1)]) 1043 replace_dict['pairingset']=pairingset 1044 1045 file = open(os.path.join(self.template_dir,\ 1046 'helas_loop_amplitude.inc')).read() 1047 file = file % replace_dict 1048 files.append(file) 1049 1050 file="\n".join(files) 1051 1052 if writer: 1053 writer.writelines(file) 1054 else: 1055 return file
1056 1057 # Helper function to split HELAS CALLS in dedicated subroutines placed 1058 # in different files.
1059 - def split_HELASCALLS(self, writer, replace_dict, template_name, masterfile, \ 1060 helas_calls, entry_name, bunch_name,n_helas=2000, 1061 required_so_broadcaster = 'LOOP_REQ_SO_DONE', 1062 continue_label = 1000):
1063 """ Finish the code generation with splitting. 1064 Split the helas calls in the argument helas_calls into bunches of 1065 size n_helas and place them in dedicated subroutine with name 1066 <bunch_name>_i. Also setup the corresponding calls to these subroutine 1067 in the replace_dict dictionary under the entry entry_name. """ 1068 helascalls_replace_dict=copy.copy(replace_dict) 1069 helascalls_replace_dict['bunch_name']=bunch_name 1070 helascalls_files=[] 1071 for i, k in enumerate(range(0, len(helas_calls), n_helas)): 1072 helascalls_replace_dict['bunch_number']=i+1 1073 helascalls_replace_dict['helas_calls']=\ 1074 '\n'.join(helas_calls[k:k + n_helas]) 1075 helascalls_replace_dict['required_so_broadcaster']=\ 1076 required_so_broadcaster 1077 helascalls_replace_dict['continue_label']=continue_label 1078 new_helascalls_file = open(os.path.join(self.template_dir,\ 1079 template_name)).read() 1080 new_helascalls_file = new_helascalls_file % helascalls_replace_dict 1081 helascalls_files.append(new_helascalls_file) 1082 # Setup the call to these HELASCALLS subroutines in loop_matrix.f 1083 helascalls_calls = [ "CALL %s%s_%d(P,NHEL,H,IC)"%\ 1084 (self.general_replace_dict['proc_prefix'] ,bunch_name,a+1) \ 1085 for a in range(len(helascalls_files))] 1086 replace_dict[entry_name]='\n'.join(helascalls_calls) 1087 if writer: 1088 for i, helascalls_file in enumerate(helascalls_files): 1089 filename = '%s_%d.f'%(bunch_name,i+1) 1090 writers.FortranWriter(filename).writelines(helascalls_file) 1091 else: 1092 masterfile='\n'.join([masterfile,]+helascalls_files) 1093 1094 return masterfile
1095
1096 - def write_loopmatrix(self, writer, matrix_element, fortran_model, \ 1097 noSplit=False):
1098 """Create the loop_matrix.f file.""" 1099 1100 if not matrix_element.get('processes') or \ 1101 not matrix_element.get('diagrams'): 1102 return 0 1103 1104 # Set lowercase/uppercase Fortran code 1105 1106 writers.FortranWriter.downcase = False 1107 1108 replace_dict = copy.copy(self.general_replace_dict) 1109 1110 # Extract overall denominator 1111 # Averaging initial state color, spin, and identical FS particles 1112 den_factor_line = self.get_den_factor_line(matrix_element) 1113 replace_dict['den_factor_line'] = den_factor_line 1114 # When the user asks for the polarized matrix element we must 1115 # multiply back by the helicity averaging factor 1116 replace_dict['hel_avg_factor'] = matrix_element.get_hel_avg_factor() 1117 1118 # These entries are specific for the output for loop-induced processes 1119 # Also sets here the details of the squaring of the loop ampltiudes 1120 # with the born or the loop ones. 1121 if not matrix_element.get('processes')[0].get('has_born'): 1122 replace_dict['compute_born']=\ 1123 """C There is of course no born for loop induced processes 1124 ANS(0)=0.0d0 1125 """ 1126 replace_dict['set_reference']='\n'.join([ 1127 'C For loop-induced, the reference for comparison is set later'+\ 1128 ' from the total contribution of the previous PS point considered.', 1129 'C But you can edit here the value to be used for the first PS point.', 1130 'if (NPSPOINTS.eq.0) then','ref=1.0d-50','else', 1131 'ref=nextRef/DBLE(NPSPOINTS)','endif']) 1132 replace_dict['loop_induced_setup'] = '\n'.join([ 1133 'HELPICKED_BU=HELPICKED','HELPICKED=H','MP_DONE=.FALSE.', 1134 'IF(SKIPLOOPEVAL) THEN','GOTO 1227','ENDIF']) 1135 replace_dict['loop_induced_finalize'] = \ 1136 ("""HELPICKED=HELPICKED_BU 1137 DO I=NCTAMPS+1,NLOOPAMPS 1138 IF((CTMODERUN.NE.-1).AND..NOT.CHECKPHASE.AND.(.NOT.S(I))) THEN 1139 WRITE(*,*) '##W03 WARNING Contribution ',I 1140 WRITE(*,*) ' is unstable for helicity ',H 1141 ENDIF 1142 C IF(.NOT.%(proc_prefix)sISZERO(ABS(AMPL(2,I))+ABS(AMPL(3,I)),REF,-1,H)) THEN 1143 C WRITE(*,*) '##W04 WARNING Contribution ',I,' for helicity ',H,' has a contribution to the poles.' 1144 C WRITE(*,*) 'Finite contribution = ',AMPL(1,I) 1145 C WRITE(*,*) 'single pole contribution = ',AMPL(2,I) 1146 C WRITE(*,*) 'double pole contribution = ',AMPL(3,I) 1147 C ENDIF 1148 ENDDO 1149 1227 CONTINUE""")%replace_dict 1150 replace_dict['loop_helas_calls']="" 1151 replace_dict['nctamps_or_nloopamps']='nloopamps' 1152 replace_dict['nbornamps_or_nloopamps']='nloopamps' 1153 replace_dict['squaring']=\ 1154 """ANS(1)=ANS(1)+DBLE(CFTOT*AMPL(1,I)*DCONJG(AMPL(1,J))) 1155 IF (J.EQ.1) THEN 1156 ANS(2)=ANS(2)+DBLE(CFTOT*AMPL(2,I))+DIMAG(CFTOT*AMPL(2,I)) 1157 ANS(3)=ANS(3)+DBLE(CFTOT*AMPL(3,I))+DIMAG(CFTOT*AMPL(3,I)) 1158 ENDIF""" 1159 else: 1160 replace_dict['compute_born']=\ 1161 """C Compute the born, for a specific helicity if asked so. 1162 call %(proc_prefix)ssmatrixhel(P_USER,USERHEL,ANS(0)) 1163 """%self.general_replace_dict 1164 replace_dict['set_reference']=\ 1165 """C We chose to use the born evaluation for the reference 1166 call %(proc_prefix)ssmatrix(p,ref)"""%self.general_replace_dict 1167 replace_dict['loop_induced_helas_calls'] = "" 1168 replace_dict['loop_induced_finalize'] = "" 1169 replace_dict['loop_induced_setup'] = "" 1170 replace_dict['nctamps_or_nloopamps']='nctamps' 1171 replace_dict['nbornamps_or_nloopamps']='nbornamps' 1172 replace_dict['squaring']='\n'.join(['DO K=1,3', 1173 'ANS(K)=ANS(K)+2.0d0*DBLE(CFTOT*AMPL(K,I)*DCONJG(AMP(J,H)))', 1174 'ENDDO']) 1175 1176 # Write a dummy nsquaredSO.inc which is used in the default 1177 # loop_matrix.f code (even though it does not support split orders evals) 1178 # just to comply with the syntax expected from the external code using MadLoop. 1179 writers.FortranWriter('nsquaredSO.inc').writelines( 1180 """INTEGER NSQUAREDSO 1181 PARAMETER (NSQUAREDSO=0)""") 1182 1183 # Actualize results from the loops computed. Only necessary for 1184 # processes with a born. 1185 actualize_ans=[] 1186 if matrix_element.get('processes')[0].get('has_born'): 1187 actualize_ans.append("DO I=NCTAMPS+1,NLOOPAMPS") 1188 actualize_ans.extend("ANS(%d)=ANS(%d)+AMPL(%d,I)"%(i,i,i) for i \ 1189 in range(1,4)) 1190 actualize_ans.append(\ 1191 "IF((CTMODERUN.NE.-1).AND..NOT.CHECKPHASE.AND.(.NOT.S(I))) THEN") 1192 actualize_ans.append(\ 1193 "WRITE(*,*) '##W03 WARNING Contribution ',I,' is unstable.'") 1194 actualize_ans.extend(["ENDIF","ENDDO"]) 1195 replace_dict['actualize_ans']='\n'.join(actualize_ans) 1196 else: 1197 replace_dict['actualize_ans']=\ 1198 ("""C We add five powers to the reference value to loosen a bit the vanishing pole check. 1199 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 1200 C WRITE(*,*) '##W05 WARNING Found a PS point with a contribution to the single pole.' 1201 C WRITE(*,*) 'Finite contribution = ',ANS(1) 1202 C WRITE(*,*) 'single pole contribution = ',ANS(2) 1203 C WRITE(*,*) 'double pole contribution = ',ANS(3) 1204 C ENDIF""")%replace_dict 1205 1206 # Write out the color matrix 1207 (CMNum,CMDenom) = self.get_color_matrix(matrix_element) 1208 CMWriter=open(pjoin('..','MadLoop5_resources', 1209 '%(proc_prefix)sColorNumFactors.dat'%self.general_replace_dict),'w') 1210 for ColorLine in CMNum: 1211 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 1212 CMWriter.close() 1213 CMWriter=open(pjoin('..','MadLoop5_resources', 1214 '%(proc_prefix)sColorDenomFactors.dat'%self.general_replace_dict),'w') 1215 for ColorLine in CMDenom: 1216 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 1217 CMWriter.close() 1218 1219 # Write out the helicity configurations 1220 HelConfigs=matrix_element.get_helicity_matrix() 1221 HelConfigWriter=open(pjoin('..','MadLoop5_resources', 1222 '%(proc_prefix)sHelConfigs.dat'%self.general_replace_dict),'w') 1223 for HelConfig in HelConfigs: 1224 HelConfigWriter.write(' '.join(['%d'%H for H in HelConfig])+'\n') 1225 HelConfigWriter.close() 1226 1227 # Extract helas calls 1228 loop_amp_helas_calls = fortran_model.get_loop_amp_helas_calls(\ 1229 matrix_element) 1230 # The proc_prefix must be replaced 1231 loop_amp_helas_calls = [lc % self.general_replace_dict 1232 for lc in loop_amp_helas_calls] 1233 1234 born_ct_helas_calls, UVCT_helas_calls = \ 1235 fortran_model.get_born_ct_helas_calls(matrix_element) 1236 # In the default output, we do not need to separate these two kind of 1237 # contributions 1238 born_ct_helas_calls = born_ct_helas_calls + UVCT_helas_calls 1239 file = open(os.path.join(self.template_dir,\ 1240 1241 'loop_matrix_standalone.inc')).read() 1242 1243 if matrix_element.get('processes')[0].get('has_born'): 1244 toBeRepaced='loop_helas_calls' 1245 else: 1246 toBeRepaced='loop_induced_helas_calls' 1247 1248 # Decide here wether we need to split the loop_matrix.f file or not. 1249 if (not noSplit and (len(matrix_element.get_all_amplitudes())>1000)): 1250 file=self.split_HELASCALLS(writer,replace_dict,\ 1251 'helas_calls_split.inc',file,born_ct_helas_calls,\ 1252 'born_ct_helas_calls','helas_calls_ampb') 1253 file=self.split_HELASCALLS(writer,replace_dict,\ 1254 'helas_calls_split.inc',file,loop_amp_helas_calls,\ 1255 toBeRepaced,'helas_calls_ampl') 1256 else: 1257 replace_dict['born_ct_helas_calls']='\n'.join(born_ct_helas_calls) 1258 replace_dict[toBeRepaced]='\n'.join(loop_amp_helas_calls) 1259 1260 file = file % replace_dict 1261 1262 loop_calls_finder = re.compile(r'^\s*CALL\S*LOOP\S*') 1263 n_loop_calls = len(filter(lambda call: 1264 not loop_calls_finder.match(call) is None, loop_amp_helas_calls)) 1265 if writer: 1266 # Write the file 1267 writer.writelines(file) 1268 return n_loop_calls 1269 else: 1270 # Return it to be written along with the others 1271 return n_loop_calls, file
1272
1273 - def write_bornmatrix(self, writer, matrix_element, fortran_model):
1274 """Create the born_matrix.f file for the born process as for a standard 1275 tree-level computation.""" 1276 1277 if not matrix_element.get('processes') or \ 1278 not matrix_element.get('diagrams'): 1279 return 0 1280 1281 if not isinstance(writer, writers.FortranWriter): 1282 raise writers.FortranWriter.FortranWriterError(\ 1283 "writer not FortranWriter") 1284 1285 # For now, we can use the exact same treatment as for tree-level 1286 # computations by redefining here a regular HelasMatrixElementf or the 1287 # born process. 1288 # It is important to make a deepcopy, as we don't want any possible 1289 # treatment on the objects of the bornME to have border effects on 1290 # the content of the LoopHelasMatrixElement object. 1291 bornME = helas_objects.HelasMatrixElement() 1292 for prop in bornME.keys(): 1293 bornME.set(prop,copy.deepcopy(matrix_element.get(prop))) 1294 bornME.set('base_amplitude',None,force=True) 1295 bornME.set('diagrams',copy.deepcopy(\ 1296 matrix_element.get_born_diagrams())) 1297 bornME.set('color_basis',copy.deepcopy(\ 1298 matrix_element.get('born_color_basis'))) 1299 bornME.set('color_matrix',copy.deepcopy(\ 1300 color_amp.ColorMatrix(bornME.get('color_basis')))) 1301 # This is to decide wether once to reuse old wavefunction to store new 1302 # ones (provided they are not used further in the code.) 1303 bornME.optimization = True 1304 1305 return super(LoopProcessExporterFortranSA,self).write_matrix_element_v4( 1306 writer, bornME, fortran_model, 1307 proc_prefix=self.general_replace_dict['proc_prefix'])
1308
1309 - def write_born_amps_and_wfs(self, writer, matrix_element, fortran_model,\ 1310 noSplit=False):
1311 """ Writes out the code for the subroutine MP_BORN_AMPS_AND_WFS which 1312 computes just the external wavefunction and born amplitudes in 1313 multiple precision. """ 1314 1315 if not matrix_element.get('processes') or \ 1316 not matrix_element.get('diagrams'): 1317 return 0 1318 1319 replace_dict = copy.copy(self.general_replace_dict) 1320 1321 # For the wavefunction copy, check what suffix is needed for the W array 1322 if matrix_element.get('processes')[0].get('has_born'): 1323 replace_dict['h_w_suffix']=',H' 1324 else: 1325 replace_dict['h_w_suffix']='' 1326 1327 # Extract helas calls 1328 born_amps_and_wfs_calls , uvct_amp_calls = \ 1329 fortran_model.get_born_ct_helas_calls(matrix_element, include_CT=True) 1330 # In the default output, these two kind of contributions do not need to 1331 # be differentiated 1332 born_amps_and_wfs_calls = born_amps_and_wfs_calls + uvct_amp_calls 1333 1334 # Turn these HELAS calls to the multiple-precision version of the HELAS 1335 # subroutines. 1336 self.turn_to_mp_calls(born_amps_and_wfs_calls) 1337 1338 file = open(os.path.join(self.template_dir,\ 1339 'mp_born_amps_and_wfs.inc')).read() 1340 # Decide here wether we need to split the loop_matrix.f file or not. 1341 if (not noSplit and (len(matrix_element.get_all_amplitudes())>2000)): 1342 file=self.split_HELASCALLS(writer,replace_dict,\ 1343 'mp_helas_calls_split.inc',file,\ 1344 born_amps_and_wfs_calls,'born_amps_and_wfs_calls',\ 1345 'mp_helas_calls') 1346 else: 1347 replace_dict['born_amps_and_wfs_calls']=\ 1348 '\n'.join(born_amps_and_wfs_calls) 1349 1350 file = file % replace_dict 1351 if writer: 1352 # Write the file 1353 writer.writelines(file) 1354 else: 1355 # Return it to be written along with the others 1356 return file 1357 1358 #=============================================================================== 1359 # LoopProcessOptimizedExporterFortranSA 1360 #=============================================================================== 1361
1362 -class LoopProcessOptimizedExporterFortranSA(LoopProcessExporterFortranSA):
1363 """Class to take care of exporting a set of loop matrix elements in the 1364 Fortran format which exploits the Pozzorini method of representing 1365 the loop numerators as polynomial to render its evaluations faster.""" 1366 1367 template_dir=os.path.join(_file_path,'iolibs/template_files/loop_optimized') 1368 # The option below controls wether one wants to group together in one single 1369 # CutTools/TIR call the loops with same denominator structure 1370 group_loops=True 1371 1372 # List of potential TIR library one wants to link to 1373 all_tir=['pjfry','iregi','golem'] 1374
1375 - def __init__(self, mgme_dir="", dir_path = "", opt=None):
1376 """Initiate the LoopProcessOptimizedExporterFortranSA with directory 1377 information on where to find all the loop-related source files, 1378 like CutTools and TIR""" 1379 1380 super(LoopProcessOptimizedExporterFortranSA,self).__init__(mgme_dir, 1381 dir_path, opt) 1382 1383 # TIR available ones 1384 self.tir_available_dict={'pjfry':True,'iregi':True,'golem':True} 1385 1386 for tir in self.all_tir: 1387 tir_dir="%s_dir"%tir 1388 if tir_dir in self.opt: 1389 setattr(self,tir_dir,self.opt[tir_dir]) 1390 else: 1391 setattr(self,tir_dir,'')
1392
1393 - def copy_v4template(self, modelname):
1394 """Additional actions needed for setup of Template 1395 """ 1396 super(LoopProcessOptimizedExporterFortranSA, self).copy_v4template(modelname) 1397 # We must link the TIR to the Library folder of the active Template 1398 link_tir_libs=[] 1399 tir_libs=[] 1400 tir_include=[] 1401 # special for PJFry++ 1402 link_pjfry_lib="" 1403 pjfry_lib="" 1404 pjdir="" 1405 for tir in self.all_tir: 1406 tir_dir="%s_dir"%tir 1407 libpath=getattr(self,tir_dir) 1408 libname="lib%s.a"%tir 1409 tir_name=tir 1410 libpath = self.link_TIR(os.path.join(self.dir_path, 'lib'), 1411 libpath,libname,tir_name=tir_name) 1412 setattr(self,tir_dir,libpath) 1413 if libpath != "": 1414 if tir in ['pjfry','golem']: 1415 # Apparently it is necessary to link against the original 1416 # location of the pjfry library, so it needs a special treatment. 1417 link_tir_libs.append('-L%s/ -l%s'%(libpath,tir)) 1418 tir_libs.append('%s/lib%s.$(libext)'%(libpath,tir)) 1419 if tir=='golem': 1420 trgt_path = pjoin(os.path.dirname(libpath),'include') 1421 golem_include = misc.find_includes_path(trgt_path,'.mod') 1422 if golem_include is None: 1423 logger.error( 1424 'Could not find the include directory for golem, looking in %s.\n' % str(trg_path)+ 1425 'Generation carries on but you will need to edit the include path by hand in the makefiles.') 1426 golem_include = '<Not_found_define_it_yourself>' 1427 tir_include.append('-I %s'%str(golem_include)) 1428 # To be able to easily compile a MadLoop library using 1429 # makefiles built outside of the MG5_aMC framework 1430 # (such as what is done with the Sherpa interface), we 1431 # place here an easy handle on the golem includes 1432 ln(golem_include, starting_dir=pjoin(self.dir_path,'lib'), 1433 name='golem95_include',abspath=True) 1434 1435 else : 1436 link_tir_libs.append('-l%s'%tir) 1437 tir_libs.append('$(LIBDIR)lib%s.$(libext)'%tir) 1438 1439 os.remove(os.path.join(self.dir_path,'SubProcesses','makefile')) 1440 cwd = os.getcwd() 1441 dirpath = os.path.join(self.dir_path, 'SubProcesses') 1442 try: 1443 os.chdir(dirpath) 1444 except os.error: 1445 logger.error('Could not cd to directory %s' % dirpath) 1446 return 0 1447 filename = 'makefile' 1448 calls = self.write_makefile_TIR(writers.MakefileWriter(filename), 1449 link_tir_libs,tir_libs, 1450 tir_include=tir_include) 1451 # Return to original PWD 1452 os.chdir(cwd)
1453 1454 1465 1466 1577
1578 - def write_matrix_element_v4(self, writer, matrix_element, fortran_model, 1579 proc_id = "", config_map = []):
1580 """ Writes loop_matrix.f, CT_interface.f,TIR_interface.f,GOLEM_inteface.f 1581 and loop_num.f only but with the optimized FortranModel""" 1582 1583 # Warn the user that the 'matrix' output where all relevant code is 1584 # put together in a single file is not supported in this loop output. 1585 if writer: 1586 raise MadGraph5Error, 'Matrix output mode no longer supported.' 1587 1588 if not isinstance(fortran_model,\ 1589 helas_call_writers.FortranUFOHelasCallWriter): 1590 raise MadGraph5Error, 'The optimized loop fortran output can only'+\ 1591 ' work with a UFO Fortran model' 1592 OptimizedFortranModel=\ 1593 helas_call_writers.FortranUFOHelasCallWriterOptimized(\ 1594 fortran_model.get('model'),False) 1595 1596 # Compute the analytical information of the loop wavefunctions in the 1597 # loop helas matrix elements using the cached aloha model to reuse 1598 # as much as possible the aloha computations already performed for 1599 # writing out the aloha fortran subroutines. 1600 matrix_element.compute_all_analytic_information( 1601 self.get_aloha_model(matrix_element.get('processes')[0].get('model'))) 1602 1603 # Initialize a general replacement dictionary with entries common to 1604 # many files generated here. 1605 self.general_replace_dict=LoopProcessExporterFortranSA.\ 1606 generate_general_replace_dict(self,matrix_element) 1607 1608 # Now fill in the replace dict entries specific to the TIR implementation 1609 self.set_TIR_replace_dict_entries() 1610 # and those specific to the optimized output 1611 self.set_optimized_output_specific_replace_dict_entries(matrix_element) 1612 1613 # Create the necessary files for the loop matrix element subroutine 1614 proc_prefix_writer = writers.FortranWriter('proc_prefix.txt','w') 1615 proc_prefix_writer.write(self.general_replace_dict['proc_prefix']) 1616 proc_prefix_writer.close() 1617 1618 filename = 'loop_matrix.f' 1619 calls = self.write_loopmatrix(writers.FortranWriter(filename), 1620 matrix_element, 1621 OptimizedFortranModel) 1622 1623 filename = 'check_sa.f' 1624 self.write_check_sa(writers.FortranWriter(filename),matrix_element) 1625 1626 filename = 'polynomial.f' 1627 calls = self.write_polynomial_subroutines( 1628 writers.FortranWriter(filename), 1629 matrix_element) 1630 1631 filename = 'improve_ps.f' 1632 calls = self.write_improve_ps(writers.FortranWriter(filename), 1633 matrix_element) 1634 1635 filename = 'CT_interface.f' 1636 self.write_CT_interface(writers.FortranWriter(filename),\ 1637 matrix_element) 1638 1639 filename = 'TIR_interface.f' 1640 self.write_TIR_interface(writers.FortranWriter(filename), 1641 matrix_element) 1642 1643 if 'golem' in self.tir_available_dict and self.tir_available_dict['golem']: 1644 filename = 'GOLEM_interface.f' 1645 self.write_GOLEM_interface(writers.FortranWriter(filename), 1646 matrix_element) 1647 1648 filename = 'loop_num.f' 1649 self.write_loop_num(writers.FortranWriter(filename),\ 1650 matrix_element,OptimizedFortranModel) 1651 1652 filename = 'mp_compute_loop_coefs.f' 1653 self.write_mp_compute_loop_coefs(writers.FortranWriter(filename),\ 1654 matrix_element,OptimizedFortranModel) 1655 1656 return calls
1657
1659 """ Specify the entries of the replacement dictionary which depend on 1660 the choice of Tensor Integral Reduction (TIR) libraries specified by the 1661 user and their availability on the system.""" 1662 1663 for tir in self.all_tir: 1664 if self.tir_available_dict[tir]: 1665 if tir=="pjfry": 1666 self.general_replace_dict['pjfry_calling']=\ 1667 (" CALL PMLOOP(NLOOPLINE,RANK,PL,PDEN,M2L,MU_R,"\ 1668 +"PJCOEFS(0:NLOOPCOEFS-1,1:3),STABLE)\n"\ 1669 +"C CONVERT TO MADLOOP CONVENTION\n"\ 1670 +" CALL %(proc_prefix)sCONVERT_PJFRY_COEFFS(RANK,PJCOEFS,TIRCOEFS)"\ 1671 )%self.general_replace_dict 1672 elif tir=="iregi": 1673 self.general_replace_dict['iregi_calling']=\ 1674 (" CALL IMLOOP(CTMODE,IREGIMODE,NLOOPLINE,LOOPMAXCOEFS,"\ 1675 +"RANK,PDEN,M2L,MU_R,PJCOEFS,STABLE)\n"\ 1676 +"C CONVERT TO MADLOOP CONVENTION\n"\ 1677 +" CALL %(proc_prefix)sCONVERT_IREGI_COEFFS(RANK,PJCOEFS,TIRCOEFS)"\ 1678 )%self.general_replace_dict 1679 self.general_replace_dict['iregi_free_ps']=\ 1680 "IF(IREGIRECY.AND.MLReductionLib(I_LIB).EQ.3)CALL IREGI_FREE_PS" 1681 self.general_replace_dict['initiregi']=\ 1682 " CALL INITIREGI(IREGIRECY,LOOPLIB,1d-6)" 1683 elif tir=="golem": 1684 self.general_replace_dict['golem_calling']=\ 1685 ("IF(MLReductionLib(I_LIB).EQ.4)THEN\n"\ 1686 +"C Using Golem95\n"\ 1687 +" CALL %(proc_prefix)sGOLEMLOOP(NLOOPLINE,PL,M2L,RANK,RES,STABLE)\n"\ 1688 +" RETURN\n"\ 1689 +"ENDIF")%self.general_replace_dict 1690 else: 1691 raise MadGraph5Error,"%s was not a well-defined TIR."%tir_name 1692 else: 1693 if tir=="pjfry": 1694 self.general_replace_dict['pjfry_calling']=\ 1695 " WRITE(*,*)'PJFRY is not installed correctly !'\n"\ 1696 +" STOP" 1697 elif tir=="iregi": 1698 self.general_replace_dict['iregi_calling']=\ 1699 " WRITE(*,*)'IREGI is not installed correctly !'\n"\ 1700 +" STOP" 1701 self.general_replace_dict['initiregi']='' 1702 self.general_replace_dict['iregi_free_ps']='' 1703 elif tir=="golem": 1704 self.general_replace_dict['golem_calling']='' 1705 # the first entry is the CutTools, we make sure it is available 1706 looplibs_av=['.TRUE.'] 1707 # one should be careful about the order in the following 1708 if "pjfry" in self.all_tir: 1709 if self.tir_available_dict["pjfry"]: 1710 looplibs_av.extend(['.TRUE.']) 1711 else: 1712 looplibs_av.extend(['.FALSE.']) 1713 else: 1714 looplibs_av.extend(['.FALSE.']) 1715 if "iregi" in self.all_tir: 1716 if self.tir_available_dict["iregi"]: 1717 looplibs_av.extend(['.TRUE.']) 1718 else: 1719 looplibs_av.extend(['.FALSE.']) 1720 else: 1721 looplibs_av.extend(['.FALSE.']) 1722 1723 if "golem" in self.all_tir: 1724 if self.tir_available_dict["golem"]: 1725 looplibs_av.extend(['.TRUE.']) 1726 else: 1727 looplibs_av.extend(['.FALSE.']) 1728 else: 1729 looplibs_av.extend(['.FALSE.']) 1730 self.general_replace_dict['data_looplibs_av']="DATA LOOPLIBS_AVAILABLE /"\ 1731 +','.join(looplibs_av)+"/"
1732 1733
1734 - def set_optimized_output_specific_replace_dict_entries(self, matrix_element):
1735 """ Specify the entries of the replacement dictionary which are specific 1736 to the optimized output and only relevant to it (the more general entries 1737 are set in the the mother class LoopProcessExporterFortranSA.""" 1738 1739 max_loop_rank=matrix_element.get_max_loop_rank() 1740 self.general_replace_dict['maxrank']=max_loop_rank 1741 self.general_replace_dict['loop_max_coefs']=\ 1742 q_polynomial.get_number_of_coefs_for_rank(max_loop_rank) 1743 max_loop_vertex_rank=matrix_element.get_max_loop_vertex_rank() 1744 self.general_replace_dict['vertex_max_coefs']=\ 1745 q_polynomial.get_number_of_coefs_for_rank(max_loop_vertex_rank) 1746 self.general_replace_dict['nloopwavefuncs']=\ 1747 matrix_element.get_number_of_loop_wavefunctions() 1748 max_spin=matrix_element.get_max_loop_particle_spin() 1749 if max_spin>3: 1750 raise MadGraph5Error, "ML5 can only handle loop particles with"+\ 1751 " spin 1 at most" 1752 self.general_replace_dict['max_lwf_size']=4 1753 self.general_replace_dict['nloops']=len(\ 1754 [1 for ldiag in matrix_element.get_loop_diagrams() for \ 1755 lamp in ldiag.get_loop_amplitudes()]) 1756 if self.group_loops and \ 1757 matrix_element.get('processes')[0].get('has_born'): 1758 self.general_replace_dict['nloop_groups']=\ 1759 len(matrix_element.get('loop_groups')) 1760 else: 1761 self.general_replace_dict['nloop_groups']=\ 1762 self.general_replace_dict['nloops'] 1763 1764 # The born amp declaration suited for also outputing the loop-induced 1765 # processes as well. (not used for now, but later) 1766 if matrix_element.get('processes')[0].get('has_born'): 1767 self.general_replace_dict['dp_born_amps_decl'] = \ 1768 self.general_replace_dict['complex_dp_format']+" AMP(NBORNAMPS)"+\ 1769 "\n common/%sAMPS/AMP"%self.general_replace_dict['proc_prefix'] 1770 self.general_replace_dict['mp_born_amps_decl'] = \ 1771 self.general_replace_dict['complex_mp_format']+" AMP(NBORNAMPS)"+\ 1772 "\n common/%sMP_AMPS/AMP"%self.general_replace_dict['proc_prefix']
1773
1774 - def write_loop_num(self, writer, matrix_element,fortran_model):
1775 """ Create the file containing the core subroutine called by CutTools 1776 which contains the Helas calls building the loop""" 1777 1778 replace_dict=copy.copy(self.general_replace_dict) 1779 1780 file = open(os.path.join(self.template_dir,'loop_num.inc')).read() 1781 file = file % replace_dict 1782 writer.writelines(file)
1783
1784 - def write_CT_interface(self, writer, matrix_element):
1785 """ We can re-use the mother one for the loop optimized output.""" 1786 LoopProcessExporterFortranSA.write_CT_interface(\ 1787 self, writer, matrix_element,optimized_output=True)
1788
1789 - def write_TIR_interface(self, writer, matrix_element):
1790 """ Create the file TIR_interface.f which does NOT contain the subroutine 1791 defining the loop HELAS-like calls along with the general interfacing 1792 subroutine. """ 1793 1794 # First write TIR_interface which interfaces MG5 with TIR. 1795 replace_dict=copy.copy(self.general_replace_dict) 1796 1797 # We finalize TIR result differently wether we used the built-in 1798 # squaring against the born. 1799 if matrix_element.get('processes')[0].get('has_born'): 1800 replace_dict['finalize_TIR']='\n'.join([\ 1801 'RES(%d)=NORMALIZATION*2.0d0*DBLE(RES(%d))'%(i,i) for i in range(1,4)]) 1802 else: 1803 replace_dict['finalize_TIR']='\n'.join([\ 1804 'RES(%d)=NORMALIZATION*RES(%d)'%(i,i) for i in range(1,4)]) 1805 1806 file = open(os.path.join(self.template_dir,'TIR_interface.inc')).read() 1807 1808 file = file % replace_dict 1809 1810 1811 FPR = q_polynomial.FortranPolynomialRoutines(replace_dict['maxrank'],\ 1812 coef_format=replace_dict['complex_dp_format'],\ 1813 sub_prefix=replace_dict['proc_prefix']) 1814 if self.tir_available_dict['pjfry']: 1815 file += '\n\n'+FPR.write_pjfry_mapping() 1816 if self.tir_available_dict['iregi']: 1817 file += '\n\n'+FPR.write_iregi_mapping() 1818 1819 if writer: 1820 writer.writelines(file) 1821 else: 1822 return file
1823
1824 - def write_GOLEM_interface(self, writer, matrix_element):
1825 """ Create the file GOLEM_interface.f which does NOT contain the subroutine 1826 defining the loop HELAS-like calls along with the general interfacing 1827 subroutine. """ 1828 1829 # First write GOLEM_interface which interfaces MG5 with TIR. 1830 replace_dict=copy.copy(self.general_replace_dict) 1831 1832 # We finalize TIR result differently wether we used the built-in 1833 # squaring against the born. 1834 if matrix_element.get('processes')[0].get('has_born'): 1835 factor='2.0D0' 1836 else: 1837 factor='1.0D0' 1838 replace_dict['finalize_GOLEM']='\n'.join([ 1839 'RES(1)=NORMALIZATION*%s*DBLE(RES_GOLEM%%c+'%factor+\ 1840 '2.0*LOG(MU_R)*RES_GOLEM%b+2.0*LOG(MU_R)**2*RES_GOLEM%a)',\ 1841 'RES(2)=NORMALIZATION*%s*DBLE(RES_GOLEM%%b+2.0*LOG(MU_R)*RES_GOLEM%%a)'%factor,\ 1842 'RES(3)=NORMALIZATION*%s*DBLE(RES_GOLEM%%a)'%factor]) 1843 1844 file = open(os.path.join(self.template_dir,'GOLEM_interface.inc')).read() 1845 1846 file = file % replace_dict 1847 1848 FPR = q_polynomial.FortranPolynomialRoutines(replace_dict['maxrank'],\ 1849 coef_format=replace_dict['complex_dp_format'],\ 1850 sub_prefix=replace_dict['proc_prefix']) 1851 1852 file += '\n\n'+FPR.write_golem95_mapping() 1853 1854 if writer: 1855 writer.writelines(file) 1856 else: 1857 return file
1858
1859 - def write_polynomial_subroutines(self,writer,matrix_element):
1860 """ Subroutine to create all the subroutines relevant for handling 1861 the polynomials representing the loop numerator """ 1862 1863 # First create 'coef_specs.inc' 1864 IncWriter=writers.FortranWriter('coef_specs.inc','w') 1865 IncWriter.writelines("""INTEGER MAXLWFSIZE 1866 PARAMETER (MAXLWFSIZE=%(max_lwf_size)d) 1867 INTEGER LOOP_MAXCOEFS 1868 PARAMETER (LOOP_MAXCOEFS=%(loop_max_coefs)d) 1869 INTEGER VERTEXMAXCOEFS 1870 PARAMETER (VERTEXMAXCOEFS=%(vertex_max_coefs)d)"""\ 1871 %self.general_replace_dict) 1872 IncWriter.close() 1873 1874 # List of all subroutines to place there 1875 subroutines=[] 1876 1877 # Start from the routine in the template 1878 replace_dict = copy.copy(self.general_replace_dict) 1879 dp_routine = open(os.path.join(self.template_dir,'polynomial.inc')).read() 1880 mp_routine = open(os.path.join(self.template_dir,'polynomial.inc')).read() 1881 # The double precision version of the basic polynomial routines, such as 1882 # create_loop_coefs 1883 replace_dict['complex_format'] = replace_dict['complex_dp_format'] 1884 replace_dict['real_format'] = replace_dict['real_dp_format'] 1885 replace_dict['born_amps_decl'] = replace_dict['dp_born_amps_decl'] 1886 replace_dict['mp_prefix'] = '' 1887 replace_dict['kind'] = 8 1888 replace_dict['zero_def'] = '0.0d0' 1889 replace_dict['one_def'] = '1.0d0' 1890 dp_routine = dp_routine % replace_dict 1891 # The quadruple precision version of the basic polynomial routines 1892 replace_dict['complex_format'] = replace_dict['complex_mp_format'] 1893 replace_dict['real_format'] = replace_dict['real_mp_format'] 1894 replace_dict['born_amps_decl'] = replace_dict['mp_born_amps_decl'] 1895 replace_dict['mp_prefix'] = 'MP_' 1896 replace_dict['kind'] = 16 1897 replace_dict['zero_def'] = '0.0e0_16' 1898 replace_dict['one_def'] = '1.0e0_16' 1899 mp_routine = mp_routine % replace_dict 1900 subroutines.append(dp_routine) 1901 subroutines.append(mp_routine) 1902 1903 # Initialize the polynomial routine writer 1904 poly_writer=q_polynomial.FortranPolynomialRoutines( 1905 matrix_element.get_max_loop_rank(), 1906 sub_prefix=replace_dict['proc_prefix']) 1907 mp_poly_writer=q_polynomial.FortranPolynomialRoutines( 1908 matrix_element.get_max_loop_rank(),coef_format='complex*32', 1909 sub_prefix='MP_'+replace_dict['proc_prefix']) 1910 # The eval subroutine 1911 subroutines.append(poly_writer.write_polynomial_evaluator()) 1912 subroutines.append(mp_poly_writer.write_polynomial_evaluator()) 1913 # The add coefs subroutine 1914 subroutines.append(poly_writer.write_add_coefs()) 1915 subroutines.append(mp_poly_writer.write_add_coefs()) 1916 # The merging one for creating the loop coefficients 1917 subroutines.append(poly_writer.write_wl_merger()) 1918 subroutines.append(mp_poly_writer.write_wl_merger()) 1919 # Now the udpate subroutines 1920 for wl_update in matrix_element.get_used_wl_updates(): 1921 subroutines.append(poly_writer.write_wl_updater(\ 1922 wl_update[0],wl_update[1])) 1923 subroutines.append(mp_poly_writer.write_wl_updater(\ 1924 wl_update[0],wl_update[1])) 1925 writer.writelines('\n\n'.join(subroutines))
1926
1927 - def write_mp_compute_loop_coefs(self, writer, matrix_element, fortran_model, \ 1928 noSplit=False):
1929 """Create the write_mp_compute_loop_coefs.f file.""" 1930 1931 if not matrix_element.get('processes') or \ 1932 not matrix_element.get('diagrams'): 1933 return 0 1934 1935 # Set lowercase/uppercase Fortran code 1936 1937 writers.FortranWriter.downcase = False 1938 1939 replace_dict = copy.copy(self.general_replace_dict) 1940 1941 # These entries are specific for the output for loop-induced processes 1942 # Also sets here the details of the squaring of the loop ampltiudes 1943 # with the born or the loop ones. 1944 if not matrix_element.get('processes')[0].get('has_born'): 1945 replace_dict['nctamps_or_nloopamps']='nctamps' 1946 replace_dict['nbornamps_or_nloopamps']='nctamps' 1947 replace_dict['mp_squaring']=\ 1948 """ITEMP = %(proc_prefix)sML5SQSOINDEX(%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(I),%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(J)) 1949 TEMP2 = DUMMY*REAL(CFTOT*AMPL(1,I)*CONJG(AMPL(1,J)),KIND=16) 1950 IF (.NOT.FILTER_SO.OR.SQSO_TARGET.EQ.ITEMP) THEN 1951 ANS(1,ITEMP)=ANS(1,ITEMP)+TEMP2 1952 ANS(1,0)=ANS(1,0)+TEMP2 1953 ENDIF"""%self.general_replace_dict 1954 else: 1955 replace_dict['nctamps_or_nloopamps']='nctamps' 1956 replace_dict['nbornamps_or_nloopamps']='nbornamps' 1957 replace_dict['mp_squaring']=\ 1958 """ITEMP = %(proc_prefix)sML5SQSOINDEX(%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(I),%(proc_prefix)sML5SOINDEX_FOR_BORN_AMP(J)) 1959 IF (.NOT.FILTER_SO.OR.SQSO_TARGET.EQ.ITEMP) THEN 1960 DO K=1,3 1961 TEMP2 = DUMMY*2.0e0_16*REAL(CFTOT*AMPL(K,I)*CONJG(AMP(J)),KIND=16) 1962 ANS(K,ITEMP)=ANS(K,ITEMP)+TEMP2 1963 ANS(K,0)=ANS(K,0)+TEMP2 1964 ENDDO 1965 ENDIF"""%self.general_replace_dict 1966 # Extract helas calls 1967 squared_orders = matrix_element.get_squared_order_contribs() 1968 split_orders = matrix_element.get('processes')[0].get('split_orders') 1969 1970 born_ct_helas_calls , uvct_helas_calls = \ 1971 fortran_model.get_born_ct_helas_calls(matrix_element, 1972 squared_orders=squared_orders, split_orders=split_orders) 1973 self.turn_to_mp_calls(born_ct_helas_calls) 1974 self.turn_to_mp_calls(uvct_helas_calls) 1975 coef_construction, coef_merging = fortran_model.get_coef_construction_calls(\ 1976 matrix_element,group_loops=self.group_loops, 1977 squared_orders=squared_orders,split_orders=split_orders) 1978 # The proc_prefix must be replaced 1979 coef_construction = [c % self.general_replace_dict for c 1980 in coef_construction] 1981 self.turn_to_mp_calls(coef_construction) 1982 self.turn_to_mp_calls(coef_merging) 1983 1984 file = open(os.path.join(self.template_dir,\ 1985 'mp_compute_loop_coefs.inc')).read() 1986 1987 # Decide here wether we need to split the loop_matrix.f file or not. 1988 # 200 is reasonable but feel free to change it. 1989 if (not noSplit and (len(matrix_element.get_all_amplitudes())>200)): 1990 file=self.split_HELASCALLS(writer,replace_dict,\ 1991 'mp_helas_calls_split.inc',file,born_ct_helas_calls,\ 1992 'mp_born_ct_helas_calls','mp_helas_calls_ampb', 1993 required_so_broadcaster = 'MP_CT_REQ_SO_DONE', 1994 continue_label = 2000) 1995 file=self.split_HELASCALLS(writer,replace_dict,\ 1996 'mp_helas_calls_split.inc',file,uvct_helas_calls,\ 1997 'mp_uvct_helas_calls','mp_helas_calls_uvct', 1998 required_so_broadcaster = 'MP_UVCT_REQ_SO_DONE', 1999 continue_label = 3000) 2000 file=self.split_HELASCALLS(writer,replace_dict,\ 2001 'mp_helas_calls_split.inc',file,coef_construction,\ 2002 'mp_coef_construction','mp_coef_construction', 2003 required_so_broadcaster = 'MP_LOOP_REQ_SO_DONE', 2004 continue_label = 4000) 2005 else: 2006 replace_dict['mp_born_ct_helas_calls']='\n'.join(born_ct_helas_calls) 2007 replace_dict['mp_uvct_helas_calls']='\n'.join(uvct_helas_calls) 2008 replace_dict['mp_coef_construction']='\n'.join(coef_construction) 2009 2010 replace_dict['mp_coef_merging']='\n'.join(coef_merging) 2011 2012 file = file % replace_dict 2013 2014 # Write the file 2015 writer.writelines(file)
2016
2017 - def fix_coef_specs(self, overall_max_lwf_size, overall_max_loop_vert_rank):
2018 """ If processes with different maximum loop wavefunction size or 2019 different maximum loop vertex rank have to be output together, then 2020 the file 'coef.inc' in the HELAS Source folder must contain the overall 2021 maximum of these quantities. It is not safe though, and the user has 2022 been appropriatly warned at the output stage """ 2023 2024 # Remove the existing link 2025 coef_specs_path=os.path.join(self.dir_path,'Source','DHELAS',\ 2026 'coef_specs.inc') 2027 os.remove(coef_specs_path) 2028 2029 # Replace it by the appropriate value 2030 IncWriter=writers.FortranWriter(coef_specs_path,'w') 2031 IncWriter.writelines("""INTEGER MAXLWFSIZE 2032 PARAMETER (MAXLWFSIZE=%(max_lwf_size)d) 2033 INTEGER VERTEXMAXCOEFS 2034 PARAMETER (VERTEXMAXCOEFS=%(vertex_max_coefs)d)"""\ 2035 %{'max_lwf_size':overall_max_lwf_size, 2036 'vertex_max_coefs':overall_max_loop_vert_rank}) 2037 IncWriter.close()
2038
2039 - def setup_check_sa_replacement_dictionary(self,\ 2040 split_orders,squared_orders,amps_orders):
2041 """ Sets up the replacement dictionary for the writeout of the steering 2042 file check_sa.f""" 2043 if len(squared_orders)<1: 2044 self.general_replace_dict['print_so_loop_results']=\ 2045 "write(*,*) 'No split orders defined.'" 2046 elif len(squared_orders)==1: 2047 self.general_replace_dict['set_coupling_target']='' 2048 self.general_replace_dict['print_so_loop_results']=\ 2049 "write(*,*) 'All loop contributions are of split orders (%s)'"%( 2050 ' '.join(['%s=%d'%(split_orders[i],squared_orders[0][i]) \ 2051 for i in range(len(split_orders))])) 2052 else: 2053 self.general_replace_dict['set_coupling_target']='\n'.join([ 2054 '# Here we leave the default target squared split order to -1, meaning that we'+ 2055 ' aim at computing all individual contributions. You can choose otherwise.', 2056 'call %(proc_prefix)sSET_COUPLINGORDERS_TARGET(-1)'%self.general_replace_dict]) 2057 self.general_replace_dict['print_so_loop_results'] = '\n'.join([ 2058 '\n'.join(["write(*,*) '%dL) Loop ME for orders (%s) :'"%((j+1),(' '.join( 2059 ['%s=%d'%(split_orders[i],so[i]) for i in range(len(split_orders))]))), 2060 "IF (PREC_FOUND(%d).NE.-1.0d0) THEN"%(j+1), 2061 "write(*,*) ' > accuracy = ',PREC_FOUND(%d)"%(j+1), 2062 "ELSE", 2063 "write(*,*) ' > accuracy = NA'", 2064 "ENDIF", 2065 "write(*,*) ' > finite = ',MATELEM(1,%d)"%(j+1), 2066 "write(*,*) ' > 1eps = ',MATELEM(2,%d)"%(j+1), 2067 "write(*,*) ' > 2eps = ',MATELEM(3,%d)"%(j+1) 2068 ]) for j, so in enumerate(squared_orders)]) 2069 self.general_replace_dict['write_so_loop_results'] = '\n'.join( 2070 ["write (69,*) 'Split_Orders_Names %s'"%(' '.join(split_orders))]+ 2071 ['\n'.join([ 2072 "write (69,*) 'Loop_SO_Results %s'"%(' '.join( 2073 ['%d'%so_value for so_value in so])), 2074 "write (69,*) 'SO_Loop ACC ',PREC_FOUND(%d)"%(j+1), 2075 "write (69,*) 'SO_Loop FIN ',MATELEM(1,%d)"%(j+1), 2076 "write (69,*) 'SO_Loop 1EPS ',MATELEM(2,%d)"%(j+1), 2077 "write (69,*) 'SO_Loop 2EPS ',MATELEM(3,%d)"%(j+1), 2078 ]) for j, so in enumerate(squared_orders)]) 2079 2080 # We must reconstruct here the born squared orders. 2081 squared_born_so_orders = [] 2082 for i, amp_order in enumerate(amps_orders['born_amp_orders']): 2083 for j in range(0,i+1): 2084 key = tuple([ord1 + ord2 for ord1,ord2 in \ 2085 zip(amp_order[0],amps_orders['born_amp_orders'][j][0])]) 2086 if not key in squared_born_so_orders: 2087 squared_born_so_orders.append(key) 2088 if len(squared_born_so_orders)<1: 2089 self.general_replace_dict['print_so_born_results'] = '' 2090 elif len(squared_born_so_orders)==1: 2091 self.general_replace_dict['print_so_born_results'] = \ 2092 "write(*,*) 'All Born contributions are of split orders (%s)'"%( 2093 ' '.join(['%s=%d'%(split_orders[i],squared_born_so_orders[0][i]) 2094 for i in range(len(split_orders))])) 2095 else: 2096 self.general_replace_dict['print_so_born_results'] = '\n'.join([ 2097 "write(*,*) '%dB) Born ME for orders (%s) = ',MATELEM(0,%d)"%(j+1,' '.join( 2098 ['%s=%d'%(split_orders[i],so[i]) for i in range(len(split_orders))]),j+1) 2099 for j, so in enumerate(squared_born_so_orders)]) 2100 self.general_replace_dict['write_so_born_results'] = '\n'.join( 2101 ['\n'.join([ 2102 "write (69,*) 'Born_SO_Results %s'"%(' '.join( 2103 ['%d'%so_value for so_value in so])), 2104 "write (69,*) 'SO_Born BORN ',MATELEM(0,%d)"%(j+1), 2105 ]) for j, so in enumerate(squared_born_so_orders)]) 2106 2107 # Add a bottom bar to both print_so_[loop|born]_results 2108 self.general_replace_dict['print_so_born_results'] += \ 2109 '\nwrite (*,*) "---------------------------------"' 2110 self.general_replace_dict['print_so_loop_results'] += \ 2111 '\nwrite (*,*) "---------------------------------"'
2112
2113 - def write_loopmatrix(self, writer, matrix_element, fortran_model, \ 2114 noSplit=False, write_auxiliary_files=True,):
2115 """Create the loop_matrix.f file.""" 2116 2117 if not matrix_element.get('processes') or \ 2118 not matrix_element.get('diagrams'): 2119 return 0 2120 2121 # Set lowercase/uppercase Fortran code 2122 2123 writers.FortranWriter.downcase = False 2124 2125 # We start off with the treatment of the split_orders since some 2126 # of the information extracted there will come into the 2127 # general_replace_dict. Split orders are abbreviated SO in all the 2128 # keys of the replacement dictionaries. 2129 2130 # Take care of the split_orders 2131 squared_orders, amps_orders = matrix_element.get_split_orders_mapping() 2132 # Creating here a temporary list containing only the information of 2133 # what are the different squared split orders contributing 2134 # (i.e. not using max_contrib_amp_number and max_contrib_ref_amp_number) 2135 sqso_contribs = [sqso[0] for sqso in squared_orders] 2136 split_orders = matrix_element.get('processes')[0].get('split_orders') 2137 # The entries set in the function below are only for check_sa written 2138 # out in write_matrix_element_v4 (it is however placed here because the 2139 # split order information is only available here). 2140 self.setup_check_sa_replacement_dictionary(\ 2141 split_orders,sqso_contribs,amps_orders) 2142 2143 # Now recast the split order basis for the loop, born and counterterm 2144 # amplitude into one single splitorderbasis. 2145 overall_so_basis = list(set( 2146 [born_so[0] for born_so in amps_orders['born_amp_orders']]+ 2147 [born_so[0] for born_so in amps_orders['loop_amp_orders']])) 2148 # We must re-sort it to make sure it follows an increasing WEIGHT order 2149 order_hierarchy = matrix_element.get('processes')[0]\ 2150 .get('model').get('order_hierarchy') 2151 if set(order_hierarchy.keys()).union(set(split_orders))==\ 2152 set(order_hierarchy.keys()): 2153 overall_so_basis.sort(key= lambda so: 2154 sum([order_hierarchy[split_orders[i]]*order_power for \ 2155 i, order_power in enumerate(so)])) 2156 2157 # Those are additional entries used throughout the different files of 2158 # MadLoop5 2159 self.general_replace_dict['split_order_str_list'] = str(split_orders) 2160 self.general_replace_dict['nSO'] = len(split_orders) 2161 self.general_replace_dict['nSquaredSO'] = len(sqso_contribs) 2162 self.general_replace_dict['nAmpSO'] = len(overall_so_basis) 2163 2164 writers.FortranWriter('nsquaredSO.inc').writelines( 2165 """INTEGER NSQUAREDSO 2166 PARAMETER (NSQUAREDSO=%d)"""%self.general_replace_dict['nSquaredSO']) 2167 2168 replace_dict = copy.copy(self.general_replace_dict) 2169 # Build the general array mapping the split orders indices to their 2170 # definition 2171 replace_dict['ampsplitorders'] = '\n'.join(self.get_split_orders_lines(\ 2172 overall_so_basis,'AMPSPLITORDERS')) 2173 replace_dict['SquaredSO'] = '\n'.join(self.get_split_orders_lines(\ 2174 sqso_contribs,'SQPLITORDERS')) 2175 2176 # Specify what are the squared split orders selected by the proc def. 2177 replace_dict['chosen_so_configs'] = self.set_chosen_SO_index( 2178 matrix_element.get('processes')[0],sqso_contribs) 2179 2180 # Now we build the different arrays storing the split_orders ID of each 2181 # amp. 2182 ampSO_list=[-1]*sum(len(el[1]) for el in amps_orders['loop_amp_orders']) 2183 for SO in amps_orders['loop_amp_orders']: 2184 for amp_number in SO[1]: 2185 ampSO_list[amp_number-1]=overall_so_basis.index(SO[0])+1 2186 2187 replace_dict['loopAmpSO'] = '\n'.join(self.format_integer_list( 2188 ampSO_list,'LOOPAMPORDERS')) 2189 ampSO_list=[-1]*sum(len(el[1]) for el in amps_orders['born_amp_orders']) 2190 for SO in amps_orders['born_amp_orders']: 2191 for amp_number in SO[1]: 2192 ampSO_list[amp_number-1]=overall_so_basis.index(SO[0])+1 2193 replace_dict['BornAmpSO'] = '\n'.join(self.format_integer_list( 2194 ampSO_list,'BORNAMPORDERS')) 2195 2196 # Helicity offset convention 2197 # For a given helicity, the attached integer 'i' means 2198 # 'i' in ]-inf;-HELOFFSET[ -> Helicity is equal, up to a sign, 2199 # to helicity number abs(i+HELOFFSET) 2200 # 'i' == -HELOFFSET -> Helicity is analytically zero 2201 # 'i' in ]-HELOFFSET,inf[ -> Helicity is contributing with weight 'i'. 2202 # If it is zero, it is skipped. 2203 # Typically, the hel_offset is 10000 2204 replace_dict['hel_offset'] = 10000 2205 2206 # Extract overall denominator 2207 # Averaging initial state color, spin, and identical FS particles 2208 den_factor_line = self.get_den_factor_line(matrix_element) 2209 replace_dict['den_factor_line'] = den_factor_line 2210 2211 # When the user asks for the polarized matrix element we must 2212 # multiply back by the helicity averaging factor 2213 replace_dict['hel_avg_factor'] = matrix_element.get_hel_avg_factor() 2214 2215 # These entries are specific for the output for loop-induced processes 2216 # Also sets here the details of the squaring of the loop ampltiudes 2217 # with the born or the loop ones. 2218 if not matrix_element.get('processes')[0].get('has_born'): 2219 replace_dict['compute_born']=\ 2220 """ 2221 C The born is of course 0 for loop-induced processes. 2222 DO I=0,NSQUAREDSO 2223 ANS(0,I)=0.0d0 2224 ENDDO 2225 """ 2226 replace_dict['set_reference']='\n'.join([ 2227 'C Chose the arbitrary scale of reference to use for comparisons'+\ 2228 ' for this loop-induced process.','ref=1.0d-50', 2229 'DO I=0,NSQUAREDSO','ANS(I,0)=0.0d0','ENDDO']) 2230 replace_dict['nctamps_or_nloopamps']='nctamps' 2231 replace_dict['nbornamps_or_nloopamps']='nctamps' 2232 replace_dict['squaring']='\n'.join([\ 2233 'ITEMP = %(proc_prefix)sML5SQSOINDEX(%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(I),%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(J))'%self.general_replace_dict, 2234 'TEMP2 = DUMMY*DBLE(CFTOT*AMPL(1,I)*DCONJG(AMPL(1,J)))', 2235 'IF (.NOT.FILTER_SO.OR.SQSO_TARGET.EQ.ITEMP) THEN', 2236 'ANS(1,ITEMP)=ANS(1,ITEMP)+TEMP2', 2237 'ANS(1,0)=ANS(1,0)+TEMP2', 2238 'ENDIF']) 2239 else: 2240 replace_dict['compute_born']=\ 2241 """ 2242 C First compute the borns, it will store them in ANS(0,I) 2243 C It is left untouched for the rest of MadLoop evaluation. 2244 C Notice that the squared split order index I does NOT 2245 C correspond to the same ordering of J for the loop ME 2246 C results stored in ANS(K,J), with K in [1-3].The ordering 2247 C of each can be obtained with ML5SOINDEX_FOR_SQUARED_ORDERS 2248 C and SQSOINDEX_FROM_ORDERS for the loop ME and born ME 2249 C respectively. For this to work, we assume that there is 2250 C always more squared split orders in the loop ME than in the 2251 C born ME, which is practically always true. In any case, only 2252 C the split_order summed value I=0 is used in ML5 code. 2253 DO I=0,NSQSO_BORN 2254 BORNBUFF(I)=0.0d0 2255 ENDDO 2256 CALL %(proc_prefix)sSMATRIXHEL_SPLITORDERS(P_USER,USERHEL,BORNBUFF(0)) 2257 DO I=0,NSQSO_BORN 2258 ANS(0,I)=BORNBUFF(I) 2259 ENDDO 2260 """%self.general_replace_dict 2261 replace_dict['set_reference']='\n'.join( 2262 ['C We set here the reference to the born summed over all split orders', 2263 'REF=0.0d0','DO I=1,NSQSO_BORN','REF=REF+ANS(0,I)','ENDDO']) 2264 replace_dict['nctamps_or_nloopamps']='nctamps' 2265 replace_dict['nbornamps_or_nloopamps']='nbornamps' 2266 replace_dict['squaring']='\n'.join([ 2267 'ITEMP = %(proc_prefix)sML5SQSOINDEX(%(proc_prefix)sML5SOINDEX_FOR_LOOP_AMP(I),%(proc_prefix)sML5SOINDEX_FOR_BORN_AMP(J))'%self.general_replace_dict, 2268 'IF (.NOT.FILTER_SO.OR.SQSO_TARGET.EQ.ITEMP) THEN', 2269 'DO K=1,3', 2270 'TEMP2 = 2.0d0*DUMMY*DBLE(CFTOT*AMPL(K,I)*DCONJG(AMP(J)))', 2271 'ANS(K,ITEMP)=ANS(K,ITEMP)+TEMP2', 2272 'ANS(K,0)=ANS(K,0)+TEMP2', 2273 'ENDDO', 2274 'ENDIF']) 2275 2276 # Actualize results from the loops computed. Only necessary for 2277 # processes with a born. 2278 actualize_ans=[] 2279 if matrix_element.get('processes')[0].get('has_born'): 2280 actualize_ans.append( 2281 """DO I=1,NLOOPGROUPS 2282 LTEMP=.TRUE. 2283 DO K=1,NSQUAREDSO 2284 IF (.NOT.FILTER_SO.OR.SQSO_TARGET.EQ.K) THEN 2285 IF (.NOT.S(K,I)) LTEMP=.FALSE. 2286 DO J=1,3 2287 ANS(J,K)=ANS(J,K)+LOOPRES(J,K,I) 2288 ANS(J,0)=ANS(J,0)+LOOPRES(J,K,I) 2289 ENDDO 2290 ENDIF 2291 ENDDO""") 2292 actualize_ans.append(\ 2293 "IF((CTMODERUN.NE.-1).AND..NOT.CHECKPHASE.AND.(.NOT.LTEMP)) THEN") 2294 actualize_ans.append(\ 2295 "WRITE(*,*) '##W03 WARNING Contribution ',I,' is unstable.'") 2296 actualize_ans.extend(["ENDIF","ENDDO"]) 2297 replace_dict['actualize_ans']='\n'.join(actualize_ans) 2298 2299 if write_auxiliary_files: 2300 # Write out the color matrix 2301 (CMNum,CMDenom) = self.get_color_matrix(matrix_element) 2302 CMWriter=open(pjoin('..','MadLoop5_resources', 2303 '%(proc_prefix)sColorNumFactors.dat'%self.general_replace_dict),'w') 2304 for ColorLine in CMNum: 2305 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 2306 CMWriter.close() 2307 CMWriter=open(pjoin('..','MadLoop5_resources', 2308 '%(proc_prefix)sColorDenomFactors.dat'%self.general_replace_dict),'w') 2309 for ColorLine in CMDenom: 2310 CMWriter.write(' '.join(['%d'%C for C in ColorLine])+'\n') 2311 CMWriter.close() 2312 2313 # Write out the helicity configurations 2314 HelConfigs=matrix_element.get_helicity_matrix() 2315 HelConfigWriter=open(pjoin('..','MadLoop5_resources', 2316 '%(proc_prefix)sHelConfigs.dat'%self.general_replace_dict),'w') 2317 for HelConfig in HelConfigs: 2318 HelConfigWriter.write(' '.join(['%d'%H for H in HelConfig])+'\n') 2319 HelConfigWriter.close() 2320 2321 # Extract helas calls 2322 born_ct_helas_calls, uvct_helas_calls = \ 2323 fortran_model.get_born_ct_helas_calls(matrix_element, 2324 squared_orders=squared_orders,split_orders=split_orders) 2325 coef_construction, coef_merging = fortran_model.get_coef_construction_calls(\ 2326 matrix_element,group_loops=self.group_loops, 2327 squared_orders=squared_orders,split_orders=split_orders) 2328 2329 loop_CT_calls = fortran_model.get_loop_CT_calls(matrix_element,\ 2330 group_loops=self.group_loops, 2331 squared_orders=squared_orders, split_orders=split_orders) 2332 # The proc_prefix must be replaced 2333 coef_construction = [c % self.general_replace_dict for c 2334 in coef_construction] 2335 loop_CT_calls = [lc % self.general_replace_dict for lc in loop_CT_calls] 2336 2337 file = open(os.path.join(self.template_dir,\ 2338 'loop_matrix_standalone.inc')).read() 2339 2340 # Decide here wether we need to split the loop_matrix.f file or not. 2341 # 200 is reasonable but feel free to change it. 2342 if (not noSplit and (len(matrix_element.get_all_amplitudes())>200)): 2343 file=self.split_HELASCALLS(writer,replace_dict,\ 2344 'helas_calls_split.inc',file,born_ct_helas_calls,\ 2345 'born_ct_helas_calls','helas_calls_ampb', 2346 required_so_broadcaster = 'CT_REQ_SO_DONE', 2347 continue_label = 2000) 2348 file=self.split_HELASCALLS(writer,replace_dict,\ 2349 'helas_calls_split.inc',file,uvct_helas_calls,\ 2350 'uvct_helas_calls','helas_calls_uvct', 2351 required_so_broadcaster = 'UVCT_REQ_SO_DONE', 2352 continue_label = 3000) 2353 file=self.split_HELASCALLS(writer,replace_dict,\ 2354 'helas_calls_split.inc',file,coef_construction,\ 2355 'coef_construction','coef_construction', 2356 required_so_broadcaster = 'LOOP_REQ_SO_DONE', 2357 continue_label = 4000) 2358 file=self.split_HELASCALLS(writer,replace_dict,\ 2359 'helas_calls_split.inc',file,loop_CT_calls,\ 2360 'loop_CT_calls','loop_CT_calls', 2361 required_so_broadcaster = 'CTCALL_REQ_SO_DONE', 2362 continue_label = 5000) 2363 else: 2364 replace_dict['born_ct_helas_calls']='\n'.join(born_ct_helas_calls) 2365 replace_dict['uvct_helas_calls']='\n'.join(uvct_helas_calls) 2366 replace_dict['coef_construction']='\n'.join(coef_construction) 2367 replace_dict['loop_CT_calls']='\n'.join(loop_CT_calls) 2368 2369 replace_dict['coef_merging']='\n'.join(coef_merging) 2370 replace_dict['iregi_free_ps']=self.general_replace_dict['iregi_free_ps'] 2371 replace_dict['data_looplibs_av']=self.general_replace_dict['data_looplibs_av'] 2372 file = file % replace_dict 2373 number_of_calls = len(filter(lambda call: call.find('CALL LOOP') != 0, \ 2374 loop_CT_calls)) 2375 if writer: 2376 # Write the file 2377 writer.writelines(file) 2378 return number_of_calls 2379 else: 2380 # Return it to be written along with the others 2381 return number_of_calls, file
2382