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