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 40
41 -class FKSMultiProcess(diagram_generation.MultiProcess): #test written
42 """A multi process class that contains informations on the born processes 43 and the reals. 44 """ 45
46 - def default_setup(self):
47 """Default values for all properties""" 48 super(FKSMultiProcess, self).default_setup() 49 self['real_amplitudes'] = diagram_generation.AmplitudeList() 50 self['pdgs'] = [] 51 self['born_processes'] = FKSProcessList() 52 if not 'OLP' in self.keys(): 53 self['OLP'] = 'MadLoop' 54 self['ncores_for_proc_gen'] = 0
55
56 - def get_sorted_keys(self):
57 """Return particle property names as a nicely sorted list.""" 58 keys = super(FKSMultiProcess, self).get_sorted_keys() 59 keys += ['born_processes', 'real_amplitudes', 'real_pdgs', 'has_isr', 60 'has_fsr', 'spltting_types', 'OLP', 'ncores_for_proc_gen'] 61 return keys
62
63 - def filter(self, name, value):
64 """Filter for valid leg property values.""" 65 66 if name == 'born_processes': 67 if not isinstance(value, FKSProcessList): 68 raise self.PhysicsObjectError, \ 69 "%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, \ 74 "%s is not a valid list for real_amplitudes " % str(value) 75 76 if name == 'real_pdgs': 77 if not isinstance(value, list): 78 raise self.PhysicsObjectError, \ 79 "%s is not a valid list for real_amplitudes " % str(value) 80 81 if name == 'OLP': 82 if not isinstance(value,str): 83 raise self.PhysicsObjectError, \ 84 "%s is not a valid string for OLP " % str(value) 85 86 if name == 'ncores_for_proc_gen': 87 if not isinstance(value,int): 88 raise self.PhysicsObjectError, \ 89 "%s is not a valid value for ncores_for_proc_gen " % str(value) 90 91 return super(FKSMultiProcess,self).filter(name, value)
92 93
94 - def check_ij_confs(self):
95 """check that there is no duplicate FKS ij configuration""" 96 ijconfs_dict = {} 97 for born in self['born_processes']: 98 # the copy.copy is needed as duplicate configurations will be removed on the fly 99 for real in copy.copy(born.real_amps): 100 pdgs = ' '.join([ '%d' % pdg for pdg in real.pdgs]) 101 for info in copy.copy(real.fks_infos): 102 ij = [info['i'], info['j']] 103 try: 104 if ij in ijconfs_dict[pdgs]: 105 logger.debug('Duplicate FKS configuration found for %s : ij = %s' % 106 (real.process.nice_string(), str(ij))) 107 #remove the configuration 108 born.real_amps[born.real_amps.index(real)].fks_infos.remove(info) 109 else: 110 ijconfs_dict[pdgs].append(ij) 111 except KeyError: 112 ijconfs_dict[pdgs] = [ij] 113 # check if any FKS configuration remains for the real emission, otherwise 114 # remove it 115 if not born.real_amps[born.real_amps.index(real)].fks_infos: 116 logger.debug('Removing real %s from born %s' % \ 117 (real.process.nice_string(), born.born_amp['process'].nice_string())) 118 born.real_amps.remove(real)
119 120
121 - def __init__(self, procdef=None, options={}):
122 """Initializes the original multiprocess, then generates the amps for the 123 borns, then generate the born processes and the reals. 124 Real amplitudes are stored in real_amplitudes according on the pdgs of their 125 legs (stored in pdgs, so that they need to be generated only once and then reicycled 126 """ 127 #swhich the other loggers off 128 loggers_off = [logging.getLogger('madgraph.diagram_generation'), 129 logging.getLogger('madgraph.loop_diagram_generation')] 130 old_levels = [logg.level for logg in loggers_off] 131 for logg in loggers_off: 132 logg.setLevel(logging.WARNING) 133 134 self['real_amplitudes'] = diagram_generation.AmplitudeList() 135 self['pdgs'] = [] 136 137 # OLP option 138 olp='MadLoop' 139 if 'OLP' in options.keys(): 140 olp = options['OLP'] 141 del options['OLP'] 142 143 self['init_lep_split']=False 144 if 'init_lep_split' in options.keys(): 145 self['init_lep_split']=options['init_lep_split'] 146 del options['init_lep_split'] 147 148 ncores_for_proc_gen = 0 149 # ncores_for_proc_gen has the following meaning 150 # 0 : do things the old way 151 # > 0 use ncores_for_proc_gen 152 # -1 : use all cores 153 if 'ncores_for_proc_gen' in options.keys(): 154 ncores_for_proc_gen = options['ncores_for_proc_gen'] 155 del options['ncores_for_proc_gen'] 156 157 try: 158 # Now generating the borns for the first time. 159 super(FKSMultiProcess, self).__init__(procdef, **options) 160 161 except diagram_generation.NoDiagramException as error: 162 # If no born, then this process most likely does not have any. 163 raise NoBornException, "Born diagrams could not be generated for the "+\ 164 self['process_definitions'][0].nice_string().replace('Process',\ 165 'process')+". Notice that aMC@NLO does not handle loop-induced"+\ 166 " processes yet, but you can still use MadLoop if you want to "+\ 167 "only generate them."+\ 168 " For this, use the 'virt=' mode, without multiparticle labels." 169 170 self['OLP'] = olp 171 self['ncores_for_proc_gen'] = ncores_for_proc_gen 172 173 #check process definition(s): 174 # a process such as g g > g g will lead to real emissions 175 # (e.g: u g > u g g ) which will miss some corresponding born, 176 # leading to non finite results 177 perturbation = [] 178 for procdef in self['process_definitions']: 179 soft_particles = [] 180 # do not warn for decay processes 181 if [ i['state'] for i in procdef['legs']].count(False) == 1: 182 continue 183 for pert in procdef['perturbation_couplings']: 184 if pert not in perturbation: 185 perturbation.append(pert) 186 soft_particles.extend(\ 187 fks_common.find_pert_particles_interactions(\ 188 procdef['model'], pert)['soft_particles']) 189 soft_particles_string = ', '.join( \ 190 [procdef['model'].get('particle_dict')[id][\ 191 {True:'name', False:'antiname'}[id >0] ] \ 192 for id in sorted(soft_particles, reverse=True)]) 193 for leg in procdef['legs']: 194 if any([id in soft_particles for id in leg['ids']]) \ 195 and sorted(leg['ids']) != soft_particles: 196 logger.warning('Use of multiparticles is non-trivial for NLO '+ \ 197 'process generation and depends on the orders included, '+ \ 198 'the process considered, as well as the PDF set chosen. '+ \ 199 'See appendix D of arXiv:1804.10017 [hep-ph] for some '+ \ 200 'guidance.') 201 break 202 203 amps = self.get('amplitudes') 204 205 # get the list of leptons from the model, in order to discard 206 # lepton-initiated processes unless the init_lep_split flag is specified 207 if self['process_definitions']: 208 leptons = self['process_definitions'][0]['model'].get_lepton_pdgs() 209 else: 210 leptons = [] 211 212 #generate reals, but combine them after having combined the borns 213 for i, amp in enumerate(amps): 214 # skip amplitudes with two initial leptons unless the init_lep_split option is True 215 if not self['init_lep_split'] and \ 216 all([l['id'] in leptons \ 217 for l in [ll for ll in amp.get('process').get('legs') if not ll['state']]]): 218 logger.info(('Discarding process%s.\n If you want to include it, set the \n' + \ 219 ' \'include_lepton_initiated_processes\' option to True') % \ 220 amp.get('process').nice_string().replace('Process', '')) 221 continue 222 223 logger.info("Generating FKS-subtracted matrix elements for born process%s (%d / %d)" \ 224 % (amp['process'].nice_string(print_weighted=False).replace('Process', ''), 225 i + 1, len(amps))) 226 227 born = FKSProcess(amp, ncores_for_proc_gen = self['ncores_for_proc_gen'], \ 228 init_lep_split=self['init_lep_split']) 229 self['born_processes'].append(born) 230 231 born.generate_reals(self['pdgs'], self['real_amplitudes'], combine = False) 232 233 # finally combine the real amplitudes 234 born.combine_real_amplitudes() 235 236 if not self['ncores_for_proc_gen']: 237 # old generation mode 238 239 born_pdg_list = [[l['id'] for l in born.get_leglist()] \ 240 for born in self['born_processes'] ] 241 242 for born in self['born_processes']: 243 for real in born.real_amps: 244 real.find_fks_j_from_i(born_pdg_list) 245 if amps: 246 if self['process_definitions'][0].get('NLO_mode') == 'all': 247 self.generate_virtuals() 248 249 elif not self['process_definitions'][0].get('NLO_mode') in ['all', 'real','LOonly']: 250 raise fks_common.FKSProcessError(\ 251 "Not a valid NLO_mode for a FKSMultiProcess: %s" % \ 252 self['process_definitions'][0].get('NLO_mode')) 253 254 # now get the total number of diagrams 255 n_diag_born = sum([len(amp.get('diagrams')) 256 for amp in self.get_born_amplitudes()]) 257 n_diag_real = sum([len(amp.get('diagrams')) 258 for amp in self.get_real_amplitudes()]) 259 n_diag_virt = sum([len(amp.get('loop_diagrams')) 260 for amp in self.get_virt_amplitudes()]) 261 262 if n_diag_virt == 0 and n_diag_real ==0 and \ 263 not self['process_definitions'][0].get('NLO_mode') == 'LOonly': 264 raise fks_common.FKSProcessError( 265 'This process does not have any correction up to NLO in %s'\ 266 %','.join(perturbation)) 267 268 logger.info(('Generated %d subprocesses with %d real emission diagrams, ' + \ 269 '%d born diagrams and %d virtual diagrams') % \ 270 (len(self['born_processes']), n_diag_real, n_diag_born, n_diag_virt)) 271 272 for i, logg in enumerate(loggers_off): 273 logg.setLevel(old_levels[i]) 274 275 self['has_isr'] = any([proc.isr for proc in self['born_processes']]) 276 self['has_fsr'] = any([proc.fsr for proc in self['born_processes']])
277
278 - def add(self, other):
279 """combines self and other, extending the lists of born/real amplitudes""" 280 self['process_definitions'].extend(other['process_definitions']) 281 self['amplitudes'].extend(other['amplitudes']) 282 self['born_processes'].extend(other['born_processes']) 283 self['real_amplitudes'].extend(other['real_amplitudes']) 284 self['pdgs'].extend(other['pdgs']) 285 self['has_isr'] = self['has_isr'] or other['has_isr'] 286 self['has_fsr'] = self['has_fsr'] or other['has_fsr'] 287 self['OLP'] = other['OLP'] 288 self['ncores_for_proc_gen'] = other['ncores_for_proc_gen']
289 290
291 - def get_born_amplitudes(self):
292 """return an amplitudelist with the born amplitudes""" 293 return diagram_generation.AmplitudeList([ 294 born.born_amp for \ 295 born in self['born_processes']])
296 297
298 - def get_virt_amplitudes(self):
299 """return an amplitudelist with the virt amplitudes""" 300 return diagram_generation.AmplitudeList([born.virt_amp \ 301 for born in self['born_processes'] if born.virt_amp])
302 303
304 - def get_real_amplitudes(self):
305 """return an amplitudelist with the real amplitudes""" 306 return self.get('real_amplitudes')
307 308
309 - def generate_virtuals(self):
310 """For each process among the born_processes, creates the corresponding 311 virtual amplitude""" 312 313 # If not using MadLoop, then the LH order file generation and processing 314 # will be entirely done during the output, so nothing must be done at 315 # this stage yet. 316 if self['OLP']!='MadLoop': 317 logger.info("The loop matrix elements will be generated by "+\ 318 '%s at the output stage only.'%self['OLP']) 319 return 320 321 # determine the orders to be used to generate the loop 322 #MZ loop_orders = {} 323 # for born in self['born_processes']: 324 # for coup, val in fks_common.find_orders(born.born_amp).items(): 325 # try: 326 # loop_orders[coup] = max([loop_orders[coup], val]) 327 # except KeyError: 328 # loop_orders[coup] = val 329 330 for i, born in enumerate(self['born_processes']): 331 myproc = copy.copy(born.born_amp['process']) 332 # include all particles in the loops 333 # i.e. allow all orders to be perturbed 334 myproc['perturbation_couplings'] = myproc['model']['coupling_orders'] 335 # take the orders that are actually used bu the matrix element 336 #MZ myproc['orders'] = loop_orders 337 myproc['legs'] = fks_common.to_legs(copy.copy(myproc['legs'])) 338 logger.info('Generating virtual matrix element with MadLoop for process%s (%d / %d)' \ 339 % (myproc.nice_string(print_weighted = False).replace(\ 340 'Process', ''), 341 i + 1, len(self['born_processes']))) 342 try: 343 myamp = loop_diagram_generation.LoopAmplitude(myproc) 344 born.virt_amp = myamp 345 except InvalidCmd: 346 pass
347 348
349 -class FKSRealProcess(object):
350 """Contains information about a real process: 351 -- fks_infos (list containing the possible fks configs for a given process 352 -- amplitude 353 -- is_to_integrate 354 """ 355
356 - def __init__(self, born_proc, leglist, ij, ij_id, born_pdgs, splitting_type, 357 perturbed_orders = ['QCD']): #test written
358 """Initializes the real process based on born_proc and leglist. 359 Stores the fks informations into the list of dictionaries fks_infos 360 """ 361 #safety check 362 assert type(splitting_type) == list and not type(splitting_type) == str 363 self.fks_infos = [] 364 for leg in leglist: 365 if leg.get('fks') == 'i': 366 i_fks = leg.get('number') 367 # i is a gluon or a photon 368 need_color_links = leg.get('massless') \ 369 and leg.get('spin') == 3 \ 370 and leg.get('self_antipart') \ 371 and leg.get('color') == 8 372 need_charge_links = leg.get('massless') \ 373 and leg.get('spin') == 3 \ 374 and leg.get('self_antipart') \ 375 and leg.get('color') == 1 376 if leg.get('fks') == 'j': 377 j_fks = leg.get('number') 378 self.fks_infos.append({'i': i_fks, 379 'j': j_fks, 380 'ij': ij, 381 'ij_id': ij_id, 382 'underlying_born': born_pdgs, 383 'splitting_type': splitting_type, 384 'need_color_links': need_color_links, 385 'need_charge_links': need_charge_links, 386 'extra_cnt_index': -1}) 387 388 self.process = copy.copy(born_proc) 389 self.process['perturbation_couplings'] = \ 390 copy.copy(born_proc['perturbation_couplings']) 391 for o in splitting_type: 392 if o not in self.process['perturbation_couplings']: 393 self.process['perturbation_couplings'].append(o) 394 # set the orders to empty, to force the use of the squared_orders 395 self.process['orders'] = copy.copy(born_proc['orders']) 396 self.process['orders'] = {} 397 398 legs = [(leg.get('id'), leg) for leg in leglist] 399 self.pdgs = array.array('i',[s[0] for s in legs]) 400 self.colors = [leg['color'] for leg in leglist] 401 if not self.process['perturbation_couplings'] == ['QCD']: 402 self.charges = [leg['charge'] for leg in leglist] 403 else: 404 self.charges = [0.] * len(leglist) 405 self.perturbation = 'QCD' 406 self.process.set('legs', MG.LegList(leglist)) 407 self.process.set('legs_with_decays', MG.LegList()) 408 self.amplitude = diagram_generation.Amplitude() 409 self.is_to_integrate = True 410 self.is_nbody_only = False 411 self.fks_j_from_i = {} 412 self.missing_borns = [] 413 414
415 - def generate_real_amplitude(self):
416 """generates the real emission amplitude starting from self.process""" 417 self.amplitude = diagram_generation.Amplitude(self.process) 418 return self.amplitude
419 420
421 - def find_fks_j_from_i(self, born_pdg_list): #test written
422 """Returns a dictionary with the entries i : [j_from_i], if the born pdgs are in 423 born_pdg_list""" 424 fks_j_from_i = {} 425 for i in self.process.get('legs'): 426 fks_j_from_i[i.get('number')] = [] 427 if i.get('state'): 428 for j in [l for l in self.process.get('legs') if \ 429 l.get('number') != i.get('number')]: 430 for pert_order in self.process.get('perturbation_couplings'): 431 ijlist = fks_common.combine_ij(i, j, self.process.get('model'), {},\ 432 pert=pert_order) 433 for ij in ijlist: 434 born_leglist = fks_common.to_fks_legs( 435 copy.deepcopy(self.process.get('legs')), 436 self.process.get('model')) 437 born_leglist.remove(i) 438 born_leglist.remove(j) 439 born_leglist.insert(ij.get('number') - 1, ij) 440 born_leglist.sort(pert = self.perturbation) 441 if [leg['id'] for leg in born_leglist] in born_pdg_list \ 442 and not j.get('number') in fks_j_from_i[i.get('number')]: 443 fks_j_from_i[i.get('number')].append(\ 444 j.get('number')) 445 446 self.fks_j_from_i = fks_j_from_i 447 return fks_j_from_i 448 449
450 - def get_leg_i(self): #test written
451 """Returns leg corresponding to i fks. 452 An error is raised if the fks_infos list has more than one entry""" 453 if len(self.fks_infos) > 1: 454 raise fks_common.FKSProcessError(\ 455 'get_leg_i should only be called before combining processes') 456 return self.process.get('legs')[self.fks_infos[0]['i'] - 1] 457
458 - def get_leg_j(self): #test written
459 """Returns leg corresponding to j fks. 460 An error is raised if the fks_infos list has more than one entry""" 461 if len(self.fks_infos) > 1: 462 raise fks_common.FKSProcessError(\ 463 'get_leg_j should only be called before combining processes') 464 return self.process.get('legs')[self.fks_infos[0]['j'] - 1] 465 466
467 -class FKSProcessList(MG.PhysicsObjectList):
468 """Class to handle lists of FKSProcesses.""" 469
470 - def is_valid_element(self, obj):
471 """Test if object obj is a valid FKSProcess for the list.""" 472 return isinstance(obj, FKSProcess)
473 474
475 -class FKSProcess(object):
476 """The class for a FKS process. Starts from the born process and finds 477 all the possible splittings.""" 478 479 480 #helper functions 481
482 - def get_colors(self):
483 """return the list of color representations 484 for each leg in born_amp""" 485 return [leg.get('color') for \ 486 leg in self.born_amp['process']['legs']]
487 488
489 - def get_charges(self):
490 """return the list of charges 491 for each leg in born_amp""" 492 return [leg.get('charge') for \ 493 leg in self.born_amp['process']['legs']]
494 495
496 - def get_nlegs(self):
497 """return the number of born legs""" 498 return len(self.born_amp['process']['legs'])
499 500
501 - def get_born_nice_string(self):
502 """Return the nice string for the born process. 503 """ 504 return self.born_amp['process'].nice_string()
505 506
507 - def get_pdg_codes(self):
508 """return the list of the pdg codes 509 of each leg in born_amp""" 510 return [leg.get('id') for \ 511 leg in self.born_amp['process']['legs']]
512 513
514 - def get_leglist(self):
515 """return the leg list 516 for the born amp""" 517 return fks_common.to_fks_legs( \ 518 self.born_amp['process']['legs'], \ 519 self.born_amp['process']['model'])
520 521 522 ############################################################################### 523
524 - def __init__(self, start_proc = None, remove_reals = True, ncores_for_proc_gen=0, init_lep_split = False):
525 """initialization: starts either from an amplitude or a process, 526 then init the needed variables. 527 remove_borns tells if the borns not needed for integration will be removed 528 from the born list (mainly used for testing) 529 ncores_for_proc_gen has the following meaning 530 0 : do things the old way 531 > 0 use ncores_for_proc_gen 532 -1 : use all cores 533 """ 534 535 self.reals = [] 536 self.myorders = {} 537 self.real_amps = [] 538 self.remove_reals = remove_reals 539 self.init_lep_split = init_lep_split 540 self.nincoming = 0 541 self.virt_amp = None 542 self.perturbation = 'QCD' 543 self.born_amp = diagram_generation.Amplitude() 544 self.extra_cnt_amp_list = diagram_generation.AmplitudeList() 545 self.ncores_for_proc_gen = ncores_for_proc_gen 546 547 if not remove_reals in [True, False]: 548 raise fks_common.FKSProcessError(\ 549 'Not valid type for remove_reals in FKSProcess') 550 551 if start_proc: 552 #initilaize with process definition (for test purporses) 553 if isinstance(start_proc, MG.Process): 554 pertur = start_proc['perturbation_couplings'] 555 if pertur: 556 self.perturbation = sorted(pertur)[0] 557 self.born_amp = diagram_generation.Amplitude(\ 558 copy.copy(fks_common.sort_proc(\ 559 start_proc, pert = self.perturbation))) 560 #initialize with an amplitude 561 elif isinstance(start_proc, diagram_generation.Amplitude): 562 pertur = start_proc.get('process')['perturbation_couplings'] 563 self.born_amp = diagram_generation.Amplitude(\ 564 copy.copy(fks_common.sort_proc(\ 565 start_proc['process'], 566 pert = self.perturbation))) 567 else: 568 raise fks_common.FKSProcessError(\ 569 'Not valid start_proc in FKSProcess') 570 self.born_amp['process'].set('legs_with_decays', MG.LegList()) 571 572 # special treatment of photon is needed ! 573 #MZ to be fixed 574 ###self.isr = set([leg.get(color) for leg in self.leglist if not leg.get('state')]) != set([zero]) 575 ###self.fsr = set([leg.get(color) for leg in self.leglist if leg.get('state')]) != set([zero]) 576 self.isr = False 577 self.fsr = False 578 ####### 579 self.nincoming = len([l for l in self.born_amp['process']['legs'] \ 580 if not l['state']]) 581 582 self.ndirs = 0 583 # generate reals, when the mode is not LOonly 584 # when is LOonly it is supposed to be a 'fake' NLO process 585 # e.g. to be used in merged sampels at high multiplicities 586 if self.born_amp['process']['NLO_mode'] != 'LOonly': 587 self.find_reals()
588 589
590 - def generate_real_amplitudes(self, pdg_list, real_amp_list):
591 """generates the real amplitudes for all the real emission processes, using pdgs and real_amps 592 to avoid multiple generation of the same amplitude. 593 Amplitude without diagrams are discarded at this stage""" 594 595 no_diags_amps = [] 596 for amp in self.real_amps: 597 try: 598 amp.amplitude = real_amp_list[pdg_list.index(amp.pdgs)] 599 except ValueError: 600 amplitude = amp.generate_real_amplitude() 601 if amplitude['diagrams']: 602 pdg_list.append(amp.pdgs) 603 real_amp_list.append(amplitude) 604 else: 605 no_diags_amps.append(amp) 606 607 for amp in no_diags_amps: 608 self.real_amps.remove(amp)
609 610 611
612 - def combine_real_amplitudes(self):
613 """combines real emission processes if the pdgs are the same, combining the lists 614 of fks_infos""" 615 pdgs = [] 616 real_amps = [] 617 old_real_amps = copy.copy(self.real_amps) 618 for amp in old_real_amps: 619 try: 620 real_amps[pdgs.index(amp.pdgs)].fks_infos.extend(amp.fks_infos) 621 except ValueError: 622 real_amps.append(amp) 623 pdgs.append(amp.pdgs) 624 625 self.real_amps = real_amps
626 627
628 - def generate_reals(self, pdg_list, real_amp_list, combine=True): #test written
629 """For all the possible splittings, creates an FKSRealProcess. 630 It removes double counted configorations from the ones to integrates and 631 sets the one which includes the bosn (is_nbody_only). 632 if combine is true, FKS_real_processes having the same pdgs (i.e. real amplitude) 633 are combined together 634 """ 635 #copy the born process 636 born_proc = copy.copy(self.born_amp['process']) 637 born_pdgs = self.get_pdg_codes() 638 leglist = self.get_leglist() 639 extra_cnt_pdgs = [] 640 for i, real_list in enumerate(self.reals): 641 # i is the born leg which splits 642 # keep track of the id of the mother (will be used to constrct the 643 # spin-correlated borns) 644 ij_id = leglist[i].get('id') 645 ij = leglist[i].get('number') 646 for real_dict in real_list: 647 nmom = 0 648 649 # check first if other counterterms need to be generated 650 # (e.g. g/a > q qbar) 651 # this is quite a tricky business, as double counting 652 # the singular configuration must be avoided. 653 # Let real, born, cnt be the real emission, the born process 654 # (that will give the name to the P0_** dir) and the 655 # extra counterterm (obtained by the born process replacing 656 # ij with the extra mother). 657 # If there are extra mothers, first check that 658 # 1) born, at order born[squared_orders] - 659 # 2 * (the perturbation type of the real emission) has diagrams 660 # 2) cnt at order born[squared_orders] - 661 # 2 * (the perturbation type of the extra mom) has diagrams 662 663 cnt_amp = diagram_generation.Amplitude() 664 born_cnt_amp = diagram_generation.Amplitude() 665 mom_cnt = 0 666 cnt_ord = None 667 668 # check condition 1) above (has_coll_sing_born) 669 born_proc_coll_sing = copy.copy(born_proc) 670 born_proc_coll_sing['squared_orders'] = copy.copy(born_proc['squared_orders']) 671 if born_proc_coll_sing['squared_orders'][real_dict['perturbation'][0]] < 2: 672 has_coll_sing_born = False 673 else: 674 born_proc_coll_sing['squared_orders'][real_dict['perturbation'][0]] += -2 675 has_coll_sing_born = bool(diagram_generation.Amplitude(born_proc_coll_sing)['diagrams']) 676 677 # check that there is at most one extra mother 678 allmothers = [] 679 for order, mothers in real_dict['extra_mothers'].items(): 680 allmothers += mothers 681 if mothers: 682 cnt_ord = order 683 684 if len(allmothers) > 1: 685 raise fks_common.FKSProcessError(\ 686 'Error, more than one extra mother has been found: %d', len(allmothers)) 687 # here we are sure to have just one extra mother 688 689 has_coll_sing_cnt = False 690 if allmothers: 691 mom_cnt = allmothers[0] 692 693 # generate a new process with the mother particle 694 # replaced by the new mother and with the 695 # squared orders changed accordingly 696 697 cnt_process = copy.copy(born_proc) 698 cnt_process['legs'] = copy.deepcopy(born_proc['legs']) 699 cnt_process['legs'][i]['id'] = mom_cnt 700 cnt_process['legs'] = fks_common.to_fks_legs( 701 cnt_process['legs'], cnt_process['model']) 702 cnt_process['squared_orders'] = \ 703 copy.copy(born_proc['squared_orders']) 704 705 # check if the cnt amplitude exists with the current 706 # squared orders (i.e. if it will appear as a P0 dir) 707 # if it does not exist, then no need to worry about anything, as all further 708 # checks will have stricter orders than here 709 710 cnt_process_for_amp = copy.copy(cnt_process) 711 cnt_process_for_amp['squared_orders'] = copy.copy(cnt_process['squared_orders']) 712 cnt_amp = diagram_generation.Amplitude(cnt_process_for_amp) 713 714 if bool(cnt_amp['diagrams']) and \ 715 cnt_process['squared_orders'][cnt_ord] >= 2: 716 717 # check condition 2) above (has_coll_sing_cnt) 718 # MZMZ17062014 beware that the Amplitude reorders the legs 719 cnt_process['squared_orders'][cnt_ord] += -2 720 born_cnt_amp = diagram_generation.Amplitude(cnt_process) 721 has_coll_sing_cnt = bool(born_cnt_amp['diagrams']) 722 723 # remember there is at most one mom 724 # now, one of these cases can occur 725 # a) no real collinear singularity exists (e.g. just the interference 726 # has to be integrated for the real emission). Add the real process to 727 # this born process if ij_id < mom_cnt. No extra_cnt is needed 728 # b) a collinear singularity exists, with the underlying born being the born 729 # process of this dir, while no singularity is there for the extra_cnt. 730 # In this case keep the real emission in this directory, without any 731 # extra_cnt 732 # c) a collinear singularity exists, with the underlying born being the 733 # extra_cnt, while no singularity is there for the born of the dir. 734 # In this case skip the real emission, it will be included in the 735 # directory of the extra cnt 736 # d) a collinear singularity exists, and both the process-dir born and 737 # the extra cnt are needed to subtract it. Add the real process to 738 # this born process if ij_id < mom_cnt and keeping the extra_cnt 739 # 740 # in all cases, remember that mom_cnt is set to 0 if no extra mother is there 741 742 # the real emission has to be skipped if mom_cnt(!=0) < ij_id and either a) or d) 743 if mom_cnt and mom_cnt < ij_id: 744 if ((not has_coll_sing_born and not has_coll_sing_cnt) or \ 745 (has_coll_sing_born and has_coll_sing_cnt)): 746 continue 747 748 # the real emission has also to be skipped in case of c) 749 if has_coll_sing_cnt and not has_coll_sing_born: 750 continue 751 752 # if we arrive here, we need to keep this real mession 753 ij = leglist[i].get('number') 754 self.real_amps.append(FKSRealProcess( \ 755 born_proc, real_dict['leglist'], ij, ij_id, \ 756 [born_pdgs], 757 real_dict['perturbation'], \ 758 perturbed_orders = born_proc['perturbation_couplings'])) 759 760 # keep the extra_cnt if needed 761 if has_coll_sing_cnt: 762 # for the moment we just check the pdgs, regardless of any 763 # permutation in the final state 764 try: 765 indx = extra_cnt_pdgs.index([l['id'] for l in cnt_process['legs']]) 766 except ValueError: 767 extra_cnt_pdgs.append([l['id'] for l in cnt_process['legs']]) 768 assert cnt_amp != None 769 self.extra_cnt_amp_list.append(cnt_amp) 770 indx = len(self.extra_cnt_amp_list) - 1 771 772 # update the fks infos 773 self.real_amps[-1].fks_infos[-1]['extra_cnt_index'] = indx 774 self.real_amps[-1].fks_infos[-1]['underlying_born'].append(\ 775 [l['id'] for l in cnt_process['legs']]) 776 self.real_amps[-1].fks_infos[-1]['splitting_type'].append(cnt_ord) 777 778 self.find_reals_to_integrate() 779 if combine: 780 self.combine_real_amplitudes() 781 if not self.ncores_for_proc_gen: 782 self.generate_real_amplitudes(pdg_list, real_amp_list) 783 self.link_born_reals()
784 785 799 800
801 - def find_reals(self, pert_orders = []):
802 """finds the FKS real configurations for a given process. 803 self.reals[i] is a list of dictionaries corresponding to the real 804 emissions obtained splitting leg i. 805 The dictionaries contain the leglist, the type (order) of the 806 splitting and extra born particles which can give the same 807 splitting (e.g. gluon/photon -> qqbar). 808 If pert orders is empty, all the orders of the model will be used 809 """ 810 811 model = self.born_amp['process']['model'] 812 if not pert_orders: 813 pert_orders = model['coupling_orders'] 814 815 leglist = self.get_leglist() 816 if range(len(leglist)) != [l['number']-1 for l in leglist]: 817 raise fks_common.FKSProcessError('Disordered numbers of leglist') 818 819 if [ i['state'] for i in leglist].count(False) == 1: 820 decay_process=True 821 else: 822 decay_process=False 823 824 # count the number of initial-state leptons 825 ninit_lep = [l['id'] in model.get_lepton_pdgs() and not l['state'] for l in leglist].count(True) 826 827 for i in leglist: 828 i_i = i['number'] - 1 829 self.reals.append([]) 830 831 # for 2->1 processes, map only the initial-state singularities 832 # this is because final-state mapping preserves shat, which 833 # is not possible in 2->1 834 if len(leglist) == 3 and not decay_process and i['state']: 835 continue 836 for pert_order in pert_orders: 837 # no splittings for initial states in decay processes 838 if decay_process and not i['state']: 839 splittings=[] 840 # if there are leptons in the initial state and init_lep_split is False, 841 # only split initial state leptons; do nothing for any other particle 842 elif not self.init_lep_split and ninit_lep >= 1 and \ 843 (i['state'] or i['id'] not in model.get_lepton_pdgs()): 844 splittings=[] 845 else: 846 splittings = fks_common.find_splittings( \ 847 i, model, {}, pert_order, \ 848 include_init_leptons=self.init_lep_split) 849 for split in splittings: 850 # find other 'mother' particles which can end up in the same splitting 851 extra_mothers = {} 852 for pert in pert_orders: 853 extra_mothers[pert] = fks_common.find_mothers(split[0], split[1], model, pert=pert, 854 mom_mass=model.get('particle_dict')[i['id']]['mass'].lower()) 855 856 #remove the current mother from the extra mothers 857 if i['state']: 858 extra_mothers[pert_order].remove(i['id']) 859 else: 860 extra_mothers[pert_order].remove(model.get('particle_dict')[i['id']].get_anti_pdg_code()) 861 862 self.reals[i_i].append({ 863 'leglist': fks_common.insert_legs(leglist, i, split ,pert=pert_order), 864 'perturbation': [pert_order], 865 'extra_mothers': extra_mothers})
866 867
868 - def find_reals_to_integrate(self): #test written
869 """Finds double countings in the real emission configurations, sets the 870 is_to_integrate variable and if "self.remove_reals" is True removes the 871 not needed ones from the born list. 872 """ 873 #find the initial number of real configurations 874 ninit = len(self.real_amps) 875 remove = self.remove_reals 876 877 for m in range(ninit): 878 for n in range(m + 1, ninit): 879 real_m = self.real_amps[m] 880 real_n = self.real_amps[n] 881 if len(real_m.fks_infos) > 1 or len(real_m.fks_infos) > 1: 882 raise fks_common.FKSProcessError(\ 883 'find_reals_to_integrate should only be called before combining processes') 884 885 i_m = real_m.fks_infos[0]['i'] 886 j_m = real_m.fks_infos[0]['j'] 887 i_n = real_n.fks_infos[-1]['i'] 888 j_n = real_n.fks_infos[0]['j'] 889 ij_id_m = real_m.fks_infos[0]['ij_id'] 890 ij_id_n = real_n.fks_infos[0]['ij_id'] 891 if j_m > self.nincoming and j_n > self.nincoming: 892 # make sure i and j in the two real emissions have the same mother 893 if (ij_id_m != ij_id_n): 894 continue 895 if (real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] \ 896 and \ 897 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']) \ 898 or \ 899 (real_m.get_leg_i()['id'] == real_n.get_leg_j()['id'] \ 900 and \ 901 real_m.get_leg_j()['id'] == real_n.get_leg_i()['id']): 902 if i_m > i_n: 903 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 904 self.real_amps[m].is_to_integrate = False 905 else: 906 self.real_amps[n].is_to_integrate = False 907 elif i_m == i_n and j_m > j_n: 908 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 909 self.real_amps[m].is_to_integrate = False 910 else: 911 self.real_amps[n].is_to_integrate = False 912 # in case of g(a) > ffx splitting, keep the lowest ij 913 elif i_m == i_n and j_m == j_n and \ 914 not real_m.get_leg_j()['self_antipart'] and \ 915 not real_m.get_leg_i()['self_antipart']: 916 if real_m.fks_infos[0]['ij'] > real_n.fks_infos[0]['ij']: 917 real_m.is_to_integrate = False 918 else: 919 real_n.is_to_integrate = False 920 else: 921 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']: 922 self.real_amps[n].is_to_integrate = False 923 else: 924 self.real_amps[m].is_to_integrate = False 925 # self.real_amps[m].is_to_integrate = False 926 elif j_m <= self.nincoming and j_n == j_m: 927 if real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] and \ 928 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']: 929 if i_m > i_n: 930 self.real_amps[n].is_to_integrate = False 931 else: 932 self.real_amps[m].is_to_integrate = False 933 if remove: 934 newreal_amps = [] 935 for real in self.real_amps: 936 if real.is_to_integrate: 937 newreal_amps.append(real) 938 self.real_amps = newreal_amps 939