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