1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Definitions of the objects needed both for MadFKS from real
17 and MadFKS from born"""
18
19 from __future__ import absolute_import
20 from __future__ import print_function
21 import madgraph.core.base_objects as MG
22 import madgraph.core.helas_objects as helas_objects
23 import madgraph.core.diagram_generation as diagram_generation
24 import madgraph.core.color_amp as color_amp
25 import madgraph.core.color_algebra as color_algebra
26 import madgraph.various.misc as misc
27 from operator import itemgetter
28 import copy
29 import logging
30 import array
31 import fractions
32 import six
33 from six.moves import range
38 """Exception for MadFKS"""
39 pass
40
43 """Modified diagram tags to be used to link born and real configurations.
44 """
45
46 @staticmethod
48 """Returns the default end link for a leg: ((id, number), number).
49 Note that the number is not taken into account if tag comparison,
50 but is used only to extract leg permutations.
51 """
52 return [((leg.get('id'), leg.get('number')), leg.get('number'))]
53
56 """computes the QED/QCD orders from the knowledge of the n of ext particles
57 and of the weighted orders"""
58 qed_w = hierarchy['QED']
59 qcd_w = hierarchy['QCD']
60
61
62
63 QED = (weighted - qcd_w * (nexternal - 2) ) / (qed_w - qcd_w)
64 QCD = (weighted - qed_w * QED) / qcd_w
65 return int(QED), int(QCD)
66
69 """finds the real configurations that match the born ones, i.e. for
70 each born configuration, the real configuration that has the ij ->
71 i j splitting. i, j and ij are integers, and refer to the leg
72 position in the real process (i, j) and in the born process (ij).
73 """
74
75 id_ij = born_amp['process']['legs'][ij - 1]['id']
76 nlegs_b = len(born_amp['process']['legs'])
77 nlegs_r = len(real_amp['process']['legs'])
78 if nlegs_r - nlegs_b != 1:
79 raise FKSProcessError('Inconsistent number of born and real legs: %d %d' % (nlegs_b, nlegs_r))
80
81
82 shift_dict = {}
83 for ir in range(1, nlegs_r + 1):
84 shift = 0
85 if ir > j:
86 shift += 1
87 if ir > i:
88 shift += 1
89 if ir > ij and ij <= max(i,j):
90 shift -= 1
91 shift_dict[ir] = ir - shift
92
93
94 minvert = min([max([len(vert.get('legs')) \
95 for vert in diag.get('vertices')]) \
96 for diag in born_amp.get('diagrams')])
97
98 born_confs = []
99 real_confs = []
100
101 k=0
102 for diag in born_amp.get('diagrams'):
103 if any([len(vert.get('legs')) > minvert for vert in
104 diag.get('vertices')]):
105 continue
106 else:
107 born_confs.append({'number' : k, 'diagram' : diag})
108 k=k+1
109
110 k=0
111 for diag in real_amp.get('diagrams'):
112 if any([len(vert.get('legs')) > minvert \
113 for vert in diag.get('vertices')]):
114 continue
115 else:
116 real_confs.append({'number': k, 'diagram': diag})
117 k=k+1
118
119 good_diags = []
120
121
122
123
124 real_confs_new = copy.deepcopy(real_confs)
125 for diag in real_confs_new:
126 for vert in diag['diagram'].get('vertices'):
127 vert_legs = [l.get('number') for l in vert.get('legs')]
128 vert_ids = [l.get('id') for l in vert.get('legs')]
129 if (i in vert_legs and not j in vert_legs) or \
130 (j in vert_legs and not i in vert_legs):
131 break
132
133 if i in vert_legs and j in vert_legs:
134 vert_ids.remove(vert_ids[vert_legs.index(i)])
135 vert_legs.remove(i)
136 vert_ids.remove(vert_ids[vert_legs.index(j)])
137 vert_legs.remove(j)
138 last_leg = vert_legs[0]
139
140
141 if abs(vert_ids[0]) == abs(id_ij):
142 diag['diagram']['vertices'].remove(vert)
143 good_diags.append({'diagram': diag['diagram'],
144 'leg_ij': last_leg,
145 'number': diag['number']})
146 break
147
148
149
150
151
152
153
154
155
156
157
158 for ir in range(1, nlegs_r + 1):
159 for good_diag in good_diags:
160 for vert in good_diag['diagram'].get('vertices'):
161 for l in vert.get('legs'):
162 if l.get('number') == ir:
163 l.set('number', shift_dict[l.get('number')])
164
165
166 if len(good_diags) == 1 and len(born_confs) == 1:
167 return [{'real_conf': good_diags[0]['number'],
168 'born_conf': born_confs[0]['number']}]
169
170
171
172 if nlegs_b ==3:
173 for diag in good_diags:
174 counts = []
175 for il in range(nlegs_b):
176 counts.append([l['number'] for v in diag['diagram']['vertices'] for l in v['legs']].count(il+1))
177
178
179 even_list = [c / 2 * 2 == c for c in counts]
180 if any(even_list):
181 if not even_list.count(True) == 2:
182 raise FKSProcessError('Linking: Don\'t know what to do in this case')
183
184 ilmax = counts.index(max([c for c in counts if even_list[counts.index(c)]]))
185 ilmin = counts.index(min([c for c in counts if even_list[counts.index(c)]]))
186
187
188 replaced = False
189 for vert in diag['diagram']['vertices']:
190 for leg in vert['legs']:
191 if leg['number'] == ilmax + 1 and not replaced:
192 leg['number'] = ilmin + 1
193 replaced = True
194
195
196 born_tags = [FKSDiagramTag(d['diagram'],
197 born_amp.get('process').get('model')) \
198 for d in born_confs]
199
200
201 real_tags = [FKSDiagramTag(d['diagram'],
202 real_amp.get('process').get('model')) \
203 for d in good_diags ]
204 real_tags = []
205 for d in good_diags:
206 tag = FKSDiagramTag(d['diagram'], real_amp.get('process').get('model'))
207 if not tag in real_tags:
208 real_tags.append(tag)
209
210
211 if len(born_tags) != len(real_tags):
212 print('\n'.join([str(r) for r in real_tags]) + '\n')
213 raise FKSProcessError('Cannot map born/real configurations between \
214 %s and %s (i,j=%d,%d): not same number of configurations: %d %d' % \
215 (born_amp.get('process').nice_string().replace('Process:',''),
216 real_amp.get('process').nice_string().replace('Process:',''),
217 i,j,
218 len(born_tags),
219 len(real_tags)))
220
221 links = []
222 for ib, btag in enumerate(born_tags):
223 try:
224 ir = real_tags.index(btag)
225 links.append({'real_conf': good_diags[ir]['number'],
226 'born_conf': born_confs[ib]['number']})
227 real_tags.remove(btag)
228 good_diags.pop(ir)
229 except ValueError:
230 print(real_tags, i, j, ij)
231 print('\n'.join( d['diagram'].nice_string() for d in good_diags))
232 raise FKSProcessError('Linking %s to %s: could not link born diagram %s' % \
233 (born_amp.get('process').nice_string().replace('Process:',''),
234 real_amp.get('process').nice_string().replace('Process:',''),
235 born_confs[ib]['diagram'].nice_string()) )
236
237 return links
238
242 """Takes an amplitude as input, and returns a dictionary with the
243 orders of the couplings.
244 """
245 assert isinstance(amp, diagram_generation.Amplitude)
246 orders = {}
247 for diag in amp.get('diagrams'):
248 for order, value in diag.get('orders').items():
249 if value != 0 or order in list(amp['process']['orders'].keys()):
250 try:
251 orders[order] = max(orders[order], value)
252 except KeyError:
253 orders[order] = value
254 return orders
255
256
257 -def find_splittings(leg, model, dict, pert='QCD', include_init_leptons=True):
258 """Finds the possible splittings corresponding to leg
259 """
260
261 leptons = model.get_lepton_pdgs()
262
263 if dict == {}:
264 dict = find_pert_particles_interactions(model, pert)
265 splittings = []
266
267
268 if leg.get('id') in dict['pert_particles']:
269 part = model.get('particle_dict')[leg.get('id')]
270 antipart = model.get('particle_dict')[part.get_anti_pdg_code()]
271 for ii in dict['interactions']:
272
273 parts = copy.deepcopy(ii['particles'])
274 nsoft = 0
275 if part in parts:
276
277
278 try:
279 parts.pop(parts.index(antipart))
280 except ValueError:
281 parts.pop(parts.index(part))
282 for p in parts:
283 if p.get_pdg_code() in dict['soft_particles']:
284 nsoft += 1
285 if nsoft >= 1:
286 for split in split_leg(leg, parts, model):
287
288
289
290 if include_init_leptons or \
291 not (any([l['id'] in leptons for l in split if not l['state']])):
292 splittings.append(split)
293 return splittings
294
295
296 -def find_mothers(leg1, leg2, model, dict={}, pert='', mom_mass=''):
297 """Find the possible mothers of leg1, leg2.
298 If mom_mass is passed, only the mothers with mom_mass are returned
299 """
300 if pert:
301 if dict == {}:
302 dict = find_pert_particles_interactions(model, pert)
303 interactions = dict['interactions']
304 mothers = []
305
306 for inte in interactions:
307
308
309 pdgs = [p.get_pdg_code() for p in inte['particles']]
310 try:
311 for l in [leg1, leg2]:
312 if not l['state']:
313 pdgs.remove(l['id'])
314 else:
315 pdgs.remove(model.get('particle_dict')[l['id']].get_anti_pdg_code())
316 except ValueError:
317 continue
318 if mom_mass and \
319 mom_mass.lower() == model.get('particle_dict')[pdgs[0]]['mass'].lower():
320 mothers.append(pdgs[0])
321
322 return mothers
323
326 """Splits the leg into parts, and returns the two new legs.
327 """
328
329 split = []
330
331 if leg['state'] :
332 split.append([])
333 for part in parts:
334 split[-1].append(to_fks_leg({'state': True, \
335 'id': part.get_pdg_code()},model))
336 ij_final(split[-1])
337
338
339 else:
340 if parts[0] != parts[1]:
341 for part in parts:
342 cparts = copy.deepcopy(parts)
343 split.append([\
344 to_fks_leg({'state': False,
345 'id': cparts.pop(cparts.index(part)).get_pdg_code(),
346 'fks': 'j'}, model),
347 to_fks_leg({'state': True,
348 'id': cparts[0].get_anti_pdg_code(),
349 'fks': 'i'}, model)\
350 ])
351 else:
352 split.append([\
353 to_fks_leg({'state': False,
354 'id': parts[0].get_pdg_code(),
355 'fks': 'j'}, model),
356 to_fks_leg({'state': True,
357 'id': parts[1].get_anti_pdg_code(),
358 'fks': 'i'}, model)])
359 return split
360
363 """given a pair of legs in the final state, assigns the i/j fks id
364 NOTE: the j partons is always put before the i one
365 """
366
367
368
369 if len(pair) == 2:
370 for i in range(len(pair)):
371 set = 0
372 if (pair[i]['massless'] and pair[i]['self_antipart']) or \
373 (not pair[i]['is_part'] and pair[1-i]['is_part'] and\
374 (pair[i]['spin']+pair[1-i]['spin'])%2==0) and not set:
375 pair[i]['fks'] = 'i'
376 pair[1-i]['fks'] = 'j'
377
378 if i < 1 - i:
379 pair.reverse()
380 set = 1
381
383 """Returns a new leglist with leg splitted into split.
384 The convention is to remove leg ij, replace it with leg j, and put
385 i at the end of the group of legs with the same color(charge) representation
386 """
387 if pert =='QCD':
388 color = 'color'
389 elif pert == 'QED':
390 color = 'charge'
391 else:
392 raise FKSProcessError("Only QCD or QED is allowed not %s" % pert)
393
394 leglist = FKSLegList(copy.deepcopy(leglist_orig))
395
396 for i in range(len(leglist)):
397 if leglist[-i - 1].get('state'):
398 firstfinal = len(leglist) - i - 1
399
400 leglist[leglist.index(leg)] = split[0]
401
402 col_maxindex = {}
403 mass_col_maxindex = {}
404 for col in set([l[color] for l in leglist[firstfinal:] if l['massless']]):
405 col_maxindex[col] = max([0] + [leglist.index(l) for l in leglist[firstfinal:]\
406 if l[color] == col and l['massless']])
407 for col in set([abs(l[color]) for l in leglist[firstfinal:] if not l['massless']]):
408 mass_col_maxindex[col] = max([0] + [leglist.index(l) for l in leglist[firstfinal:]\
409 if abs(l[color]) == col and not l['massless']])
410
411 if pert == 'QCD':
412 for col in copy.copy(list(col_maxindex.keys())):
413 if abs(col) > abs(split[1][color]):
414 del col_maxindex[col]
415
416
417
418
419 if split[1]['is_part'] and not split[1]['self_antipart']:
420
421
422
423
424 try:
425 del col_maxindex[-split[1][color]]
426 except KeyError:
427 pass
428
429 leglist.insert(max(list(col_maxindex.values()) + list(mass_col_maxindex.values()) + [firstfinal - 1] ) + 1, split[1])
430
431
432
433
434
435
436
437
438
439
440
441 for i, leg in enumerate(leglist):
442 leg['number'] = i + 1
443 return leglist
444
445
446 -def combine_ij( i, j, model, dict, pert='QCD'):
447 """checks whether FKSlegs i and j can be combined together in the given model
448 and with given perturbation order and if so combines them into ij.
449 If dict is empty it is initialized with find_pert_particles_interactions
450 """
451 if dict == {}:
452 dict = find_pert_particles_interactions(model, pert)
453 ij = []
454 num = copy.copy(min(i.get('number'), j.get('number')))
455
456
457 not_double_counting = (j.get('spin') == 3 and j.get('massless') and
458 i.get('spin') == 3 and i.get('massless')) or \
459 j.get('spin') != 3 or not j.get('massless') or \
460 not j.get('state')
461
462
463
464 if j.get('state') and j.get('id') == - i.get('id'):
465 not_double_counting = not_double_counting and j.get('id') >0
466
467 if i.get('id') in dict['soft_particles'] and \
468 j.get('id') in dict['pert_particles'] and \
469 i.get('state') and not_double_counting:
470 for int in dict['interactions']:
471 parts= copy.copy(int['particles'])
472
473 try:
474 parts.remove(model.get('particle_dict')[i.get('id')])
475 except ValueError:
476 continue
477
478
479 if j.get('state'):
480 j_id = j.get('id')
481 else:
482 j_id = model.get('particle_dict')[j.get('id')].get_anti_pdg_code()
483 try:
484 parts.remove(model.get('particle_dict')[j_id])
485 except ValueError:
486 continue
487
488
489 if j.get('state'):
490 ij.append(MG.Leg({
491 'id': parts[0].get_anti_pdg_code(),
492 'state': True,
493 'number': num}))
494 else:
495 ij.append(MG.Leg({
496 'id': parts[0].get_pdg_code(),
497 'state': False,
498 'number': num}))
499 return to_fks_legs(ij, model)
500
501
502 -def find_pert_particles_interactions(model, pert_order = 'QCD'):
503 """given a model and pert_order, returns a dictionary with as entries:
504 --interactions : the interactions of order pert_order
505 --pert_particles : pdgs of particles taking part to interactions
506 --soft_particles : pdgs of massless particles in pert_particles
507 """
508
509 ghost_list = []
510 ghost_list += [ p.get_pdg_code() for p in model.get('particles')
511 if p.get('ghost') or p.get('goldstone')]
512
513 qcd_inter = MG.InteractionList()
514 pert_parts = []
515 soft_parts = []
516 for i, ii in model.get('interaction_dict').items():
517
518
519
520 if ii.get('type') != 'base': continue
521
522 if ii.get('orders') == {pert_order:1} and len(ii['particles']) == 3 :
523 masslist = [p.get('mass').lower() for p in ii['particles']]
524
525
526 found_soft_even_spin_particle = False
527 for p in ii['particles']:
528 if p.get('mass').lower()=='zero':
529 if p.get('spin')%2==1:
530 found_soft_even_spin_particle = True
531 break
532 if not found_soft_even_spin_particle:
533 continue
534
535
536
537
538
539 try:
540 masslist.remove('zero')
541 except ValueError:
542 continue
543 if len(set(masslist)) == 1 and not \
544 any( [ p.get_pdg_code() in ghost_list or \
545 p.get_anti_pdg_code() in ghost_list for p in ii['particles']]) :
546 qcd_inter.append(ii)
547 for pp in ii['particles']:
548 pert_parts.append(pp.get_pdg_code())
549 if pp['mass'].lower() == 'zero':
550 soft_parts.append(pp.get_pdg_code())
551
552 return {'interactions': sorted(qcd_inter, key=misc.cmp_to_key(misc.dict_cmp)),
553 'pert_particles': sorted(set(pert_parts)),
554 'soft_particles': sorted(set(soft_parts))}
555
558 """insert the color links in col_obj: returns a list of dictionaries
559 (one for each link) with the following entries:
560 --link: the numbers of the linked legs
561 --link_basis: the linked color basis
562 --link_matrix: the color matrix created from the original basis and the linked one
563 """
564 assert isinstance(col_basis, color_amp.ColorBasis)
565 assert isinstance(col_obj, list)
566 result =[]
567 for link in links:
568 this = {}
569
570 l =[]
571 for leg in link['legs']:
572 l.append(leg.get('number'))
573 this['link'] = l
574
575
576
577
578
579 this_col_obj = []
580 for old_dict in col_obj:
581 new_dict = dict(old_dict)
582 for k, string in new_dict.items():
583 new_dict[k] = string.create_copy()
584 for col in new_dict[k]:
585 for ind in col:
586 for pair in link['replacements']:
587 if ind == pair[0]:
588 col[col.index(ind)] = pair[1]
589 new_dict[k].product(link['string'])
590 this_col_obj.append(new_dict)
591 basis_link = color_amp.ColorBasis()
592 for ind, new_dict in enumerate(this_col_obj):
593 basis_link.update_color_basis(new_dict, ind)
594
595 this['link_basis'] = basis_link
596 this['link_matrix'] = color_amp.ColorMatrix(col_basis,basis_link)
597 result.append(this)
598 basis_orig = color_amp.ColorBasis()
599 for ind, new_dict in enumerate(col_obj):
600 basis_orig.update_color_basis(new_dict, ind)
601
602 for link in result:
603 link['orig_basis'] = basis_orig
604 return result
605
609 """Finds all the possible color(charge) links between any
610 two legs of the born.
611 If symm is true, only half of the color links are generated, those
612 for which leg1['number'] <= leg2['number']
613 """
614 if pert == 'QCD':
615 color = 'color'
616 zero = 1
617 elif pert == 'QED':
618 color = 'charge'
619 zero = 0.
620 else:
621 raise FKSProcessError("Only QCD or QED is allowed not %s" % pert)
622 color_links = []
623 for leg1 in leglist:
624 for leg2 in leglist:
625
626 if (leg1.get(color) != zero and leg2.get(color) != zero) \
627 and (leg1 != leg2 or not leg1.get('massless')):
628 if not symm or leg1['number'] <= leg2['number']:
629 col_dict = legs_to_color_link_string(leg1,leg2,pert = pert)
630 color_links.append({
631 'legs': [leg1, leg2],
632 'string': col_dict['string'],
633 'replacements': col_dict['replacements']})
634
635 return color_links
636
639 """given two FKSlegs, returns a dictionary containing:
640 --string: the color link between the two particles, to be appended to
641 the old color string
642 extra minus or 1/2 factor are included as it was done in MadDipole
643 --replacements: a pair of lists containing the replacements of the color
644 indices in the old string to match the link
645 """
646
647
648
649 legs = FKSLegList([leg1, leg2])
650 dict = {}
651 min_index = -3000
652 iglu = min_index*2
653 string = color_algebra.ColorString()
654 replacements = []
655 if pert == 'QCD':
656 if leg1 != leg2:
657 for leg in legs:
658 min_index -= 1
659 num = leg.get('number')
660 replacements.append([num, min_index])
661 icol = 1
662 if not leg.get('state'):
663 icol = - 1
664 if leg.get('color') * icol == 3:
665 string.product(color_algebra.ColorString([
666 color_algebra.T(iglu, num, min_index)]))
667 string.coeff = string.coeff * (-1)
668 elif leg.get('color') * icol == - 3:
669 string.product(color_algebra.ColorString([
670 color_algebra.T(iglu, min_index, num)]))
671 elif leg.get('color') == 8:
672 string.product(color_algebra.ColorString(init_list = [
673 color_algebra.f(min_index,iglu,num)],
674 is_imaginary =True))
675
676 else:
677 icol = 1
678 if not leg1.get('state'):
679 icol = - 1
680 num = leg1.get('number')
681 replacements.append([num, min_index -1])
682 if leg1.get('color') * icol == 3:
683 string = color_algebra.ColorString(
684 [color_algebra.T(iglu, iglu, num, min_index -1)])
685 elif leg1.get('color') * icol == - 3:
686 string = color_algebra.ColorString(
687 [color_algebra.T(iglu, iglu, min_index-1, num)])
688 elif leg1.get('color') == 8:
689 string = color_algebra.ColorString(init_list = [
690 color_algebra.f(min_index-1,iglu,min_index)],
691 is_imaginary =True)
692 string.product(color_algebra.ColorString(init_list = [
693 color_algebra.f(min_index,iglu,num)],
694 is_imaginary =True))
695 string.coeff = string.coeff * fractions.Fraction(1, 2)
696
697 elif pert == 'QED':
698 for leg in legs:
699
700 string.coeff = string.coeff * fractions.Fraction(leg['charge']*3.)*\
701 fractions.Fraction(1,3)
702 else:
703 raise FKSProcessError("Only QCD or QED is allowed not %s"% pert)
704
705 dict['replacements'] = replacements
706 dict['string'] = string
707 return dict
708
711 """Given a process, this function returns the same process
712 but with sorted FKSLegs.
713 """
714 leglist = to_fks_legs(process.get('legs'), process.get('model'))
715 leglist.sort(pert = pert)
716 for n, leg in enumerate(leglist):
717 leg['number'] = n + 1
718 process['legs'] = leglist
719
720 process['legs_with_decays']=MG.LegList()
721
722 return process
723
726 """Given a FKSLeg, returns the original Leg.
727 """
728 leg = MG.Leg( \
729 {'id': fksleg.get('id'),
730 'number': fksleg.get('number'),
731 'state': fksleg.get('state'),
732 'from_group': fksleg.get('from_group'),
733 'polarization': fksleg.get('polarization')
734 })
735 return leg
736
739 """Given a FKSLegList, returns the corresponding LegList.
740 """
741 leglist = MG.LegList()
742 for leg in fkslegs:
743 leglist.append(to_leg(leg))
744 return leglist
745
748 """Given a leg or a dict with Leg entries,
749 adds color, spin and massless entries, according to model"""
750 fksleg = FKSLeg(leg)
751 part = model.get('particle_dict')[leg['id']]
752 fksleg['color'] = part.get_color()
753 fksleg['charge'] = part.get_charge()
754 fksleg['massless'] = part['mass'].lower() == 'zero'
755 fksleg['spin'] = part.get('spin')
756 fksleg['is_part'] = part.get('is_part')
757 fksleg['self_antipart'] = part.get('self_antipart')
758 return fksleg
759
762 """given leglist, sets color and massless entries according to the model
763 variable.
764 return a FKSLeglist"""
765 fkslegs = FKSLegList()
766 for leg in leglist:
767 fkslegs.append(to_fks_leg(leg, model))
768 return fkslegs
769
772 """list of FKSLegs"""
773
775 """Test if object obj is a valid FKSLeg for the list."""
776 return isinstance(obj, FKSLeg)
777
778
779 - def sort(self,pert='QCD'):
780 """Sorting routine, sorting chosen to be optimal for madfks"""
781 sorted_leglist = FKSLegList()
782
783 initial_legs = FKSLegList([l for l in copy.copy(self) if not l['state']])
784
785 final_legs = FKSLegList([l for l in copy.copy(self) if l['state']])
786 if len(initial_legs) == 1:
787 sorted_leglist.extend(initial_legs)
788 elif len(initial_legs) == 2:
789 if initial_legs[0]['number'] > initial_legs[1]['number']:
790 initial_legs.reverse()
791 sorted_leglist.extend(initial_legs)
792 else:
793 raise FKSProcessError('Too many initial legs')
794
795
796
797 massive_legs = [l for l in final_legs if not l['massless']]
798 massless_legs = [l for l in final_legs if l['massless']]
799
800 for leglist in [massive_legs, massless_legs]:
801 spins = sorted(set([abs(l['spin']) for l in leglist]))
802 for spin in spins:
803 spin_legs = FKSLegList([l for l in leglist if abs(l['spin']) == spin])
804 init_pdg_legs = []
805 if len(initial_legs) == 2:
806
807 for j in range(len(set([ abs(l['id']) for l in initial_legs]))):
808 pdg = abs(initial_legs[j]['id'])
809 init_pdg_legs = [l for l in spin_legs if abs(l['id']) == pdg]
810 if init_pdg_legs:
811
812
813 init_pdg_legs.sort(key = itemgetter('id'), reverse=True)
814 sorted_leglist.extend(FKSLegList(init_pdg_legs))
815
816 init_pdgs = [ abs(l['id']) for l in initial_legs]
817 other_legs = [l for l in spin_legs if not abs(l['id']) in init_pdgs]
818 other_legs.sort(key = itemgetter('id'), reverse=True)
819 sorted_leglist.extend(FKSLegList(other_legs))
820 else:
821
822 sorted_leglist.extend(FKSLegList(spin_legs))
823
824 for i, l in enumerate(sorted_leglist):
825 self[i] = l
826
829 """a class for FKS legs: it inherits from the ususal leg class, with two
830 extra keys in the dictionary:
831 -'fks', whose value can be 'i', 'j' or 'n' (for "normal" particles)
832 -'color', which gives the color of the leg
833 -'charge', which gives the charge of the leg
834 -'massless', boolean, true if leg is massless
835 -'spin' which gives the spin of leg
836 -'is_part', boolean, true if leg is an particle
837 -'self_antipart', boolean, true if leg is an self-conjugated particle
838 """
839
841 """Default values for all properties"""
842 super(FKSLeg, self).default_setup()
843
844 self['fks'] = 'n'
845 self['color'] = 0
846 self['charge'] = 0.
847 self['massless'] = True
848 self['spin'] = 0
849 self['is_part'] = True
850 self['self_antipart'] = False
851
853 """Return particle property names as a nicely sorted list."""
854 keys = super(FKSLeg, self).get_sorted_keys()
855 keys += ['fks', 'color','charge', 'massless', 'spin','is_part','self_antipart']
856 return keys
857
858
859 - def filter(self, name, value):
860 """Filter for valid leg property values."""
861
862 if name == 'fks':
863 if not isinstance(value, str):
864 raise self.PhysicsObjectError("%s is not a valid string for leg fks flag" \
865 % str(value))
866 if name in ['color', 'spin']:
867 if not isinstance(value, int):
868 six.reraise(self.PhysicsObjectError, "%s is not a valid leg %s flag" % \
869 str(value), name)
870
871 if name in ['massless','self_antipart','is_part']:
872 if not isinstance(value, bool):
873 six.reraise(self.PhysicsObjectError, "%s is not a valid boolean for leg flag %s" % \
874 str(value), name)
875 if name == 'charge':
876 if not isinstance(value, float):
877 raise self.PhysicsObjectError("%s is not a valid float for leg flag charge" \
878 % str(value))
879 return super(FKSLeg,self).filter(name, value)
880