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