1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Definitions of all basic objects used in the core code: particle,
16 interaction, model, leg, vertex, process, ..."""
17
18 import copy
19 import itertools
20 import logging
21 import math
22 import numbers
23 import os
24 import re
25 import StringIO
26 import madgraph.core.color_algebra as color
27 from madgraph import MadGraph5Error, MG5DIR
28 import madgraph.various.misc as misc
29
30 logger = logging.getLogger('madgraph.base_objects')
31 pjoin = os.path.join
37 """A parent class for all physics objects."""
38
40 """Exception raised if an error occurs in the definition
41 or the execution of a physics object."""
42 pass
43
45 """Creates a new particle object. If a dictionary is given, tries to
46 use it to give values to properties."""
47
48 dict.__init__(self)
49 self.default_setup()
50
51 assert isinstance(init_dict, dict), \
52 "Argument %s is not a dictionary" % repr(init_dict)
53
54
55 for item in init_dict.keys():
56 self.set(item, init_dict[item])
57
58
60 """ force the check that the property exist before returning the
61 value associated to value. This ensure that the correct error
62 is always raise
63 """
64
65 try:
66 return dict.__getitem__(self, name)
67 except KeyError:
68 self.is_valid_prop(name)
69
70
72 """Function called to create and setup default values for all object
73 properties"""
74 pass
75
77 """Check if a given property name is valid"""
78
79 assert isinstance(name, str), \
80 "Property name %s is not a string" % repr(name)
81
82 if name not in self.keys():
83 raise self.PhysicsObjectError, \
84 """%s is not a valid property for this object: %s\n
85 Valid property are %s""" % (name,self.__class__.__name__, self.keys())
86 return True
87
88 - def get(self, name):
89 """Get the value of the property name."""
90
91 return self[name]
92
93 - def set(self, name, value, force=False):
94 """Set the value of the property name. First check if value
95 is a valid value for the considered property. Return True if the
96 value has been correctly set, False otherwise."""
97 if not __debug__ or force:
98 self[name] = value
99 return True
100
101 if self.is_valid_prop(name):
102 try:
103 self.filter(name, value)
104 self[name] = value
105 return True
106 except self.PhysicsObjectError, why:
107 logger.warning("Property " + name + " cannot be changed:" + \
108 str(why))
109 return False
110
111 - def filter(self, name, value):
112 """Checks if the proposed value is valid for a given property
113 name. Returns True if OK. Raises an error otherwise."""
114
115 return True
116
118 """Returns the object keys sorted in a certain way. By default,
119 alphabetical."""
120
121 return self.keys().sort()
122
124 """String representation of the object. Outputs valid Python
125 with improved format."""
126
127 mystr = '{\n'
128 for prop in self.get_sorted_keys():
129 if isinstance(self[prop], str):
130 mystr = mystr + ' \'' + prop + '\': \'' + \
131 self[prop] + '\',\n'
132 elif isinstance(self[prop], float):
133 mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop]
134 else:
135 mystr = mystr + ' \'' + prop + '\': ' + \
136 repr(self[prop]) + ',\n'
137 mystr = mystr.rstrip(',\n')
138 mystr = mystr + '\n}'
139
140 return mystr
141
142 __repr__ = __str__
143
149 """A class to store lists of physics object."""
150
152 """Exception raised if an error occurs in the definition
153 or execution of a physics object list."""
154 pass
155
157 """Creates a new particle list object. If a list of physics
158 object is given, add them."""
159
160 list.__init__(self)
161
162 if init_list is not None:
163 for object in init_list:
164 self.append(object)
165
167 """Appends an element, but test if valid before."""
168
169 assert self.is_valid_element(object), \
170 "Object %s is not a valid object for the current list" % repr(object)
171
172 list.append(self, object)
173
174
176 """Test if object obj is a valid element for the list."""
177 return True
178
180 """String representation of the physics object list object.
181 Outputs valid Python with improved format."""
182
183 mystr = '['
184
185 for obj in self:
186 mystr = mystr + str(obj) + ',\n'
187
188 mystr = mystr.rstrip(',\n')
189
190 return mystr + ']'
191
192
193
194
195 -class Particle(PhysicsObject):
196 """The particle object containing the whole set of information required to
197 univocally characterize a given type of physical particle: name, spin,
198 color, mass, width, charge,... The is_part flag tells if the considered
199 particle object is a particle or an antiparticle. The self_antipart flag
200 tells if the particle is its own antiparticle."""
201
202 sorted_keys = ['name', 'antiname', 'spin', 'color',
203 'charge', 'mass', 'width', 'pdg_code',
204 'texname', 'antitexname', 'line', 'propagating', 'propagator',
205 'is_part', 'self_antipart', 'ghost', 'counterterm']
206
207 - def default_setup(self):
208 """Default values for all properties"""
209
210 self['name'] = 'none'
211 self['antiname'] = 'none'
212 self['spin'] = 1
213 self['color'] = 1
214 self['charge'] = 1.
215 self['mass'] = 'ZERO'
216 self['width'] = 'ZERO'
217 self['pdg_code'] = 0
218 self['texname'] = 'none'
219 self['antitexname'] = 'none'
220 self['line'] = 'dashed'
221 self['propagating'] = True
222 self['propagator'] = ''
223 self['is_part'] = True
224 self['self_antipart'] = False
225
226 self['ghost'] = False
227
228
229 self['counterterm'] = {}
230
231 - def filter(self, name, value):
232 """Filter for valid particle property values."""
233
234 if name in ['name', 'antiname']:
235
236 p=re.compile('''^[\w\-\+~_]+$''')
237 if not p.match(value):
238 raise self.PhysicsObjectError, \
239 "%s is not a valid particle name" % value
240
241 if name is 'ghost':
242 if not isinstance(value,bool):
243 raise self.PhysicsObjectError, \
244 "%s is not a valid bool for the 'ghost' attribute" % str(value)
245
246 if name is 'counterterm':
247 if not isinstance(value,dict):
248 raise self.PhysicsObjectError, \
249 "counterterm %s is not a valid dictionary" % repr(value)
250 for key, val in value.items():
251 if not isinstance(key,tuple):
252 raise self.PhysicsObjectError, \
253 "key %s is not a valid tuple for counterterm key" % repr(key)
254 if not isinstance(key[0],str):
255 raise self.PhysicsObjectError, \
256 "%s is not a valid string" % repr(key[0])
257 if not isinstance(key[1],tuple):
258 raise self.PhysicsObjectError, \
259 "%s is not a valid list" % repr(key[1])
260 for elem in key[1]:
261 if not isinstance(elem,tuple):
262 raise self.PhysicsObjectError, \
263 "%s is not a valid list" % repr(elem)
264 for partPDG in elem:
265 if not isinstance(partPDG,int):
266 raise self.PhysicsObjectError, \
267 "%s is not a valid integer for PDG" % repr(partPDG)
268 if partPDG<=0:
269 raise self.PhysicsObjectError, \
270 "%s is not a valid positive PDG" % repr(partPDG)
271 if not isinstance(val,dict):
272 raise self.PhysicsObjectError, \
273 "value %s is not a valid dictionary for counterterm value" % repr(val)
274 for vkey, vvalue in val.items():
275 if vkey not in [0,-1,-2]:
276 raise self.PhysicsObjectError, \
277 "Key %s is not a valid laurent serie order" % repr(vkey)
278 if not isinstance(vvalue,str):
279 raise self.PhysicsObjectError, \
280 "Coupling %s is not a valid string" % repr(vvalue)
281 if name is 'spin':
282 if not isinstance(value, int):
283 raise self.PhysicsObjectError, \
284 "Spin %s is not an integer" % repr(value)
285 if (value < 1 or value > 5) and value != 99:
286 raise self.PhysicsObjectError, \
287 "Spin %i not valid" % value
288
289 if name is 'color':
290 if not isinstance(value, int):
291 raise self.PhysicsObjectError, \
292 "Color %s is not an integer" % repr(value)
293 if value not in [1, 3, 6, 8]:
294 raise self.PhysicsObjectError, \
295 "Color %i is not valid" % value
296
297 if name in ['mass', 'width']:
298
299 p = re.compile('\A[a-zA-Z]+[\w\_]*\Z')
300 if not p.match(value):
301 raise self.PhysicsObjectError, \
302 "%s is not a valid name for mass/width variable" % \
303 value
304
305 if name is 'pdg_code':
306 if not isinstance(value, int):
307 raise self.PhysicsObjectError, \
308 "PDG code %s is not an integer" % repr(value)
309
310 if name is 'line':
311 if not isinstance(value, str):
312 raise self.PhysicsObjectError, \
313 "Line type %s is not a string" % repr(value)
314 if value not in ['dashed', 'straight', 'wavy', 'curly', 'double','swavy','scurly','dotted']:
315 raise self.PhysicsObjectError, \
316 "Line type %s is unknown" % value
317
318 if name is 'charge':
319 if not isinstance(value, float):
320 raise self.PhysicsObjectError, \
321 "Charge %s is not a float" % repr(value)
322
323 if name is 'propagating':
324 if not isinstance(value, bool):
325 raise self.PhysicsObjectError, \
326 "Propagating tag %s is not a boolean" % repr(value)
327
328 if name in ['is_part', 'self_antipart']:
329 if not isinstance(value, bool):
330 raise self.PhysicsObjectError, \
331 "%s tag %s is not a boolean" % (name, repr(value))
332
333 return True
334
335 - def get_sorted_keys(self):
336 """Return particle property names as a nicely sorted list."""
337
338 return self.sorted_keys
339
340
341
342 - def is_perturbating(self,order,model):
343 """Returns wether this particle contributes in perturbation of the order passed
344 in argument given the model specified. It is very fast for usual models"""
345
346 for int in model['interactions'].get_type('base'):
347
348
349
350
351
352
353
354
355 if len(int.get('orders'))>1:
356 continue
357 if order in int.get('orders').keys() and self.get('pdg_code') in \
358 [part.get('pdg_code') for part in int.get('particles')]:
359 return True
360
361 return False
362
363 - def get_pdg_code(self):
364 """Return the PDG code with a correct minus sign if the particle is its
365 own antiparticle"""
366
367 if not self['is_part'] and not self['self_antipart']:
368 return - self['pdg_code']
369 else:
370 return self['pdg_code']
371
373 """Return the PDG code of the antiparticle with a correct minus sign
374 if the particle is its own antiparticle"""
375
376 if not self['self_antipart']:
377 return - self.get_pdg_code()
378 else:
379 return self['pdg_code']
380
381 - def get_color(self):
382 """Return the color code with a correct minus sign"""
383
384 if not self['is_part'] and abs(self['color']) in [3, 6]:
385 return - self['color']
386 else:
387 return self['color']
388
389 - def get_anti_color(self):
390 """Return the color code of the antiparticle with a correct minus sign
391 """
392
393 if self['is_part'] and self['color'] not in [1, 8]:
394 return - self['color']
395 else:
396 return self['color']
397
398 - def get_charge(self):
399 """Return the charge code with a correct minus sign"""
400
401 if not self['is_part']:
402 return - self['charge']
403 else:
404 return self['charge']
405
406 - def get_anti_charge(self):
407 """Return the charge code of the antiparticle with a correct minus sign
408 """
409
410 if self['is_part']:
411 return - self['charge']
412 else:
413 return self['charge']
414
415 - def get_name(self):
416 """Return the name if particle, antiname if antiparticle"""
417
418 if not self['is_part'] and not self['self_antipart']:
419 return self['antiname']
420 else:
421 return self['name']
422
424 """Return a list of the helicity states for the onshell particle"""
425
426 spin = self.get('spin')
427 if spin ==1:
428
429 return [ 0 ]
430 elif spin == 2:
431
432 return [ -1, 1 ]
433 elif spin == 3 and self.get('mass').lower() == 'zero':
434
435 return [ -1, 1 ]
436 elif spin == 3:
437
438 return [ -1, 0, 1 ]
439 elif spin == 4 and self.get('mass').lower() == 'zero':
440
441 return [-3, 3]
442 elif spin == 4:
443
444 return [-3, -1, 1, 3]
445
446 elif spin == 5 and self.get('mass').lower() == 'zero':
447
448 return [-2, -1, 1, 2]
449 elif spin in [5, 99]:
450
451 return [-2, -1, 0, 1, 2]
452
453 raise self.PhysicsObjectError, \
454 "No helicity state assignment for spin %d particles" % spin
455
456 - def is_fermion(self):
457 """Returns True if this is a fermion, False if boson"""
458
459 return self['spin'] % 2 == 0
460
461 - def is_boson(self):
462 """Returns True if this is a boson, False if fermion"""
463
464 return self['spin'] % 2 == 1
465
466
467
468
469 -class ParticleList(PhysicsObjectList):
470 """A class to store lists of particles."""
471
472 - def is_valid_element(self, obj):
473 """Test if object obj is a valid Particle for the list."""
474 return isinstance(obj, Particle)
475
476 - def get_copy(self, name):
477 """Try to find a particle with the given name. Check both name
478 and antiname. If a match is found, return the a copy of the
479 corresponding particle (first one in the list), with the
480 is_part flag set accordingly. None otherwise."""
481
482 assert isinstance(name, str)
483
484 part = self.find_name(name)
485 if not part:
486
487 try:
488 pdg = int(name)
489 except ValueError:
490 return None
491
492 for p in self:
493 if p.get_pdg_code()==pdg:
494 part = copy.copy(p)
495 part.set('is_part', True)
496 return part
497 elif p.get_anti_pdg_code()==pdg:
498 part = copy.copy(p)
499 part.set('is_part', False)
500 return part
501
502 return None
503 part = copy.copy(part)
504
505 if part.get('name') == name:
506 part.set('is_part', True)
507 return part
508 elif part.get('antiname') == name:
509 part.set('is_part', False)
510 return part
511 return None
512
513 - def find_name(self, name):
514 """Try to find a particle with the given name. Check both name
515 and antiname. If a match is found, return the a copy of the
516 corresponding particle (first one in the list), with the
517 is_part flag set accordingly. None otherwise."""
518
519 assert isinstance(name, str), "%s is not a valid string" % str(name)
520
521 for part in self:
522 if part.get('name') == name:
523 return part
524 elif part.get('antiname') == name:
525 return part
526
527 return None
528
530 """Generate a dictionary of part/antipart pairs (as keys) and
531 0 (as value)"""
532
533 ref_dict_to0 = {}
534
535 for part in self:
536 ref_dict_to0[(part.get_pdg_code(), part.get_anti_pdg_code())] = [0]
537 ref_dict_to0[(part.get_anti_pdg_code(), part.get_pdg_code())] = [0]
538
539 return ref_dict_to0
540
541 - def generate_dict(self):
542 """Generate a dictionary from particle id to particle.
543 Include antiparticles.
544 """
545
546 particle_dict = {}
547
548 for particle in self:
549 particle_dict[particle.get('pdg_code')] = particle
550 if not particle.get('self_antipart'):
551 antipart = copy.deepcopy(particle)
552 antipart.set('is_part', False)
553 particle_dict[antipart.get_pdg_code()] = antipart
554
555 return particle_dict
556
562 """The interaction object containing the whole set of information
563 required to univocally characterize a given type of physical interaction:
564
565 particles: a list of particle ids
566 color: a list of string describing all the color structures involved
567 lorentz: a list of variable names describing all the Lorentz structure
568 involved
569 couplings: dictionary listing coupling variable names. The key is a
570 2-tuple of integers referring to color and Lorentz structures
571 orders: dictionary listing order names (as keys) with their value
572 """
573
574 sorted_keys = ['id', 'particles', 'color', 'lorentz', 'couplings',
575 'orders','loop_particles','type','perturbation_type']
576
578 """Default values for all properties"""
579
580 self['id'] = 0
581 self['particles'] = []
582 self['color'] = []
583 self['lorentz'] = []
584 self['couplings'] = { (0, 0):'none'}
585 self['orders'] = {}
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
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 self['loop_particles']=[[]]
641 self['type'] = 'base'
642 self['perturbation_type'] = None
643
644 - def filter(self, name, value):
645 """Filter for valid interaction property values."""
646
647 if name == 'id':
648
649 if not isinstance(value, int):
650 raise self.PhysicsObjectError, \
651 "%s is not a valid integer" % str(value)
652
653 if name == 'particles':
654
655 if not isinstance(value, ParticleList):
656 raise self.PhysicsObjectError, \
657 "%s is not a valid list of particles" % str(value)
658
659 if name == 'perturbation_type':
660 if value!=None and not isinstance(value, str):
661 raise self.PhysicsObjectError, \
662 "%s is not a valid string" % str(value)
663
664 if name == 'type':
665
666 if not isinstance(value, str):
667 raise self.PhysicsObjectError, \
668 "%s is not a valid string" % str(value)
669 if name == 'loop_particles':
670 if isinstance(value,list):
671 for l in value:
672 if isinstance(l,list):
673 for part in l:
674 if not isinstance(part,int):
675 raise self.PhysicsObjectError, \
676 "%s is not a valid integer" % str(part)
677 if part<0:
678 raise self.PhysicsObjectError, \
679 "%s is not a valid positive integer" % str(part)
680
681 if name == 'orders':
682
683 if not isinstance(value, dict):
684 raise self.PhysicsObjectError, \
685 "%s is not a valid dict for coupling orders" % \
686 str(value)
687 for order in value.keys():
688 if not isinstance(order, str):
689 raise self.PhysicsObjectError, \
690 "%s is not a valid string" % str(order)
691 if not isinstance(value[order], int):
692 raise self.PhysicsObjectError, \
693 "%s is not a valid integer" % str(value[order])
694
695 if name in ['color']:
696
697 if not isinstance(value, list):
698 raise self.PhysicsObjectError, \
699 "%s is not a valid list of Color Strings" % str(value)
700 for mycolstring in value:
701 if not isinstance(mycolstring, color.ColorString):
702 raise self.PhysicsObjectError, \
703 "%s is not a valid list of Color Strings" % str(value)
704
705 if name in ['lorentz']:
706
707 if not isinstance(value, list):
708 raise self.PhysicsObjectError, \
709 "%s is not a valid list of strings" % str(value)
710 for mystr in value:
711 if not isinstance(mystr, str):
712 raise self.PhysicsObjectError, \
713 "%s is not a valid string" % str(mystr)
714
715 if name == 'couplings':
716
717 if not isinstance(value, dict):
718 raise self.PhysicsObjectError, \
719 "%s is not a valid dictionary for couplings" % \
720 str(value)
721
722 for key in value.keys():
723 if not isinstance(key, tuple):
724 raise self.PhysicsObjectError, \
725 "%s is not a valid tuple" % str(key)
726 if len(key) != 2:
727 raise self.PhysicsObjectError, \
728 "%s is not a valid tuple with 2 elements" % str(key)
729 if not isinstance(key[0], int) or not isinstance(key[1], int):
730 raise self.PhysicsObjectError, \
731 "%s is not a valid tuple of integer" % str(key)
732 if not isinstance(value[key], str):
733 raise self.PhysicsObjectError, \
734 "%s is not a valid string" % value[key]
735
736 return True
737
739 """Return particle property names as a nicely sorted list."""
740
741 return self.sorted_keys
742
744 """ Returns if this interaction comes from the perturbation of one of
745 the order listed in the argument """
746
747 if self['perturbation_type']==None:
748 return True
749 else:
750 return (self['perturbation_type'] in orders_considered)
751
753 """ Returns if the interaction is of R2 type."""
754
755
756
757 if 'type' in self.keys():
758 return (len(self['type'])>=2 and self['type'][:2]=='R2')
759 else:
760 return False
761
763 """ Returns if the interaction is of UV type."""
764
765
766
767 if 'type' in self.keys():
768 return (len(self['type'])>=2 and self['type'][:2]=='UV')
769 else:
770 return False
771
773 """ Returns if the interaction is of UVmass type."""
774
775
776
777 if 'type' in self.keys():
778 return (len(self['type'])>=6 and self['type'][:6]=='UVmass')
779 else:
780 return False
781
783 """ Returns if the interaction is of UVmass type."""
784
785
786
787 if 'type' in self.keys():
788 return (len(self['type'])>=6 and self['type'][:6]=='UVloop')
789 else:
790 return False
791
793 """ Returns if the interaction is of UVmass type."""
794
795
796
797 if 'type' in self.keys():
798 return (len(self['type'])>=6 and self['type'][:6]=='UVtree')
799 else:
800 return False
801
803 """ Returns if the interaction is of the UVCT type which means that
804 it has been selected as a possible UV counterterm interaction for this
805 process. Such interactions are marked by having the 'UVCT_SPECIAL' order
806 key in their orders."""
807
808
809
810 if 'UVCT_SPECIAL' in self['orders'].keys():
811 return True
812 else:
813 return False
814
816 """ Returns 0 if this interaction contributes to the finite part of the
817 amplitude and 1 (2) is it contributes to its single (double) pole """
818
819 if 'type' in self.keys():
820 if '1eps' in self['type']:
821 return 1
822 elif '2eps' in self['type']:
823 return 2
824 else:
825 return 0
826 else:
827 return 0
828
830 """Add entries corresponding to the current interactions to
831 the reference dictionaries (for n>0 and n-1>1)"""
832
833
834
835
836 pdg_tuple = tuple(sorted([p.get_pdg_code() for p in self['particles']]))
837 if pdg_tuple not in ref_dict_to0.keys():
838 ref_dict_to0[pdg_tuple] = [self['id']]
839 else:
840 ref_dict_to0[pdg_tuple].append(self['id'])
841
842
843
844
845
846
847
848 for part in self['particles']:
849
850
851 pdg_tuple = tuple(sorted([p.get_pdg_code() for (i, p) in \
852 enumerate(self['particles']) if \
853 i != self['particles'].index(part)]))
854 pdg_part = part.get_anti_pdg_code()
855 if pdg_tuple in ref_dict_to1.keys():
856 if (pdg_part, self['id']) not in ref_dict_to1[pdg_tuple]:
857 ref_dict_to1[pdg_tuple].append((pdg_part, self['id']))
858 else:
859 ref_dict_to1[pdg_tuple] = [(pdg_part, self['id'])]
860
862 """Get the WEIGHTED order for this interaction, for equivalent
863 3-particle vertex. Note that it can be fractional."""
864
865 return float(sum([model.get('order_hierarchy')[key]*self.get('orders')[key]\
866 for key in self.get('orders')]))/ \
867 max((len(self.get('particles'))-2), 1)
868
870 """String representation of an interaction. Outputs valid Python
871 with improved format. Overrides the PhysicsObject __str__ to only
872 display PDG code of involved particles."""
873
874 mystr = '{\n'
875
876 for prop in self.get_sorted_keys():
877 if isinstance(self[prop], str):
878 mystr = mystr + ' \'' + prop + '\': \'' + \
879 self[prop] + '\',\n'
880 elif isinstance(self[prop], float):
881 mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop]
882 elif isinstance(self[prop], ParticleList):
883 mystr = mystr + ' \'' + prop + '\': [%s],\n' % \
884 ','.join([str(part.get_pdg_code()) for part in self[prop]])
885 else:
886 mystr = mystr + ' \'' + prop + '\': ' + \
887 repr(self[prop]) + ',\n'
888 mystr = mystr.rstrip(',\n')
889 mystr = mystr + '\n}'
890
891 return mystr
892
897 """A class to store lists of interactionss."""
898
900 """Test if object obj is a valid Interaction for the list."""
901
902 return isinstance(obj, Interaction)
903
905 """Generate the reference dictionaries from interaction list.
906 Return a list where the first element is the n>0 dictionary and
907 the second one is n-1>1."""
908
909 ref_dict_to0 = {}
910 ref_dict_to1 = {}
911 buffer = {}
912
913 for inter in self:
914 if useR2UV or (not inter.is_UV() and not inter.is_R2() and \
915 not inter.is_UVCT()):
916 inter.generate_dict_entries(ref_dict_to0, ref_dict_to1)
917 if useUVCT and inter.is_UVCT():
918 inter.generate_dict_entries(ref_dict_to0, ref_dict_to1)
919
920 return [ref_dict_to0, ref_dict_to1]
921
923 """Generate a dictionary from interaction id to interaction.
924 """
925
926 interaction_dict = {}
927
928 for inter in self:
929 interaction_dict[inter.get('id')] = inter
930
931 return interaction_dict
932
934 """Make sure that the particles in the interactions are those
935 in the particle_dict, and that there are no interactions
936 refering to particles that don't exist. To be called when the
937 particle_dict is updated in a model.
938 """
939
940 iint = 0
941 while iint < len(self):
942 inter = self[iint]
943 particles = inter.get('particles')
944 try:
945 for ipart, part in enumerate(particles):
946 particles[ipart] = particle_dict[part.get_pdg_code()]
947 iint += 1
948 except KeyError:
949
950 self.pop(iint)
951
953 """ return all interactions in the list of type 'type' """
954 return InteractionList([int for int in self if int.get('type')==type])
955
957 """ return all interactions in the list of type R2 """
958 return InteractionList([int for int in self if int.is_R2()])
959
961 """ return all interactions in the list of type UV """
962 return InteractionList([int for int in self if int.is_UV()])
963
965 """ return all interactions in the list of type UVmass """
966 return InteractionList([int for int in self if int.is_UVmass()])
967
969 """ return all interactions in the list of type UVtree """
970 return InteractionList([int for int in self if int.is_UVtree()])
971
973 """ return all interactions in the list of type UVloop """
974 return InteractionList([int for int in self if int.is_UVloop()])
975
976
977
978
979 -class Model(PhysicsObject):
980 """A class to store all the model information."""
981
983
984 self['name'] = ""
985 self['particles'] = ParticleList()
986 self['interactions'] = InteractionList()
987 self['parameters'] = None
988 self['functions'] = None
989 self['couplings'] = None
990 self['lorentz'] = None
991 self['particle_dict'] = {}
992 self['interaction_dict'] = {}
993 self['ref_dict_to0'] = {}
994 self['ref_dict_to1'] = {}
995 self['got_majoranas'] = None
996 self['order_hierarchy'] = {}
997 self['conserved_charge'] = set()
998 self['coupling_orders'] = None
999 self['expansion_order'] = None
1000 self['version_tag'] = None
1001 self['gauge'] = [0, 1]
1002 self['case_sensitive'] = True
1003
1004
1005
1006
1007 - def filter(self, name, value):
1008 """Filter for model property values"""
1009
1010 if name in ['name']:
1011 if not isinstance(value, str):
1012 raise self.PhysicsObjectError, \
1013 "Object of type %s is not a string" %type(value)
1014
1015 elif name == 'particles':
1016 if not isinstance(value, ParticleList):
1017 raise self.PhysicsObjectError, \
1018 "Object of type %s is not a ParticleList object" % \
1019 type(value)
1020 elif name == 'interactions':
1021 if not isinstance(value, InteractionList):
1022 raise self.PhysicsObjectError, \
1023 "Object of type %s is not a InteractionList object" % \
1024 type(value)
1025 elif name == 'particle_dict':
1026 if not isinstance(value, dict):
1027 raise self.PhysicsObjectError, \
1028 "Object of type %s is not a dictionary" % \
1029 type(value)
1030 elif name == 'interaction_dict':
1031 if not isinstance(value, dict):
1032 raise self.PhysicsObjectError, \
1033 "Object of type %s is not a dictionary" % type(value)
1034
1035 elif name == 'ref_dict_to0':
1036 if not isinstance(value, dict):
1037 raise self.PhysicsObjectError, \
1038 "Object of type %s is not a dictionary" % type(value)
1039
1040 elif name == 'ref_dict_to1':
1041 if not isinstance(value, dict):
1042 raise self.PhysicsObjectError, \
1043 "Object of type %s is not a dictionary" % type(value)
1044
1045 elif name == 'got_majoranas':
1046 if not (isinstance(value, bool) or value == None):
1047 raise self.PhysicsObjectError, \
1048 "Object of type %s is not a boolean" % type(value)
1049
1050 elif name == 'conserved_charge':
1051 if not (isinstance(value, set)):
1052 raise self.PhysicsObjectError, \
1053 "Object of type %s is not a set" % type(value)
1054
1055 elif name == 'version_tag':
1056 if not (isinstance(value, str)):
1057 raise self.PhysicsObjectError, \
1058 "Object of type %s is not a string" % type(value)
1059
1060 elif name == 'order_hierarchy':
1061 if not isinstance(value, dict):
1062 raise self.PhysicsObjectError, \
1063 "Object of type %s is not a dictionary" % \
1064 type(value)
1065 for key in value.keys():
1066 if not isinstance(value[key],int):
1067 raise self.PhysicsObjectError, \
1068 "Object of type %s is not an integer" % \
1069 type(value[key])
1070 elif name == 'gauge':
1071 if not (isinstance(value, list)):
1072 raise self.PhysicsObjectError, \
1073 "Object of type %s is not a list" % type(value)
1074
1075 elif name == 'case_sensitive':
1076 if not value in [True ,False]:
1077 raise self.PhysicsObjectError, \
1078 "Object of type %s is not a boolean" % type(value)
1079 return True
1080
1081 - def get(self, name):
1082 """Get the value of the property name."""
1083
1084 if (name == 'ref_dict_to0' or name == 'ref_dict_to1') and \
1085 not self[name]:
1086 if self['interactions']:
1087 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1088 self['interactions'].generate_ref_dict()
1089 self['ref_dict_to0'].update(
1090 self['particles'].generate_ref_dict())
1091
1092 if (name == 'particle_dict') and not self[name]:
1093 if self['particles']:
1094 self['particle_dict'] = self['particles'].generate_dict()
1095 if self['interactions']:
1096 self['interactions'].synchronize_interactions_with_particles(\
1097 self['particle_dict'])
1098 if name == 'modelpath':
1099 modeldir = self.get('version_tag').rsplit('##',1)[0]
1100 if os.path.exists(modeldir):
1101 return modeldir
1102 else:
1103 raise Exception, "path %s not valid anymore." % modeldir
1104
1105
1106
1107
1108
1109 elif name == 'modelpath+restriction':
1110 modeldir = self.get('version_tag').rsplit('##',1)[0]
1111 modelname = self['name']
1112 if not os.path.exists(modeldir):
1113 raise Exception, "path %s not valid anymore" % modeldir
1114 modeldir = os.path.dirname(modeldir)
1115 modeldir = pjoin(modeldir, modelname)
1116 return modeldir
1117 elif name == 'restrict_name':
1118 modeldir = self.get('version_tag').rsplit('##',1)[0]
1119 modelname = self['name']
1120 basename = os.path.basename(modeldir)
1121 restriction = modelname[len(basename)+1:]
1122 return restriction
1123
1124 if (name == 'interaction_dict') and not self[name]:
1125 if self['interactions']:
1126 self['interaction_dict'] = self['interactions'].generate_dict()
1127
1128 if (name == 'got_majoranas') and self[name] == None:
1129 if self['particles']:
1130 self['got_majoranas'] = self.check_majoranas()
1131
1132 if (name == 'coupling_orders') and self[name] == None:
1133 if self['interactions']:
1134 self['coupling_orders'] = self.get_coupling_orders()
1135
1136 if (name == 'order_hierarchy') and not self[name]:
1137 if self['interactions']:
1138 self['order_hierarchy'] = self.get_order_hierarchy()
1139
1140 if (name == 'expansion_order') and self[name] == None:
1141 if self['interactions']:
1142 self['expansion_order'] = \
1143 dict([(order, -1) for order in self.get('coupling_orders')])
1144
1145 if (name == 'name2pdg') and 'name2pdg' not in self:
1146 self['name2pdg'] = {}
1147 for p in self.get('particles'):
1148 self['name2pdg'][p.get('antiname')] = -1*p.get('pdg_code')
1149 self['name2pdg'][p.get('name')] = p.get('pdg_code')
1150
1151 return Model.__bases__[0].get(self, name)
1152
1153 - def set(self, name, value, force = False):
1154 """Special set for particles and interactions - need to
1155 regenerate dictionaries."""
1156
1157 if name == 'particles':
1158
1159 make_unique(value)
1160
1161 self['particle_dict'] = {}
1162 self['ref_dict_to0'] = {}
1163 self['got_majoranas'] = None
1164
1165 if name == 'interactions':
1166
1167 make_unique(value)
1168
1169 self['interaction_dict'] = {}
1170 self['ref_dict_to1'] = {}
1171 self['ref_dict_to0'] = {}
1172 self['got_majoranas'] = None
1173 self['coupling_orders'] = None
1174 self['order_hierarchy'] = {}
1175 self['expansion_order'] = None
1176
1177 result = Model.__bases__[0].set(self, name, value, force)
1178
1179 if name == 'particles':
1180
1181 self.get('particle_dict')
1182
1183 return result
1184
1186 """This function actualizes the dictionaries"""
1187
1188 [self['ref_dict_to0'], self['ref_dict_to1']] = \
1189 self['interactions'].generate_ref_dict()
1190 self['ref_dict_to0'].update(
1191 self['particles'].generate_ref_dict())
1192
1194 """Return process property names as a nicely sorted list."""
1195
1196 return ['name', 'particles', 'parameters', 'interactions',
1197 'couplings','lorentz', 'gauge']
1198
1199 - def get_particle(self, id):
1200 """Return the particle corresponding to the id / name"""
1201
1202 try:
1203 return self["particle_dict"][id]
1204 except Exception:
1205 if isinstance(id, int):
1206 try:
1207 return self.get("particle_dict")[id]
1208 except Exception:
1209 return None
1210 else:
1211 if not hasattr(self, 'name2part'):
1212 self.create_name2part()
1213 try:
1214 return self.name2part[id]
1215 except:
1216 return None
1217
1219 """create a dictionary name 2 part"""
1220
1221 self.name2part = {}
1222 for part in self.get("particle_dict").values():
1223 self.name2part[part.get('name')] = part
1224
1225
1226
1228 """return the lorentz object from the associate name"""
1229 if hasattr(self, 'lorentz_name2obj'):
1230 return self.lorentz_name2obj[name]
1231 else:
1232 self.create_lorentz_dict()
1233 return self.lorentz_name2obj[name]
1234
1236 """create the dictionary linked to the lorentz structure"""
1237 self.lorentz_name2obj = {}
1238 self.lorentz_expr2name = {}
1239 if not self.get('lorentz'):
1240 return
1241 for lor in self.get('lorentz'):
1242 self.lorentz_name2obj[lor.name] = lor
1243 self.lorentz_expr2name[lor.structure] = lor.name
1244
1246 """Return the interaction corresponding to the id"""
1247
1248 try:
1249 return self.get("interaction_dict")[id]
1250 except Exception:
1251 return None
1252
1254 """Return the parameter associated to the name NAME"""
1255
1256
1257 if hasattr(self, 'parameters_dict') and self.parameters_dict:
1258 try:
1259 return self.parameters_dict[name]
1260 except Exception:
1261
1262 pass
1263
1264
1265 self.parameters_dict = {}
1266 for data in self['parameters'].values():
1267 [self.parameters_dict.__setitem__(p.name,p) for p in data]
1268
1269 return self.parameters_dict[name]
1270
1272 """Determine the coupling orders of the model"""
1273 return set(sum([i.get('orders').keys() for i in \
1274 self.get('interactions')], []))
1275
1277 """Set a default order hierarchy for the model if not set by the UFO."""
1278
1279 hierarchy = dict([(order, 1) for order in self.get('coupling_orders')])
1280
1281 if self.get('coupling_orders') == set(['QCD', 'QED']):
1282 hierarchy['QED'] = 2
1283 return hierarchy
1284
1285
1287 """returns the number of light quark flavours in the model."""
1288 return len([p for p in self.get('particles') \
1289 if p['spin'] == 2 and p['is_part'] and \
1290 p ['color'] != 1 and p['mass'].lower() == 'zero'])
1291
1292
1294 """Returns the order hierarchies of the model and the
1295 particles which have interactions in at least this hierarchy
1296 (used in find_optimal_process_orders in MultiProcess diagram
1297 generation):
1298
1299 Check the coupling hierarchy of the model. Assign all
1300 particles to the different coupling hierarchies so that a
1301 particle is considered to be in the highest hierarchy (i.e.,
1302 with lowest value) where it has an interaction.
1303 """
1304
1305
1306 coupling_orders = self.get('coupling_orders')
1307
1308
1309 hierarchy = sorted(list(set([self.get('order_hierarchy')[k] for \
1310 k in coupling_orders])))
1311
1312
1313 orders = []
1314 for value in hierarchy:
1315 orders.append([ k for (k, v) in \
1316 self.get('order_hierarchy').items() if \
1317 v == value ])
1318
1319
1320
1321 interactions = []
1322 particles = []
1323 for iorder, order in enumerate(orders):
1324 sum_orders = sum(orders[:iorder+1], [])
1325 sum_interactions = sum(interactions[:iorder], [])
1326 sum_particles = sum([list(p) for p in particles[:iorder]], [])
1327
1328
1329 interactions.append([i for i in self.get('interactions') if \
1330 not i in sum_interactions and \
1331 not any([k not in sum_orders for k in \
1332 i.get('orders').keys()])])
1333
1334
1335 particles.append(set(sum([[p.get_pdg_code() for p in \
1336 inter.get('particles') if \
1337 p.get_pdg_code() not in sum_particles] \
1338 for inter in interactions[-1]], [])))
1339
1340 return particles, hierarchy
1341
1343 """Return the maximum WEIGHTED order for any interaction in the model,
1344 for equivalent 3-particle vertices. Note that it can be fractional."""
1345
1346 return max([inter.get_WEIGHTED_order(self) for inter in \
1347 self.get('interactions')])
1348
1349
1351 """Return True if there is fermion flow violation, False otherwise"""
1352
1353 if any([part.is_fermion() and part.get('self_antipart') \
1354 for part in self.get('particles')]):
1355 return True
1356
1357
1358
1359 for inter in self.get('interactions'):
1360
1361 if len(inter.get('particles'))==1:
1362 continue
1363 fermions = [p for p in inter.get('particles') if p.is_fermion()]
1364 for i in range(0, len(fermions), 2):
1365 if fermions[i].get('is_part') == \
1366 fermions[i+1].get('is_part'):
1367
1368 return True
1369
1370 return False
1371
1373 """Reset all dictionaries and got_majoranas. This is necessary
1374 whenever the particle or interaction content has changed. If
1375 particles or interactions are set using the set routine, this
1376 is done automatically."""
1377
1378 self['particle_dict'] = {}
1379 self['ref_dict_to0'] = {}
1380 self['got_majoranas'] = None
1381 self['interaction_dict'] = {}
1382 self['ref_dict_to1'] = {}
1383 self['ref_dict_to0'] = {}
1384
1386 """Change the name of the particles such that all SM and MSSM particles
1387 follows the MG convention"""
1388
1389
1390 def check_name_free(self, name):
1391 """ check if name is not use for a particle in the model if it is
1392 raise an MadGraph5error"""
1393 part = self['particles'].find_name(name)
1394 if part:
1395 error_text = \
1396 '%s particles with pdg code %s is in conflict with MG ' + \
1397 'convention name for particle %s.\n Use -modelname in order ' + \
1398 'to use the particles name defined in the model and not the ' + \
1399 'MadGraph5_aMC@NLO convention'
1400
1401 raise MadGraph5Error, error_text % \
1402 (part.get_name(), part.get_pdg_code(), pdg)
1403
1404 default = self.load_default_name()
1405
1406 for pdg in default.keys():
1407 part = self.get_particle(pdg)
1408 if not part:
1409 continue
1410 antipart = self.get_particle(-pdg)
1411 name = part.get_name()
1412 if name != default[pdg]:
1413 check_name_free(self, default[pdg])
1414 if part.get('is_part'):
1415 part.set('name', default[pdg])
1416 if antipart:
1417 antipart.set('name', default[pdg])
1418 else:
1419 part.set('antiname', default[pdg])
1420 else:
1421 part.set('antiname', default[pdg])
1422 if antipart:
1423 antipart.set('antiname', default[pdg])
1424
1425
1426 if self.get('name') == 'mssm' or self.get('name').startswith('mssm-'):
1427 part = self.get_particle(25)
1428 part.set('name', 'h1')
1429 part.set('antiname', 'h1')
1430
1431
1432
1434 """ Change all model parameter by a given prefix.
1435 Modify the parameter if some of them are identical up to the case"""
1436
1437 lower_dict={}
1438 duplicate = set()
1439 keys = self.get('parameters').keys()
1440 for key in keys:
1441 for param in self['parameters'][key]:
1442 lower_name = param.name.lower()
1443 if not lower_name:
1444 continue
1445 try:
1446 lower_dict[lower_name].append(param)
1447 except KeyError:
1448 lower_dict[lower_name] = [param]
1449 else:
1450 duplicate.add(lower_name)
1451 logger.debug('%s is define both as lower case and upper case.'
1452 % lower_name)
1453
1454 if prefix == '' and not duplicate:
1455 return
1456
1457 re_expr = r'''\b(%s)\b'''
1458 to_change = []
1459 change={}
1460
1461 for key in keys:
1462 for param in self['parameters'][key]:
1463 value = param.name.lower()
1464 if value in ['as','mu_r', 'zero','aewm1','g']:
1465 continue
1466 elif value.startswith(prefix):
1467 continue
1468 elif value in duplicate:
1469 continue
1470 elif value:
1471 change[param.name] = '%s%s' % (prefix,param.name)
1472 to_change.append(param.name)
1473 param.name = change[param.name]
1474
1475 for value in duplicate:
1476 for i, var in enumerate(lower_dict[value][1:]):
1477 to_change.append(var.name)
1478 change[var.name] = '%s%s__%s' % (prefix, var.name.lower(), i+2)
1479 var.name = '%s%s__%s' %(prefix, var.name.lower(), i+2)
1480 to_change.append(var.name)
1481 assert 'zero' not in to_change
1482 replace = lambda match_pattern: change[match_pattern.groups()[0]]
1483
1484 if not to_change:
1485 return
1486
1487 if 'parameter_dict' in self:
1488 new_dict = dict( (change[name] if (name in change) else name, value) for
1489 name, value in self['parameter_dict'].items())
1490 self['parameter_dict'] = new_dict
1491
1492 i=0
1493 while i*1000 <= len(to_change):
1494 one_change = to_change[i*1000: min((i+1)*1000,len(to_change))]
1495 i+=1
1496 rep_pattern = re.compile('\\b%s\\b'% (re_expr % ('\\b|\\b'.join(one_change))))
1497
1498
1499 for key in keys:
1500 if key == ('external',):
1501 continue
1502 for param in self['parameters'][key]:
1503 param.expr = rep_pattern.sub(replace, param.expr)
1504
1505 for key in self['couplings'].keys():
1506 for coup in self['couplings'][key]:
1507 coup.expr = rep_pattern.sub(replace, coup.expr)
1508
1509
1510 for part in self['particles']:
1511 if str(part.get('mass')) in one_change:
1512 part.set('mass', rep_pattern.sub(replace, str(part.get('mass'))))
1513 if str(part.get('width')) in one_change:
1514 part.set('width', rep_pattern.sub(replace, str(part.get('width'))))
1515 if hasattr(part, 'partial_widths'):
1516 for key, value in part.partial_widths.items():
1517 part.partial_widths[key] = rep_pattern.sub(replace, value)
1518
1519
1520 self['particle_dict'] =''
1521 self.get('particle_dict')
1522
1523
1524
1526 """Return the first positive number that is not a valid PDG code"""
1527 return [c for c in range(1, len(self.get('particles')) + 1) if \
1528 c not in self.get('particle_dict').keys()][0]
1529
1539
1540 @ staticmethod
1542 """ load the default for name convention """
1543
1544 logger.info('Change particles name to pass to MG5 convention')
1545 default = {}
1546 for line in open(os.path.join(MG5DIR, 'input', \
1547 'particles_name_default.txt')):
1548 line = line.lstrip()
1549 if line.startswith('#'):
1550 continue
1551
1552 args = line.split()
1553 if len(args) != 2:
1554 logger.warning('Invalid syntax in interface/default_name:\n %s' % line)
1555 continue
1556 default[int(args[0])] = args[1].lower()
1557
1558 return default
1559
1561 """modify the expression changing the mass to complex mass scheme"""
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 to_change = {}
1574 mass_widths = []
1575 for particle in self.get('particles'):
1576 m = particle.get('width')
1577 if m in mass_widths:
1578 continue
1579 mass_widths.append(particle.get('width'))
1580 mass_widths.append(particle.get('mass'))
1581 if particle.get('width') == 'ZERO':
1582
1583 continue
1584 width = self.get_parameter(particle.get('width'))
1585 if not isinstance(width, ParamCardVariable):
1586 width.expr = 're(%s)' % width.expr
1587 if particle.get('mass') != 'ZERO':
1588 mass = self.get_parameter(particle.get('mass'))
1589
1590 if particle.get('pdg_code') == 24:
1591 if hasattr(mass, 'expr') and mass.expr == 'cmath.sqrt(MZ__exp__2/2. + cmath.sqrt(MZ__exp__4/4. - (aEW*cmath.pi*MZ__exp__2)/(Gf*sqrt__2)))':
1592
1593 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1594 if not MW.value:
1595 MW.value = 80.385
1596 self.get('parameters')[('external',)].append(MW)
1597 self.get('parameters')[mass.depend].remove(mass)
1598
1599 new_param = ModelVariable('Gf',
1600 '-aEW*MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - MZ**2))' %\
1601 {'MW': mass.name}, 'complex', mass.depend)
1602 Gf = self.get_parameter('Gf')
1603 self.get('parameters')[('external',)].remove(Gf)
1604 self.add_param(new_param, ['aEW'])
1605
1606 mass = MW
1607
1608 elif hasattr(mass, 'expr') and mass.expr == 'cmath.sqrt(mdl_MZ__exp__2/2. + cmath.sqrt(mdl_MZ__exp__4/4. - (mdl_aEW*cmath.pi*mdl_MZ__exp__2)/(mdl_Gf*mdl_sqrt__2)))':
1609
1610 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1611 if not MW.value:
1612 MW.value = 80.385
1613 self.get('parameters')[('external',)].append(MW)
1614 self.get('parameters')[mass.depend].remove(mass)
1615
1616 new_param = ModelVariable('mdl_Gf',
1617 '-mdl_aEW*mdl_MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - mdl_MZ**2))' %\
1618 {'MW': mass.name}, 'complex', mass.depend)
1619 Gf = self.get_parameter('mdl_Gf')
1620 self.get('parameters')[('external',)].remove(Gf)
1621 self.add_param(new_param, ['mdl_aEW'])
1622
1623 mass = MW
1624 elif isinstance(mass, ModelVariable):
1625 logger.warning('W mass is not an external parameter. This is not adviced for the complex mass scheme.')
1626
1627
1628
1629 depend = list(set(mass.depend + width.depend))
1630 if len(depend)>1 and 'external' in depend:
1631 depend.remove('external')
1632 depend = tuple(depend)
1633 if depend == ('external',):
1634 depend = ()
1635
1636
1637 if isinstance(mass, ParamCardVariable):
1638 New_param = ModelVariable('CMASS_'+mass.name,
1639 'cmath.sqrt(%(mass)s**2 - complex(0,1) * %(mass)s * %(width)s)' \
1640 % {'mass': mass.name, 'width': width.name},
1641 'complex', depend)
1642 else:
1643 New_param = ModelVariable('CMASS_'+mass.name,
1644 mass.expr, 'complex', depend)
1645
1646 if not isinstance(width, ParamCardVariable):
1647 width.expr = '- im(%s**2) / cmath.sqrt(re(%s**2))' % (mass.expr, mass.expr)
1648 else:
1649
1650 New_width = ModelVariable(width.name,
1651 '-1 * im(CMASS_%s**2) / %s' % (mass.name, mass.name), 'real', mass.depend)
1652 self.get('parameters')[('external',)].remove(width)
1653 self.add_param(New_param, (mass,))
1654 self.add_param(New_width, (New_param,))
1655 mass.expr = 'cmath.sqrt(re(%s**2))' % mass.expr
1656 to_change[mass.name] = New_param.name
1657 continue
1658
1659 mass.expr = 're(%s)' % mass.expr
1660 self.add_param(New_param, (mass, width))
1661 to_change[mass.name] = New_param.name
1662
1663
1664 yukawas = [p for p in self.get('parameters')[('external',)]
1665 if p.lhablock.lower() == 'yukawa']
1666 for yukawa in yukawas:
1667
1668 self.get('parameters')[('external',)].remove(yukawa)
1669
1670 particle = self.get_particle(yukawa.lhacode[0])
1671 mass = self.get_parameter(particle.get('mass'))
1672
1673
1674 if mass.depend == ('external',):
1675 depend = ()
1676 else:
1677 depend = mass.depend
1678
1679 New_param = ModelVariable(yukawa.name, mass.name, 'real', depend)
1680
1681
1682 if mass.name in to_change:
1683 expr = 'CMASS_%s' % mass.name
1684 else:
1685 expr = mass.name
1686 param_depend = self.get_parameter(expr)
1687 self.add_param(New_param, [param_depend])
1688
1689 if not to_change:
1690 return
1691
1692
1693
1694
1695
1696 pat = '|'.join(to_change.keys())
1697 pat = r'(%s)\b' % pat
1698 pat = re.compile(pat)
1699 def replace(match):
1700 return to_change[match.group()]
1701
1702
1703 for dep, list_param in self['parameters'].items():
1704 for param in list_param:
1705 if param.name.startswith('CMASS_') or param.name in mass_widths or\
1706 isinstance(param, ParamCardVariable):
1707 continue
1708 param.type = 'complex'
1709
1710
1711 param.expr = pat.sub(replace, param.expr)
1712
1713
1714 for dep, list_coup in self['couplings'].items():
1715 for coup in list_coup:
1716 coup.expr = pat.sub(replace, coup.expr)
1717
1718 - def add_param(self, new_param, depend_param):
1719 """add the parameter in the list of parameter in a correct position"""
1720
1721 pos = 0
1722 for i,param in enumerate(self.get('parameters')[new_param.depend]):
1723 if param.name in depend_param:
1724 pos = i + 1
1725 self.get('parameters')[new_param.depend].insert(pos, new_param)
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736 -class ModelVariable(object):
1737 """A Class for storing the information about coupling/ parameter"""
1738
1739 - def __init__(self, name, expression, type, depend=()):
1740 """Initialize a new parameter/coupling"""
1741
1742 self.name = name
1743 self.expr = expression
1744 self.type = type
1745 self.depend = depend
1746 self.value = None
1747
1749 """Object with same name are identical, If the object is a string we check
1750 if the attribute name is equal to this string"""
1751
1752 try:
1753 return other.name == self.name
1754 except Exception:
1755 return other == self.name
1756
1758 """ A class for storing the information linked to all the parameter
1759 which should be define in the param_card.dat"""
1760
1761 depend = ('external',)
1762 type = 'real'
1763
1764 - def __init__(self, name, value, lhablock, lhacode):
1765 """Initialize a new ParamCardVariable
1766 name: name of the variable
1767 value: default numerical value
1768 lhablock: name of the block in the param_card.dat
1769 lhacode: code associate to the variable
1770 """
1771 self.name = name
1772 self.value = value
1773 self.lhablock = lhablock
1774 self.lhacode = lhacode
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785 -class Leg(PhysicsObject):
1786 """Leg object: id (Particle), number, I/F state, flag from_group
1787 """
1788
1790 """Default values for all properties"""
1791
1792 self['id'] = 0
1793 self['number'] = 0
1794
1795 self['state'] = True
1796
1797 self['loop_line'] = False
1798
1799 self['from_group'] = True
1800
1801 self['onshell'] = None
1802
1803 - def filter(self, name, value):
1804 """Filter for valid leg property values."""
1805
1806 if name in ['id', 'number']:
1807 if not isinstance(value, int):
1808 raise self.PhysicsObjectError, \
1809 "%s is not a valid integer for leg id" % str(value)
1810
1811 if name == 'state':
1812 if not isinstance(value, bool):
1813 raise self.PhysicsObjectError, \
1814 "%s is not a valid leg state (True|False)" % \
1815 str(value)
1816
1817 if name == 'from_group':
1818 if not isinstance(value, bool) and value != None:
1819 raise self.PhysicsObjectError, \
1820 "%s is not a valid boolean for leg flag from_group" % \
1821 str(value)
1822
1823 if name == 'loop_line':
1824 if not isinstance(value, bool) and value != None:
1825 raise self.PhysicsObjectError, \
1826 "%s is not a valid boolean for leg flag loop_line" % \
1827 str(value)
1828
1829 if name == 'onshell':
1830 if not isinstance(value, bool) and value != None:
1831 raise self.PhysicsObjectError, \
1832 "%s is not a valid boolean for leg flag onshell" % \
1833 str(value)
1834 return True
1835
1837 """Return particle property names as a nicely sorted list."""
1838
1839 return ['id', 'number', 'state', 'from_group', 'loop_line', 'onshell']
1840
1842 """Returns True if the particle corresponding to the leg is a
1843 fermion"""
1844
1845 assert isinstance(model, Model), "%s is not a model" % str(model)
1846
1847 return model.get('particle_dict')[self['id']].is_fermion()
1848
1850 """Returns True if leg is an incoming fermion, i.e., initial
1851 particle or final antiparticle"""
1852
1853 assert isinstance(model, Model), "%s is not a model" % str(model)
1854
1855 part = model.get('particle_dict')[self['id']]
1856 return part.is_fermion() and \
1857 (self.get('state') == False and part.get('is_part') or \
1858 self.get('state') == True and not part.get('is_part'))
1859
1861 """Returns True if leg is an outgoing fermion, i.e., initial
1862 antiparticle or final particle"""
1863
1864 assert isinstance(model, Model), "%s is not a model" % str(model)
1865
1866 part = model.get('particle_dict')[self['id']]
1867 return part.is_fermion() and \
1868 (self.get('state') == True and part.get('is_part') or \
1869 self.get('state') == False and not part.get('is_part'))
1870
1871
1872
1873
1874 - def same(self, leg):
1875 """ Returns true if the leg in argument has the same ID and the same numer """
1876
1877
1878
1879 if isinstance(leg,int):
1880 if self['number']==leg:
1881 return True
1882 else:
1883 return False
1884
1885
1886
1887 elif isinstance(leg, Leg):
1888 if self['id']==leg.get('id') and \
1889 self['number']==leg.get('number') and \
1890 self['loop_line']==leg.get('loop_line') :
1891 return True
1892 else:
1893 return False
1894
1895 else :
1896 return False
1897
1898
1900 return self['number'] < other['number']
1901
1902
1903
1904
1905 -class LegList(PhysicsObjectList):
1906 """List of Leg objects
1907 """
1908
1910 """Test if object obj is a valid Leg for the list."""
1911
1912 return isinstance(obj, Leg)
1913
1914
1915
1917 """Return all elements which have 'from_group' True"""
1918
1919 return filter(lambda leg: leg.get('from_group'), self)
1920
1922 """Return True if at least one element has 'from_group' True"""
1923
1924 return len(self.from_group_elements()) > 0
1925
1927 """Return True if at least two elements have 'from_group' True"""
1928
1929 return len(self.from_group_elements()) > 1
1930
1932 """If has at least one 'from_group' True and in ref_dict_to1,
1933 return the return list from ref_dict_to1, otherwise return False"""
1934 if self.minimum_one_from_group():
1935 return ref_dict_to1.has_key(tuple(sorted([leg.get('id') for leg in self])))
1936 else:
1937 return False
1938
1940 """If has at least two 'from_group' True and in ref_dict_to0,
1941
1942 return the vertex (with id from ref_dict_to0), otherwise return None
1943
1944 If is_decay_chain = True, we only allow clustering of the
1945 initial leg, since we want this to be the last wavefunction to
1946 be evaluated.
1947 """
1948 if is_decay_chain:
1949
1950
1951
1952
1953 return any(leg.get('from_group') == None for leg in self) and \
1954 ref_dict_to0.has_key(tuple(sorted([leg.get('id') \
1955 for leg in self])))
1956
1957 if self.minimum_two_from_group():
1958 return ref_dict_to0.has_key(tuple(sorted([leg.get('id') for leg in self])))
1959 else:
1960 return False
1961
1963 """Returns the list of ids corresponding to the leglist with
1964 all particles outgoing"""
1965
1966 res = []
1967
1968 assert isinstance(model, Model), "Error! model not model"
1969
1970
1971 for leg in self:
1972 if leg.get('state') == False:
1973 res.append(model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
1974 else:
1975 res.append(leg.get('id'))
1976
1977 return res
1978
1979 - def sort(self,*args, **opts):
1980 """Match with FKSLegList"""
1981 Opts=copy.copy(opts)
1982 if 'pert' in Opts.keys():
1983 del Opts['pert']
1984 return super(LegList,self).sort(*args, **Opts)
1985
1986
1987
1988
1989
1990 -class MultiLeg(PhysicsObject):
1991 """MultiLeg object: ids (Particle or particles), I/F state
1992 """
1993
1995 """Default values for all properties"""
1996
1997 self['ids'] = []
1998 self['state'] = True
1999
2000 - def filter(self, name, value):
2001 """Filter for valid multileg property values."""
2002
2003 if name == 'ids':
2004 if not isinstance(value, list):
2005 raise self.PhysicsObjectError, \
2006 "%s is not a valid list" % str(value)
2007 for i in value:
2008 if not isinstance(i, int):
2009 raise self.PhysicsObjectError, \
2010 "%s is not a valid list of integers" % str(value)
2011
2012 if name == 'state':
2013 if not isinstance(value, bool):
2014 raise self.PhysicsObjectError, \
2015 "%s is not a valid leg state (initial|final)" % \
2016 str(value)
2017
2018 return True
2019
2021 """Return particle property names as a nicely sorted list."""
2022
2023 return ['ids', 'state']
2024
2029 """List of MultiLeg objects
2030 """
2031
2033 """Test if object obj is a valid MultiLeg for the list."""
2034
2035 return isinstance(obj, MultiLeg)
2036
2037
2038
2039
2040 -class Vertex(PhysicsObject):
2041 """Vertex: list of legs (ordered), id (Interaction)
2042 """
2043
2044 sorted_keys = ['id', 'legs']
2045
2047 """Default values for all properties"""
2048
2049 self['id'] = 0
2050 self['legs'] = LegList()
2051
2052 - def filter(self, name, value):
2053 """Filter for valid vertex property values."""
2054
2055 if name == 'id':
2056 if not isinstance(value, int):
2057 raise self.PhysicsObjectError, \
2058 "%s is not a valid integer for vertex id" % str(value)
2059
2060 if name == 'legs':
2061 if not isinstance(value, LegList):
2062 raise self.PhysicsObjectError, \
2063 "%s is not a valid LegList object" % str(value)
2064
2065 return True
2066
2068 """Return particle property names as a nicely sorted list."""
2069
2070 return self.sorted_keys
2071
2073 """Returns the id for the last leg as an outgoing
2074 s-channel. Returns 0 if leg is t-channel, or if identity
2075 vertex. Used to check for required and forbidden s-channel
2076 particles."""
2077
2078 leg = self.get('legs')[-1]
2079
2080 if ninitial == 1:
2081
2082
2083 if leg.get('state') == True:
2084 return leg.get('id')
2085 else:
2086 return model.get('particle_dict')[leg.get('id')].\
2087 get_anti_pdg_code()
2088
2089
2090 if self.get('id') == 0 or \
2091 leg.get('state') == False:
2092
2093 return 0
2094
2095 if leg.get('loop_line'):
2096
2097 return 0
2098
2099
2100
2101 if leg.get('number') > ninitial:
2102 return leg.get('id')
2103 else:
2104 return model.get('particle_dict')[leg.get('id')].\
2105 get_anti_pdg_code()
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118 -class VertexList(PhysicsObjectList):
2119 """List of Vertex objects
2120 """
2121
2122 orders = {}
2123
2125 """Test if object obj is a valid Vertex for the list."""
2126
2127 return isinstance(obj, Vertex)
2128
2129 - def __init__(self, init_list=None, orders=None):
2130 """Creates a new list object, with an optional dictionary of
2131 coupling orders."""
2132
2133 list.__init__(self)
2134
2135 if init_list is not None:
2136 for object in init_list:
2137 self.append(object)
2138
2139 if isinstance(orders, dict):
2140 self.orders = orders
2141
2142
2143
2144
2145
2146 -class Diagram(PhysicsObject):
2147 """Diagram: list of vertices (ordered)
2148 """
2149
2151 """Default values for all properties"""
2152
2153 self['vertices'] = VertexList()
2154 self['orders'] = {}
2155
2156 - def filter(self, name, value):
2168
2170 """Return particle property names as a nicely sorted list."""
2171
2172 return ['vertices', 'orders']
2173
2175 """Returns a nicely formatted string of the diagram content."""
2176
2177 if self['vertices']:
2178 mystr = '('
2179 for vert in self['vertices']:
2180 mystr = mystr + '('
2181 for leg in vert['legs'][:-1]:
2182 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
2183
2184 if self['vertices'].index(vert) < len(self['vertices']) - 1:
2185
2186 mystr = mystr[:-1] + '>'
2187 mystr = mystr + str(vert['legs'][-1]['number']) + '(%s)' % str(vert['legs'][-1]['id']) + ','
2188 mystr = mystr + 'id:' + str(vert['id']) + '),'
2189 mystr = mystr[:-1] + ')'
2190 mystr += " (%s)" % ",".join(["%s=%d" % (key, self['orders'][key]) \
2191 for key in sorted(self['orders'].keys())])
2192 return mystr
2193 else:
2194 return '()'
2195
2197 """Calculate the actual coupling orders of this diagram. Note
2198 that the special order WEIGTHED corresponds to the sum of
2199 hierarchys for the couplings."""
2200
2201 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
2202 weight = 0
2203 for vertex in self['vertices']:
2204 if vertex.get('id') in [0,-1]: continue
2205 couplings = model.get('interaction_dict')[vertex.get('id')].\
2206 get('orders')
2207 for coupling in couplings:
2208 coupling_orders[coupling] += couplings[coupling]
2209 weight += sum([model.get('order_hierarchy')[c]*n for \
2210 (c,n) in couplings.items()])
2211 coupling_orders['WEIGHTED'] = weight
2212 self.set('orders', coupling_orders)
2213
2216 """ Returns wether the contributiong consisting in the current diagram
2217 multiplied by diag_multiplier passes the *positive* squared_orders
2218 specified ( a dictionary ) of types sq_order_types (a dictionary whose
2219 values are the relational operator used to define the constraint of the
2220 order in key)."""
2221
2222 for order, value in squared_orders.items():
2223 if value<0:
2224 continue
2225 combined_order = self.get_order(order) + \
2226 diag_multiplier.get_order(order)
2227 if ( sq_orders_types[order]=='==' and combined_order != value ) or \
2228 ( sq_orders_types[order] in ['=', '<='] and combined_order > value) or \
2229 ( sq_orders_types[order]=='>' and combined_order <= value) :
2230 return False
2231 return True
2232
2234 """Return the order of this diagram. It returns 0 if it is not present."""
2235
2236 try:
2237 return self['orders'][order]
2238 except Exception:
2239 return 0
2240
2242 """Renumber legs in all vertices according to perm_map"""
2243 vertices = VertexList()
2244 min_dict = copy.copy(perm_map)
2245
2246 state_dict = dict([(l.get('number'), l.get('state')) for l in leg_list])
2247
2248 for vertex in self.get('vertices')[:-1]:
2249 vertex = copy.copy(vertex)
2250 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2251 for leg in leg_list[:-1]:
2252 leg.set('number', min_dict[leg.get('number')])
2253 leg.set('state', state_dict[leg.get('number')])
2254 min_number = min([leg.get('number') for leg in leg_list[:-1]])
2255 leg = leg_list[-1]
2256 min_dict[leg.get('number')] = min_number
2257
2258
2259 state_dict[min_number] = len([l for l in leg_list[:-1] if \
2260 not l.get('state')]) != 1
2261 leg.set('number', min_number)
2262 leg.set('state', state_dict[min_number])
2263 vertex.set('legs', leg_list)
2264 vertices.append(vertex)
2265
2266 vertex = copy.copy(self.get('vertices')[-1])
2267 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2268 for leg in leg_list:
2269 leg.set('number', min_dict[leg.get('number')])
2270 leg.set('state', state_dict[leg.get('number')])
2271 vertex.set('legs', leg_list)
2272 vertices.append(vertex)
2273
2274 new_diag = copy.copy(self)
2275 new_diag.set('vertices', vertices)
2276 state_dict = {True:'T',False:'F'}
2277 return new_diag
2278
2280 """Return a list of the number of legs in the vertices for
2281 this diagram"""
2282
2283 return [len(v.get('legs')) for v in self.get('vertices')]
2284
2286 """Return the maximum number of configs from this diagram,
2287 given by 2^(number of non-zero width s-channel propagators)"""
2288
2289 s_channels = [v.get_s_channel_id(model,ninitial) for v in \
2290 self.get('vertices')[:-1]]
2291 num_props = len([i for i in s_channels if i != 0 and \
2292 model.get_particle(i).get('width').lower() != 'zero'])
2293
2294 if num_props < 1:
2295 return 1
2296 else:
2297 return 2**num_props
2298
2300 """return the difference of total diff of charge occuring on the
2301 lofw of the initial parton. return [None,None] if the two initial parton
2302 are connected and the (partial) value if None if the initial parton is
2303 not a fermiom"""
2304
2305 import madgraph.core.drawing as drawing
2306 drawdiag = drawing.FeynmanDiagram(self, model)
2307 drawdiag.load_diagram()
2308 out = []
2309
2310 for v in drawdiag.initial_vertex:
2311 init_part = v.lines[0]
2312 if not init_part.is_fermion():
2313 out.append(None)
2314 continue
2315
2316 init_charge = model.get_particle(init_part.id).get('charge')
2317
2318 l_last = init_part
2319 v_last = v
2320 vcurrent = l_last.end
2321 if vcurrent == v:
2322 vcurrent = l_last.begin
2323 security =0
2324 while not vcurrent.is_external():
2325 if security > 1000:
2326 raise Exception, 'wrong diagram'
2327 next_l = [l for l in vcurrent.lines if l is not l_last and l.is_fermion()][0]
2328 next_v = next_l.end
2329 if next_v == vcurrent:
2330 next_v = next_l.begin
2331 l_last, vcurrent = next_l, next_v
2332 if vcurrent in drawdiag.initial_vertex:
2333 return [None, None]
2334
2335 out.append(model.get_particle(l_last.id).get('charge') - init_charge)
2336 return out
2337
2338
2339
2340
2341
2342 -class DiagramList(PhysicsObjectList):
2343 """List of Diagram objects
2344 """
2345
2347 """Test if object obj is a valid Diagram for the list."""
2348
2349 return isinstance(obj, Diagram)
2350
2352 """Returns a nicely formatted string"""
2353 mystr = " " * indent + str(len(self)) + ' diagrams:\n'
2354 for i, diag in enumerate(self):
2355 mystr = mystr + " " * indent + str(i+1) + " " + \
2356 diag.nice_string() + '\n'
2357 return mystr[:-1]
2358
2359
2360
2362 """ Return the order of the diagram in the list with the maximum coupling
2363 order for the coupling specified """
2364 max_order=-1
2365
2366 for diag in self:
2367 if order in diag['orders'].keys():
2368 if max_order==-1 or diag['orders'][order] > max_order:
2369 max_order = diag['orders'][order]
2370
2371 return max_order
2372
2374 """ This function returns a fitlered version of the diagram list self
2375 which satisfy the negative squared_order constraint 'order' with negative
2376 value 'value' and of type 'order_type', assuming that the diagram_list
2377 it must be squared against is 'reg_diag_list'. It also returns the
2378 new postive target squared order which correspond to this negative order
2379 constraint. Example: u u~ > d d~ QED^2<=-2 means that one wants to
2380 pick terms only up to the the next-to-leading order contributiong in QED,
2381 which is QED=2 in this case, so that target_order=4 is returned."""
2382
2383
2384 target_order = min(ref_diag_list.get_order_values(order))+\
2385 min(self.get_order_values(order))+2*(-value-1)
2386
2387 new_list = self.apply_positive_sq_orders(ref_diag_list,
2388 {order:target_order}, {order:order_type})
2389
2390 return new_list, target_order
2391
2393 """ This function returns a filtered version of self which contain
2394 only the diagram which satisfy the positive squared order constraints
2395 sq_orders of type sq_order_types and assuming that the diagrams are
2396 multiplied with those of the reference diagram list ref_diag_list."""
2397
2398 new_diag_list = DiagramList()
2399 for tested_diag in self:
2400 for ref_diag in ref_diag_list:
2401 if tested_diag.pass_squared_order_constraints(ref_diag,
2402 sq_orders,sq_order_types):
2403 new_diag_list.append(tested_diag)
2404 break
2405 return new_diag_list
2406
2408 """ Return the order of the diagram in the list with the mimimum coupling
2409 order for the coupling specified """
2410 min_order=-1
2411 for diag in self:
2412 if order in diag['orders'].keys():
2413 if min_order==-1 or diag['orders'][order] < min_order:
2414 min_order = diag['orders'][order]
2415 else:
2416 return 0
2417
2418 return min_order
2419
2421 """ Return the list of possible values appearing in the diagrams of this
2422 list for the order given in argument """
2423
2424 values=set([])
2425 for diag in self:
2426 if order in diag['orders'].keys():
2427 values.add(diag['orders'][order])
2428 else:
2429 values.add(0)
2430
2431 return list(values)
2432
2433
2434
2435
2436 -class Process(PhysicsObject):
2437 """Process: list of legs (ordered)
2438 dictionary of orders
2439 model
2440 process id
2441 """
2442
2444 """Default values for all properties"""
2445
2446 self['legs'] = LegList()
2447
2448 self['orders'] = {}
2449 self['model'] = Model()
2450
2451 self['id'] = 0
2452 self['uid'] = 0
2453
2454
2455
2456
2457 self['required_s_channels'] = []
2458 self['forbidden_onsh_s_channels'] = []
2459 self['forbidden_s_channels'] = []
2460 self['forbidden_particles'] = []
2461 self['is_decay_chain'] = False
2462 self['overall_orders'] = {}
2463
2464 self['decay_chains'] = ProcessList()
2465
2466 self['legs_with_decays'] = LegList()
2467
2468 self['perturbation_couplings']=[]
2469
2470
2471
2472
2473 self['squared_orders'] = {}
2474
2475
2476
2477
2478 self['sqorders_types'] = {}
2479 self['has_born'] = True
2480
2481
2482 self['NLO_mode'] = 'tree'
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492 self['split_orders'] = []
2493
2494 - def filter(self, name, value):
2495 """Filter for valid process property values."""
2496
2497 if name in ['legs', 'legs_with_decays'] :
2498 if not isinstance(value, LegList):
2499 raise self.PhysicsObjectError, \
2500 "%s is not a valid LegList object" % str(value)
2501
2502 if name in ['orders', 'overall_orders','squared_orders']:
2503 Interaction.filter(Interaction(), 'orders', value)
2504
2505 if name == 'sqorders_types':
2506 if not isinstance(value, dict):
2507 raise self.PhysicsObjectError, \
2508 "%s is not a valid dictionary" % str(value)
2509 for order in value.keys()+value.values():
2510 if not isinstance(order, str):
2511 raise self.PhysicsObjectError, \
2512 "%s is not a valid string" % str(value)
2513
2514 if name == 'split_orders':
2515 if not isinstance(value, list):
2516 raise self.PhysicsObjectError, \
2517 "%s is not a valid list" % str(value)
2518 for order in value:
2519 if not isinstance(order, str):
2520 raise self.PhysicsObjectError, \
2521 "%s is not a valid string" % str(value)
2522
2523 if name == 'model':
2524 if not isinstance(value, Model):
2525 raise self.PhysicsObjectError, \
2526 "%s is not a valid Model object" % str(value)
2527 if name in ['id', 'uid']:
2528 if not isinstance(value, int):
2529 raise self.PhysicsObjectError, \
2530 "Process %s %s is not an integer" % (name, repr(value))
2531
2532 if name == 'required_s_channels':
2533 if not isinstance(value, list):
2534 raise self.PhysicsObjectError, \
2535 "%s is not a valid list" % str(value)
2536 for l in value:
2537 if not isinstance(l, list):
2538 raise self.PhysicsObjectError, \
2539 "%s is not a valid list of lists" % str(value)
2540 for i in l:
2541 if not isinstance(i, int):
2542 raise self.PhysicsObjectError, \
2543 "%s is not a valid list of integers" % str(l)
2544 if i == 0:
2545 raise self.PhysicsObjectError, \
2546 "Not valid PDG code %d for s-channel particle" % i
2547
2548 if name in ['forbidden_onsh_s_channels', 'forbidden_s_channels']:
2549 if not isinstance(value, list):
2550 raise self.PhysicsObjectError, \
2551 "%s is not a valid list" % str(value)
2552 for i in value:
2553 if not isinstance(i, int):
2554 raise self.PhysicsObjectError, \
2555 "%s is not a valid list of integers" % str(value)
2556 if i == 0:
2557 raise self.PhysicsObjectError, \
2558 "Not valid PDG code %d for s-channel particle" % str(value)
2559
2560 if name == 'forbidden_particles':
2561 if not isinstance(value, list):
2562 raise self.PhysicsObjectError, \
2563 "%s is not a valid list" % str(value)
2564 for i in value:
2565 if not isinstance(i, int):
2566 raise self.PhysicsObjectError, \
2567 "%s is not a valid list of integers" % str(value)
2568 if i <= 0:
2569 raise self.PhysicsObjectError, \
2570 "Forbidden particles should have a positive PDG code" % str(value)
2571
2572 if name == 'perturbation_couplings':
2573 if not isinstance(value, list):
2574 raise self.PhysicsObjectError, \
2575 "%s is not a valid list" % str(value)
2576 for order in value:
2577 if not isinstance(order, str):
2578 raise self.PhysicsObjectError, \
2579 "%s is not a valid string" % str(value)
2580
2581 if name == 'is_decay_chain':
2582 if not isinstance(value, bool):
2583 raise self.PhysicsObjectError, \
2584 "%s is not a valid bool" % str(value)
2585
2586 if name == 'has_born':
2587 if not isinstance(value, bool):
2588 raise self.PhysicsObjectError, \
2589 "%s is not a valid bool" % str(value)
2590
2591 if name == 'decay_chains':
2592 if not isinstance(value, ProcessList):
2593 raise self.PhysicsObjectError, \
2594 "%s is not a valid ProcessList" % str(value)
2595
2596 if name == 'NLO_mode':
2597 if value not in ['real','all','virt','tree']:
2598 raise self.PhysicsObjectError, \
2599 "%s is not a valid NLO_mode" % str(value)
2600 return True
2601
2603 """ A process, not being a ProcessDefinition never carries multiple
2604 particles labels"""
2605
2606 return False
2607
2608 - def set(self, name, value):
2609 """Special set for forbidden particles - set to abs value."""
2610
2611 if name == 'forbidden_particles':
2612 try:
2613 value = [abs(i) for i in value]
2614 except Exception:
2615 pass
2616
2617 if name == 'required_s_channels':
2618
2619 if value and isinstance(value, list) and \
2620 not isinstance(value[0], list):
2621 value = [value]
2622
2623 return super(Process, self).set(name, value)
2624
2626 """ Return what kind of squared order constraint was specified for the
2627 order 'order'."""
2628
2629 if order in self['sqorders_types'].keys():
2630 return self['sqorders_types'][order]
2631 else:
2632
2633 return '='
2634
2635 - def get(self, name):
2636 """Special get for legs_with_decays"""
2637
2638 if name == 'legs_with_decays':
2639 self.get_legs_with_decays()
2640
2641 if name == 'sqorders_types':
2642
2643 for order in self['squared_orders'].keys():
2644 if order not in self['sqorders_types']:
2645
2646 self['sqorders_types'][order]='='
2647
2648 return super(Process, self).get(name)
2649
2651 """Return process property names as a nicely sorted list."""
2652
2653 return ['legs', 'orders', 'overall_orders', 'squared_orders',
2654 'model', 'id', 'required_s_channels',
2655 'forbidden_onsh_s_channels', 'forbidden_s_channels',
2656 'forbidden_particles', 'is_decay_chain', 'decay_chains',
2657 'legs_with_decays', 'perturbation_couplings', 'has_born',
2658 'NLO_mode','split_orders']
2659
2660 - def nice_string(self, indent=0, print_weighted = True):
2661 """Returns a nicely formated string about current process
2662 content. Since the WEIGHTED order is automatically set and added to
2663 the user-defined list of orders, it can be ommitted for some info
2664 displays."""
2665
2666 mystr = " " * indent + "Process: "
2667 prevleg = None
2668 for leg in self['legs']:
2669 mypart = self['model'].get('particle_dict')[leg['id']]
2670 if prevleg and prevleg['state'] == False \
2671 and leg['state'] == True:
2672
2673 mystr = mystr + '> '
2674
2675 if self['required_s_channels'] and \
2676 self['required_s_channels'][0]:
2677 mystr += "|".join([" ".join([self['model'].\
2678 get('particle_dict')[req_id].get_name() \
2679 for req_id in id_list]) \
2680 for id_list in self['required_s_channels']])
2681 mystr = mystr + ' > '
2682
2683 mystr = mystr + mypart.get_name() + ' '
2684
2685 prevleg = leg
2686
2687
2688 if self['orders']:
2689 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
2690 for key in self['orders'] if (print_weighted or key!='WEIGHTED') \
2691 and not key in self['squared_orders'].keys()]) + ' '
2692
2693
2694 if self['perturbation_couplings']:
2695 mystr = mystr + '[ '
2696 if self['NLO_mode']!='tree':
2697 mystr = mystr + self['NLO_mode'] + ' = '
2698 for order in self['perturbation_couplings']:
2699 mystr = mystr + order + ' '
2700 mystr = mystr + '] '
2701
2702
2703 if self['squared_orders']:
2704 mystr = mystr + " ".join([key + '^2%s%d'%\
2705 (self.get_squared_order_type(key),self['squared_orders'][key]) \
2706 for key in self['squared_orders'].keys() \
2707 if print_weighted or key!='WEIGHTED']) + ' '
2708
2709
2710 if self['forbidden_onsh_s_channels']:
2711 mystr = mystr + '$ '
2712 for forb_id in self['forbidden_onsh_s_channels']:
2713 forbpart = self['model'].get('particle_dict')[forb_id]
2714 mystr = mystr + forbpart.get_name() + ' '
2715
2716
2717 if self['forbidden_s_channels']:
2718 mystr = mystr + '$$ '
2719 for forb_id in self['forbidden_s_channels']:
2720 forbpart = self['model'].get('particle_dict')[forb_id]
2721 mystr = mystr + forbpart.get_name() + ' '
2722
2723
2724 if self['forbidden_particles']:
2725 mystr = mystr + '/ '
2726 for forb_id in self['forbidden_particles']:
2727 forbpart = self['model'].get('particle_dict')[forb_id]
2728 mystr = mystr + forbpart.get_name() + ' '
2729
2730
2731 mystr = mystr[:-1]
2732
2733 if self.get('id') or self.get('overall_orders'):
2734 mystr += " @%d" % self.get('id')
2735 if self.get('overall_orders'):
2736 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
2737 for key in sorted(self['orders'])]) + ' '
2738
2739 if not self.get('decay_chains'):
2740 return mystr
2741
2742 for decay in self['decay_chains']:
2743 mystr = mystr + '\n' + \
2744 decay.nice_string(indent + 2).replace('Process', 'Decay')
2745
2746 return mystr
2747
2838
2840 """Returns a string containing only the basic process (w/o decays)."""
2841
2842 mystr = ""
2843 prevleg = None
2844 for leg in self.get_legs_with_decays():
2845 mypart = self['model'].get('particle_dict')[leg['id']]
2846 if prevleg and prevleg['state'] == False \
2847 and leg['state'] == True:
2848
2849 mystr = mystr + '> '
2850 mystr = mystr + mypart.get_name() + ' '
2851 prevleg = leg
2852
2853
2854 return mystr[:-1]
2855
2856 - def shell_string(self, schannel=True, forbid=True, main=True, pdg_order=False):
2857 """Returns process as string with '~' -> 'x', '>' -> '_',
2858 '+' -> 'p' and '-' -> 'm', including process number,
2859 intermediate s-channels and forbidden particles,
2860 pdg_order allow to order to leg order by pid."""
2861
2862 mystr = ""
2863 if not self.get('is_decay_chain'):
2864 mystr += "%d_" % self['id']
2865
2866 prevleg = None
2867 if pdg_order:
2868 legs = [l for l in self['legs'][1:]]
2869 def order_leg(l1,l2):
2870 id1 = l1.get('id')
2871 id2 = l2.get('id')
2872 return id2-id1
2873 legs.sort(cmp=order_leg)
2874 legs.insert(0, self['legs'][0])
2875 else:
2876 legs = self['legs']
2877
2878
2879 for leg in legs:
2880 mypart = self['model'].get('particle_dict')[leg['id']]
2881 if prevleg and prevleg['state'] == False \
2882 and leg['state'] == True:
2883
2884 mystr = mystr + '_'
2885
2886 if self['required_s_channels'] and \
2887 self['required_s_channels'][0] and schannel:
2888 mystr += "_or_".join(["".join([self['model'].\
2889 get('particle_dict')[req_id].get_name() \
2890 for req_id in id_list]) \
2891 for id_list in self['required_s_channels']])
2892 mystr = mystr + '_'
2893 if mypart['is_part']:
2894 mystr = mystr + mypart['name']
2895 else:
2896 mystr = mystr + mypart['antiname']
2897 prevleg = leg
2898
2899
2900 if self['forbidden_particles'] and forbid:
2901 mystr = mystr + '_no_'
2902 for forb_id in self['forbidden_particles']:
2903 forbpart = self['model'].get('particle_dict')[forb_id]
2904 mystr = mystr + forbpart.get_name()
2905
2906
2907 mystr = mystr.replace('~', 'x')
2908
2909 mystr = mystr.replace('+', 'p')
2910
2911 mystr = mystr.replace('-', 'm')
2912
2913 mystr = mystr.replace(' ', '')
2914
2915 for decay in self.get('decay_chains'):
2916 mystr = mystr + "_" + decay.shell_string(schannel,forbid, main=False,
2917 pdg_order=pdg_order)
2918
2919
2920 if len(mystr) > 64 and main:
2921 if schannel and forbid:
2922 out = self.shell_string(True, False, True, pdg_order)
2923 elif schannel:
2924 out = self.shell_string(False, False, True, pdg_order)
2925 else:
2926 out = mystr[:64]
2927 if not out.endswith('_%s' % self['uid']):
2928 out += '_%s' % self['uid']
2929 return out
2930
2931 return mystr
2932
2934 """Returns process as v4-compliant string with '~' -> 'x' and
2935 '>' -> '_'"""
2936
2937 mystr = "%d_" % self['id']
2938 prevleg = None
2939 for leg in self.get_legs_with_decays():
2940 mypart = self['model'].get('particle_dict')[leg['id']]
2941 if prevleg and prevleg['state'] == False \
2942 and leg['state'] == True:
2943
2944 mystr = mystr + '_'
2945 if mypart['is_part']:
2946 mystr = mystr + mypart['name']
2947 else:
2948 mystr = mystr + mypart['antiname']
2949 prevleg = leg
2950
2951
2952 mystr = mystr.replace('~', 'x')
2953
2954 mystr = mystr.replace(' ', '')
2955
2956 return mystr
2957
2958
2959
2961 """ Check iteratively that no coupling order constraint include negative
2962 values."""
2963
2964 if any(val<0 for val in self.get('orders').values()+\
2965 self.get('squared_orders').values()):
2966 return True
2967
2968 for procdef in self['decay_chains']:
2969 if procdef.are_negative_orders_present():
2970 return True
2971
2972 return False
2973
2975 """ Check iteratively that the decayed processes are not perturbed """
2976
2977 for procdef in self['decay_chains']:
2978 if procdef['perturbation_couplings'] or procdef.are_decays_perturbed():
2979 return True
2980 return False
2981
2983 """ Check iteratively that the decayed processes are not perturbed """
2984
2985 for procdef in self['decay_chains']:
2986 if procdef['squared_orders']!={} or procdef.decays_have_squared_orders():
2987 return True
2988 return False
2989
2991 """Gives number of initial state particles"""
2992
2993 return len(filter(lambda leg: leg.get('state') == False,
2994 self.get('legs')))
2995
2997 """Gives the pdg codes for initial state particles"""
2998
2999 return [leg.get('id') for leg in \
3000 filter(lambda leg: leg.get('state') == False,
3001 self.get('legs'))]
3002
3004 """Return the pdg codes for initial state particles for beam number"""
3005
3006 return filter(lambda leg: leg.get('state') == False and\
3007 leg.get('number') == number,
3008 self.get('legs'))[0].get('id')
3009
3011 """return a tuple of two tuple containing the id of the initial/final
3012 state particles. Each list is ordered"""
3013
3014 initial = []
3015 final = [l.get('id') for l in self.get('legs')\
3016 if l.get('state') or initial.append(l.get('id'))]
3017 initial.sort()
3018 final.sort()
3019 return (tuple(initial), tuple(final))
3020
3041
3042
3044 """Gives the final state legs"""
3045
3046 return filter(lambda leg: leg.get('state') == True,
3047 self.get('legs'))
3048
3050 """Gives the pdg codes for final state particles"""
3051
3052 return [l.get('id') for l in self.get_final_legs()]
3053
3054
3056 """Return process with all decay chains substituted in."""
3057
3058 if self['legs_with_decays']:
3059 return self['legs_with_decays']
3060
3061 legs = copy.deepcopy(self.get('legs'))
3062 org_decay_chains = copy.copy(self.get('decay_chains'))
3063 sorted_decay_chains = []
3064
3065 for leg in legs:
3066 if not leg.get('state'): continue
3067 org_ids = [l.get('legs')[0].get('id') for l in \
3068 org_decay_chains]
3069 if leg.get('id') in org_ids:
3070 sorted_decay_chains.append(org_decay_chains.pop(\
3071 org_ids.index(leg.get('id'))))
3072 assert not org_decay_chains
3073 ileg = 0
3074 for decay in sorted_decay_chains:
3075 while legs[ileg].get('state') == False or \
3076 legs[ileg].get('id') != decay.get('legs')[0].get('id'):
3077 ileg = ileg + 1
3078 decay_legs = decay.get_legs_with_decays()
3079 legs = legs[:ileg] + decay_legs[1:] + legs[ileg+1:]
3080 ileg = ileg + len(decay_legs) - 1
3081
3082
3083 legs = [copy.copy(l) for l in legs]
3084
3085 for ileg, leg in enumerate(legs):
3086 leg.set('number', ileg + 1)
3087
3088 self['legs_with_decays'] = LegList(legs)
3089
3090 return self['legs_with_decays']
3091
3093 """Output a list that can be compared to other processes as:
3094 [id, sorted(initial leg ids), sorted(final leg ids),
3095 sorted(decay list_for_sorts)]"""
3096
3097 sorted_list = [self.get('id'),
3098 sorted(self.get_initial_ids()),
3099 sorted(self.get_final_ids())]
3100
3101 if self.get('decay_chains'):
3102 sorted_list.extend(sorted([d.list_for_sort() for d in \
3103 self.get('decay_chains')]))
3104
3105 return sorted_list
3106
3108 """Sorting routine which allows to sort processes for
3109 comparison. Compare only process id and legs."""
3110
3111 if self.list_for_sort() > other.list_for_sort():
3112 return 1
3113 if self.list_for_sort() < other.list_for_sort():
3114 return -1
3115 return 0
3116
3118 """Calculate the denominator factor for identical final state particles
3119 """
3120
3121 final_legs = filter(lambda leg: leg.get('state') == True, \
3122 self.get_legs_with_decays())
3123
3124 identical_indices = {}
3125 for leg in final_legs:
3126 if leg.get('id') in identical_indices:
3127 identical_indices[leg.get('id')] = \
3128 identical_indices[leg.get('id')] + 1
3129 else:
3130 identical_indices[leg.get('id')] = 1
3131 return reduce(lambda x, y: x * y, [ math.factorial(val) for val in \
3132 identical_indices.values() ], 1)
3133
3135 """Ensure that maximum expansion orders from the model are
3136 properly taken into account in the process"""
3137
3138
3139 expansion_orders = self.get('model').get('expansion_order')
3140 orders = self.get('orders')
3141 sq_orders = self.get('squared_orders')
3142
3143 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
3144 for (k,v) in tmp:
3145 if k in orders:
3146 if v < orders[k]:
3147 if k in sq_orders.keys() and \
3148 (sq_orders[k]>v or sq_orders[k]<0):
3149 logger.warning(
3150 '''The process with the squared coupling order (%s^2%s%s) specified can potentially
3151 recieve contributions with powers of the coupling %s larger than the maximal
3152 value allowed by the model builder (%s). Hence, MG5_aMC sets the amplitude order
3153 for that coupling to be this maximal one. '''%(k,self.get('sqorders_types')[k],
3154 self.get('squared_orders')[k],k,v))
3155 else:
3156 logger.warning(
3157 '''The coupling order (%s=%s) specified is larger than the one allowed
3158 by the model builder. The maximal value allowed is %s.
3159 We set the %s order to this value''' % (k,orders[k],v,k))
3160 orders[k] = v
3161 else:
3162 orders[k] = v
3163
3165 """Overloading the equality operator, so that only comparison
3166 of process id and legs is being done, using compare_for_sort."""
3167
3168 if not isinstance(other, Process):
3169 return False
3170
3171 return self.compare_for_sort(other) == 0
3172
3174 return not self.__eq__(other)
3175
3180 """List of Process objects
3181 """
3182
3184 """Test if object obj is a valid Process for the list."""
3185
3186 return isinstance(obj, Process)
3187
3189 """Returns a nicely formatted string of the matrix element processes."""
3190
3191 mystr = "\n".join([p.nice_string(indent) for p in self])
3192
3193 return mystr
3194
3199 """ProcessDefinition: list of multilegs (ordered)
3200 dictionary of orders
3201 model
3202 process id
3203 """
3204
3214
3215 - def filter(self, name, value):
3231
3233 """ Check that this process definition will yield a single process, as
3234 each multileg only has one leg"""
3235
3236 for mleg in self['legs']:
3237 if len(mleg['ids'])>1:
3238 return True
3239
3240 return False
3241
3249
3251 """Retrieve the minimum starting guess for WEIGHTED order, to
3252 use in find_optimal_process_orders in MultiProcess diagram
3253 generation (as well as particles and hierarchy). The algorithm:
3254
3255 1) Pick out the legs in the multiprocess according to the
3256 highest hierarchy represented (so don't mix particles from
3257 different hierarchy classes in the same multiparticles!)
3258
3259 2) Find the starting maximum WEIGHTED order as the sum of the
3260 highest n-2 weighted orders
3261
3262 3) Pick out required s-channel particle hierarchies, and use
3263 the highest of the maximum WEIGHTED order from the legs and
3264 the minimum WEIGHTED order extracted from 2*s-channel
3265 hierarchys plus the n-2-2*(number of s-channels) lowest
3266 leg weighted orders.
3267 """
3268
3269 model = self.get('model')
3270
3271
3272
3273 particles, hierarchy = model.get_particles_hierarchy()
3274
3275
3276
3277 max_order_now = []
3278 new_legs = copy.copy(self.get('legs'))
3279 for parts, value in zip(particles, hierarchy):
3280 ileg = 0
3281 while ileg < len(new_legs):
3282 if any([id in parts for id in new_legs[ileg].get('ids')]):
3283 max_order_now.append(value)
3284 new_legs.pop(ileg)
3285 else:
3286 ileg += 1
3287
3288
3289
3290 max_order_now = sorted(max_order_now)[2:]
3291
3292
3293 max_order_prop = []
3294 for idlist in self.get('required_s_channels'):
3295 max_order_prop.append([0,0])
3296 for id in idlist:
3297 for parts, value in zip(particles, hierarchy):
3298 if id in parts:
3299 max_order_prop[-1][0] += 2*value
3300 max_order_prop[-1][1] += 1
3301 break
3302
3303 if max_order_prop:
3304 if len(max_order_prop) >1:
3305 max_order_prop = min(*max_order_prop, key=lambda x:x[0])
3306 else:
3307 max_order_prop = max_order_prop[0]
3308
3309
3310
3311
3312 max_order_now = max(sum(max_order_now),
3313 max_order_prop[0] + \
3314 sum(max_order_now[:-2 * max_order_prop[1]]))
3315 else:
3316 max_order_now = sum(max_order_now)
3317
3318 return max_order_now, particles, hierarchy
3319
3320 - def nice_string(self, indent=0, print_weighted=False):
3321 """Returns a nicely formated string about current process
3322 content"""
3323
3324 mystr = " " * indent + "Process: "
3325 prevleg = None
3326 for leg in self['legs']:
3327 myparts = \
3328 "/".join([self['model'].get('particle_dict')[id].get_name() \
3329 for id in leg.get('ids')])
3330 if prevleg and prevleg['state'] == False \
3331 and leg['state'] == True:
3332
3333 mystr = mystr + '> '
3334
3335 if self['required_s_channels'] and \
3336 self['required_s_channels'][0]:
3337 mystr += "|".join([" ".join([self['model'].\
3338 get('particle_dict')[req_id].get_name() \
3339 for req_id in id_list]) \
3340 for id_list in self['required_s_channels']])
3341 mystr = mystr + '> '
3342
3343 mystr = mystr + myparts + ' '
3344
3345 prevleg = leg
3346
3347
3348 if self['forbidden_onsh_s_channels']:
3349 mystr = mystr + '$ '
3350 for forb_id in self['forbidden_onsh_s_channels']:
3351 forbpart = self['model'].get('particle_dict')[forb_id]
3352 mystr = mystr + forbpart.get_name() + ' '
3353
3354
3355 if self['forbidden_s_channels']:
3356 mystr = mystr + '$$ '
3357 for forb_id in self['forbidden_s_channels']:
3358 forbpart = self['model'].get('particle_dict')[forb_id]
3359 mystr = mystr + forbpart.get_name() + ' '
3360
3361
3362 if self['forbidden_particles']:
3363 mystr = mystr + '/ '
3364 for forb_id in self['forbidden_particles']:
3365 forbpart = self['model'].get('particle_dict')[forb_id]
3366 mystr = mystr + forbpart.get_name() + ' '
3367
3368 if self['orders']:
3369 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
3370 for key in sorted(self['orders'])]) + ' '
3371
3372
3373 if self['perturbation_couplings']:
3374 mystr = mystr + '[ '
3375 if self['NLO_mode']:
3376 mystr = mystr + self['NLO_mode'] + ' = '
3377 for order in self['perturbation_couplings']:
3378 mystr = mystr + order + ' '
3379 mystr = mystr + '] '
3380
3381 if self['squared_orders']:
3382 mystr = mystr + " ".join([key + '^2%s%d'%\
3383 (self.get_squared_order_type(key),self['squared_orders'][key]) \
3384 for key in self['squared_orders'].keys() \
3385 if print_weighted or key!='WEIGHTED']) + ' '
3386
3387
3388 mystr = mystr[:-1]
3389
3390 if self.get('id') or self.get('overall_orders'):
3391 mystr += " @%d" % self.get('id')
3392 if self.get('overall_orders'):
3393 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3394 for key in sorted(self['orders'])]) + ' '
3395
3396 if not self.get('decay_chains'):
3397 return mystr
3398
3399 for decay in self['decay_chains']:
3400 mystr = mystr + '\n' + \
3401 decay.nice_string(indent + 2).replace('Process', 'Decay')
3402
3403 return mystr
3404
3406 """ Return a Process object which has the same properties of this
3407 ProcessDefinition but with the specified LegList as legs attribute.
3408 """
3409
3410 return Process({\
3411 'legs': LegList,
3412 'model':self.get('model'),
3413 'id': self.get('id'),
3414 'orders': self.get('orders'),
3415 'sqorders_types': self.get('sqorders_types'),
3416 'squared_orders': self.get('squared_orders'),
3417 'has_born': self.get('has_born'),
3418 'required_s_channels': self.get('required_s_channels'),
3419 'forbidden_onsh_s_channels': self.get('forbidden_onsh_s_channels'),
3420 'forbidden_s_channels': self.get('forbidden_s_channels'),
3421 'forbidden_particles': self.get('forbidden_particles'),
3422 'perturbation_couplings': self.get('perturbation_couplings'),
3423 'is_decay_chain': self.get('is_decay_chain'),
3424 'overall_orders': self.get('overall_orders'),
3425 'split_orders': self.get('split_orders'),
3426 'NLO_mode': self.get('NLO_mode')
3427 })
3428
3429 - def get_process(self, initial_state_ids, final_state_ids):
3430 """ Return a Process object which has the same properties of this
3431 ProcessDefinition but with the specified given leg ids. """
3432
3433
3434
3435 my_isids = [leg.get('ids') for leg in self.get('legs') \
3436 if not leg.get('state')]
3437 my_fsids = [leg.get('ids') for leg in self.get('legs') \
3438 if leg.get('state')]
3439 for i, is_id in enumerate(initial_state_ids):
3440 assert is_id in my_isids[i]
3441 for i, fs_id in enumerate(final_state_ids):
3442 assert fs_id in my_fsids[i]
3443
3444 return self.get_process_with_legs(LegList(\
3445 [Leg({'id': id, 'state':False}) for id in initial_state_ids] + \
3446 [Leg({'id': id, 'state':True}) for id in final_state_ids]))
3447
3449 """Overloading the equality operator, so that only comparison
3450 of process id and legs is being done, using compare_for_sort."""
3451
3452 return super(Process, self).__eq__(other)
3453
3458 """List of ProcessDefinition objects
3459 """
3460
3462 """Test if object obj is a valid ProcessDefinition for the list."""
3463
3464 return isinstance(obj, ProcessDefinition)
3465
3471 """Make sure there are no doublets in the list doubletlist.
3472 Note that this is a slow implementation, so don't use if speed
3473 is needed"""
3474
3475 assert isinstance(doubletlist, list), \
3476 "Argument to make_unique must be list"
3477
3478
3479 uniquelist = []
3480 for elem in doubletlist:
3481 if elem not in uniquelist:
3482 uniquelist.append(elem)
3483
3484 doubletlist[:] = uniquelist[:]
3485