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