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