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 if isinstance(filter,str) and filter.lower() == 'true':
391 edit_filter_manually = True
392 filter=None
393
394
395 if filter not in [None,'None']:
396 filter_func = LoopAmplitude.get_loop_filter(filter)
397 else:
398 filter_func = None
399
400 new_diag_selection = base_objects.DiagramList()
401 discarded_diags = base_objects.DiagramList()
402 i=0
403 for diag in self['loop_diagrams']:
404 if diag.get('tag')==[]:
405 raise MadGraph5Error, "Before using the user_filter, please "+\
406 "make sure that the loop diagrams have been tagged first."
407 valid_diag = True
408 i=i+1
409
410
411 if filter_func:
412 try:
413 valid_diag = filter_func(diag, structs, model, i)
414 except Exception as e:
415 raise InvalidCmd("The user-defined filter '%s' did not"%filter+
416 " returned the following error:\n > %s"%str(e))
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
471
472
473
474 if valid_diag:
475 new_diag_selection.append(diag)
476 else:
477 discarded_diags.append(diag)
478
479 self['loop_diagrams'] = new_diag_selection
480 if filter in [None,'None']:
481 warn_msg = """
482 The user-defined loop diagrams filter is turned on and discarded %d loops."""\
483 %len(discarded_diags)
484 else:
485 warn_msg = """
486 The loop diagrams filter '%s' is turned on and discarded %d loops."""\
487 %(filter,len(discarded_diags))
488 logger.warning(warn_msg)
489
491 """ Filter the loop diagrams to make sure they belong to the class
492 of coupling orders perturbed. """
493
494
495 allowedpart=[]
496 for part in self['process']['model']['particles']:
497 for order in self['process']['perturbation_couplings']:
498 if part.is_perturbating(order,self['process']['model']):
499 allowedpart.append(part.get_pdg_code())
500 break
501
502 newloopselection=base_objects.DiagramList()
503 warned=False
504 warning_msg = ("Some loop diagrams contributing to this process"+\
505 " are discarded because they are not pure (%s)-perturbation.\nMake sure"+\
506 " you did not want to include them.")%\
507 ('+'.join(self['process']['perturbation_couplings']))
508 for i,diag in enumerate(self['loop_diagrams']):
509
510
511 loop_orders=diag.get_loop_orders(self['process']['model'])
512 pert_loop_order=set(loop_orders.keys()).intersection(\
513 set(self['process']['perturbation_couplings']))
514
515
516
517
518 valid_diag=True
519 if (diag.get_loop_line_types()-set(allowedpart))!=set() or \
520 pert_loop_order==set([]):
521 valid_diag=False
522 if not warned:
523 logger.warning(warning_msg)
524 warned=True
525 if len([col for col in [
526 self['process'].get('model').get_particle(pdg).get('color') \
527 for pdg in diag.get_pdgs_attached_to_loop(\
528 self['structure_repository'])] if col!=1])==1:
529 valid_diag=False
530
531 if valid_diag:
532 newloopselection.append(diag)
533 self['loop_diagrams']=newloopselection
534
535
536
537
538
540 """ Makes sure that all non perturbed orders factorize the born diagrams
541 """
542 warning_msg = "All Born diagrams do not factorize the same sum of power(s) "+\
543 "of the the perturbed order(s) %s.\nThis is potentially dangerous"+\
544 " as the real-emission diagrams from aMC@NLO will not be consistent"+\
545 " with these virtual contributions."
546 if self['process']['has_born']:
547 trgt_summed_order = sum([self['born_diagrams'][0].get_order(order)
548 for order in self['process']['perturbation_couplings']])
549 for diag in self['born_diagrams'][1:]:
550 if sum([diag.get_order(order) for order in self['process']
551 ['perturbation_couplings']])!=trgt_summed_order:
552 logger.warning(warning_msg%' '.join(self['process']
553 ['perturbation_couplings']))
554 break
555
556 warning_msg = "All born diagrams do not factorize the same power of "+\
557 "the order %s which is not perturbed and for which you have not"+\
558 "specified any amplitude order. \nThis is potentially dangerous"+\
559 " as the real-emission diagrams from aMC@NLO will not be consistent"+\
560 " with these virtual contributions."
561 if self['process']['has_born']:
562 for order in self['process']['model']['coupling_orders']:
563 if order not in self['process']['perturbation_couplings'] and \
564 order not in user_orders.keys():
565 order_power=self['born_diagrams'][0].get_order(order)
566 for diag in self['born_diagrams'][1:]:
567 if diag.get_order(order)!=order_power:
568 logger.warning(warning_msg%order)
569 break
570
571
573 """ Return a dictionary of all the coupling orders of this diagram which
574 are not the perturbed ones."""
575 return dict([(order, diagram.get_order(order)) for \
576 order in self['process']['model']['coupling_orders'] if \
577 not order in self['process']['perturbation_couplings'] ])
578
580 """Return a string describing the coupling order configuration"""
581 res = []
582 for order in self['process']['model']['coupling_orders']:
583 try:
584 res.append('%s=%d'%(order,config[order]))
585 except KeyError:
586 res.append('%s=*'%order)
587 return ','.join(res)
588
590 """ Generates all diagrams relevant to this Loop Process """
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614 if (not self.loop_filter is None) and (loop_filter is None):
615 loop_filter = self.loop_filter
616
617 logger.debug("Generating %s "\
618 %self['process'].nice_string().replace('Process', 'process'))
619
620
621 model = self['process']['model']
622 hierarchy = model['order_hierarchy']
623
624
625
626
627 user_orders=copy.copy(self['process']['orders'])
628
629 if self['process']['has_born']:
630 bornsuccessful = self.generate_born_diagrams()
631 ldg_debug_info("# born diagrams after first generation",\
632 len(self['born_diagrams']))
633 else:
634 self['born_diagrams'] = base_objects.DiagramList()
635 bornsuccessful = True
636 logger.debug("Born diagrams generation skipped by user request.")
637
638
639 for order in self['process']['orders'].keys()+\
640 self['process']['squared_orders'].keys():
641 if not order in model.get('coupling_orders') and \
642 order != 'WEIGHTED':
643 raise InvalidCmd("Coupling order %s not found"%order +\
644 " in any interaction of the current model %s."%model['name'])
645
646
647
648
649 if self['process']['has_born']:
650 self['process']['has_born'] = self['born_diagrams']!=[]
651 self['has_born'] = self['process']['has_born']
652
653 ldg_debug_info("User input born orders",self['process']['orders'])
654 ldg_debug_info("User input squared orders",
655 self['process']['squared_orders'])
656 ldg_debug_info("User input perturbation",\
657 self['process']['perturbation_couplings'])
658
659
660
661
662
663 user_orders=copy.copy(self['process']['orders'])
664 user_squared_orders=copy.copy(self['process']['squared_orders'])
665
666
667
668
669
670
671
672 chosen_order_config={}
673 if self['process']['squared_orders']=={} and \
674 self['process']['orders']=={} and self['process']['has_born']:
675 chosen_order_config = self.choose_order_config()
676
677 discarded_configurations = []
678
679 if chosen_order_config != {}:
680 self.filter_from_order_config('born_diagrams', \
681 chosen_order_config,discarded_configurations)
682
683
684
685
686
687
688
689
690
691
692
693 self.check_factorization(user_orders)
694
695
696 self.guess_loop_orders_from_squared()
697
698
699
700
701
702
703
704
705 if [k.upper() for k in self['process']['squared_orders'].keys()] in \
706 [[],['WEIGHTED']] and self['process']['has_born']:
707 self.guess_loop_orders(user_orders)
708
709
710
711
712 for order in user_orders.keys():
713 if order in self['process']['perturbation_couplings']:
714 self['process']['orders'][order]=user_orders[order]+2
715 else:
716 self['process']['orders'][order]=user_orders[order]
717 if 'WEIGHTED' in user_orders.keys():
718 self['process']['orders']['WEIGHTED']=user_orders['WEIGHTED']+\
719 2*min([hierarchy[order] for order in \
720 self['process']['perturbation_couplings']])
721
722 ldg_debug_info("Orders used for loop generation",\
723 self['process']['orders'])
724
725
726
727 warning_msg = ("Some loop diagrams contributing to this process might "+\
728 "be discarded because they are not pure (%s)-perturbation.\nMake sure"+\
729 " there are none or that you did not want to include them.")%(\
730 ','.join(self['process']['perturbation_couplings']))
731
732 if self['process']['has_born']:
733 for order in model['coupling_orders']:
734 if order not in self['process']['perturbation_couplings']:
735 try:
736 if self['process']['orders'][order]< \
737 self['born_diagrams'].get_max_order(order):
738 logger.warning(warning_msg)
739 break
740 except KeyError:
741 pass
742
743
744 totloopsuccessful=self.generate_loop_diagrams()
745
746
747 if not self['process']['has_born'] and not self['loop_diagrams']:
748 self['process']['orders'].clear()
749 self['process']['orders'].update(user_orders)
750 return False
751
752
753
754
755 if self['process']['has_born']:
756 self.set_Born_CT()
757
758 ldg_debug_info("#UVCTDiags generated",len(self['loop_UVCT_diagrams']))
759
760
761 self['process']['orders'].clear()
762 self['process']['orders'].update(user_orders)
763
764
765
766
767 if not self['process']['has_born'] and not \
768 self['process']['squared_orders'] and not\
769 self['process']['orders'] and hierarchy:
770 pert_order_weights=[hierarchy[order] for order in \
771 self['process']['perturbation_couplings']]
772 self['process']['squared_orders']['WEIGHTED']=2*(\
773 self['loop_diagrams'].get_min_order('WEIGHTED')+\
774 max(pert_order_weights)-min(pert_order_weights))
775
776 ldg_debug_info("Squared orders after treatment",\
777 self['process']['squared_orders'])
778 ldg_debug_info("#Diags after diagram generation",\
779 len(self['loop_diagrams']))
780
781
782
783
784
785
786 if chosen_order_config != {}:
787 self.filter_from_order_config('loop_diagrams', \
788 chosen_order_config,discarded_configurations)
789
790 if discarded_configurations!=[]:
791 msg = ("The contribution%s of th%s coupling orders "+\
792 "configuration%s %s discarded :%s")%(('s','ese','s','are','\n')\
793 if len(discarded_configurations)>1 else ('','is','','is',' '))
794 msg = msg + '\n'.join(['(%s)'%self.print_config(conf) for conf \
795 in discarded_configurations])
796 msg = msg + "\nManually set the coupling orders to "+\
797 "generate %sthe contribution%s above."%(('any of ','s') if \
798 len(discarded_configurations)>1 else ('',''))
799 logger.info(msg)
800
801
802
803
804
805
806
807 regular_constraints = dict([(key,val) for (key,val) in
808 self['process']['squared_orders'].items() if val>=0])
809 negative_constraints = dict([(key,val) for (key,val) in
810 self['process']['squared_orders'].items() if val<0])
811 while True:
812 ndiag_remaining=len(self['loop_diagrams']+self['born_diagrams'])
813 self.check_squared_orders(regular_constraints)
814 if len(self['loop_diagrams']+self['born_diagrams'])==ndiag_remaining:
815 break
816
817 if negative_constraints!={}:
818
819
820
821
822
823
824
825
826 self.check_squared_orders(negative_constraints,user_squared_orders)
827
828 ldg_debug_info("#Diags after constraints",len(self['loop_diagrams']))
829 ldg_debug_info("#Born diagrams after constraints",len(self['born_diagrams']))
830 ldg_debug_info("#UVCTDiags after constraints",len(self['loop_UVCT_diagrams']))
831
832
833 tag_selected=[]
834 loop_basis=base_objects.DiagramList()
835 for diag in self['loop_diagrams']:
836 diag.tag(self['structure_repository'],model)
837
838
839 if not diag.is_wf_correction(self['structure_repository'], \
840 model) and not diag.is_vanishing_tadpole(model) and \
841 diag['canonical_tag'] not in tag_selected:
842 loop_basis.append(diag)
843 tag_selected.append(diag['canonical_tag'])
844
845 self['loop_diagrams']=loop_basis
846
847
848
849 self.filter_loop_for_perturbative_orders()
850
851 if len(self['loop_diagrams'])==0 and len(self['born_diagrams'])!=0:
852 raise InvalidCmd('All loop diagrams discarded by user selection.\n'+\
853 'Consider using a tree-level generation or relaxing the coupling'+\
854 ' order constraints.')
855
856 if not self['process']['has_born'] and not self['loop_diagrams']:
857 self['process']['squared_orders'].clear()
858 self['process']['squared_orders'].update(user_squared_orders)
859 return False
860
861
862
863 self.remove_Furry_loops(model,self['structure_repository'])
864
865
866
867
868
869 self.user_filter(model,self['structure_repository'], filter=loop_filter)
870
871
872 self.set_LoopCT_vertices()
873
874
875
876
877
878
879
880
881
882
883
884 self['process']['squared_orders'].clear()
885 self['process']['squared_orders'].update(user_squared_orders)
886
887
888
889 self.print_split_order_infos()
890
891
892 nLoopDiag = 0
893 nCT={'UV':0,'R2':0}
894 for ldiag in self['loop_UVCT_diagrams']:
895 nCT[ldiag['type'][:2]]+=len(ldiag['UVCT_couplings'])
896 for ldiag in self['loop_diagrams']:
897 nLoopDiag+=1
898 nCT['UV']+=len(ldiag.get_CT(model,'UV'))
899 nCT['R2']+=len(ldiag.get_CT(model,'R2'))
900
901
902
903
904 nLoopsIdentified = self.identify_loop_diagrams()
905 if nLoopsIdentified > 0:
906 logger.debug("A total of %d loop diagrams "%nLoopsIdentified+\
907 "were identified with equivalent ones.")
908 logger.info("Contributing diagrams generated: "+\
909 "%d Born, %d%s loops, %d R2, %d UV"%(len(self['born_diagrams']),
910 len(self['loop_diagrams']),'(+%d)'%nLoopsIdentified \
911 if nLoopsIdentified>0 else '' ,nCT['R2'],nCT['UV']))
912
913 ldg_debug_info("#Diags after filtering",len(self['loop_diagrams']))
914 ldg_debug_info("# of different structures identified",\
915 len(self['structure_repository']))
916
917 return (bornsuccessful or totloopsuccessful)
918
920 """ Uses a loop_tag characterizing the loop with only physical
921 information about it (mass, coupling, width, color, etc...) so as to
922 recognize numerically equivalent diagrams and group them together,
923 such as massless quark loops in pure QCD gluon loop amplitudes."""
924
925
926
927
928
929
930
931
932
933 diagram_identification = {}
934
935 for i, loop_diag in enumerate(self['loop_diagrams']):
936 loop_tag = loop_diag.build_loop_tag_for_diagram_identification(
937 self['process']['model'], self.get('structure_repository'),
938 use_FDStructure_ID_for_tag = True)
939
940
941
942 try:
943 diagram_identification[loop_tag].append((i+1,loop_diag))
944 except KeyError:
945 diagram_identification[loop_tag] = [(i+1,loop_diag)]
946
947
948 sorted_loop_tag_keys = sorted(diagram_identification.keys(),
949 key=lambda k:diagram_identification[k][0][0])
950
951 new_loop_diagram_base = base_objects.DiagramList([])
952 n_loops_identified = 0
953 for loop_tag in sorted_loop_tag_keys:
954 n_diag_in_class = len(diagram_identification[loop_tag])
955 n_loops_identified += n_diag_in_class-1
956 new_loop_diagram_base.append(diagram_identification[loop_tag][0][1])
957
958
959 new_loop_diagram_base[-1]['multiplier'] = n_diag_in_class
960 for ldiag in diagram_identification[loop_tag][1:]:
961 new_loop_diagram_base[-1].get('CT_vertices').extend(
962 copy.copy(ldiag[1].get('CT_vertices')))
963 if n_diag_in_class > 1:
964 ldg_debug_info("# Diagram equivalence class detected","#(%s) -> #%d"\
965 %(','.join('%d'%diag[0] for diag in diagram_identification[loop_tag][1:])+
966 (',' if n_diag_in_class==2 else ''),diagram_identification[loop_tag][0][0]))
967
968
969 self.set('loop_diagrams',new_loop_diagram_base)
970 return n_loops_identified
971
973 """This function is solely for monitoring purposes. It reports what are
974 the coupling order combination which are obtained with the diagram
975 genarated and among those which ones correspond to those selected by
976 the process definition and which ones are the extra combinations which
977 comes as a byproduct of the computation of the desired one. The typical
978 example is that if you ask for d d~ > u u~ QCD^2==2 [virt=QCD, QED],
979 you will not only get (QCD,QED)=(2,2);(2,4) which are the desired ones
980 but the code output will in principle also be able to return
981 (QCD,QED)=(4,0);(4,2);(0,4);(0,6) because they involve the same amplitudes
982 """
983
984 hierarchy = self['process']['model']['order_hierarchy']
985
986 sqorders_types=copy.copy(self['process'].get('sqorders_types'))
987
988
989 if 'WEIGHTED' not in sqorders_types:
990 sqorders_types['WEIGHTED']='<='
991
992 sorted_hierarchy = [order[0] for order in \
993 sorted(hierarchy.items(), key=lambda el: el[1])]
994
995 loop_SOs = set(tuple([d.get_order(order) for order in sorted_hierarchy])
996 for d in self['loop_diagrams']+self['loop_UVCT_diagrams'])
997
998 if self['process']['has_born']:
999 born_SOs = set(tuple([d.get_order(order) for order in \
1000 sorted_hierarchy]) for d in self['born_diagrams'])
1001 else:
1002 born_SOs = set([])
1003
1004 born_sqSOs = set(tuple([x + y for x, y in zip(b1_SO, b2_SO)]) for b1_SO
1005 in born_SOs for b2_SO in born_SOs)
1006 if self['process']['has_born']:
1007 ref_amps = born_SOs
1008 else:
1009 ref_amps = loop_SOs
1010 loop_sqSOs = set(tuple([x + y for x, y in zip(b_SO, l_SO)]) for b_SO in
1011 ref_amps for l_SO in loop_SOs)
1012
1013
1014 sorted_hierarchy.append('WEIGHTED')
1015 born_sqSOs = sorted([b_sqso+(sum([b*hierarchy[sorted_hierarchy[i]] for
1016 i, b in enumerate(b_sqso)]),) for b_sqso in born_sqSOs],
1017 key=lambda el: el[1])
1018 loop_sqSOs = sorted([l_sqso+(sum([l*hierarchy[sorted_hierarchy[i]] for
1019 i, l in enumerate(l_sqso)]),) for l_sqso in loop_sqSOs],
1020 key=lambda el: el[1])
1021
1022
1023 logger.debug("Coupling order combinations considered:"+\
1024 " (%s)"%','.join(sorted_hierarchy))
1025
1026
1027 born_considered = []
1028 loop_considered = []
1029 for i, sqSOList in enumerate([born_sqSOs,loop_sqSOs]):
1030 considered = []
1031 extra = []
1032 for sqSO in sqSOList:
1033 for sqo, constraint in self['process']['squared_orders'].items():
1034 sqo_index = sorted_hierarchy.index(sqo)
1035
1036
1037
1038 if (sqorders_types[sqo]=='==' and
1039 sqSO[sqo_index]!=constraint ) or \
1040 (sqorders_types[sqo] in ['=','<='] and
1041 sqSO[sqo_index]>constraint) or \
1042 (sqorders_types[sqo] in ['>'] and
1043 sqSO[sqo_index]<=constraint):
1044 extra.append(sqSO)
1045 break;
1046
1047
1048 considered = [sqSO for sqSO in sqSOList if sqSO not in extra]
1049
1050 if i==0:
1051 born_considered = considered
1052 name = "Born"
1053 if not self['process']['has_born']:
1054 logger.debug(" > No Born contributions for this process.")
1055 continue
1056 elif i==1:
1057 loop_considered = considered
1058 name = "loop"
1059
1060 if len(considered)==0:
1061 logger.debug(" > %s : None"%name)
1062 else:
1063 logger.debug(" > %s : %s"%(name,' '.join(['(%s,W%d)'%(
1064 ','.join(list('%d'%s for s in c[:-1])),c[-1])
1065 for c in considered])))
1066
1067 if len(extra)!=0:
1068 logger.debug(" > %s (not selected but available): %s"%(name,' '.
1069 join(['(%s,W%d)'%(','.join(list('%d'%s for s in e[:-1])),
1070 e[-1]) for e in extra])))
1071
1072
1073
1074 return (born_considered,
1075 [sqSO for sqSO in born_sqSOs if sqSO not in born_considered],
1076 loop_considered,
1077 [sqSO for sqSO in loop_sqSOs if sqSO not in loop_considered])
1078
1079
1087
1089 """ Generates all loop diagrams relevant to this NLO Process """
1090
1091
1092 self['loop_diagrams']=base_objects.DiagramList()
1093 totloopsuccessful=False
1094
1095
1096 self.lcutpartemployed=[]
1097
1098 for order in self['process']['perturbation_couplings']:
1099 ldg_debug_info("Perturbation coupling generated now ",order)
1100 lcutPart=[particle for particle in \
1101 self['process']['model']['particles'] if \
1102 (particle.is_perturbating(order, self['process']['model']) and \
1103 particle.get_pdg_code() not in \
1104 self['process']['forbidden_particles'])]
1105
1106
1107 for part in lcutPart:
1108 if part.get_pdg_code() not in self.lcutpartemployed:
1109
1110
1111
1112
1113
1114
1115
1116
1117 ldg_debug_info("Generating loop diagram with L-cut type",\
1118 part.get_name())
1119 lcutone=base_objects.Leg({'id': part.get_pdg_code(),
1120 'state': True,
1121 'loop_line': True})
1122 lcuttwo=base_objects.Leg({'id': part.get_anti_pdg_code(),
1123 'state': True,
1124 'loop_line': True})
1125 self['process'].get('legs').extend([lcutone,lcuttwo])
1126
1127
1128
1129
1130
1131
1132
1133 loopsuccessful, lcutdiaglist = \
1134 super(LoopAmplitude, self).generate_diagrams(True)
1135
1136
1137 leg_to_remove=[leg for leg in self['process']['legs'] \
1138 if leg['loop_line']]
1139 for leg in leg_to_remove:
1140 self['process']['legs'].remove(leg)
1141
1142
1143 for diag in lcutdiaglist:
1144 diag.set('type',part.get_pdg_code())
1145 self['loop_diagrams']+=lcutdiaglist
1146
1147
1148
1149 self.lcutpartemployed.append(part.get_pdg_code())
1150 self.lcutpartemployed.append(part.get_anti_pdg_code())
1151
1152 ldg_debug_info("#Diags generated w/ this L-cut particle",\
1153 len(lcutdiaglist))
1154
1155 if loopsuccessful:
1156 totloopsuccessful=True
1157
1158
1159 self.lcutpartemployed=[]
1160
1161 return totloopsuccessful
1162
1163
1165 """ Scan all born diagrams and add for each all the corresponding UV
1166 counterterms. It creates one LoopUVCTDiagram per born diagram and set
1167 of possible coupling_order (so that QCD and QED wavefunction corrections
1168 are not in the same LoopUVCTDiagram for example). Notice that this takes
1169 care only of the UV counterterm which factorize with the born and the
1170 other contributions like the UV mass renormalization are added in the
1171 function setLoopCTVertices"""
1172
1173
1174
1175
1176
1177
1178
1179
1180 UVCTvertex_interactions = base_objects.InteractionList()
1181 for inter in self['process']['model']['interactions'].get_UV():
1182 if inter.is_UVtree() and len(inter['particles'])>1 and \
1183 inter.is_perturbating(self['process']['perturbation_couplings']) \
1184 and (set(inter['orders'].keys()).intersection(\
1185 set(self['process']['perturbation_couplings'])))!=set([]) and \
1186 (any([set(loop_parts).intersection(set(self['process']\
1187 ['forbidden_particles']))==set([]) for loop_parts in \
1188 inter.get('loop_particles')]) or \
1189 inter.get('loop_particles')==[[]]):
1190 UVCTvertex_interactions.append(inter)
1191
1192
1193 self['process']['model'].get('order_hierarchy')['UVCT_SPECIAL']=0
1194 self['process']['model'].get('coupling_orders').add('UVCT_SPECIAL')
1195 for inter in UVCTvertex_interactions:
1196 neworders=copy.copy(inter.get('orders'))
1197 neworders['UVCT_SPECIAL']=1
1198 inter.set('orders',neworders)
1199
1200
1201 self['process']['model'].actualize_dictionaries(useUVCT=True)
1202
1203
1204
1205 self['process']['orders']['UVCT_SPECIAL']=1
1206
1207 UVCTsuccessful, UVCTdiagrams = \
1208 super(LoopAmplitude, self).generate_diagrams(True)
1209
1210 for UVCTdiag in UVCTdiagrams:
1211 if UVCTdiag.get_order('UVCT_SPECIAL')==1:
1212 newUVCTDiag = loop_base_objects.LoopUVCTDiagram({\
1213 'vertices':copy.deepcopy(UVCTdiag['vertices'])})
1214 UVCTinter = newUVCTDiag.get_UVCTinteraction(self['process']['model'])
1215 newUVCTDiag.set('type',UVCTinter.get('type'))
1216
1217
1218
1219 newUVCTDiag.get('UVCT_couplings').append((len([1 for loop_parts \
1220 in UVCTinter.get('loop_particles') if set(loop_parts).intersection(\
1221 set(self['process']['forbidden_particles']))==set([])])) if
1222 loop_parts!=[[]] else 1)
1223 self['loop_UVCT_diagrams'].append(newUVCTDiag)
1224
1225
1226
1227 del self['process']['orders']['UVCT_SPECIAL']
1228
1229 del self['process']['model'].get('order_hierarchy')['UVCT_SPECIAL']
1230 self['process']['model'].get('coupling_orders').remove('UVCT_SPECIAL')
1231 for inter in UVCTvertex_interactions:
1232 del inter.get('orders')['UVCT_SPECIAL']
1233
1234 self['process']['model'].actualize_dictionaries(useUVCT=False)
1235
1236
1237 for UVCTdiag in self['loop_UVCT_diagrams']:
1238 UVCTdiag.calculate_orders(self['process']['model'])
1239
1240
1241
1242
1243
1244 if not self['process']['has_born']:
1245 return UVCTsuccessful
1246
1247
1248
1249 for bornDiag in self['born_diagrams']:
1250
1251
1252
1253
1254
1255
1256
1257 LoopUVCTDiagramsAdded={}
1258 for leg in self['process']['legs']:
1259 counterterm=self['process']['model'].get_particle(abs(leg['id'])).\
1260 get('counterterm')
1261 for key, value in counterterm.items():
1262 if key[0] in self['process']['perturbation_couplings']:
1263 for laurentOrder, CTCoupling in value.items():
1264
1265 orderKey=[(key[0],2),]
1266 orderKey.sort()
1267 orderKey.append(('EpsilonOrder',-laurentOrder))
1268 CTCouplings=[CTCoupling for loop_parts in key[1] if
1269 set(loop_parts).intersection(set(self['process']\
1270 ['forbidden_particles']))==set([])]
1271 if CTCouplings!=[]:
1272 try:
1273 LoopUVCTDiagramsAdded[tuple(orderKey)].get(\
1274 'UVCT_couplings').extend(CTCouplings)
1275 except KeyError:
1276 LoopUVCTDiagramsAdded[tuple(orderKey)]=\
1277 loop_base_objects.LoopUVCTDiagram({\
1278 'vertices':copy.deepcopy(bornDiag['vertices']),
1279 'type':'UV'+('' if laurentOrder==0 else
1280 str(-laurentOrder)+'eps'),
1281 'UVCT_orders':{key[0]:2},
1282 'UVCT_couplings':CTCouplings})
1283
1284 for LoopUVCTDiagram in LoopUVCTDiagramsAdded.values():
1285 LoopUVCTDiagram.calculate_orders(self['process']['model'])
1286 self['loop_UVCT_diagrams'].append(LoopUVCTDiagram)
1287
1288 return UVCTsuccessful
1289
1291 """ Scan each loop diagram and recognizes what are the R2/UVmass
1292 CounterTerms associated to them """
1293
1294
1295
1296
1297
1298
1299
1300
1301 CT_interactions = {}
1302 for inter in self['process']['model']['interactions']:
1303 if inter.is_UVmass() or inter.is_UVloop() or inter.is_R2() and \
1304 len(inter['particles'])>1 and inter.is_perturbating(\
1305 self['process']['perturbation_couplings']):
1306
1307
1308
1309 for i, lparts in enumerate(inter['loop_particles']):
1310 keya=copy.copy(lparts)
1311 keya.sort()
1312 if inter.is_UVloop():
1313
1314
1315
1316
1317 if (set(self['process']['forbidden_particles']) & \
1318 set(lparts)) != set([]):
1319 continue
1320 else:
1321 keya=[]
1322 keyb=[part.get_pdg_code() for part in inter['particles']]
1323 keyb.sort()
1324 key=(tuple(keyb),tuple(keya))
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345 try:
1346 CT_interactions[key].append((inter['id'],i))
1347 except KeyError:
1348 CT_interactions[key]=[(inter['id'],i),]
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368 CT_added = {}
1369
1370 for diag in self['loop_diagrams']:
1371
1372
1373 searchingKeyA=[]
1374
1375 searchingKeyB=[]
1376 trackingKeyA=[]
1377 for tagElement in diag['canonical_tag']:
1378 for structID in tagElement[1]:
1379 trackingKeyA.append(structID)
1380 searchingKeyA.append(self['process']['model'].get_particle(\
1381 self['structure_repository'][structID]['binding_leg']['id']).\
1382 get_pdg_code())
1383 searchingKeyB.append(self['process']['model'].get_particle(\
1384 tagElement[0]).get('pdg_code'))
1385 searchingKeyA.sort()
1386
1387 searchingKeyB=list(set(searchingKeyB))
1388 searchingKeyB.sort()
1389 trackingKeyA.sort()
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 searchingKeySimple=(tuple(searchingKeyA),())
1410 searchingKeyLoopPart=(tuple(searchingKeyA),tuple(searchingKeyB))
1411 trackingKeySimple=(tuple(trackingKeyA),())
1412 trackingKeyLoopPart=(tuple(trackingKeyA),tuple(searchingKeyB))
1413
1414
1415
1416
1417 try:
1418 CTIDs=copy.copy(CT_interactions[searchingKeySimple])
1419 except KeyError:
1420 CTIDs=[]
1421 try:
1422 CTIDs.extend(copy.copy(CT_interactions[searchingKeyLoopPart]))
1423 except KeyError:
1424 pass
1425 if not CTIDs:
1426 continue
1427
1428
1429 try:
1430 usedIDs=copy.copy(CT_added[trackingKeySimple])
1431 except KeyError:
1432 usedIDs=[]
1433 try:
1434 usedIDs.extend(copy.copy(CT_added[trackingKeyLoopPart]))
1435 except KeyError:
1436 pass
1437
1438 for CTID in CTIDs:
1439
1440
1441 if CTID not in usedIDs and diag.get_loop_orders(\
1442 self['process']['model'])==\
1443 self['process']['model']['interaction_dict'][CTID[0]]['orders']:
1444
1445
1446 CTleglist = base_objects.LegList()
1447 for tagElement in diag['canonical_tag']:
1448 for structID in tagElement[1]:
1449 CTleglist.append(\
1450 self['structure_repository'][structID]['binding_leg'])
1451 CTVertex = base_objects.Vertex({'id':CTID[0], \
1452 'legs':CTleglist})
1453 diag['CT_vertices'].append(CTVertex)
1454
1455
1456 if self['process']['model']['interaction_dict'][CTID[0]]\
1457 ['loop_particles'][CTID[1]]==[] or \
1458 self['process']['model']['interaction_dict'][CTID[0]].\
1459 is_UVloop():
1460 try:
1461 CT_added[trackingKeySimple].append(CTID)
1462 except KeyError:
1463 CT_added[trackingKeySimple] = [CTID, ]
1464 else:
1465 try:
1466 CT_added[trackingKeyLoopPart].append(CTID)
1467 except KeyError:
1468 CT_added[trackingKeyLoopPart] = [CTID, ]
1469
1473
1475 """ Returns a DGLoopLeg list instead of the default copy_leglist
1476 defined in base_objects.Amplitude """
1477
1478 dgloopleglist=base_objects.LegList()
1479 for leg in leglist:
1480 dgloopleglist.append(loop_base_objects.DGLoopLeg(leg))
1481
1482 return dgloopleglist
1483
1485 """ Overloaded here to convert back all DGLoopLegs into Legs. """
1486 for vertexlist in vertexdoublelist:
1487 for vertex in vertexlist:
1488 if not isinstance(vertex['legs'][0],loop_base_objects.DGLoopLeg):
1489 continue
1490 vertex['legs'][:]=[leg.convert_to_leg() for leg in \
1491 vertex['legs']]
1492 return True
1493
1495 """Create a set of new legs from the info given."""
1496
1497 looplegs=[leg for leg in legs if leg['loop_line']]
1498
1499
1500
1501 model=self['process']['model']
1502 exlegs=[leg for leg in looplegs if leg['depth']==0]
1503 if(len(exlegs)==2):
1504 if(any([part['mass'].lower()=='zero' for pdg,part in model.get('particle_dict').items() if pdg==abs(exlegs[0]['id'])])):
1505 return []
1506
1507
1508 loopline=(len(looplegs)==1)
1509 mylegs = []
1510 for i, (leg_id, vert_id) in enumerate(leg_vert_ids):
1511
1512
1513
1514
1515 if not loopline or not (leg_id in self.lcutpartemployed):
1516
1517
1518
1519
1520 if len(legs)==2 and len(looplegs)==2:
1521
1522 depths=(looplegs[0]['depth'],looplegs[1]['depth'])
1523 if (0 in depths) and (-1 not in depths) and depths!=(0,0):
1524
1525
1526
1527 continue
1528
1529
1530
1531
1532
1533 depth=-1
1534
1535
1536 if len(legs)==2 and loopline and (legs[0]['depth'],\
1537 legs[1]['depth'])==(0,0):
1538 if not legs[0]['loop_line']:
1539 depth=legs[0]['id']
1540 else:
1541 depth=legs[1]['id']
1542
1543
1544 if len(legs)==1 and legs[0]['id']==leg_id:
1545 depth=legs[0]['depth']
1546
1547
1548
1549
1550 mylegs.append((loop_base_objects.DGLoopLeg({'id':leg_id,
1551 'number':number,
1552 'state':state,
1553 'from_group':True,
1554 'depth': depth,
1555 'loop_line': loopline}),
1556 vert_id))
1557 return mylegs
1558
1560 """Allow for selection of vertex ids."""
1561
1562 looplegs=[leg for leg in legs if leg['loop_line']]
1563 nonlooplegs=[leg for leg in legs if not leg['loop_line']]
1564
1565
1566 model=self['process']['model']
1567 exlegs=[leg for leg in looplegs if leg['depth']==0]
1568 if(len(exlegs)==2):
1569 if(any([part['mass'].lower()=='zero' for pdg,part in \
1570 model.get('particle_dict').items() if pdg==abs(exlegs[0]['id'])])):
1571 return []
1572
1573
1574
1575
1576 if(len(legs)==3 and len(looplegs)==2):
1577 depths=(looplegs[0]['depth'],looplegs[1]['depth'])
1578 if (0 in depths) and (-1 not in depths) and depths!=(0,0):
1579 return []
1580
1581 return vert_ids
1582
1583
1584
1586 """ Filters the diagrams according to the constraints on the squared
1587 orders in argument and wether the process has a born or not. """
1588
1589 diagRef=base_objects.DiagramList()
1590 AllLoopDiagrams=base_objects.DiagramList(self['loop_diagrams']+\
1591 self['loop_UVCT_diagrams'])
1592
1593 AllBornDiagrams=base_objects.DiagramList(self['born_diagrams'])
1594 if self['process']['has_born']:
1595 diagRef=AllBornDiagrams
1596 else:
1597 diagRef=AllLoopDiagrams
1598
1599 sqorders_types=copy.copy(self['process'].get('sqorders_types'))
1600
1601
1602
1603 if 'WEIGHTED' not in sqorders_types:
1604 sqorders_types['WEIGHTED']='<='
1605
1606 if len(diagRef)==0:
1607
1608
1609
1610
1611
1612 AllLoopDiagrams = base_objects.DiagramList()
1613
1614
1615
1616 AllLoopDiagrams = AllLoopDiagrams.apply_positive_sq_orders(diagRef,
1617 sq_order_constrains, sqorders_types)
1618
1619 if self['process']['has_born']:
1620
1621 AllBornDiagrams = AllBornDiagrams.apply_positive_sq_orders(
1622 AllLoopDiagrams+AllBornDiagrams, sq_order_constrains, sqorders_types)
1623
1624
1625 neg_orders = [(order, value) for order, value in \
1626 sq_order_constrains.items() if value<0]
1627 if len(neg_orders)==1:
1628 neg_order, neg_value = neg_orders[0]
1629
1630
1631 if self['process']['has_born']:
1632 AllBornDiagrams, target_order =\
1633 AllBornDiagrams.apply_negative_sq_order(
1634 base_objects.DiagramList(AllLoopDiagrams+AllBornDiagrams),
1635 neg_order,neg_value,sqorders_types[neg_order])
1636
1637
1638 AllLoopDiagrams = AllLoopDiagrams.apply_positive_sq_orders(
1639 diagRef,{neg_order:target_order},
1640 {neg_order:sqorders_types[neg_order]})
1641
1642
1643
1644 else:
1645 AllLoopDiagrams, target_order = \
1646 AllLoopDiagrams.apply_negative_sq_order(
1647 diagRef,neg_order,neg_value,sqorders_types[neg_order])
1648
1649
1650
1651
1652
1653 self['process']['squared_orders'][neg_order]=target_order
1654 user_squared_orders[neg_order]=target_order
1655
1656 elif len(neg_orders)>1:
1657 raise MadGraph5Error('At most one negative squared order constraint'+\
1658 ' can be specified, not %s.'%str(neg_orders))
1659
1660 if self['process']['has_born']:
1661 self['born_diagrams'] = AllBornDiagrams
1662 self['loop_diagrams']=[diag for diag in AllLoopDiagrams if not \
1663 isinstance(diag,loop_base_objects.LoopUVCTDiagram)]
1664 self['loop_UVCT_diagrams']=[diag for diag in AllLoopDiagrams if \
1665 isinstance(diag,loop_base_objects.LoopUVCTDiagram)]
1666
1668 """ This is a helper function for order_diagrams_according_to_split_orders
1669 and intended to be used from LoopHelasAmplitude only"""
1670
1671
1672
1673 diag_by_so = {}
1674
1675 for diag in diag_set:
1676 so_key = tuple([diag.get_order(order) for order in split_orders])
1677 try:
1678 diag_by_so[so_key].append(diag)
1679 except KeyError:
1680 diag_by_so[so_key]=base_objects.DiagramList([diag,])
1681
1682 so_keys = diag_by_so.keys()
1683
1684
1685 order_hierarchy = self.get('process').get('model').get('order_hierarchy')
1686 order_weights = copy.copy(order_hierarchy)
1687 for so in split_orders:
1688 if so not in order_hierarchy.keys():
1689 order_weights[so]=0
1690
1691
1692
1693
1694 so_keys = sorted(so_keys, key = lambda elem: (sum([power*order_weights[\
1695 split_orders[i]] for i,power in enumerate(elem)])))
1696
1697
1698 diag_set[:] = []
1699 for so_key in so_keys:
1700 diag_set.extend(diag_by_so[so_key])
1701
1702
1704 """ Reorder the loop and Born diagrams (if any) in group of diagrams
1705 sharing the same coupling orders are put together and these groups are
1706 order in decreasing WEIGHTED orders.
1707 Notice that this function is only called for now by the
1708 LoopHelasMatrixElement instances at the output stage.
1709 """
1710
1711
1712
1713 if len(split_orders)==0:
1714 return
1715
1716 self.order_diagram_set(self['born_diagrams'], split_orders)
1717 self.order_diagram_set(self['loop_diagrams'], split_orders)
1718 self.order_diagram_set(self['loop_UVCT_diagrams'], split_orders)
1719
1724 """LoopMultiProcess: MultiProcess with loop features.
1725 """
1726
1727 @classmethod
1729 """ Return the correct amplitude type according to the characteristics
1730 of the process proc """
1731 return LoopAmplitude({"process": proc},**opts)
1732
1737 """Special mode for the LoopInduced."""
1738
1739 @classmethod
1741 """ Return the correct amplitude type according to the characteristics of
1742 the process proc """
1743 return LoopAmplitude({"process": proc, 'has_born':False},**opts)
1744