1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Classes for diagram generation with loop features.
16 """
17
18 import array
19 import copy
20 import itertools
21 import logging
22
23 import madgraph.loop.loop_base_objects as loop_base_objects
24 import madgraph.core.base_objects as base_objects
25 import madgraph.core.diagram_generation as diagram_generation
26 import madgraph.various.misc as misc
27
28 from madgraph import MadGraph5Error
29 from madgraph import InvalidCmd
30 logger = logging.getLogger('madgraph.loop_diagram_generation')
33
34
35
36 if not force: return
37
38 flag = "LoopGenInfo: "
39 if len(msg)>40:
40 logger.debug(flag+msg[:35]+" [...] = %s"%str(val))
41 else:
42 logger.debug(flag+msg+''.join([' ']*(40-len(msg)))+' = %s'%str(val))
43
48 """NLOAmplitude: process + list of diagrams (ordered)
49 Initialize with a process, then call generate_diagrams() to
50 generate the diagrams for the amplitude
51 """
52
76
77 - def __init__(self, argument=None, loop_filter=None):
94
96 """Return diagram property names as a nicely sorted list."""
97
98 return ['process', 'diagrams', 'has_mirror_process', 'born_diagrams',
99 'loop_diagrams','has_born',
100 'structure_repository']
101
102 - def filter(self, name, value):
103 """Filter for valid amplitude property values."""
104
105 if name == 'diagrams':
106 if not isinstance(value, base_objects.DiagramList):
107 raise self.PhysicsObjectError, \
108 "%s is not a valid DiagramList" % str(value)
109 for diag in value:
110 if not isinstance(diag,loop_base_objects.LoopDiagram) and \
111 not isinstance(diag,loop_base_objects.LoopUVCTDiagram):
112 raise self.PhysicsObjectError, \
113 "%s contains a diagram which is not an NLODiagrams." % str(value)
114 if name == 'born_diagrams':
115 if not isinstance(value, base_objects.DiagramList):
116 raise self.PhysicsObjectError, \
117 "%s is not a valid DiagramList" % str(value)
118 for diag in value:
119 if not isinstance(diag,loop_base_objects.LoopDiagram):
120 raise self.PhysicsObjectError, \
121 "%s contains a diagram which is not an NLODiagrams." % str(value)
122 if name == 'loop_diagrams':
123 if not isinstance(value, base_objects.DiagramList):
124 raise self.PhysicsObjectError, \
125 "%s is not a valid DiagramList" % str(value)
126 for diag in value:
127 if not isinstance(diag,loop_base_objects.LoopDiagram):
128 raise self.PhysicsObjectError, \
129 "%s contains a diagram which is not an NLODiagrams." % str(value)
130 if name == 'has_born':
131 if not isinstance(value, bool):
132 raise self.PhysicsObjectError, \
133 "%s is not a valid bool" % str(value)
134 if name == 'structure_repository':
135 if not isinstance(value, loop_base_objects.FDStructureList):
136 raise self.PhysicsObjectError, \
137 "%s is not a valid bool" % str(value)
138
139 else:
140 super(LoopAmplitude, self).filter(name, value)
141
142 return True
143
144 - def set(self, name, value):
160
161 - def get(self, name):
162 """Redefine get for the particular case of '*_diagrams' property"""
163
164 if name == 'diagrams':
165 if self['process'] and self['loop_diagrams'] == None:
166 self.generate_diagrams()
167 return base_objects.DiagramList(self['born_diagrams']+\
168 self['loop_diagrams']+\
169 self['loop_UVCT_diagrams'])
170
171 if name == 'born_diagrams':
172 if self['born_diagrams'] == None:
173
174 if self['process']['has_born']:
175 if self['process']:
176 self.generate_born_diagrams()
177 else:
178 self['born_diagrams']=base_objects.DiagramList()
179
180 return LoopAmplitude.__bases__[0].get(self, name)
181
182
184 """ Choose the configuration of non-perturbed coupling orders to be
185 retained for all diagrams. This is used when the user did not specify
186 any order. """
187 chosen_order_config = {}
188 min_wgt = self['born_diagrams'].get_min_order('WEIGHTED')
189
190
191 min_non_pert_order_wgt = -1
192 for diag in [d for d in self['born_diagrams'] if \
193 d.get_order('WEIGHTED')==min_wgt]:
194 non_pert_order_wgt = min_wgt - sum([diag.get_order(order)*\
195 self['process']['model']['order_hierarchy'][order] for order in \
196 self['process']['perturbation_couplings']])
197 if min_non_pert_order_wgt == -1 or \
198 non_pert_order_wgt<min_non_pert_order_wgt:
199 chosen_order_config = self.get_non_pert_order_config(diag)
200 logger.info("Chosen coupling orders configuration: (%s)"\
201 %self.print_config(chosen_order_config))
202 return chosen_order_config
203
205 """If squared orders (other than WEIGHTED) are defined, then they can be
206 used for determining what is the expected upper bound for the order
207 restricting loop diagram generation."""
208 for order, value in self['process']['squared_orders'].items():
209 if order.upper()!='WEIGHTED' and order not in self['process']['orders']:
210
211 if self['process'].get('sqorders_types')[order]=='>':
212 continue
213
214 bornminorder=self['born_diagrams'].get_min_order(order)
215 if value>=0:
216 self['process']['orders'][order]=value-bornminorder
217 elif self['process']['has_born']:
218
219
220
221
222
223 self['process']['orders'][order]=bornminorder+2*(-value-1)
224
226 """Guess the upper bound for the orders for loop diagram generation
227 based on either no squared orders or simply 'Weighted'"""
228
229 hierarchy = self['process']['model']['order_hierarchy']
230
231
232 max_pert_wgt = max([hierarchy[order] for order in \
233 self['process']['perturbation_couplings']])
234
235
236
237
238
239
240 user_min_wgt = 0
241
242
243
244
245
246
247
248
249 min_born_wgt=max(self['born_diagrams'].get_min_order('WEIGHTED'),
250 sum([hierarchy[order]*val for order, val in user_orders.items() \
251 if order!='WEIGHTED']))
252
253 if 'WEIGHTED' not in [key.upper() for key in \
254 self['process']['squared_orders'].keys()]:
255
256 self['process']['squared_orders']['WEIGHTED']= 2*(min_born_wgt+\
257 max_pert_wgt)
258
259
260
261
262
263
264
265 if self['process']['squared_orders']['WEIGHTED']>=0:
266 trgt_wgt=self['process']['squared_orders']['WEIGHTED']-min_born_wgt
267 else:
268 trgt_wgt=min_born_wgt+(-self['process']['squared_orders']['WEIGHTED']+1)*2
269
270 min_nvert=min([len([1 for vert in diag['vertices'] if vert['id']!=0]) \
271 for diag in self['born_diagrams']])
272
273 min_pert=min([hierarchy[order] for order in \
274 self['process']['perturbation_couplings']])
275
276 for order, value in hierarchy.items():
277 if order not in self['process']['orders']:
278
279
280
281 if order in self['process']['perturbation_couplings']:
282 if value!=1:
283 self['process']['orders'][order]=\
284 int((trgt_wgt-min_nvert-2)/(value-1))
285 else:
286 self['process']['orders'][order]=int(trgt_wgt)
287 else:
288 if value!=1:
289 self['process']['orders'][order]=\
290 int((trgt_wgt-min_nvert-2*min_pert)/(value-1))
291 else:
292 self['process']['orders'][order]=\
293 int(trgt_wgt-2*min_pert)
294
295
296
297
298
299 for order in self['process']['model']['coupling_orders']:
300 neworder=self['born_diagrams'].get_max_order(order)
301 if order in self['process']['perturbation_couplings']:
302 neworder+=2
303 if order not in self['process']['orders'].keys() or \
304 neworder<self['process']['orders'][order]:
305 self['process']['orders'][order]=neworder
306
308 """ Filter diags to select only the diagram with the non perturbed orders
309 configuration config and update discarded_configurations.Diags is the
310 name of the key attribute of this class containing the diagrams to
311 filter."""
312 newdiagselection = base_objects.DiagramList()
313 for diag in self[diags]:
314 diag_config = self.get_non_pert_order_config(diag)
315 if diag_config == config:
316 newdiagselection.append(diag)
317 elif diag_config not in discarded_configurations:
318 discarded_configurations.append(diag_config)
319 self[diags] = newdiagselection
320
322 """ Remove the loops which are zero because of Furry theorem. So as to
323 limit any possible mistake in case of BSM model, I limit myself here to
324 removing SM-quark loops with external legs with an odd number of photons,
325 possibly including exactly two gluons."""
326
327 new_diag_selection = base_objects.DiagramList()
328
329 n_discarded = 0
330 for diag in self['loop_diagrams']:
331 if diag.get('tag')==[]:
332 raise MadGraph5Error, "The loop diagrams should have been tagged"+\
333 " before going through the Furry filter."
334
335 loop_line_pdgs = diag.get_loop_lines_pdgs()
336 attached_pdgs = diag.get_pdgs_attached_to_loop(structs)
337 if (attached_pdgs.count(22)%2==1) and \
338 (attached_pdgs.count(21) in [0,2]) and \
339 (all(pdg in [22,21] for pdg in attached_pdgs)) and \
340 (abs(loop_line_pdgs[0]) in list(range(1,7))) and \
341 (all(abs(pdg)==abs(loop_line_pdgs[0]) for pdg in loop_line_pdgs)):
342 n_discarded += 1
343 else:
344 new_diag_selection.append(diag)
345
346 self['loop_diagrams'] = new_diag_selection
347
348 if n_discarded > 0:
349 logger.debug(("MadLoop discarded %i diagram%s because they appeared"+\
350 " to be zero because of Furry theorem.")%(n_discarded,'' if \
351 n_discarded<=1 else 's'))
352
353 @staticmethod
355 """ Returns a function which applies the filter corresponding to the
356 conditional expression encoded in filterdef."""
357
358 def filter(diag, structs, model, id):
359 """ The filter function generated '%s'."""%filterdef
360
361 loop_pdgs = diag.get_loop_lines_pdgs()
362 struct_pdgs = diag.get_pdgs_attached_to_loop(structs)
363 loop_masses = [model.get_particle(pdg).get('mass') for pdg in loop_pdgs]
364 struct_masses = [model.get_particle(pdg).get('mass') for pdg in struct_pdgs]
365 if not eval(filterdef.lower(),{'n':len(loop_pdgs),
366 'loop_pdgs':loop_pdgs,
367 'struct_pdgs':struct_pdgs,
368 'loop_masses':loop_masses,
369 'struct_masses':struct_masses,
370 'id':id}):
371 return False
372 else:
373 return True
374
375 return filter
376
378 """ User-defined user-filter. By default it is not called, but the expert
379 user can turn it on and code here is own filter. Some default examples
380 are provided here.
381 The tagging of the loop diagrams must be performed before using this
382 user loop filter"""
383
384
385
386
387 edit_filter_manually = False
388 if not edit_filter_manually and filter in [None,'None']:
389 return
390
391 if filter not in [None,'None']:
392 filter_func = LoopAmplitude.get_loop_filter(filter)
393 else:
394 filter_func = None
395
396 new_diag_selection = base_objects.DiagramList()
397 discarded_diags = base_objects.DiagramList()
398 i=0
399 for diag in self['loop_diagrams']:
400 if diag.get('tag')==[]:
401 raise MadGraph5Error, "Before using the user_filter, please "+\
402 "make sure that the loop diagrams have been tagged first."
403 valid_diag = True
404 i=i+1
405
406
407 if filter_func:
408 try:
409 valid_diag = filter_func(diag, structs, model, i)
410 except Exception as e:
411 raise InvalidCmd("The user-defined filter '%s' did not"%filter+
412 " returned the following error:\n > %s"%str(e))
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470 if valid_diag:
471 new_diag_selection.append(diag)
472 else:
473 discarded_diags.append(diag)
474
475 self['loop_diagrams'] = new_diag_selection
476 if filter in [None,'None']:
477 warn_msg = """
478 The user-defined loop diagrams filter is turned on and discarded %d loops."""\
479 %len(discarded_diags)
480 else:
481 warn_msg = """
482 The loop diagrams filter '%s' is turned on and discarded %d loops."""\
483 %(filter,len(discarded_diags))
484 logger.warning(warn_msg)
485
487 """ Filter the loop diagrams to make sure they belong to the class
488 of coupling orders perturbed. """
489
490
491 allowedpart=[]
492 for part in self['process']['model']['particles']:
493 for order in self['process']['perturbation_couplings']:
494 if part.is_perturbating(order,self['process']['model']):
495 allowedpart.append(part.get_pdg_code())
496 break
497
498 newloopselection=base_objects.DiagramList()
499 warned=False
500 warning_msg = ("Some loop diagrams contributing to this process"+\
501 " are discarded because they are not pure (%s)-perturbation.\nMake sure"+\
502 " you did not want to include them.")%\
503 ('+'.join(self['process']['perturbation_couplings']))
504 for i,diag in enumerate(self['loop_diagrams']):
505
506
507 loop_orders=diag.get_loop_orders(self['process']['model'])
508 pert_loop_order=set(loop_orders.keys()).intersection(\
509 set(self['process']['perturbation_couplings']))
510
511
512
513
514 valid_diag=True
515 if (diag.get_loop_line_types()-set(allowedpart))!=set() or \
516 pert_loop_order==set([]):
517 valid_diag=False
518 if not warned:
519 logger.warning(warning_msg)
520 warned=True
521 if len([col for col in [
522 self['process'].get('model').get_particle(pdg).get('color') \
523 for pdg in diag.get_pdgs_attached_to_loop(\
524 self['structure_repository'])] if col!=1])==1:
525 valid_diag=False
526
527 if valid_diag:
528 newloopselection.append(diag)
529 self['loop_diagrams']=newloopselection
530
531
532
533
534
536 """ Makes sure that all non perturbed orders factorize the born diagrams
537 """
538 warning_msg = "All Born diagrams do not factorize the same sum of power(s) "+\
539 "of the the perturbed order(s) %s.\nThis is potentially dangerous"+\
540 " as the real-emission diagrams from aMC@NLO will not be consistent"+\
541 " with these virtual contributions."
542 if self['process']['has_born']:
543 trgt_summed_order = sum([self['born_diagrams'][0].get_order(order)
544 for order in self['process']['perturbation_couplings']])
545 for diag in self['born_diagrams'][1:]:
546 if sum([diag.get_order(order) for order in self['process']
547 ['perturbation_couplings']])!=trgt_summed_order:
548 logger.warning(warning_msg%' '.join(self['process']
549 ['perturbation_couplings']))
550 break
551
552 warning_msg = "All born diagrams do not factorize the same power of "+\
553 "the order %s which is not perturbed and for which you have not"+\
554 "specified any amplitude order. \nThis is potentially dangerous"+\
555 " as the real-emission diagrams from aMC@NLO will not be consistent"+\
556 " with these virtual contributions."
557 if self['process']['has_born']:
558 for order in self['process']['model']['coupling_orders']:
559 if order not in self['process']['perturbation_couplings'] and \
560 order not in user_orders.keys():
561 order_power=self['born_diagrams'][0].get_order(order)
562 for diag in self['born_diagrams'][1:]:
563 if diag.get_order(order)!=order_power:
564 logger.warning(warning_msg%order)
565 break
566
567
569 """ Return a dictionary of all the coupling orders of this diagram which
570 are not the perturbed ones."""
571 return dict([(order, diagram.get_order(order)) for \
572 order in self['process']['model']['coupling_orders'] if \
573 not order in self['process']['perturbation_couplings'] ])
574
576 """Return a string describing the coupling order configuration"""
577 res = []
578 for order in self['process']['model']['coupling_orders']:
579 try:
580 res.append('%s=%d'%(order,config[order]))
581 except KeyError:
582 res.append('%s=*'%order)
583 return ','.join(res)
584
586 """ Generates all diagrams relevant to this Loop Process """
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610 if (not self.loop_filter is None) and (loop_filter is None):
611 loop_filter = self.loop_filter
612
613 logger.debug("Generating %s "\
614 %self['process'].nice_string().replace('Process', 'process'))
615
616
617 model = self['process']['model']
618 hierarchy = model['order_hierarchy']
619
620
621
622
623 user_orders=copy.copy(self['process']['orders'])
624
625 if self['process']['has_born']:
626 bornsuccessful = self.generate_born_diagrams()
627 ldg_debug_info("# born diagrams after first generation",\
628 len(self['born_diagrams']))
629 else:
630 self['born_diagrams'] = base_objects.DiagramList()
631 bornsuccessful = True
632 logger.debug("Born diagrams generation skipped by user request.")
633
634
635 for order in self['process']['orders'].keys()+\
636 self['process']['squared_orders'].keys():
637 if not order in model.get('coupling_orders') and \
638 order != 'WEIGHTED':
639 raise InvalidCmd("Coupling order %s not found"%order +\
640 " in any interaction of the current model %s."%model['name'])
641
642
643
644
645 if self['process']['has_born']:
646 self['process']['has_born'] = self['born_diagrams']!=[]
647 self['has_born'] = self['process']['has_born']
648
649 ldg_debug_info("User input born orders",self['process']['orders'])
650 ldg_debug_info("User input squared orders",
651 self['process']['squared_orders'])
652 ldg_debug_info("User input perturbation",\
653 self['process']['perturbation_couplings'])
654
655
656
657
658
659 user_orders=copy.copy(self['process']['orders'])
660 user_squared_orders=copy.copy(self['process']['squared_orders'])
661
662
663
664
665
666
667
668 chosen_order_config={}
669 if self['process']['squared_orders']=={} and \
670 self['process']['orders']=={} and self['process']['has_born']:
671 chosen_order_config = self.choose_order_config()
672
673 discarded_configurations = []
674
675 if chosen_order_config != {}:
676 self.filter_from_order_config('born_diagrams', \
677 chosen_order_config,discarded_configurations)
678
679
680
681
682
683
684
685
686
687
688
689 self.check_factorization(user_orders)
690
691
692 self.guess_loop_orders_from_squared()
693
694
695
696
697
698
699
700
701 if [k.upper() for k in self['process']['squared_orders'].keys()] in \
702 [[],['WEIGHTED']] and self['process']['has_born']:
703 self.guess_loop_orders(user_orders)
704
705
706
707
708 for order in user_orders.keys():
709 if order in self['process']['perturbation_couplings']:
710 self['process']['orders'][order]=user_orders[order]+2
711 else:
712 self['process']['orders'][order]=user_orders[order]
713 if 'WEIGHTED' in user_orders.keys():
714 self['process']['orders']['WEIGHTED']=user_orders['WEIGHTED']+\
715 2*min([hierarchy[order] for order in \
716 self['process']['perturbation_couplings']])
717
718 ldg_debug_info("Orders used for loop generation",\
719 self['process']['orders'])
720
721
722
723 warning_msg = ("Some loop diagrams contributing to this process might "+\
724 "be discarded because they are not pure (%s)-perturbation.\nMake sure"+\
725 " there are none or that you did not want to include them.")%(\
726 ','.join(self['process']['perturbation_couplings']))
727
728 if self['process']['has_born']:
729 for order in model['coupling_orders']:
730 if order not in self['process']['perturbation_couplings']:
731 try:
732 if self['process']['orders'][order]< \
733 self['born_diagrams'].get_max_order(order):
734 logger.warning(warning_msg)
735 break
736 except KeyError:
737 pass
738
739
740 totloopsuccessful=self.generate_loop_diagrams()
741
742
743 if not self['process']['has_born'] and not self['loop_diagrams']:
744 self['process']['orders'].clear()
745 self['process']['orders'].update(user_orders)
746 return False
747
748
749
750
751 if self['process']['has_born']:
752 self.set_Born_CT()
753
754 ldg_debug_info("#UVCTDiags generated",len(self['loop_UVCT_diagrams']))
755
756
757 self['process']['orders'].clear()
758 self['process']['orders'].update(user_orders)
759
760
761
762
763 if not self['process']['has_born'] and not \
764 self['process']['squared_orders'] and not\
765 self['process']['orders'] and hierarchy:
766 pert_order_weights=[hierarchy[order] for order in \
767 self['process']['perturbation_couplings']]
768 self['process']['squared_orders']['WEIGHTED']=2*(\
769 self['loop_diagrams'].get_min_order('WEIGHTED')+\
770 max(pert_order_weights)-min(pert_order_weights))
771
772 ldg_debug_info("Squared orders after treatment",\
773 self['process']['squared_orders'])
774 ldg_debug_info("#Diags after diagram generation",\
775 len(self['loop_diagrams']))
776
777
778
779
780
781
782 if chosen_order_config != {}:
783 self.filter_from_order_config('loop_diagrams', \
784 chosen_order_config,discarded_configurations)
785
786 if discarded_configurations!=[]:
787 msg = ("The contribution%s of th%s coupling orders "+\
788 "configuration%s %s discarded :%s")%(('s','ese','s','are','\n')\
789 if len(discarded_configurations)>1 else ('','is','','is',' '))
790 msg = msg + '\n'.join(['(%s)'%self.print_config(conf) for conf \
791 in discarded_configurations])
792 msg = msg + "\nManually set the coupling orders to "+\
793 "generate %sthe contribution%s above."%(('any of ','s') if \
794 len(discarded_configurations)>1 else ('',''))
795 logger.info(msg)
796
797
798
799
800
801
802
803 regular_constraints = dict([(key,val) for (key,val) in
804 self['process']['squared_orders'].items() if val>=0])
805 negative_constraints = dict([(key,val) for (key,val) in
806 self['process']['squared_orders'].items() if val<0])
807 while True:
808 ndiag_remaining=len(self['loop_diagrams']+self['born_diagrams'])
809 self.check_squared_orders(regular_constraints)
810 if len(self['loop_diagrams']+self['born_diagrams'])==ndiag_remaining:
811 break
812
813 if negative_constraints!={}:
814
815
816
817
818
819
820
821
822 self.check_squared_orders(negative_constraints,user_squared_orders)
823
824 ldg_debug_info("#Diags after constraints",len(self['loop_diagrams']))
825 ldg_debug_info("#Born diagrams after constraints",len(self['born_diagrams']))
826 ldg_debug_info("#UVCTDiags after constraints",len(self['loop_UVCT_diagrams']))
827
828
829 tag_selected=[]
830 loop_basis=base_objects.DiagramList()
831 for diag in self['loop_diagrams']:
832 diag.tag(self['structure_repository'],model)
833
834
835 if not diag.is_wf_correction(self['structure_repository'], \
836 model) and not diag.is_vanishing_tadpole(model) and \
837 diag['canonical_tag'] not in tag_selected:
838 loop_basis.append(diag)
839 tag_selected.append(diag['canonical_tag'])
840
841 self['loop_diagrams']=loop_basis
842
843
844
845 self.filter_loop_for_perturbative_orders()
846
847 if len(self['loop_diagrams'])==0 and len(self['born_diagrams'])!=0:
848 raise InvalidCmd('All loop diagrams discarded by user selection.\n'+\
849 'Consider using a tree-level generation or relaxing the coupling'+\
850 ' order constraints.')
851
852 if not self['process']['has_born'] and not self['loop_diagrams']:
853 self['process']['squared_orders'].clear()
854 self['process']['squared_orders'].update(user_squared_orders)
855 return False
856
857
858
859 self.remove_Furry_loops(model,self['structure_repository'])
860
861
862
863
864
865 self.user_filter(model,self['structure_repository'], filter=loop_filter)
866
867
868 self.set_LoopCT_vertices()
869
870
871
872
873
874
875
876
877
878
879
880 self['process']['squared_orders'].clear()
881 self['process']['squared_orders'].update(user_squared_orders)
882
883
884
885 self.print_split_order_infos()
886
887
888 nLoopDiag = 0
889 nCT={'UV':0,'R2':0}
890 for ldiag in self['loop_UVCT_diagrams']:
891 nCT[ldiag['type'][:2]]+=len(ldiag['UVCT_couplings'])
892 for ldiag in self['loop_diagrams']:
893 nLoopDiag+=1
894 nCT['UV']+=len(ldiag.get_CT(model,'UV'))
895 nCT['R2']+=len(ldiag.get_CT(model,'R2'))
896
897
898
899
900 nLoopsIdentified = self.identify_loop_diagrams()
901 if nLoopsIdentified > 0:
902 logger.debug("A total of %d loop diagrams "%nLoopsIdentified+\
903 "were identified with equivalent ones.")
904 logger.info("Contributing diagrams generated: "+\
905 "%d Born, %d%s loops, %d R2, %d UV"%(len(self['born_diagrams']),
906 len(self['loop_diagrams']),'(+%d)'%nLoopsIdentified \
907 if nLoopsIdentified>0 else '' ,nCT['R2'],nCT['UV']))
908
909 ldg_debug_info("#Diags after filtering",len(self['loop_diagrams']))
910 ldg_debug_info("# of different structures identified",\
911 len(self['structure_repository']))
912
913 return (bornsuccessful or totloopsuccessful)
914
916 """ Uses a loop_tag characterizing the loop with only physical
917 information about it (mass, coupling, width, color, etc...) so as to
918 recognize numerically equivalent diagrams and group them together,
919 such as massless quark loops in pure QCD gluon loop amplitudes."""
920
921
922
923
924
925
926
927
928
929 diagram_identification = {}
930
931 for i, loop_diag in enumerate(self['loop_diagrams']):
932 loop_tag = loop_diag.build_loop_tag_for_diagram_identification(
933 self['process']['model'], self.get('structure_repository'),
934 use_FDStructure_ID_for_tag = True)
935
936
937
938 try:
939 diagram_identification[loop_tag].append((i+1,loop_diag))
940 except KeyError:
941 diagram_identification[loop_tag] = [(i+1,loop_diag)]
942
943
944 sorted_loop_tag_keys = sorted(diagram_identification.keys(),
945 key=lambda k:diagram_identification[k][0][0])
946
947 new_loop_diagram_base = base_objects.DiagramList([])
948 n_loops_identified = 0
949 for loop_tag in sorted_loop_tag_keys:
950 n_diag_in_class = len(diagram_identification[loop_tag])
951 n_loops_identified += n_diag_in_class-1
952 new_loop_diagram_base.append(diagram_identification[loop_tag][0][1])
953
954
955 new_loop_diagram_base[-1]['multiplier'] = n_diag_in_class
956 for ldiag in diagram_identification[loop_tag][1:]:
957 new_loop_diagram_base[-1].get('CT_vertices').extend(
958 copy.copy(ldiag[1].get('CT_vertices')))
959 if n_diag_in_class > 1:
960 ldg_debug_info("# Diagram equivalence class detected","#(%s) -> #%d"\
961 %(','.join('%d'%diag[0] for diag in diagram_identification[loop_tag][1:])+
962 (',' if n_diag_in_class==2 else ''),diagram_identification[loop_tag][0][0]))
963
964
965 self.set('loop_diagrams',new_loop_diagram_base)
966 return n_loops_identified
967
969 """This function is solely for monitoring purposes. It reports what are
970 the coupling order combination which are obtained with the diagram
971 genarated and among those which ones correspond to those selected by
972 the process definition and which ones are the extra combinations which
973 comes as a byproduct of the computation of the desired one. The typical
974 example is that if you ask for d d~ > u u~ QCD^2==2 [virt=QCD, QED],
975 you will not only get (QCD,QED)=(2,2);(2,4) which are the desired ones
976 but the code output will in principle also be able to return
977 (QCD,QED)=(4,0);(4,2);(0,4);(0,6) because they involve the same amplitudes
978 """
979
980 hierarchy = self['process']['model']['order_hierarchy']
981
982 sqorders_types=copy.copy(self['process'].get('sqorders_types'))
983
984
985 if 'WEIGHTED' not in sqorders_types:
986 sqorders_types['WEIGHTED']='<='
987
988 sorted_hierarchy = [order[0] for order in \
989 sorted(hierarchy.items(), key=lambda el: el[1])]
990
991 loop_SOs = set(tuple([d.get_order(order) for order in sorted_hierarchy])
992 for d in self['loop_diagrams']+self['loop_UVCT_diagrams'])
993
994 if self['process']['has_born']:
995 born_SOs = set(tuple([d.get_order(order) for order in \
996 sorted_hierarchy]) for d in self['born_diagrams'])
997 else:
998 born_SOs = set([])
999
1000 born_sqSOs = set(tuple([x + y for x, y in zip(b1_SO, b2_SO)]) for b1_SO
1001 in born_SOs for b2_SO in born_SOs)
1002 if self['process']['has_born']:
1003 ref_amps = born_SOs
1004 else:
1005 ref_amps = loop_SOs
1006 loop_sqSOs = set(tuple([x + y for x, y in zip(b_SO, l_SO)]) for b_SO in
1007 ref_amps for l_SO in loop_SOs)
1008
1009
1010 sorted_hierarchy.append('WEIGHTED')
1011 born_sqSOs = sorted([b_sqso+(sum([b*hierarchy[sorted_hierarchy[i]] for
1012 i, b in enumerate(b_sqso)]),) for b_sqso in born_sqSOs],
1013 key=lambda el: el[1])
1014 loop_sqSOs = sorted([l_sqso+(sum([l*hierarchy[sorted_hierarchy[i]] for
1015 i, l in enumerate(l_sqso)]),) for l_sqso in loop_sqSOs],
1016 key=lambda el: el[1])
1017
1018
1019 logger.debug("Coupling order combinations considered:"+\
1020 " (%s)"%','.join(sorted_hierarchy))
1021
1022
1023 born_considered = []
1024 loop_considered = []
1025 for i, sqSOList in enumerate([born_sqSOs,loop_sqSOs]):
1026 considered = []
1027 extra = []
1028 for sqSO in sqSOList:
1029 for sqo, constraint in self['process']['squared_orders'].items():
1030 sqo_index = sorted_hierarchy.index(sqo)
1031
1032
1033
1034 if (sqorders_types[sqo]=='==' and
1035 sqSO[sqo_index]!=constraint ) or \
1036 (sqorders_types[sqo] in ['=','<='] and
1037 sqSO[sqo_index]>constraint) or \
1038 (sqorders_types[sqo] in ['>'] and
1039 sqSO[sqo_index]<=constraint):
1040 extra.append(sqSO)
1041 break;
1042
1043
1044 considered = [sqSO for sqSO in sqSOList if sqSO not in extra]
1045
1046 if i==0:
1047 born_considered = considered
1048 name = "Born"
1049 if not self['process']['has_born']:
1050 logger.debug(" > No Born contributions for this process.")
1051 continue
1052 elif i==1:
1053 loop_considered = considered
1054 name = "loop"
1055
1056 if len(considered)==0:
1057 logger.debug(" > %s : None"%name)
1058 else:
1059 logger.debug(" > %s : %s"%(name,' '.join(['(%s,W%d)'%(
1060 ','.join(list('%d'%s for s in c[:-1])),c[-1])
1061 for c in considered])))
1062
1063 if len(extra)!=0:
1064 logger.debug(" > %s (not selected but available): %s"%(name,' '.
1065 join(['(%s,W%d)'%(','.join(list('%d'%s for s in e[:-1])),
1066 e[-1]) for e in extra])))
1067
1068
1069
1070 return (born_considered,
1071 [sqSO for sqSO in born_sqSOs if sqSO not in born_considered],
1072 loop_considered,
1073 [sqSO for sqSO in loop_sqSOs if sqSO not in loop_considered])
1074
1075
1083
1085 """ Generates all loop diagrams relevant to this NLO Process """
1086
1087
1088 self['loop_diagrams']=base_objects.DiagramList()
1089 totloopsuccessful=False
1090
1091
1092 self.lcutpartemployed=[]
1093
1094 for order in self['process']['perturbation_couplings']:
1095 ldg_debug_info("Perturbation coupling generated now ",order)
1096 lcutPart=[particle for particle in \
1097 self['process']['model']['particles'] if \
1098 (particle.is_perturbating(order, self['process']['model']) and \
1099 particle.get_pdg_code() not in \
1100 self['process']['forbidden_particles'])]
1101
1102
1103 for part in lcutPart:
1104 if part.get_pdg_code() not in self.lcutpartemployed:
1105
1106
1107
1108
1109
1110
1111
1112
1113 ldg_debug_info("Generating loop diagram with L-cut type",\
1114 part.get_name())
1115 lcutone=base_objects.Leg({'id': part.get_pdg_code(),
1116 'state': True,
1117 'loop_line': True})
1118 lcuttwo=base_objects.Leg({'id': part.get_anti_pdg_code(),
1119 'state': True,
1120 'loop_line': True})
1121 self['process'].get('legs').extend([lcutone,lcuttwo])
1122
1123
1124
1125
1126
1127
1128
1129 loopsuccessful, lcutdiaglist = \
1130 super(LoopAmplitude, self).generate_diagrams(True)
1131
1132
1133 leg_to_remove=[leg for leg in self['process']['legs'] \
1134 if leg['loop_line']]
1135 for leg in leg_to_remove:
1136 self['process']['legs'].remove(leg)
1137
1138
1139 for diag in lcutdiaglist:
1140 diag.set('type',part.get_pdg_code())
1141 self['loop_diagrams']+=lcutdiaglist
1142
1143
1144
1145 self.lcutpartemployed.append(part.get_pdg_code())
1146 self.lcutpartemployed.append(part.get_anti_pdg_code())
1147
1148 ldg_debug_info("#Diags generated w/ this L-cut particle",\
1149 len(lcutdiaglist))
1150
1151 if loopsuccessful:
1152 totloopsuccessful=True
1153
1154
1155 self.lcutpartemployed=[]
1156
1157 return totloopsuccessful
1158
1159
1161 """ Scan all born diagrams and add for each all the corresponding UV
1162 counterterms. It creates one LoopUVCTDiagram per born diagram and set
1163 of possible coupling_order (so that QCD and QED wavefunction corrections
1164 are not in the same LoopUVCTDiagram for example). Notice that this takes
1165 care only of the UV counterterm which factorize with the born and the
1166 other contributions like the UV mass renormalization are added in the
1167 function setLoopCTVertices"""
1168
1169
1170
1171
1172
1173
1174
1175
1176 UVCTvertex_interactions = base_objects.InteractionList()
1177 for inter in self['process']['model']['interactions'].get_UV():
1178 if inter.is_UVtree() and len(inter['particles'])>1 and \
1179 inter.is_perturbating(self['process']['perturbation_couplings']) \
1180 and (set(inter['orders'].keys()).intersection(\
1181 set(self['process']['perturbation_couplings'])))!=set([]) and \
1182 (any([set(loop_parts).intersection(set(self['process']\
1183 ['forbidden_particles']))==set([]) for loop_parts in \
1184 inter.get('loop_particles')]) or \
1185 inter.get('loop_particles')==[[]]):
1186 UVCTvertex_interactions.append(inter)
1187
1188
1189 self['process']['model'].get('order_hierarchy')['UVCT_SPECIAL']=0
1190 self['process']['model'].get('coupling_orders').add('UVCT_SPECIAL')
1191 for inter in UVCTvertex_interactions:
1192 neworders=copy.copy(inter.get('orders'))
1193 neworders['UVCT_SPECIAL']=1
1194 inter.set('orders',neworders)
1195
1196
1197 self['process']['model'].actualize_dictionaries(useUVCT=True)
1198
1199
1200
1201 self['process']['orders']['UVCT_SPECIAL']=1
1202
1203 UVCTsuccessful, UVCTdiagrams = \
1204 super(LoopAmplitude, self).generate_diagrams(True)
1205
1206 for UVCTdiag in UVCTdiagrams:
1207 if UVCTdiag.get_order('UVCT_SPECIAL')==1:
1208 newUVCTDiag = loop_base_objects.LoopUVCTDiagram({\
1209 'vertices':copy.deepcopy(UVCTdiag['vertices'])})
1210 UVCTinter = newUVCTDiag.get_UVCTinteraction(self['process']['model'])
1211 newUVCTDiag.set('type',UVCTinter.get('type'))
1212
1213
1214
1215 newUVCTDiag.get('UVCT_couplings').append((len([1 for loop_parts \
1216 in UVCTinter.get('loop_particles') if set(loop_parts).intersection(\
1217 set(self['process']['forbidden_particles']))==set([])])) if
1218 loop_parts!=[[]] else 1)
1219 self['loop_UVCT_diagrams'].append(newUVCTDiag)
1220
1221
1222
1223 del self['process']['orders']['UVCT_SPECIAL']
1224
1225 del self['process']['model'].get('order_hierarchy')['UVCT_SPECIAL']
1226 self['process']['model'].get('coupling_orders').remove('UVCT_SPECIAL')
1227 for inter in UVCTvertex_interactions:
1228 del inter.get('orders')['UVCT_SPECIAL']
1229
1230 self['process']['model'].actualize_dictionaries(useUVCT=False)
1231
1232
1233 for UVCTdiag in self['loop_UVCT_diagrams']:
1234 UVCTdiag.calculate_orders(self['process']['model'])
1235
1236
1237
1238
1239
1240 if not self['process']['has_born']:
1241 return UVCTsuccessful
1242
1243
1244
1245 for bornDiag in self['born_diagrams']:
1246
1247
1248
1249
1250
1251
1252
1253 LoopUVCTDiagramsAdded={}
1254 for leg in self['process']['legs']:
1255 counterterm=self['process']['model'].get_particle(abs(leg['id'])).\
1256 get('counterterm')
1257 for key, value in counterterm.items():
1258 if key[0] in self['process']['perturbation_couplings']:
1259 for laurentOrder, CTCoupling in value.items():
1260
1261 orderKey=[(key[0],2),]
1262 orderKey.sort()
1263 orderKey.append(('EpsilonOrder',-laurentOrder))
1264 CTCouplings=[CTCoupling for loop_parts in key[1] if
1265 set(loop_parts).intersection(set(self['process']\
1266 ['forbidden_particles']))==set([])]
1267 if CTCouplings!=[]:
1268 try:
1269 LoopUVCTDiagramsAdded[tuple(orderKey)].get(\
1270 'UVCT_couplings').extend(CTCouplings)
1271 except KeyError:
1272 LoopUVCTDiagramsAdded[tuple(orderKey)]=\
1273 loop_base_objects.LoopUVCTDiagram({\
1274 'vertices':copy.deepcopy(bornDiag['vertices']),
1275 'type':'UV'+('' if laurentOrder==0 else
1276 str(-laurentOrder)+'eps'),
1277 'UVCT_orders':{key[0]:2},
1278 'UVCT_couplings':CTCouplings})
1279
1280 for LoopUVCTDiagram in LoopUVCTDiagramsAdded.values():
1281 LoopUVCTDiagram.calculate_orders(self['process']['model'])
1282 self['loop_UVCT_diagrams'].append(LoopUVCTDiagram)
1283
1284 return UVCTsuccessful
1285
1287 """ Scan each loop diagram and recognizes what are the R2/UVmass
1288 CounterTerms associated to them """
1289
1290
1291
1292
1293
1294
1295
1296
1297 CT_interactions = {}
1298 for inter in self['process']['model']['interactions']:
1299 if inter.is_UVmass() or inter.is_UVloop() or inter.is_R2() and \
1300 len(inter['particles'])>1 and inter.is_perturbating(\
1301 self['process']['perturbation_couplings']):
1302
1303
1304
1305 for i, lparts in enumerate(inter['loop_particles']):
1306 keya=copy.copy(lparts)
1307 keya.sort()
1308 if inter.is_UVloop():
1309
1310
1311
1312
1313 if (set(self['process']['forbidden_particles']) & \
1314 set(lparts)) != set([]):
1315 continue
1316 else:
1317 keya=[]
1318 keyb=[part.get_pdg_code() for part in inter['particles']]
1319 keyb.sort()
1320 key=(tuple(keyb),tuple(keya))
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341 try:
1342 CT_interactions[key].append((inter['id'],i))
1343 except KeyError:
1344 CT_interactions[key]=[(inter['id'],i),]
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364 CT_added = {}
1365
1366 for diag in self['loop_diagrams']:
1367
1368
1369 searchingKeyA=[]
1370
1371 searchingKeyB=[]
1372 trackingKeyA=[]
1373 for tagElement in diag['canonical_tag']:
1374 for structID in tagElement[1]:
1375 trackingKeyA.append(structID)
1376 searchingKeyA.append(self['process']['model'].get_particle(\
1377 self['structure_repository'][structID]['binding_leg']['id']).\
1378 get_pdg_code())
1379 searchingKeyB.append(self['process']['model'].get_particle(\
1380 tagElement[0]).get('pdg_code'))
1381 searchingKeyA.sort()
1382
1383 searchingKeyB=list(set(searchingKeyB))
1384 searchingKeyB.sort()
1385 trackingKeyA.sort()
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405 searchingKeySimple=(tuple(searchingKeyA),())
1406 searchingKeyLoopPart=(tuple(searchingKeyA),tuple(searchingKeyB))
1407 trackingKeySimple=(tuple(trackingKeyA),())
1408 trackingKeyLoopPart=(tuple(trackingKeyA),tuple(searchingKeyB))
1409
1410
1411
1412
1413 try:
1414 CTIDs=copy.copy(CT_interactions[searchingKeySimple])
1415 except KeyError:
1416 CTIDs=[]
1417 try:
1418 CTIDs.extend(copy.copy(CT_interactions[searchingKeyLoopPart]))
1419 except KeyError:
1420 pass
1421 if not CTIDs:
1422 continue
1423
1424
1425 try:
1426 usedIDs=copy.copy(CT_added[trackingKeySimple])
1427 except KeyError:
1428 usedIDs=[]
1429 try:
1430 usedIDs.extend(copy.copy(CT_added[trackingKeyLoopPart]))
1431 except KeyError:
1432 pass
1433
1434 for CTID in CTIDs:
1435
1436
1437 if CTID not in usedIDs and diag.get_loop_orders(\
1438 self['process']['model'])==\
1439 self['process']['model']['interaction_dict'][CTID[0]]['orders']:
1440
1441
1442 CTleglist = base_objects.LegList()
1443 for tagElement in diag['canonical_tag']:
1444 for structID in tagElement[1]:
1445 CTleglist.append(\
1446 self['structure_repository'][structID]['binding_leg'])
1447 CTVertex = base_objects.Vertex({'id':CTID[0], \
1448 'legs':CTleglist})
1449 diag['CT_vertices'].append(CTVertex)
1450
1451
1452 if self['process']['model']['interaction_dict'][CTID[0]]\
1453 ['loop_particles'][CTID[1]]==[] or \
1454 self['process']['model']['interaction_dict'][CTID[0]].\
1455 is_UVloop():
1456 try:
1457 CT_added[trackingKeySimple].append(CTID)
1458 except KeyError:
1459 CT_added[trackingKeySimple] = [CTID, ]
1460 else:
1461 try:
1462 CT_added[trackingKeyLoopPart].append(CTID)
1463 except KeyError:
1464 CT_added[trackingKeyLoopPart] = [CTID, ]
1465
1469
1471 """ Returns a DGLoopLeg list instead of the default copy_leglist
1472 defined in base_objects.Amplitude """
1473
1474 dgloopleglist=base_objects.LegList()
1475 for leg in leglist:
1476 dgloopleglist.append(loop_base_objects.DGLoopLeg(leg))
1477
1478 return dgloopleglist
1479
1481 """ Overloaded here to convert back all DGLoopLegs into Legs. """
1482 for vertexlist in vertexdoublelist:
1483 for vertex in vertexlist:
1484 if not isinstance(vertex['legs'][0],loop_base_objects.DGLoopLeg):
1485 continue
1486 vertex['legs'][:]=[leg.convert_to_leg() for leg in \
1487 vertex['legs']]
1488 return True
1489
1491 """Create a set of new legs from the info given."""
1492
1493 looplegs=[leg for leg in legs if leg['loop_line']]
1494
1495
1496
1497 model=self['process']['model']
1498 exlegs=[leg for leg in looplegs if leg['depth']==0]
1499 if(len(exlegs)==2):
1500 if(any([part['mass'].lower()=='zero' for pdg,part in model.get('particle_dict').items() if pdg==abs(exlegs[0]['id'])])):
1501 return []
1502
1503
1504 loopline=(len(looplegs)==1)
1505 mylegs = []
1506 for i, (leg_id, vert_id) in enumerate(leg_vert_ids):
1507
1508
1509
1510
1511 if not loopline or not (leg_id in self.lcutpartemployed):
1512
1513
1514
1515
1516 if len(legs)==2 and len(looplegs)==2:
1517
1518 depths=(looplegs[0]['depth'],looplegs[1]['depth'])
1519 if (0 in depths) and (-1 not in depths) and depths!=(0,0):
1520
1521
1522
1523 continue
1524
1525
1526
1527
1528
1529 depth=-1
1530
1531
1532 if len(legs)==2 and loopline and (legs[0]['depth'],\
1533 legs[1]['depth'])==(0,0):
1534 if not legs[0]['loop_line']:
1535 depth=legs[0]['id']
1536 else:
1537 depth=legs[1]['id']
1538
1539
1540 if len(legs)==1 and legs[0]['id']==leg_id:
1541 depth=legs[0]['depth']
1542
1543
1544
1545
1546 mylegs.append((loop_base_objects.DGLoopLeg({'id':leg_id,
1547 'number':number,
1548 'state':state,
1549 'from_group':True,
1550 'depth': depth,
1551 'loop_line': loopline}),
1552 vert_id))
1553 return mylegs
1554
1556 """Allow for selection of vertex ids."""
1557
1558 looplegs=[leg for leg in legs if leg['loop_line']]
1559 nonlooplegs=[leg for leg in legs if not leg['loop_line']]
1560
1561
1562 model=self['process']['model']
1563 exlegs=[leg for leg in looplegs if leg['depth']==0]
1564 if(len(exlegs)==2):
1565 if(any([part['mass'].lower()=='zero' for pdg,part in \
1566 model.get('particle_dict').items() if pdg==abs(exlegs[0]['id'])])):
1567 return []
1568
1569
1570
1571
1572 if(len(legs)==3 and len(looplegs)==2):
1573 depths=(looplegs[0]['depth'],looplegs[1]['depth'])
1574 if (0 in depths) and (-1 not in depths) and depths!=(0,0):
1575 return []
1576
1577 return vert_ids
1578
1579
1580
1582 """ Filters the diagrams according to the constraints on the squared
1583 orders in argument and wether the process has a born or not. """
1584
1585 diagRef=base_objects.DiagramList()
1586 AllLoopDiagrams=base_objects.DiagramList(self['loop_diagrams']+\
1587 self['loop_UVCT_diagrams'])
1588
1589 AllBornDiagrams=base_objects.DiagramList(self['born_diagrams'])
1590 if self['process']['has_born']:
1591 diagRef=AllBornDiagrams
1592 else:
1593 diagRef=AllLoopDiagrams
1594
1595 sqorders_types=copy.copy(self['process'].get('sqorders_types'))
1596
1597
1598
1599 if 'WEIGHTED' not in sqorders_types:
1600 sqorders_types['WEIGHTED']='<='
1601
1602 if len(diagRef)==0:
1603
1604
1605
1606
1607
1608 AllLoopDiagrams = base_objects.DiagramList()
1609
1610
1611
1612 AllLoopDiagrams = AllLoopDiagrams.apply_positive_sq_orders(diagRef,
1613 sq_order_constrains, sqorders_types)
1614
1615 if self['process']['has_born']:
1616
1617 AllBornDiagrams = AllBornDiagrams.apply_positive_sq_orders(
1618 AllLoopDiagrams+AllBornDiagrams, sq_order_constrains, sqorders_types)
1619
1620
1621 neg_orders = [(order, value) for order, value in \
1622 sq_order_constrains.items() if value<0]
1623 if len(neg_orders)==1:
1624 neg_order, neg_value = neg_orders[0]
1625
1626
1627 if self['process']['has_born']:
1628 AllBornDiagrams, target_order =\
1629 AllBornDiagrams.apply_negative_sq_order(
1630 base_objects.DiagramList(AllLoopDiagrams+AllBornDiagrams),
1631 neg_order,neg_value,sqorders_types[neg_order])
1632
1633
1634 AllLoopDiagrams = AllLoopDiagrams.apply_positive_sq_orders(
1635 diagRef,{neg_order:target_order},
1636 {neg_order:sqorders_types[neg_order]})
1637
1638
1639
1640 else:
1641 AllLoopDiagrams, target_order = \
1642 AllLoopDiagrams.apply_negative_sq_order(
1643 diagRef,neg_order,neg_value,sqorders_types[neg_order])
1644
1645
1646
1647
1648
1649 self['process']['squared_orders'][neg_order]=target_order
1650 user_squared_orders[neg_order]=target_order
1651
1652 elif len(neg_orders)>1:
1653 raise MadGraph5Error('At most one negative squared order constraint'+\
1654 ' can be specified, not %s.'%str(neg_orders))
1655
1656 if self['process']['has_born']:
1657 self['born_diagrams'] = AllBornDiagrams
1658 self['loop_diagrams']=[diag for diag in AllLoopDiagrams if not \
1659 isinstance(diag,loop_base_objects.LoopUVCTDiagram)]
1660 self['loop_UVCT_diagrams']=[diag for diag in AllLoopDiagrams if \
1661 isinstance(diag,loop_base_objects.LoopUVCTDiagram)]
1662
1664 """ This is a helper function for order_diagrams_according_to_split_orders
1665 and intended to be used from LoopHelasAmplitude only"""
1666
1667
1668
1669 diag_by_so = {}
1670
1671 for diag in diag_set:
1672 so_key = tuple([diag.get_order(order) for order in split_orders])
1673 try:
1674 diag_by_so[so_key].append(diag)
1675 except KeyError:
1676 diag_by_so[so_key]=base_objects.DiagramList([diag,])
1677
1678 so_keys = diag_by_so.keys()
1679
1680
1681 order_hierarchy = self.get('process').get('model').get('order_hierarchy')
1682 order_weights = copy.copy(order_hierarchy)
1683 for so in split_orders:
1684 if so not in order_hierarchy.keys():
1685 order_weights[so]=0
1686
1687
1688
1689
1690 so_keys = sorted(so_keys, key = lambda elem: (sum([power*order_weights[\
1691 split_orders[i]] for i,power in enumerate(elem)])))
1692
1693
1694 diag_set[:] = []
1695 for so_key in so_keys:
1696 diag_set.extend(diag_by_so[so_key])
1697
1698
1700 """ Reorder the loop and Born diagrams (if any) in group of diagrams
1701 sharing the same coupling orders are put together and these groups are
1702 order in decreasing WEIGHTED orders.
1703 Notice that this function is only called for now by the
1704 LoopHelasMatrixElement instances at the output stage.
1705 """
1706
1707
1708
1709 if len(split_orders)==0:
1710 return
1711
1712 self.order_diagram_set(self['born_diagrams'], split_orders)
1713 self.order_diagram_set(self['loop_diagrams'], split_orders)
1714 self.order_diagram_set(self['loop_UVCT_diagrams'], split_orders)
1715
1720 """LoopMultiProcess: MultiProcess with loop features.
1721 """
1722
1723 @classmethod
1725 """ Return the correct amplitude type according to the characteristics
1726 of the process proc """
1727 return LoopAmplitude({"process": proc},**opts)
1728
1733 """Special mode for the LoopInduced."""
1734
1735 @classmethod
1737 """ Return the correct amplitude type according to the characteristics of
1738 the process proc """
1739 return LoopAmplitude({"process": proc, 'has_born':False},**opts)
1740