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