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