1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Definitions of all basic objects used in the core code: particle,
16 interaction, model, leg, vertex, process, ..."""
17
18 import copy
19 import itertools
20 import logging
21 import math
22 import numbers
23 import os
24 import re
25 import StringIO
26 import madgraph.core.color_algebra as color
27 from madgraph import MadGraph5Error, MG5DIR, InvalidCmd
28 import madgraph.various.misc as misc
29
30
31 logger = logging.getLogger('madgraph.base_objects')
32 pjoin = os.path.join
38 """A parent class for all physics objects."""
39
41 """Exception raised if an error occurs in the definition
42 or the execution of a physics object."""
43 pass
44
46 """Creates a new particle object. If a dictionary is given, tries to
47 use it to give values to properties."""
48
49 dict.__init__(self)
50 self.default_setup()
51
52 assert isinstance(init_dict, dict), \
53 "Argument %s is not a dictionary" % repr(init_dict)
54
55
56 for item in init_dict.keys():
57 self.set(item, init_dict[item])
58
59
61 """ force the check that the property exist before returning the
62 value associated to value. This ensure that the correct error
63 is always raise
64 """
65
66 try:
67 return dict.__getitem__(self, name)
68 except KeyError:
69 self.is_valid_prop(name)
70
71
73 """Function called to create and setup default values for all object
74 properties"""
75 pass
76
78 """Check if a given property name is valid"""
79
80 assert isinstance(name, str), \
81 "Property name %s is not a string" % repr(name)
82
83 if name not in self.keys():
84 raise self.PhysicsObjectError, \
85 """%s is not a valid property for this object: %s\n
86 Valid property are %s""" % (name,self.__class__.__name__, self.keys())
87 return True
88
89 - def get(self, name):
90 """Get the value of the property name."""
91
92 return self[name]
93
94 - def set(self, name, value, force=False):
95 """Set the value of the property name. First check if value
96 is a valid value for the considered property. Return True if the
97 value has been correctly set, False otherwise."""
98 if not __debug__ or force:
99 self[name] = value
100 return True
101
102 if self.is_valid_prop(name):
103 try:
104 self.filter(name, value)
105 self[name] = value
106 return True
107 except self.PhysicsObjectError, why:
108 logger.warning("Property " + name + " cannot be changed:" + \
109 str(why))
110 return False
111
112 - def filter(self, name, value):
113 """Checks if the proposed value is valid for a given property
114 name. Returns True if OK. Raises an error otherwise."""
115
116 return True
117
119 """Returns the object keys sorted in a certain way. By default,
120 alphabetical."""
121
122 return self.keys().sort()
123
125 """String representation of the object. Outputs valid Python
126 with improved format."""
127
128 mystr = '{\n'
129 for prop in self.get_sorted_keys():
130 if isinstance(self[prop], str):
131 mystr = mystr + ' \'' + prop + '\': \'' + \
132 self[prop] + '\',\n'
133 elif isinstance(self[prop], float):
134 mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop]
135 else:
136 mystr = mystr + ' \'' + prop + '\': ' + \
137 repr(self[prop]) + ',\n'
138 mystr = mystr.rstrip(',\n')
139 mystr = mystr + '\n}'
140
141 return mystr
142
143 __repr__ = __str__
144
150 """A class to store lists of physics object."""
151
153 """Exception raised if an error occurs in the definition
154 or execution of a physics object list."""
155 pass
156
158 """Creates a new particle list object. If a list of physics
159 object is given, add them."""
160
161 list.__init__(self)
162
163 if init_list is not None:
164 for object in init_list:
165 self.append(object)
166
168 """Appends an element, but test if valid before."""
169
170 assert self.is_valid_element(object), \
171 "Object %s is not a valid object for the current list" % repr(object)
172
173 list.append(self, object)
174
175
177 """Test if object obj is a valid element for the list."""
178 return True
179
181 """String representation of the physics object list object.
182 Outputs valid Python with improved format."""
183
184 mystr = '['
185
186 for obj in self:
187 mystr = mystr + str(obj) + ',\n'
188
189 mystr = mystr.rstrip(',\n')
190
191 return mystr + ']'
192
193
194
195
196 -class Particle(PhysicsObject):
197 """The particle object containing the whole set of information required to
198 univocally characterize a given type of physical particle: name, spin,
199 color, mass, width, charge,... The is_part flag tells if the considered
200 particle object is a particle or an antiparticle. The self_antipart flag
201 tells if the particle is its own antiparticle."""
202
203 sorted_keys = ['name', 'antiname', 'spin', 'color',
204 'charge', 'mass', 'width', 'pdg_code',
205 'line', 'propagator',
206 'is_part', 'self_antipart', 'type', 'counterterm']
207
208 - def default_setup(self):
209 """Default values for all properties"""
210
211 self['name'] = 'none'
212 self['antiname'] = 'none'
213 self['spin'] = 1
214 self['color'] = 1
215 self['charge'] = 1.
216 self['mass'] = 'ZERO'
217 self['width'] = 'ZERO'
218 self['pdg_code'] = 0
219
220
221 self['line'] = 'dashed'
222
223 self['propagator'] = ''
224 self['is_part'] = True
225 self['self_antipart'] = False
226
227
228 self['type'] = ''
229
230
231 self['counterterm'] = {}
232
233 - def get(self, name):
234
235 if name == 'ghost':
236 return self['type'] == 'ghost'
237 elif name == 'goldstone':
238 return self['type'] == 'goldstone'
239 elif name == 'propagating':
240 return self['line'] is not None
241 else:
242 return super(Particle, self).get(name)
243
244 - def set(self, name, value, force=False):
245
246 if name in ['texname', 'antitexname']:
247 return True
248 elif name == 'propagating':
249 if not value:
250 return self.set('line', None, force=force)
251 elif not self.get('line'):
252 return self.set('line', 'dashed',force=force)
253 return True
254 elif name in ['ghost', 'goldstone']:
255 if self.get('type') == name:
256 if value:
257 return True
258 else:
259 return self.set('type', '', force=force)
260 else:
261 if value:
262 return self.set('type', name, force=force)
263 else:
264 return True
265 return super(Particle, self).set(name, value,force=force)
266
267
268 - def filter(self, name, value):
269 """Filter for valid particle property values."""
270
271 if name in ['name', 'antiname']:
272
273 p=re.compile('''^[\w\-\+~_]+$''')
274 if not p.match(value):
275 raise self.PhysicsObjectError, \
276 "%s is not a valid particle name" % value
277
278 if name is 'ghost':
279 if not isinstance(value,bool):
280 raise self.PhysicsObjectError, \
281 "%s is not a valid bool for the 'ghost' attribute" % str(value)
282
283 if name is 'counterterm':
284 if not isinstance(value,dict):
285 raise self.PhysicsObjectError, \
286 "counterterm %s is not a valid dictionary" % repr(value)
287 for key, val in value.items():
288 if not isinstance(key,tuple):
289 raise self.PhysicsObjectError, \
290 "key %s is not a valid tuple for counterterm key" % repr(key)
291 if not isinstance(key[0],str):
292 raise self.PhysicsObjectError, \
293 "%s is not a valid string" % repr(key[0])
294 if not isinstance(key[1],tuple):
295 raise self.PhysicsObjectError, \
296 "%s is not a valid list" % repr(key[1])
297 for elem in key[1]:
298 if not isinstance(elem,tuple):
299 raise self.PhysicsObjectError, \
300 "%s is not a valid list" % repr(elem)
301 for partPDG in elem:
302 if not isinstance(partPDG,int):
303 raise self.PhysicsObjectError, \
304 "%s is not a valid integer for PDG" % repr(partPDG)
305 if partPDG<=0:
306 raise self.PhysicsObjectError, \
307 "%s is not a valid positive PDG" % repr(partPDG)
308 if not isinstance(val,dict):
309 raise self.PhysicsObjectError, \
310 "value %s is not a valid dictionary for counterterm value" % repr(val)
311 for vkey, vvalue in val.items():
312 if vkey not in [0,-1,-2]:
313 raise self.PhysicsObjectError, \
314 "Key %s is not a valid laurent serie order" % repr(vkey)
315 if not isinstance(vvalue,str):
316 raise self.PhysicsObjectError, \
317 "Coupling %s is not a valid string" % repr(vvalue)
318 if name is 'spin':
319 if not isinstance(value, int):
320 raise self.PhysicsObjectError, \
321 "Spin %s is not an integer" % repr(value)
322 if (value < 1 or value > 5) and value != 99:
323 raise self.PhysicsObjectError, \
324 "Spin %i not valid" % value
325
326 if name is 'color':
327 if not isinstance(value, int):
328 raise self.PhysicsObjectError, \
329 "Color %s is not an integer" % repr(value)
330 if value not in [1, 3, 6, 8]:
331 raise self.PhysicsObjectError, \
332 "Color %i is not valid" % value
333
334 if name in ['mass', 'width']:
335
336 p = re.compile('\A[a-zA-Z]+[\w\_]*\Z')
337 if not p.match(value):
338 raise self.PhysicsObjectError, \
339 "%s is not a valid name for mass/width variable" % \
340 value
341
342 if name is 'pdg_code':
343 if not isinstance(value, int):
344 raise self.PhysicsObjectError, \
345 "PDG code %s is not an integer" % repr(value)
346
347 if name is 'line':
348 if not isinstance(value, str):
349 raise self.PhysicsObjectError, \
350 "Line type %s is not a string" % repr(value)
351 if value not in ['dashed', 'straight', 'wavy', 'curly', 'double','swavy','scurly','dotted']:
352 raise self.PhysicsObjectError, \
353 "Line type %s is unknown" % value
354
355 if name is 'charge':
356 if not isinstance(value, float):
357 raise self.PhysicsObjectError, \
358 "Charge %s is not a float" % repr(value)
359
360 if name is 'propagating':
361 if not isinstance(value, bool):
362 raise self.PhysicsObjectError, \
363 "Propagating tag %s is not a boolean" % repr(value)
364
365 if name in ['is_part', 'self_antipart']:
366 if not isinstance(value, bool):
367 raise self.PhysicsObjectError, \
368 "%s tag %s is not a boolean" % (name, repr(value))
369
370 return True
371
372 - def get_sorted_keys(self):
373 """Return particle property names as a nicely sorted list."""
374
375 return self.sorted_keys
376
377
378
379 - def is_perturbating(self,order,model):
380 """Returns wether this particle contributes in perturbation of the order passed
381 in argument given the model specified. It is very fast for usual models"""
382
383 for int in model['interactions'].get_type('base'):
384
385
386
387
388
389
390
391
392 if len(int.get('orders'))>1:
393 continue
394 if order in int.get('orders').keys() and self.get('pdg_code') in \
395 [part.get('pdg_code') for part in int.get('particles')]:
396 return True
397
398 return False
399
400 - def get_pdg_code(self):
401 """Return the PDG code with a correct minus sign if the particle is its
402 own antiparticle"""
403
404 if not self['is_part'] and not self['self_antipart']:
405 return - self['pdg_code']
406 else:
407 return self['pdg_code']
408
410 """Return the PDG code of the antiparticle with a correct minus sign
411 if the particle is its own antiparticle"""
412
413 if not self['self_antipart']:
414 return - self.get_pdg_code()
415 else:
416 return self['pdg_code']
417
418 - def get_color(self):
419 """Return the color code with a correct minus sign"""
420
421 if not self['is_part'] and abs(self['color']) in [3, 6]:
422 return - self['color']
423 else:
424 return self['color']
425
426 - def get_anti_color(self):
427 """Return the color code of the antiparticle with a correct minus sign
428 """
429
430 if self['is_part'] and self['color'] not in [1, 8]:
431 return - self['color']
432 else:
433 return self['color']
434
435 - def get_charge(self):
436 """Return the charge code with a correct minus sign"""
437
438 if not self['is_part']:
439 return - self['charge']
440 else:
441 return self['charge']
442
443 - def get_anti_charge(self):
444 """Return the charge code of the antiparticle with a correct minus sign
445 """
446
447 if self['is_part']:
448 return - self['charge']
449 else:
450 return self['charge']
451
452 - def get_name(self):
453 """Return the name if particle, antiname if antiparticle"""
454
455 if not self['is_part'] and not self['self_antipart']:
456 return self['antiname']
457 else:
458 return self['name']
459
460 - def get_helicity_states(self, allow_reverse=True):
461 """Return a list of the helicity states for the onshell particle"""
462
463 spin = self.get('spin')
464 if spin ==1:
465
466 res = [ 0 ]
467 elif spin == 2:
468
469 res = [ -1, 1 ]
470 elif spin == 3 and self.get('mass').lower() == 'zero':
471
472 res = [ -1, 1 ]
473 elif spin == 3:
474
475 res = [ -1, 0, 1 ]
476 elif spin == 4 and self.get('mass').lower() == 'zero':
477
478 res = [-3, 3]
479 elif spin == 4:
480
481 res = [-3, -1, 1, 3]
482 elif spin == 5 and self.get('mass').lower() == 'zero':
483
484 res = [-2, -1, 1, 2]
485 elif spin in [5, 99]:
486
487 res = [-2, -1, 0, 1, 2]
488 else:
489 raise self.PhysicsObjectError, \
490 "No helicity state assignment for spin %d particles" % spin
491
492 if allow_reverse and not self.get('is_part'):
493 res.reverse()
494
495
496 return res
497
498 - def is_fermion(self):
499 """Returns True if this is a fermion, False if boson"""
500
501 return self['spin'] % 2 == 0
502
503 - def is_boson(self):
504 """Returns True if this is a boson, False if fermion"""
505
506 return self['spin'] % 2 == 1
507
508
509
510
511 -class ParticleList(PhysicsObjectList):
512 """A class to store lists of particles."""
513
514 - def is_valid_element(self, obj):
515 """Test if object obj is a valid Particle for the list."""
516 return isinstance(obj, Particle)
517
518 - def get_copy(self, name):
519 """Try to find a particle with the given name. Check both name
520 and antiname. If a match is found, return the a copy of the
521 corresponding particle (first one in the list), with the
522 is_part flag set accordingly. None otherwise."""
523
524 assert isinstance(name, str)
525
526 part = self.find_name(name)
527 if not part:
528
529 try:
530 pdg = int(name)
531 except ValueError:
532 return None
533
534 for p in self:
535 if p.get_pdg_code()==pdg:
536 part = copy.copy(p)
537 part.set('is_part', True)
538 return part
539 elif p.get_anti_pdg_code()==pdg:
540 part = copy.copy(p)
541 part.set('is_part', False)
542 return part
543
544 return None
545 part = copy.copy(part)
546
547 if part.get('name') == name:
548 part.set('is_part', True)
549 return part
550 elif part.get('antiname') == name:
551 part.set('is_part', False)
552 return part
553 return None
554
555 - def find_name(self, name):
556 """Try to find a particle with the given name. Check both name
557 and antiname. If a match is found, return the a copy of the
558 corresponding particle (first one in the list), with the
559 is_part flag set accordingly. None otherwise."""
560
561 assert isinstance(name, str), "%s is not a valid string" % str(name)
562
563 for part in self:
564 if part.get('name') == name:
565 return part
566 elif part.get('antiname') == name:
567 return part
568
569 return None
570
572 """Generate a dictionary of part/antipart pairs (as keys) and
573 0 (as value)"""
574
575 ref_dict_to0 = {}
576
577 for part in self:
578 ref_dict_to0[(part.get_pdg_code(), part.get_anti_pdg_code())] = [0]
579 ref_dict_to0[(part.get_anti_pdg_code(), part.get_pdg_code())] = [0]
580
581 return ref_dict_to0
582
583 - def generate_dict(self):
584 """Generate a dictionary from particle id to particle.
585 Include antiparticles.
586 """
587
588 particle_dict = {}
589
590 for particle in self:
591 particle_dict[particle.get('pdg_code')] = particle
592 if not particle.get('self_antipart'):
593 antipart = copy.deepcopy(particle)
594 antipart.set('is_part', False)
595 particle_dict[antipart.get_pdg_code()] = antipart
596
597 return particle_dict
598
604 """The interaction object containing the whole set of information
605 required to univocally characterize a given type of physical interaction:
606
607 particles: a list of particle ids
608 color: a list of string describing all the color structures involved
609 lorentz: a list of variable names describing all the Lorentz structure
610 involved
611 couplings: dictionary listing coupling variable names. The key is a
612 2-tuple of integers referring to color and Lorentz structures
613 orders: dictionary listing order names (as keys) with their value
614 """
615
616 sorted_keys = ['id', 'particles', 'color', 'lorentz', 'couplings',
617 'orders','loop_particles','type','perturbation_type']
618
620 """Default values for all properties"""
621
622 self['id'] = 0
623 self['particles'] = []
624 self['color'] = []
625 self['lorentz'] = []
626 self['couplings'] = { (0, 0):'none'}
627 self['orders'] = {}
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 self['loop_particles']=[[]]
683 self['type'] = 'base'
684 self['perturbation_type'] = None
685
686 - def filter(self, name, value):
687 """Filter for valid interaction property values."""
688
689 if name == 'id':
690
691 if not isinstance(value, int):
692 raise self.PhysicsObjectError, \
693 "%s is not a valid integer" % str(value)
694
695 if name == 'particles':
696
697 if not isinstance(value, ParticleList):
698 raise self.PhysicsObjectError, \
699 "%s is not a valid list of particles" % str(value)
700
701 if name == 'perturbation_type':
702 if value!=None and not isinstance(value, str):
703 raise self.PhysicsObjectError, \
704 "%s is not a valid string" % str(value)
705
706 if name == 'type':
707
708 if not isinstance(value, str):
709 raise self.PhysicsObjectError, \
710 "%s is not a valid string" % str(value)
711 if name == 'loop_particles':
712 if isinstance(value,list):
713 for l in value:
714 if isinstance(l,list):
715 for part in l:
716 if not isinstance(part,int):
717 raise self.PhysicsObjectError, \
718 "%s is not a valid integer" % str(part)
719 if part<0:
720 raise self.PhysicsObjectError, \
721 "%s is not a valid positive integer" % str(part)
722
723 if name == 'orders':
724
725 if not isinstance(value, dict):
726 raise self.PhysicsObjectError, \
727 "%s is not a valid dict for coupling orders" % \
728 str(value)
729 for order in value.keys():
730 if not isinstance(order, str):
731 raise self.PhysicsObjectError, \
732 "%s is not a valid string" % str(order)
733 if not isinstance(value[order], int):
734 raise self.PhysicsObjectError, \
735 "%s is not a valid integer" % str(value[order])
736
737 if name in ['color']:
738
739 if not isinstance(value, list):
740 raise self.PhysicsObjectError, \
741 "%s is not a valid list of Color Strings" % str(value)
742 for mycolstring in value:
743 if not isinstance(mycolstring, color.ColorString):
744 raise self.PhysicsObjectError, \
745 "%s is not a valid list of Color Strings" % str(value)
746
747 if name in ['lorentz']:
748
749 if not isinstance(value, list):
750 raise self.PhysicsObjectError, \
751 "%s is not a valid list of strings" % str(value)
752 for mystr in value:
753 if not isinstance(mystr, str):
754 raise self.PhysicsObjectError, \
755 "%s is not a valid string" % str(mystr)
756
757 if name == 'couplings':
758
759 if not isinstance(value, dict):
760 raise self.PhysicsObjectError, \
761 "%s is not a valid dictionary for couplings" % \
762 str(value)
763
764 for key in value.keys():
765 if not isinstance(key, tuple):
766 raise self.PhysicsObjectError, \
767 "%s is not a valid tuple" % str(key)
768 if len(key) != 2:
769 raise self.PhysicsObjectError, \
770 "%s is not a valid tuple with 2 elements" % str(key)
771 if not isinstance(key[0], int) or not isinstance(key[1], int):
772 raise self.PhysicsObjectError, \
773 "%s is not a valid tuple of integer" % str(key)
774 if not isinstance(value[key], str):
775 raise self.PhysicsObjectError, \
776 "%s is not a valid string" % value[key]
777
778 return True
779
781 """Return particle property names as a nicely sorted list."""
782
783 return self.sorted_keys
784
786 """ Returns if this interaction comes from the perturbation of one of
787 the order listed in the argument """
788
789 if self['perturbation_type']==None:
790 return True
791 else:
792 return (self['perturbation_type'] in orders_considered)
793
795 """ Returns if the interaction is of R2 type."""
796
797
798
799 if 'type' in self.keys():
800 return (len(self['type'])>=2 and self['type'][:2]=='R2')
801 else:
802 return False
803
805 """ Returns if the interaction is of UV type."""
806
807
808
809 if 'type' in self.keys():
810 return (len(self['type'])>=2 and self['type'][:2]=='UV')
811 else:
812 return False
813
815 """ Returns if the interaction is of UVmass type."""
816
817
818
819 if 'type' in self.keys():
820 return (len(self['type'])>=6 and self['type'][:6]=='UVmass')
821 else:
822 return False
823
825 """ Returns if the interaction is of UVmass type."""
826
827
828
829 if 'type' in self.keys():
830 return (len(self['type'])>=6 and self['type'][:6]=='UVloop')
831 else:
832 return False
833
835 """ Returns if the interaction is of UVmass type."""
836
837
838
839 if 'type' in self.keys():
840 return (len(self['type'])>=6 and self['type'][:6]=='UVtree')
841 else:
842 return False
843
845 """ Returns if the interaction is of the UVCT type which means that
846 it has been selected as a possible UV counterterm interaction for this
847 process. Such interactions are marked by having the 'UVCT_SPECIAL' order
848 key in their orders."""
849
850
851
852 if 'UVCT_SPECIAL' in self['orders'].keys():
853 return True
854 else:
855 return False
856
858 """ Returns 0 if this interaction contributes to the finite part of the
859 amplitude and 1 (2) is it contributes to its single (double) pole """
860
861 if 'type' in self.keys():
862 if '1eps' in self['type']:
863 return 1
864 elif '2eps' in self['type']:
865 return 2
866 else:
867 return 0
868 else:
869 return 0
870
872 """Add entries corresponding to the current interactions to
873 the reference dictionaries (for n>0 and n-1>1)"""
874
875
876
877
878 pdg_tuple = tuple(sorted([p.get_pdg_code() for p in self['particles']]))
879 if pdg_tuple not in ref_dict_to0.keys():
880 ref_dict_to0[pdg_tuple] = [self['id']]
881 else:
882 ref_dict_to0[pdg_tuple].append(self['id'])
883
884
885
886
887
888
889
890 for part in self['particles']:
891
892
893 pdg_tuple = tuple(sorted([p.get_pdg_code() for (i, p) in \
894 enumerate(self['particles']) if \
895 i != self['particles'].index(part)]))
896 pdg_part = part.get_anti_pdg_code()
897 if pdg_tuple in ref_dict_to1.keys():
898 if (pdg_part, self['id']) not in ref_dict_to1[pdg_tuple]:
899 ref_dict_to1[pdg_tuple].append((pdg_part, self['id']))
900 else:
901 ref_dict_to1[pdg_tuple] = [(pdg_part, self['id'])]
902
904 """Get the WEIGHTED order for this interaction, for equivalent
905 3-particle vertex. Note that it can be fractional."""
906
907 return float(sum([model.get('order_hierarchy')[key]*self.get('orders')[key]\
908 for key in self.get('orders')]))/ \
909 max((len(self.get('particles'))-2), 1)
910
912 """String representation of an interaction. Outputs valid Python
913 with improved format. Overrides the PhysicsObject __str__ to only
914 display PDG code of involved particles."""
915
916 mystr = '{\n'
917
918 for prop in self.get_sorted_keys():
919 if isinstance(self[prop], str):
920 mystr = mystr + ' \'' + prop + '\': \'' + \
921 self[prop] + '\',\n'
922 elif isinstance(self[prop], float):
923 mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop]
924 elif isinstance(self[prop], ParticleList):
925 mystr = mystr + ' \'' + prop + '\': [%s],\n' % \
926 ','.join([str(part.get_pdg_code()) for part in self[prop]])
927 else:
928 mystr = mystr + ' \'' + prop + '\': ' + \
929 repr(self[prop]) + ',\n'
930 mystr = mystr.rstrip(',\n')
931 mystr = mystr + '\n}'
932
933 return mystr
934
939 """A class to store lists of interactionss."""
940
942 """Test if object obj is a valid Interaction for the list."""
943
944 return isinstance(obj, Interaction)
945
947 """Generate the reference dictionaries from interaction list.
948 Return a list where the first element is the n>0 dictionary and
949 the second one is n-1>1."""
950
951 ref_dict_to0 = {}
952 ref_dict_to1 = {}
953 buffer = {}
954
955 for inter in self:
956 if useR2UV or (not inter.is_UV() and not inter.is_R2() and \
957 not inter.is_UVCT()):
958 inter.generate_dict_entries(ref_dict_to0, ref_dict_to1)
959 if useUVCT and inter.is_UVCT():
960 inter.generate_dict_entries(ref_dict_to0, ref_dict_to1)
961
962 return [ref_dict_to0, ref_dict_to1]
963
965 """Generate a dictionary from interaction id to interaction.
966 """
967
968 interaction_dict = {}
969
970 for inter in self:
971 interaction_dict[inter.get('id')] = inter
972
973 return interaction_dict
974
976 """Make sure that the particles in the interactions are those
977 in the particle_dict, and that there are no interactions
978 refering to particles that don't exist. To be called when the
979 particle_dict is updated in a model.
980 """
981
982 iint = 0
983 while iint < len(self):
984 inter = self[iint]
985 particles = inter.get('particles')
986 try:
987 for ipart, part in enumerate(particles):
988 particles[ipart] = particle_dict[part.get_pdg_code()]
989 iint += 1
990 except KeyError:
991
992 self.pop(iint)
993
995 """ return all interactions in the list of type 'type' """
996 return InteractionList([int for int in self if int.get('type')==type])
997
999 """ return all interactions in the list of type R2 """
1000 return InteractionList([int for int in self if int.is_R2()])
1001
1003 """ return all interactions in the list of type UV """
1004 return InteractionList([int for int in self if int.is_UV()])
1005
1007 """ return all interactions in the list of type UVmass """
1008 return InteractionList([int for int in self if int.is_UVmass()])
1009
1011 """ return all interactions in the list of type UVtree """
1012 return InteractionList([int for int in self if int.is_UVtree()])
1013
1015 """ return all interactions in the list of type UVloop """
1016 return InteractionList([int for int in self if int.is_UVloop()])
1017
1018
1019
1020
1021 -class Model(PhysicsObject):
1022 """A class to store all the model information."""
1023
1025
1026 self['name'] = ""
1027 self['particles'] = ParticleList()
1028 self['interactions'] = InteractionList()
1029 self['parameters'] = None
1030 self['functions'] = None
1031 self['couplings'] = None
1032 self['lorentz'] = None
1033 self['particle_dict'] = {}
1034 self['interaction_dict'] = {}
1035 self['ref_dict_to0'] = {}
1036 self['ref_dict_to1'] = {}
1037 self['got_majoranas'] = None
1038 self['order_hierarchy'] = {}
1039 self['conserved_charge'] = set()
1040 self['coupling_orders'] = None
1041 self['expansion_order'] = None
1042 self['version_tag'] = None
1043 self['gauge'] = [0, 1]
1044 self['case_sensitive'] = True
1045
1046
1047
1048
1049 - def filter(self, name, value):
1050 """Filter for model property values"""
1051
1052 if name in ['name']:
1053 if not isinstance(value, str):
1054 raise self.PhysicsObjectError, \
1055 "Object of type %s is not a string" %type(value)
1056
1057 elif name == 'particles':
1058 if not isinstance(value, ParticleList):
1059 raise self.PhysicsObjectError, \
1060 "Object of type %s is not a ParticleList object" % \
1061 type(value)
1062 elif name == 'interactions':
1063 if not isinstance(value, InteractionList):
1064 raise self.PhysicsObjectError, \
1065 "Object of type %s is not a InteractionList object" % \
1066 type(value)
1067 elif name == 'particle_dict':
1068 if not isinstance(value, dict):
1069 raise self.PhysicsObjectError, \
1070 "Object of type %s is not a dictionary" % \
1071 type(value)
1072 elif name == 'interaction_dict':
1073 if not isinstance(value, dict):
1074 raise self.PhysicsObjectError, \
1075 "Object of type %s is not a dictionary" % type(value)
1076
1077 elif name == 'ref_dict_to0':
1078 if not isinstance(value, dict):
1079 raise self.PhysicsObjectError, \
1080 "Object of type %s is not a dictionary" % type(value)
1081
1082 elif name == 'ref_dict_to1':
1083 if not isinstance(value, dict):
1084 raise self.PhysicsObjectError, \
1085 "Object of type %s is not a dictionary" % type(value)
1086
1087 elif name == 'got_majoranas':
1088 if not (isinstance(value, bool) or value == None):
1089 raise self.PhysicsObjectError, \
1090 "Object of type %s is not a boolean" % type(value)
1091
1092 elif name == 'conserved_charge':
1093 if not (isinstance(value, set)):
1094 raise self.PhysicsObjectError, \
1095 "Object of type %s is not a set" % type(value)
1096
1097 elif name == 'version_tag':
1098 if not (isinstance(value, str)):
1099 raise self.PhysicsObjectError, \
1100 "Object of type %s is not a string" % type(value)
1101
1102 elif name == 'order_hierarchy':
1103 if not isinstance(value, dict):
1104 raise self.PhysicsObjectError, \
1105 "Object of type %s is not a dictionary" % \
1106 type(value)
1107 for key in value.keys():
1108 if not isinstance(value[key],int):
1109 raise self.PhysicsObjectError, \
1110 "Object of type %s is not an integer" % \
1111 type(value[key])
1112 elif name == 'gauge':
1113 if not (isinstance(value, list)):
1114 raise self.PhysicsObjectError, \
1115 "Object of type %s is not a list" % type(value)
1116
1117 elif name == 'case_sensitive':
1118 if not value in [True ,False]:
1119 raise self.PhysicsObjectError, \
1120 "Object of type %s is not a boolean" % type(value)
1121
1122
1123 return True
1124
1125 - def get(self, name):
1126 """Get the value of the property name."""
1127
1128 if (name == 'ref_dict_to0' or name == 'ref_dict_to1') and \
1129 not self[name]:
1130 if self['interactions']:
1131 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1132 self['interactions'].generate_ref_dict()
1133 self['ref_dict_to0'].update(
1134 self['particles'].generate_ref_dict())
1135
1136 if (name == 'particle_dict') and not self[name]:
1137 if self['particles']:
1138 self['particle_dict'] = self['particles'].generate_dict()
1139 if self['interactions']:
1140 self['interactions'].synchronize_interactions_with_particles(\
1141 self['particle_dict'])
1142 if name == 'modelpath':
1143 modeldir = self.get('version_tag').rsplit('##',1)[0]
1144 if os.path.exists(modeldir):
1145 return modeldir
1146 else:
1147 raise Exception, "path %s not valid anymore." % modeldir
1148
1149
1150
1151
1152
1153 elif name == 'modelpath+restriction':
1154 modeldir = self.get('version_tag').rsplit('##',1)[0]
1155 modelname = self['name']
1156 if not os.path.exists(modeldir):
1157 raise Exception, "path %s not valid anymore" % modeldir
1158 modeldir = os.path.dirname(modeldir)
1159 modeldir = pjoin(modeldir, modelname)
1160 return modeldir
1161 elif name == 'restrict_name':
1162 modeldir = self.get('version_tag').rsplit('##',1)[0]
1163 modelname = self['name']
1164 basename = os.path.basename(modeldir)
1165 restriction = modelname[len(basename)+1:]
1166 return restriction
1167
1168 if (name == 'interaction_dict') and not self[name]:
1169 if self['interactions']:
1170 self['interaction_dict'] = self['interactions'].generate_dict()
1171
1172 if (name == 'got_majoranas') and self[name] == None:
1173 if self['particles']:
1174 self['got_majoranas'] = self.check_majoranas()
1175
1176 if (name == 'coupling_orders') and self[name] == None:
1177 if self['interactions']:
1178 self['coupling_orders'] = self.get_coupling_orders()
1179
1180 if (name == 'order_hierarchy') and not self[name]:
1181 if self['interactions']:
1182 self['order_hierarchy'] = self.get_order_hierarchy()
1183
1184 if (name == 'expansion_order') and self[name] == None:
1185 if self['interactions']:
1186 self['expansion_order'] = \
1187 dict([(order, -1) for order in self.get('coupling_orders')])
1188
1189 if (name == 'name2pdg') and 'name2pdg' not in self:
1190 self['name2pdg'] = {}
1191 for p in self.get('particles'):
1192 self['name2pdg'][p.get('antiname')] = -1*p.get('pdg_code')
1193 self['name2pdg'][p.get('name')] = p.get('pdg_code')
1194
1195 return Model.__bases__[0].get(self, name)
1196
1197 - def set(self, name, value, force = False):
1198 """Special set for particles and interactions - need to
1199 regenerate dictionaries."""
1200
1201 if name == 'particles':
1202
1203 make_unique(value)
1204
1205 self['particle_dict'] = {}
1206 self['ref_dict_to0'] = {}
1207 self['got_majoranas'] = None
1208
1209 if name == 'interactions':
1210
1211 make_unique(value)
1212
1213 self['interaction_dict'] = {}
1214 self['ref_dict_to1'] = {}
1215 self['ref_dict_to0'] = {}
1216 self['got_majoranas'] = None
1217 self['coupling_orders'] = None
1218 self['order_hierarchy'] = {}
1219 self['expansion_order'] = None
1220
1221 result = Model.__bases__[0].set(self, name, value, force)
1222
1223 if name == 'particles':
1224
1225 self.get('particle_dict')
1226
1227 return result
1228
1230 """This function actualizes the dictionaries"""
1231
1232 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1233 self['interactions'].generate_ref_dict()
1234 self['ref_dict_to0'].update(
1235 self['particles'].generate_ref_dict())
1236
1238 """Return process property names as a nicely sorted list."""
1239
1240 return ['name', 'particles', 'parameters', 'interactions',
1241 'couplings','lorentz', 'gauge']
1242
1243 - def get_particle(self, id):
1244 """Return the particle corresponding to the id / name"""
1245
1246 try:
1247 return self["particle_dict"][id]
1248 except Exception:
1249 if isinstance(id, int):
1250 try:
1251 return self.get("particle_dict")[id]
1252 except Exception,error:
1253 return None
1254 else:
1255 if not hasattr(self, 'name2part'):
1256 self.create_name2part()
1257 try:
1258 return self.name2part[id]
1259 except:
1260 return None
1261
1263 """create a dictionary name 2 part"""
1264
1265 self.name2part = {}
1266 for part in self.get("particle_dict").values():
1267 self.name2part[part.get('name')] = part
1268
1269
1270
1272 """return the lorentz object from the associate name"""
1273 if hasattr(self, 'lorentz_name2obj'):
1274 return self.lorentz_name2obj[name]
1275 else:
1276 self.create_lorentz_dict()
1277 return self.lorentz_name2obj[name]
1278
1280 """create the dictionary linked to the lorentz structure"""
1281 self.lorentz_name2obj = {}
1282 self.lorentz_expr2name = {}
1283 if not self.get('lorentz'):
1284 return
1285 for lor in self.get('lorentz'):
1286 self.lorentz_name2obj[lor.name] = lor
1287 self.lorentz_expr2name[lor.structure] = lor.name
1288
1290 """Return the interaction corresponding to the id"""
1291
1292 try:
1293 return self.get("interaction_dict")[id]
1294 except Exception:
1295 return None
1296
1298 """Return the parameter associated to the name NAME"""
1299
1300
1301 if hasattr(self, 'parameters_dict') and self.parameters_dict:
1302 try:
1303 return self.parameters_dict[name]
1304 except Exception:
1305
1306 pass
1307
1308
1309 self.parameters_dict = {}
1310 for data in self['parameters'].values():
1311 [self.parameters_dict.__setitem__(p.name,p) for p in data]
1312
1313 return self.parameters_dict[name]
1314
1316 """Determine the coupling orders of the model"""
1317 return set(sum([i.get('orders').keys() for i in \
1318 self.get('interactions')], []))
1319
1321 """Set a default order hierarchy for the model if not set by the UFO."""
1322
1323 hierarchy = dict([(order, 1) for order in self.get('coupling_orders')])
1324
1325 if self.get('coupling_orders') == set(['QCD', 'QED']):
1326 hierarchy['QED'] = 2
1327 return hierarchy
1328
1329
1331 """returns the number of light quark flavours in the model."""
1332 return len([p for p in self.get('particles') \
1333 if p['spin'] == 2 and p['is_part'] and \
1334 p ['color'] != 1 and p['mass'].lower() == 'zero'])
1335
1336
1338 """Returns the order hierarchies of the model and the
1339 particles which have interactions in at least this hierarchy
1340 (used in find_optimal_process_orders in MultiProcess diagram
1341 generation):
1342
1343 Check the coupling hierarchy of the model. Assign all
1344 particles to the different coupling hierarchies so that a
1345 particle is considered to be in the highest hierarchy (i.e.,
1346 with lowest value) where it has an interaction.
1347 """
1348
1349
1350 coupling_orders = self.get('coupling_orders')
1351
1352
1353 hierarchy = sorted(list(set([self.get('order_hierarchy')[k] for \
1354 k in coupling_orders])))
1355
1356
1357 orders = []
1358 for value in hierarchy:
1359 orders.append([ k for (k, v) in \
1360 self.get('order_hierarchy').items() if \
1361 v == value ])
1362
1363
1364
1365 interactions = []
1366 particles = []
1367 for iorder, order in enumerate(orders):
1368 sum_orders = sum(orders[:iorder+1], [])
1369 sum_interactions = sum(interactions[:iorder], [])
1370 sum_particles = sum([list(p) for p in particles[:iorder]], [])
1371
1372
1373 interactions.append([i for i in self.get('interactions') if \
1374 not i in sum_interactions and \
1375 not any([k not in sum_orders for k in \
1376 i.get('orders').keys()])])
1377
1378
1379 particles.append(set(sum([[p.get_pdg_code() for p in \
1380 inter.get('particles') if \
1381 p.get_pdg_code() not in sum_particles] \
1382 for inter in interactions[-1]], [])))
1383
1384 return particles, hierarchy
1385
1387 """Return the maximum WEIGHTED order for any interaction in the model,
1388 for equivalent 3-particle vertices. Note that it can be fractional."""
1389
1390 return max([inter.get_WEIGHTED_order(self) for inter in \
1391 self.get('interactions')])
1392
1393
1395 """Return True if there is fermion flow violation, False otherwise"""
1396
1397 if any([part.is_fermion() and part.get('self_antipart') \
1398 for part in self.get('particles')]):
1399 return True
1400
1401
1402
1403 for inter in self.get('interactions'):
1404
1405 if len(inter.get('particles'))==1:
1406 continue
1407 fermions = [p for p in inter.get('particles') if p.is_fermion()]
1408 for i in range(0, len(fermions), 2):
1409 if fermions[i].get('is_part') == \
1410 fermions[i+1].get('is_part'):
1411
1412 return True
1413
1414 return False
1415
1417 """Reset all dictionaries and got_majoranas. This is necessary
1418 whenever the particle or interaction content has changed. If
1419 particles or interactions are set using the set routine, this
1420 is done automatically."""
1421
1422 self['particle_dict'] = {}
1423 self['ref_dict_to0'] = {}
1424 self['got_majoranas'] = None
1425 self['interaction_dict'] = {}
1426 self['ref_dict_to1'] = {}
1427 self['ref_dict_to0'] = {}
1428
1430 """Change the name of the particles such that all SM and MSSM particles
1431 follows the MG convention"""
1432
1433
1434 def check_name_free(self, name):
1435 """ check if name is not use for a particle in the model if it is
1436 raise an MadGraph5error"""
1437 part = self['particles'].find_name(name)
1438 if part:
1439 error_text = \
1440 '%s particles with pdg code %s is in conflict with MG ' + \
1441 'convention name for particle %s.\n Use -modelname in order ' + \
1442 'to use the particles name defined in the model and not the ' + \
1443 'MadGraph5_aMC@NLO convention'
1444
1445 raise MadGraph5Error, error_text % \
1446 (part.get_name(), part.get_pdg_code(), pdg)
1447
1448 default = self.load_default_name()
1449
1450 for pdg in default.keys():
1451 part = self.get_particle(pdg)
1452 if not part:
1453 continue
1454 antipart = self.get_particle(-pdg)
1455 name = part.get_name()
1456 if name != default[pdg]:
1457 check_name_free(self, default[pdg])
1458 if part.get('is_part'):
1459 part.set('name', default[pdg])
1460 if antipart:
1461 antipart.set('name', default[pdg])
1462 else:
1463 part.set('antiname', default[pdg])
1464 else:
1465 part.set('antiname', default[pdg])
1466 if antipart:
1467 antipart.set('antiname', default[pdg])
1468
1469
1470 if self.get('name') == 'mssm' or self.get('name').startswith('mssm-'):
1471 part = self.get_particle(25)
1472 part.set('name', 'h1')
1473 part.set('antiname', 'h1')
1474
1475
1476
1478 """ Change all model parameter by a given prefix.
1479 Modify the parameter if some of them are identical up to the case"""
1480
1481 lower_dict={}
1482 duplicate = set()
1483 keys = self.get('parameters').keys()
1484 for key in keys:
1485 for param in self['parameters'][key]:
1486 lower_name = param.name.lower()
1487 if not lower_name:
1488 continue
1489 try:
1490 lower_dict[lower_name].append(param)
1491 except KeyError:
1492 lower_dict[lower_name] = [param]
1493 else:
1494 duplicate.add(lower_name)
1495 logger.debug('%s is defined both as lower case and upper case.'
1496 % lower_name)
1497
1498 if prefix == '' and not duplicate:
1499 return
1500
1501 re_expr = r'''\b(%s)\b'''
1502 to_change = []
1503 change={}
1504
1505 for key in keys:
1506 for param in self['parameters'][key]:
1507 value = param.name.lower()
1508 if value in ['as','mu_r', 'zero','aewm1','g']:
1509 continue
1510 elif value.startswith(prefix):
1511 continue
1512 elif value in duplicate:
1513 continue
1514 elif value:
1515 change[param.name] = '%s%s' % (prefix,param.name)
1516 to_change.append(param.name)
1517 param.name = change[param.name]
1518
1519 for value in duplicate:
1520 for i, var in enumerate(lower_dict[value]):
1521 to_change.append(var.name)
1522 new_name = '%s%s%s' % (prefix, var.name.lower(),
1523 ('__%d'%(i+1) if i>0 else ''))
1524 change[var.name] = new_name
1525 var.name = new_name
1526 to_change.append(var.name)
1527 assert 'zero' not in to_change
1528 replace = lambda match_pattern: change[match_pattern.groups()[0]]
1529
1530 if not to_change:
1531 return
1532
1533 if 'parameter_dict' in self:
1534 new_dict = dict( (change[name] if (name in change) else name, value) for
1535 name, value in self['parameter_dict'].items())
1536 self['parameter_dict'] = new_dict
1537
1538 if hasattr(self,'map_CTcoup_CTparam'):
1539
1540
1541 self.map_CTcoup_CTparam = dict( (coup_name,
1542 [change[name] if (name in change) else name for name in params])
1543 for coup_name, params in self.map_CTcoup_CTparam.items() )
1544
1545 i=0
1546 while i*1000 <= len(to_change):
1547 one_change = to_change[i*1000: min((i+1)*1000,len(to_change))]
1548 i+=1
1549 rep_pattern = re.compile('\\b%s\\b'% (re_expr % ('\\b|\\b'.join(one_change))))
1550
1551
1552 for key in keys:
1553 if key == ('external',):
1554 continue
1555 for param in self['parameters'][key]:
1556 param.expr = rep_pattern.sub(replace, param.expr)
1557
1558 for key in self['couplings'].keys():
1559 for coup in self['couplings'][key]:
1560 coup.expr = rep_pattern.sub(replace, coup.expr)
1561
1562
1563 for part in self['particles']:
1564 if str(part.get('mass')) in one_change:
1565 part.set('mass', rep_pattern.sub(replace, str(part.get('mass'))))
1566 if str(part.get('width')) in one_change:
1567 part.set('width', rep_pattern.sub(replace, str(part.get('width'))))
1568 if hasattr(part, 'partial_widths'):
1569 for key, value in part.partial_widths.items():
1570 part.partial_widths[key] = rep_pattern.sub(replace, value)
1571
1572
1573 self['particle_dict'] =''
1574 self.get('particle_dict')
1575
1576
1577
1579 """Return the first positive number that is not a valid PDG code"""
1580 return [c for c in range(1, len(self.get('particles')) + 1) if \
1581 c not in self.get('particle_dict').keys()][0]
1582
1592
1593 @ staticmethod
1595 """ load the default for name convention """
1596
1597 logger.info('Change particles name to pass to MG5 convention')
1598 default = {}
1599 for line in open(os.path.join(MG5DIR, 'input', \
1600 'particles_name_default.txt')):
1601 line = line.lstrip()
1602 if line.startswith('#'):
1603 continue
1604
1605 args = line.split()
1606 if len(args) != 2:
1607 logger.warning('Invalid syntax in interface/default_name:\n %s' % line)
1608 continue
1609 default[int(args[0])] = args[1].lower()
1610
1611 return default
1612
1614 """Change the electroweak mode. The only valid mode now is external.
1615 Where in top of the default MW and sw2 are external parameters."""
1616
1617 assert mode in ["external",set(['mz','mw','alpha'])]
1618
1619 try:
1620 W = self.get('particle_dict')[24]
1621 except KeyError:
1622 raise InvalidCmd('No W particle in the model impossible to '+
1623 'change the EW scheme!')
1624
1625 if mode=='external':
1626 MW = self.get_parameter(W.get('mass'))
1627 if not isinstance(MW, ParamCardVariable):
1628 newMW = ParamCardVariable(MW.name, MW.value, 'MASS', [24])
1629 if not newMW.value:
1630 newMW.value = 80.385
1631
1632 self.get('parameters')[MW.depend].remove(MW)
1633
1634 self.add_param(newMW, ['external'])
1635
1636
1637 try:
1638 sw2 = self.get_parameter('sw2')
1639 except KeyError:
1640 try:
1641 sw2 = self.get_parameter('mdl_sw2')
1642 except KeyError:
1643 sw2=None
1644
1645 if sw2:
1646 newsw2 = ParamCardVariable(sw2.name,sw2.value, 'SMINPUTS', [4])
1647 if not newsw2.value:
1648 newsw2.value = 0.222246485786
1649
1650 self.get('parameters')[sw2.depend].remove(sw2)
1651
1652 self.add_param(newsw2, ['external'])
1653
1654 self.parameters_dict = None
1655 return true
1656
1657 elif mode==set(['mz','mw','alpha']):
1658
1659 W = self.get('particle_dict')[24]
1660 mass = self.get_parameter(W.get('mass'))
1661 mass_expr = 'cmath.sqrt(%(prefix)sMZ__exp__2/2. + cmath.sqrt('+\
1662 '%(prefix)sMZ__exp__4/4. - (%(prefix)saEW*cmath.pi*%(prefix)s'+\
1663 'MZ__exp__2)/(%(prefix)sGf*%(prefix)ssqrt__2)))'
1664 if 'external' in mass.depend:
1665
1666 return True
1667 match = False
1668 if mass.expr == mass_expr%{'prefix':''}:
1669 prefix = ''
1670 match = True
1671 elif mass.expr == mass_expr%{'prefix':'mdl_'}:
1672 prefix = 'mdl_'
1673 match = True
1674 if match:
1675 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1676 if not MW.value:
1677 MW.value = 80.385
1678 self.get('parameters')[('external',)].append(MW)
1679 self.get('parameters')[mass.depend].remove(mass)
1680
1681 new_param = ModelVariable('Gf',
1682 '-%(prefix)saEW*%(prefix)sMZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - %(prefix)sMZ**2))' %\
1683 {'MW': mass.name,'prefix':prefix}, 'complex', mass.depend)
1684 Gf = self.get_parameter('%sGf'%prefix)
1685 self.get('parameters')[('external',)].remove(Gf)
1686 self.add_param(new_param, ['%saEW'%prefix])
1687
1688 self.parameters_dict = None
1689 return True
1690 else:
1691 return False
1692
1694 """modify the expression changing the mass to complex mass scheme"""
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710 try:
1711 CMSParam = self.get_parameter('CMSParam')
1712 except KeyError:
1713 try:
1714 CMSParam = self.get_parameter('mdl_CMSParam')
1715 except KeyError:
1716 CMSParam = None
1717
1718
1719 if not toCMS:
1720 if CMSParam:
1721 CMSParam.expr = '0.0'
1722 return
1723
1724
1725 if CMSParam:
1726 CMSParam.expr = '1.0'
1727
1728 to_change = {}
1729 mass_widths = []
1730 for particle in self.get('particles'):
1731 m = particle.get('width')
1732 if m in mass_widths:
1733 continue
1734 mass_widths.append(particle.get('width'))
1735 mass_widths.append(particle.get('mass'))
1736 width = self.get_parameter(particle.get('width'))
1737 if (isinstance(width.value, (complex,float)) and abs(width.value)==0.0) or \
1738 width.name.lower() =='zero':
1739
1740 continue
1741 if not isinstance(width, ParamCardVariable):
1742 width.expr = 're(%s)' % width.expr
1743 mass = self.get_parameter(particle.get('mass'))
1744 if (isinstance(width.value, (complex,float)) and abs(width.value)!=0.0) or \
1745 mass.name.lower() != 'zero':
1746
1747 if particle.get('pdg_code') == 24 and isinstance(mass,
1748 ModelVariable):
1749 status = self.change_electroweak_mode(
1750 set(['mz','mw','alpha']))
1751
1752 mass = self.get_parameter(particle.get('mass'))
1753 if not status:
1754 logger.warning('The W mass is not an external '+
1755 'parameter in this model and the automatic change of'+
1756 ' electroweak scheme changed. This is not advised for '+
1757 'applying the complex mass scheme.')
1758
1759
1760
1761 depend = list(set(mass.depend + width.depend))
1762 if len(depend)>1 and 'external' in depend:
1763 depend.remove('external')
1764 depend = tuple(depend)
1765 if depend == ('external',):
1766 depend = ()
1767
1768
1769 if isinstance(mass, ParamCardVariable):
1770 New_param = ModelVariable('CMASS_'+mass.name,
1771 'cmath.sqrt(%(mass)s**2 - complex(0,1) * %(mass)s * %(width)s)' \
1772 % {'mass': mass.name, 'width': width.name},
1773 'complex', depend)
1774 else:
1775 New_param = ModelVariable('CMASS_'+mass.name,
1776 mass.expr, 'complex', depend)
1777
1778 if not isinstance(width, ParamCardVariable):
1779 width.expr = '- im(%s**2) / cmath.sqrt(re(%s**2))' % (mass.expr, mass.expr)
1780 else:
1781
1782 New_width = ModelVariable(width.name,
1783 '-1 * im(CMASS_%s**2) / %s' % (mass.name, mass.name), 'real', mass.depend)
1784 self.get('parameters')[('external',)].remove(width)
1785 self.add_param(New_param, (mass,))
1786 self.add_param(New_width, (New_param,))
1787 mass.expr = 'cmath.sqrt(re(%s**2))' % mass.expr
1788 to_change[mass.name] = New_param.name
1789 continue
1790
1791 mass.expr = 're(%s)' % mass.expr
1792 self.add_param(New_param, (mass, width))
1793 to_change[mass.name] = New_param.name
1794
1795
1796 yukawas = [p for p in self.get('parameters')[('external',)]
1797 if p.lhablock.lower() == 'yukawa']
1798 for yukawa in yukawas:
1799
1800 self.get('parameters')[('external',)].remove(yukawa)
1801
1802 particle = self.get_particle(yukawa.lhacode[0])
1803 mass = self.get_parameter(particle.get('mass'))
1804
1805
1806 if mass.depend == ('external',):
1807 depend = ()
1808 else:
1809 depend = mass.depend
1810
1811 New_param = ModelVariable(yukawa.name, mass.name, 'real', depend)
1812
1813
1814 if mass.name in to_change:
1815 expr = 'CMASS_%s' % mass.name
1816 else:
1817 expr = mass.name
1818 param_depend = self.get_parameter(expr)
1819 self.add_param(New_param, [param_depend])
1820
1821 if not to_change:
1822 return
1823
1824
1825
1826
1827
1828 pat = '|'.join(to_change.keys())
1829 pat = r'(%s)\b' % pat
1830 pat = re.compile(pat)
1831 def replace(match):
1832 return to_change[match.group()]
1833
1834
1835 for dep, list_param in self['parameters'].items():
1836 for param in list_param:
1837 if param.name.startswith('CMASS_') or param.name in mass_widths or\
1838 isinstance(param, ParamCardVariable):
1839 continue
1840 param.type = 'complex'
1841
1842
1843 param.expr = pat.sub(replace, param.expr)
1844
1845
1846 for dep, list_coup in self['couplings'].items():
1847 for coup in list_coup:
1848 coup.expr = pat.sub(replace, coup.expr)
1849
1850 - def add_param(self, new_param, depend_param):
1851 """add the parameter in the list of parameter in a correct position"""
1852
1853 pos = 0
1854 for i,param in enumerate(self.get('parameters')[new_param.depend]):
1855 if param.name in depend_param:
1856 pos = i + 1
1857 self.get('parameters')[new_param.depend].insert(pos, new_param)
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868 -class ModelVariable(object):
1869 """A Class for storing the information about coupling/ parameter"""
1870
1871 - def __init__(self, name, expression, type, depend=()):
1872 """Initialize a new parameter/coupling"""
1873
1874 self.name = name
1875 self.expr = expression
1876 self.type = type
1877 self.depend = depend
1878 self.value = None
1879
1881 """Object with same name are identical, If the object is a string we check
1882 if the attribute name is equal to this string"""
1883
1884 try:
1885 return other.name == self.name
1886 except Exception:
1887 return other == self.name
1888
1890 """ A class for storing the information linked to all the parameter
1891 which should be define in the param_card.dat"""
1892
1893 depend = ('external',)
1894 type = 'real'
1895
1896 - def __init__(self, name, value, lhablock, lhacode):
1897 """Initialize a new ParamCardVariable
1898 name: name of the variable
1899 value: default numerical value
1900 lhablock: name of the block in the param_card.dat
1901 lhacode: code associate to the variable
1902 """
1903 self.name = name
1904 self.value = value
1905 self.lhablock = lhablock
1906 self.lhacode = lhacode
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917 -class Leg(PhysicsObject):
1918 """Leg object: id (Particle), number, I/F state, flag from_group
1919 """
1920
1922 """Default values for all properties"""
1923
1924 self['id'] = 0
1925 self['number'] = 0
1926
1927 self['state'] = True
1928
1929 self['loop_line'] = False
1930
1931 self['from_group'] = True
1932
1933 self['onshell'] = None
1934
1935 - def filter(self, name, value):
1936 """Filter for valid leg property values."""
1937
1938 if name in ['id', 'number']:
1939 if not isinstance(value, int):
1940 raise self.PhysicsObjectError, \
1941 "%s is not a valid integer for leg id" % str(value)
1942
1943 if name == 'state':
1944 if not isinstance(value, bool):
1945 raise self.PhysicsObjectError, \
1946 "%s is not a valid leg state (True|False)" % \
1947 str(value)
1948
1949 if name == 'from_group':
1950 if not isinstance(value, bool) and value != None:
1951 raise self.PhysicsObjectError, \
1952 "%s is not a valid boolean for leg flag from_group" % \
1953 str(value)
1954
1955 if name == 'loop_line':
1956 if not isinstance(value, bool) and value != None:
1957 raise self.PhysicsObjectError, \
1958 "%s is not a valid boolean for leg flag loop_line" % \
1959 str(value)
1960
1961 if name == 'onshell':
1962 if not isinstance(value, bool) and value != None:
1963 raise self.PhysicsObjectError, \
1964 "%s is not a valid boolean for leg flag onshell" % \
1965 str(value)
1966 return True
1967
1969 """Return particle property names as a nicely sorted list."""
1970
1971 return ['id', 'number', 'state', 'from_group', 'loop_line', 'onshell']
1972
1974 """Returns True if the particle corresponding to the leg is a
1975 fermion"""
1976
1977 assert isinstance(model, Model), "%s is not a model" % str(model)
1978
1979 return model.get('particle_dict')[self['id']].is_fermion()
1980
1982 """Returns True if leg is an incoming fermion, i.e., initial
1983 particle or final antiparticle"""
1984
1985 assert isinstance(model, Model), "%s is not a model" % str(model)
1986
1987 part = model.get('particle_dict')[self['id']]
1988 return part.is_fermion() and \
1989 (self.get('state') == False and part.get('is_part') or \
1990 self.get('state') == True and not part.get('is_part'))
1991
1993 """Returns True if leg is an outgoing fermion, i.e., initial
1994 antiparticle or final particle"""
1995
1996 assert isinstance(model, Model), "%s is not a model" % str(model)
1997
1998 part = model.get('particle_dict')[self['id']]
1999 return part.is_fermion() and \
2000 (self.get('state') == True and part.get('is_part') or \
2001 self.get('state') == False and not part.get('is_part'))
2002
2003
2004
2005
2006 - def same(self, leg):
2007 """ Returns true if the leg in argument has the same ID and the same numer """
2008
2009
2010
2011 if isinstance(leg,int):
2012 if self['number']==leg:
2013 return True
2014 else:
2015 return False
2016
2017
2018
2019 elif isinstance(leg, Leg):
2020 if self['id']==leg.get('id') and \
2021 self['number']==leg.get('number') and \
2022 self['loop_line']==leg.get('loop_line') :
2023 return True
2024 else:
2025 return False
2026
2027 else :
2028 return False
2029
2030
2032 return self['number'] < other['number']
2033
2034
2035
2036
2037 -class LegList(PhysicsObjectList):
2038 """List of Leg objects
2039 """
2040
2042 """Test if object obj is a valid Leg for the list."""
2043
2044 return isinstance(obj, Leg)
2045
2046
2047
2049 """Return all elements which have 'from_group' True"""
2050
2051 return filter(lambda leg: leg.get('from_group'), self)
2052
2054 """Return True if at least one element has 'from_group' True"""
2055
2056 return len(self.from_group_elements()) > 0
2057
2059 """Return True if at least two elements have 'from_group' True"""
2060
2061 return len(self.from_group_elements()) > 1
2062
2064 """If has at least one 'from_group' True and in ref_dict_to1,
2065 return the return list from ref_dict_to1, otherwise return False"""
2066 if self.minimum_one_from_group():
2067 return ref_dict_to1.has_key(tuple(sorted([leg.get('id') for leg in self])))
2068 else:
2069 return False
2070
2072 """If has at least two 'from_group' True and in ref_dict_to0,
2073
2074 return the vertex (with id from ref_dict_to0), otherwise return None
2075
2076 If is_decay_chain = True, we only allow clustering of the
2077 initial leg, since we want this to be the last wavefunction to
2078 be evaluated.
2079 """
2080 if is_decay_chain:
2081
2082
2083
2084
2085 return any(leg.get('from_group') == None for leg in self) and \
2086 ref_dict_to0.has_key(tuple(sorted([leg.get('id') \
2087 for leg in self])))
2088
2089 if self.minimum_two_from_group():
2090 return ref_dict_to0.has_key(tuple(sorted([leg.get('id') for leg in self])))
2091 else:
2092 return False
2093
2095 """Returns the list of ids corresponding to the leglist with
2096 all particles outgoing"""
2097
2098 res = []
2099
2100 assert isinstance(model, Model), "Error! model not model"
2101
2102
2103 for leg in self:
2104 if leg.get('state') == False:
2105 res.append(model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
2106 else:
2107 res.append(leg.get('id'))
2108
2109 return res
2110
2111 - def sort(self,*args, **opts):
2112 """Match with FKSLegList"""
2113 Opts=copy.copy(opts)
2114 if 'pert' in Opts.keys():
2115 del Opts['pert']
2116 return super(LegList,self).sort(*args, **Opts)
2117
2118
2119
2120
2121
2122 -class MultiLeg(PhysicsObject):
2123 """MultiLeg object: ids (Particle or particles), I/F state
2124 """
2125
2127 """Default values for all properties"""
2128
2129 self['ids'] = []
2130 self['state'] = True
2131
2132 - def filter(self, name, value):
2133 """Filter for valid multileg property values."""
2134
2135 if name == 'ids':
2136 if not isinstance(value, list):
2137 raise self.PhysicsObjectError, \
2138 "%s is not a valid list" % str(value)
2139 for i in value:
2140 if not isinstance(i, int):
2141 raise self.PhysicsObjectError, \
2142 "%s is not a valid list of integers" % str(value)
2143
2144 if name == 'state':
2145 if not isinstance(value, bool):
2146 raise self.PhysicsObjectError, \
2147 "%s is not a valid leg state (initial|final)" % \
2148 str(value)
2149
2150 return True
2151
2153 """Return particle property names as a nicely sorted list."""
2154
2155 return ['ids', 'state']
2156
2161 """List of MultiLeg objects
2162 """
2163
2165 """Test if object obj is a valid MultiLeg for the list."""
2166
2167 return isinstance(obj, MultiLeg)
2168
2169
2170
2171
2172 -class Vertex(PhysicsObject):
2173 """Vertex: list of legs (ordered), id (Interaction)
2174 """
2175
2176 sorted_keys = ['id', 'legs']
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186 ID_to_veto_for_multichanneling = [0,-1,-2]
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196 max_n_loop_for_multichanneling = 4
2197
2199 """Default values for all properties"""
2200
2201
2202
2203
2204
2205
2206
2207
2208 self['id'] = 0
2209 self['legs'] = LegList()
2210
2211 - def filter(self, name, value):
2212 """Filter for valid vertex property values."""
2213
2214 if name == 'id':
2215 if not isinstance(value, int):
2216 raise self.PhysicsObjectError, \
2217 "%s is not a valid integer for vertex id" % str(value)
2218
2219 if name == 'legs':
2220 if not isinstance(value, LegList):
2221 raise self.PhysicsObjectError, \
2222 "%s is not a valid LegList object" % str(value)
2223
2224 return True
2225
2227 """Return particle property names as a nicely sorted list."""
2228
2229 return self.sorted_keys
2230
2232 """return a nice string"""
2233
2234 mystr = []
2235 for leg in self['legs']:
2236 mystr.append( str(leg['number']) + '(%s)' % str(leg['id']))
2237 mystr = '(%s,id=%s ,obj_id:%s)' % (', '.join(mystr), self['id'], id(self))
2238
2239 return(mystr)
2240
2241
2243 """Returns the id for the last leg as an outgoing
2244 s-channel. Returns 0 if leg is t-channel, or if identity
2245 vertex. Used to check for required and forbidden s-channel
2246 particles."""
2247
2248 leg = self.get('legs')[-1]
2249
2250 if ninitial == 1:
2251
2252
2253 if leg.get('state') == True:
2254 return leg.get('id')
2255 else:
2256 return model.get('particle_dict')[leg.get('id')].\
2257 get_anti_pdg_code()
2258
2259
2260 if self.get('id') == 0 or \
2261 leg.get('state') == False:
2262
2263 return 0
2264
2265 if leg.get('loop_line'):
2266
2267 return 0
2268
2269
2270
2271 if leg.get('number') > ninitial:
2272 return leg.get('id')
2273 else:
2274 return model.get('particle_dict')[leg.get('id')].\
2275 get_anti_pdg_code()
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288 -class VertexList(PhysicsObjectList):
2289 """List of Vertex objects
2290 """
2291
2292 orders = {}
2293
2295 """Test if object obj is a valid Vertex for the list."""
2296
2297 return isinstance(obj, Vertex)
2298
2299 - def __init__(self, init_list=None, orders=None):
2300 """Creates a new list object, with an optional dictionary of
2301 coupling orders."""
2302
2303 list.__init__(self)
2304
2305 if init_list is not None:
2306 for object in init_list:
2307 self.append(object)
2308
2309 if isinstance(orders, dict):
2310 self.orders = orders
2311
2316 """ContractedVertex: When contracting a loop to a given vertex, the created
2317 vertex object is then a ContractedVertex object which has additional
2318 information with respect to a regular vertex object. For example, it contains
2319 the PDG of the particles attached to it. (necessary because the contracted
2320 vertex doesn't have an interaction ID which would allow to retrieve such
2321 information).
2322 """
2323
2325 """Default values for all properties"""
2326
2327 self['PDGs'] = []
2328 self['loop_tag'] = tuple()
2329 self['loop_orders'] = {}
2330 super(ContractedVertex, self).default_setup()
2331
2332 - def filter(self, name, value):
2333 """Filter for valid vertex property values."""
2334
2335 if name == 'PDGs':
2336 if isinstance(value, list):
2337 for elem in value:
2338 if not isinstance(elem,int):
2339 raise self.PhysicsObjectError, \
2340 "%s is not a valid integer for leg PDG" % str(elem)
2341 else:
2342 raise self.PhysicsObjectError, \
2343 "%s is not a valid list for contracted vertex PDGs"%str(value)
2344 if name == 'loop_tag':
2345 if isinstance(value, tuple):
2346 for elem in value:
2347 if not (isinstance(elem,int) or isinstance(elem,tuple)):
2348 raise self.PhysicsObjectError, \
2349 "%s is not a valid int or tuple for loop tag element"%str(elem)
2350 else:
2351 raise self.PhysicsObjectError, \
2352 "%s is not a valid tuple for a contracted vertex loop_tag."%str(value)
2353 if name == 'loop_orders':
2354 Interaction.filter(Interaction(), 'orders', value)
2355 else:
2356 return super(ContractedVertex, self).filter(name, value)
2357
2358 return True
2359
2364
2365
2366
2367
2368 -class Diagram(PhysicsObject):
2369 """Diagram: list of vertices (ordered)
2370 """
2371
2373 """Default values for all properties"""
2374
2375 self['vertices'] = VertexList()
2376 self['orders'] = {}
2377
2378 - def filter(self, name, value):
2390
2392 """Return particle property names as a nicely sorted list."""
2393
2394 return ['vertices', 'orders']
2395
2397 """Returns a nicely formatted string of the diagram content."""
2398
2399 pass_sanity = True
2400 if self['vertices']:
2401 mystr = '('
2402 for vert in self['vertices']:
2403 used_leg = []
2404 mystr = mystr + '('
2405 for leg in vert['legs'][:-1]:
2406 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
2407 used_leg.append(leg['number'])
2408 if __debug__ and len(used_leg) != len(set(used_leg)):
2409 pass_sanity = False
2410 responsible = id(vert)
2411
2412 if self['vertices'].index(vert) < len(self['vertices']) - 1:
2413
2414 mystr = mystr[:-1] + '>'
2415 mystr = mystr + str(vert['legs'][-1]['number']) + '(%s)' % str(vert['legs'][-1]['id']) + ','
2416 mystr = mystr + 'id:' + str(vert['id']) + '),'
2417
2418 mystr = mystr[:-1] + ')'
2419 mystr += " (%s)" % (",".join(["%s=%d" % (key, self['orders'][key]) \
2420 for key in sorted(self['orders'].keys())]))
2421
2422 if not pass_sanity:
2423 raise Exception, "invalid diagram: %s. vert_id: %s" % (mystr, responsible)
2424
2425 return mystr
2426 else:
2427 return '()'
2428
2430 """Calculate the actual coupling orders of this diagram. Note
2431 that the special order WEIGTHED corresponds to the sum of
2432 hierarchys for the couplings."""
2433
2434 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
2435 weight = 0
2436 for vertex in self['vertices']:
2437 if vertex.get('id') in [0,-1]: continue
2438 if vertex.get('id') == -2:
2439 couplings = vertex.get('loop_orders')
2440 else:
2441 couplings = model.get('interaction_dict')[vertex.get('id')].\
2442 get('orders')
2443 for coupling in couplings:
2444 coupling_orders[coupling] += couplings[coupling]
2445 weight += sum([model.get('order_hierarchy')[c]*n for \
2446 (c,n) in couplings.items()])
2447 coupling_orders['WEIGHTED'] = weight
2448 self.set('orders', coupling_orders)
2449
2452 """ Returns wether the contributiong consisting in the current diagram
2453 multiplied by diag_multiplier passes the *positive* squared_orders
2454 specified ( a dictionary ) of types sq_order_types (a dictionary whose
2455 values are the relational operator used to define the constraint of the
2456 order in key)."""
2457
2458 for order, value in squared_orders.items():
2459 if value<0:
2460 continue
2461 combined_order = self.get_order(order) + \
2462 diag_multiplier.get_order(order)
2463 if ( sq_orders_types[order]=='==' and combined_order != value ) or \
2464 ( sq_orders_types[order] in ['=', '<='] and combined_order > value) or \
2465 ( sq_orders_types[order]=='>' and combined_order <= value) :
2466 return False
2467 return True
2468
2470 """Return the order of this diagram. It returns 0 if it is not present."""
2471
2472 try:
2473 return self['orders'][order]
2474 except Exception:
2475 return 0
2476
2478 """ Returns a Diagram which correspond to the loop diagram with the
2479 loop shrunk to a point. Of course for a instance of base_objects.Diagram
2480 one must simply return self."""
2481
2482 return self
2483
2485 """ Return the list of external legs of this diagram """
2486
2487 external_legs = LegList([])
2488 for leg in sum([vert.get('legs') for vert in self.get('vertices')],[]):
2489 if not leg.get('number') in [l.get('number') for l in external_legs]:
2490 external_legs.append(leg)
2491
2492 return external_legs
2493
2495 """Renumber legs in all vertices according to perm_map"""
2496
2497 vertices = VertexList()
2498 min_dict = copy.copy(perm_map)
2499
2500 state_dict = dict([(l.get('number'), l.get('state')) for l in leg_list])
2501
2502 for vertex in self.get('vertices')[:-1]:
2503 vertex = copy.copy(vertex)
2504 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2505 for leg in leg_list[:-1]:
2506 leg.set('number', min_dict[leg.get('number')])
2507 leg.set('state', state_dict[leg.get('number')])
2508 min_number = min([leg.get('number') for leg in leg_list[:-1]])
2509 leg = leg_list[-1]
2510 min_dict[leg.get('number')] = min_number
2511
2512
2513 state_dict[min_number] = len([l for l in leg_list[:-1] if \
2514 not l.get('state')]) != 1
2515 leg.set('number', min_number)
2516 leg.set('state', state_dict[min_number])
2517 vertex.set('legs', leg_list)
2518 vertices.append(vertex)
2519
2520 vertex = copy.copy(self.get('vertices')[-1])
2521 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2522 for leg in leg_list:
2523 leg.set('number', min_dict[leg.get('number')])
2524 leg.set('state', state_dict[leg.get('number')])
2525 vertex.set('legs', leg_list)
2526 vertices.append(vertex)
2527
2528 new_diag = copy.copy(self)
2529 new_diag.set('vertices', vertices)
2530 state_dict = {True:'T',False:'F'}
2531 return new_diag
2532
2536 """Return a list of the number of legs in the vertices for
2537 this diagram.
2538 This function is only used for establishing the multi-channeling, so that
2539 we exclude from it all the fake vertices and the vertices resulting from
2540 shrunk loops (id=-2)"""
2541
2542
2543 if max_n_loop == 0:
2544 max_n_loop = Vertex.max_n_loop_for_multichanneling
2545
2546 res = [len(v.get('legs')) for v in self.get('vertices') if (v.get('id') \
2547 not in veto_inter_id) or (v.get('id')==-2 and
2548 len(v.get('legs'))>max_n_loop)]
2549
2550 return res
2551
2553 """Return the maximum number of configs from this diagram,
2554 given by 2^(number of non-zero width s-channel propagators)"""
2555
2556 s_channels = [v.get_s_channel_id(model,ninitial) for v in \
2557 self.get('vertices')[:-1]]
2558 num_props = len([i for i in s_channels if i != 0 and \
2559 model.get_particle(i).get('width').lower() != 'zero'])
2560
2561 if num_props < 1:
2562 return 1
2563 else:
2564 return 2**num_props
2565
2567 """return the difference of total diff of charge occuring on the
2568 lofw of the initial parton. return [None,None] if the two initial parton
2569 are connected and the (partial) value if None if the initial parton is
2570 not a fermiom"""
2571
2572 import madgraph.core.drawing as drawing
2573 drawdiag = drawing.FeynmanDiagram(self, model)
2574 drawdiag.load_diagram()
2575 out = []
2576
2577 for v in drawdiag.initial_vertex:
2578 init_part = v.lines[0]
2579 if not init_part.is_fermion():
2580 out.append(None)
2581 continue
2582
2583 init_charge = model.get_particle(init_part.id).get('charge')
2584
2585 l_last = init_part
2586 v_last = v
2587 vcurrent = l_last.end
2588 if vcurrent == v:
2589 vcurrent = l_last.begin
2590 security =0
2591 while not vcurrent.is_external():
2592 if security > 1000:
2593 raise Exception, 'wrong diagram'
2594 next_l = [l for l in vcurrent.lines if l is not l_last and l.is_fermion()][0]
2595 next_v = next_l.end
2596 if next_v == vcurrent:
2597 next_v = next_l.begin
2598 l_last, vcurrent = next_l, next_v
2599 if vcurrent in drawdiag.initial_vertex:
2600 return [None, None]
2601
2602 out.append(model.get_particle(l_last.id).get('charge') - init_charge)
2603 return out
2604
2605
2606
2607
2608
2609 -class DiagramList(PhysicsObjectList):
2610 """List of Diagram objects
2611 """
2612
2614 """Test if object obj is a valid Diagram for the list."""
2615
2616 return isinstance(obj, Diagram)
2617
2619 """Returns a nicely formatted string"""
2620 mystr = " " * indent + str(len(self)) + ' diagrams:\n'
2621 for i, diag in enumerate(self):
2622 mystr = mystr + " " * indent + str(i+1) + " " + \
2623 diag.nice_string() + '\n'
2624 return mystr[:-1]
2625
2626
2627
2629 """ Return the order of the diagram in the list with the maximum coupling
2630 order for the coupling specified """
2631 max_order=-1
2632
2633 for diag in self:
2634 if order in diag['orders'].keys():
2635 if max_order==-1 or diag['orders'][order] > max_order:
2636 max_order = diag['orders'][order]
2637
2638 return max_order
2639
2641 """ This function returns a fitlered version of the diagram list self
2642 which satisfy the negative squared_order constraint 'order' with negative
2643 value 'value' and of type 'order_type', assuming that the diagram_list
2644 it must be squared against is 'reg_diag_list'. It also returns the
2645 new postive target squared order which correspond to this negative order
2646 constraint. Example: u u~ > d d~ QED^2<=-2 means that one wants to
2647 pick terms only up to the the next-to-leading order contributiong in QED,
2648 which is QED=2 in this case, so that target_order=4 is returned."""
2649
2650
2651 target_order = min(ref_diag_list.get_order_values(order))+\
2652 min(self.get_order_values(order))+2*(-value-1)
2653
2654 new_list = self.apply_positive_sq_orders(ref_diag_list,
2655 {order:target_order}, {order:order_type})
2656
2657 return new_list, target_order
2658
2660 """ This function returns a filtered version of self which contain
2661 only the diagram which satisfy the positive squared order constraints
2662 sq_orders of type sq_order_types and assuming that the diagrams are
2663 multiplied with those of the reference diagram list ref_diag_list."""
2664
2665 new_diag_list = DiagramList()
2666 for tested_diag in self:
2667 for ref_diag in ref_diag_list:
2668 if tested_diag.pass_squared_order_constraints(ref_diag,
2669 sq_orders,sq_order_types):
2670 new_diag_list.append(tested_diag)
2671 break
2672 return new_diag_list
2673
2675 """ Return the order of the diagram in the list with the mimimum coupling
2676 order for the coupling specified """
2677 min_order=-1
2678 for diag in self:
2679 if order in diag['orders'].keys():
2680 if min_order==-1 or diag['orders'][order] < min_order:
2681 min_order = diag['orders'][order]
2682 else:
2683 return 0
2684
2685 return min_order
2686
2688 """ Return the list of possible values appearing in the diagrams of this
2689 list for the order given in argument """
2690
2691 values=set([])
2692 for diag in self:
2693 if order in diag['orders'].keys():
2694 values.add(diag['orders'][order])
2695 else:
2696 values.add(0)
2697
2698 return list(values)
2699
2700
2701
2702
2703 -class Process(PhysicsObject):
2704 """Process: list of legs (ordered)
2705 dictionary of orders
2706 model
2707 process id
2708 """
2709
2711 """Default values for all properties"""
2712
2713 self['legs'] = LegList()
2714
2715 self['orders'] = {}
2716 self['model'] = Model()
2717
2718 self['id'] = 0
2719 self['uid'] = 0
2720
2721
2722
2723
2724 self['required_s_channels'] = []
2725 self['forbidden_onsh_s_channels'] = []
2726 self['forbidden_s_channels'] = []
2727 self['forbidden_particles'] = []
2728 self['is_decay_chain'] = False
2729 self['overall_orders'] = {}
2730
2731 self['decay_chains'] = ProcessList()
2732
2733 self['legs_with_decays'] = LegList()
2734
2735 self['perturbation_couplings']=[]
2736
2737
2738
2739
2740 self['squared_orders'] = {}
2741
2742
2743
2744
2745 self['sqorders_types'] = {}
2746 self['has_born'] = True
2747
2748
2749 self['NLO_mode'] = 'tree'
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759 self['split_orders'] = []
2760
2761 - def filter(self, name, value):
2762 """Filter for valid process property values."""
2763
2764 if name in ['legs', 'legs_with_decays'] :
2765 if not isinstance(value, LegList):
2766 raise self.PhysicsObjectError, \
2767 "%s is not a valid LegList object" % str(value)
2768
2769 if name in ['orders', 'overall_orders','squared_orders']:
2770 Interaction.filter(Interaction(), 'orders', value)
2771
2772 if name == 'sqorders_types':
2773 if not isinstance(value, dict):
2774 raise self.PhysicsObjectError, \
2775 "%s is not a valid dictionary" % str(value)
2776 for order in value.keys()+value.values():
2777 if not isinstance(order, str):
2778 raise self.PhysicsObjectError, \
2779 "%s is not a valid string" % str(value)
2780
2781 if name == 'split_orders':
2782 if not isinstance(value, list):
2783 raise self.PhysicsObjectError, \
2784 "%s is not a valid list" % str(value)
2785 for order in value:
2786 if not isinstance(order, str):
2787 raise self.PhysicsObjectError, \
2788 "%s is not a valid string" % str(value)
2789
2790 if name == 'model':
2791 if not isinstance(value, Model):
2792 raise self.PhysicsObjectError, \
2793 "%s is not a valid Model object" % str(value)
2794 if name in ['id', 'uid']:
2795 if not isinstance(value, int):
2796 raise self.PhysicsObjectError, \
2797 "Process %s %s is not an integer" % (name, repr(value))
2798
2799 if name == 'required_s_channels':
2800 if not isinstance(value, list):
2801 raise self.PhysicsObjectError, \
2802 "%s is not a valid list" % str(value)
2803 for l in value:
2804 if not isinstance(l, list):
2805 raise self.PhysicsObjectError, \
2806 "%s is not a valid list of lists" % str(value)
2807 for i in l:
2808 if not isinstance(i, int):
2809 raise self.PhysicsObjectError, \
2810 "%s is not a valid list of integers" % str(l)
2811 if i == 0:
2812 raise self.PhysicsObjectError, \
2813 "Not valid PDG code %d for s-channel particle" % i
2814
2815 if name in ['forbidden_onsh_s_channels', 'forbidden_s_channels']:
2816 if not isinstance(value, list):
2817 raise self.PhysicsObjectError, \
2818 "%s is not a valid list" % str(value)
2819 for i in value:
2820 if not isinstance(i, int):
2821 raise self.PhysicsObjectError, \
2822 "%s is not a valid list of integers" % str(value)
2823 if i == 0:
2824 raise self.PhysicsObjectError, \
2825 "Not valid PDG code %d for s-channel particle" % str(value)
2826
2827 if name == 'forbidden_particles':
2828 if not isinstance(value, list):
2829 raise self.PhysicsObjectError, \
2830 "%s is not a valid list" % str(value)
2831 for i in value:
2832 if not isinstance(i, int):
2833 raise self.PhysicsObjectError, \
2834 "%s is not a valid list of integers" % str(value)
2835 if i <= 0:
2836 raise self.PhysicsObjectError, \
2837 "Forbidden particles should have a positive PDG code" % str(value)
2838
2839 if name == 'perturbation_couplings':
2840 if not isinstance(value, list):
2841 raise self.PhysicsObjectError, \
2842 "%s is not a valid list" % str(value)
2843 for order in value:
2844 if not isinstance(order, str):
2845 raise self.PhysicsObjectError, \
2846 "%s is not a valid string" % str(value)
2847
2848 if name == 'is_decay_chain':
2849 if not isinstance(value, bool):
2850 raise self.PhysicsObjectError, \
2851 "%s is not a valid bool" % str(value)
2852
2853 if name == 'has_born':
2854 if not isinstance(value, bool):
2855 raise self.PhysicsObjectError, \
2856 "%s is not a valid bool" % str(value)
2857
2858 if name == 'decay_chains':
2859 if not isinstance(value, ProcessList):
2860 raise self.PhysicsObjectError, \
2861 "%s is not a valid ProcessList" % str(value)
2862
2863 if name == 'NLO_mode':
2864 import madgraph.interface.madgraph_interface as mg
2865 if value not in mg.MadGraphCmd._valid_nlo_modes:
2866 raise self.PhysicsObjectError, \
2867 "%s is not a valid NLO_mode" % str(value)
2868 return True
2869
2871 """ A process, not being a ProcessDefinition never carries multiple
2872 particles labels"""
2873
2874 return False
2875
2876 - def set(self, name, value):
2877 """Special set for forbidden particles - set to abs value."""
2878
2879 if name == 'forbidden_particles':
2880 try:
2881 value = [abs(i) for i in value]
2882 except Exception:
2883 pass
2884
2885 if name == 'required_s_channels':
2886
2887 if value and isinstance(value, list) and \
2888 not isinstance(value[0], list):
2889 value = [value]
2890
2891 return super(Process, self).set(name, value)
2892
2894 """ Return what kind of squared order constraint was specified for the
2895 order 'order'."""
2896
2897 if order in self['sqorders_types'].keys():
2898 return self['sqorders_types'][order]
2899 else:
2900
2901 return '='
2902
2903 - def get(self, name):
2904 """Special get for legs_with_decays"""
2905
2906 if name == 'legs_with_decays':
2907 self.get_legs_with_decays()
2908
2909 if name == 'sqorders_types':
2910
2911 for order in self['squared_orders'].keys():
2912 if order not in self['sqorders_types']:
2913
2914 self['sqorders_types'][order]='='
2915
2916 return super(Process, self).get(name)
2917
2919 """Return process property names as a nicely sorted list."""
2920
2921 return ['legs', 'orders', 'overall_orders', 'squared_orders',
2922 'model', 'id', 'required_s_channels',
2923 'forbidden_onsh_s_channels', 'forbidden_s_channels',
2924 'forbidden_particles', 'is_decay_chain', 'decay_chains',
2925 'legs_with_decays', 'perturbation_couplings', 'has_born',
2926 'NLO_mode','split_orders']
2927
2928 - def nice_string(self, indent=0, print_weighted = True):
2929 """Returns a nicely formated string about current process
2930 content. Since the WEIGHTED order is automatically set and added to
2931 the user-defined list of orders, it can be ommitted for some info
2932 displays."""
2933
2934 mystr = " " * indent + "Process: "
2935 prevleg = None
2936 for leg in self['legs']:
2937 mypart = self['model'].get('particle_dict')[leg['id']]
2938 if prevleg and prevleg['state'] == False \
2939 and leg['state'] == True:
2940
2941 mystr = mystr + '> '
2942
2943 if self['required_s_channels'] and \
2944 self['required_s_channels'][0]:
2945 mystr += "|".join([" ".join([self['model'].\
2946 get('particle_dict')[req_id].get_name() \
2947 for req_id in id_list]) \
2948 for id_list in self['required_s_channels']])
2949 mystr = mystr + ' > '
2950
2951 mystr = mystr + mypart.get_name() + ' '
2952
2953 prevleg = leg
2954
2955
2956 if self['orders']:
2957 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
2958 for key in self['orders'] if (print_weighted or key!='WEIGHTED') \
2959 and not key in self['squared_orders'].keys()]) + ' '
2960
2961
2962 if self['perturbation_couplings']:
2963 mystr = mystr + '[ '
2964 if self['NLO_mode']!='tree':
2965 if self['NLO_mode']=='virt' and not self['has_born']:
2966 mystr = mystr + 'sqrvirt = '
2967 else:
2968 mystr = mystr + self['NLO_mode'] + ' = '
2969 for order in self['perturbation_couplings']:
2970 mystr = mystr + order + ' '
2971 mystr = mystr + '] '
2972
2973
2974 if self['squared_orders']:
2975 mystr = mystr + " ".join([key + '^2%s%d'%\
2976 (self.get_squared_order_type(key),self['squared_orders'][key]) \
2977 for key in self['squared_orders'].keys() \
2978 if print_weighted or key!='WEIGHTED']) + ' '
2979
2980
2981 if self['forbidden_onsh_s_channels']:
2982 mystr = mystr + '$ '
2983 for forb_id in self['forbidden_onsh_s_channels']:
2984 forbpart = self['model'].get('particle_dict')[forb_id]
2985 mystr = mystr + forbpart.get_name() + ' '
2986
2987
2988 if self['forbidden_s_channels']:
2989 mystr = mystr + '$$ '
2990 for forb_id in self['forbidden_s_channels']:
2991 forbpart = self['model'].get('particle_dict')[forb_id]
2992 mystr = mystr + forbpart.get_name() + ' '
2993
2994
2995 if self['forbidden_particles']:
2996 mystr = mystr + '/ '
2997 for forb_id in self['forbidden_particles']:
2998 forbpart = self['model'].get('particle_dict')[forb_id]
2999 mystr = mystr + forbpart.get_name() + ' '
3000
3001
3002 mystr = mystr[:-1]
3003
3004 if self.get('id') or self.get('overall_orders'):
3005 mystr += " @%d" % self.get('id')
3006 if self.get('overall_orders'):
3007 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3008 for key in sorted(self['orders'])]) + ' '
3009
3010 if not self.get('decay_chains'):
3011 return mystr
3012
3013 for decay in self['decay_chains']:
3014 mystr = mystr + '\n' + \
3015 decay.nice_string(indent + 2).replace('Process', 'Decay')
3016
3017 return mystr
3018
3109
3111 """Returns a string containing only the basic process (w/o decays)."""
3112
3113 mystr = ""
3114 prevleg = None
3115 for leg in self.get_legs_with_decays():
3116 mypart = self['model'].get('particle_dict')[leg['id']]
3117 if prevleg and prevleg['state'] == False \
3118 and leg['state'] == True:
3119
3120 mystr = mystr + '> '
3121 mystr = mystr + mypart.get_name() + ' '
3122 prevleg = leg
3123
3124
3125 return mystr[:-1]
3126
3127 - def shell_string(self, schannel=True, forbid=True, main=True, pdg_order=False,
3128 print_id = True):
3129 """Returns process as string with '~' -> 'x', '>' -> '_',
3130 '+' -> 'p' and '-' -> 'm', including process number,
3131 intermediate s-channels and forbidden particles,
3132 pdg_order allow to order to leg order by pid."""
3133
3134 mystr = ""
3135 if not self.get('is_decay_chain') and print_id:
3136 mystr += "%d_" % self['id']
3137
3138 prevleg = None
3139 if pdg_order:
3140 legs = [l for l in self['legs'][1:]]
3141 def order_leg(l1,l2):
3142 id1 = l1.get('id')
3143 id2 = l2.get('id')
3144 return id2-id1
3145 legs.sort(cmp=order_leg)
3146 legs.insert(0, self['legs'][0])
3147 else:
3148 legs = self['legs']
3149
3150
3151 for leg in legs:
3152 mypart = self['model'].get('particle_dict')[leg['id']]
3153 if prevleg and prevleg['state'] == False \
3154 and leg['state'] == True:
3155
3156 mystr = mystr + '_'
3157
3158 if self['required_s_channels'] and \
3159 self['required_s_channels'][0] and schannel:
3160 mystr += "_or_".join(["".join([self['model'].\
3161 get('particle_dict')[req_id].get_name() \
3162 for req_id in id_list]) \
3163 for id_list in self['required_s_channels']])
3164 mystr = mystr + '_'
3165 if mypart['is_part']:
3166 mystr = mystr + mypart['name']
3167 else:
3168 mystr = mystr + mypart['antiname']
3169 prevleg = leg
3170
3171
3172 if self['forbidden_particles'] and forbid:
3173 mystr = mystr + '_no_'
3174 for forb_id in self['forbidden_particles']:
3175 forbpart = self['model'].get('particle_dict')[forb_id]
3176 mystr = mystr + forbpart.get_name()
3177
3178
3179 mystr = mystr.replace('~', 'x')
3180
3181 mystr = mystr.replace('+', 'p')
3182
3183 mystr = mystr.replace('-', 'm')
3184
3185 mystr = mystr.replace(' ', '')
3186
3187 for decay in self.get('decay_chains'):
3188 mystr = mystr + "_" + decay.shell_string(schannel,forbid, main=False,
3189 pdg_order=pdg_order)
3190
3191
3192 if len(mystr) > 64 and main:
3193 if schannel and forbid:
3194 out = self.shell_string(True, False, True, pdg_order)
3195 elif schannel:
3196 out = self.shell_string(False, False, True, pdg_order)
3197 else:
3198 out = mystr[:64]
3199 if not out.endswith('_%s' % self['uid']):
3200 out += '_%s' % self['uid']
3201 return out
3202
3203 return mystr
3204
3206 """Returns process as v4-compliant string with '~' -> 'x' and
3207 '>' -> '_'"""
3208
3209 mystr = "%d_" % self['id']
3210 prevleg = None
3211 for leg in self.get_legs_with_decays():
3212 mypart = self['model'].get('particle_dict')[leg['id']]
3213 if prevleg and prevleg['state'] == False \
3214 and leg['state'] == True:
3215
3216 mystr = mystr + '_'
3217 if mypart['is_part']:
3218 mystr = mystr + mypart['name']
3219 else:
3220 mystr = mystr + mypart['antiname']
3221 prevleg = leg
3222
3223
3224 mystr = mystr.replace('~', 'x')
3225
3226 mystr = mystr.replace(' ', '')
3227
3228 return mystr
3229
3230
3231
3233 """ Check iteratively that no coupling order constraint include negative
3234 values."""
3235
3236 if any(val<0 for val in self.get('orders').values()+\
3237 self.get('squared_orders').values()):
3238 return True
3239
3240 for procdef in self['decay_chains']:
3241 if procdef.are_negative_orders_present():
3242 return True
3243
3244 return False
3245
3247 """ Check iteratively that the decayed processes are not perturbed """
3248
3249 for procdef in self['decay_chains']:
3250 if procdef['perturbation_couplings'] or procdef.are_decays_perturbed():
3251 return True
3252 return False
3253
3255 """ Check iteratively that the decayed processes are not perturbed """
3256
3257 for procdef in self['decay_chains']:
3258 if procdef['squared_orders']!={} or procdef.decays_have_squared_orders():
3259 return True
3260 return False
3261
3263 """Gives number of initial state particles"""
3264
3265 return len(filter(lambda leg: leg.get('state') == False,
3266 self.get('legs')))
3267
3269 """Gives the pdg codes for initial state particles"""
3270
3271 return [leg.get('id') for leg in \
3272 filter(lambda leg: leg.get('state') == False,
3273 self.get('legs'))]
3274
3276 """Return the pdg codes for initial state particles for beam number"""
3277
3278 return filter(lambda leg: leg.get('state') == False and\
3279 leg.get('number') == number,
3280 self.get('legs'))[0].get('id')
3281
3283 """return a tuple of two tuple containing the id of the initial/final
3284 state particles. Each list is ordered"""
3285
3286 initial = []
3287 final = [l.get('id') for l in self.get('legs')\
3288 if l.get('state') or initial.append(l.get('id'))]
3289 initial.sort()
3290 final.sort()
3291 return (tuple(initial), tuple(final))
3292
3313
3314
3316 """Gives the final state legs"""
3317
3318 return filter(lambda leg: leg.get('state') == True,
3319 self.get('legs'))
3320
3322 """Gives the pdg codes for final state particles"""
3323
3324 return [l.get('id') for l in self.get_final_legs()]
3325
3326
3328 """Return process with all decay chains substituted in."""
3329
3330 if self['legs_with_decays']:
3331 return self['legs_with_decays']
3332
3333 legs = copy.deepcopy(self.get('legs'))
3334 org_decay_chains = copy.copy(self.get('decay_chains'))
3335 sorted_decay_chains = []
3336
3337 for leg in legs:
3338 if not leg.get('state'): continue
3339 org_ids = [l.get('legs')[0].get('id') for l in \
3340 org_decay_chains]
3341 if leg.get('id') in org_ids:
3342 sorted_decay_chains.append(org_decay_chains.pop(\
3343 org_ids.index(leg.get('id'))))
3344 assert not org_decay_chains
3345 ileg = 0
3346 for decay in sorted_decay_chains:
3347 while legs[ileg].get('state') == False or \
3348 legs[ileg].get('id') != decay.get('legs')[0].get('id'):
3349 ileg = ileg + 1
3350 decay_legs = decay.get_legs_with_decays()
3351 legs = legs[:ileg] + decay_legs[1:] + legs[ileg+1:]
3352 ileg = ileg + len(decay_legs) - 1
3353
3354
3355 legs = [copy.copy(l) for l in legs]
3356
3357 for ileg, leg in enumerate(legs):
3358 leg.set('number', ileg + 1)
3359
3360 self['legs_with_decays'] = LegList(legs)
3361
3362 return self['legs_with_decays']
3363
3365 """Output a list that can be compared to other processes as:
3366 [id, sorted(initial leg ids), sorted(final leg ids),
3367 sorted(decay list_for_sorts)]"""
3368
3369 sorted_list = [self.get('id'),
3370 sorted(self.get_initial_ids()),
3371 sorted(self.get_final_ids())]
3372
3373 if self.get('decay_chains'):
3374 sorted_list.extend(sorted([d.list_for_sort() for d in \
3375 self.get('decay_chains')]))
3376
3377 return sorted_list
3378
3380 """Sorting routine which allows to sort processes for
3381 comparison. Compare only process id and legs."""
3382
3383 if self.list_for_sort() > other.list_for_sort():
3384 return 1
3385 if self.list_for_sort() < other.list_for_sort():
3386 return -1
3387 return 0
3388
3390 """Calculate the denominator factor for identical final state particles
3391 """
3392
3393 final_legs = filter(lambda leg: leg.get('state') == True, \
3394 self.get_legs_with_decays())
3395
3396 identical_indices = {}
3397 for leg in final_legs:
3398 if leg.get('id') in identical_indices:
3399 identical_indices[leg.get('id')] = \
3400 identical_indices[leg.get('id')] + 1
3401 else:
3402 identical_indices[leg.get('id')] = 1
3403 return reduce(lambda x, y: x * y, [ math.factorial(val) for val in \
3404 identical_indices.values() ], 1)
3405
3407 """Ensure that maximum expansion orders from the model are
3408 properly taken into account in the process"""
3409
3410
3411 expansion_orders = self.get('model').get('expansion_order')
3412 orders = self.get('orders')
3413 sq_orders = self.get('squared_orders')
3414
3415 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
3416 for (k,v) in tmp:
3417 if k in orders:
3418 if v < orders[k]:
3419 if k in sq_orders.keys() and \
3420 (sq_orders[k]>v or sq_orders[k]<0):
3421 logger.warning(
3422 '''The process with the squared coupling order (%s^2%s%s) specified can potentially
3423 recieve contributions with powers of the coupling %s larger than the maximal
3424 value allowed by the model builder (%s). Hence, MG5_aMC sets the amplitude order
3425 for that coupling to be this maximal one. '''%(k,self.get('sqorders_types')[k],
3426 self.get('squared_orders')[k],k,v))
3427 else:
3428 logger.warning(
3429 '''The coupling order (%s=%s) specified is larger than the one allowed
3430 by the model builder. The maximal value allowed is %s.
3431 We set the %s order to this value''' % (k,orders[k],v,k))
3432 orders[k] = v
3433 else:
3434 orders[k] = v
3435
3437 """Overloading the equality operator, so that only comparison
3438 of process id and legs is being done, using compare_for_sort."""
3439
3440 if not isinstance(other, Process):
3441 return False
3442
3443 return self.compare_for_sort(other) == 0
3444
3446 return not self.__eq__(other)
3447
3452 """List of Process objects
3453 """
3454
3456 """Test if object obj is a valid Process for the list."""
3457
3458 return isinstance(obj, Process)
3459
3461 """Returns a nicely formatted string of the matrix element processes."""
3462
3463 mystr = "\n".join([p.nice_string(indent) for p in self])
3464
3465 return mystr
3466
3471 """ProcessDefinition: list of multilegs (ordered)
3472 dictionary of orders
3473 model
3474 process id
3475 """
3476
3486
3487 - def filter(self, name, value):
3503
3505 """ Check that this process definition will yield a single process, as
3506 each multileg only has one leg"""
3507
3508 for process in self['decay_chains']:
3509 if process.has_multiparticle_label():
3510 return True
3511
3512 for mleg in self['legs']:
3513 if len(mleg['ids'])>1:
3514 return True
3515
3516 return False
3517
3525
3527 """Retrieve the minimum starting guess for WEIGHTED order, to
3528 use in find_optimal_process_orders in MultiProcess diagram
3529 generation (as well as particles and hierarchy). The algorithm:
3530
3531 1) Pick out the legs in the multiprocess according to the
3532 highest hierarchy represented (so don't mix particles from
3533 different hierarchy classes in the same multiparticles!)
3534
3535 2) Find the starting maximum WEIGHTED order as the sum of the
3536 highest n-2 weighted orders
3537
3538 3) Pick out required s-channel particle hierarchies, and use
3539 the highest of the maximum WEIGHTED order from the legs and
3540 the minimum WEIGHTED order extracted from 2*s-channel
3541 hierarchys plus the n-2-2*(number of s-channels) lowest
3542 leg weighted orders.
3543 """
3544
3545 model = self.get('model')
3546
3547
3548
3549 particles, hierarchy = model.get_particles_hierarchy()
3550
3551
3552
3553 max_order_now = []
3554 new_legs = copy.copy(self.get('legs'))
3555 for parts, value in zip(particles, hierarchy):
3556 ileg = 0
3557 while ileg < len(new_legs):
3558 if any([id in parts for id in new_legs[ileg].get('ids')]):
3559 max_order_now.append(value)
3560 new_legs.pop(ileg)
3561 else:
3562 ileg += 1
3563
3564
3565
3566 max_order_now = sorted(max_order_now)[2:]
3567
3568
3569 max_order_prop = []
3570 for idlist in self.get('required_s_channels'):
3571 max_order_prop.append([0,0])
3572 for id in idlist:
3573 for parts, value in zip(particles, hierarchy):
3574 if id in parts:
3575 max_order_prop[-1][0] += 2*value
3576 max_order_prop[-1][1] += 1
3577 break
3578
3579 if max_order_prop:
3580 if len(max_order_prop) >1:
3581 max_order_prop = min(*max_order_prop, key=lambda x:x[0])
3582 else:
3583 max_order_prop = max_order_prop[0]
3584
3585
3586
3587
3588 max_order_now = max(sum(max_order_now),
3589 max_order_prop[0] + \
3590 sum(max_order_now[:-2 * max_order_prop[1]]))
3591 else:
3592 max_order_now = sum(max_order_now)
3593
3594 return max_order_now, particles, hierarchy
3595
3596 - def nice_string(self, indent=0, print_weighted=False):
3597 """Returns a nicely formated string about current process
3598 content"""
3599
3600 mystr = " " * indent + "Process: "
3601 prevleg = None
3602 for leg in self['legs']:
3603 myparts = \
3604 "/".join([self['model'].get('particle_dict')[id].get_name() \
3605 for id in leg.get('ids')])
3606 if prevleg and prevleg['state'] == False \
3607 and leg['state'] == True:
3608
3609 mystr = mystr + '> '
3610
3611 if self['required_s_channels'] and \
3612 self['required_s_channels'][0]:
3613 mystr += "|".join([" ".join([self['model'].\
3614 get('particle_dict')[req_id].get_name() \
3615 for req_id in id_list]) \
3616 for id_list in self['required_s_channels']])
3617 mystr = mystr + '> '
3618
3619 mystr = mystr + myparts + ' '
3620
3621 prevleg = leg
3622
3623
3624 if self['forbidden_onsh_s_channels']:
3625 mystr = mystr + '$ '
3626 for forb_id in self['forbidden_onsh_s_channels']:
3627 forbpart = self['model'].get('particle_dict')[forb_id]
3628 mystr = mystr + forbpart.get_name() + ' '
3629
3630
3631 if self['forbidden_s_channels']:
3632 mystr = mystr + '$$ '
3633 for forb_id in self['forbidden_s_channels']:
3634 forbpart = self['model'].get('particle_dict')[forb_id]
3635 mystr = mystr + forbpart.get_name() + ' '
3636
3637
3638 if self['forbidden_particles']:
3639 mystr = mystr + '/ '
3640 for forb_id in self['forbidden_particles']:
3641 forbpart = self['model'].get('particle_dict')[forb_id]
3642 mystr = mystr + forbpart.get_name() + ' '
3643
3644 if self['orders']:
3645 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
3646 for key in sorted(self['orders'])]) + ' '
3647
3648
3649 if self['perturbation_couplings']:
3650 mystr = mystr + '[ '
3651 if self['NLO_mode']!='tree':
3652 if self['NLO_mode']=='virt' and not self['has_born']:
3653 mystr = mystr + 'sqrvirt = '
3654 else:
3655 mystr = mystr + self['NLO_mode'] + ' = '
3656 for order in self['perturbation_couplings']:
3657 mystr = mystr + order + ' '
3658 mystr = mystr + '] '
3659
3660 if self['squared_orders']:
3661 mystr = mystr + " ".join([key + '^2%s%d'%\
3662 (self.get_squared_order_type(key),self['squared_orders'][key]) \
3663 for key in self['squared_orders'].keys() \
3664 if print_weighted or key!='WEIGHTED']) + ' '
3665
3666
3667 mystr = mystr[:-1]
3668
3669 if self.get('id') or self.get('overall_orders'):
3670 mystr += " @%d" % self.get('id')
3671 if self.get('overall_orders'):
3672 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3673 for key in sorted(self['orders'])]) + ' '
3674
3675 if not self.get('decay_chains'):
3676 return mystr
3677
3678 for decay in self['decay_chains']:
3679 mystr = mystr + '\n' + \
3680 decay.nice_string(indent + 2).replace('Process', 'Decay')
3681
3682 return mystr
3683
3685 """ Return a Process object which has the same properties of this
3686 ProcessDefinition but with the specified LegList as legs attribute.
3687 """
3688
3689 return Process({\
3690 'legs': LegList,
3691 'model':self.get('model'),
3692 'id': self.get('id'),
3693 'orders': self.get('orders'),
3694 'sqorders_types': self.get('sqorders_types'),
3695 'squared_orders': self.get('squared_orders'),
3696 'has_born': self.get('has_born'),
3697 'required_s_channels': self.get('required_s_channels'),
3698 'forbidden_onsh_s_channels': self.get('forbidden_onsh_s_channels'),
3699 'forbidden_s_channels': self.get('forbidden_s_channels'),
3700 'forbidden_particles': self.get('forbidden_particles'),
3701 'perturbation_couplings': self.get('perturbation_couplings'),
3702 'is_decay_chain': self.get('is_decay_chain'),
3703 'overall_orders': self.get('overall_orders'),
3704 'split_orders': self.get('split_orders'),
3705 'NLO_mode': self.get('NLO_mode')
3706 })
3707
3708 - def get_process(self, initial_state_ids, final_state_ids):
3709 """ Return a Process object which has the same properties of this
3710 ProcessDefinition but with the specified given leg ids. """
3711
3712
3713
3714 my_isids = [leg.get('ids') for leg in self.get('legs') \
3715 if not leg.get('state')]
3716 my_fsids = [leg.get('ids') for leg in self.get('legs') \
3717 if leg.get('state')]
3718 for i, is_id in enumerate(initial_state_ids):
3719 assert is_id in my_isids[i]
3720 for i, fs_id in enumerate(final_state_ids):
3721 assert fs_id in my_fsids[i]
3722
3723 return self.get_process_with_legs(LegList(\
3724 [Leg({'id': id, 'state':False}) for id in initial_state_ids] + \
3725 [Leg({'id': id, 'state':True}) for id in final_state_ids]))
3726
3728 """Overloading the equality operator, so that only comparison
3729 of process id and legs is being done, using compare_for_sort."""
3730
3731 return super(Process, self).__eq__(other)
3732
3737 """List of ProcessDefinition objects
3738 """
3739
3741 """Test if object obj is a valid ProcessDefinition for the list."""
3742
3743 return isinstance(obj, ProcessDefinition)
3744
3750 """Make sure there are no doublets in the list doubletlist.
3751 Note that this is a slow implementation, so don't use if speed
3752 is needed"""
3753
3754 assert isinstance(doubletlist, list), \
3755 "Argument to make_unique must be list"
3756
3757
3758 uniquelist = []
3759 for elem in doubletlist:
3760 if elem not in uniquelist:
3761 uniquelist.append(elem)
3762
3763 doubletlist[:] = uniquelist[:]
3764