1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Methods and classes to group subprocesses according to initial
16 states, and produce the corresponding grouped subprocess directories."""
17
18 from __future__ import absolute_import
19 import array
20 import copy
21 import fractions
22 import glob
23 import itertools
24 import logging
25 import os
26 import re
27 import shutil
28 import subprocess
29
30 import madgraph.core.base_objects as base_objects
31 import madgraph.loop.loop_base_objects as loop_base_objects
32 import madgraph.core.diagram_generation as diagram_generation
33 import madgraph.core.helas_objects as helas_objects
34 import madgraph.iolibs.drawing_eps as draw
35 import madgraph.iolibs.files as files
36 import madgraph.iolibs.file_writers as writers
37 import madgraph.iolibs.template_files as template_files
38 import madgraph.iolibs.ufo_expression_parsers as parsers
39 import madgraph.loop.loop_diagram_generation as loop_diagram_generation
40 import madgraph.loop.loop_helas_objects as loop_helas_objects
41
42 import madgraph.various.misc as misc
43
44 import aloha.create_aloha as create_aloha
45
46 import models.write_param_card as write_param_card
47 from madgraph import MG5DIR
48 from madgraph.iolibs.files import cp, ln, mv
49 from six.moves import range
50 from six.moves import zip
51 _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/'
52 logger = logging.getLogger('madgraph.group_subprocs')
59 """DiagramTag daughter class to identify diagrams giving the same
60 config. Need to compare leg number, mass, width, and color."""
61
62 @staticmethod
64 """Returns the end link for a leg needed to identify configs:
65 ((leg numer, spin, mass, width, color), number)."""
66
67 part = model.get_particle(leg.get('id'))
68 if abs(part.get('pdg_code')) in [23,25] and leg.get('state') == False:
69 part2 = model.get_particle(22)
70 mass = part2.get('mass')
71 width = part2.get('mass')
72 spin = part2.get('spin')
73 else:
74 mass = part.get('mass')
75 width = part.get('width')
76 spin = part.get('spin')
77
78 return [((leg.get('number'), spin,
79 mass, width, part.get('color')),
80 leg.get('number'))]
81
82
83 @staticmethod
85 """Returns the info needed to identify configs:
86 interaction color, mass, width."""
87
88 inter = model.get_interaction(vertex.get('id'))
89
90 if last_vertex:
91 return ((0,),)
92 else:
93 leg = vertex.get('legs')[-1]
94 part = model.get_particle(leg.get('id'))
95 if abs(part.get('pdg_code')) in [23,25] and leg.get('state') == False:
96 part2 = model.get_particle(22)
97 mass = part2.get('mass')
98 width = part2.get('width')
99 else:
100 mass = part.get('mass')
101 width = part.get('width')
102
103 return ((part.get('color'),
104 mass, width),
105 0)
106
107 @staticmethod
109 """Move the wavefunction part of propagator id appropriately"""
110
111 if len(new_vertex[0]) == 1 and len(old_vertex[0]) > 1:
112
113 return (old_vertex[0], new_vertex[0][0])
114 elif len(new_vertex[0]) > 1 and len(old_vertex[0]) == 1:
115
116 return (old_vertex[0],)
117
118 raise diagram_generation.DiagramTag.DiagramTagError("Error in IdentifyConfigTag, wrong setup of vertices in link.")
119
125 """Class to group a number of amplitudes with same initial states
126 into a subprocess group"""
127
129 """Define object and give default values"""
130
131 self['number'] = 0
132 self['name'] = ""
133 self['amplitudes'] = diagram_generation.AmplitudeList()
134 self['matrix_elements'] = helas_objects.HelasMatrixElementList()
135 self['mapping_diagrams'] = []
136 self['diagram_maps'] = {}
137 self['diagrams_for_configs'] = []
138 self['amplitude_map'] = {}
139 self['matrix_element_opts'] = {}
140
141 - def filter(self, name, value):
172
174 """Return diagram property names as a nicely sorted list."""
175
176 return ['number', 'name', 'amplitudes', 'mapping_diagrams',
177 'diagram_maps', 'matrix_elements', 'amplitude_map']
178
179
180 - def get(self, name):
193
195 """Set mapping_diagrams and diagram_maps, to prepare for
196 generation of the super-config.inc files."""
197
198
199 mapping_diagrams, diagram_maps = \
200 self.find_mapping_diagrams()
201
202 self.set('mapping_diagrams', mapping_diagrams)
203 self.set('diagram_maps', diagram_maps)
204
205
206
207
232
234 """Generate a convenient name for the group, based on and
235 masses"""
236
237 beam = [l.get('id') for l in process.get('legs') if not l.get('state')]
238 fs = [(l.get('id'), l) for l in process.get('legs') if l.get('state')]
239 name = ""
240 for beam in beam:
241 part = process.get('model').get_particle(beam)
242 if part.get('mass').lower() == 'zero' and part.is_fermion() and \
243 part.get('color') != 1:
244 name += "q"
245 elif criteria == 'madweight':
246 name += part.get_name().replace('~', 'x').\
247 replace('+', 'p').replace('-', 'm')
248 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
249 part.get('color') == 1 and part.get('pdg_code') % 2 == 1:
250 name += "l"
251 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
252 part.get('color') == 1 and part.get('pdg_code') % 2 == 0:
253 name += "vl"
254 else:
255 name += part.get_name().replace('~', 'x').\
256 replace('+', 'p').replace('-', 'm')
257 name += "_"
258 for (fs_part, leg) in fs:
259 part = process.get('model').get_particle(fs_part)
260 if part.get('mass').lower() == 'zero' and part.get('color') != 1 \
261 and part.get('spin') == 2:
262 name += "q"
263 elif criteria == 'madweight':
264 name += part.get_name().replace('~', 'x').\
265 replace('+', 'p').replace('-', 'm')
266 elif part.get('mass').lower() == 'zero' and part.get('color') == 1 \
267 and part.get('spin') == 2:
268 if part.get('charge') == 0:
269 name += "vl"
270 else:
271 name += "l"
272 else:
273 name += part.get_name().replace('~', 'x').\
274 replace('+', 'p').replace('-', 'm')
275 if leg.get('polarization'):
276 if leg.get('polarization') in [[-1,1],[1,-1]]:
277 name += 'T'
278 elif leg.get('polarization') == [-1]:
279 name += 'L'
280 elif leg.get('polarization') == [1]:
281 name += 'R'
282 else:
283 name += '%s' %''.join([str(p).replace('-','m') for p in leg.get('polarization')])
284
285
286 for dc in process.get('decay_chains'):
287 name += "_" + self.generate_name(dc, criteria)
288
289 return name
290
292 """Get number of external and initial particles for this group"""
293
294 assert self.get('matrix_elements'), \
295 "Need matrix element to call get_nexternal_ninitial"
296
297 return self.get('matrix_elements')[0].\
298 get_nexternal_ninitial()
299
301 """Get number of configs for this group"""
302
303 model = self.get('matrix_elements')[0].get('processes')[0].\
304 get('model')
305
306 next, nini = self.get_nexternal_ninitial()
307
308 return sum([md.get_num_configs(model, nini) for md in
309 self.get('mapping_diagrams')])
310
382
384 """Find the diagrams (number + 1) for all subprocesses
385 corresponding to config number iconfig. Return 0 for subprocesses
386 without corresponding diagram. Note that the iconfig should
387 start at 0."""
388
389 assert self.get('diagram_maps'), \
390 "Need diagram_maps to run get_subproc_diagrams_for_config"
391
392 subproc_diagrams = []
393 for iproc in \
394 range(len(self.get('matrix_elements'))):
395 try:
396 subproc_diagrams.append(self.get('diagram_maps')[iproc].\
397 index(iconfig + 1) + 1)
398 except ValueError:
399 subproc_diagrams.append(0)
400
401 return subproc_diagrams
402
404 """Get a list of all diagrams_for_configs"""
405
406 subproc_diagrams_for_config = []
407 for iconf in range(len(self.get('mapping_diagrams'))):
408 subproc_diagrams_for_config.append(\
409 self.get_subproc_diagrams_for_config(iconf))
410
411 self['diagrams_for_configs'] = subproc_diagrams_for_config
412
413
414
415
416 @staticmethod
417 - def group_amplitudes(amplitudes, criteria='madevent', matrix_elements_opts={}):
418 """Return a SubProcessGroupList with the amplitudes divided
419 into subprocess groups"""
420
421 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
422 "Argument to group_amplitudes must be AmplitudeList"
423
424 if not criteria:
425 criteria = 'madevent'
426 assert criteria in ['madevent', 'madweight']
427
428 logger.info("Organizing processes into subprocess groups")
429
430 process_classes = SubProcessGroup.find_process_classes(amplitudes,criteria)
431 ret_list = SubProcessGroupList()
432 process_class_numbers = sorted(list(set(process_classes.values())))
433 for num in process_class_numbers:
434 amp_nums = [key for (key, val) in process_classes.items() if \
435 val == num]
436 group = SubProcessGroup({'matrix_element_opts':matrix_elements_opts})
437 group.set('amplitudes',
438 diagram_generation.AmplitudeList([amplitudes[i] for i in \
439 amp_nums]))
440 group.set('number', group.get('amplitudes')[0].get('process').\
441 get('id'))
442 group.set('name', group.generate_name(\
443 group.get('amplitudes')[0].get('process'),
444 criteria=criteria))
445 ret_list.append(group)
446
447 return ret_list
448
449 @staticmethod
451 """Find all different process classes, classified according to
452 initial state and final state. For initial state, we
453 differentiate fermions, antifermions, gluons, and masses. For
454 final state, only masses."""
455
456 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
457 "Argument to find_process_classes must be AmplitudeList"
458 assert amplitudes
459 assert criteria in ['madevent','madweight']
460
461 model = amplitudes[0].get('process').get('model')
462 proc_classes = []
463 amplitude_classes = {}
464
465 for iamp, amplitude in enumerate(amplitudes):
466 process = amplitude.get('process')
467 is_parts = [model.get_particle(l.get('id')) for l in \
468 process.get('legs') if not \
469 l.get('state')]
470 fs_parts = [model.get_particle(l.get('id')) for l in \
471 process.get('legs') if l.get('state')]
472
473
474
475
476
477
478 if (criteria=="madevent"):
479 proc_class = [ [(p.is_fermion(), ) \
480 for p in is_parts],
481 [(p.get('mass'), p.get('spin'),
482 p.get('pdg_code') % 2 if p.get('color') == 1 else 0,
483 abs(p.get('color')),l.get('onshell')) for (p, l) \
484 in zip(is_parts + fs_parts, process.get('legs'))],
485 amplitude.get('process').get('id'),
486 process.get('id')]
487 if (criteria=="madweight"):
488 proc_class = [ [(abs(p.get('pdg_code'))==5, abs(p.get('pdg_code'))==11,
489 abs(p.get('pdg_code'))==13, abs(p.get('pdg_code'))==15) for p in \
490 fs_parts],
491 amplitude.get('process').get('id')]
492
493 try:
494 amplitude_classes[iamp] = proc_classes.index(proc_class)
495 except ValueError:
496 proc_classes.append(proc_class)
497 amplitude_classes[iamp] = len(proc_classes)-1
498
499 return amplitude_classes
500
505 """List of SubProcessGroup objects"""
506
508 """Test if object obj is a valid element."""
509
510 return isinstance(obj, SubProcessGroup)
511
516
522
528
530 """Return a list of grouping where they are no groupoing over the leptons."""
531
532 output = SubProcessGroupList()
533 for group in self:
534 new_mes = {}
535 for me in group['matrix_elements']:
536 tags = {}
537 for proc in me['processes']:
538 ids = proc.get_final_ids_after_decay()
539 ids = tuple([t if abs(t) in [11, 13,15] else 0 for t in ids])
540 if ids not in tags:
541 tags[ids] = base_objects.ProcessList()
542 tags[ids].append(proc)
543 for tag in tags:
544 new_me = copy.copy(me)
545 new_me['processes'] = tags[tag]
546 if tag not in new_mes:
547 new_mes[tag] = helas_objects.HelasMatrixElementList()
548 new_mes[tag].append(new_me)
549 for tag in tags:
550 new_group = copy.copy(group)
551 new_group['matrix_elements'] = new_mes[tag]
552 new_group.set('name', new_group.generate_name(\
553 new_group['matrix_elements'][0]['processes'][0],
554 criteria='madweight'))
555 output.append(new_group)
556 return output
557
565 """Class to keep track of subprocess groups from a list of decay chains"""
566
574
575 - def filter(self, name, value):
588
590 """Return diagram property names as a nicely sorted list."""
591
592 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
593
595 """Returns a nicely formatted string of the content."""
596
597 mystr = ""
598 for igroup, group in enumerate(self.get('core_groups')):
599 mystr += " " * indent + "Group %d:\n" % (igroup + 1)
600 for amplitude in group.get('amplitudes'):
601 mystr = mystr + amplitude.nice_string(indent + 2) + "\n"
602
603 if self.get('decay_groups'):
604 mystr += " " * indent + "Decay groups:\n"
605 for dec in self.get('decay_groups'):
606 mystr = mystr + dec.nice_string(indent + 2) + "\n"
607
608 return mystr[:-1]
609
610
611
612
653
655 """Recursively identify which group process belongs to."""
656
657
658
659
660
661
662 group_assignments = []
663
664 for decay in process.get('decay_chains'):
665
666 ids = [l.get('id') for l in decay.get('legs')]
667 decay_groups = [(i, group) for (i, group) in \
668 enumerate(self.get('decay_groups')) \
669 if any([ids in [[l.get('id') for l in \
670 a.get('process').get('legs')] \
671 for a in g.get('amplitudes')] \
672 for g in group.get('core_groups')])]
673
674 for decay_group in decay_groups:
675
676 group_assignment = \
677 decay_group[1].assign_group_to_decay_process(decay)
678
679 if group_assignment:
680 group_assignments.append((decay_group[0], group_assignment))
681
682 if process.get('decay_chains') and not group_assignments:
683 return None
684
685
686
687
688 ids = [(l.get('id'),l.get('onshell')) for l in process.get('legs')]
689 core_groups = [(i, group) for (i, group) in \
690 enumerate(self.get('core_groups')) \
691 if ids in [[(l.get('id'),l.get('onshell')) for l in \
692 a.get('process').get('legs')] \
693 for a in group.get('amplitudes')] \
694 and process.get('id') == group.get('number')]
695
696 if not core_groups:
697 return None
698
699 assert len(core_groups) == 1
700
701 core_group = core_groups[0]
702
703 group_assignment = (core_group[0],
704 tuple([g for g in group_assignments]))
705
706 if not group_assignments:
707
708 return group_assignment
709
710 return group_assignment
711
712
713
714
715 @staticmethod
716 - def group_amplitudes(decay_chain_amps, criteria='madevent', matrix_elements_opts={}):
717 """Recursive function. Starting from a DecayChainAmplitude,
718 return a DecayChainSubProcessGroup with the core amplitudes
719 and decay chains divided into subprocess groups"""
720
721 assert isinstance(decay_chain_amps, diagram_generation.DecayChainAmplitudeList), \
722 "Argument to group_amplitudes must be DecayChainAmplitudeList"
723 if criteria in ['matrix', 'standalone','pythia8','standalone_cpp', False]:
724 criteria = 'madevent'
725 assert criteria in ['madevent', 'madweight']
726
727
728 amplitudes = diagram_generation.AmplitudeList()
729 for amp in decay_chain_amps:
730 amplitudes.extend(amp.get('amplitudes'))
731
732
733 core_groups = SubProcessGroup.group_amplitudes(amplitudes, criteria)
734
735 dc_subproc_group = DecayChainSubProcessGroup(\
736 {'core_groups': core_groups,
737 'decay_chain_amplitudes': decay_chain_amps})
738
739 decays = diagram_generation.DecayChainAmplitudeList()
740
741
742 for decay_chain_amp in decay_chain_amps:
743 decays.extend(decay_chain_amp.get('decay_chains'))
744
745 if decays:
746 dc_subproc_group.get('decay_groups').append(\
747 DecayChainSubProcessGroup.group_amplitudes(decays, criteria))
748
749 return dc_subproc_group
750
758 """List of DecayChainSubProcessGroup objects"""
759
764