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