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(): part.partial_widths[key] = rep_pattern.sub(replace, value)
1517
1518
1519 self['particle_dict'] =''
1520 self.get('particle_dict')
1521
1522
1523
1525 """Return the first positive number that is not a valid PDG code"""
1526 return [c for c in range(1, len(self.get('particles')) + 1) if \
1527 c not in self.get('particle_dict').keys()][0]
1528
1538
1539 @ staticmethod
1541 """ load the default for name convention """
1542
1543 logger.info('Change particles name to pass to MG5 convention')
1544 default = {}
1545 for line in open(os.path.join(MG5DIR, 'input', \
1546 'particles_name_default.txt')):
1547 line = line.lstrip()
1548 if line.startswith('#'):
1549 continue
1550
1551 args = line.split()
1552 if len(args) != 2:
1553 logger.warning('Invalid syntax in interface/default_name:\n %s' % line)
1554 continue
1555 default[int(args[0])] = args[1].lower()
1556
1557 return default
1558
1560 """modify the expression changing the mass to complex mass scheme"""
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572 to_change = {}
1573 mass_widths = []
1574 for particle in self.get('particles'):
1575 m = particle.get('width')
1576 if m in mass_widths:
1577 continue
1578 mass_widths.append(particle.get('width'))
1579 mass_widths.append(particle.get('mass'))
1580 if particle.get('width') == 'ZERO':
1581
1582 continue
1583 width = self.get_parameter(particle.get('width'))
1584 if not isinstance(width, ParamCardVariable):
1585 width.expr = 're(%s)' % width.expr
1586 if particle.get('mass') != 'ZERO':
1587 mass = self.get_parameter(particle.get('mass'))
1588
1589 if particle.get('pdg_code') == 24:
1590 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)))':
1591
1592 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1593 if not MW.value:
1594 MW.value = 80.385
1595 self.get('parameters')[('external',)].append(MW)
1596 self.get('parameters')[mass.depend].remove(mass)
1597
1598 new_param = ModelVariable('Gf',
1599 '-aEW*MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - MZ**2))' %\
1600 {'MW': mass.name}, 'complex', mass.depend)
1601 Gf = self.get_parameter('Gf')
1602 self.get('parameters')[('external',)].remove(Gf)
1603 self.add_param(new_param, ['aEW'])
1604
1605 mass = MW
1606
1607 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)))':
1608
1609 MW = ParamCardVariable(mass.name, mass.value, 'MASS', [24])
1610 if not MW.value:
1611 MW.value = 80.385
1612 self.get('parameters')[('external',)].append(MW)
1613 self.get('parameters')[mass.depend].remove(mass)
1614
1615 new_param = ModelVariable('mdl_Gf',
1616 '-mdl_aEW*mdl_MZ**2*cmath.pi/(cmath.sqrt(2)*%(MW)s**2*(%(MW)s**2 - mdl_MZ**2))' %\
1617 {'MW': mass.name}, 'complex', mass.depend)
1618 Gf = self.get_parameter('mdl_Gf')
1619 self.get('parameters')[('external',)].remove(Gf)
1620 self.add_param(new_param, ['mdl_aEW'])
1621
1622 mass = MW
1623 elif isinstance(mass, ModelVariable):
1624 logger.warning('W mass is not an external parameter. This is not adviced for the complex mass scheme.')
1625
1626
1627
1628 depend = list(set(mass.depend + width.depend))
1629 if len(depend)>1 and 'external' in depend:
1630 depend.remove('external')
1631 depend = tuple(depend)
1632 if depend == ('external',):
1633 depend = ()
1634
1635
1636 if isinstance(mass, ParamCardVariable):
1637 New_param = ModelVariable('CMASS_'+mass.name,
1638 'cmath.sqrt(%(mass)s**2 - complex(0,1) * %(mass)s * %(width)s)' \
1639 % {'mass': mass.name, 'width': width.name},
1640 'complex', depend)
1641 else:
1642 New_param = ModelVariable('CMASS_'+mass.name,
1643 mass.expr, 'complex', depend)
1644
1645 if not isinstance(width, ParamCardVariable):
1646 width.expr = '- im(%s**2) / cmath.sqrt(re(%s**2))' % (mass.expr, mass.expr)
1647 else:
1648
1649 New_width = ModelVariable(width.name,
1650 '-1 * im(CMASS_%s**2) / %s' % (mass.name, mass.name), 'real', mass.depend)
1651 self.get('parameters')[('external',)].remove(width)
1652 self.add_param(New_param, (mass,))
1653 self.add_param(New_width, (New_param,))
1654 mass.expr = 'cmath.sqrt(re(%s**2))' % mass.expr
1655 to_change[mass.name] = New_param.name
1656 continue
1657
1658 mass.expr = 're(%s)' % mass.expr
1659 self.add_param(New_param, (mass, width))
1660 to_change[mass.name] = New_param.name
1661
1662
1663 yukawas = [p for p in self.get('parameters')[('external',)]
1664 if p.lhablock.lower() == 'yukawa']
1665 for yukawa in yukawas:
1666
1667 self.get('parameters')[('external',)].remove(yukawa)
1668
1669 particle = self.get_particle(yukawa.lhacode[0])
1670 mass = self.get_parameter(particle.get('mass'))
1671
1672
1673 if mass.depend == ('external',):
1674 depend = ()
1675 else:
1676 depend = mass.depend
1677
1678 New_param = ModelVariable(yukawa.name, mass.name, 'real', depend)
1679
1680
1681 if mass.name in to_change:
1682 expr = 'CMASS_%s' % mass.name
1683 else:
1684 expr = mass.name
1685 param_depend = self.get_parameter(expr)
1686 self.add_param(New_param, [param_depend])
1687
1688 if not to_change:
1689 return
1690
1691
1692
1693
1694
1695 pat = '|'.join(to_change.keys())
1696 pat = r'(%s)\b' % pat
1697 pat = re.compile(pat)
1698 def replace(match):
1699 return to_change[match.group()]
1700
1701
1702 for dep, list_param in self['parameters'].items():
1703 for param in list_param:
1704 if param.name.startswith('CMASS_') or param.name in mass_widths or\
1705 isinstance(param, ParamCardVariable):
1706 continue
1707 param.type = 'complex'
1708
1709
1710 param.expr = pat.sub(replace, param.expr)
1711
1712
1713 for dep, list_coup in self['couplings'].items():
1714 for coup in list_coup:
1715 coup.expr = pat.sub(replace, coup.expr)
1716
1717 - def add_param(self, new_param, depend_param):
1718 """add the parameter in the list of parameter in a correct position"""
1719
1720 pos = 0
1721 for i,param in enumerate(self.get('parameters')[new_param.depend]):
1722 if param.name in depend_param:
1723 pos = i + 1
1724 self.get('parameters')[new_param.depend].insert(pos, new_param)
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735 -class ModelVariable(object):
1736 """A Class for storing the information about coupling/ parameter"""
1737
1738 - def __init__(self, name, expression, type, depend=()):
1739 """Initialize a new parameter/coupling"""
1740
1741 self.name = name
1742 self.expr = expression
1743 self.type = type
1744 self.depend = depend
1745 self.value = None
1746
1748 """Object with same name are identical, If the object is a string we check
1749 if the attribute name is equal to this string"""
1750
1751 try:
1752 return other.name == self.name
1753 except Exception:
1754 return other == self.name
1755
1757 """ A class for storing the information linked to all the parameter
1758 which should be define in the param_card.dat"""
1759
1760 depend = ('external',)
1761 type = 'real'
1762
1763 - def __init__(self, name, value, lhablock, lhacode):
1764 """Initialize a new ParamCardVariable
1765 name: name of the variable
1766 value: default numerical value
1767 lhablock: name of the block in the param_card.dat
1768 lhacode: code associate to the variable
1769 """
1770 self.name = name
1771 self.value = value
1772 self.lhablock = lhablock
1773 self.lhacode = lhacode
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784 -class Leg(PhysicsObject):
1785 """Leg object: id (Particle), number, I/F state, flag from_group
1786 """
1787
1789 """Default values for all properties"""
1790
1791 self['id'] = 0
1792 self['number'] = 0
1793
1794 self['state'] = True
1795
1796 self['loop_line'] = False
1797
1798 self['from_group'] = True
1799
1800 self['onshell'] = None
1801
1802 - def filter(self, name, value):
1803 """Filter for valid leg property values."""
1804
1805 if name in ['id', 'number']:
1806 if not isinstance(value, int):
1807 raise self.PhysicsObjectError, \
1808 "%s is not a valid integer for leg id" % str(value)
1809
1810 if name == 'state':
1811 if not isinstance(value, bool):
1812 raise self.PhysicsObjectError, \
1813 "%s is not a valid leg state (True|False)" % \
1814 str(value)
1815
1816 if name == 'from_group':
1817 if not isinstance(value, bool) and value != None:
1818 raise self.PhysicsObjectError, \
1819 "%s is not a valid boolean for leg flag from_group" % \
1820 str(value)
1821
1822 if name == 'loop_line':
1823 if not isinstance(value, bool) and value != None:
1824 raise self.PhysicsObjectError, \
1825 "%s is not a valid boolean for leg flag loop_line" % \
1826 str(value)
1827
1828 if name == 'onshell':
1829 if not isinstance(value, bool) and value != None:
1830 raise self.PhysicsObjectError, \
1831 "%s is not a valid boolean for leg flag onshell" % \
1832 str(value)
1833 return True
1834
1836 """Return particle property names as a nicely sorted list."""
1837
1838 return ['id', 'number', 'state', 'from_group', 'loop_line', 'onshell']
1839
1841 """Returns True if the particle corresponding to the leg is a
1842 fermion"""
1843
1844 assert isinstance(model, Model), "%s is not a model" % str(model)
1845
1846 return model.get('particle_dict')[self['id']].is_fermion()
1847
1849 """Returns True if leg is an incoming fermion, i.e., initial
1850 particle or final antiparticle"""
1851
1852 assert isinstance(model, Model), "%s is not a model" % str(model)
1853
1854 part = model.get('particle_dict')[self['id']]
1855 return part.is_fermion() and \
1856 (self.get('state') == False and part.get('is_part') or \
1857 self.get('state') == True and not part.get('is_part'))
1858
1860 """Returns True if leg is an outgoing fermion, i.e., initial
1861 antiparticle or final particle"""
1862
1863 assert isinstance(model, Model), "%s is not a model" % str(model)
1864
1865 part = model.get('particle_dict')[self['id']]
1866 return part.is_fermion() and \
1867 (self.get('state') == True and part.get('is_part') or \
1868 self.get('state') == False and not part.get('is_part'))
1869
1870
1871
1872
1873 - def same(self, leg):
1874 """ Returns true if the leg in argument has the same ID and the same numer """
1875
1876
1877
1878 if isinstance(leg,int):
1879 if self['number']==leg:
1880 return True
1881 else:
1882 return False
1883
1884
1885
1886 elif isinstance(leg, Leg):
1887 if self['id']==leg.get('id') and \
1888 self['number']==leg.get('number') and \
1889 self['loop_line']==leg.get('loop_line') :
1890 return True
1891 else:
1892 return False
1893
1894 else :
1895 return False
1896
1897
1899 return self['number'] < other['number']
1900
1901
1902
1903
1904 -class LegList(PhysicsObjectList):
1905 """List of Leg objects
1906 """
1907
1909 """Test if object obj is a valid Leg for the list."""
1910
1911 return isinstance(obj, Leg)
1912
1913
1914
1916 """Return all elements which have 'from_group' True"""
1917
1918 return filter(lambda leg: leg.get('from_group'), self)
1919
1921 """Return True if at least one element has 'from_group' True"""
1922
1923 return len(self.from_group_elements()) > 0
1924
1926 """Return True if at least two elements have 'from_group' True"""
1927
1928 return len(self.from_group_elements()) > 1
1929
1931 """If has at least one 'from_group' True and in ref_dict_to1,
1932 return the return list from ref_dict_to1, otherwise return False"""
1933 if self.minimum_one_from_group():
1934 return ref_dict_to1.has_key(tuple(sorted([leg.get('id') for leg in self])))
1935 else:
1936 return False
1937
1939 """If has at least two 'from_group' True and in ref_dict_to0,
1940
1941 return the vertex (with id from ref_dict_to0), otherwise return None
1942
1943 If is_decay_chain = True, we only allow clustering of the
1944 initial leg, since we want this to be the last wavefunction to
1945 be evaluated.
1946 """
1947 if is_decay_chain:
1948
1949
1950
1951
1952 return any(leg.get('from_group') == None for leg in self) and \
1953 ref_dict_to0.has_key(tuple(sorted([leg.get('id') \
1954 for leg in self])))
1955
1956 if self.minimum_two_from_group():
1957 return ref_dict_to0.has_key(tuple(sorted([leg.get('id') for leg in self])))
1958 else:
1959 return False
1960
1962 """Returns the list of ids corresponding to the leglist with
1963 all particles outgoing"""
1964
1965 res = []
1966
1967 assert isinstance(model, Model), "Error! model not model"
1968
1969
1970 for leg in self:
1971 if leg.get('state') == False:
1972 res.append(model.get('particle_dict')[leg.get('id')].get_anti_pdg_code())
1973 else:
1974 res.append(leg.get('id'))
1975
1976 return res
1977
1978 - def sort(self,*args, **opts):
1979 """Match with FKSLegList"""
1980 Opts=copy.copy(opts)
1981 if 'pert' in Opts.keys():
1982 del Opts['pert']
1983 return super(LegList,self).sort(*args, **Opts)
1984
1985
1986
1987
1988
1989 -class MultiLeg(PhysicsObject):
1990 """MultiLeg object: ids (Particle or particles), I/F state
1991 """
1992
1994 """Default values for all properties"""
1995
1996 self['ids'] = []
1997 self['state'] = True
1998
1999 - def filter(self, name, value):
2000 """Filter for valid multileg property values."""
2001
2002 if name == 'ids':
2003 if not isinstance(value, list):
2004 raise self.PhysicsObjectError, \
2005 "%s is not a valid list" % str(value)
2006 for i in value:
2007 if not isinstance(i, int):
2008 raise self.PhysicsObjectError, \
2009 "%s is not a valid list of integers" % str(value)
2010
2011 if name == 'state':
2012 if not isinstance(value, bool):
2013 raise self.PhysicsObjectError, \
2014 "%s is not a valid leg state (initial|final)" % \
2015 str(value)
2016
2017 return True
2018
2020 """Return particle property names as a nicely sorted list."""
2021
2022 return ['ids', 'state']
2023
2028 """List of MultiLeg objects
2029 """
2030
2032 """Test if object obj is a valid MultiLeg for the list."""
2033
2034 return isinstance(obj, MultiLeg)
2035
2036
2037
2038
2039 -class Vertex(PhysicsObject):
2040 """Vertex: list of legs (ordered), id (Interaction)
2041 """
2042
2043 sorted_keys = ['id', 'legs']
2044
2046 """Default values for all properties"""
2047
2048 self['id'] = 0
2049 self['legs'] = LegList()
2050
2051 - def filter(self, name, value):
2052 """Filter for valid vertex property values."""
2053
2054 if name == 'id':
2055 if not isinstance(value, int):
2056 raise self.PhysicsObjectError, \
2057 "%s is not a valid integer for vertex id" % str(value)
2058
2059 if name == 'legs':
2060 if not isinstance(value, LegList):
2061 raise self.PhysicsObjectError, \
2062 "%s is not a valid LegList object" % str(value)
2063
2064 return True
2065
2067 """Return particle property names as a nicely sorted list."""
2068
2069 return self.sorted_keys
2070
2072 """Returns the id for the last leg as an outgoing
2073 s-channel. Returns 0 if leg is t-channel, or if identity
2074 vertex. Used to check for required and forbidden s-channel
2075 particles."""
2076
2077 leg = self.get('legs')[-1]
2078
2079 if ninitial == 1:
2080
2081
2082 if leg.get('state') == True:
2083 return leg.get('id')
2084 else:
2085 return model.get('particle_dict')[leg.get('id')].\
2086 get_anti_pdg_code()
2087
2088
2089 if self.get('id') == 0 or \
2090 leg.get('state') == False:
2091
2092 return 0
2093
2094 if leg.get('loop_line'):
2095
2096 return 0
2097
2098
2099
2100 if leg.get('number') > ninitial:
2101 return leg.get('id')
2102 else:
2103 return model.get('particle_dict')[leg.get('id')].\
2104 get_anti_pdg_code()
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117 -class VertexList(PhysicsObjectList):
2118 """List of Vertex objects
2119 """
2120
2121 orders = {}
2122
2124 """Test if object obj is a valid Vertex for the list."""
2125
2126 return isinstance(obj, Vertex)
2127
2128 - def __init__(self, init_list=None, orders=None):
2129 """Creates a new list object, with an optional dictionary of
2130 coupling orders."""
2131
2132 list.__init__(self)
2133
2134 if init_list is not None:
2135 for object in init_list:
2136 self.append(object)
2137
2138 if isinstance(orders, dict):
2139 self.orders = orders
2140
2141
2142
2143
2144
2145 -class Diagram(PhysicsObject):
2146 """Diagram: list of vertices (ordered)
2147 """
2148
2150 """Default values for all properties"""
2151
2152 self['vertices'] = VertexList()
2153 self['orders'] = {}
2154
2155 - def filter(self, name, value):
2167
2169 """Return particle property names as a nicely sorted list."""
2170
2171 return ['vertices', 'orders']
2172
2174 """Returns a nicely formatted string of the diagram content."""
2175
2176 if self['vertices']:
2177 mystr = '('
2178 for vert in self['vertices']:
2179 mystr = mystr + '('
2180 for leg in vert['legs'][:-1]:
2181 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
2182
2183 if self['vertices'].index(vert) < len(self['vertices']) - 1:
2184
2185 mystr = mystr[:-1] + '>'
2186 mystr = mystr + str(vert['legs'][-1]['number']) + '(%s)' % str(vert['legs'][-1]['id']) + ','
2187 mystr = mystr + 'id:' + str(vert['id']) + '),'
2188 mystr = mystr[:-1] + ')'
2189 mystr += " (%s)" % ",".join(["%s=%d" % (key, self['orders'][key]) \
2190 for key in sorted(self['orders'].keys())])
2191 return mystr
2192 else:
2193 return '()'
2194
2196 """Calculate the actual coupling orders of this diagram. Note
2197 that the special order WEIGTHED corresponds to the sum of
2198 hierarchys for the couplings."""
2199
2200 coupling_orders = dict([(c, 0) for c in model.get('coupling_orders')])
2201 weight = 0
2202 for vertex in self['vertices']:
2203 if vertex.get('id') in [0,-1]: continue
2204 couplings = model.get('interaction_dict')[vertex.get('id')].\
2205 get('orders')
2206 for coupling in couplings:
2207 coupling_orders[coupling] += couplings[coupling]
2208 weight += sum([model.get('order_hierarchy')[c]*n for \
2209 (c,n) in couplings.items()])
2210 coupling_orders['WEIGHTED'] = weight
2211 self.set('orders', coupling_orders)
2212
2215 """ Returns wether the contributiong consisting in the current diagram
2216 multiplied by diag_multiplier passes the *positive* squared_orders
2217 specified ( a dictionary ) of types sq_order_types (a dictionary whose
2218 values are the relational operator used to define the constraint of the
2219 order in key)."""
2220
2221 for order, value in squared_orders.items():
2222 if value<0:
2223 continue
2224 combined_order = self.get_order(order) + \
2225 diag_multiplier.get_order(order)
2226 if ( sq_orders_types[order]=='==' and combined_order != value ) or \
2227 ( sq_orders_types[order] in ['=', '<='] and combined_order > value) or \
2228 ( sq_orders_types[order]=='>' and combined_order <= value) :
2229 return False
2230 return True
2231
2233 """Return the order of this diagram. It returns 0 if it is not present."""
2234
2235 try:
2236 return self['orders'][order]
2237 except Exception:
2238 return 0
2239
2241 """Renumber legs in all vertices according to perm_map"""
2242 vertices = VertexList()
2243 min_dict = copy.copy(perm_map)
2244
2245 state_dict = dict([(l.get('number'), l.get('state')) for l in leg_list])
2246
2247 for vertex in self.get('vertices')[:-1]:
2248 vertex = copy.copy(vertex)
2249 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2250 for leg in leg_list[:-1]:
2251 leg.set('number', min_dict[leg.get('number')])
2252 leg.set('state', state_dict[leg.get('number')])
2253 min_number = min([leg.get('number') for leg in leg_list[:-1]])
2254 leg = leg_list[-1]
2255 min_dict[leg.get('number')] = min_number
2256
2257
2258 state_dict[min_number] = len([l for l in leg_list[:-1] if \
2259 not l.get('state')]) != 1
2260 leg.set('number', min_number)
2261 leg.set('state', state_dict[min_number])
2262 vertex.set('legs', leg_list)
2263 vertices.append(vertex)
2264
2265 vertex = copy.copy(self.get('vertices')[-1])
2266 leg_list = LegList([copy.copy(l) for l in vertex.get('legs')])
2267 for leg in leg_list:
2268 leg.set('number', min_dict[leg.get('number')])
2269 leg.set('state', state_dict[leg.get('number')])
2270 vertex.set('legs', leg_list)
2271 vertices.append(vertex)
2272
2273 new_diag = copy.copy(self)
2274 new_diag.set('vertices', vertices)
2275 state_dict = {True:'T',False:'F'}
2276 return new_diag
2277
2279 """Return a list of the number of legs in the vertices for
2280 this diagram"""
2281
2282 return [len(v.get('legs')) for v in self.get('vertices')]
2283
2285 """Return the maximum number of configs from this diagram,
2286 given by 2^(number of non-zero width s-channel propagators)"""
2287
2288 s_channels = [v.get_s_channel_id(model,ninitial) for v in \
2289 self.get('vertices')[:-1]]
2290 num_props = len([i for i in s_channels if i != 0 and \
2291 model.get_particle(i).get('width').lower() != 'zero'])
2292
2293 if num_props < 1:
2294 return 1
2295 else:
2296 return 2**num_props
2297
2299 """return the difference of total diff of charge occuring on the
2300 lofw of the initial parton. return [None,None] if the two initial parton
2301 are connected and the (partial) value if None if the initial parton is
2302 not a fermiom"""
2303
2304 import madgraph.core.drawing as drawing
2305 drawdiag = drawing.FeynmanDiagram(self, model)
2306 drawdiag.load_diagram()
2307 out = []
2308
2309 for v in drawdiag.initial_vertex:
2310 init_part = v.lines[0]
2311 if not init_part.is_fermion():
2312 out.append(None)
2313 continue
2314
2315 init_charge = model.get_particle(init_part.id).get('charge')
2316
2317 l_last = init_part
2318 v_last = v
2319 vcurrent = l_last.end
2320 if vcurrent == v:
2321 vcurrent = l_last.begin
2322 security =0
2323 while not vcurrent.is_external():
2324 if security > 1000:
2325 raise Exception, 'wrong diagram'
2326 next_l = [l for l in vcurrent.lines if l is not l_last and l.is_fermion()][0]
2327 next_v = next_l.end
2328 if next_v == vcurrent:
2329 next_v = next_l.begin
2330 l_last, vcurrent = next_l, next_v
2331 if vcurrent in drawdiag.initial_vertex:
2332 return [None, None]
2333
2334 out.append(model.get_particle(l_last.id).get('charge') - init_charge)
2335 return out
2336
2337
2338
2339
2340
2341 -class DiagramList(PhysicsObjectList):
2342 """List of Diagram objects
2343 """
2344
2346 """Test if object obj is a valid Diagram for the list."""
2347
2348 return isinstance(obj, Diagram)
2349
2351 """Returns a nicely formatted string"""
2352 mystr = " " * indent + str(len(self)) + ' diagrams:\n'
2353 for i, diag in enumerate(self):
2354 mystr = mystr + " " * indent + str(i+1) + " " + \
2355 diag.nice_string() + '\n'
2356 return mystr[:-1]
2357
2358
2359
2361 """ Return the order of the diagram in the list with the maximum coupling
2362 order for the coupling specified """
2363 max_order=-1
2364
2365 for diag in self:
2366 if order in diag['orders'].keys():
2367 if max_order==-1 or diag['orders'][order] > max_order:
2368 max_order = diag['orders'][order]
2369
2370 return max_order
2371
2373 """ This function returns a fitlered version of the diagram list self
2374 which satisfy the negative squared_order constraint 'order' with negative
2375 value 'value' and of type 'order_type', assuming that the diagram_list
2376 it must be squared against is 'reg_diag_list'. It also returns the
2377 new postive target squared order which correspond to this negative order
2378 constraint. Example: u u~ > d d~ QED^2<=-2 means that one wants to
2379 pick terms only up to the the next-to-leading order contributiong in QED,
2380 which is QED=2 in this case, so that target_order=4 is returned."""
2381
2382
2383 target_order = min(ref_diag_list.get_order_values(order))+\
2384 min(self.get_order_values(order))+2*(-value-1)
2385
2386 new_list = self.apply_positive_sq_orders(ref_diag_list,
2387 {order:target_order}, {order:order_type})
2388
2389 return new_list, target_order
2390
2392 """ This function returns a filtered version of self which contain
2393 only the diagram which satisfy the positive squared order constraints
2394 sq_orders of type sq_order_types and assuming that the diagrams are
2395 multiplied with those of the reference diagram list ref_diag_list."""
2396
2397 new_diag_list = DiagramList()
2398 for tested_diag in self:
2399 for ref_diag in ref_diag_list:
2400 if tested_diag.pass_squared_order_constraints(ref_diag,
2401 sq_orders,sq_order_types):
2402 new_diag_list.append(tested_diag)
2403 break
2404 return new_diag_list
2405
2407 """ Return the order of the diagram in the list with the mimimum coupling
2408 order for the coupling specified """
2409 min_order=-1
2410 for diag in self:
2411 if order in diag['orders'].keys():
2412 if min_order==-1 or diag['orders'][order] < min_order:
2413 min_order = diag['orders'][order]
2414 else:
2415 return 0
2416
2417 return min_order
2418
2420 """ Return the list of possible values appearing in the diagrams of this
2421 list for the order given in argument """
2422
2423 values=set([])
2424 for diag in self:
2425 if order in diag['orders'].keys():
2426 values.add(diag['orders'][order])
2427 else:
2428 values.add(0)
2429
2430 return list(values)
2431
2432
2433
2434
2435 -class Process(PhysicsObject):
2436 """Process: list of legs (ordered)
2437 dictionary of orders
2438 model
2439 process id
2440 """
2441
2443 """Default values for all properties"""
2444
2445 self['legs'] = LegList()
2446
2447 self['orders'] = {}
2448 self['model'] = Model()
2449
2450 self['id'] = 0
2451 self['uid'] = 0
2452
2453
2454
2455
2456 self['required_s_channels'] = []
2457 self['forbidden_onsh_s_channels'] = []
2458 self['forbidden_s_channels'] = []
2459 self['forbidden_particles'] = []
2460 self['is_decay_chain'] = False
2461 self['overall_orders'] = {}
2462
2463 self['decay_chains'] = ProcessList()
2464
2465 self['legs_with_decays'] = LegList()
2466
2467 self['perturbation_couplings']=[]
2468
2469
2470
2471
2472 self['squared_orders'] = {}
2473
2474
2475
2476
2477 self['sqorders_types'] = {}
2478 self['has_born'] = True
2479
2480
2481 self['NLO_mode'] = 'tree'
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491 self['split_orders'] = []
2492
2493 - def filter(self, name, value):
2494 """Filter for valid process property values."""
2495
2496 if name in ['legs', 'legs_with_decays'] :
2497 if not isinstance(value, LegList):
2498 raise self.PhysicsObjectError, \
2499 "%s is not a valid LegList object" % str(value)
2500
2501 if name in ['orders', 'overall_orders','squared_orders']:
2502 Interaction.filter(Interaction(), 'orders', value)
2503
2504 if name == 'sqorders_types':
2505 if not isinstance(value, dict):
2506 raise self.PhysicsObjectError, \
2507 "%s is not a valid dictionary" % str(value)
2508 for order in value.keys()+value.values():
2509 if not isinstance(order, str):
2510 raise self.PhysicsObjectError, \
2511 "%s is not a valid string" % str(value)
2512
2513 if name == 'split_orders':
2514 if not isinstance(value, list):
2515 raise self.PhysicsObjectError, \
2516 "%s is not a valid list" % str(value)
2517 for order in value:
2518 if not isinstance(order, str):
2519 raise self.PhysicsObjectError, \
2520 "%s is not a valid string" % str(value)
2521
2522 if name == 'model':
2523 if not isinstance(value, Model):
2524 raise self.PhysicsObjectError, \
2525 "%s is not a valid Model object" % str(value)
2526 if name in ['id', 'uid']:
2527 if not isinstance(value, int):
2528 raise self.PhysicsObjectError, \
2529 "Process %s %s is not an integer" % (name, repr(value))
2530
2531 if name == 'required_s_channels':
2532 if not isinstance(value, list):
2533 raise self.PhysicsObjectError, \
2534 "%s is not a valid list" % str(value)
2535 for l in value:
2536 if not isinstance(l, list):
2537 raise self.PhysicsObjectError, \
2538 "%s is not a valid list of lists" % str(value)
2539 for i in l:
2540 if not isinstance(i, int):
2541 raise self.PhysicsObjectError, \
2542 "%s is not a valid list of integers" % str(l)
2543 if i == 0:
2544 raise self.PhysicsObjectError, \
2545 "Not valid PDG code %d for s-channel particle" % i
2546
2547 if name in ['forbidden_onsh_s_channels', 'forbidden_s_channels']:
2548 if not isinstance(value, list):
2549 raise self.PhysicsObjectError, \
2550 "%s is not a valid list" % str(value)
2551 for i in value:
2552 if not isinstance(i, int):
2553 raise self.PhysicsObjectError, \
2554 "%s is not a valid list of integers" % str(value)
2555 if i == 0:
2556 raise self.PhysicsObjectError, \
2557 "Not valid PDG code %d for s-channel particle" % str(value)
2558
2559 if name == 'forbidden_particles':
2560 if not isinstance(value, list):
2561 raise self.PhysicsObjectError, \
2562 "%s is not a valid list" % str(value)
2563 for i in value:
2564 if not isinstance(i, int):
2565 raise self.PhysicsObjectError, \
2566 "%s is not a valid list of integers" % str(value)
2567 if i <= 0:
2568 raise self.PhysicsObjectError, \
2569 "Forbidden particles should have a positive PDG code" % str(value)
2570
2571 if name == 'perturbation_couplings':
2572 if not isinstance(value, list):
2573 raise self.PhysicsObjectError, \
2574 "%s is not a valid list" % str(value)
2575 for order in value:
2576 if not isinstance(order, str):
2577 raise self.PhysicsObjectError, \
2578 "%s is not a valid string" % str(value)
2579
2580 if name == 'is_decay_chain':
2581 if not isinstance(value, bool):
2582 raise self.PhysicsObjectError, \
2583 "%s is not a valid bool" % str(value)
2584
2585 if name == 'has_born':
2586 if not isinstance(value, bool):
2587 raise self.PhysicsObjectError, \
2588 "%s is not a valid bool" % str(value)
2589
2590 if name == 'decay_chains':
2591 if not isinstance(value, ProcessList):
2592 raise self.PhysicsObjectError, \
2593 "%s is not a valid ProcessList" % str(value)
2594
2595 if name == 'NLO_mode':
2596 if value not in ['real','all','virt','tree']:
2597 raise self.PhysicsObjectError, \
2598 "%s is not a valid NLO_mode" % str(value)
2599 return True
2600
2602 """ A process, not being a ProcessDefinition never carries multiple
2603 particles labels"""
2604
2605 return False
2606
2607 - def set(self, name, value):
2608 """Special set for forbidden particles - set to abs value."""
2609
2610 if name == 'forbidden_particles':
2611 try:
2612 value = [abs(i) for i in value]
2613 except Exception:
2614 pass
2615
2616 if name == 'required_s_channels':
2617
2618 if value and isinstance(value, list) and \
2619 not isinstance(value[0], list):
2620 value = [value]
2621
2622 return super(Process, self).set(name, value)
2623
2625 """ Return what kind of squared order constraint was specified for the
2626 order 'order'."""
2627
2628 if order in self['sqorders_types'].keys():
2629 return self['sqorders_types'][order]
2630 else:
2631
2632 return '='
2633
2634 - def get(self, name):
2635 """Special get for legs_with_decays"""
2636
2637 if name == 'legs_with_decays':
2638 self.get_legs_with_decays()
2639
2640 if name == 'sqorders_types':
2641
2642 for order in self['squared_orders'].keys():
2643 if order not in self['sqorders_types']:
2644
2645 self['sqorders_types'][order]='='
2646
2647 return super(Process, self).get(name)
2648
2650 """Return process property names as a nicely sorted list."""
2651
2652 return ['legs', 'orders', 'overall_orders', 'squared_orders',
2653 'model', 'id', 'required_s_channels',
2654 'forbidden_onsh_s_channels', 'forbidden_s_channels',
2655 'forbidden_particles', 'is_decay_chain', 'decay_chains',
2656 'legs_with_decays', 'perturbation_couplings', 'has_born',
2657 'NLO_mode','split_orders']
2658
2659 - def nice_string(self, indent=0, print_weighted = True):
2660 """Returns a nicely formated string about current process
2661 content. Since the WEIGHTED order is automatically set and added to
2662 the user-defined list of orders, it can be ommitted for some info
2663 displays."""
2664
2665 mystr = " " * indent + "Process: "
2666 prevleg = None
2667 for leg in self['legs']:
2668 mypart = self['model'].get('particle_dict')[leg['id']]
2669 if prevleg and prevleg['state'] == False \
2670 and leg['state'] == True:
2671
2672 mystr = mystr + '> '
2673
2674 if self['required_s_channels'] and \
2675 self['required_s_channels'][0]:
2676 mystr += "|".join([" ".join([self['model'].\
2677 get('particle_dict')[req_id].get_name() \
2678 for req_id in id_list]) \
2679 for id_list in self['required_s_channels']])
2680 mystr = mystr + ' > '
2681
2682 mystr = mystr + mypart.get_name() + ' '
2683
2684 prevleg = leg
2685
2686
2687 if self['orders']:
2688 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
2689 for key in self['orders'] if (print_weighted or key!='WEIGHTED') \
2690 and not key in self['squared_orders'].keys()]) + ' '
2691
2692
2693 if self['perturbation_couplings']:
2694 mystr = mystr + '[ '
2695 if self['NLO_mode']!='tree':
2696 mystr = mystr + self['NLO_mode'] + ' = '
2697 for order in self['perturbation_couplings']:
2698 mystr = mystr + order + ' '
2699 mystr = mystr + '] '
2700
2701
2702 if self['squared_orders']:
2703 mystr = mystr + " ".join([key + '^2%s%d'%\
2704 (self.get_squared_order_type(key),self['squared_orders'][key]) \
2705 for key in self['squared_orders'].keys() \
2706 if print_weighted or key!='WEIGHTED']) + ' '
2707
2708
2709 if self['forbidden_onsh_s_channels']:
2710 mystr = mystr + '$ '
2711 for forb_id in self['forbidden_onsh_s_channels']:
2712 forbpart = self['model'].get('particle_dict')[forb_id]
2713 mystr = mystr + forbpart.get_name() + ' '
2714
2715
2716 if self['forbidden_s_channels']:
2717 mystr = mystr + '$$ '
2718 for forb_id in self['forbidden_s_channels']:
2719 forbpart = self['model'].get('particle_dict')[forb_id]
2720 mystr = mystr + forbpart.get_name() + ' '
2721
2722
2723 if self['forbidden_particles']:
2724 mystr = mystr + '/ '
2725 for forb_id in self['forbidden_particles']:
2726 forbpart = self['model'].get('particle_dict')[forb_id]
2727 mystr = mystr + forbpart.get_name() + ' '
2728
2729
2730 mystr = mystr[:-1]
2731
2732 if self.get('id') or self.get('overall_orders'):
2733 mystr += " @%d" % self.get('id')
2734 if self.get('overall_orders'):
2735 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
2736 for key in sorted(self['orders'])]) + ' '
2737
2738 if not self.get('decay_chains'):
2739 return mystr
2740
2741 for decay in self['decay_chains']:
2742 mystr = mystr + '\n' + \
2743 decay.nice_string(indent + 2).replace('Process', 'Decay')
2744
2745 return mystr
2746
2837
2839 """Returns a string containing only the basic process (w/o decays)."""
2840
2841 mystr = ""
2842 prevleg = None
2843 for leg in self.get_legs_with_decays():
2844 mypart = self['model'].get('particle_dict')[leg['id']]
2845 if prevleg and prevleg['state'] == False \
2846 and leg['state'] == True:
2847
2848 mystr = mystr + '> '
2849 mystr = mystr + mypart.get_name() + ' '
2850 prevleg = leg
2851
2852
2853 return mystr[:-1]
2854
2855 - def shell_string(self, schannel=True, forbid=True, main=True, pdg_order=False):
2856 """Returns process as string with '~' -> 'x', '>' -> '_',
2857 '+' -> 'p' and '-' -> 'm', including process number,
2858 intermediate s-channels and forbidden particles,
2859 pdg_order allow to order to leg order by pid."""
2860
2861 mystr = ""
2862 if not self.get('is_decay_chain'):
2863 mystr += "%d_" % self['id']
2864
2865 prevleg = None
2866 if pdg_order:
2867 legs = [l for l in self['legs'][1:]]
2868 def order_leg(l1,l2):
2869 id1 = l1.get('id')
2870 id2 = l2.get('id')
2871 return id2-id1
2872 legs.sort(cmp=order_leg)
2873 legs.insert(0, self['legs'][0])
2874 else:
2875 legs = self['legs']
2876
2877
2878 for leg in legs:
2879 mypart = self['model'].get('particle_dict')[leg['id']]
2880 if prevleg and prevleg['state'] == False \
2881 and leg['state'] == True:
2882
2883 mystr = mystr + '_'
2884
2885 if self['required_s_channels'] and \
2886 self['required_s_channels'][0] and schannel:
2887 mystr += "_or_".join(["".join([self['model'].\
2888 get('particle_dict')[req_id].get_name() \
2889 for req_id in id_list]) \
2890 for id_list in self['required_s_channels']])
2891 mystr = mystr + '_'
2892 if mypart['is_part']:
2893 mystr = mystr + mypart['name']
2894 else:
2895 mystr = mystr + mypart['antiname']
2896 prevleg = leg
2897
2898
2899 if self['forbidden_particles'] and forbid:
2900 mystr = mystr + '_no_'
2901 for forb_id in self['forbidden_particles']:
2902 forbpart = self['model'].get('particle_dict')[forb_id]
2903 mystr = mystr + forbpart.get_name()
2904
2905
2906 mystr = mystr.replace('~', 'x')
2907
2908 mystr = mystr.replace('+', 'p')
2909
2910 mystr = mystr.replace('-', 'm')
2911
2912 mystr = mystr.replace(' ', '')
2913
2914 for decay in self.get('decay_chains'):
2915 mystr = mystr + "_" + decay.shell_string(schannel,forbid, main=False,
2916 pdg_order=pdg_order)
2917
2918
2919 if len(mystr) > 64 and main:
2920 if schannel and forbid:
2921 out = self.shell_string(True, False, True, pdg_order)
2922 elif schannel:
2923 out = self.shell_string(False, False, True, pdg_order)
2924 else:
2925 out = mystr[:64]
2926 if not out.endswith('_%s' % self['uid']):
2927 out += '_%s' % self['uid']
2928 return out
2929
2930 return mystr
2931
2933 """Returns process as v4-compliant string with '~' -> 'x' and
2934 '>' -> '_'"""
2935
2936 mystr = "%d_" % self['id']
2937 prevleg = None
2938 for leg in self.get_legs_with_decays():
2939 mypart = self['model'].get('particle_dict')[leg['id']]
2940 if prevleg and prevleg['state'] == False \
2941 and leg['state'] == True:
2942
2943 mystr = mystr + '_'
2944 if mypart['is_part']:
2945 mystr = mystr + mypart['name']
2946 else:
2947 mystr = mystr + mypart['antiname']
2948 prevleg = leg
2949
2950
2951 mystr = mystr.replace('~', 'x')
2952
2953 mystr = mystr.replace(' ', '')
2954
2955 return mystr
2956
2957
2958
2960 """ Check iteratively that no coupling order constraint include negative
2961 values."""
2962
2963 if any(val<0 for val in self.get('orders').values()+\
2964 self.get('squared_orders').values()):
2965 return True
2966
2967 for procdef in self['decay_chains']:
2968 if procdef.are_negative_orders_present():
2969 return True
2970
2971 return False
2972
2974 """ Check iteratively that the decayed processes are not perturbed """
2975
2976 for procdef in self['decay_chains']:
2977 if procdef['perturbation_couplings'] or procdef.are_decays_perturbed():
2978 return True
2979 return False
2980
2982 """ Check iteratively that the decayed processes are not perturbed """
2983
2984 for procdef in self['decay_chains']:
2985 if procdef['squared_orders']!={} or procdef.decays_have_squared_orders():
2986 return True
2987 return False
2988
2990 """Gives number of initial state particles"""
2991
2992 return len(filter(lambda leg: leg.get('state') == False,
2993 self.get('legs')))
2994
2996 """Gives the pdg codes for initial state particles"""
2997
2998 return [leg.get('id') for leg in \
2999 filter(lambda leg: leg.get('state') == False,
3000 self.get('legs'))]
3001
3003 """Return the pdg codes for initial state particles for beam number"""
3004
3005 return filter(lambda leg: leg.get('state') == False and\
3006 leg.get('number') == number,
3007 self.get('legs'))[0].get('id')
3008
3010 """return a tuple of two tuple containing the id of the initial/final
3011 state particles. Each list is ordered"""
3012
3013 initial = []
3014 final = [l.get('id') for l in self.get('legs')\
3015 if l.get('state') or initial.append(l.get('id'))]
3016 initial.sort()
3017 final.sort()
3018 return (tuple(initial), tuple(final))
3019
3040
3041
3043 """Gives the final state legs"""
3044
3045 return filter(lambda leg: leg.get('state') == True,
3046 self.get('legs'))
3047
3049 """Gives the pdg codes for final state particles"""
3050
3051 return [l.get('id') for l in self.get_final_legs()]
3052
3053
3055 """Return process with all decay chains substituted in."""
3056
3057 if self['legs_with_decays']:
3058 return self['legs_with_decays']
3059
3060 legs = copy.deepcopy(self.get('legs'))
3061 org_decay_chains = copy.copy(self.get('decay_chains'))
3062 sorted_decay_chains = []
3063
3064 for leg in legs:
3065 if not leg.get('state'): continue
3066 org_ids = [l.get('legs')[0].get('id') for l in \
3067 org_decay_chains]
3068 if leg.get('id') in org_ids:
3069 sorted_decay_chains.append(org_decay_chains.pop(\
3070 org_ids.index(leg.get('id'))))
3071 assert not org_decay_chains
3072 ileg = 0
3073 for decay in sorted_decay_chains:
3074 while legs[ileg].get('state') == False or \
3075 legs[ileg].get('id') != decay.get('legs')[0].get('id'):
3076 ileg = ileg + 1
3077 decay_legs = decay.get_legs_with_decays()
3078 legs = legs[:ileg] + decay_legs[1:] + legs[ileg+1:]
3079 ileg = ileg + len(decay_legs) - 1
3080
3081
3082 legs = [copy.copy(l) for l in legs]
3083
3084 for ileg, leg in enumerate(legs):
3085 leg.set('number', ileg + 1)
3086
3087 self['legs_with_decays'] = LegList(legs)
3088
3089 return self['legs_with_decays']
3090
3092 """Output a list that can be compared to other processes as:
3093 [id, sorted(initial leg ids), sorted(final leg ids),
3094 sorted(decay list_for_sorts)]"""
3095
3096 sorted_list = [self.get('id'),
3097 sorted(self.get_initial_ids()),
3098 sorted(self.get_final_ids())]
3099
3100 if self.get('decay_chains'):
3101 sorted_list.extend(sorted([d.list_for_sort() for d in \
3102 self.get('decay_chains')]))
3103
3104 return sorted_list
3105
3107 """Sorting routine which allows to sort processes for
3108 comparison. Compare only process id and legs."""
3109
3110 if self.list_for_sort() > other.list_for_sort():
3111 return 1
3112 if self.list_for_sort() < other.list_for_sort():
3113 return -1
3114 return 0
3115
3117 """Calculate the denominator factor for identical final state particles
3118 """
3119
3120 final_legs = filter(lambda leg: leg.get('state') == True, \
3121 self.get_legs_with_decays())
3122
3123 identical_indices = {}
3124 for leg in final_legs:
3125 if leg.get('id') in identical_indices:
3126 identical_indices[leg.get('id')] = \
3127 identical_indices[leg.get('id')] + 1
3128 else:
3129 identical_indices[leg.get('id')] = 1
3130 return reduce(lambda x, y: x * y, [ math.factorial(val) for val in \
3131 identical_indices.values() ], 1)
3132
3134 """Ensure that maximum expansion orders from the model are
3135 properly taken into account in the process"""
3136
3137
3138 expansion_orders = self.get('model').get('expansion_order')
3139 orders = self.get('orders')
3140 sq_orders = self.get('squared_orders')
3141
3142 tmp = [(k,v) for (k,v) in expansion_orders.items() if 0 < v < 99]
3143 for (k,v) in tmp:
3144 if k in orders:
3145 if v < orders[k]:
3146 if k in sq_orders.keys() and \
3147 (sq_orders[k]>v or sq_orders[k]<0):
3148 logger.warning(
3149 '''The process with the squared coupling order (%s^2%s%s) specified can potentially
3150 recieve contributions with powers of the coupling %s larger than the maximal
3151 value allowed by the model builder (%s). Hence, MG5_aMC sets the amplitude order
3152 for that coupling to be this maximal one. '''%(k,self.get('sqorders_types')[k],
3153 self.get('squared_orders')[k],k,v))
3154 else:
3155 logger.warning(
3156 '''The coupling order (%s=%s) specified is larger than the one allowed
3157 by the model builder. The maximal value allowed is %s.
3158 We set the %s order to this value''' % (k,orders[k],v,k))
3159 orders[k] = v
3160 else:
3161 orders[k] = v
3162
3164 """Overloading the equality operator, so that only comparison
3165 of process id and legs is being done, using compare_for_sort."""
3166
3167 if not isinstance(other, Process):
3168 return False
3169
3170 return self.compare_for_sort(other) == 0
3171
3173 return not self.__eq__(other)
3174
3179 """List of Process objects
3180 """
3181
3183 """Test if object obj is a valid Process for the list."""
3184
3185 return isinstance(obj, Process)
3186
3188 """Returns a nicely formatted string of the matrix element processes."""
3189
3190 mystr = "\n".join([p.nice_string(indent) for p in self])
3191
3192 return mystr
3193
3198 """ProcessDefinition: list of multilegs (ordered)
3199 dictionary of orders
3200 model
3201 process id
3202 """
3203
3213
3214 - def filter(self, name, value):
3230
3232 """ Check that this process definition will yield a single process, as
3233 each multileg only has one leg"""
3234
3235 for mleg in self['legs']:
3236 if len(mleg['ids'])>1:
3237 return True
3238
3239 return False
3240
3248
3250 """Retrieve the minimum starting guess for WEIGHTED order, to
3251 use in find_optimal_process_orders in MultiProcess diagram
3252 generation (as well as particles and hierarchy). The algorithm:
3253
3254 1) Pick out the legs in the multiprocess according to the
3255 highest hierarchy represented (so don't mix particles from
3256 different hierarchy classes in the same multiparticles!)
3257
3258 2) Find the starting maximum WEIGHTED order as the sum of the
3259 highest n-2 weighted orders
3260
3261 3) Pick out required s-channel particle hierarchies, and use
3262 the highest of the maximum WEIGHTED order from the legs and
3263 the minimum WEIGHTED order extracted from 2*s-channel
3264 hierarchys plus the n-2-2*(number of s-channels) lowest
3265 leg weighted orders.
3266 """
3267
3268 model = self.get('model')
3269
3270
3271
3272 particles, hierarchy = model.get_particles_hierarchy()
3273
3274
3275
3276 max_order_now = []
3277 new_legs = copy.copy(self.get('legs'))
3278 for parts, value in zip(particles, hierarchy):
3279 ileg = 0
3280 while ileg < len(new_legs):
3281 if any([id in parts for id in new_legs[ileg].get('ids')]):
3282 max_order_now.append(value)
3283 new_legs.pop(ileg)
3284 else:
3285 ileg += 1
3286
3287
3288
3289 max_order_now = sorted(max_order_now)[2:]
3290
3291
3292 max_order_prop = []
3293 for idlist in self.get('required_s_channels'):
3294 max_order_prop.append([0,0])
3295 for id in idlist:
3296 for parts, value in zip(particles, hierarchy):
3297 if id in parts:
3298 max_order_prop[-1][0] += 2*value
3299 max_order_prop[-1][1] += 1
3300 break
3301
3302 if max_order_prop:
3303 if len(max_order_prop) >1:
3304 max_order_prop = min(*max_order_prop, key=lambda x:x[0])
3305 else:
3306 max_order_prop = max_order_prop[0]
3307
3308
3309
3310
3311 max_order_now = max(sum(max_order_now),
3312 max_order_prop[0] + \
3313 sum(max_order_now[:-2 * max_order_prop[1]]))
3314 else:
3315 max_order_now = sum(max_order_now)
3316
3317 return max_order_now, particles, hierarchy
3318
3319 - def nice_string(self, indent=0, print_weighted=False):
3320 """Returns a nicely formated string about current process
3321 content"""
3322
3323 mystr = " " * indent + "Process: "
3324 prevleg = None
3325 for leg in self['legs']:
3326 myparts = \
3327 "/".join([self['model'].get('particle_dict')[id].get_name() \
3328 for id in leg.get('ids')])
3329 if prevleg and prevleg['state'] == False \
3330 and leg['state'] == True:
3331
3332 mystr = mystr + '> '
3333
3334 if self['required_s_channels'] and \
3335 self['required_s_channels'][0]:
3336 mystr += "|".join([" ".join([self['model'].\
3337 get('particle_dict')[req_id].get_name() \
3338 for req_id in id_list]) \
3339 for id_list in self['required_s_channels']])
3340 mystr = mystr + '> '
3341
3342 mystr = mystr + myparts + ' '
3343
3344 prevleg = leg
3345
3346
3347 if self['forbidden_onsh_s_channels']:
3348 mystr = mystr + '$ '
3349 for forb_id in self['forbidden_onsh_s_channels']:
3350 forbpart = self['model'].get('particle_dict')[forb_id]
3351 mystr = mystr + forbpart.get_name() + ' '
3352
3353
3354 if self['forbidden_s_channels']:
3355 mystr = mystr + '$$ '
3356 for forb_id in self['forbidden_s_channels']:
3357 forbpart = self['model'].get('particle_dict')[forb_id]
3358 mystr = mystr + forbpart.get_name() + ' '
3359
3360
3361 if self['forbidden_particles']:
3362 mystr = mystr + '/ '
3363 for forb_id in self['forbidden_particles']:
3364 forbpart = self['model'].get('particle_dict')[forb_id]
3365 mystr = mystr + forbpart.get_name() + ' '
3366
3367 if self['orders']:
3368 mystr = mystr + " ".join([key + '=' + repr(self['orders'][key]) \
3369 for key in sorted(self['orders'])]) + ' '
3370
3371
3372 if self['perturbation_couplings']:
3373 mystr = mystr + '[ '
3374 if self['NLO_mode']:
3375 mystr = mystr + self['NLO_mode'] + ' = '
3376 for order in self['perturbation_couplings']:
3377 mystr = mystr + order + ' '
3378 mystr = mystr + '] '
3379
3380 if self['squared_orders']:
3381 mystr = mystr + " ".join([key + '^2%s%d'%\
3382 (self.get_squared_order_type(key),self['squared_orders'][key]) \
3383 for key in self['squared_orders'].keys() \
3384 if print_weighted or key!='WEIGHTED']) + ' '
3385
3386
3387 mystr = mystr[:-1]
3388
3389 if self.get('id') or self.get('overall_orders'):
3390 mystr += " @%d" % self.get('id')
3391 if self.get('overall_orders'):
3392 mystr += " " + " ".join([key + '=' + repr(self['orders'][key]) \
3393 for key in sorted(self['orders'])]) + ' '
3394
3395 if not self.get('decay_chains'):
3396 return mystr
3397
3398 for decay in self['decay_chains']:
3399 mystr = mystr + '\n' + \
3400 decay.nice_string(indent + 2).replace('Process', 'Decay')
3401
3402 return mystr
3403
3405 """ Return a Process object which has the same properties of this
3406 ProcessDefinition but with the specified LegList as legs attribute.
3407 """
3408
3409 return Process({\
3410 'legs': LegList,
3411 'model':self.get('model'),
3412 'id': self.get('id'),
3413 'orders': self.get('orders'),
3414 'sqorders_types': self.get('sqorders_types'),
3415 'squared_orders': self.get('squared_orders'),
3416 'has_born': self.get('has_born'),
3417 'required_s_channels': self.get('required_s_channels'),
3418 'forbidden_onsh_s_channels': self.get('forbidden_onsh_s_channels'),
3419 'forbidden_s_channels': self.get('forbidden_s_channels'),
3420 'forbidden_particles': self.get('forbidden_particles'),
3421 'perturbation_couplings': self.get('perturbation_couplings'),
3422 'is_decay_chain': self.get('is_decay_chain'),
3423 'overall_orders': self.get('overall_orders'),
3424 'split_orders': self.get('split_orders'),
3425 'NLO_mode': self.get('NLO_mode')
3426 })
3427
3428 - def get_process(self, initial_state_ids, final_state_ids):
3429 """ Return a Process object which has the same properties of this
3430 ProcessDefinition but with the specified given leg ids. """
3431
3432
3433
3434 my_isids = [leg.get('ids') for leg in self.get('legs') \
3435 if not leg.get('state')]
3436 my_fsids = [leg.get('ids') for leg in self.get('legs') \
3437 if leg.get('state')]
3438 for i, is_id in enumerate(initial_state_ids):
3439 assert is_id in my_isids[i]
3440 for i, fs_id in enumerate(final_state_ids):
3441 assert fs_id in my_fsids[i]
3442
3443 return self.get_process_with_legs(LegList(\
3444 [Leg({'id': id, 'state':False}) for id in initial_state_ids] + \
3445 [Leg({'id': id, 'state':True}) for id in final_state_ids]))
3446
3448 """Overloading the equality operator, so that only comparison
3449 of process id and legs is being done, using compare_for_sort."""
3450
3451 return super(Process, self).__eq__(other)
3452
3457 """List of ProcessDefinition objects
3458 """
3459
3461 """Test if object obj is a valid ProcessDefinition for the list."""
3462
3463 return isinstance(obj, ProcessDefinition)
3464
3470 """Make sure there are no doublets in the list doubletlist.
3471 Note that this is a slow implementation, so don't use if speed
3472 is needed"""
3473
3474 assert isinstance(doubletlist, list), \
3475 "Argument to make_unique must be list"
3476
3477
3478 uniquelist = []
3479 for elem in doubletlist:
3480 if elem not in uniquelist:
3481 uniquelist.append(elem)
3482
3483 doubletlist[:] = uniquelist[:]
3484