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