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