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