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