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 return True
1086
1087 - def get(self, name):
1088 """Get the value of the property name."""
1089
1090 if (name == 'ref_dict_to0' or name == 'ref_dict_to1') and \
1091 not self[name]:
1092 if self['interactions']:
1093 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1094 self['interactions'].generate_ref_dict()
1095 self['ref_dict_to0'].update(
1096 self['particles'].generate_ref_dict())
1097
1098 if (name == 'particle_dict') and not self[name]:
1099 if self['particles']:
1100 self['particle_dict'] = self['particles'].generate_dict()
1101 if self['interactions']:
1102 self['interactions'].synchronize_interactions_with_particles(\
1103 self['particle_dict'])
1104 if name == 'modelpath':
1105 modeldir = self.get('version_tag').rsplit('##',1)[0]
1106 if os.path.exists(modeldir):
1107 return modeldir
1108 else:
1109 raise Exception, "path %s not valid anymore." % modeldir
1110
1111
1112
1113
1114
1115 elif name == 'modelpath+restriction':
1116 modeldir = self.get('version_tag').rsplit('##',1)[0]
1117 modelname = self['name']
1118 if not os.path.exists(modeldir):
1119 raise Exception, "path %s not valid anymore" % modeldir
1120 modeldir = os.path.dirname(modeldir)
1121 modeldir = pjoin(modeldir, modelname)
1122 return modeldir
1123 elif name == 'restrict_name':
1124 modeldir = self.get('version_tag').rsplit('##',1)[0]
1125 modelname = self['name']
1126 basename = os.path.basename(modeldir)
1127 restriction = modelname[len(basename)+1:]
1128 return restriction
1129
1130 if (name == 'interaction_dict') and not self[name]:
1131 if self['interactions']:
1132 self['interaction_dict'] = self['interactions'].generate_dict()
1133
1134 if (name == 'got_majoranas') and self[name] == None:
1135 if self['particles']:
1136 self['got_majoranas'] = self.check_majoranas()
1137
1138 if (name == 'coupling_orders') and self[name] == None:
1139 if self['interactions']:
1140 self['coupling_orders'] = self.get_coupling_orders()
1141
1142 if (name == 'order_hierarchy') and not self[name]:
1143 if self['interactions']:
1144 self['order_hierarchy'] = self.get_order_hierarchy()
1145
1146 if (name == 'expansion_order') and self[name] == None:
1147 if self['interactions']:
1148 self['expansion_order'] = \
1149 dict([(order, -1) for order in self.get('coupling_orders')])
1150
1151 if (name == 'name2pdg') and 'name2pdg' not in self:
1152 self['name2pdg'] = {}
1153 for p in self.get('particles'):
1154 self['name2pdg'][p.get('antiname')] = -1*p.get('pdg_code')
1155 self['name2pdg'][p.get('name')] = p.get('pdg_code')
1156
1157 return Model.__bases__[0].get(self, name)
1158
1159 - def set(self, name, value, force = False):
1160 """Special set for particles and interactions - need to
1161 regenerate dictionaries."""
1162
1163 if name == 'particles':
1164
1165 make_unique(value)
1166
1167 self['particle_dict'] = {}
1168 self['ref_dict_to0'] = {}
1169 self['got_majoranas'] = None
1170
1171 if name == 'interactions':
1172
1173 make_unique(value)
1174
1175 self['interaction_dict'] = {}
1176 self['ref_dict_to1'] = {}
1177 self['ref_dict_to0'] = {}
1178 self['got_majoranas'] = None
1179 self['coupling_orders'] = None
1180 self['order_hierarchy'] = {}
1181 self['expansion_order'] = None
1182
1183 result = Model.__bases__[0].set(self, name, value, force)
1184
1185 if name == 'particles':
1186
1187 self.get('particle_dict')
1188
1189 return result
1190
1192 """This function actualizes the dictionaries"""
1193
1194 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1195 self['interactions'].generate_ref_dict()
1196 self['ref_dict_to0'].update(
1197 self['particles'].generate_ref_dict())
1198
1200 """Return process property names as a nicely sorted list."""
1201
1202 return ['name', 'particles', 'parameters', 'interactions',
1203 'couplings','lorentz', 'gauge']
1204
1205 - def get_particle(self, id):
1206 """Return the particle corresponding to the id / name"""
1207
1208 try:
1209 return self["particle_dict"][id]
1210 except Exception:
1211 if isinstance(id, int):
1212 try:
1213 return self.get("particle_dict")[id]
1214 except Exception:
1215 return None
1216 else:
1217 if not hasattr(self, 'name2part'):
1218 self.create_name2part()
1219 try:
1220 return self.name2part[id]
1221 except:
1222 return None
1223
1225 """create a dictionary name 2 part"""
1226
1227 self.name2part = {}
1228 for part in self.get("particle_dict").values():
1229 self.name2part[part.get('name')] = part
1230
1231
1232
1234 """return the lorentz object from the associate name"""
1235 if hasattr(self, 'lorentz_name2obj'):
1236 return self.lorentz_name2obj[name]
1237 else:
1238 self.create_lorentz_dict()
1239 return self.lorentz_name2obj[name]
1240
1242 """create the dictionary linked to the lorentz structure"""
1243 self.lorentz_name2obj = {}
1244 self.lorentz_expr2name = {}
1245 if not self.get('lorentz'):
1246 return
1247 for lor in self.get('lorentz'):
1248 self.lorentz_name2obj[lor.name] = lor
1249 self.lorentz_expr2name[lor.structure] = lor.name
1250
1252 """Return the interaction corresponding to the id"""
1253
1254 try:
1255 return self.get("interaction_dict")[id]
1256 except Exception:
1257 return None
1258
1260 """Return the parameter associated to the name NAME"""
1261
1262
1263 if hasattr(self, 'parameters_dict') and self.parameters_dict:
1264 try:
1265 return self.parameters_dict[name]
1266 except Exception:
1267
1268 pass
1269
1270
1271 self.parameters_dict = {}
1272 for data in self['parameters'].values():
1273 [self.parameters_dict.__setitem__(p.name,p) for p in data]
1274
1275 return self.parameters_dict[name]
1276
1278 """Determine the coupling orders of the model"""
1279 return set(sum([i.get('orders').keys() for i in \
1280 self.get('interactions')], []))
1281
1283 """Set a default order hierarchy for the model if not set by the UFO."""
1284
1285 hierarchy = dict([(order, 1) for order in self.get('coupling_orders')])
1286
1287 if self.get('coupling_orders') == set(['QCD', 'QED']):
1288 hierarchy['QED'] = 2
1289 return hierarchy
1290
1291
1293 """returns the number of light quark flavours in the model."""
1294 return len([p for p in self.get('particles') \
1295 if p['spin'] == 2 and p['is_part'] and \
1296 p ['color'] != 1 and p['mass'].lower() == 'zero'])
1297
1298
1300 """Returns the order hierarchies of the model and the
1301 particles which have interactions in at least this hierarchy
1302 (used in find_optimal_process_orders in MultiProcess diagram
1303 generation):
1304
1305 Check the coupling hierarchy of the model. Assign all
1306 particles to the different coupling hierarchies so that a
1307 particle is considered to be in the highest hierarchy (i.e.,
1308 with lowest value) where it has an interaction.
1309 """
1310
1311
1312 coupling_orders = self.get('coupling_orders')
1313
1314
1315 hierarchy = sorted(list(set([self.get('order_hierarchy')[k] for \
1316 k in coupling_orders])))
1317
1318
1319 orders = []
1320 for value in hierarchy:
1321 orders.append([ k for (k, v) in \
1322 self.get('order_hierarchy').items() if \
1323 v == value ])
1324
1325
1326
1327 interactions = []
1328 particles = []
1329 for iorder, order in enumerate(orders):
1330 sum_orders = sum(orders[:iorder+1], [])
1331 sum_interactions = sum(interactions[:iorder], [])
1332 sum_particles = sum([list(p) for p in particles[:iorder]], [])
1333
1334
1335 interactions.append([i for i in self.get('interactions') if \
1336 not i in sum_interactions and \
1337 not any([k not in sum_orders for k in \
1338 i.get('orders').keys()])])
1339
1340
1341 particles.append(set(sum([[p.get_pdg_code() for p in \
1342 inter.get('particles') if \
1343 p.get_pdg_code() not in sum_particles] \
1344 for inter in interactions[-1]], [])))
1345
1346 return particles, hierarchy
1347
1349 """Return the maximum WEIGHTED order for any interaction in the model,
1350 for equivalent 3-particle vertices. Note that it can be fractional."""
1351
1352 return max([inter.get_WEIGHTED_order(self) for inter in \
1353 self.get('interactions')])
1354
1355
1357 """Return True if there is fermion flow violation, False otherwise"""
1358
1359 if any([part.is_fermion() and part.get('self_antipart') \
1360 for part in self.get('particles')]):
1361 return True
1362
1363
1364
1365 for inter in self.get('interactions'):
1366
1367 if len(inter.get('particles'))==1:
1368 continue
1369 fermions = [p for p in inter.get('particles') if p.is_fermion()]
1370 for i in range(0, len(fermions), 2):
1371 if fermions[i].get('is_part') == \
1372 fermions[i+1].get('is_part'):
1373
1374 return True
1375
1376 return False
1377
1379 """Reset all dictionaries and got_majoranas. This is necessary
1380 whenever the particle or interaction content has changed. If
1381 particles or interactions are set using the set routine, this
1382 is done automatically."""
1383
1384 self['particle_dict'] = {}
1385 self['ref_dict_to0'] = {}
1386 self['got_majoranas'] = None
1387 self['interaction_dict'] = {}
1388 self['ref_dict_to1'] = {}
1389 self['ref_dict_to0'] = {}
1390
1392 """Change the name of the particles such that all SM and MSSM particles
1393 follows the MG convention"""
1394
1395
1396 def check_name_free(self, name):
1397 """ check if name is not use for a particle in the model if it is
1398 raise an MadGraph5error"""
1399 part = self['particles'].find_name(name)
1400 if part:
1401 error_text = \
1402 '%s particles with pdg code %s is in conflict with MG ' + \
1403 'convention name for particle %s.\n Use -modelname in order ' + \
1404 'to use the particles name defined in the model and not the ' + \
1405 'MadGraph5_aMC@NLO convention'
1406
1407 raise MadGraph5Error, error_text % \
1408 (part.get_name(), part.get_pdg_code(), pdg)
1409
1410 default = self.load_default_name()
1411
1412 for pdg in default.keys():
1413 part = self.get_particle(pdg)
1414 if not part:
1415 continue
1416 antipart = self.get_particle(-pdg)
1417 name = part.get_name()
1418 if name != default[pdg]:
1419 check_name_free(self, default[pdg])
1420 if part.get('is_part'):
1421 part.set('name', default[pdg])
1422 if antipart:
1423 antipart.set('name', default[pdg])
1424 else:
1425 part.set('antiname', default[pdg])
1426 else:
1427 part.set('antiname', default[pdg])
1428 if antipart:
1429 antipart.set('antiname', default[pdg])
1430
1431
1432 if self.get('name') == 'mssm' or self.get('name').startswith('mssm-'):
1433 part = self.get_particle(25)
1434 part.set('name', 'h1')
1435 part.set('antiname', 'h1')
1436
1437
1438
1440 """ Change all model parameter by a given prefix.
1441 Modify the parameter if some of them are identical up to the case"""
1442
1443 lower_dict={}
1444 duplicate = set()
1445 keys = self.get('parameters').keys()
1446 for key in keys:
1447 for param in self['parameters'][key]:
1448 lower_name = param.name.lower()
1449 if not lower_name:
1450 continue
1451 try:
1452 lower_dict[lower_name].append(param)
1453 except KeyError:
1454 lower_dict[lower_name] = [param]
1455 else:
1456 duplicate.add(lower_name)
1457 logger.debug('%s is define both as lower case and upper case.'
1458 % lower_name)
1459
1460 if prefix == '' and not duplicate:
1461 return
1462
1463 re_expr = r'''\b(%s)\b'''
1464 to_change = []
1465 change={}
1466
1467 for key in keys:
1468 for param in self['parameters'][key]:
1469 value = param.name.lower()
1470 if value in ['as','mu_r', 'zero','aewm1','g']:
1471 continue
1472 elif value.startswith(prefix):
1473 continue
1474 elif value in duplicate:
1475 continue
1476 elif value:
1477 change[param.name] = '%s%s' % (prefix,param.name)
1478 to_change.append(param.name)
1479 param.name = change[param.name]
1480
1481 for value in duplicate:
1482 for i, var in enumerate(lower_dict[value][1:]):
1483 to_change.append(var.name)
1484 change[var.name] = '%s%s__%s' % (prefix, var.name.lower(), i+2)
1485 var.name = '%s%s__%s' %(prefix, var.name.lower(), i+2)
1486 to_change.append(var.name)
1487 assert 'zero' not in to_change
1488 replace = lambda match_pattern: change[match_pattern.groups()[0]]
1489
1490 if not to_change:
1491 return
1492
1493 if 'parameter_dict' in self:
1494 new_dict = dict( (change[name] if (name in change) else name, value) for
1495 name, value in self['parameter_dict'].items())
1496 self['parameter_dict'] = new_dict
1497
1498 i=0
1499 while i*1000 <= len(to_change):
1500 one_change = to_change[i*1000: min((i+1)*1000,len(to_change))]
1501 i+=1
1502 rep_pattern = re.compile('\\b%s\\b'% (re_expr % ('\\b|\\b'.join(one_change))))
1503
1504
1505 for key in keys:
1506 if key == ('external',):
1507 continue
1508 for param in self['parameters'][key]:
1509 param.expr = rep_pattern.sub(replace, param.expr)
1510
1511 for key in self['couplings'].keys():
1512 for coup in self['couplings'][key]:
1513 coup.expr = rep_pattern.sub(replace, coup.expr)
1514
1515
1516 for part in self['particles']:
1517 if str(part.get('mass')) in one_change:
1518 part.set('mass', rep_pattern.sub(replace, str(part.get('mass'))))
1519 if str(part.get('width')) in one_change:
1520 part.set('width', rep_pattern.sub(replace, str(part.get('width'))))
1521 if hasattr(part, 'partial_widths'):
1522 for key, value in part.partial_widths.items():
1523 part.partial_widths[key] = rep_pattern.sub(replace, value)
1524
1525
1526 self['particle_dict'] =''
1527 self.get('particle_dict')
1528
1529
1530
1532 """Return the first positive number that is not a valid PDG code"""
1533 return [c for c in range(1, len(self.get('particles')) + 1) if \
1534 c not in self.get('particle_dict').keys()][0]
1535
1545
1546 @ staticmethod
1548 """ load the default for name convention """
1549
1550 logger.info('Change particles name to pass to MG5 convention')
1551 default = {}
1552 for line in open(os.path.join(MG5DIR, 'input', \
1553 'particles_name_default.txt')):
1554 line = line.lstrip()
1555 if line.startswith('#'):
1556 continue
1557
1558 args = line.split()
1559 if len(args) != 2:
1560 logger.warning('Invalid syntax in interface/default_name:\n %s' % line)
1561 continue
1562 default[int(args[0])] = args[1].lower()
1563
1564 return default
1565
1567 """Change the electroweak mode. The only valid mode now is external.
1568 Where in top of the default MW and sw2 are external parameters."""
1569
1570 assert mode == "external"
1571
1572 try:
1573 W = self.get('particle_dict')[24]
1574 except KeyError:
1575 raise InvalidCmd('No W particle in the model impossible to change the EW scheme!')
1576
1577 MW = self.get_parameter(W.get('mass'))
1578 if not isinstance(MW, ParamCardVariable):
1579 newMW = ParamCardVariable(MW.name, MW.value, 'MASS', [24])
1580 if not newMW.value:
1581 newMW.value = 80.385
1582
1583 self.get('parameters')[MW.depend].remove(MW)
1584
1585 self.add_param(newMW, ['external'])
1586
1587
1588 try:
1589 sw2 = self.get_parameter('sw2')
1590 except KeyError:
1591 try:
1592 sw2 = self.get_parameter('mdl_sw2')
1593 except KeyError:
1594 sw2=None
1595
1596 if sw2:
1597 newsw2 = ParamCardVariable(sw2.name,sw2.value, 'SMINPUTS', [4])
1598 if not newsw2.value:
1599 newsw2.value = 0.222246485786
1600
1601 self.get('parameters')[sw2.depend].remove(sw2)
1602
1603 self.add_param(newsw2, ['external'])
1604
1606 """modify the expression changing the mass to complex mass scheme"""
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618 to_change = {}
1619 mass_widths = []
1620 for particle in self.get('particles'):
1621 m = particle.get('width')
1622 if m in mass_widths:
1623 continue
1624 mass_widths.append(particle.get('width'))
1625 mass_widths.append(particle.get('mass'))
1626 if particle.get('width') == 'ZERO':
1627
1628 continue
1629 width = self.get_parameter(particle.get('width'))
1630 if not isinstance(width, ParamCardVariable):
1631 width.expr = 're(%s)' % width.expr
1632 if particle.get('mass') != 'ZERO':
1633 mass = self.get_parameter(particle.get('mass'))
1634
1635 if particle.get('pdg_code') == 24:
1636 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)))':
1637
1638 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1639 if not MW.value:
1640 MW.value = 80.385
1641 self.get('parameters')[('external',)].append(MW)
1642 self.get('parameters')[mass.depend].remove(mass)
1643
1644 new_param = ModelVariable('Gf',
1645 '-aEW*MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - MZ**2))' %\
1646 {'MW': mass.name}, 'complex', mass.depend)
1647 Gf = self.get_parameter('Gf')
1648 self.get('parameters')[('external',)].remove(Gf)
1649 self.add_param(new_param, ['aEW'])
1650
1651 mass = MW
1652
1653 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)))':
1654
1655 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1656 if not MW.value:
1657 MW.value = 80.385
1658 self.get('parameters')[('external',)].append(MW)
1659 self.get('parameters')[mass.depend].remove(mass)
1660
1661 new_param = ModelVariable('mdl_Gf',
1662 '-mdl_aEW*mdl_MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - mdl_MZ**2))' %\
1663 {'MW': mass.name}, 'complex', mass.depend)
1664 Gf = self.get_parameter('mdl_Gf')
1665 self.get('parameters')[('external',)].remove(Gf)
1666 self.add_param(new_param, ['mdl_aEW'])
1667
1668 mass = MW
1669 elif isinstance(mass, ModelVariable):
1670 logger.warning('W mass is not an external parameter. This is not adviced for the complex mass scheme.')
1671
1672
1673
1674 depend = list(set(mass.depend + width.depend))
1675 if len(depend)>1 and 'external' in depend:
1676 depend.remove('external')
1677 depend = tuple(depend)
1678 if depend == ('external',):
1679 depend = ()
1680
1681
1682 if isinstance(mass, ParamCardVariable):
1683 New_param = ModelVariable('CMASS_'+mass.name,
1684 'cmath.sqrt(%(mass)s**2 - complex(0,1) * %(mass)s * %(width)s)' \
1685 % {'mass': mass.name, 'width': width.name},
1686 'complex', depend)
1687 else:
1688 New_param = ModelVariable('CMASS_'+mass.name,
1689 mass.expr, 'complex', depend)
1690
1691 if not isinstance(width, ParamCardVariable):
1692 width.expr = '- im(%s**2) / cmath.sqrt(re(%s**2))' % (mass.expr, mass.expr)
1693 else:
1694
1695 New_width = ModelVariable(width.name,
1696 '-1 * im(CMASS_%s**2) / %s' % (mass.name, mass.name), 'real', mass.depend)
1697 self.get('parameters')[('external',)].remove(width)
1698 self.add_param(New_param, (mass,))
1699 self.add_param(New_width, (New_param,))
1700 mass.expr = 'cmath.sqrt(re(%s**2))' % mass.expr
1701 to_change[mass.name] = New_param.name
1702 continue
1703
1704 mass.expr = 're(%s)' % mass.expr
1705 self.add_param(New_param, (mass, width))
1706 to_change[mass.name] = New_param.name
1707
1708
1709 yukawas = [p for p in self.get('parameters')[('external',)]
1710 if p.lhablock.lower() == 'yukawa']
1711 for yukawa in yukawas:
1712
1713 self.get('parameters')[('external',)].remove(yukawa)
1714
1715 particle = self.get_particle(yukawa.lhacode[0])
1716 mass = self.get_parameter(particle.get('mass'))
1717
1718
1719 if mass.depend == ('external',):
1720 depend = ()
1721 else:
1722 depend = mass.depend
1723
1724 New_param = ModelVariable(yukawa.name, mass.name, 'real', depend)
1725
1726
1727 if mass.name in to_change:
1728 expr = 'CMASS_%s' % mass.name
1729 else:
1730 expr = mass.name
1731 param_depend = self.get_parameter(expr)
1732 self.add_param(New_param, [param_depend])
1733
1734 if not to_change:
1735 return
1736
1737
1738
1739
1740
1741 pat = '|'.join(to_change.keys())
1742 pat = r'(%s)\b' % pat
1743 pat = re.compile(pat)
1744 def replace(match):
1745 return to_change[match.group()]
1746
1747
1748 for dep, list_param in self['parameters'].items():
1749 for param in list_param:
1750 if param.name.startswith('CMASS_') or param.name in mass_widths or\
1751 isinstance(param, ParamCardVariable):
1752 continue
1753 param.type = 'complex'
1754
1755
1756 param.expr = pat.sub(replace, param.expr)
1757
1758
1759 for dep, list_coup in self['couplings'].items():
1760 for coup in list_coup:
1761 coup.expr = pat.sub(replace, coup.expr)
1762
1763 - def add_param(self, new_param, depend_param):
1764 """add the parameter in the list of parameter in a correct position"""
1765
1766 pos = 0
1767 for i,param in enumerate(self.get('parameters')[new_param.depend]):
1768 if param.name in depend_param:
1769 pos = i + 1
1770 self.get('parameters')[new_param.depend].insert(pos, new_param)
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781 -class ModelVariable(object):
1782 """A Class for storing the information about coupling/ parameter"""
1783
1784 - def __init__(self, name, expression, type, depend=()):
1785 """Initialize a new parameter/coupling"""
1786
1787 self.name = name
1788 self.expr = expression
1789 self.type = type
1790 self.depend = depend
1791 self.value = None
1792
1794 """Object with same name are identical, If the object is a string we check
1795 if the attribute name is equal to this string"""
1796
1797 try:
1798 return other.name == self.name
1799 except Exception:
1800 return other == self.name
1801
1803 """ A class for storing the information linked to all the parameter
1804 which should be define in the param_card.dat"""
1805
1806 depend = ('external',)
1807 type = 'real'
1808
1809 - def __init__(self, name, value, lhablock, lhacode):
1810 """Initialize a new ParamCardVariable
1811 name: name of the variable
1812 value: default numerical value
1813 lhablock: name of the block in the param_card.dat
1814 lhacode: code associate to the variable
1815 """
1816 self.name = name
1817 self.value = value
1818 self.lhablock = lhablock
1819 self.lhacode = lhacode
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830 -class Leg(PhysicsObject):
1831 """Leg object: id (Particle), number, I/F state, flag from_group
1832 """
1833
1835 """Default values for all properties"""
1836
1837 self['id'] = 0
1838 self['number'] = 0
1839
1840 self['state'] = True
1841
1842 self['loop_line'] = False
1843
1844 self['from_group'] = True
1845
1846 self['onshell'] = None
1847
1848 - def filter(self, name, value):
1849 """Filter for valid leg property values."""
1850
1851 if name in ['id', 'number']:
1852 if not isinstance(value, int):
1853 raise self.PhysicsObjectError, \
1854 "%s is not a valid integer for leg id" % str(value)
1855
1856 if name == 'state':
1857 if not isinstance(value, bool):
1858 raise self.PhysicsObjectError, \
1859 "%s is not a valid leg state (True|False)" % \
1860 str(value)
1861
1862 if name == 'from_group':
1863 if not isinstance(value, bool) and value != None:
1864 raise self.PhysicsObjectError, \
1865 "%s is not a valid boolean for leg flag from_group" % \
1866 str(value)
1867
1868 if name == 'loop_line':
1869 if not isinstance(value, bool) and value != None:
1870 raise self.PhysicsObjectError, \
1871 "%s is not a valid boolean for leg flag loop_line" % \
1872 str(value)
1873
1874 if name == 'onshell':
1875 if not isinstance(value, bool) and value != None:
1876 raise self.PhysicsObjectError, \
1877 "%s is not a valid boolean for leg flag onshell" % \
1878 str(value)
1879 return True
1880
1882 """Return particle property names as a nicely sorted list."""
1883
1884 return ['id', 'number', 'state', 'from_group', 'loop_line', 'onshell']
1885
1887 """Returns True if the particle corresponding to the leg is a
1888 fermion"""
1889
1890 assert isinstance(model, Model), "%s is not a model" % str(model)
1891
1892 return model.get('particle_dict')[self['id']].is_fermion()
1893
1895 """Returns True if leg is an incoming fermion, i.e., initial
1896 particle or final antiparticle"""
1897
1898 assert isinstance(model, Model), "%s is not a model" % str(model)
1899
1900 part = model.get('particle_dict')[self['id']]
1901 return part.is_fermion() and \
1902 (self.get('state') == False and part.get('is_part') or \
1903 self.get('state') == True and not part.get('is_part'))
1904
1906 """Returns True if leg is an outgoing fermion, i.e., initial
1907 antiparticle or final particle"""
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') == True and part.get('is_part') or \
1914 self.get('state') == False and not part.get('is_part'))
1915
1916
1917
1918
1919 - def same(self, leg):
1920 """ Returns true if the leg in argument has the same ID and the same numer """
1921
1922
1923
1924 if isinstance(leg,int):
1925 if self['number']==leg:
1926 return True
1927 else:
1928 return False
1929
1930
1931
1932 elif isinstance(leg, Leg):
1933 if self['id']==leg.get('id') and \
1934 self['number']==leg.get('number') and \
1935 self['loop_line']==leg.get('loop_line') :
1936 return True
1937 else:
1938 return False
1939
1940 else :
1941 return False
1942
1943
1945 return self['number'] < other['number']
1946
1947
1948
1949
1950 -class LegList(PhysicsObjectList):
1951 """List of Leg objects
1952 """
1953
1955 """Test if object obj is a valid Leg for the list."""
1956
1957 return isinstance(obj, Leg)
1958
1959
1960
1962 """Return all elements which have 'from_group' True"""
1963
1964 return filter(lambda leg: leg.get('from_group'), self)
1965
1967 """Return True if at least one element has 'from_group' True"""
1968
1969 return len(self.from_group_elements()) > 0
1970
1972 """Return True if at least two elements have 'from_group' True"""
1973
1974 return len(self.from_group_elements()) > 1
1975
1977 """If has at least one 'from_group' True and in ref_dict_to1,
1978 return the return list from ref_dict_to1, otherwise return False"""
1979 if self.minimum_one_from_group():
1980 return ref_dict_to1.has_key(tuple(sorted([leg.get('id') for leg in self])))
1981 else:
1982 return False
1983
1985 """If has at least two 'from_group' True and in ref_dict_to0,
1986
1987 return the vertex (with id from ref_dict_to0), otherwise return None
1988
1989 If is_decay_chain = True, we only allow clustering of the
1990 initial leg, since we want this to be the last wavefunction to
1991 be evaluated.
1992 """
1993 if is_decay_chain:
1994
1995
1996
1997
1998 return any(leg.get('from_group') == None for leg in self) and \
1999 ref_dict_to0.has_key(tuple(sorted([leg.get('id') \
2000 for leg in self])))
2001
2002 if self.minimum_two_from_group():
2003 return ref_dict_to0.has_key(tuple(sorted([leg.get('id') for leg in self])))
2004 else:
2005 return False
2006
2008 """Returns the list of ids corresponding to the leglist with
2009 all particles outgoing"""
2010
2011 res = []
2012
2013 assert isinstance(model, Model), "Error! model not model"
2014
2015
2016 for leg in self:
2017 if leg.get('state') == False:
2018 res.append(model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
2019 else:
2020 res.append(leg.get('id'))
2021
2022 return res
2023
2024 - def sort(self,*args, **opts):
2025 """Match with FKSLegList"""
2026 Opts=copy.copy(opts)
2027 if 'pert' in Opts.keys():
2028 del Opts['pert']
2029 return super(LegList,self).sort(*args, **Opts)
2030
2031
2032
2033
2034
2035 -class MultiLeg(PhysicsObject):
2036 """MultiLeg object: ids (Particle or particles), I/F state
2037 """
2038
2040 """Default values for all properties"""
2041
2042 self['ids'] = []
2043 self['state'] = True
2044
2045 - def filter(self, name, value):
2046 """Filter for valid multileg property values."""
2047
2048 if name == 'ids':
2049 if not isinstance(value, list):
2050 raise self.PhysicsObjectError, \
2051 "%s is not a valid list" % str(value)
2052 for i in value:
2053 if not isinstance(i, int):
2054 raise self.PhysicsObjectError, \
2055 "%s is not a valid list of integers" % str(value)
2056
2057 if name == 'state':
2058 if not isinstance(value, bool):
2059 raise self.PhysicsObjectError, \
2060 "%s is not a valid leg state (initial|final)" % \
2061 str(value)
2062
2063 return True
2064
2066 """Return particle property names as a nicely sorted list."""
2067
2068 return ['ids', 'state']
2069
2074 """List of MultiLeg objects
2075 """
2076
2078 """Test if object obj is a valid MultiLeg for the list."""
2079
2080 return isinstance(obj, MultiLeg)
2081
2082
2083
2084
2085 -class Vertex(PhysicsObject):
2086 """Vertex: list of legs (ordered), id (Interaction)
2087 """
2088
2089 sorted_keys = ['id', 'legs']
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099 ID_to_veto_for_multichanneling = [0,-1,-2]
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109 max_n_loop_for_multichanneling = 4
2110
2112 """Default values for all properties"""
2113
2114
2115
2116
2117
2118
2119
2120
2121 self['id'] = 0
2122 self['legs'] = LegList()
2123
2124 - def filter(self, name, value):
2125 """Filter for valid vertex property values."""
2126
2127 if name == 'id':
2128 if not isinstance(value, int):
2129 raise self.PhysicsObjectError, \
2130 "%s is not a valid integer for vertex id" % str(value)
2131
2132 if name == 'legs':
2133 if not isinstance(value, LegList):
2134 raise self.PhysicsObjectError, \
2135 "%s is not a valid LegList object" % str(value)
2136
2137 return True
2138
2140 """Return particle property names as a nicely sorted list."""
2141
2142 return self.sorted_keys
2143
2145 """return a nice string"""
2146
2147 mystr = []
2148 for leg in self['legs']:
2149 mystr.append( str(leg['number']) + '(%s)' % str(leg['id']))
2150 mystr = '(%s,id=%s ,obj_id:%s)' % (', '.join(mystr), self['id'], id(self))
2151
2152 return(mystr)
2153
2154
2156 """Returns the id for the last leg as an outgoing
2157 s-channel. Returns 0 if leg is t-channel, or if identity
2158 vertex. Used to check for required and forbidden s-channel
2159 particles."""
2160
2161 leg = self.get('legs')[-1]
2162
2163 if ninitial == 1:
2164
2165
2166 if leg.get('state') == True:
2167 return leg.get('id')
2168 else:
2169 return model.get('particle_dict')[leg.get('id')].\
2170 get_anti_pdg_code()
2171
2172
2173 if self.get('id') == 0 or \
2174 leg.get('state') == False:
2175
2176 return 0
2177
2178 if leg.get('loop_line'):
2179
2180 return 0
2181
2182
2183
2184 if leg.get('number') > ninitial:
2185 return leg.get('id')
2186 else:
2187 return model.get('particle_dict')[leg.get('id')].\
2188 get_anti_pdg_code()
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201 -class VertexList(PhysicsObjectList):
2202 """List of Vertex objects
2203 """
2204
2205 orders = {}
2206
2208 """Test if object obj is a valid Vertex for the list."""
2209
2210 return isinstance(obj, Vertex)
2211
2212 - def __init__(self, init_list=None, orders=None):
2213 """Creates a new list object, with an optional dictionary of
2214 coupling orders."""
2215
2216 list.__init__(self)
2217
2218 if init_list is not None:
2219 for object in init_list:
2220 self.append(object)
2221
2222 if isinstance(orders, dict):
2223 self.orders = orders
2224
2229 """ContractedVertex: When contracting a loop to a given vertex, the created
2230 vertex object is then a ContractedVertex object which has additional
2231 information with respect to a regular vertex object. For example, it contains
2232 the PDG of the particles attached to it. (necessary because the contracted
2233 vertex doesn't have an interaction ID which would allow to retrieve such
2234 information).
2235 """
2236
2238 """Default values for all properties"""
2239
2240 self['PDGs'] = []
2241 self['loop_tag'] = tuple()
2242 self['loop_orders'] = {}
2243 super(ContractedVertex, self).default_setup()
2244
2245 - def filter(self, name, value):
2246 """Filter for valid vertex property values."""
2247
2248 if name == 'PDGs':
2249 if isinstance(value, list):
2250 for elem in value:
2251 if not isinstance(elem,int):
2252 raise self.PhysicsObjectError, \
2253 "%s is not a valid integer for leg PDG" % str(elem)
2254 else:
2255 raise self.PhysicsObjectError, \
2256 "%s is not a valid list for contracted vertex PDGs"%str(value)
2257 if name == 'loop_tag':
2258 if isinstance(value, tuple):
2259 for elem in value:
2260 if not (isinstance(elem,int) or isinstance(elem,tuple)):
2261 raise self.PhysicsObjectError, \
2262 "%s is not a valid int or tuple for loop tag element"%str(elem)
2263 else:
2264 raise self.PhysicsObjectError, \
2265 "%s is not a valid tuple for a contracted vertex loop_tag."%str(value)
2266 if name == 'loop_orders':
2267 Interaction.filter(Interaction(), 'orders', value)
2268 else:
2269 return super(ContractedVertex, self).filter(name, value)
2270
2271 return True
2272
2277
2278
2279
2280
2281 -class Diagram(PhysicsObject):
2282 """Diagram: list of vertices (ordered)
2283 """
2284
2286 """Default values for all properties"""
2287
2288 self['vertices'] = VertexList()
2289 self['orders'] = {}
2290
2291 - def filter(self, name, value):
2303
2305 """Return particle property names as a nicely sorted list."""
2306
2307 return ['vertices', 'orders']
2308
2310 """Returns a nicely formatted string of the diagram content."""
2311
2312 pass_sanity = True
2313 if self['vertices']:
2314 mystr = '('
2315 for vert in self['vertices']:
2316 used_leg = []
2317 mystr = mystr + '('
2318 for leg in vert['legs'][:-1]:
2319 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
2320 used_leg.append(leg['number'])
2321 if __debug__ and len(used_leg) != len(set(used_leg)):
2322 pass_sanity = False
2323 responsible = id(vert)
2324
2325 if self['vertices'].index(vert) < len(self['vertices']) - 1:
2326
2327 mystr = mystr[:-1] + '>'
2328 mystr = mystr + str(vert['legs'][-1]['number']) + '(%s)' % str(vert['legs'][-1]['id']) + ','
2329 mystr = mystr + 'id:' + str(vert['id']) + '),'
2330
2331 mystr = mystr[:-1] + ')'
2332 mystr += " (%s)" % (",".join(["%s=%d" % (key, self['orders'][key]) \
2333 for key in sorted(self['orders'].keys())]))
2334
2335 if not pass_sanity:
2336 raise Exception, "invalid diagram: %s. vert_id: %s" % (mystr, responsible)
2337
2338 return mystr
2339 else:
2340 return '()'
2341
2343 """Calculate the actual coupling orders of this diagram. Note
2344 that the special order WEIGTHED corresponds to the sum of
2345 hierarchys for the couplings."""
2346
2347 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
2348 weight = 0
2349 for vertex in self['vertices']:
2350 if vertex.get('id') in [0,-1]: continue
2351 if vertex.get('id') == -2:
2352 couplings = vertex.get('loop_orders')
2353 else:
2354 couplings = model.get('interaction_dict')[vertex.get('id')].\
2355 get('orders')
2356 for coupling in couplings:
2357 coupling_orders[coupling] += couplings[coupling]
2358 weight += sum([model.get('order_hierarchy')[c]*n for \
2359 (c,n) in couplings.items()])
2360 coupling_orders['WEIGHTED'] = weight
2361 self.set('orders', coupling_orders)
2362
2365 """ Returns wether the contributiong consisting in the current diagram
2366 multiplied by diag_multiplier passes the *positive* squared_orders
2367 specified ( a dictionary ) of types sq_order_types (a dictionary whose
2368 values are the relational operator used to define the constraint of the
2369 order in key)."""
2370
2371 for order, value in squared_orders.items():
2372 if value<0:
2373 continue
2374 combined_order = self.get_order(order) + \
2375 diag_multiplier.get_order(order)
2376 if ( sq_orders_types[order]=='==' and combined_order != value ) or \
2377 ( sq_orders_types[order] in ['=', '<='] and combined_order > value) or \
2378 ( sq_orders_types[order]=='>' and combined_order <= value) :
2379 return False
2380 return True
2381
2383 """Return the order of this diagram. It returns 0 if it is not present."""
2384
2385 try:
2386 return self['orders'][order]
2387 except Exception:
2388 return 0
2389
2391 """ Returns a Diagram which correspond to the loop diagram with the
2392 loop shrunk to a point. Of course for a instance of base_objects.Diagram
2393 one must simply return self."""
2394
2395 return self
2396
2398 """ Return the list of external legs of this diagram """
2399
2400 external_legs = LegList([])
2401 for leg in sum([vert.get('legs') for vert in self.get('vertices')],[]):
2402 if not leg.get('number') in [l.get('number') for l in external_legs]:
2403 external_legs.append(leg)
2404
2405 return external_legs
2406
2408 """Renumber legs in all vertices according to perm_map"""
2409
2410 vertices = VertexList()
2411 min_dict = copy.copy(perm_map)
2412
2413 state_dict = dict([(l.get('number'), l.get('state')) for l in leg_list])
2414
2415 for vertex in self.get('vertices')[:-1]:
2416 vertex = copy.copy(vertex)
2417 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2418 for leg in leg_list[:-1]:
2419 leg.set('number', min_dict[leg.get('number')])
2420 leg.set('state', state_dict[leg.get('number')])
2421 min_number = min([leg.get('number') for leg in leg_list[:-1]])
2422 leg = leg_list[-1]
2423 min_dict[leg.get('number')] = min_number
2424
2425
2426 state_dict[min_number] = len([l for l in leg_list[:-1] if \
2427 not l.get('state')]) != 1
2428 leg.set('number', min_number)
2429 leg.set('state', state_dict[min_number])
2430 vertex.set('legs', leg_list)
2431 vertices.append(vertex)
2432
2433 vertex = copy.copy(self.get('vertices')[-1])
2434 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2435 for leg in leg_list:
2436 leg.set('number', min_dict[leg.get('number')])
2437 leg.set('state', state_dict[leg.get('number')])
2438 vertex.set('legs', leg_list)
2439 vertices.append(vertex)
2440
2441 new_diag = copy.copy(self)
2442 new_diag.set('vertices', vertices)
2443 state_dict = {True:'T',False:'F'}
2444 return new_diag
2445
2449 """Return a list of the number of legs in the vertices for
2450 this diagram.
2451 This function is only used for establishing the multi-channeling, so that
2452 we exclude from it all the fake vertices and the vertices resulting from
2453 shrunk loops (id=-2)"""
2454
2455
2456 if max_n_loop == 0:
2457 max_n_loop = Vertex.max_n_loop_for_multichanneling
2458
2459 res = [len(v.get('legs')) for v in self.get('vertices') if (v.get('id') \
2460 not in veto_inter_id) or (v.get('id')==-2 and
2461 len(v.get('legs'))>max_n_loop)]
2462
2463 return res
2464
2466 """Return the maximum number of configs from this diagram,
2467 given by 2^(number of non-zero width s-channel propagators)"""
2468
2469 s_channels = [v.get_s_channel_id(model,ninitial) for v in \
2470 self.get('vertices')[:-1]]
2471 num_props = len([i for i in s_channels if i != 0 and \
2472 model.get_particle(i).get('width').lower() != 'zero'])
2473
2474 if num_props < 1:
2475 return 1
2476 else:
2477 return 2**num_props
2478
2480 """return the difference of total diff of charge occuring on the
2481 lofw of the initial parton. return [None,None] if the two initial parton
2482 are connected and the (partial) value if None if the initial parton is
2483 not a fermiom"""
2484
2485 import madgraph.core.drawing as drawing
2486 drawdiag = drawing.FeynmanDiagram(self, model)
2487 drawdiag.load_diagram()
2488 out = []
2489
2490 for v in drawdiag.initial_vertex:
2491 init_part = v.lines[0]
2492 if not init_part.is_fermion():
2493 out.append(None)
2494 continue
2495
2496 init_charge = model.get_particle(init_part.id).get('charge')
2497
2498 l_last = init_part
2499 v_last = v
2500 vcurrent = l_last.end
2501 if vcurrent == v:
2502 vcurrent = l_last.begin
2503 security =0
2504 while not vcurrent.is_external():
2505 if security > 1000:
2506 raise Exception, 'wrong diagram'
2507 next_l = [l for l in vcurrent.lines if l is not l_last and l.is_fermion()][0]
2508 next_v = next_l.end
2509 if next_v == vcurrent:
2510 next_v = next_l.begin
2511 l_last, vcurrent = next_l, next_v
2512 if vcurrent in drawdiag.initial_vertex:
2513 return [None, None]
2514
2515 out.append(model.get_particle(l_last.id).get('charge') - init_charge)
2516 return out
2517
2518
2519
2520
2521
2522 -class DiagramList(PhysicsObjectList):
2523 """List of Diagram objects
2524 """
2525
2527 """Test if object obj is a valid Diagram for the list."""
2528
2529 return isinstance(obj, Diagram)
2530
2532 """Returns a nicely formatted string"""
2533 mystr = " " * indent + str(len(self)) + ' diagrams:\n'
2534 for i, diag in enumerate(self):
2535 mystr = mystr + " " * indent + str(i+1) + " " + \
2536 diag.nice_string() + '\n'
2537 return mystr[:-1]
2538
2539
2540
2542 """ Return the order of the diagram in the list with the maximum coupling
2543 order for the coupling specified """
2544 max_order=-1
2545
2546 for diag in self:
2547 if order in diag['orders'].keys():
2548 if max_order==-1 or diag['orders'][order] > max_order:
2549 max_order = diag['orders'][order]
2550
2551 return max_order
2552
2554 """ This function returns a fitlered version of the diagram list self
2555 which satisfy the negative squared_order constraint 'order' with negative
2556 value 'value' and of type 'order_type', assuming that the diagram_list
2557 it must be squared against is 'reg_diag_list'. It also returns the
2558 new postive target squared order which correspond to this negative order
2559 constraint. Example: u u~ > d d~ QED^2<=-2 means that one wants to
2560 pick terms only up to the the next-to-leading order contributiong in QED,
2561 which is QED=2 in this case, so that target_order=4 is returned."""
2562
2563
2564 target_order = min(ref_diag_list.get_order_values(order))+\
2565 min(self.get_order_values(order))+2*(-value-1)
2566
2567 new_list = self.apply_positive_sq_orders(ref_diag_list,
2568 {order:target_order}, {order:order_type})
2569
2570 return new_list, target_order
2571
2573 """ This function returns a filtered version of self which contain
2574 only the diagram which satisfy the positive squared order constraints
2575 sq_orders of type sq_order_types and assuming that the diagrams are
2576 multiplied with those of the reference diagram list ref_diag_list."""
2577
2578 new_diag_list = DiagramList()
2579 for tested_diag in self:
2580 for ref_diag in ref_diag_list:
2581 if tested_diag.pass_squared_order_constraints(ref_diag,
2582 sq_orders,sq_order_types):
2583 new_diag_list.append(tested_diag)
2584 break
2585 return new_diag_list
2586
2588 """ Return the order of the diagram in the list with the mimimum coupling
2589 order for the coupling specified """
2590 min_order=-1
2591 for diag in self:
2592 if order in diag['orders'].keys():
2593 if min_order==-1 or diag['orders'][order] < min_order:
2594 min_order = diag['orders'][order]
2595 else:
2596 return 0
2597
2598 return min_order
2599
2601 """ Return the list of possible values appearing in the diagrams of this
2602 list for the order given in argument """
2603
2604 values=set([])
2605 for diag in self:
2606 if order in diag['orders'].keys():
2607 values.add(diag['orders'][order])
2608 else:
2609 values.add(0)
2610
2611 return list(values)
2612
2613
2614
2615
2616 -class Process(PhysicsObject):
2617 """Process: list of legs (ordered)
2618 dictionary of orders
2619 model
2620 process id
2621 """
2622
2624 """Default values for all properties"""
2625
2626 self['legs'] = LegList()
2627
2628 self['orders'] = {}
2629 self['model'] = Model()
2630
2631 self['id'] = 0
2632 self['uid'] = 0
2633
2634
2635
2636
2637 self['required_s_channels'] = []
2638 self['forbidden_onsh_s_channels'] = []
2639 self['forbidden_s_channels'] = []
2640 self['forbidden_particles'] = []
2641 self['is_decay_chain'] = False
2642 self['overall_orders'] = {}
2643
2644 self['decay_chains'] = ProcessList()
2645
2646 self['legs_with_decays'] = LegList()
2647
2648 self['perturbation_couplings']=[]
2649
2650
2651
2652
2653 self['squared_orders'] = {}
2654
2655
2656
2657
2658 self['sqorders_types'] = {}
2659 self['has_born'] = True
2660
2661
2662 self['NLO_mode'] = 'tree'
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672 self['split_orders'] = []
2673
2674 - def filter(self, name, value):
2675 """Filter for valid process property values."""
2676
2677 if name in ['legs', 'legs_with_decays'] :
2678 if not isinstance(value, LegList):
2679 raise self.PhysicsObjectError, \
2680 "%s is not a valid LegList object" % str(value)
2681
2682 if name in ['orders', 'overall_orders','squared_orders']:
2683 Interaction.filter(Interaction(), 'orders', value)
2684
2685 if name == 'sqorders_types':
2686 if not isinstance(value, dict):
2687 raise self.PhysicsObjectError, \
2688 "%s is not a valid dictionary" % str(value)
2689 for order in value.keys()+value.values():
2690 if not isinstance(order, str):
2691 raise self.PhysicsObjectError, \
2692 "%s is not a valid string" % str(value)
2693
2694 if name == 'split_orders':
2695 if not isinstance(value, list):
2696 raise self.PhysicsObjectError, \
2697 "%s is not a valid list" % str(value)
2698 for order in value:
2699 if not isinstance(order, str):
2700 raise self.PhysicsObjectError, \
2701 "%s is not a valid string" % str(value)
2702
2703 if name == 'model':
2704 if not isinstance(value, Model):
2705 raise self.PhysicsObjectError, \
2706 "%s is not a valid Model object" % str(value)
2707 if name in ['id', 'uid']:
2708 if not isinstance(value, int):
2709 raise self.PhysicsObjectError, \
2710 "Process %s %s is not an integer" % (name, repr(value))
2711
2712 if name == 'required_s_channels':
2713 if not isinstance(value, list):
2714 raise self.PhysicsObjectError, \
2715 "%s is not a valid list" % str(value)
2716 for l in value:
2717 if not isinstance(l, list):
2718 raise self.PhysicsObjectError, \
2719 "%s is not a valid list of lists" % str(value)
2720 for i in l:
2721 if not isinstance(i, int):
2722 raise self.PhysicsObjectError, \
2723 "%s is not a valid list of integers" % str(l)
2724 if i == 0:
2725 raise self.PhysicsObjectError, \
2726 "Not valid PDG code %d for s-channel particle" % i
2727
2728 if name in ['forbidden_onsh_s_channels', 'forbidden_s_channels']:
2729 if not isinstance(value, list):
2730 raise self.PhysicsObjectError, \
2731 "%s is not a valid list" % str(value)
2732 for i in value:
2733 if not isinstance(i, int):
2734 raise self.PhysicsObjectError, \
2735 "%s is not a valid list of integers" % str(value)
2736 if i == 0:
2737 raise self.PhysicsObjectError, \
2738 "Not valid PDG code %d for s-channel particle" % str(value)
2739
2740 if name == 'forbidden_particles':
2741 if not isinstance(value, list):
2742 raise self.PhysicsObjectError, \
2743 "%s is not a valid list" % str(value)
2744 for i in value:
2745 if not isinstance(i, int):
2746 raise self.PhysicsObjectError, \
2747 "%s is not a valid list of integers" % str(value)
2748 if i <= 0:
2749 raise self.PhysicsObjectError, \
2750 "Forbidden particles should have a positive PDG code" % str(value)
2751
2752 if name == 'perturbation_couplings':
2753 if not isinstance(value, list):
2754 raise self.PhysicsObjectError, \
2755 "%s is not a valid list" % str(value)
2756 for order in value:
2757 if not isinstance(order, str):
2758 raise self.PhysicsObjectError, \
2759 "%s is not a valid string" % str(value)
2760
2761 if name == 'is_decay_chain':
2762 if not isinstance(value, bool):
2763 raise self.PhysicsObjectError, \
2764 "%s is not a valid bool" % str(value)
2765
2766 if name == 'has_born':
2767 if not isinstance(value, bool):
2768 raise self.PhysicsObjectError, \
2769 "%s is not a valid bool" % str(value)
2770
2771 if name == 'decay_chains':
2772 if not isinstance(value, ProcessList):
2773 raise self.PhysicsObjectError, \
2774 "%s is not a valid ProcessList" % str(value)
2775
2776 if name == 'NLO_mode':
2777 import madgraph.interface.madgraph_interface as mg
2778 if value not in mg.MadGraphCmd._valid_nlo_modes:
2779 raise self.PhysicsObjectError, \
2780 "%s is not a valid NLO_mode" % str(value)
2781 return True
2782
2784 """ A process, not being a ProcessDefinition never carries multiple
2785 particles labels"""
2786
2787 return False
2788
2789 - def set(self, name, value):
2790 """Special set for forbidden particles - set to abs value."""
2791
2792 if name == 'forbidden_particles':
2793 try:
2794 value = [abs(i) for i in value]
2795 except Exception:
2796 pass
2797
2798 if name == 'required_s_channels':
2799
2800 if value and isinstance(value, list) and \
2801 not isinstance(value[0], list):
2802 value = [value]
2803
2804 return super(Process, self).set(name, value)
2805
2807 """ Return what kind of squared order constraint was specified for the
2808 order 'order'."""
2809
2810 if order in self['sqorders_types'].keys():
2811 return self['sqorders_types'][order]
2812 else:
2813
2814 return '='
2815
2816 - def get(self, name):
2817 """Special get for legs_with_decays"""
2818
2819 if name == 'legs_with_decays':
2820 self.get_legs_with_decays()
2821
2822 if name == 'sqorders_types':
2823
2824 for order in self['squared_orders'].keys():
2825 if order not in self['sqorders_types']:
2826
2827 self['sqorders_types'][order]='='
2828
2829 return super(Process, self).get(name)
2830
2832 """Return process property names as a nicely sorted list."""
2833
2834 return ['legs', 'orders', 'overall_orders', 'squared_orders',
2835 'model', 'id', 'required_s_channels',
2836 'forbidden_onsh_s_channels', 'forbidden_s_channels',
2837 'forbidden_particles', 'is_decay_chain', 'decay_chains',
2838 'legs_with_decays', 'perturbation_couplings', 'has_born',
2839 'NLO_mode','split_orders']
2840
2841 - def nice_string(self, indent=0, print_weighted = True):
2842 """Returns a nicely formated string about current process
2843 content. Since the WEIGHTED order is automatically set and added to
2844 the user-defined list of orders, it can be ommitted for some info
2845 displays."""
2846
2847 mystr = " " * indent + "Process: "
2848 prevleg = None
2849 for leg in self['legs']:
2850 mypart = self['model'].get('particle_dict')[leg['id']]
2851 if prevleg and prevleg['state'] == False \
2852 and leg['state'] == True:
2853
2854 mystr = mystr + '> '
2855
2856 if self['required_s_channels'] and \
2857 self['required_s_channels'][0]:
2858 mystr += "|".join([" ".join([self['model'].\
2859 get('particle_dict')[req_id].get_name() \
2860 for req_id in id_list]) \
2861 for id_list in self['required_s_channels']])
2862 mystr = mystr + ' > '
2863
2864 mystr = mystr + mypart.get_name() + ' '
2865
2866 prevleg = leg
2867
2868
2869 if self['orders']:
2870 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
2871 for key in self['orders'] if (print_weighted or key!='WEIGHTED') \
2872 and not key in self['squared_orders'].keys()]) + ' '
2873
2874
2875 if self['perturbation_couplings']:
2876 mystr = mystr + '[ '
2877 if self['NLO_mode']!='tree':
2878 if self['NLO_mode']=='virt' and not self['has_born']:
2879 mystr = mystr + 'sqrvirt = '
2880 else:
2881 mystr = mystr + self['NLO_mode'] + ' = '
2882 for order in self['perturbation_couplings']:
2883 mystr = mystr + order + ' '
2884 mystr = mystr + '] '
2885
2886
2887 if self['squared_orders']:
2888 mystr = mystr + " ".join([key + '^2%s%d'%\
2889 (self.get_squared_order_type(key),self['squared_orders'][key]) \
2890 for key in self['squared_orders'].keys() \
2891 if print_weighted or key!='WEIGHTED']) + ' '
2892
2893
2894 if self['forbidden_onsh_s_channels']:
2895 mystr = mystr + '$ '
2896 for forb_id in self['forbidden_onsh_s_channels']:
2897 forbpart = self['model'].get('particle_dict')[forb_id]
2898 mystr = mystr + forbpart.get_name() + ' '
2899
2900
2901 if self['forbidden_s_channels']:
2902 mystr = mystr + '$$ '
2903 for forb_id in self['forbidden_s_channels']:
2904 forbpart = self['model'].get('particle_dict')[forb_id]
2905 mystr = mystr + forbpart.get_name() + ' '
2906
2907
2908 if self['forbidden_particles']:
2909 mystr = mystr + '/ '
2910 for forb_id in self['forbidden_particles']:
2911 forbpart = self['model'].get('particle_dict')[forb_id]
2912 mystr = mystr + forbpart.get_name() + ' '
2913
2914
2915 mystr = mystr[:-1]
2916
2917 if self.get('id') or self.get('overall_orders'):
2918 mystr += " @%d" % self.get('id')
2919 if self.get('overall_orders'):
2920 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
2921 for key in sorted(self['orders'])]) + ' '
2922
2923 if not self.get('decay_chains'):
2924 return mystr
2925
2926 for decay in self['decay_chains']:
2927 mystr = mystr + '\n' + \
2928 decay.nice_string(indent + 2).replace('Process', 'Decay')
2929
2930 return mystr
2931
3022
3024 """Returns a string containing only the basic process (w/o decays)."""
3025
3026 mystr = ""
3027 prevleg = None
3028 for leg in self.get_legs_with_decays():
3029 mypart = self['model'].get('particle_dict')[leg['id']]
3030 if prevleg and prevleg['state'] == False \
3031 and leg['state'] == True:
3032
3033 mystr = mystr + '> '
3034 mystr = mystr + mypart.get_name() + ' '
3035 prevleg = leg
3036
3037
3038 return mystr[:-1]
3039
3040 - def shell_string(self, schannel=True, forbid=True, main=True, pdg_order=False,
3041 print_id = True):
3042 """Returns process as string with '~' -> 'x', '>' -> '_',
3043 '+' -> 'p' and '-' -> 'm', including process number,
3044 intermediate s-channels and forbidden particles,
3045 pdg_order allow to order to leg order by pid."""
3046
3047 mystr = ""
3048 if not self.get('is_decay_chain') and print_id:
3049 mystr += "%d_" % self['id']
3050
3051 prevleg = None
3052 if pdg_order:
3053 legs = [l for l in self['legs'][1:]]
3054 def order_leg(l1,l2):
3055 id1 = l1.get('id')
3056 id2 = l2.get('id')
3057 return id2-id1
3058 legs.sort(cmp=order_leg)
3059 legs.insert(0, self['legs'][0])
3060 else:
3061 legs = self['legs']
3062
3063
3064 for leg in legs:
3065 mypart = self['model'].get('particle_dict')[leg['id']]
3066 if prevleg and prevleg['state'] == False \
3067 and leg['state'] == True:
3068
3069 mystr = mystr + '_'
3070
3071 if self['required_s_channels'] and \
3072 self['required_s_channels'][0] and schannel:
3073 mystr += "_or_".join(["".join([self['model'].\
3074 get('particle_dict')[req_id].get_name() \
3075 for req_id in id_list]) \
3076 for id_list in self['required_s_channels']])
3077 mystr = mystr + '_'
3078 if mypart['is_part']:
3079 mystr = mystr + mypart['name']
3080 else:
3081 mystr = mystr + mypart['antiname']
3082 prevleg = leg
3083
3084
3085 if self['forbidden_particles'] and forbid:
3086 mystr = mystr + '_no_'
3087 for forb_id in self['forbidden_particles']:
3088 forbpart = self['model'].get('particle_dict')[forb_id]
3089 mystr = mystr + forbpart.get_name()
3090
3091
3092 mystr = mystr.replace('~', 'x')
3093
3094 mystr = mystr.replace('+', 'p')
3095
3096 mystr = mystr.replace('-', 'm')
3097
3098 mystr = mystr.replace(' ', '')
3099
3100 for decay in self.get('decay_chains'):
3101 mystr = mystr + "_" + decay.shell_string(schannel,forbid, main=False,
3102 pdg_order=pdg_order)
3103
3104
3105 if len(mystr) > 64 and main:
3106 if schannel and forbid:
3107 out = self.shell_string(True, False, True, pdg_order)
3108 elif schannel:
3109 out = self.shell_string(False, False, True, pdg_order)
3110 else:
3111 out = mystr[:64]
3112 if not out.endswith('_%s' % self['uid']):
3113 out += '_%s' % self['uid']
3114 return out
3115
3116 return mystr
3117
3119 """Returns process as v4-compliant string with '~' -> 'x' and
3120 '>' -> '_'"""
3121
3122 mystr = "%d_" % self['id']
3123 prevleg = None
3124 for leg in self.get_legs_with_decays():
3125 mypart = self['model'].get('particle_dict')[leg['id']]
3126 if prevleg and prevleg['state'] == False \
3127 and leg['state'] == True:
3128
3129 mystr = mystr + '_'
3130 if mypart['is_part']:
3131 mystr = mystr + mypart['name']
3132 else:
3133 mystr = mystr + mypart['antiname']
3134 prevleg = leg
3135
3136
3137 mystr = mystr.replace('~', 'x')
3138
3139 mystr = mystr.replace(' ', '')
3140
3141 return mystr
3142
3143
3144
3146 """ Check iteratively that no coupling order constraint include negative
3147 values."""
3148
3149 if any(val<0 for val in self.get('orders').values()+\
3150 self.get('squared_orders').values()):
3151 return True
3152
3153 for procdef in self['decay_chains']:
3154 if procdef.are_negative_orders_present():
3155 return True
3156
3157 return False
3158
3160 """ Check iteratively that the decayed processes are not perturbed """
3161
3162 for procdef in self['decay_chains']:
3163 if procdef['perturbation_couplings'] or procdef.are_decays_perturbed():
3164 return True
3165 return False
3166
3168 """ Check iteratively that the decayed processes are not perturbed """
3169
3170 for procdef in self['decay_chains']:
3171 if procdef['squared_orders']!={} or procdef.decays_have_squared_orders():
3172 return True
3173 return False
3174
3176 """Gives number of initial state particles"""
3177
3178 return len(filter(lambda leg: leg.get('state') == False,
3179 self.get('legs')))
3180
3182 """Gives the pdg codes for initial state particles"""
3183
3184 return [leg.get('id') for leg in \
3185 filter(lambda leg: leg.get('state') == False,
3186 self.get('legs'))]
3187
3189 """Return the pdg codes for initial state particles for beam number"""
3190
3191 return filter(lambda leg: leg.get('state') == False and\
3192 leg.get('number') == number,
3193 self.get('legs'))[0].get('id')
3194
3196 """return a tuple of two tuple containing the id of the initial/final
3197 state particles. Each list is ordered"""
3198
3199 initial = []
3200 final = [l.get('id') for l in self.get('legs')\
3201 if l.get('state') or initial.append(l.get('id'))]
3202 initial.sort()
3203 final.sort()
3204 return (tuple(initial), tuple(final))
3205
3226
3227
3229 """Gives the final state legs"""
3230
3231 return filter(lambda leg: leg.get('state') == True,
3232 self.get('legs'))
3233
3235 """Gives the pdg codes for final state particles"""
3236
3237 return [l.get('id') for l in self.get_final_legs()]
3238
3239
3241 """Return process with all decay chains substituted in."""
3242
3243 if self['legs_with_decays']:
3244 return self['legs_with_decays']
3245
3246 legs = copy.deepcopy(self.get('legs'))
3247 org_decay_chains = copy.copy(self.get('decay_chains'))
3248 sorted_decay_chains = []
3249
3250 for leg in legs:
3251 if not leg.get('state'): continue
3252 org_ids = [l.get('legs')[0].get('id') for l in \
3253 org_decay_chains]
3254 if leg.get('id') in org_ids:
3255 sorted_decay_chains.append(org_decay_chains.pop(\
3256 org_ids.index(leg.get('id'))))
3257 assert not org_decay_chains
3258 ileg = 0
3259 for decay in sorted_decay_chains:
3260 while legs[ileg].get('state') == False or \
3261 legs[ileg].get('id') != decay.get('legs')[0].get('id'):
3262 ileg = ileg + 1
3263 decay_legs = decay.get_legs_with_decays()
3264 legs = legs[:ileg] + decay_legs[1:] + legs[ileg+1:]
3265 ileg = ileg + len(decay_legs) - 1
3266
3267
3268 legs = [copy.copy(l) for l in legs]
3269
3270 for ileg, leg in enumerate(legs):
3271 leg.set('number', ileg + 1)
3272
3273 self['legs_with_decays'] = LegList(legs)
3274
3275 return self['legs_with_decays']
3276
3278 """Output a list that can be compared to other processes as:
3279 [id, sorted(initial leg ids), sorted(final leg ids),
3280 sorted(decay list_for_sorts)]"""
3281
3282 sorted_list = [self.get('id'),
3283 sorted(self.get_initial_ids()),
3284 sorted(self.get_final_ids())]
3285
3286 if self.get('decay_chains'):
3287 sorted_list.extend(sorted([d.list_for_sort() for d in \
3288 self.get('decay_chains')]))
3289
3290 return sorted_list
3291
3293 """Sorting routine which allows to sort processes for
3294 comparison. Compare only process id and legs."""
3295
3296 if self.list_for_sort() > other.list_for_sort():
3297 return 1
3298 if self.list_for_sort() < other.list_for_sort():
3299 return -1
3300 return 0
3301
3303 """Calculate the denominator factor for identical final state particles
3304 """
3305
3306 final_legs = filter(lambda leg: leg.get('state') == True, \
3307 self.get_legs_with_decays())
3308
3309 identical_indices = {}
3310 for leg in final_legs:
3311 if leg.get('id') in identical_indices:
3312 identical_indices[leg.get('id')] = \
3313 identical_indices[leg.get('id')] + 1
3314 else:
3315 identical_indices[leg.get('id')] = 1
3316 return reduce(lambda x, y: x * y, [ math.factorial(val) for val in \
3317 identical_indices.values() ], 1)
3318
3320 """Ensure that maximum expansion orders from the model are
3321 properly taken into account in the process"""
3322
3323
3324 expansion_orders = self.get('model').get('expansion_order')
3325 orders = self.get('orders')
3326 sq_orders = self.get('squared_orders')
3327
3328 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
3329 for (k,v) in tmp:
3330 if k in orders:
3331 if v < orders[k]:
3332 if k in sq_orders.keys() and \
3333 (sq_orders[k]>v or sq_orders[k]<0):
3334 logger.warning(
3335 '''The process with the squared coupling order (%s^2%s%s) specified can potentially
3336 recieve contributions with powers of the coupling %s larger than the maximal
3337 value allowed by the model builder (%s). Hence, MG5_aMC sets the amplitude order
3338 for that coupling to be this maximal one. '''%(k,self.get('sqorders_types')[k],
3339 self.get('squared_orders')[k],k,v))
3340 else:
3341 logger.warning(
3342 '''The coupling order (%s=%s) specified is larger than the one allowed
3343 by the model builder. The maximal value allowed is %s.
3344 We set the %s order to this value''' % (k,orders[k],v,k))
3345 orders[k] = v
3346 else:
3347 orders[k] = v
3348
3350 """Overloading the equality operator, so that only comparison
3351 of process id and legs is being done, using compare_for_sort."""
3352
3353 if not isinstance(other, Process):
3354 return False
3355
3356 return self.compare_for_sort(other) == 0
3357
3359 return not self.__eq__(other)
3360
3365 """List of Process objects
3366 """
3367
3369 """Test if object obj is a valid Process for the list."""
3370
3371 return isinstance(obj, Process)
3372
3374 """Returns a nicely formatted string of the matrix element processes."""
3375
3376 mystr = "\n".join([p.nice_string(indent) for p in self])
3377
3378 return mystr
3379
3384 """ProcessDefinition: list of multilegs (ordered)
3385 dictionary of orders
3386 model
3387 process id
3388 """
3389
3399
3400 - def filter(self, name, value):
3416
3418 """ Check that this process definition will yield a single process, as
3419 each multileg only has one leg"""
3420
3421 for mleg in self['legs']:
3422 if len(mleg['ids'])>1:
3423 return True
3424
3425 return False
3426
3434
3436 """Retrieve the minimum starting guess for WEIGHTED order, to
3437 use in find_optimal_process_orders in MultiProcess diagram
3438 generation (as well as particles and hierarchy). The algorithm:
3439
3440 1) Pick out the legs in the multiprocess according to the
3441 highest hierarchy represented (so don't mix particles from
3442 different hierarchy classes in the same multiparticles!)
3443
3444 2) Find the starting maximum WEIGHTED order as the sum of the
3445 highest n-2 weighted orders
3446
3447 3) Pick out required s-channel particle hierarchies, and use
3448 the highest of the maximum WEIGHTED order from the legs and
3449 the minimum WEIGHTED order extracted from 2*s-channel
3450 hierarchys plus the n-2-2*(number of s-channels) lowest
3451 leg weighted orders.
3452 """
3453
3454 model = self.get('model')
3455
3456
3457
3458 particles, hierarchy = model.get_particles_hierarchy()
3459
3460
3461
3462 max_order_now = []
3463 new_legs = copy.copy(self.get('legs'))
3464 for parts, value in zip(particles, hierarchy):
3465 ileg = 0
3466 while ileg < len(new_legs):
3467 if any([id in parts for id in new_legs[ileg].get('ids')]):
3468 max_order_now.append(value)
3469 new_legs.pop(ileg)
3470 else:
3471 ileg += 1
3472
3473
3474
3475 max_order_now = sorted(max_order_now)[2:]
3476
3477
3478 max_order_prop = []
3479 for idlist in self.get('required_s_channels'):
3480 max_order_prop.append([0,0])
3481 for id in idlist:
3482 for parts, value in zip(particles, hierarchy):
3483 if id in parts:
3484 max_order_prop[-1][0] += 2*value
3485 max_order_prop[-1][1] += 1
3486 break
3487
3488 if max_order_prop:
3489 if len(max_order_prop) >1:
3490 max_order_prop = min(*max_order_prop, key=lambda x:x[0])
3491 else:
3492 max_order_prop = max_order_prop[0]
3493
3494
3495
3496
3497 max_order_now = max(sum(max_order_now),
3498 max_order_prop[0] + \
3499 sum(max_order_now[:-2 * max_order_prop[1]]))
3500 else:
3501 max_order_now = sum(max_order_now)
3502
3503 return max_order_now, particles, hierarchy
3504
3505 - def nice_string(self, indent=0, print_weighted=False):
3506 """Returns a nicely formated string about current process
3507 content"""
3508
3509 mystr = " " * indent + "Process: "
3510 prevleg = None
3511 for leg in self['legs']:
3512 myparts = \
3513 "/".join([self['model'].get('particle_dict')[id].get_name() \
3514 for id in leg.get('ids')])
3515 if prevleg and prevleg['state'] == False \
3516 and leg['state'] == True:
3517
3518 mystr = mystr + '> '
3519
3520 if self['required_s_channels'] and \
3521 self['required_s_channels'][0]:
3522 mystr += "|".join([" ".join([self['model'].\
3523 get('particle_dict')[req_id].get_name() \
3524 for req_id in id_list]) \
3525 for id_list in self['required_s_channels']])
3526 mystr = mystr + '> '
3527
3528 mystr = mystr + myparts + ' '
3529
3530 prevleg = leg
3531
3532
3533 if self['forbidden_onsh_s_channels']:
3534 mystr = mystr + '$ '
3535 for forb_id in self['forbidden_onsh_s_channels']:
3536 forbpart = self['model'].get('particle_dict')[forb_id]
3537 mystr = mystr + forbpart.get_name() + ' '
3538
3539
3540 if self['forbidden_s_channels']:
3541 mystr = mystr + '$$ '
3542 for forb_id in self['forbidden_s_channels']:
3543 forbpart = self['model'].get('particle_dict')[forb_id]
3544 mystr = mystr + forbpart.get_name() + ' '
3545
3546
3547 if self['forbidden_particles']:
3548 mystr = mystr + '/ '
3549 for forb_id in self['forbidden_particles']:
3550 forbpart = self['model'].get('particle_dict')[forb_id]
3551 mystr = mystr + forbpart.get_name() + ' '
3552
3553 if self['orders']:
3554 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
3555 for key in sorted(self['orders'])]) + ' '
3556
3557
3558 if self['perturbation_couplings']:
3559 mystr = mystr + '[ '
3560 if self['NLO_mode']!='tree':
3561 if self['NLO_mode']=='virt' and not self['has_born']:
3562 mystr = mystr + 'sqrvirt = '
3563 else:
3564 mystr = mystr + self['NLO_mode'] + ' = '
3565 for order in self['perturbation_couplings']:
3566 mystr = mystr + order + ' '
3567 mystr = mystr + '] '
3568
3569 if self['squared_orders']:
3570 mystr = mystr + " ".join([key + '^2%s%d'%\
3571 (self.get_squared_order_type(key),self['squared_orders'][key]) \
3572 for key in self['squared_orders'].keys() \
3573 if print_weighted or key!='WEIGHTED']) + ' '
3574
3575
3576 mystr = mystr[:-1]
3577
3578 if self.get('id') or self.get('overall_orders'):
3579 mystr += " @%d" % self.get('id')
3580 if self.get('overall_orders'):
3581 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3582 for key in sorted(self['orders'])]) + ' '
3583
3584 if not self.get('decay_chains'):
3585 return mystr
3586
3587 for decay in self['decay_chains']:
3588 mystr = mystr + '\n' + \
3589 decay.nice_string(indent + 2).replace('Process', 'Decay')
3590
3591 return mystr
3592
3594 """ Return a Process object which has the same properties of this
3595 ProcessDefinition but with the specified LegList as legs attribute.
3596 """
3597
3598 return Process({\
3599 'legs': LegList,
3600 'model':self.get('model'),
3601 'id': self.get('id'),
3602 'orders': self.get('orders'),
3603 'sqorders_types': self.get('sqorders_types'),
3604 'squared_orders': self.get('squared_orders'),
3605 'has_born': self.get('has_born'),
3606 'required_s_channels': self.get('required_s_channels'),
3607 'forbidden_onsh_s_channels': self.get('forbidden_onsh_s_channels'),
3608 'forbidden_s_channels': self.get('forbidden_s_channels'),
3609 'forbidden_particles': self.get('forbidden_particles'),
3610 'perturbation_couplings': self.get('perturbation_couplings'),
3611 'is_decay_chain': self.get('is_decay_chain'),
3612 'overall_orders': self.get('overall_orders'),
3613 'split_orders': self.get('split_orders'),
3614 'NLO_mode': self.get('NLO_mode')
3615 })
3616
3617 - def get_process(self, initial_state_ids, final_state_ids):
3618 """ Return a Process object which has the same properties of this
3619 ProcessDefinition but with the specified given leg ids. """
3620
3621
3622
3623 my_isids = [leg.get('ids') for leg in self.get('legs') \
3624 if not leg.get('state')]
3625 my_fsids = [leg.get('ids') for leg in self.get('legs') \
3626 if leg.get('state')]
3627 for i, is_id in enumerate(initial_state_ids):
3628 assert is_id in my_isids[i]
3629 for i, fs_id in enumerate(final_state_ids):
3630 assert fs_id in my_fsids[i]
3631
3632 return self.get_process_with_legs(LegList(\
3633 [Leg({'id': id, 'state':False}) for id in initial_state_ids] + \
3634 [Leg({'id': id, 'state':True}) for id in final_state_ids]))
3635
3637 """Overloading the equality operator, so that only comparison
3638 of process id and legs is being done, using compare_for_sort."""
3639
3640 return super(Process, self).__eq__(other)
3641
3646 """List of ProcessDefinition objects
3647 """
3648
3650 """Test if object obj is a valid ProcessDefinition for the list."""
3651
3652 return isinstance(obj, ProcessDefinition)
3653
3659 """Make sure there are no doublets in the list doubletlist.
3660 Note that this is a slow implementation, so don't use if speed
3661 is needed"""
3662
3663 assert isinstance(doubletlist, list), \
3664 "Argument to make_unique must be list"
3665
3666
3667 uniquelist = []
3668 for elem in doubletlist:
3669 if elem not in uniquelist:
3670 uniquelist.append(elem)
3671
3672 doubletlist[:] = uniquelist[:]
3673