Package madgraph :: Package fks :: Module fks_base
[hide private]
[frames] | no frames]

Source Code for Module madgraph.fks.fks_base

  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   
 16  """Definitions of the objects needed for the implementation of MadFKS""" 
 17   
 18  import madgraph.core.base_objects as MG 
 19  import madgraph.core.helas_objects as helas_objects 
 20  import madgraph.core.diagram_generation as diagram_generation 
 21  import madgraph.core.color_amp as color_amp 
 22  import madgraph.core.color_algebra as color_algebra 
 23  import madgraph.loop.loop_diagram_generation as loop_diagram_generation 
 24  import madgraph.fks.fks_common as fks_common 
 25  import copy 
 26  import logging 
 27  import array 
 28  import madgraph.various.misc as misc 
 29  from madgraph import InvalidCmd 
 30   
 31  logger = logging.getLogger('madgraph.fks_base') 
 32   
 33   
34 -class NoBornException(Exception): pass
35 36 #=============================================================================== 37 # FKS Process 38 #===============================================================================
39 -class FKSMultiProcess(diagram_generation.MultiProcess): #test written
40 """A multi process class that contains informations on the born processes 41 and the reals. 42 """ 43
44 - def default_setup(self):
45 """Default values for all properties""" 46 super(FKSMultiProcess, self).default_setup() 47 self['real_amplitudes'] = diagram_generation.AmplitudeList() 48 self['pdgs'] = [] 49 self['born_processes'] = FKSProcessList() 50 if not 'OLP' in self.keys(): 51 self['OLP'] = 'MadLoop' 52 self['ncores_for_proc_gen'] = 0
53
54 - def get_sorted_keys(self):
55 """Return particle property names as a nicely sorted list.""" 56 keys = super(FKSMultiProcess, self).get_sorted_keys() 57 keys += ['born_processes', 'real_amplitudes', 'real_pdgs', 'has_isr', 58 'has_fsr', 'OLP', 'ncores_for_proc_gen'] 59 return keys
60
61 - def filter(self, name, value):
62 """Filter for valid leg property values.""" 63 64 if name == 'born_processes': 65 if not isinstance(value, FKSProcessList): 66 raise self.PhysicsObjectError, \ 67 "%s is not a valid list for born_processes " % str(value) 68 69 if name == 'real_amplitudes': 70 if not isinstance(value, diagram_generation.AmplitudeList): 71 raise self.PhysicsObjectError, \ 72 "%s is not a valid list for real_amplitudes " % str(value) 73 74 if name == 'real_pdgs': 75 if not isinstance(value, list): 76 raise self.PhysicsObjectError, \ 77 "%s is not a valid list for real_amplitudes " % str(value) 78 79 if name == 'OLP': 80 if not isinstance(value,str): 81 raise self.PhysicsObjectError, \ 82 "%s is not a valid string for OLP " % str(value) 83 84 if name == 'ncores_for_proc_gen': 85 if not isinstance(value,int): 86 raise self.PhysicsObjectError, \ 87 "%s is not a valid value for ncores_for_proc_gen " % str(value) 88 89 return super(FKSMultiProcess,self).filter(name, value)
90
91 - def __init__(self, procdef=None, options={}):
92 """Initializes the original multiprocess, then generates the amps for the 93 borns, then generate the born processes and the reals. 94 Real amplitudes are stored in real_amplitudes according on the pdgs of their 95 legs (stored in pdgs, so that they need to be generated only once and then reicycled 96 """ 97 98 #swhich the other loggers off 99 loggers_off = [logging.getLogger('madgraph.diagram_generation'), 100 logging.getLogger('madgraph.loop_diagram_generation')] 101 old_levels = [logg.level for logg in loggers_off] 102 for logg in loggers_off: 103 logg.setLevel(logging.WARNING) 104 105 # OLP option 106 olp='MadLoop' 107 if 'OLP' in options.keys(): 108 olp = options['OLP'] 109 del options['OLP'] 110 111 ncores_for_proc_gen = 0 112 # ncores_for_proc_gen has the following meaning 113 # 0 : do things the old way 114 # > 0 use ncores_for_proc_gen 115 # -1 : use all cores 116 if 'ncores_for_proc_gen' in options.keys(): 117 ncores_for_proc_gen = options['ncores_for_proc_gen'] 118 del options['ncores_for_proc_gen'] 119 120 try: 121 # Now generating the borns for the first time. 122 super(FKSMultiProcess, self).__init__(procdef, **options) 123 124 except diagram_generation.NoDiagramException as error: 125 # If no born, then this process most likely does not have any. 126 raise NoBornException, "Born diagrams could not be generated for the "+\ 127 self['process_definitions'][0].nice_string().replace('Process',\ 128 'process')+". Notice that aMC@NLO does not handle loop-induced"+\ 129 " processes yet, but you can still use MadLoop if you want to "+\ 130 "only generate them."+\ 131 " For this, use the 'virt=' mode, without multiparticle labels." 132 133 self['OLP'] = olp 134 self['ncores_for_proc_gen'] = ncores_for_proc_gen 135 136 #check process definition(s): 137 # a process such as g g > g g will lead to real emissions 138 # (e.g: u g > u g g ) which will miss some corresponding born, 139 # leading to non finite results 140 perturbation = [] 141 for procdef in self['process_definitions']: 142 soft_particles = [] 143 # do not warn for decay processes 144 if [ i['state'] for i in procdef['legs']].count(False) == 1: 145 continue 146 for pert in procdef['perturbation_couplings']: 147 if pert not in perturbation: 148 perturbation.append(pert) 149 soft_particles.extend(\ 150 fks_common.find_pert_particles_interactions(\ 151 procdef['model'], pert)['soft_particles']) 152 soft_particles_string = ', '.join( \ 153 [procdef['model'].get('particle_dict')[id][\ 154 {True:'name', False:'antiname'}[id >0] ] \ 155 for id in sorted(soft_particles, reverse=True)]) 156 for leg in procdef['legs']: 157 if any([id in soft_particles for id in leg['ids']]) \ 158 and sorted(leg['ids']) != soft_particles: 159 logger.warning(('%s can have real emission processes ' + \ 160 'which are not finite.\nTo avoid this, please use multiparticles ' + \ 161 'when generating the process and be sure to include all the following ' + \ 162 'particles in the multiparticle definition:\n %s' ) \ 163 % (procdef.nice_string(), soft_particles_string) ) 164 break 165 for procdef in self['process_definitions']: 166 procdef.set('orders', diagram_generation.MultiProcess.find_optimal_process_orders(procdef)) 167 168 amps = self.get('amplitudes') 169 for i, amp in enumerate(amps): 170 logger.info("Generating FKS-subtracted matrix elements for born process%s (%d / %d)" \ 171 % (amp['process'].nice_string(print_weighted=False).replace(\ 172 'Process', ''), 173 i + 1, len(amps))) 174 175 born = FKSProcess(amp, ncores_for_proc_gen = self['ncores_for_proc_gen']) 176 self['born_processes'].append(born) 177 born.generate_reals(self['pdgs'], self['real_amplitudes']) 178 179 if not self['ncores_for_proc_gen']: 180 # old generation mode 181 182 born_pdg_list = [[l['id'] for l in born.born_proc['legs']] \ 183 for born in self['born_processes'] ] 184 185 for born in self['born_processes']: 186 for real in born.real_amps: 187 real.find_fks_j_from_i(born_pdg_list) 188 if amps: 189 if self['process_definitions'][0].get('NLO_mode') == 'all': 190 self.generate_virtuals() 191 192 elif not self['process_definitions'][0].get('NLO_mode') in ['all', 'real','LOonly']: 193 raise fks_common.FKSProcessError(\ 194 "Not a valid NLO_mode for a FKSMultiProcess: %s" % \ 195 self['process_definitions'][0].get('NLO_mode')) 196 197 # now get the total number of diagrams 198 n_diag_born = sum([len(amp.get('diagrams')) 199 for amp in self.get_born_amplitudes()]) 200 n_diag_real = sum([len(amp.get('diagrams')) 201 for amp in self.get_real_amplitudes()]) 202 n_diag_virt = sum([len(amp.get('loop_diagrams')) 203 for amp in self.get_virt_amplitudes()]) 204 205 if n_diag_virt == 0 and n_diag_real ==0 and \ 206 not self['process_definitions'][0].get('NLO_mode') == 'LOonly': 207 raise fks_common.FKSProcessError( 208 'This process does not have any correction up to NLO in %s'\ 209 %','.join(perturbation)) 210 211 logger.info(('Generated %d subprocesses with %d real emission diagrams, ' + \ 212 '%d born diagrams and %d virtual diagrams') % \ 213 (len(self['born_processes']), n_diag_real, n_diag_born, n_diag_virt)) 214 215 for i, logg in enumerate(loggers_off): 216 logg.setLevel(old_levels[i]) 217 218 self['has_isr'] = any([proc.isr for proc in self['born_processes']]) 219 self['has_fsr'] = any([proc.fsr for proc in self['born_processes']])
220
221 - def add(self, other):
222 """combines self and other, extending the lists of born/real amplitudes""" 223 self['process_definitions'].extend(other['process_definitions']) 224 self['amplitudes'].extend(other['amplitudes']) 225 self['born_processes'].extend(other['born_processes']) 226 self['real_amplitudes'].extend(other['real_amplitudes']) 227 self['pdgs'].extend(other['pdgs']) 228 self['has_isr'] = self['has_isr'] or other['has_isr'] 229 self['has_fsr'] = self['has_fsr'] or other['has_fsr'] 230 self['OLP'] = other['OLP'] 231 self['ncores_for_proc_gen'] = other['ncores_for_proc_gen']
232
233 - def get_born_amplitudes(self):
234 """return an amplitudelist with the born amplitudes""" 235 return diagram_generation.AmplitudeList([born.born_amp \ 236 for born in self['born_processes']])
237
238 - def get_virt_amplitudes(self):
239 """return an amplitudelist with the virt amplitudes""" 240 return diagram_generation.AmplitudeList([born.virt_amp \ 241 for born in self['born_processes'] if born.virt_amp])
242
243 - def get_real_amplitudes(self):
244 """return an amplitudelist with the real amplitudes""" 245 return self.get('real_amplitudes')
246 247
248 - def generate_virtuals(self):
249 """For each process among the born_processes, creates the corresponding 250 virtual amplitude""" 251 252 # If not using MadLoop, then the LH order file generation and processing 253 # will be entirely done during the output, so nothing must be done at 254 # this stage yet. 255 if self['OLP']!='MadLoop': 256 logger.info("The loop matrix elements will be generated by "+\ 257 '%s at the output stage only.'%self['OLP']) 258 return 259 260 # determine the orders to be used to generate the loop 261 loop_orders = {} 262 for born in self['born_processes']: 263 for coup, val in fks_common.find_orders(born.born_amp).items(): 264 try: 265 loop_orders[coup] = max([loop_orders[coup], val]) 266 except KeyError: 267 loop_orders[coup] = val 268 269 for i, born in enumerate(self['born_processes']): 270 logger.info('Generating virtual matrix elements using MadLoop:') 271 myproc = copy.copy(born.born_proc) 272 # take the orders that are actually used bu the matrix element 273 myproc['orders'] = loop_orders 274 myproc['legs'] = fks_common.to_legs(copy.copy(myproc['legs'])) 275 logger.info('Generating virtual matrix element with MadLoop for process%s (%d / %d)' \ 276 % (myproc.nice_string(print_weighted = False).replace(\ 277 'Process', ''), 278 i + 1, len(self['born_processes']))) 279 myamp = loop_diagram_generation.LoopAmplitude(myproc) 280 if myamp.get('diagrams'): 281 born.virt_amp = myamp
282 283
284 -class FKSRealProcess(object):
285 """Contains information about a real process: 286 -- fks_infos (list containing the possible fks configs for a given process 287 -- amplitude 288 -- is_to_integrate 289 -- leg permutation<<REMOVED!. 290 """ 291
292 - def __init__(self, born_proc, leglist, ij, ijglu, 293 perturbed_orders = ['QCD']): #test written
294 """Initializes the real process based on born_proc and leglist. 295 Stores the fks informations into the list of dictionaries fks_infos 296 """ 297 self.fks_infos = [] 298 for leg in leglist: 299 if leg.get('fks') == 'i': 300 i_fks = leg.get('number') 301 # i is a gluon or a photon 302 need_color_links = leg.get('massless') \ 303 and leg.get('spin') == 3 \ 304 and leg.get('self_antipart') 305 if leg.get('fks') == 'j': 306 j_fks = leg.get('number') 307 self.fks_infos.append({'i' : i_fks, 308 'j' : j_fks, 309 'ij' : ij, 310 'ij_glu': ijglu, 311 'need_color_links' : need_color_links}) 312 313 self.process = copy.copy(born_proc) 314 orders = copy.copy(born_proc.get('orders')) 315 # compute the weighted order if not present 316 if not 'WEIGHTED' in orders: 317 orders['WEIGHTED'] = sum([v * born_proc.get('model').get('order_hierarchy')[o] \ 318 for o, v in orders.items()]) 319 320 for order in perturbed_orders: 321 try: 322 orders[order] +=1 323 except KeyError: 324 pass 325 orders['WEIGHTED'] += born_proc.get('model').get('order_hierarchy')[order] 326 327 self.process.set('orders', orders) 328 legs = [(leg.get('id'), leg) for leg in leglist] 329 self.pdgs = array.array('i',[s[0] for s in legs]) 330 if 'QCD' in perturbed_orders: 331 self.colors = [leg['color'] for leg in leglist] 332 # in QCD, charges are irrelevant ! 333 self.charges = [0. for leg in leglist] 334 self.perturbation = 'QCD' 335 else: 336 self.colors = [leg['color'] for leg in leglist] 337 self.charges = [leg['charge'] for leg in leglist] 338 self.perturbation = 'QED' 339 self.process.set('legs', MG.LegList(leglist)) 340 self.process.set('legs_with_decays', MG.LegList()) 341 self.amplitude = diagram_generation.Amplitude() 342 self.is_to_integrate = True 343 self.is_nbody_only = False 344 self.fks_j_from_i = {} 345 346
347 - def generate_real_amplitude(self):
348 """generates the real emission amplitude starting from self.process""" 349 self.amplitude = diagram_generation.Amplitude(self.process) 350 return self.amplitude
351 352
353 - def find_fks_j_from_i(self, born_pdg_list): #test written
354 """Returns a dictionary with the entries i : [j_from_i], if the born pdgs are in 355 born_pdg_list""" 356 fks_j_from_i = {} 357 dict = {} 358 for i in self.process.get('legs'): 359 fks_j_from_i[i.get('number')] = [] 360 if i.get('state'): 361 for j in [l for l in self.process.get('legs') if \ 362 l.get('number') != i.get('number')]: 363 ijlist = fks_common.combine_ij(i, j, self.process.get('model'), dict,\ 364 pert=self.perturbation) 365 for ij in ijlist: 366 born_leglist = fks_common.to_fks_legs( 367 copy.deepcopy(self.process.get('legs')), 368 self.process.get('model')) 369 born_leglist.remove(i) 370 born_leglist.remove(j) 371 born_leglist.insert(ij.get('number') - 1, ij) 372 born_leglist.sort(pert = self.perturbation) 373 if [l['id'] for l in born_leglist] in born_pdg_list: 374 fks_j_from_i[i.get('number')].append(\ 375 j.get('number')) 376 377 self.fks_j_from_i = fks_j_from_i 378 return fks_j_from_i 379 380
381 - def get_leg_i(self): #test written
382 """Returns leg corresponding to i fks. 383 An error is raised if the fks_infos list has more than one entry""" 384 if len(self.fks_infos) > 1: 385 raise fks_common.FKSProcessError(\ 386 'get_leg_i should only be called before combining processes') 387 return self.process.get('legs')[self.fks_infos[0]['i'] - 1] 388
389 - def get_leg_j(self): #test written
390 """Returns leg corresponding to j fks. 391 An error is raised if the fks_infos list has more than one entry""" 392 if len(self.fks_infos) > 1: 393 raise fks_common.FKSProcessError(\ 394 'get_leg_j should only be called before combining processes') 395 return self.process.get('legs')[self.fks_infos[0]['j'] - 1] 396 397
398 -class FKSProcessList(MG.PhysicsObjectList):
399 """Class to handle lists of FKSProcesses.""" 400
401 - def is_valid_element(self, obj):
402 """Test if object obj is a valid FKSProcess for the list.""" 403 return isinstance(obj, FKSProcess)
404 405
406 -class FKSProcess(object):
407 """The class for a FKS process. Starts from the born process and finds 408 all the possible splittings.""" 409
410 - def __init__(self, start_proc = None, remove_reals = True, ncores_for_proc_gen=0):
411 """initialization: starts either from an amplitude or a process, 412 then init the needed variables. 413 remove_borns tells if the borns not needed for integration will be removed 414 from the born list (mainly used for testing) 415 ncores_for_proc_gen has the following meaning 416 0 : do things the old way 417 > 0 use ncores_for_proc_gen 418 -1 : use all cores 419 """ 420 421 self.splittings = {} 422 self.reals = [] 423 self.fks_dirs = [] 424 self.leglist = [] 425 self.myorders = {} 426 self.pdg_codes = [] 427 self.colors = [] # color 428 self.charges = [] # charge 429 self.nlegs = 0 430 self.fks_ipos = [] 431 self.fks_j_from_i = {} 432 self.real_amps = [] 433 self.remove_reals = remove_reals 434 self.nincoming = 0 435 self.virt_amp = None 436 self.perturbation = 'QCD' 437 self.ncores_for_proc_gen = ncores_for_proc_gen 438 439 if not remove_reals in [True, False]: 440 raise fks_common.FKSProcessError(\ 441 'Not valid type for remove_reals in FKSProcess') 442 443 if start_proc: 444 if isinstance(start_proc, MG.Process): 445 pertur = start_proc['perturbation_couplings'] 446 if pertur: 447 self.perturbation = sorted(pertur)[0] 448 self.born_proc = fks_common.sort_proc(start_proc,pert = self.perturbation) 449 # filter in Amplitude will legs sorted in bornproc 450 bornproc = copy.copy(self.born_proc) # deepcopy might let T -> array 451 assert bornproc==self.born_proc 452 self.born_amp = diagram_generation.Amplitude(bornproc) 453 elif isinstance(start_proc, diagram_generation.Amplitude): 454 pertur = start_proc.get('process')['perturbation_couplings'] 455 if pertur: 456 self.perturbation = sorted(pertur)[0] 457 self.born_proc = fks_common.sort_proc(start_proc.get('process'),\ 458 pert = self.perturbation) 459 # filter in Amplitude will legs sorted in bornproc 460 bornproc = copy.copy(self.born_proc) 461 assert bornproc == self.born_proc 462 self.born_amp = diagram_generation.Amplitude(bornproc) 463 else: 464 raise fks_common.FKSProcessError(\ 465 'Not valid start_proc in FKSProcess') 466 self.born_proc.set('legs_with_decays', MG.LegList()) 467 468 self.leglist = fks_common.to_fks_legs( 469 self.born_proc['legs'], self.born_proc['model']) 470 self.nlegs = len(self.leglist) 471 self.pdg_codes = [leg.get('id') for leg in self.leglist] 472 if self.perturbation == 'QCD': 473 self.colors = [leg.get('color') for leg in self.leglist] 474 # in QCD, charge is irrelevant but impact the combine process ! 475 self.charges = [0. for leg in self.leglist] 476 color = 'color' 477 zero = 1 478 elif self.perturbation == 'QED': 479 self.colors = [leg.get('color') for leg in self.leglist] 480 self.charges = [leg.get('charge') for leg in self.leglist] 481 color = 'charge' 482 zero = 0. 483 # special treatment of photon is needed ! 484 self.isr = set([leg.get(color) for leg in self.leglist if not leg.get('state')]) != set([zero]) 485 self.fsr = set([leg.get(color) for leg in self.leglist if leg.get('state')]) != set([zero]) 486 for leg in self.leglist: 487 if not leg['state']: 488 self.nincoming += 1 489 self.orders = self.born_amp['process']['orders'] 490 # this is for cases in which the user specifies e.g. QED=0 491 if sum(self.orders.values()) == 0: 492 self.orders = fks_common.find_orders(self.born_amp) 493 494 self.ndirs = 0 495 # generate reals, when the mode is not LOonly 496 # when is LOonly it is supposed to be a 'fake' NLO process 497 # e.g. to be used in merged sampels at high multiplicities 498 if self.born_proc['NLO_mode'] != 'LOonly': 499 for order in self.born_proc.get('perturbation_couplings'): 500 self.find_reals(order)
501 502
503 - def generate_real_amplitudes(self, pdg_list, real_amp_list):
504 """generates the real amplitudes for all the real emission processes, using pdgs and real_amps 505 to avoid multiple generation of the same amplitude""" 506 507 for amp in self.real_amps: 508 try: 509 amp.amplitude = real_amp_list[pdg_list.index(amp.pdgs)] 510 except ValueError: 511 pdg_list.append(amp.pdgs) 512 real_amp_list.append(amp.generate_real_amplitude())
513 514
515 - def combine_real_amplitudes(self):
516 """combines real emission processes if the pdgs are the same, combining the lists 517 of fks_infos""" 518 pdgs = [] 519 real_amps = [] 520 old_real_amps = copy.copy(self.real_amps) 521 for amp in old_real_amps: 522 try: 523 real_amps[pdgs.index(amp.pdgs)].fks_infos.extend(amp.fks_infos) 524 except ValueError: 525 real_amps.append(amp) 526 pdgs.append(amp.pdgs) 527 528 self.real_amps = real_amps
529 530
531 - def generate_reals(self, pdg_list, real_amp_list, combine=True): #test written
532 """For all the possible splittings, creates an FKSRealProcess. 533 It removes double counted configorations from the ones to integrates and 534 sets the one which includes the bosn (is_nbody_only). 535 if combine is true, FKS_real_processes having the same pdgs (i.e. real amplitude) 536 are combined together 537 """ 538 539 born_proc = copy.copy(self.born_proc) 540 born_proc['orders'] = self.orders 541 for i, list in enumerate(self.reals): 542 # i is a gluon or photon,i.e. g(a) > ffx or gg 543 if self.leglist[i]['massless'] and self.leglist[i]['spin'] == 3: 544 ijglu = i + 1 545 else: 546 ijglu = 0 547 for l in list: 548 ij = self.leglist[i].get('number') 549 self.real_amps.append(FKSRealProcess( \ 550 born_proc, l, ij, ijglu,\ 551 perturbed_orders = [self.perturbation])) 552 self.find_reals_to_integrate() 553 if combine: 554 self.combine_real_amplitudes() 555 if not self.ncores_for_proc_gen: 556 self.generate_real_amplitudes(pdg_list, real_amp_list) 557 self.link_born_reals()
558 559 569 570
571 - def find_reals(self, pert_order):
572 """finds the FKS real configurations for a given process""" 573 if range(len(self.leglist)) != [l['number']-1 for l in self.leglist]: 574 raise fks_common.FKSProcessError('Disordered numbers of leglist') 575 576 if [ i['state'] for i in self.leglist].count(False) == 1: 577 decay_process=True 578 else: 579 decay_process=False 580 581 for i in self.leglist: 582 i_i = i['number'] - 1 583 self.reals.append([]) 584 if decay_process and not i['state']: 585 self.splittings[i_i]=[] 586 else: 587 self.splittings[i_i] = fks_common.find_splittings(i, self.born_proc['model'], {}, pert_order) 588 for split in self.splittings[i_i]: 589 self.reals[i_i].append( 590 fks_common.insert_legs(self.leglist, i, split,pert=pert_order))
591 592 593
594 - def find_reals_to_integrate(self): #test written
595 """Finds double countings in the real emission configurations, sets the 596 is_to_integrate variable and if "self.remove_reals" is True removes the 597 not needed ones from the born list. 598 """ 599 #find the initial number of real configurations 600 ninit = len(self.real_amps) 601 remove = self.remove_reals 602 603 for m in range(ninit): 604 for n in range(m + 1, ninit): 605 real_m = self.real_amps[m] 606 real_n = self.real_amps[n] 607 if len(real_m.fks_infos) > 1 or len(real_m.fks_infos) > 1: 608 raise fks_common.FKSProcessError(\ 609 'find_reals_to_integrate should only be called before combining processes') 610 611 i_m = real_m.fks_infos[0]['i'] 612 j_m = real_m.fks_infos[0]['j'] 613 i_n = real_n.fks_infos[0]['i'] 614 j_n = real_n.fks_infos[0]['j'] 615 if j_m > self.nincoming and j_n > self.nincoming: 616 if (real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] \ 617 and \ 618 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']) \ 619 or \ 620 (real_m.get_leg_i()['id'] == real_n.get_leg_j()['id'] \ 621 and \ 622 real_m.get_leg_j()['id'] == real_n.get_leg_i()['id']): 623 if i_m > i_n: 624 print real_m.get_leg_i()['id'], real_m.get_leg_j()['id'] 625 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 626 self.real_amps[m].is_to_integrate = False 627 else: 628 self.real_amps[n].is_to_integrate = False 629 elif i_m == i_n and j_m > j_n: 630 print real_m.get_leg_i()['id'], real_m.get_leg_j()['id'] 631 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 632 self.real_amps[m].is_to_integrate = False 633 else: 634 self.real_amps[n].is_to_integrate = False 635 # in case of g(a) > ffx splitting, keep the lowest ij 636 elif i_m == i_n and j_m == j_n and \ 637 not real_m.get_leg_j()['self_antipart'] and \ 638 not real_m.get_leg_i()['self_antipart']: 639 if real_m.fks_infos[0]['ij'] > real_n.fks_infos[0]['ij']: 640 real_m.is_to_integrate = False 641 else: 642 real_n.is_to_integrate = False 643 else: 644 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 645 self.real_amps[n].is_to_integrate = False 646 else: 647 self.real_amps[m].is_to_integrate = False 648 # self.real_amps[m].is_to_integrate = False 649 elif j_m <= self.nincoming and j_n == j_m: 650 if real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] and \ 651 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']: 652 if i_m > i_n: 653 self.real_amps[n].is_to_integrate = False 654 else: 655 self.real_amps[m].is_to_integrate = False 656 if remove: 657 newreal_amps = [] 658 for real in self.real_amps: 659 if real.is_to_integrate: 660 newreal_amps.append(real) 661 self.real_amps = newreal_amps 662