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

Source Code for Module madgraph.fks.fks_common

  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 both for MadFKS from real  
 17  and MadFKS from born""" 
 18   
 19  import madgraph.core.base_objects as MG 
 20  import madgraph.core.helas_objects as helas_objects 
 21  import madgraph.core.diagram_generation as diagram_generation 
 22  import madgraph.core.color_amp as color_amp 
 23  import madgraph.core.color_algebra as color_algebra 
 24  from operator import itemgetter 
 25  import copy 
 26  import logging 
 27  import array 
 28  import fractions 
29 30 31 32 -class FKSProcessError(Exception):
33 """Exception for MadFKS""" 34 pass
35
36 37 -class FKSDiagramTag(diagram_generation.DiagramTag): #test written
38 """Modified diagram tags to be used to link born and real configurations. 39 """ 40 41 @staticmethod 48 203
204 205 206 -def find_orders(amp): #test_written
207 """Takes an amplitude as input, and returns a dictionary with the 208 orders of the couplings. 209 """ 210 assert isinstance(amp, diagram_generation.Amplitude) 211 orders = {} 212 for diag in amp.get('diagrams'): 213 for order, value in diag.get('orders').items(): 214 if value != 0 or order in amp['process']['orders'].keys(): 215 try: 216 orders[order] = max(orders[order], value) 217 except KeyError: 218 orders[order] = value 219 return orders 220
221 222 -def find_splittings(leg, model, dict, pert='QCD'): #test written
223 """Finds the possible splittings corresponding to leg 224 """ 225 if dict == {}: 226 dict = find_pert_particles_interactions(model, pert) 227 splittings = [] 228 #check that the leg is a qcd leg 229 230 if leg.get('id') in dict['pert_particles']: 231 part = model.get('particle_dict')[leg.get('id')] 232 antipart = model.get('particle_dict')[part.get_anti_pdg_code()] 233 for ii in dict['interactions']: 234 #check which interactions contain leg and at least one soft particles: 235 parts = copy.deepcopy(ii['particles']) 236 nsoft = 0 237 if part in parts: 238 #pops the ANTI-particle of part from the interaction 239 parts.pop(parts.index(antipart)) 240 for p in parts: 241 if p.get_pdg_code() in dict['soft_particles']: 242 nsoft += 1 243 if nsoft >= 1: 244 splittings.extend(split_leg(leg, parts, model)) 245 return splittings 246
247 248 -def split_leg(leg, parts, model): #test written
249 """Splits the leg into parts, and returns the two new legs. 250 """ 251 #for an outgoing leg take the antiparticles 252 split = [] 253 #for a final state particle one can have only a splitting 254 if leg['state'] : 255 split.append([]) 256 for part in parts: 257 split[-1].append(to_fks_leg({'state': True, \ 258 'id': part.get_pdg_code()},model)) 259 ij_final(split[-1]) 260 #while for an initial state particle one can have two splittings 261 # if the two partons are different 262 else: 263 if parts[0] != parts[1]: 264 for part in parts: 265 cparts = copy.deepcopy(parts) 266 split.append([\ 267 to_fks_leg({'state': False, 268 'id': cparts.pop(cparts.index(part)).get_pdg_code(), 269 'fks': 'j'}, model), 270 to_fks_leg({'state': True, 271 'id': cparts[0].get_anti_pdg_code(), 272 'fks': 'i'}, model)\ 273 ]) 274 else: 275 split.append([\ 276 to_fks_leg({'state': False, 277 'id': parts[0].get_pdg_code(), 278 'fks': 'j'}, model), 279 to_fks_leg({'state': True, 280 'id': parts[1].get_anti_pdg_code(), 281 'fks': 'i'}, model)]) 282 return split 283
284 285 -def ij_final(pair):
286 """given a pair of legs in the final state, assigns the i/j fks id 287 NOTE: the j partons is always put before the i one 288 """ 289 #if a massless bosonic particle is in the pair, it is i 290 #else by convention the anti-particle is labeled i 291 #the order of the splitting is [ j, i] 292 if len(pair) == 2: 293 for i in range(len(pair)): 294 set = 0 295 if (pair[i]['massless'] and pair[i]['self_antipart']) or \ 296 (not pair[i]['is_part'] and pair[1-i]['is_part'] and\ 297 (pair[i]['spin']+pair[1-i]['spin'])%2==0) and not set: 298 pair[i]['fks'] = 'i' 299 pair[1-i]['fks'] = 'j' 300 #check that first j then i 301 if i < 1 - i: 302 pair.reverse() 303 set = 1
304
305 -def insert_legs(leglist_orig, leg, split,pert='QCD'):
306 """Returns a new leglist with leg splitted into split. 307 The convention is to remove leg ij, replace it with leg j, and put 308 i at the end of the group of legs with the same color(charge) representation 309 """ 310 if pert =='QCD': 311 color = 'color' 312 elif pert == 'QED': 313 color = 'charge' 314 else: 315 raise FKSProcessError, "Only QCD or QED is allowed not %s" % pert 316 # the deepcopy statement is crucial 317 leglist = FKSLegList(copy.deepcopy(leglist_orig)) 318 #find the position of the first final state leg 319 for i in range(len(leglist)): 320 if leglist[-i - 1].get('state'): 321 firstfinal = len(leglist) - i - 1 322 # replace leg with leg_j (split[0]) 323 leglist[leglist.index(leg)] = split[0] 324 # and find where to insert i (split[1]) 325 col_maxindex = {} 326 mass_col_maxindex = {} 327 for col in set([l[color] for l in leglist[firstfinal:] if l['massless']]): 328 col_maxindex[col] = max([0] + [leglist.index(l) for l in leglist[firstfinal:]\ 329 if l[color] == col and l['massless']]) 330 for col in set([abs(l[color]) for l in leglist[firstfinal:] if not l['massless']]): 331 mass_col_maxindex[col] = max([0] + [leglist.index(l) for l in leglist[firstfinal:]\ 332 if abs(l[color]) == col and not l['massless']]) 333 #no need to keep info on particles with color > i 334 if pert == 'QCD': 335 for col in copy.copy(col_maxindex.keys()): 336 if abs(col) > abs(split[1][color]): 337 del col_maxindex[col] 338 ### for col in copy.copy(mass_col_maxindex.keys()): 339 ### if abs(col) > abs(split[1][color]): 340 ### del mass_col_maxindex[col] 341 #also remove antiquarks if i is a quark or a fermion 342 if split[1]['is_part'] and not split[1]['self_antipart']: 343 # In old MADFKS5, the line below was used instead. It is however equivalent in principle. 344 # We can remove this comment and the line below altogether after validation and complete 345 # merge of the EW branch in aMC@NLO trunk. 346 #if split[1][color] > 0: 347 try: 348 del col_maxindex[-split[1][color]] 349 except KeyError: 350 pass 351 #so now the maximum of the max_col entries should be the position to insert leg i 352 leglist.insert(max(col_maxindex.values() + mass_col_maxindex.values() + [firstfinal - 1] ) + 1, split[1]) 353 ### leglist.insert(max(col_maxindex.values() + [firstfinal - 1] ) + 1, split[1]) 354 # for sleg in split: 355 # leglist.insert(i, sleg) 356 # #keep track of the number for initial state legs 357 # #if not sleg.get('state') and not leg.get('state'): 358 # leglist[i]['number'] = leg['number'] 359 # i += 1 360 # if i < firstfinal: 361 # i = firstfinal 362 # 363 # leglist.sort() 364 for i, leg in enumerate(leglist): 365 leg['number'] = i + 1 366 return leglist
367
368 369 -def combine_ij( i, j, model, dict, pert='QCD'): #test written
370 """checks whether FKSlegs i and j can be combined together in the given model 371 and with given perturbation order and if so combines them into ij. 372 If dict is empty it is initialized with find_pert_particles_interactions 373 """ 374 if dict == {}: 375 dict = find_pert_particles_interactions(model, pert) 376 ij = [] 377 num = copy.copy(min(i.get('number'), j.get('number'))) 378 379 # we do not want j being a massless vector unless also i is or j is initial 380 not_double_counting = (j.get('spin') == 3 and j.get('massless') and 381 i.get('spin') == 3 and i.get('massless')) or \ 382 j.get('spin') != 3 or not j.get('massless') or \ 383 not j.get('state') 384 385 #if i and j are a final state particle and antiparticle pair, 386 # then we want i to be antipart and j to be 387 if j.get('state') and j.get('id') == - i.get('id'): 388 not_double_counting = not_double_counting and j.get('id') >0 389 390 if i.get('id') in dict['soft_particles'] and \ 391 j.get('id') in dict['pert_particles'] and \ 392 i.get('state') and not_double_counting: 393 for int in dict['interactions']: 394 parts= copy.copy(int['particles']) 395 #remove i 396 try: 397 parts.remove(model.get('particle_dict')[i.get('id')]) 398 except ValueError: 399 continue 400 401 #remove j if final state, anti j if initial state 402 if j.get('state'): 403 j_id = j.get('id') 404 else: 405 j_id = model.get('particle_dict')[j.get('id')].get_anti_pdg_code() 406 try: 407 parts.remove(model.get('particle_dict')[j_id]) 408 except ValueError: 409 continue 410 411 #ij is what remains if j is initial, the anti of if j is final 412 if j.get('state'): 413 ij.append(MG.Leg({ 414 'id': parts[0].get_anti_pdg_code(), 415 'state': True, 416 'number': num})) 417 else: 418 ij.append(MG.Leg({ 419 'id': parts[0].get_pdg_code(), 420 'state': False, 421 'number': num})) 422 return to_fks_legs(ij, model) 423
424 425 -def find_pert_particles_interactions(model, pert_order = 'QCD'): #test written
426 """given a model and pert_order, returns a dictionary with as entries: 427 --interactions : the interactions of order pert_order 428 --pert_particles : pdgs of particles taking part to interactions 429 --soft_particles : pdgs of massless particles in pert_particles 430 """ 431 #ghost_list = [82, -82] # make sure ghost_list is non-empty 432 ghost_list = [] 433 ghost_list += [ p.get_pdg_code() for p in model.get('particles') if p.get('ghost')] 434 qcd_inter = MG.InteractionList() 435 pert_parts = [] 436 soft_parts = [] 437 for i, ii in model.get('interaction_dict').items(): 438 # i want interections of pert_order: 1 (from LO to NLO), 439 # without any other orders 440 if ii.get('orders') == {pert_order:1} and len(ii['particles']) == 3 : 441 masslist = [p.get('mass').lower() for p in ii['particles']] 442 # check that there is at least a massless particle, and that the 443 # remaining ones have the same mass 444 # (otherwise the real emission final state will not be degenerate 445 # with the born one 446 try: 447 masslist.remove('zero') 448 except ValueError: 449 continue 450 if len(set(masslist)) == 1 and not \ 451 any( [ p.get_pdg_code() in ghost_list for p in ii['particles']]) : 452 qcd_inter.append(ii) 453 for pp in ii['particles']: 454 pert_parts.append(pp.get_pdg_code()) 455 if pp['mass'].lower() == 'zero': 456 soft_parts.append(pp.get_pdg_code()) 457 458 return {'interactions': sorted(qcd_inter), 459 'pert_particles': sorted(set(pert_parts)), 460 'soft_particles': sorted(set(soft_parts))} 461 464 """insert the color links in col_obj: returns a list of dictionaries 465 (one for each link) with the following entries: 466 --link: the numbers of the linked legs 467 --link_basis: the linked color basis 468 --link_matrix: the color matrix created from the original basis and the linked one 469 """ 470 assert isinstance(col_basis, color_amp.ColorBasis) 471 assert isinstance(col_obj, list) 472 result =[] 473 for link in links: 474 this = {} 475 #define the link 476 l =[] 477 for leg in link['legs']: 478 l.append(leg.get('number')) 479 this['link'] = l 480 481 #replace the indices in col_obj of the linked legs according to 482 # link['replacements'] 483 # and extend-> product the color strings 484 485 this_col_obj = [] 486 for old_dict in col_obj: 487 new_dict = dict(old_dict) 488 for k, string in new_dict.items(): 489 new_dict[k] = string.create_copy() 490 for col in new_dict[k]: 491 for ind in col: 492 for pair in link['replacements']: 493 if ind == pair[0]: 494 col[col.index(ind)] = pair[1] 495 new_dict[k].product(link['string']) 496 this_col_obj.append(new_dict) 497 basis_link = color_amp.ColorBasis() 498 for ind, new_dict in enumerate(this_col_obj): 499 basis_link.update_color_basis(new_dict, ind) 500 501 this['link_basis'] = basis_link 502 this['link_matrix'] = color_amp.ColorMatrix(col_basis,basis_link) 503 result.append(this) 504 basis_orig = color_amp.ColorBasis() 505 for ind, new_dict in enumerate(col_obj): 506 basis_orig.update_color_basis(new_dict, ind) 507 508 for link in result: 509 link['orig_basis'] = basis_orig 510 return result 511 515 """Finds all the possible color(charge) links between any 516 two legs of the born. 517 If symm is true, only half of the color links are generated, those 518 for which leg1['number'] <= leg2['number'] 519 """ 520 if pert == 'QCD': 521 color = 'color' 522 zero = 1 523 elif pert == 'QED': 524 color = 'charge' 525 zero = 0. 526 else: 527 raise FKSProcessError,"Only QCD or QED is allowed not %s" % pert 528 color_links = [] 529 for leg1 in leglist: 530 for leg2 in leglist: 531 #legs must be colored(charged) and different, unless massive 532 if (leg1.get(color) != zero and leg2.get(color) != zero) \ 533 and (leg1 != leg2 or not leg1.get('massless')): 534 if not symm or leg1['number'] <= leg2['number']: 535 col_dict = legs_to_color_link_string(leg1,leg2,pert = pert) 536 color_links.append({ 537 'legs': [leg1, leg2], 538 'string': col_dict['string'], 539 'replacements': col_dict['replacements']}) 540 541 return color_links 542 545 """given two FKSlegs, returns a dictionary containing: 546 --string: the color link between the two particles, to be appended to 547 the old color string 548 extra minus or 1/2 factor are included as it was done in MadDipole 549 --replacements: a pair of lists containing the replacements of the color 550 indices in the old string to match the link 551 """ 552 #the second-to-last index of the t is the triplet, 553 # the last is the anti-triplet 554 555 legs = FKSLegList([leg1, leg2]) 556 dict = {} 557 min_index = -3000 558 iglu = min_index*2 559 string = color_algebra.ColorString() 560 replacements = [] 561 if pert == 'QCD': 562 if leg1 != leg2: 563 for leg in legs: 564 min_index -= 1 565 num = leg.get('number') 566 replacements.append([num, min_index]) 567 icol = 1 568 if not leg.get('state'): 569 icol = - 1 570 if leg.get('color') * icol == 3: 571 string.product(color_algebra.ColorString([ 572 color_algebra.T(iglu, num, min_index)])) 573 string.coeff = string.coeff * (-1) 574 elif leg.get('color') * icol == - 3: 575 string.product(color_algebra.ColorString([ 576 color_algebra.T(iglu, min_index, num)])) 577 elif leg.get('color') == 8: 578 string.product(color_algebra.ColorString(init_list = [ 579 color_algebra.f(min_index,iglu,num)], 580 is_imaginary =True)) 581 582 else: 583 icol = 1 584 if not leg1.get('state'): 585 icol = - 1 586 num = leg1.get('number') 587 replacements.append([num, min_index -1]) 588 if leg1.get('color') * icol == 3: 589 string = color_algebra.ColorString( 590 [color_algebra.T(iglu, iglu, num, min_index -1)]) 591 elif leg1.get('color') * icol == - 3: 592 string = color_algebra.ColorString( 593 [color_algebra.T(iglu, iglu, min_index-1, num)]) 594 elif leg1.get('color') == 8: 595 string = color_algebra.ColorString(init_list = [ 596 color_algebra.f(min_index-1,iglu,min_index)], 597 is_imaginary =True) 598 string.product(color_algebra.ColorString(init_list = [ 599 color_algebra.f(min_index,iglu,num)], 600 is_imaginary =True)) 601 string.coeff = string.coeff * fractions.Fraction(1, 2) 602 603 elif pert == 'QED': 604 for leg in legs: 605 # make it a fraction 606 string.coeff = string.coeff * fractions.Fraction(leg['charge']*3.)*\ 607 fractions.Fraction(1,3) 608 else: 609 raise FKSProcessError,"Only QCD or QED is allowed not %s"% pert 610 611 dict['replacements'] = replacements 612 dict['string'] = string 613 return dict 614
615 616 -def sort_proc(process,pert = 'QCD'):
617 """Given a process, this function returns the same process 618 but with sorted FKSLegs. 619 """ 620 leglist = to_fks_legs(process.get('legs'), process.get('model')) 621 leglist.sort(pert = pert) 622 for n, leg in enumerate(leglist): 623 leg['number'] = n + 1 624 process['legs'] = leglist 625 # add this line to pass ./test_managers.py -p A test_check_ppzjj 626 process['legs_with_decays']=MG.LegList() 627 628 return process
629
630 631 -def to_leg(fksleg):
632 """Given a FKSLeg, returns the original Leg. 633 """ 634 leg = MG.Leg( \ 635 {'id': fksleg.get('id'), 636 'number': fksleg.get('number'), 637 'state': fksleg.get('state'), 638 'from_group': fksleg.get('from_group'), 639 }) 640 return leg
641
642 643 -def to_legs(fkslegs):
644 """Given a FKSLegList, returns the corresponding LegList. 645 """ 646 leglist = MG.LegList() 647 for leg in fkslegs: 648 leglist.append(to_leg(leg)) 649 return leglist
650
651 652 -def to_fks_leg(leg, model): #test written
653 """Given a leg or a dict with Leg entries, 654 adds color, spin and massless entries, according to model""" 655 fksleg = FKSLeg(leg) 656 part = model.get('particle_dict')[leg['id']] 657 fksleg['color'] = part.get_color() 658 fksleg['charge'] = part.get_charge() 659 fksleg['massless'] = part['mass'].lower() == 'zero' 660 fksleg['spin'] = part.get('spin') 661 fksleg['is_part'] = part.get('is_part') 662 fksleg['self_antipart'] = part.get('self_antipart') 663 return fksleg 664
665 666 -def to_fks_legs(leglist, model): #test written
667 """given leglist, sets color and massless entries according to the model 668 variable. 669 return a FKSLeglist""" 670 fkslegs = FKSLegList() 671 for leg in leglist: 672 fkslegs.append(to_fks_leg(leg, model)) 673 return fkslegs 674
675 676 -class FKSLegList(MG.LegList):
677 """list of FKSLegs""" 678
679 - def is_valid_element(self, obj):
680 """Test if object obj is a valid FKSLeg for the list.""" 681 return isinstance(obj, FKSLeg)
682
683 - def sort(self,pert='QCD'):
684 """Sorting routine, sorting chosen to be optimal for madfks""" 685 sorted_leglist = FKSLegList() 686 #find initial state legs 687 initial_legs = FKSLegList([l for l in copy.copy(self) if not l['state']]) 688 #find final state legs 689 final_legs = FKSLegList([l for l in copy.copy(self) if l['state']]) 690 if len(initial_legs) == 1: 691 sorted_leglist.extend(initial_legs) 692 elif len(initial_legs) == 2: 693 if initial_legs[0]['number'] > initial_legs[1]['number']: 694 initial_legs.reverse() 695 sorted_leglist.extend(initial_legs) 696 else: 697 raise FKSProcessError('Too many initial legs') 698 #find color representations 699 if pert == 'QCD': 700 color = 'color' 701 zero = 1 702 elif pert == 'QED': 703 color = 'charge' 704 zero = 0. 705 else: 706 raise FKSProcessError,"Only QCD and QED is allowed not %s"% pert 707 colors = sorted(set([abs(l[color]) for l in final_legs])) 708 # first put massless particles, without any rearrangment 709 if zero in colors: 710 sorted_leglist.extend(sorted(\ 711 [l for l in final_legs if l[color] == zero], key = itemgetter('number'))) 712 colors.remove(zero) 713 714 #now go for colored legs, put first all massive legs, then all massless legs 715 massless_dict = {} 716 massive_dict = {} 717 for col in colors: 718 col_legs = FKSLegList([l for l in final_legs if abs(l[color]) == col]) 719 #find massive and massless legs in this color repr 720 massive_dict[col] = [l for l in col_legs if not l['massless']] 721 massless_dict[col] = [l for l in col_legs if l['massless']] 722 723 for i_m, dict in enumerate([massive_dict, massless_dict]): 724 for col in colors: 725 # sorting may be different for massive and massless particles 726 # for color singlets, do not change order 727 if col == zero: 728 keys = [itemgetter('number'), itemgetter('number')] 729 reversing = False 730 else: 731 keys = [itemgetter('id'), itemgetter('id')] 732 reversing = True 733 734 init_pdg_legs = [] 735 list = dict[col] 736 if len(initial_legs) == 2: 737 #put first legs which have the same abs(pdg) of the initial ones 738 for i in range(len(set([ abs(l['id']) for l in initial_legs]))): 739 pdg = abs(initial_legs[i]['id']) 740 init_pdg_legs = [l for l in list if abs(l['id']) == pdg] 741 if init_pdg_legs: 742 # sort in order to put first quarks then antiparticles, 743 # and to put fks partons as n j i 744 init_pdg_legs.sort(key = keys[i_m], reverse=reversing) 745 sorted_leglist.extend(FKSLegList(init_pdg_legs)) 746 747 init_pdgs = [ abs(l['id']) for l in initial_legs] 748 other_legs = [l for l in list if not abs(l['id']) in init_pdgs] 749 other_legs.sort(key = keys[i_m], reverse=reversing) 750 sorted_leglist.extend(FKSLegList(other_legs)) 751 else: 752 list.sort(key = keys[i_m], reverse=reversing) 753 sorted_leglist.extend(FKSLegList(list)) 754 755 for i, l in enumerate(sorted_leglist): 756 self[i] = l
757
758 759 760 -class FKSLeg(MG.Leg):
761 """a class for FKS legs: it inherits from the ususal leg class, with two 762 extra keys in the dictionary: 763 -'fks', whose value can be 'i', 'j' or 'n' (for "normal" particles) 764 -'color', which gives the color of the leg 765 -'charge', which gives the charge of the leg 766 -'massless', boolean, true if leg is massless 767 -'spin' which gives the spin of leg 768 -'is_part', boolean, true if leg is an particle 769 -'self_antipart', boolean, true if leg is an self-conjugated particle 770 """ 771
772 - def default_setup(self):
773 """Default values for all properties""" 774 super(FKSLeg, self).default_setup() 775 776 self['fks'] = 'n' 777 self['color'] = 0 778 self['charge'] = 0. 779 self['massless'] = True 780 self['spin'] = 0 781 self['is_part'] = True 782 self['self_antipart'] = False
783
784 - def get_sorted_keys(self):
785 """Return particle property names as a nicely sorted list.""" 786 keys = super(FKSLeg, self).get_sorted_keys() 787 keys += ['fks', 'color','charge', 'massless', 'spin','is_part','self_antipart'] 788 return keys
789 790
791 - def filter(self, name, value):
792 """Filter for valid leg property values.""" 793 794 if name == 'fks': 795 if not isinstance(value, str): 796 raise self.PhysicsObjectError, \ 797 "%s is not a valid string for leg fks flag" \ 798 % str(value) 799 if name in ['color', 'spin']: 800 if not isinstance(value, int): 801 raise self.PhysicsObjectError, \ 802 "%s is not a valid leg %s flag" % \ 803 str(value),name 804 805 if name in ['massless','self_antipart','is_part']: 806 if not isinstance(value, bool): 807 raise self.PhysicsObjectError, \ 808 "%s is not a valid boolean for leg flag %s" % \ 809 str(value),name 810 if name is 'charge': 811 if not isinstance(value, float): 812 raise self.PhysicsObjectError, \ 813 "%s is not a valid float for leg flag charge" \ 814 % str(value) 815 return super(FKSLeg,self).filter(name, value)
816