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