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