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 self['allow_pickle'] = True
1048 self['limitations'] = []
1049
1050
1051
1052
1053
1054 - def filter(self, name, value):
1055 """Filter for model property values"""
1056
1057 if name in ['name']:
1058 if not isinstance(value, str):
1059 raise self.PhysicsObjectError, \
1060 "Object of type %s is not a string" %type(value)
1061
1062 elif name == 'particles':
1063 if not isinstance(value, ParticleList):
1064 raise self.PhysicsObjectError, \
1065 "Object of type %s is not a ParticleList object" % \
1066 type(value)
1067 elif name == 'interactions':
1068 if not isinstance(value, InteractionList):
1069 raise self.PhysicsObjectError, \
1070 "Object of type %s is not a InteractionList object" % \
1071 type(value)
1072 elif name == 'particle_dict':
1073 if not isinstance(value, dict):
1074 raise self.PhysicsObjectError, \
1075 "Object of type %s is not a dictionary" % \
1076 type(value)
1077 elif name == 'interaction_dict':
1078 if not isinstance(value, dict):
1079 raise self.PhysicsObjectError, \
1080 "Object of type %s is not a dictionary" % type(value)
1081
1082 elif name == 'ref_dict_to0':
1083 if not isinstance(value, dict):
1084 raise self.PhysicsObjectError, \
1085 "Object of type %s is not a dictionary" % type(value)
1086
1087 elif name == 'ref_dict_to1':
1088 if not isinstance(value, dict):
1089 raise self.PhysicsObjectError, \
1090 "Object of type %s is not a dictionary" % type(value)
1091
1092 elif name == 'got_majoranas':
1093 if not (isinstance(value, bool) or value == None):
1094 raise self.PhysicsObjectError, \
1095 "Object of type %s is not a boolean" % type(value)
1096
1097 elif name == 'conserved_charge':
1098 if not (isinstance(value, set)):
1099 raise self.PhysicsObjectError, \
1100 "Object of type %s is not a set" % type(value)
1101
1102 elif name == 'version_tag':
1103 if not (isinstance(value, str)):
1104 raise self.PhysicsObjectError, \
1105 "Object of type %s is not a string" % type(value)
1106
1107 elif name == 'order_hierarchy':
1108 if not isinstance(value, dict):
1109 raise self.PhysicsObjectError, \
1110 "Object of type %s is not a dictionary" % \
1111 type(value)
1112 for key in value.keys():
1113 if not isinstance(value[key],int):
1114 raise self.PhysicsObjectError, \
1115 "Object of type %s is not an integer" % \
1116 type(value[key])
1117 elif name == 'gauge':
1118 if not (isinstance(value, list)):
1119 raise self.PhysicsObjectError, \
1120 "Object of type %s is not a list" % type(value)
1121
1122 elif name == 'case_sensitive':
1123 if not value in [True ,False]:
1124 raise self.PhysicsObjectError, \
1125 "Object of type %s is not a boolean" % type(value)
1126
1127
1128 return True
1129
1130 - def get(self, name):
1131 """Get the value of the property name."""
1132
1133 if (name == 'ref_dict_to0' or name == 'ref_dict_to1') and \
1134 not self[name]:
1135 if self['interactions']:
1136 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1137 self['interactions'].generate_ref_dict()
1138 self['ref_dict_to0'].update(
1139 self['particles'].generate_ref_dict())
1140
1141 if (name == 'particle_dict') and not self[name]:
1142 if self['particles']:
1143 self['particle_dict'] = self['particles'].generate_dict()
1144 if self['interactions']:
1145 self['interactions'].synchronize_interactions_with_particles(\
1146 self['particle_dict'])
1147 if name == 'modelpath':
1148 modeldir = self.get('version_tag').rsplit('##',1)[0]
1149 if os.path.exists(modeldir):
1150 modeldir = os.path.expanduser(modeldir)
1151 return modeldir
1152 else:
1153 raise Exception, "path %s not valid anymore." % modeldir
1154
1155
1156
1157
1158
1159 elif name == 'modelpath+restriction':
1160 modeldir = self.get('version_tag').rsplit('##',1)[0]
1161 modelname = self['name']
1162 if not os.path.exists(modeldir):
1163 raise Exception, "path %s not valid anymore" % modeldir
1164 modeldir = os.path.dirname(modeldir)
1165 modeldir = pjoin(modeldir, modelname)
1166 modeldir = os.path.expanduser(modeldir)
1167 return modeldir
1168 elif name == 'restrict_name':
1169 modeldir = self.get('version_tag').rsplit('##',1)[0]
1170 modelname = self['name']
1171 basename = os.path.basename(modeldir)
1172 restriction = modelname[len(basename)+1:]
1173 return restriction
1174
1175 if (name == 'interaction_dict') and not self[name]:
1176 if self['interactions']:
1177 self['interaction_dict'] = self['interactions'].generate_dict()
1178
1179 if (name == 'got_majoranas') and self[name] == None:
1180 if self['particles']:
1181 self['got_majoranas'] = self.check_majoranas()
1182
1183 if (name == 'coupling_orders') and self[name] == None:
1184 if self['interactions']:
1185 self['coupling_orders'] = self.get_coupling_orders()
1186
1187 if (name == 'order_hierarchy') and not self[name]:
1188 if self['interactions']:
1189 self['order_hierarchy'] = self.get_order_hierarchy()
1190
1191 if (name == 'expansion_order') and self[name] == None:
1192 if self['interactions']:
1193 self['expansion_order'] = \
1194 dict([(order, -1) for order in self.get('coupling_orders')])
1195
1196 if (name == 'name2pdg') and 'name2pdg' not in self:
1197 self['name2pdg'] = {}
1198 for p in self.get('particles'):
1199 self['name2pdg'][p.get('antiname')] = -1*p.get('pdg_code')
1200 self['name2pdg'][p.get('name')] = p.get('pdg_code')
1201
1202 return Model.__bases__[0].get(self, name)
1203
1204 - def set(self, name, value, force = False):
1205 """Special set for particles and interactions - need to
1206 regenerate dictionaries."""
1207
1208 if name == 'particles':
1209
1210 make_unique(value)
1211
1212 self['particle_dict'] = {}
1213 self['ref_dict_to0'] = {}
1214 self['got_majoranas'] = None
1215
1216 if name == 'interactions':
1217
1218 make_unique(value)
1219
1220 self['interaction_dict'] = {}
1221 self['ref_dict_to1'] = {}
1222 self['ref_dict_to0'] = {}
1223 self['got_majoranas'] = None
1224 self['coupling_orders'] = None
1225 self['order_hierarchy'] = {}
1226 self['expansion_order'] = None
1227
1228 if name == 'name2pdg':
1229 self['name2pgg'] = value
1230 return
1231
1232 result = Model.__bases__[0].set(self, name, value, force)
1233
1234 if name == 'particles':
1235
1236 self.get('particle_dict')
1237
1238 return result
1239
1241 """This function actualizes the dictionaries"""
1242
1243 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1244 self['interactions'].generate_ref_dict()
1245 self['ref_dict_to0'].update(
1246 self['particles'].generate_ref_dict())
1247
1249 """Return process property names as a nicely sorted list."""
1250
1251 return ['name', 'particles', 'parameters', 'interactions',
1252 'couplings','lorentz', 'gauge']
1253
1254 - def get_particle(self, id):
1255 """Return the particle corresponding to the id / name"""
1256
1257 try:
1258 return self["particle_dict"][id]
1259 except Exception:
1260 if isinstance(id, int):
1261 try:
1262 return self.get("particle_dict")[id]
1263 except Exception, error:
1264 return None
1265 else:
1266 if not hasattr(self, 'name2part'):
1267 self.create_name2part()
1268 try:
1269 return self.name2part[id]
1270 except:
1271 return None
1272
1274 """create a dictionary name 2 part"""
1275
1276 self.name2part = {}
1277 for part in self.get("particle_dict").values():
1278 self.name2part[part.get('name')] = part
1279 self.name2part[part.get('antiname')] = part
1280
1282 """return the lorentz object from the associate name"""
1283 if hasattr(self, 'lorentz_name2obj'):
1284 return self.lorentz_name2obj[name]
1285 else:
1286 self.create_lorentz_dict()
1287 return self.lorentz_name2obj[name]
1288
1290 """create the dictionary linked to the lorentz structure"""
1291 self.lorentz_name2obj = {}
1292 self.lorentz_expr2name = {}
1293 if not self.get('lorentz'):
1294 return
1295 for lor in self.get('lorentz'):
1296 self.lorentz_name2obj[lor.name] = lor
1297 self.lorentz_expr2name[lor.structure] = lor.name
1298
1300 """Return the interaction corresponding to the id"""
1301
1302 try:
1303 return self.get("interaction_dict")[id]
1304 except Exception:
1305 return None
1306
1308 """Return the parameter associated to the name NAME"""
1309
1310
1311 if hasattr(self, 'parameters_dict') and self.parameters_dict:
1312 try:
1313 return self.parameters_dict[name]
1314 except Exception:
1315
1316 pass
1317
1318
1319 self.parameters_dict = {}
1320 for data in self['parameters'].values():
1321 [self.parameters_dict.__setitem__(p.name,p) for p in data]
1322
1323 return self.parameters_dict[name]
1324
1326 """Determine the coupling orders of the model"""
1327 return set(sum([i.get('orders').keys() for i in \
1328 self.get('interactions')], []))
1329
1331 """Set a default order hierarchy for the model if not set by the UFO."""
1332
1333 hierarchy = dict([(order, 1) for order in self.get('coupling_orders')])
1334
1335 if self.get('coupling_orders') == set(['QCD', 'QED']):
1336 hierarchy['QED'] = 2
1337 return hierarchy
1338
1339
1341 """returns the number of light quark flavours in the model."""
1342 return len([p for p in self.get('particles') \
1343 if p['spin'] == 2 and p['is_part'] and \
1344 p ['color'] != 1 and p['mass'].lower() == 'zero'])
1345
1346
1348 """Returns the order hierarchies of the model and the
1349 particles which have interactions in at least this hierarchy
1350 (used in find_optimal_process_orders in MultiProcess diagram
1351 generation):
1352
1353 Check the coupling hierarchy of the model. Assign all
1354 particles to the different coupling hierarchies so that a
1355 particle is considered to be in the highest hierarchy (i.e.,
1356 with lowest value) where it has an interaction.
1357 """
1358
1359
1360 coupling_orders = self.get('coupling_orders')
1361
1362
1363 hierarchy = sorted(list(set([self.get('order_hierarchy')[k] for \
1364 k in coupling_orders])))
1365
1366
1367 orders = []
1368 for value in hierarchy:
1369 orders.append([ k for (k, v) in \
1370 self.get('order_hierarchy').items() if \
1371 v == value ])
1372
1373
1374
1375 interactions = []
1376 particles = []
1377 for iorder, order in enumerate(orders):
1378 sum_orders = sum(orders[:iorder+1], [])
1379 sum_interactions = sum(interactions[:iorder], [])
1380 sum_particles = sum([list(p) for p in particles[:iorder]], [])
1381
1382
1383 interactions.append([i for i in self.get('interactions') if \
1384 not i in sum_interactions and \
1385 not any([k not in sum_orders for k in \
1386 i.get('orders').keys()])])
1387
1388
1389 particles.append(set(sum([[p.get_pdg_code() for p in \
1390 inter.get('particles') if \
1391 p.get_pdg_code() not in sum_particles] \
1392 for inter in interactions[-1]], [])))
1393
1394 return particles, hierarchy
1395
1397 """Return the maximum WEIGHTED order for any interaction in the model,
1398 for equivalent 3-particle vertices. Note that it can be fractional."""
1399
1400 return max([inter.get_WEIGHTED_order(self) for inter in \
1401 self.get('interactions')])
1402
1403
1405 """Return True if there is fermion flow violation, False otherwise"""
1406
1407 if any([part.is_fermion() and part.get('self_antipart') \
1408 for part in self.get('particles')]):
1409 return True
1410
1411
1412
1413 for inter in self.get('interactions'):
1414
1415 if len(inter.get('particles'))==1:
1416 continue
1417 fermions = [p for p in inter.get('particles') if p.is_fermion()]
1418 for i in range(0, len(fermions), 2):
1419 if fermions[i].get('is_part') == \
1420 fermions[i+1].get('is_part'):
1421
1422 return True
1423
1424 return False
1425
1427 """Reset all dictionaries and got_majoranas. This is necessary
1428 whenever the particle or interaction content has changed. If
1429 particles or interactions are set using the set routine, this
1430 is done automatically."""
1431
1432 self['particle_dict'] = {}
1433 self['ref_dict_to0'] = {}
1434 self['got_majoranas'] = None
1435 self['interaction_dict'] = {}
1436 self['ref_dict_to1'] = {}
1437 self['ref_dict_to0'] = {}
1438
1440 """Change the name of the particles such that all SM and MSSM particles
1441 follows the MG convention"""
1442
1443 self.mg5_name = True
1444
1445
1446 def check_name_free(self, name):
1447 """ check if name is not use for a particle in the model if it is
1448 raise an MadGraph5error"""
1449 part = self['particles'].find_name(name)
1450 if part:
1451 error_text = \
1452 '%s particles with pdg code %s is in conflict with MG ' + \
1453 'convention name for particle %s.\n Use -modelname in order ' + \
1454 'to use the particles name defined in the model and not the ' + \
1455 'MadGraph5_aMC@NLO convention'
1456
1457 raise MadGraph5Error, error_text % \
1458 (part.get_name(), part.get_pdg_code(), pdg)
1459
1460 default = self.load_default_name()
1461
1462 for pdg in default.keys():
1463 part = self.get_particle(pdg)
1464 if not part:
1465 continue
1466 antipart = self.get_particle(-pdg)
1467 name = part.get_name()
1468 if name != default[pdg]:
1469 check_name_free(self, default[pdg])
1470 if part.get('is_part'):
1471 part.set('name', default[pdg])
1472 if antipart:
1473 antipart.set('name', default[pdg])
1474 else:
1475 part.set('antiname', default[pdg])
1476 else:
1477 part.set('antiname', default[pdg])
1478 if antipart:
1479 antipart.set('antiname', default[pdg])
1480
1481
1482 if self.get('name') == 'mssm' or self.get('name').startswith('mssm-'):
1483 part = self.get_particle(25)
1484 part.set('name', 'h1')
1485 part.set('antiname', 'h1')
1486
1487
1488
1490 """ Change all model parameter by a given prefix.
1491 Modify the parameter if some of them are identical up to the case"""
1492
1493 lower_dict={}
1494 duplicate = set()
1495 keys = self.get('parameters').keys()
1496 for key in keys:
1497 for param in self['parameters'][key]:
1498 lower_name = param.name.lower()
1499 if not lower_name:
1500 continue
1501 try:
1502 lower_dict[lower_name].append(param)
1503 except KeyError:
1504 lower_dict[lower_name] = [param]
1505 else:
1506 duplicate.add(lower_name)
1507 logger.debug('%s is defined both as lower case and upper case.'
1508 % lower_name)
1509
1510 if prefix == '' and not duplicate:
1511 return
1512
1513 re_expr = r'''\b(%s)\b'''
1514 to_change = []
1515 change={}
1516
1517 for key in keys:
1518 for param in self['parameters'][key]:
1519 value = param.name.lower()
1520 if value in ['as','mu_r', 'zero','aewm1','g']:
1521 continue
1522 elif value.startswith(prefix):
1523 continue
1524 elif value in duplicate:
1525 continue
1526 elif value:
1527 change[param.name] = '%s%s' % (prefix,param.name)
1528 to_change.append(param.name)
1529 param.name = change[param.name]
1530
1531 for value in duplicate:
1532 for i, var in enumerate(lower_dict[value]):
1533 to_change.append(var.name)
1534 new_name = '%s%s%s' % (prefix, var.name.lower(),
1535 ('__%d'%(i+1) if i>0 else ''))
1536 change[var.name] = new_name
1537 var.name = new_name
1538 to_change.append(var.name)
1539 assert 'zero' not in to_change
1540 replace = lambda match_pattern: change[match_pattern.groups()[0]]
1541
1542 if not to_change:
1543 return
1544
1545 if 'parameter_dict' in self:
1546 new_dict = dict( (change[name] if (name in change) else name, value) for
1547 name, value in self['parameter_dict'].items())
1548 self['parameter_dict'] = new_dict
1549
1550 if hasattr(self,'map_CTcoup_CTparam'):
1551
1552
1553 self.map_CTcoup_CTparam = dict( (coup_name,
1554 [change[name] if (name in change) else name for name in params])
1555 for coup_name, params in self.map_CTcoup_CTparam.items() )
1556
1557 i=0
1558 while i*1000 <= len(to_change):
1559 one_change = to_change[i*1000: min((i+1)*1000,len(to_change))]
1560 i+=1
1561 rep_pattern = re.compile('\\b%s\\b'% (re_expr % ('\\b|\\b'.join(one_change))))
1562
1563
1564 for key in keys:
1565 if key == ('external',):
1566 continue
1567 for param in self['parameters'][key]:
1568 param.expr = rep_pattern.sub(replace, param.expr)
1569
1570 for key in self['couplings'].keys():
1571 for coup in self['couplings'][key]:
1572 coup.expr = rep_pattern.sub(replace, coup.expr)
1573
1574
1575 ff = [l.formfactors for l in self['lorentz'] if hasattr(l, 'formfactors')]
1576 ff = set(sum(ff,[]))
1577 for f in ff:
1578 f.value = rep_pattern.sub(replace, f.value)
1579
1580
1581 for part in self['particles']:
1582 if str(part.get('mass')) in one_change:
1583 part.set('mass', rep_pattern.sub(replace, str(part.get('mass'))))
1584 if str(part.get('width')) in one_change:
1585 part.set('width', rep_pattern.sub(replace, str(part.get('width'))))
1586 if hasattr(part, 'partial_widths'):
1587 for key, value in part.partial_widths.items():
1588 part.partial_widths[key] = rep_pattern.sub(replace, value)
1589
1590
1591 self['particle_dict'] =''
1592 self.get('particle_dict')
1593
1594
1595
1597 """Return the first positive number that is not a valid PDG code"""
1598 return [c for c in range(1, len(self.get('particles')) + 1) if \
1599 c not in self.get('particle_dict').keys()][0]
1600
1601
1603 """Write out the param_card, and return as string."""
1604
1605 import models.write_param_card as writer
1606 if not filepath:
1607 out = StringIO.StringIO()
1608 else:
1609 out = filepath
1610 param = writer.ParamCardWriter(self, filepath=out)
1611 if not filepath:
1612 return out.getvalue()
1613 else:
1614 return param
1615
1616 @ staticmethod
1618 """ load the default for name convention """
1619
1620 logger.info('Change particles name to pass to MG5 convention')
1621 default = {}
1622 for line in open(os.path.join(MG5DIR, 'input', \
1623 'particles_name_default.txt')):
1624 line = line.lstrip()
1625 if line.startswith('#'):
1626 continue
1627
1628 args = line.split()
1629 if len(args) != 2:
1630 logger.warning('Invalid syntax in interface/default_name:\n %s' % line)
1631 continue
1632 default[int(args[0])] = args[1].lower()
1633
1634 return default
1635
1637 """Change the electroweak mode. The only valid mode now is external.
1638 Where in top of the default MW and sw2 are external parameters."""
1639
1640 assert mode in ["external",set(['mz','mw','alpha'])]
1641
1642 try:
1643 W = self.get('particle_dict')[24]
1644 except KeyError:
1645 raise InvalidCmd('No W particle in the model impossible to '+
1646 'change the EW scheme!')
1647
1648 if mode=='external':
1649 MW = self.get_parameter(W.get('mass'))
1650 if not isinstance(MW, ParamCardVariable):
1651 newMW = ParamCardVariable(MW.name, MW.value, 'MASS', [24])
1652 if not newMW.value:
1653 newMW.value = 80.385
1654
1655 self.get('parameters')[MW.depend].remove(MW)
1656
1657 self.add_param(newMW, ['external'])
1658
1659
1660 try:
1661 sw2 = self.get_parameter('sw2')
1662 except KeyError:
1663 try:
1664 sw2 = self.get_parameter('mdl_sw2')
1665 except KeyError:
1666 sw2=None
1667
1668 if sw2:
1669 newsw2 = ParamCardVariable(sw2.name,sw2.value, 'SMINPUTS', [4])
1670 if not newsw2.value:
1671 newsw2.value = 0.222246485786
1672
1673 self.get('parameters')[sw2.depend].remove(sw2)
1674
1675 self.add_param(newsw2, ['external'])
1676
1677 self.parameters_dict = None
1678 return True
1679
1680 elif mode==set(['mz','mw','alpha']):
1681
1682 W = self.get('particle_dict')[24]
1683 mass = self.get_parameter(W.get('mass'))
1684 mass_expr = 'cmath.sqrt(%(prefix)sMZ__exp__2/2. + cmath.sqrt('+\
1685 '%(prefix)sMZ__exp__4/4. - (%(prefix)saEW*cmath.pi*%(prefix)s'+\
1686 'MZ__exp__2)/(%(prefix)sGf*%(prefix)ssqrt__2)))'
1687 if 'external' in mass.depend:
1688
1689 return True
1690 match = False
1691 if mass.expr == mass_expr%{'prefix':''}:
1692 prefix = ''
1693 match = True
1694 elif mass.expr == mass_expr%{'prefix':'mdl_'}:
1695 prefix = 'mdl_'
1696 match = True
1697 if match:
1698 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1699 if not MW.value:
1700 MW.value = 80.385
1701 self.get('parameters')[('external',)].append(MW)
1702 self.get('parameters')[mass.depend].remove(mass)
1703
1704 new_param = ModelVariable('Gf',
1705 '-%(prefix)saEW*%(prefix)sMZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - %(prefix)sMZ**2))' %\
1706 {'MW': mass.name,'prefix':prefix}, 'complex', mass.depend)
1707 Gf = self.get_parameter('%sGf'%prefix)
1708 self.get('parameters')[('external',)].remove(Gf)
1709 self.add_param(new_param, ['%saEW'%prefix])
1710
1711 self.parameters_dict = None
1712 return True
1713 else:
1714 return False
1715
1717 """modify the expression changing the mass to complex mass scheme"""
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733 try:
1734 CMSParam = self.get_parameter('CMSParam')
1735 except KeyError:
1736 try:
1737 CMSParam = self.get_parameter('mdl_CMSParam')
1738 except KeyError:
1739 CMSParam = None
1740
1741
1742 if not toCMS:
1743 if CMSParam:
1744 CMSParam.expr = '0.0'
1745 return
1746
1747
1748 if CMSParam:
1749 CMSParam.expr = '1.0'
1750
1751 to_change = {}
1752 mass_widths = []
1753 for particle in self.get('particles'):
1754 m = particle.get('width')
1755 if m in mass_widths:
1756 continue
1757 mass_widths.append(particle.get('width'))
1758 mass_widths.append(particle.get('mass'))
1759 width = self.get_parameter(particle.get('width'))
1760 if (isinstance(width.value, (complex,float)) and abs(width.value)==0.0) or \
1761 width.name.lower() =='zero':
1762
1763 continue
1764 if not isinstance(width, ParamCardVariable):
1765 width.expr = 're(%s)' % width.expr
1766 mass = self.get_parameter(particle.get('mass'))
1767 if (isinstance(width.value, (complex,float)) and abs(width.value)!=0.0) or \
1768 mass.name.lower() != 'zero':
1769
1770 if particle.get('pdg_code') == 24 and isinstance(mass,
1771 ModelVariable):
1772 status = self.change_electroweak_mode(
1773 set(['mz','mw','alpha']))
1774
1775 mass = self.get_parameter(particle.get('mass'))
1776 if not status:
1777 logger.warning('The W mass is not an external '+
1778 'parameter in this model and the automatic change of'+
1779 ' electroweak scheme changed. This is not advised for '+
1780 'applying the complex mass scheme.')
1781
1782
1783
1784 depend = list(set(mass.depend + width.depend))
1785 if len(depend)>1 and 'external' in depend:
1786 depend.remove('external')
1787 depend = tuple(depend)
1788 if depend == ('external',):
1789 depend = ()
1790
1791
1792 if isinstance(mass, ParamCardVariable):
1793 New_param = ModelVariable('CMASS_'+mass.name,
1794 'cmath.sqrt(%(mass)s**2 - complex(0,1) * %(mass)s * %(width)s)' \
1795 % {'mass': mass.name, 'width': width.name},
1796 'complex', depend)
1797 else:
1798 New_param = ModelVariable('CMASS_'+mass.name,
1799 mass.expr, 'complex', depend)
1800
1801 if not isinstance(width, ParamCardVariable):
1802 width.expr = '- im(%s**2) / cmath.sqrt(re(%s**2))' % (mass.expr, mass.expr)
1803 else:
1804
1805 New_width = ModelVariable(width.name,
1806 '-1 * im(CMASS_%s**2) / %s' % (mass.name, mass.name), 'real', mass.depend)
1807 self.get('parameters')[('external',)].remove(width)
1808 self.add_param(New_param, (mass,))
1809 self.add_param(New_width, (New_param,))
1810 mass.expr = 'cmath.sqrt(re(%s**2))' % mass.expr
1811 to_change[mass.name] = New_param.name
1812 continue
1813
1814 mass.expr = 're(%s)' % mass.expr
1815 self.add_param(New_param, (mass, width))
1816 to_change[mass.name] = New_param.name
1817
1818
1819 yukawas = [p for p in self.get('parameters')[('external',)]
1820 if p.lhablock.lower() == 'yukawa']
1821 for yukawa in yukawas:
1822
1823 self.get('parameters')[('external',)].remove(yukawa)
1824
1825 particle = self.get_particle(yukawa.lhacode[0])
1826 mass = self.get_parameter(particle.get('mass'))
1827
1828
1829 if mass.depend == ('external',):
1830 depend = ()
1831 else:
1832 depend = mass.depend
1833
1834 New_param = ModelVariable(yukawa.name, mass.name, 'real', depend)
1835
1836
1837 if mass.name in to_change:
1838 expr = 'CMASS_%s' % mass.name
1839 else:
1840 expr = mass.name
1841 param_depend = self.get_parameter(expr)
1842 self.add_param(New_param, [param_depend])
1843
1844 if not to_change:
1845 return
1846
1847
1848
1849
1850
1851 pat = '|'.join(to_change.keys())
1852 pat = r'(%s)\b' % pat
1853 pat = re.compile(pat)
1854 def replace(match):
1855 return to_change[match.group()]
1856
1857
1858 for dep, list_param in self['parameters'].items():
1859 for param in list_param:
1860 if param.name.startswith('CMASS_') or param.name in mass_widths or\
1861 isinstance(param, ParamCardVariable):
1862 continue
1863 param.type = 'complex'
1864
1865
1866 param.expr = pat.sub(replace, param.expr)
1867
1868
1869 for dep, list_coup in self['couplings'].items():
1870 for coup in list_coup:
1871 coup.expr = pat.sub(replace, coup.expr)
1872
1873 - def add_param(self, new_param, depend_param):
1874 """add the parameter in the list of parameter in a correct position"""
1875
1876 pos = 0
1877 for i,param in enumerate(self.get('parameters')[new_param.depend]):
1878 if param.name in depend_param:
1879 pos = i + 1
1880 self.get('parameters')[new_param.depend].insert(pos, new_param)
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891 -class ModelVariable(object):
1892 """A Class for storing the information about coupling/ parameter"""
1893
1894 - def __init__(self, name, expression, type, depend=()):
1895 """Initialize a new parameter/coupling"""
1896
1897 self.name = name
1898 self.expr = expression
1899 self.type = type
1900 self.depend = depend
1901 self.value = None
1902
1904 """Object with same name are identical, If the object is a string we check
1905 if the attribute name is equal to this string"""
1906
1907 try:
1908 return other.name == self.name
1909 except Exception:
1910 return other == self.name
1911
1913 """ A class for storing the information linked to all the parameter
1914 which should be define in the param_card.dat"""
1915
1916 depend = ('external',)
1917 type = 'real'
1918
1919 - def __init__(self, name, value, lhablock, lhacode):
1920 """Initialize a new ParamCardVariable
1921 name: name of the variable
1922 value: default numerical value
1923 lhablock: name of the block in the param_card.dat
1924 lhacode: code associate to the variable
1925 """
1926 self.name = name
1927 self.value = value
1928 self.lhablock = lhablock
1929 self.lhacode = lhacode
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940 -class Leg(PhysicsObject):
1941 """Leg object: id (Particle), number, I/F state, flag from_group
1942 """
1943
1945 """Default values for all properties"""
1946
1947 self['id'] = 0
1948 self['number'] = 0
1949
1950 self['state'] = True
1951
1952 self['loop_line'] = False
1953
1954 self['from_group'] = True
1955
1956 self['onshell'] = None
1957
1958 - def filter(self, name, value):
1959 """Filter for valid leg property values."""
1960
1961 if name in ['id', 'number']:
1962 if not isinstance(value, int):
1963 raise self.PhysicsObjectError, \
1964 "%s is not a valid integer for leg id" % str(value)
1965
1966 if name == 'state':
1967 if not isinstance(value, bool):
1968 raise self.PhysicsObjectError, \
1969 "%s is not a valid leg state (True|False)" % \
1970 str(value)
1971
1972 if name == 'from_group':
1973 if not isinstance(value, bool) and value != None:
1974 raise self.PhysicsObjectError, \
1975 "%s is not a valid boolean for leg flag from_group" % \
1976 str(value)
1977
1978 if name == 'loop_line':
1979 if not isinstance(value, bool) and value != None:
1980 raise self.PhysicsObjectError, \
1981 "%s is not a valid boolean for leg flag loop_line" % \
1982 str(value)
1983
1984 if name == 'onshell':
1985 if not isinstance(value, bool) and value != None:
1986 raise self.PhysicsObjectError, \
1987 "%s is not a valid boolean for leg flag onshell" % \
1988 str(value)
1989 return True
1990
1992 """Return particle property names as a nicely sorted list."""
1993
1994 return ['id', 'number', 'state', 'from_group', 'loop_line', 'onshell']
1995
1997 """Returns True if the particle corresponding to the leg is a
1998 fermion"""
1999
2000 assert isinstance(model, Model), "%s is not a model" % str(model)
2001
2002 return model.get('particle_dict')[self['id']].is_fermion()
2003
2005 """Returns True if leg is an incoming fermion, i.e., initial
2006 particle or final antiparticle"""
2007
2008 assert isinstance(model, Model), "%s is not a model" % str(model)
2009
2010 part = model.get('particle_dict')[self['id']]
2011 return part.is_fermion() and \
2012 (self.get('state') == False and part.get('is_part') or \
2013 self.get('state') == True and not part.get('is_part'))
2014
2016 """Returns True if leg is an outgoing fermion, i.e., initial
2017 antiparticle or final particle"""
2018
2019 assert isinstance(model, Model), "%s is not a model" % str(model)
2020
2021 part = model.get('particle_dict')[self['id']]
2022 return part.is_fermion() and \
2023 (self.get('state') == True and part.get('is_part') or \
2024 self.get('state') == False and not part.get('is_part'))
2025
2026
2027
2028
2029 - def same(self, leg):
2030 """ Returns true if the leg in argument has the same ID and the same numer """
2031
2032
2033
2034 if isinstance(leg,int):
2035 if self['number']==leg:
2036 return True
2037 else:
2038 return False
2039
2040
2041
2042 elif isinstance(leg, Leg):
2043 if self['id']==leg.get('id') and \
2044 self['number']==leg.get('number') and \
2045 self['loop_line']==leg.get('loop_line') :
2046 return True
2047 else:
2048 return False
2049
2050 else :
2051 return False
2052
2053
2055 return self['number'] < other['number']
2056
2057
2058
2059
2060 -class LegList(PhysicsObjectList):
2061 """List of Leg objects
2062 """
2063
2065 """Test if object obj is a valid Leg for the list."""
2066
2067 return isinstance(obj, Leg)
2068
2069
2070
2072 """Return all elements which have 'from_group' True"""
2073
2074 return filter(lambda leg: leg.get('from_group'), self)
2075
2077 """Return True if at least one element has 'from_group' True"""
2078
2079 return len(self.from_group_elements()) > 0
2080
2082 """Return True if at least two elements have 'from_group' True"""
2083
2084 return len(self.from_group_elements()) > 1
2085
2087 """If has at least one 'from_group' True and in ref_dict_to1,
2088 return the return list from ref_dict_to1, otherwise return False"""
2089 if self.minimum_one_from_group():
2090 return ref_dict_to1.has_key(tuple(sorted([leg.get('id') for leg in self])))
2091 else:
2092 return False
2093
2095 """If has at least two 'from_group' True and in ref_dict_to0,
2096
2097 return the vertex (with id from ref_dict_to0), otherwise return None
2098
2099 If is_decay_chain = True, we only allow clustering of the
2100 initial leg, since we want this to be the last wavefunction to
2101 be evaluated.
2102 """
2103 if is_decay_chain:
2104
2105
2106
2107
2108 return any(leg.get('from_group') == None for leg in self) and \
2109 ref_dict_to0.has_key(tuple(sorted([leg.get('id') \
2110 for leg in self])))
2111
2112 if self.minimum_two_from_group():
2113 return ref_dict_to0.has_key(tuple(sorted([leg.get('id') for leg in self])))
2114 else:
2115 return False
2116
2118 """Returns the list of ids corresponding to the leglist with
2119 all particles outgoing"""
2120
2121 res = []
2122
2123 assert isinstance(model, Model), "Error! model not model"
2124
2125
2126 for leg in self:
2127 if leg.get('state') == False:
2128 res.append(model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
2129 else:
2130 res.append(leg.get('id'))
2131
2132 return res
2133
2134 - def sort(self,*args, **opts):
2135 """Match with FKSLegList"""
2136 Opts=copy.copy(opts)
2137 if 'pert' in Opts.keys():
2138 del Opts['pert']
2139 return super(LegList,self).sort(*args, **Opts)
2140
2141
2142
2143
2144
2145 -class MultiLeg(PhysicsObject):
2146 """MultiLeg object: ids (Particle or particles), I/F state
2147 """
2148
2150 """Default values for all properties"""
2151
2152 self['ids'] = []
2153 self['state'] = True
2154
2155 - def filter(self, name, value):
2156 """Filter for valid multileg property values."""
2157
2158 if name == 'ids':
2159 if not isinstance(value, list):
2160 raise self.PhysicsObjectError, \
2161 "%s is not a valid list" % str(value)
2162 for i in value:
2163 if not isinstance(i, int):
2164 raise self.PhysicsObjectError, \
2165 "%s is not a valid list of integers" % str(value)
2166
2167 if name == 'state':
2168 if not isinstance(value, bool):
2169 raise self.PhysicsObjectError, \
2170 "%s is not a valid leg state (initial|final)" % \
2171 str(value)
2172
2173 return True
2174
2176 """Return particle property names as a nicely sorted list."""
2177
2178 return ['ids', 'state']
2179
2184 """List of MultiLeg objects
2185 """
2186
2188 """Test if object obj is a valid MultiLeg for the list."""
2189
2190 return isinstance(obj, MultiLeg)
2191
2192
2193
2194
2195 -class Vertex(PhysicsObject):
2196 """Vertex: list of legs (ordered), id (Interaction)
2197 """
2198
2199 sorted_keys = ['id', 'legs']
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209 ID_to_veto_for_multichanneling = [0,-1,-2]
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219 max_n_loop_for_multichanneling = 4
2220
2222 """Default values for all properties"""
2223
2224
2225
2226
2227
2228
2229
2230
2231 self['id'] = 0
2232 self['legs'] = LegList()
2233
2234 - def filter(self, name, value):
2235 """Filter for valid vertex property values."""
2236
2237 if name == 'id':
2238 if not isinstance(value, int):
2239 raise self.PhysicsObjectError, \
2240 "%s is not a valid integer for vertex id" % str(value)
2241
2242 if name == 'legs':
2243 if not isinstance(value, LegList):
2244 raise self.PhysicsObjectError, \
2245 "%s is not a valid LegList object" % str(value)
2246
2247 return True
2248
2250 """Return particle property names as a nicely sorted list."""
2251
2252 return self.sorted_keys
2253
2255 """return a nice string"""
2256
2257 mystr = []
2258 for leg in self['legs']:
2259 mystr.append( str(leg['number']) + '(%s)' % str(leg['id']))
2260 mystr = '(%s,id=%s ,obj_id:%s)' % (', '.join(mystr), self['id'], id(self))
2261
2262 return(mystr)
2263
2264
2266 """Returns the id for the last leg as an outgoing
2267 s-channel. Returns 0 if leg is t-channel, or if identity
2268 vertex. Used to check for required and forbidden s-channel
2269 particles."""
2270
2271 leg = self.get('legs')[-1]
2272
2273 if ninitial == 1:
2274
2275
2276 if leg.get('state') == True:
2277 return leg.get('id')
2278 else:
2279 return model.get('particle_dict')[leg.get('id')].\
2280 get_anti_pdg_code()
2281
2282
2283 if self.get('id') == 0 or \
2284 leg.get('state') == False:
2285
2286 return 0
2287
2288 if leg.get('loop_line'):
2289
2290 return 0
2291
2292
2293
2294 if leg.get('number') > ninitial:
2295 return leg.get('id')
2296 else:
2297 return model.get('particle_dict')[leg.get('id')].\
2298 get_anti_pdg_code()
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311 -class VertexList(PhysicsObjectList):
2312 """List of Vertex objects
2313 """
2314
2315 orders = {}
2316
2318 """Test if object obj is a valid Vertex for the list."""
2319
2320 return isinstance(obj, Vertex)
2321
2322 - def __init__(self, init_list=None, orders=None):
2323 """Creates a new list object, with an optional dictionary of
2324 coupling orders."""
2325
2326 list.__init__(self)
2327
2328 if init_list is not None:
2329 for object in init_list:
2330 self.append(object)
2331
2332 if isinstance(orders, dict):
2333 self.orders = orders
2334
2339 """ContractedVertex: When contracting a loop to a given vertex, the created
2340 vertex object is then a ContractedVertex object which has additional
2341 information with respect to a regular vertex object. For example, it contains
2342 the PDG of the particles attached to it. (necessary because the contracted
2343 vertex doesn't have an interaction ID which would allow to retrieve such
2344 information).
2345 """
2346
2348 """Default values for all properties"""
2349
2350 self['PDGs'] = []
2351 self['loop_tag'] = tuple()
2352 self['loop_orders'] = {}
2353 super(ContractedVertex, self).default_setup()
2354
2355 - def filter(self, name, value):
2356 """Filter for valid vertex property values."""
2357
2358 if name == 'PDGs':
2359 if isinstance(value, list):
2360 for elem in value:
2361 if not isinstance(elem,int):
2362 raise self.PhysicsObjectError, \
2363 "%s is not a valid integer for leg PDG" % str(elem)
2364 else:
2365 raise self.PhysicsObjectError, \
2366 "%s is not a valid list for contracted vertex PDGs"%str(value)
2367 if name == 'loop_tag':
2368 if isinstance(value, tuple):
2369 for elem in value:
2370 if not (isinstance(elem,int) or isinstance(elem,tuple)):
2371 raise self.PhysicsObjectError, \
2372 "%s is not a valid int or tuple for loop tag element"%str(elem)
2373 else:
2374 raise self.PhysicsObjectError, \
2375 "%s is not a valid tuple for a contracted vertex loop_tag."%str(value)
2376 if name == 'loop_orders':
2377 Interaction.filter(Interaction(), 'orders', value)
2378 else:
2379 return super(ContractedVertex, self).filter(name, value)
2380
2381 return True
2382
2387
2388
2389
2390
2391 -class Diagram(PhysicsObject):
2392 """Diagram: list of vertices (ordered)
2393 """
2394
2396 """Default values for all properties"""
2397
2398 self['vertices'] = VertexList()
2399 self['orders'] = {}
2400
2401 - def filter(self, name, value):
2413
2415 """Return particle property names as a nicely sorted list."""
2416
2417 return ['vertices', 'orders']
2418
2420 """Returns a nicely formatted string of the diagram content."""
2421
2422 pass_sanity = True
2423 if self['vertices']:
2424 mystr = '('
2425 for vert in self['vertices']:
2426 used_leg = []
2427 mystr = mystr + '('
2428 for leg in vert['legs'][:-1]:
2429 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
2430 used_leg.append(leg['number'])
2431 if __debug__ and len(used_leg) != len(set(used_leg)):
2432 pass_sanity = False
2433 responsible = id(vert)
2434
2435 if self['vertices'].index(vert) < len(self['vertices']) - 1:
2436
2437 mystr = mystr[:-1] + '>'
2438 mystr = mystr + str(vert['legs'][-1]['number']) + '(%s)' % str(vert['legs'][-1]['id']) + ','
2439 mystr = mystr + 'id:' + str(vert['id']) + '),'
2440
2441 mystr = mystr[:-1] + ')'
2442 mystr += " (%s)" % (",".join(["%s=%d" % (key, self['orders'][key]) \
2443 for key in sorted(self['orders'].keys())]))
2444
2445 if not pass_sanity:
2446 raise Exception, "invalid diagram: %s. vert_id: %s" % (mystr, responsible)
2447
2448 return mystr
2449 else:
2450 return '()'
2451
2453 """Calculate the actual coupling orders of this diagram. Note
2454 that the special order WEIGTHED corresponds to the sum of
2455 hierarchys for the couplings."""
2456
2457 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
2458 weight = 0
2459 for vertex in self['vertices']:
2460 if vertex.get('id') in [0,-1]: continue
2461 if vertex.get('id') == -2:
2462 couplings = vertex.get('loop_orders')
2463 else:
2464 couplings = model.get('interaction_dict')[vertex.get('id')].\
2465 get('orders')
2466 for coupling in couplings:
2467 coupling_orders[coupling] += couplings[coupling]
2468 weight += sum([model.get('order_hierarchy')[c]*n for \
2469 (c,n) in couplings.items()])
2470 coupling_orders['WEIGHTED'] = weight
2471 self.set('orders', coupling_orders)
2472
2475 """ Returns wether the contributiong consisting in the current diagram
2476 multiplied by diag_multiplier passes the *positive* squared_orders
2477 specified ( a dictionary ) of types sq_order_types (a dictionary whose
2478 values are the relational operator used to define the constraint of the
2479 order in key)."""
2480
2481 for order, value in squared_orders.items():
2482 if value<0:
2483 continue
2484 combined_order = self.get_order(order) + \
2485 diag_multiplier.get_order(order)
2486 if ( sq_orders_types[order]=='==' and combined_order != value ) or \
2487 ( sq_orders_types[order] in ['=', '<='] and combined_order > value) or \
2488 ( sq_orders_types[order]=='>' and combined_order <= value) :
2489 return False
2490 return True
2491
2493 """Return the order of this diagram. It returns 0 if it is not present."""
2494
2495 try:
2496 return self['orders'][order]
2497 except Exception:
2498 return 0
2499
2501 """ Returns a Diagram which correspond to the loop diagram with the
2502 loop shrunk to a point. Of course for a instance of base_objects.Diagram
2503 one must simply return self."""
2504
2505 return self
2506
2508 """ Return the list of external legs of this diagram """
2509
2510 external_legs = LegList([])
2511 for leg in sum([vert.get('legs') for vert in self.get('vertices')],[]):
2512 if not leg.get('number') in [l.get('number') for l in external_legs]:
2513 external_legs.append(leg)
2514
2515 return external_legs
2516
2518 """Renumber legs in all vertices according to perm_map"""
2519
2520 vertices = VertexList()
2521 min_dict = copy.copy(perm_map)
2522
2523 state_dict = dict([(l.get('number'), l.get('state')) for l in leg_list])
2524
2525 for vertex in self.get('vertices')[:-1]:
2526 vertex = copy.copy(vertex)
2527 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2528 for leg in leg_list[:-1]:
2529 leg.set('number', min_dict[leg.get('number')])
2530 leg.set('state', state_dict[leg.get('number')])
2531 min_number = min([leg.get('number') for leg in leg_list[:-1]])
2532 leg = leg_list[-1]
2533 min_dict[leg.get('number')] = min_number
2534
2535
2536 state_dict[min_number] = len([l for l in leg_list[:-1] if \
2537 not l.get('state')]) != 1
2538 leg.set('number', min_number)
2539 leg.set('state', state_dict[min_number])
2540 vertex.set('legs', leg_list)
2541 vertices.append(vertex)
2542
2543 vertex = copy.copy(self.get('vertices')[-1])
2544 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2545 for leg in leg_list:
2546 leg.set('number', min_dict[leg.get('number')])
2547 leg.set('state', state_dict[leg.get('number')])
2548 vertex.set('legs', leg_list)
2549 vertices.append(vertex)
2550
2551 new_diag = copy.copy(self)
2552 new_diag.set('vertices', vertices)
2553 state_dict = {True:'T',False:'F'}
2554 return new_diag
2555
2559 """Return a list of the number of legs in the vertices for
2560 this diagram.
2561 This function is only used for establishing the multi-channeling, so that
2562 we exclude from it all the fake vertices and the vertices resulting from
2563 shrunk loops (id=-2)"""
2564
2565
2566 if max_n_loop == 0:
2567 max_n_loop = Vertex.max_n_loop_for_multichanneling
2568
2569 res = [len(v.get('legs')) for v in self.get('vertices') if (v.get('id') \
2570 not in veto_inter_id) or (v.get('id')==-2 and
2571 len(v.get('legs'))>max_n_loop)]
2572
2573 return res
2574
2576 """Return the maximum number of configs from this diagram,
2577 given by 2^(number of non-zero width s-channel propagators)"""
2578
2579 s_channels = [v.get_s_channel_id(model,ninitial) for v in \
2580 self.get('vertices')[:-1]]
2581 num_props = len([i for i in s_channels if i != 0 and \
2582 model.get_particle(i).get('width').lower() != 'zero'])
2583
2584 if num_props < 1:
2585 return 1
2586 else:
2587 return 2**num_props
2588
2590 """return the difference of total diff of charge occuring on the
2591 lofw of the initial parton. return [None,None] if the two initial parton
2592 are connected and the (partial) value if None if the initial parton is
2593 not a fermiom"""
2594
2595 import madgraph.core.drawing as drawing
2596 drawdiag = drawing.FeynmanDiagram(self, model)
2597 drawdiag.load_diagram()
2598 out = []
2599
2600 for v in drawdiag.initial_vertex:
2601 init_part = v.lines[0]
2602 if not init_part.is_fermion():
2603 out.append(None)
2604 continue
2605
2606 init_charge = model.get_particle(init_part.id).get('charge')
2607
2608 l_last = init_part
2609 v_last = v
2610 vcurrent = l_last.end
2611 if vcurrent == v:
2612 vcurrent = l_last.begin
2613 security =0
2614 while not vcurrent.is_external():
2615 if security > 1000:
2616 raise Exception, 'wrong diagram'
2617 next_l = [l for l in vcurrent.lines if l is not l_last and l.is_fermion()][0]
2618 next_v = next_l.end
2619 if next_v == vcurrent:
2620 next_v = next_l.begin
2621 l_last, vcurrent = next_l, next_v
2622 if vcurrent in drawdiag.initial_vertex:
2623 return [None, None]
2624
2625 out.append(model.get_particle(l_last.id).get('charge') - init_charge)
2626 return out
2627
2628
2629
2630
2631
2632 -class DiagramList(PhysicsObjectList):
2633 """List of Diagram objects
2634 """
2635
2637 """Test if object obj is a valid Diagram for the list."""
2638
2639 return isinstance(obj, Diagram)
2640
2642 """Returns a nicely formatted string"""
2643 mystr = " " * indent + str(len(self)) + ' diagrams:\n'
2644 for i, diag in enumerate(self):
2645 mystr = mystr + " " * indent + str(i+1) + " " + \
2646 diag.nice_string() + '\n'
2647 return mystr[:-1]
2648
2649
2650
2652 """ Return the order of the diagram in the list with the maximum coupling
2653 order for the coupling specified """
2654 max_order=-1
2655
2656 for diag in self:
2657 if order in diag['orders'].keys():
2658 if max_order==-1 or diag['orders'][order] > max_order:
2659 max_order = diag['orders'][order]
2660
2661 return max_order
2662
2664 """ This function returns a fitlered version of the diagram list self
2665 which satisfy the negative squared_order constraint 'order' with negative
2666 value 'value' and of type 'order_type', assuming that the diagram_list
2667 it must be squared against is 'reg_diag_list'. It also returns the
2668 new postive target squared order which correspond to this negative order
2669 constraint. Example: u u~ > d d~ QED^2<=-2 means that one wants to
2670 pick terms only up to the the next-to-leading order contributiong in QED,
2671 which is QED=2 in this case, so that target_order=4 is returned."""
2672
2673
2674 target_order = min(ref_diag_list.get_order_values(order))+\
2675 min(self.get_order_values(order))+2*(-value-1)
2676
2677 new_list = self.apply_positive_sq_orders(ref_diag_list,
2678 {order:target_order}, {order:order_type})
2679
2680 return new_list, target_order
2681
2683 """ This function returns a filtered version of self which contain
2684 only the diagram which satisfy the positive squared order constraints
2685 sq_orders of type sq_order_types and assuming that the diagrams are
2686 multiplied with those of the reference diagram list ref_diag_list."""
2687
2688 new_diag_list = DiagramList()
2689 for tested_diag in self:
2690 for ref_diag in ref_diag_list:
2691 if tested_diag.pass_squared_order_constraints(ref_diag,
2692 sq_orders,sq_order_types):
2693 new_diag_list.append(tested_diag)
2694 break
2695 return new_diag_list
2696
2698 """ This function modifies the current object and remove the diagram
2699 which do not obey the condition """
2700
2701 new = []
2702 for tested_diag in self:
2703 if operator == '==':
2704 if tested_diag['orders'][order] == value:
2705 new.append(tested_diag)
2706 elif operator == '>':
2707 if tested_diag['orders'][order] > value:
2708 new.append(tested_diag)
2709 self[:] = new
2710 return self
2711
2712
2714 """ Return the order of the diagram in the list with the mimimum coupling
2715 order for the coupling specified """
2716 min_order=-1
2717 for diag in self:
2718 if order in diag['orders'].keys():
2719 if min_order==-1 or diag['orders'][order] < min_order:
2720 min_order = diag['orders'][order]
2721 else:
2722 return 0
2723
2724 return min_order
2725
2727 """ Return the list of possible values appearing in the diagrams of this
2728 list for the order given in argument """
2729
2730 values=set([])
2731 for diag in self:
2732 if order in diag['orders'].keys():
2733 values.add(diag['orders'][order])
2734 else:
2735 values.add(0)
2736
2737 return list(values)
2738
2739
2740
2741
2742 -class Process(PhysicsObject):
2743 """Process: list of legs (ordered)
2744 dictionary of orders
2745 model
2746 process id
2747 """
2748
2750 """Default values for all properties"""
2751
2752 self['legs'] = LegList()
2753
2754 self['orders'] = {}
2755 self['model'] = Model()
2756
2757 self['id'] = 0
2758 self['uid'] = 0
2759
2760
2761
2762
2763 self['required_s_channels'] = []
2764 self['forbidden_onsh_s_channels'] = []
2765 self['forbidden_s_channels'] = []
2766 self['forbidden_particles'] = []
2767 self['is_decay_chain'] = False
2768 self['overall_orders'] = {}
2769
2770 self['decay_chains'] = ProcessList()
2771
2772 self['legs_with_decays'] = LegList()
2773
2774 self['perturbation_couplings']=[]
2775
2776
2777
2778
2779 self['squared_orders'] = {}
2780
2781
2782
2783
2784 self['sqorders_types'] = {}
2785
2786 self['constrained_orders'] = {}
2787 self['has_born'] = True
2788
2789
2790 self['NLO_mode'] = 'tree'
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800 self['split_orders'] = []
2801
2802 - def filter(self, name, value):
2803 """Filter for valid process property values."""
2804
2805 if name in ['legs', 'legs_with_decays'] :
2806 if not isinstance(value, LegList):
2807 raise self.PhysicsObjectError, \
2808 "%s is not a valid LegList object" % str(value)
2809
2810 if name in ['orders', 'overall_orders','squared_orders']:
2811 Interaction.filter(Interaction(), 'orders', value)
2812
2813 if name == 'constrained_orders':
2814 if not isinstance(value, dict):
2815 raise self.PhysicsObjectError, \
2816 "%s is not a valid dictionary" % str(value)
2817
2818 if name == 'sqorders_types':
2819 if not isinstance(value, dict):
2820 raise self.PhysicsObjectError, \
2821 "%s is not a valid dictionary" % str(value)
2822 for order in value.keys()+value.values():
2823 if not isinstance(order, str):
2824 raise self.PhysicsObjectError, \
2825 "%s is not a valid string" % str(value)
2826
2827 if name == 'split_orders':
2828 if not isinstance(value, list):
2829 raise self.PhysicsObjectError, \
2830 "%s is not a valid list" % str(value)
2831 for order in value:
2832 if not isinstance(order, str):
2833 raise self.PhysicsObjectError, \
2834 "%s is not a valid string" % str(value)
2835
2836 if name == 'model':
2837 if not isinstance(value, Model):
2838 raise self.PhysicsObjectError, \
2839 "%s is not a valid Model object" % str(value)
2840 if name in ['id', 'uid']:
2841 if not isinstance(value, int):
2842 raise self.PhysicsObjectError, \
2843 "Process %s %s is not an integer" % (name, repr(value))
2844
2845 if name == 'required_s_channels':
2846 if not isinstance(value, list):
2847 raise self.PhysicsObjectError, \
2848 "%s is not a valid list" % str(value)
2849 for l in value:
2850 if not isinstance(l, list):
2851 raise self.PhysicsObjectError, \
2852 "%s is not a valid list of lists" % str(value)
2853 for i in l:
2854 if not isinstance(i, int):
2855 raise self.PhysicsObjectError, \
2856 "%s is not a valid list of integers" % str(l)
2857 if i == 0:
2858 raise self.PhysicsObjectError, \
2859 "Not valid PDG code %d for s-channel particle" % i
2860
2861 if name in ['forbidden_onsh_s_channels', 'forbidden_s_channels']:
2862 if not isinstance(value, list):
2863 raise self.PhysicsObjectError, \
2864 "%s is not a valid list" % str(value)
2865 for i in value:
2866 if not isinstance(i, int):
2867 raise self.PhysicsObjectError, \
2868 "%s is not a valid list of integers" % str(value)
2869 if i == 0:
2870 raise self.PhysicsObjectError, \
2871 "Not valid PDG code %d for s-channel particle" % str(value)
2872
2873 if name == 'forbidden_particles':
2874 if not isinstance(value, list):
2875 raise self.PhysicsObjectError, \
2876 "%s is not a valid list" % str(value)
2877 for i in value:
2878 if not isinstance(i, int):
2879 raise self.PhysicsObjectError, \
2880 "%s is not a valid list of integers" % str(value)
2881 if i <= 0:
2882 raise self.PhysicsObjectError, \
2883 "Forbidden particles should have a positive PDG code" % str(value)
2884
2885 if name == 'perturbation_couplings':
2886 if not isinstance(value, list):
2887 raise self.PhysicsObjectError, \
2888 "%s is not a valid list" % str(value)
2889 for order in value:
2890 if not isinstance(order, str):
2891 raise self.PhysicsObjectError, \
2892 "%s is not a valid string" % str(value)
2893
2894 if name == 'is_decay_chain':
2895 if not isinstance(value, bool):
2896 raise self.PhysicsObjectError, \
2897 "%s is not a valid bool" % str(value)
2898
2899 if name == 'has_born':
2900 if not isinstance(value, bool):
2901 raise self.PhysicsObjectError, \
2902 "%s is not a valid bool" % str(value)
2903
2904 if name == 'decay_chains':
2905 if not isinstance(value, ProcessList):
2906 raise self.PhysicsObjectError, \
2907 "%s is not a valid ProcessList" % str(value)
2908
2909 if name == 'NLO_mode':
2910 import madgraph.interface.madgraph_interface as mg
2911 if value not in mg.MadGraphCmd._valid_nlo_modes:
2912 raise self.PhysicsObjectError, \
2913 "%s is not a valid NLO_mode" % str(value)
2914 return True
2915
2917 """ A process, not being a ProcessDefinition never carries multiple
2918 particles labels"""
2919
2920 return False
2921
2922 - def set(self, name, value):
2923 """Special set for forbidden particles - set to abs value."""
2924
2925 if name == 'forbidden_particles':
2926 try:
2927 value = [abs(i) for i in value]
2928 except Exception:
2929 pass
2930
2931 if name == 'required_s_channels':
2932
2933 if value and isinstance(value, list) and \
2934 not isinstance(value[0], list):
2935 value = [value]
2936
2937 return super(Process, self).set(name, value)
2938
2940 """ Return what kind of squared order constraint was specified for the
2941 order 'order'."""
2942
2943 if order in self['sqorders_types'].keys():
2944 return self['sqorders_types'][order]
2945 else:
2946
2947 return '='
2948
2949 - def get(self, name):
2950 """Special get for legs_with_decays"""
2951
2952 if name == 'legs_with_decays':
2953 self.get_legs_with_decays()
2954
2955 if name == 'sqorders_types':
2956
2957 for order in self['squared_orders'].keys():
2958 if order not in self['sqorders_types']:
2959
2960 self['sqorders_types'][order]='='
2961
2962 return super(Process, self).get(name)
2963
2964
2965
2967 """Return process property names as a nicely sorted list."""
2968
2969 return ['legs', 'orders', 'overall_orders', 'squared_orders',
2970 'constrained_orders',
2971 'model', 'id', 'required_s_channels',
2972 'forbidden_onsh_s_channels', 'forbidden_s_channels',
2973 'forbidden_particles', 'is_decay_chain', 'decay_chains',
2974 'legs_with_decays', 'perturbation_couplings', 'has_born',
2975 'NLO_mode','split_orders']
2976
2977 - def nice_string(self, indent=0, print_weighted = True, prefix=True):
2978 """Returns a nicely formated string about current process
2979 content. Since the WEIGHTED order is automatically set and added to
2980 the user-defined list of orders, it can be ommitted for some info
2981 displays."""
2982
2983 if isinstance(prefix, bool) and prefix:
2984 mystr = " " * indent + "Process: "
2985 elif isinstance(prefix, str):
2986 mystr = prefix
2987 else:
2988 mystr = ""
2989 prevleg = None
2990 for leg in self['legs']:
2991 mypart = self['model'].get('particle_dict')[leg['id']]
2992 if prevleg and prevleg['state'] == False \
2993 and leg['state'] == True:
2994
2995 mystr = mystr + '> '
2996
2997 if self['required_s_channels'] and \
2998 self['required_s_channels'][0]:
2999 mystr += "|".join([" ".join([self['model'].\
3000 get('particle_dict')[req_id].get_name() \
3001 for req_id in id_list]) \
3002 for id_list in self['required_s_channels']])
3003 mystr = mystr + ' > '
3004
3005 mystr = mystr + mypart.get_name() + ' '
3006
3007 prevleg = leg
3008
3009
3010 if self['orders']:
3011 to_add = []
3012 for key in sorted(self['orders'].keys()):
3013 if not print_weighted and key == 'WEIGHTED':
3014 continue
3015 value = int(self['orders'][key])
3016 if key in self['squared_orders']:
3017 if self.get_squared_order_type(key) in ['<=', '==', '='] and \
3018 self['squared_orders'][key] == value:
3019 continue
3020 if self.get_squared_order_type(key) in ['>'] and value == 99:
3021 continue
3022 if key in self['constrained_orders']:
3023 if value == self['constrained_orders'][key][0] and\
3024 self['constrained_orders'][key][1] in ['=', '<=', '==']:
3025 continue
3026 if value == 0:
3027 to_add.append('%s=0' % key)
3028 else:
3029 to_add.append('%s<=%s' % (key,value))
3030
3031 if to_add:
3032 mystr = mystr + " ".join(to_add) + ' '
3033
3034 if self['constrained_orders']:
3035 mystr = mystr + " ".join('%s%s%d' % (key,
3036 self['constrained_orders'][key][1], self['constrained_orders'][key][0])
3037 for key in sorted(self['constrained_orders'].keys())) + ' '
3038
3039
3040 if self['perturbation_couplings']:
3041 mystr = mystr + '[ '
3042 if self['NLO_mode']!='tree':
3043 if self['NLO_mode']=='virt' and not self['has_born']:
3044 mystr = mystr + 'sqrvirt = '
3045 else:
3046 mystr = mystr + self['NLO_mode'] + ' = '
3047 for order in self['perturbation_couplings']:
3048 mystr = mystr + order + ' '
3049 mystr = mystr + '] '
3050
3051
3052 if self['squared_orders']:
3053 to_add = []
3054 for key in sorted(self['squared_orders'].keys()):
3055 if not print_weighted and key == 'WEIGHTED':
3056 continue
3057 if key in self['constrained_orders']:
3058 if self['constrained_orders'][key][0] == self['squared_orders'][key]/2 and \
3059 self['constrained_orders'][key][1] == self.get_squared_order_type(key):
3060 continue
3061 to_add.append(key + '^2%s%d'%\
3062 (self.get_squared_order_type(key),self['squared_orders'][key]))
3063
3064 if to_add:
3065 mystr = mystr + " ".join(to_add) + ' '
3066
3067
3068
3069 if self['forbidden_onsh_s_channels']:
3070 mystr = mystr + '$ '
3071 for forb_id in self['forbidden_onsh_s_channels']:
3072 forbpart = self['model'].get('particle_dict')[forb_id]
3073 mystr = mystr + forbpart.get_name() + ' '
3074
3075
3076 if self['forbidden_s_channels']:
3077 mystr = mystr + '$$ '
3078 for forb_id in self['forbidden_s_channels']:
3079 forbpart = self['model'].get('particle_dict')[forb_id]
3080 mystr = mystr + forbpart.get_name() + ' '
3081
3082
3083 if self['forbidden_particles']:
3084 mystr = mystr + '/ '
3085 for forb_id in self['forbidden_particles']:
3086 forbpart = self['model'].get('particle_dict')[forb_id]
3087 mystr = mystr + forbpart.get_name() + ' '
3088
3089
3090 mystr = mystr[:-1]
3091
3092 if self.get('id') or self.get('overall_orders'):
3093 mystr += " @%d" % self.get('id')
3094 if self.get('overall_orders'):
3095 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3096 for key in sorted(self['orders'])]) + ' '
3097
3098 if not self.get('decay_chains'):
3099 return mystr
3100
3101 for decay in self['decay_chains']:
3102 mystr = mystr + '\n' + \
3103 decay.nice_string(indent + 2).replace('Process', 'Decay')
3104
3105 return mystr
3106
3198
3200 """Returns a string containing only the basic process (w/o decays)."""
3201
3202 mystr = ""
3203 prevleg = None
3204 for leg in self.get_legs_with_decays():
3205 mypart = self['model'].get('particle_dict')[leg['id']]
3206 if prevleg and prevleg['state'] == False \
3207 and leg['state'] == True:
3208
3209 mystr = mystr + '> '
3210 mystr = mystr + mypart.get_name() + ' '
3211 prevleg = leg
3212
3213
3214 return mystr[:-1]
3215
3216 - def shell_string(self, schannel=True, forbid=True, main=True, pdg_order=False,
3217 print_id = True):
3218 """Returns process as string with '~' -> 'x', '>' -> '_',
3219 '+' -> 'p' and '-' -> 'm', including process number,
3220 intermediate s-channels and forbidden particles,
3221 pdg_order allow to order to leg order by pid."""
3222
3223 mystr = ""
3224 if not self.get('is_decay_chain') and print_id:
3225 mystr += "%d_" % self['id']
3226
3227 prevleg = None
3228 if pdg_order:
3229 legs = [l for l in self['legs'][1:]]
3230 def order_leg(l1,l2):
3231 id1 = l1.get('id')
3232 id2 = l2.get('id')
3233 return id2-id1
3234 legs.sort(cmp=order_leg)
3235 legs.insert(0, self['legs'][0])
3236 else:
3237 legs = self['legs']
3238
3239
3240 for leg in legs:
3241 mypart = self['model'].get('particle_dict')[leg['id']]
3242 if prevleg and prevleg['state'] == False \
3243 and leg['state'] == True:
3244
3245 mystr = mystr + '_'
3246
3247 if self['required_s_channels'] and \
3248 self['required_s_channels'][0] and schannel:
3249 mystr += "_or_".join(["".join([self['model'].\
3250 get('particle_dict')[req_id].get_name() \
3251 for req_id in id_list]) \
3252 for id_list in self['required_s_channels']])
3253 mystr = mystr + '_'
3254 if mypart['is_part']:
3255 mystr = mystr + mypart['name']
3256 else:
3257 mystr = mystr + mypart['antiname']
3258 prevleg = leg
3259
3260
3261 if self['forbidden_particles'] and forbid:
3262 mystr = mystr + '_no_'
3263 for forb_id in self['forbidden_particles']:
3264 forbpart = self['model'].get('particle_dict')[forb_id]
3265 mystr = mystr + forbpart.get_name()
3266
3267
3268 mystr = mystr.replace('~', 'x')
3269
3270 mystr = mystr.replace('+', 'p')
3271
3272 mystr = mystr.replace('-', 'm')
3273
3274 mystr = mystr.replace(' ', '')
3275
3276 for decay in self.get('decay_chains'):
3277 mystr = mystr + "_" + decay.shell_string(schannel,forbid, main=False,
3278 pdg_order=pdg_order)
3279
3280
3281 if len(mystr) > 64 and main:
3282 if schannel and forbid:
3283 out = self.shell_string(True, False, True, pdg_order)
3284 elif schannel:
3285 out = self.shell_string(False, False, True, pdg_order)
3286 else:
3287 out = mystr[:64]
3288 if not out.endswith('_%s' % self['uid']):
3289 out += '_%s' % self['uid']
3290 return out
3291
3292 return mystr
3293
3295 """Returns process as v4-compliant string with '~' -> 'x' and
3296 '>' -> '_'"""
3297
3298 mystr = "%d_" % self['id']
3299 prevleg = None
3300 for leg in self.get_legs_with_decays():
3301 mypart = self['model'].get('particle_dict')[leg['id']]
3302 if prevleg and prevleg['state'] == False \
3303 and leg['state'] == True:
3304
3305 mystr = mystr + '_'
3306 if mypart['is_part']:
3307 mystr = mystr + mypart['name']
3308 else:
3309 mystr = mystr + mypart['antiname']
3310 prevleg = leg
3311
3312
3313 mystr = mystr.replace('~', 'x')
3314
3315 mystr = mystr.replace(' ', '')
3316
3317 return mystr
3318
3319
3320
3322 """ Check iteratively that no coupling order constraint include negative
3323 values."""
3324
3325 if any(val<0 for val in self.get('orders').values()+\
3326 self.get('squared_orders').values()):
3327 return True
3328
3329 for procdef in self['decay_chains']:
3330 if procdef.are_negative_orders_present():
3331 return True
3332
3333 return False
3334
3336 """ Check iteratively that the decayed processes are not perturbed """
3337
3338 for procdef in self['decay_chains']:
3339 if procdef['perturbation_couplings'] or procdef.are_decays_perturbed():
3340 return True
3341 return False
3342
3344 """ Check iteratively that the decayed processes are not perturbed """
3345
3346 for procdef in self['decay_chains']:
3347 if procdef['squared_orders']!={} or procdef.decays_have_squared_orders():
3348 return True
3349 return False
3350
3352 """Gives number of initial state particles"""
3353
3354 return len(filter(lambda leg: leg.get('state') == False,
3355 self.get('legs')))
3356
3358 """Gives the pdg codes for initial state particles"""
3359
3360 return [leg.get('id') for leg in \
3361 filter(lambda leg: leg.get('state') == False,
3362 self.get('legs'))]
3363
3365 """Return the pdg codes for initial state particles for beam number"""
3366
3367 legs = filter(lambda leg: leg.get('state') == False and\
3368 leg.get('number') == number,
3369 self.get('legs'))
3370 if not legs:
3371 return None
3372 else:
3373 return legs[0].get('id')
3374
3376 """return a tuple of two tuple containing the id of the initial/final
3377 state particles. Each list is ordered"""
3378
3379 initial = []
3380 final = [l.get('id') for l in self.get('legs')\
3381 if l.get('state') or initial.append(l.get('id'))]
3382 initial.sort()
3383 final.sort()
3384 return (tuple(initial), tuple(final))
3385
3406
3407
3409 """Gives the final state legs"""
3410
3411 return filter(lambda leg: leg.get('state') == True,
3412 self.get('legs'))
3413
3415 """Gives the pdg codes for final state particles"""
3416
3417 return [l.get('id') for l in self.get_final_legs()]
3418
3419
3421 """Return process with all decay chains substituted in."""
3422
3423 if self['legs_with_decays']:
3424 return self['legs_with_decays']
3425
3426 legs = copy.deepcopy(self.get('legs'))
3427 org_decay_chains = copy.copy(self.get('decay_chains'))
3428 sorted_decay_chains = []
3429
3430 for leg in legs:
3431 if not leg.get('state'): continue
3432 org_ids = [l.get('legs')[0].get('id') for l in \
3433 org_decay_chains]
3434 if leg.get('id') in org_ids:
3435 sorted_decay_chains.append(org_decay_chains.pop(\
3436 org_ids.index(leg.get('id'))))
3437 assert not org_decay_chains
3438 ileg = 0
3439 for decay in sorted_decay_chains:
3440 while legs[ileg].get('state') == False or \
3441 legs[ileg].get('id') != decay.get('legs')[0].get('id'):
3442 ileg = ileg + 1
3443 decay_legs = decay.get_legs_with_decays()
3444 legs = legs[:ileg] + decay_legs[1:] + legs[ileg+1:]
3445 ileg = ileg + len(decay_legs) - 1
3446
3447
3448 legs = [copy.copy(l) for l in legs]
3449
3450 for ileg, leg in enumerate(legs):
3451 leg.set('number', ileg + 1)
3452
3453 self['legs_with_decays'] = LegList(legs)
3454
3455 return self['legs_with_decays']
3456
3458 """return the tag for standalone call"""
3459
3460 initial = []
3461 final = [l.get('id') for l in self.get('legs')\
3462 if l.get('state') or initial.append(l.get('id'))]
3463 decay_finals = self.get_final_ids_after_decay()
3464 decay_finals.sort()
3465 tag = (tuple(initial), tuple(decay_finals))
3466 return tag
3467
3468
3470 """Output a list that can be compared to other processes as:
3471 [id, sorted(initial leg ids), sorted(final leg ids),
3472 sorted(decay list_for_sorts)]"""
3473
3474 sorted_list = [self.get('id'),
3475 sorted(self.get_initial_ids()),
3476 sorted(self.get_final_ids())]
3477
3478 if self.get('decay_chains'):
3479 sorted_list.extend(sorted([d.list_for_sort() for d in \
3480 self.get('decay_chains')]))
3481
3482 return sorted_list
3483
3485 """Sorting routine which allows to sort processes for
3486 comparison. Compare only process id and legs."""
3487
3488 if self.list_for_sort() > other.list_for_sort():
3489 return 1
3490 if self.list_for_sort() < other.list_for_sort():
3491 return -1
3492 return 0
3493
3495 """Calculate the denominator factor for identical final state particles
3496 """
3497
3498 final_legs = filter(lambda leg: leg.get('state') == True, \
3499 self.get_legs_with_decays())
3500
3501 identical_indices = {}
3502 for leg in final_legs:
3503 if leg.get('id') in identical_indices:
3504 identical_indices[leg.get('id')] = \
3505 identical_indices[leg.get('id')] + 1
3506 else:
3507 identical_indices[leg.get('id')] = 1
3508 return reduce(lambda x, y: x * y, [ math.factorial(val) for val in \
3509 identical_indices.values() ], 1)
3510
3512 """Ensure that maximum expansion orders from the model are
3513 properly taken into account in the process"""
3514
3515
3516 expansion_orders = self.get('model').get('expansion_order')
3517 orders = self.get('orders')
3518 sq_orders = self.get('squared_orders')
3519
3520 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
3521 for (k,v) in tmp:
3522 if k in orders:
3523 if v < orders[k]:
3524 if k in sq_orders.keys() and \
3525 (sq_orders[k]>v or sq_orders[k]<0):
3526 logger.warning(
3527 '''The process with the squared coupling order (%s^2%s%s) specified can potentially
3528 recieve contributions with powers of the coupling %s larger than the maximal
3529 value allowed by the model builder (%s). Hence, MG5_aMC sets the amplitude order
3530 for that coupling to be this maximal one. '''%(k,self.get('sqorders_types')[k],
3531 self.get('squared_orders')[k],k,v))
3532 else:
3533 logger.warning(
3534 '''The coupling order (%s=%s) specified is larger than the one allowed
3535 by the model builder. The maximal value allowed is %s.
3536 We set the %s order to this value''' % (k,orders[k],v,k))
3537 orders[k] = v
3538 else:
3539 orders[k] = v
3540
3542 """Overloading the equality operator, so that only comparison
3543 of process id and legs is being done, using compare_for_sort."""
3544
3545 if not isinstance(other, Process):
3546 return False
3547
3548 return self.compare_for_sort(other) == 0
3549
3551 return not self.__eq__(other)
3552
3557 """List of Process objects
3558 """
3559
3561 """Test if object obj is a valid Process for the list."""
3562
3563 return isinstance(obj, Process)
3564
3566 """Returns a nicely formatted string of the matrix element processes."""
3567
3568 mystr = "\n".join([p.nice_string(indent) for p in self])
3569
3570 return mystr
3571
3576 """ProcessDefinition: list of multilegs (ordered)
3577 dictionary of orders
3578 model
3579 process id
3580 """
3581
3591
3592 - def filter(self, name, value):
3608
3610 """ Check that this process definition will yield a single process, as
3611 each multileg only has one leg"""
3612
3613 for process in self['decay_chains']:
3614 if process.has_multiparticle_label():
3615 return True
3616
3617 for mleg in self['legs']:
3618 if len(mleg['ids'])>1:
3619 return True
3620
3621 return False
3622
3630
3632 """Retrieve the minimum starting guess for WEIGHTED order, to
3633 use in find_optimal_process_orders in MultiProcess diagram
3634 generation (as well as particles and hierarchy). The algorithm:
3635
3636 1) Pick out the legs in the multiprocess according to the
3637 highest hierarchy represented (so don't mix particles from
3638 different hierarchy classes in the same multiparticles!)
3639
3640 2) Find the starting maximum WEIGHTED order as the sum of the
3641 highest n-2 weighted orders
3642
3643 3) Pick out required s-channel particle hierarchies, and use
3644 the highest of the maximum WEIGHTED order from the legs and
3645 the minimum WEIGHTED order extracted from 2*s-channel
3646 hierarchys plus the n-2-2*(number of s-channels) lowest
3647 leg weighted orders.
3648 """
3649
3650 model = self.get('model')
3651
3652
3653
3654 particles, hierarchy = model.get_particles_hierarchy()
3655
3656
3657 max_order_now = []
3658 new_legs = copy.copy(self.get('legs'))
3659 import madgraph.core.base_objects as base_objects
3660 for parts, value in zip(particles, hierarchy):
3661 ileg = 0
3662 while ileg < len(new_legs):
3663 if any([id in parts for id in new_legs[ileg].get('ids')]):
3664 max_order_now.append(value)
3665 new_legs.pop(ileg)
3666 else:
3667 ileg += 1
3668
3669
3670
3671 max_order_now = sorted(max_order_now)[2:]
3672
3673
3674 max_order_prop = []
3675 for idlist in self.get('required_s_channels'):
3676 max_order_prop.append([0,0])
3677 for id in idlist:
3678 for parts, value in zip(particles, hierarchy):
3679 if id in parts:
3680 max_order_prop[-1][0] += 2*value
3681 max_order_prop[-1][1] += 1
3682 break
3683
3684 if max_order_prop:
3685 if len(max_order_prop) >1:
3686 max_order_prop = min(*max_order_prop, key=lambda x:x[0])
3687 else:
3688 max_order_prop = max_order_prop[0]
3689
3690
3691
3692
3693 max_order_now = max(sum(max_order_now),
3694 max_order_prop[0] + \
3695 sum(max_order_now[:-2 * max_order_prop[1]]))
3696 else:
3697 max_order_now = sum(max_order_now)
3698
3699 return max_order_now, particles, hierarchy
3700
3702 """basic way to loop over all the process definition.
3703 not used by MG which used some smarter version (use by ML)"""
3704
3705 isids = [leg['ids'] for leg in self['legs'] \
3706 if leg['state'] == False]
3707 fsids = [leg['ids'] for leg in self['legs'] \
3708 if leg['state'] == True]
3709
3710 red_isidlist = []
3711
3712 for prod in itertools.product(*isids):
3713 islegs = [Leg({'id':id, 'state': False}) for id in prod]
3714 if tuple(sorted(prod)) in red_isidlist:
3715 continue
3716 red_isidlist.append(tuple(sorted(prod)))
3717 red_fsidlist = []
3718 for prod in itertools.product(*fsids):
3719
3720 if tuple(sorted(prod)) in red_fsidlist:
3721 continue
3722 red_fsidlist.append(tuple(sorted(prod)))
3723 leg_list = [copy.copy(leg) for leg in islegs]
3724 leg_list.extend([Leg({'id':id, 'state': True}) for id in prod])
3725 legs = LegList(leg_list)
3726 process = self.get_process_with_legs(legs)
3727 yield process
3728
3729 - def nice_string(self, indent=0, print_weighted=False, prefix=True):
3730 """Returns a nicely formated string about current process
3731 content"""
3732
3733 if prefix:
3734 mystr = " " * indent + "Process: "
3735 else:
3736 mystr=""
3737 prevleg = None
3738 for leg in self['legs']:
3739 myparts = \
3740 "/".join([self['model'].get('particle_dict')[id].get_name() \
3741 for id in leg.get('ids')])
3742 if prevleg and prevleg['state'] == False \
3743 and leg['state'] == True:
3744
3745 mystr = mystr + '> '
3746
3747 if self['required_s_channels'] and \
3748 self['required_s_channels'][0]:
3749 mystr += "|".join([" ".join([self['model'].\
3750 get('particle_dict')[req_id].get_name() \
3751 for req_id in id_list]) \
3752 for id_list in self['required_s_channels']])
3753 mystr = mystr + '> '
3754
3755 mystr = mystr + myparts + ' '
3756
3757 prevleg = leg
3758
3759
3760 if self['forbidden_onsh_s_channels']:
3761 mystr = mystr + '$ '
3762 for forb_id in self['forbidden_onsh_s_channels']:
3763 forbpart = self['model'].get('particle_dict')[forb_id]
3764 mystr = mystr + forbpart.get_name() + ' '
3765
3766
3767 if self['forbidden_s_channels']:
3768 mystr = mystr + '$$ '
3769 for forb_id in self['forbidden_s_channels']:
3770 forbpart = self['model'].get('particle_dict')[forb_id]
3771 mystr = mystr + forbpart.get_name() + ' '
3772
3773
3774 if self['forbidden_particles']:
3775 mystr = mystr + '/ '
3776 for forb_id in self['forbidden_particles']:
3777 forbpart = self['model'].get('particle_dict')[forb_id]
3778 mystr = mystr + forbpart.get_name() + ' '
3779
3780 if self['orders']:
3781 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
3782 for key in sorted(self['orders'])]) + ' '
3783
3784 if self['constrained_orders']:
3785 mystr = mystr + " ".join('%s%s%d' % (key, operator, value) for
3786 (key,(value, operator))
3787 in self['constrained_orders'].items()) + ' '
3788
3789
3790 if self['perturbation_couplings']:
3791 mystr = mystr + '[ '
3792 if self['NLO_mode']!='tree':
3793 if self['NLO_mode']=='virt' and not self['has_born']:
3794 mystr = mystr + 'sqrvirt = '
3795 else:
3796 mystr = mystr + self['NLO_mode'] + ' = '
3797 for order in self['perturbation_couplings']:
3798 mystr = mystr + order + ' '
3799 mystr = mystr + '] '
3800
3801 if self['squared_orders']:
3802 mystr = mystr + " ".join([key + '^2%s%d'%\
3803 (self.get_squared_order_type(key),self['squared_orders'][key]) \
3804 for key in self['squared_orders'].keys() \
3805 if print_weighted or key!='WEIGHTED']) + ' '
3806
3807
3808 mystr = mystr[:-1]
3809
3810 if self.get('id') or self.get('overall_orders'):
3811 mystr += " @%d" % self.get('id')
3812 if self.get('overall_orders'):
3813 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3814 for key in sorted(self['orders'])]) + ' '
3815
3816 if not self.get('decay_chains'):
3817 return mystr
3818
3819 for decay in self['decay_chains']:
3820 mystr = mystr + '\n' + \
3821 decay.nice_string(indent + 2).replace('Process', 'Decay')
3822
3823 return mystr
3824
3826 """ Return a Process object which has the same properties of this
3827 ProcessDefinition but with the specified LegList as legs attribute.
3828 """
3829
3830 return Process({\
3831 'legs': LegList,
3832 'model':self.get('model'),
3833 'id': self.get('id'),
3834 'orders': self.get('orders'),
3835 'sqorders_types': self.get('sqorders_types'),
3836 'squared_orders': self.get('squared_orders'),
3837 'constrained_orders': self.get('constrained_orders'),
3838 'has_born': self.get('has_born'),
3839 'required_s_channels': self.get('required_s_channels'),
3840 'forbidden_onsh_s_channels': self.get('forbidden_onsh_s_channels'),
3841 'forbidden_s_channels': self.get('forbidden_s_channels'),
3842 'forbidden_particles': self.get('forbidden_particles'),
3843 'perturbation_couplings': self.get('perturbation_couplings'),
3844 'is_decay_chain': self.get('is_decay_chain'),
3845 'overall_orders': self.get('overall_orders'),
3846 'split_orders': self.get('split_orders'),
3847 'NLO_mode': self.get('NLO_mode')
3848 })
3849
3850 - def get_process(self, initial_state_ids, final_state_ids):
3851 """ Return a Process object which has the same properties of this
3852 ProcessDefinition but with the specified given leg ids. """
3853
3854
3855
3856 my_isids = [leg.get('ids') for leg in self.get('legs') \
3857 if not leg.get('state')]
3858 my_fsids = [leg.get('ids') for leg in self.get('legs') \
3859 if leg.get('state')]
3860 for i, is_id in enumerate(initial_state_ids):
3861 assert is_id in my_isids[i]
3862 for i, fs_id in enumerate(final_state_ids):
3863 assert fs_id in my_fsids[i]
3864
3865 return self.get_process_with_legs(LegList(\
3866 [Leg({'id': id, 'state':False}) for id in initial_state_ids] + \
3867 [Leg({'id': id, 'state':True}) for id in final_state_ids]))
3868
3870 """Overloading the equality operator, so that only comparison
3871 of process id and legs is being done, using compare_for_sort."""
3872
3873 return super(Process, self).__eq__(other)
3874
3879 """List of ProcessDefinition objects
3880 """
3881
3883 """Test if object obj is a valid ProcessDefinition for the list."""
3884
3885 return isinstance(obj, ProcessDefinition)
3886
3892 """Make sure there are no doublets in the list doubletlist.
3893 Note that this is a slow implementation, so don't use if speed
3894 is needed"""
3895
3896 assert isinstance(doubletlist, list), \
3897 "Argument to make_unique must be list"
3898
3899
3900 uniquelist = []
3901 for elem in doubletlist:
3902 if elem not in uniquelist:
3903 uniquelist.append(elem)
3904
3905 doubletlist[:] = uniquelist[:]
3906