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 import array
19 import copy
20 import fractions
21 import glob
22 import itertools
23 import logging
24 import os
25 import re
26 import shutil
27 import subprocess
28
29 import madgraph.core.base_objects as base_objects
30 import madgraph.loop.loop_base_objects as loop_base_objects
31 import madgraph.core.diagram_generation as diagram_generation
32 import madgraph.core.helas_objects as helas_objects
33 import madgraph.iolibs.drawing_eps as draw
34 import madgraph.iolibs.files as files
35 import madgraph.iolibs.file_writers as writers
36 import madgraph.iolibs.template_files as template_files
37 import madgraph.iolibs.ufo_expression_parsers as parsers
38 import madgraph.loop.loop_diagram_generation as loop_diagram_generation
39 import madgraph.loop.loop_helas_objects as loop_helas_objects
40
41 import madgraph.various.misc as misc
42
43 import aloha.create_aloha as create_aloha
44
45 import models.write_param_card as write_param_card
46 from madgraph import MG5DIR
47 from madgraph.iolibs.files import cp, ln, mv
48 _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/'
49 logger = logging.getLogger('madgraph.group_subprocs')
56 """DiagramTag daughter class to identify diagrams giving the same
57 config. Need to compare leg number, mass, width, and color."""
58
59 @staticmethod
61 """Returns the end link for a leg needed to identify configs:
62 ((leg numer, spin, mass, width, color), number)."""
63
64 part = model.get_particle(leg.get('id'))
65
66 return [((leg.get('number'), part.get('spin'),
67 part.get('mass'), part.get('width'), part.get('color')),
68 leg.get('number'))]
69
70 @staticmethod
72 """Returns the info needed to identify configs:
73 interaction color, mass, width."""
74
75 inter = model.get_interaction(vertex.get('id'))
76
77 if last_vertex:
78 return ((0,),)
79 else:
80 part = model.get_particle(vertex.get('legs')[-1].get('id'))
81 return ((part.get('color'),
82 part.get('mass'), part.get('width')),
83 0)
84
85 @staticmethod
87 """Move the wavefunction part of propagator id appropriately"""
88
89 if len(new_vertex[0]) == 1 and len(old_vertex[0]) > 1:
90
91 return (old_vertex[0], new_vertex[0][0])
92 elif len(new_vertex[0]) > 1 and len(old_vertex[0]) == 1:
93
94 return (old_vertex[0],)
95
96 raise diagram_generation.DiagramTag.DiagramTagError, \
97 "Error in IdentifyConfigTag, wrong setup of vertices in link."
98
104 """Class to group a number of amplitudes with same initial states
105 into a subprocess group"""
106
108 """Define object and give default values"""
109
110 self['number'] = 0
111 self['name'] = ""
112 self['amplitudes'] = diagram_generation.AmplitudeList()
113 self['matrix_elements'] = helas_objects.HelasMatrixElementList()
114 self['mapping_diagrams'] = []
115 self['diagram_maps'] = {}
116 self['diagrams_for_configs'] = []
117 self['amplitude_map'] = {}
118 self['matrix_element_opts'] = {}
119
120 - def filter(self, name, value):
121 """Filter for valid property values."""
122
123 if name == 'number':
124 if not isinstance(value, int):
125 raise self.PhysicsObjectError, \
126 "%s is not a valid int object" % str(value)
127 if name == 'name':
128 if not isinstance(value, str):
129 raise self.PhysicsObjectError, \
130 "%s is not a valid str object" % str(value)
131 if name == 'amplitudes':
132 if not isinstance(value, diagram_generation.AmplitudeList):
133 raise self.PhysicsObjectError, \
134 "%s is not a valid amplitudelist" % str(value)
135 if name in ['mapping_diagrams', 'diagrams_for_configs']:
136 if not isinstance(value, list):
137 raise self.PhysicsObjectError, \
138 "%s is not a valid list" % str(value)
139 if name == 'diagram_maps':
140 if not isinstance(value, dict):
141 raise self.PhysicsObjectError, \
142 "%s is not a valid dict" % str(value)
143 if name == 'matrix_elements':
144 if not isinstance(value, helas_objects.HelasMatrixElementList):
145 raise self.PhysicsObjectError, \
146 "%s is not a valid HelasMatrixElementList" % str(value)
147
148 if name == 'amplitude_map':
149 if not isinstance(value, dict):
150 raise self.PhysicsObjectError, \
151 "%s is not a valid dict object" % str(value)
152
153 if name == 'matrix_element_opts':
154 if not isinstance(value, dict):
155 raise self.PhysicsObjectError, \
156 "%s is not a valid dictionary object" % str(value)
157
158 return True
159
161 """Return diagram property names as a nicely sorted list."""
162
163 return ['number', 'name', 'amplitudes', 'mapping_diagrams',
164 'diagram_maps', 'matrix_elements', 'amplitude_map']
165
166
167 - def get(self, name):
180
182 """Set mapping_diagrams and diagram_maps, to prepare for
183 generation of the super-config.inc files."""
184
185
186 mapping_diagrams, diagram_maps = \
187 self.find_mapping_diagrams()
188
189 self.set('mapping_diagrams', mapping_diagrams)
190 self.set('diagram_maps', diagram_maps)
191
192
193
194
220
222 """Generate a convenient name for the group, based on and
223 masses"""
224
225 beam = [l.get('id') for l in process.get('legs') if not l.get('state')]
226 fs = [l.get('id') for l in process.get('legs') if l.get('state')]
227 name = ""
228 for beam in beam:
229 part = process.get('model').get_particle(beam)
230 if part.get('mass').lower() == 'zero' and part.is_fermion() and \
231 part.get('color') != 1:
232 name += "q"
233 elif criteria == 'madweight':
234 name += part.get_name().replace('~', 'x').\
235 replace('+', 'p').replace('-', 'm')
236 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
237 part.get('color') == 1 and part.get('pdg_code') % 2 == 1:
238 name += "l"
239 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
240 part.get('color') == 1 and part.get('pdg_code') % 2 == 0:
241 name += "vl"
242 else:
243 name += part.get_name().replace('~', 'x').\
244 replace('+', 'p').replace('-', 'm')
245 name += "_"
246 for fs_part in fs:
247 part = process.get('model').get_particle(fs_part)
248 if part.get('mass').lower() == 'zero' and part.get('color') != 1 \
249 and part.get('spin') == 2:
250 name += "q"
251 elif criteria == 'madweight':
252 name += part.get_name().replace('~', 'x').\
253 replace('+', 'p').replace('-', 'm')
254 elif part.get('mass').lower() == 'zero' and part.get('color') == 1 \
255 and part.get('spin') == 2:
256 if part.get('charge') == 0:
257 name += "vl"
258 else:
259 name += "l"
260 else:
261 name += part.get_name().replace('~', 'x').\
262 replace('+', 'p').replace('-', 'm')
263
264 for dc in process.get('decay_chains'):
265 name += "_" + self.generate_name(dc, criteria)
266
267 return name
268
270 """Get number of external and initial particles for this group"""
271
272 assert self.get('matrix_elements'), \
273 "Need matrix element to call get_nexternal_ninitial"
274
275 return self.get('matrix_elements')[0].\
276 get_nexternal_ninitial()
277
279 """Get number of configs for this group"""
280
281 model = self.get('matrix_elements')[0].get('processes')[0].\
282 get('model')
283
284 next, nini = self.get_nexternal_ninitial()
285
286 return sum([md.get_num_configs(model, nini) for md in
287 self.get('mapping_diagrams')])
288
290 """Find all unique diagrams for all processes in this
291 process class, and the mapping of their diagrams unto this
292 unique diagram."""
293
294 assert self.get('matrix_elements'), \
295 "Need matrix elements to run find_mapping_diagrams"
296
297 matrix_elements = self.get('matrix_elements')
298 model = matrix_elements[0].get('processes')[0].get('model')
299
300
301 mapping_diagrams = []
302
303
304 equiv_diagrams = []
305
306
307
308 diagram_maps = {}
309
310 for ime, me in enumerate(matrix_elements):
311
312
313
314
315
316 if isinstance(me, loop_helas_objects.LoopHelasMatrixElement):
317 FDStructRepo = loop_base_objects.FDStructureList([])
318 diagrams = [(d.get_contracted_loop_diagram(model,FDStructRepo) if
319 isinstance(d,loop_base_objects.LoopDiagram) else d) for d in
320 me.get('base_amplitude').get('loop_diagrams') if d.get('type')>0]
321 else:
322 diagrams = me.get('base_amplitude').get('diagrams')
323
324
325
326 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in \
327 diagrams if diag.get_vertex_leg_numbers()!=[]]
328 minvert = min(vert_list) if vert_list!=[] else 0
329
330 diagram_maps[ime] = []
331
332 for diagram in diagrams:
333
334
335
336
337 if diagram.get_vertex_leg_numbers()!=[] and \
338 max(diagram.get_vertex_leg_numbers()) > minvert:
339 diagram_maps[ime].append(0)
340 continue
341
342
343
344 equiv_diag = IdentifyConfigTag(diagram, model)
345 try:
346 diagram_maps[ime].append(equiv_diagrams.index(\
347 equiv_diag) + 1)
348 except ValueError:
349 equiv_diagrams.append(equiv_diag)
350 mapping_diagrams.append(diagram)
351 diagram_maps[ime].append(equiv_diagrams.index(\
352 equiv_diag) + 1)
353 return mapping_diagrams, diagram_maps
354
356 """Find the diagrams (number + 1) for all subprocesses
357 corresponding to config number iconfig. Return 0 for subprocesses
358 without corresponding diagram. Note that the iconfig should
359 start at 0."""
360
361 assert self.get('diagram_maps'), \
362 "Need diagram_maps to run get_subproc_diagrams_for_config"
363
364 subproc_diagrams = []
365 for iproc in \
366 range(len(self.get('matrix_elements'))):
367 try:
368 subproc_diagrams.append(self.get('diagram_maps')[iproc].\
369 index(iconfig + 1) + 1)
370 except ValueError:
371 subproc_diagrams.append(0)
372
373 return subproc_diagrams
374
376 """Get a list of all diagrams_for_configs"""
377
378 subproc_diagrams_for_config = []
379 for iconf in range(len(self.get('mapping_diagrams'))):
380 subproc_diagrams_for_config.append(\
381 self.get_subproc_diagrams_for_config(iconf))
382
383 self['diagrams_for_configs'] = subproc_diagrams_for_config
384
385
386
387
388 @staticmethod
389 - def group_amplitudes(amplitudes, criteria='madevent', matrix_elements_opts={}):
390 """Return a SubProcessGroupList with the amplitudes divided
391 into subprocess groups"""
392
393 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
394 "Argument to group_amplitudes must be AmplitudeList"
395
396 if not criteria:
397 criteria = 'madevent'
398 assert criteria in ['madevent', 'madweight']
399
400 logger.info("Organizing processes into subprocess groups")
401
402 process_classes = SubProcessGroup.find_process_classes(amplitudes,criteria)
403 ret_list = SubProcessGroupList()
404 process_class_numbers = sorted(list(set(process_classes.values())))
405 for num in process_class_numbers:
406 amp_nums = [key for (key, val) in process_classes.items() if \
407 val == num]
408 group = SubProcessGroup({'matrix_element_opts':matrix_elements_opts})
409 group.set('amplitudes',
410 diagram_generation.AmplitudeList([amplitudes[i] for i in \
411 amp_nums]))
412 group.set('number', group.get('amplitudes')[0].get('process').\
413 get('id'))
414 group.set('name', group.generate_name(\
415 group.get('amplitudes')[0].get('process'),
416 criteria=criteria))
417 ret_list.append(group)
418
419 return ret_list
420
421 @staticmethod
423 """Find all different process classes, classified according to
424 initial state and final state. For initial state, we
425 differentiate fermions, antifermions, gluons, and masses. For
426 final state, only masses."""
427
428 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
429 "Argument to find_process_classes must be AmplitudeList"
430 assert amplitudes
431 assert criteria in ['madevent','madweight']
432
433 model = amplitudes[0].get('process').get('model')
434 proc_classes = []
435 amplitude_classes = {}
436
437 for iamp, amplitude in enumerate(amplitudes):
438 process = amplitude.get('process')
439 is_parts = [model.get_particle(l.get('id')) for l in \
440 process.get('legs') if not \
441 l.get('state')]
442 fs_parts = [model.get_particle(l.get('id')) for l in \
443 process.get('legs') if l.get('state')]
444
445
446
447
448
449
450 if (criteria=="madevent"):
451 proc_class = [ [(p.is_fermion(), ) \
452 for p in is_parts],
453 [(p.get('mass'), p.get('spin'),
454 p.get('pdg_code') % 2 if p.get('color') == 1 else 0,
455 abs(p.get('color')),l.get('onshell')) for (p, l) \
456 in zip(is_parts + fs_parts, process.get('legs'))],
457 amplitude.get('process').get('id'),
458 process.get('id')]
459 if (criteria=="madweight"):
460 proc_class = [ [(abs(p.get('pdg_code'))==5, abs(p.get('pdg_code'))==11,
461 abs(p.get('pdg_code'))==13, abs(p.get('pdg_code'))==15) for p in \
462 fs_parts],
463 amplitude.get('process').get('id')]
464
465 try:
466 amplitude_classes[iamp] = proc_classes.index(proc_class)
467 except ValueError:
468 proc_classes.append(proc_class)
469 amplitude_classes[iamp] = len(proc_classes)-1
470
471 return amplitude_classes
472
477 """List of SubProcessGroup objects"""
478
480 """Test if object obj is a valid element."""
481
482 return isinstance(obj, SubProcessGroup)
483
488
494
500
502 """Return a list of grouping where they are no groupoing over the leptons."""
503
504 output = SubProcessGroupList()
505 for group in self:
506 new_mes = {}
507 for me in group['matrix_elements']:
508 tags = {}
509 for proc in me['processes']:
510 ids = proc.get_final_ids_after_decay()
511 ids = tuple([t if abs(t) in [11, 13,15] else 0 for t in ids])
512 if ids not in tags:
513 tags[ids] = base_objects.ProcessList()
514 tags[ids].append(proc)
515 for tag in tags:
516 new_me = copy.copy(me)
517 new_me['processes'] = tags[tag]
518 if tag not in new_mes:
519 new_mes[tag] = helas_objects.HelasMatrixElementList()
520 new_mes[tag].append(new_me)
521 for tag in tags:
522 new_group = copy.copy(group)
523 new_group['matrix_elements'] = new_mes[tag]
524 new_group.set('name', new_group.generate_name(\
525 new_group['matrix_elements'][0]['processes'][0],
526 criteria='madweight'))
527 output.append(new_group)
528 return output
529
537 """Class to keep track of subprocess groups from a list of decay chains"""
538
546
547 - def filter(self, name, value):
563
565 """Return diagram property names as a nicely sorted list."""
566
567 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
568
570 """Returns a nicely formatted string of the content."""
571
572 mystr = ""
573 for igroup, group in enumerate(self.get('core_groups')):
574 mystr += " " * indent + "Group %d:\n" % (igroup + 1)
575 for amplitude in group.get('amplitudes'):
576 mystr = mystr + amplitude.nice_string(indent + 2) + "\n"
577
578 if self.get('decay_groups'):
579 mystr += " " * indent + "Decay groups:\n"
580 for dec in self.get('decay_groups'):
581 mystr = mystr + dec.nice_string(indent + 2) + "\n"
582
583 return mystr[:-1]
584
585
586
587
628
630 """Recursively identify which group process belongs to."""
631
632
633
634
635
636
637 group_assignments = []
638
639 for decay in process.get('decay_chains'):
640
641 ids = [l.get('id') for l in decay.get('legs')]
642 decay_groups = [(i, group) for (i, group) in \
643 enumerate(self.get('decay_groups')) \
644 if any([ids in [[l.get('id') for l in \
645 a.get('process').get('legs')] \
646 for a in g.get('amplitudes')] \
647 for g in group.get('core_groups')])]
648
649 for decay_group in decay_groups:
650
651 group_assignment = \
652 decay_group[1].assign_group_to_decay_process(decay)
653
654 if group_assignment:
655 group_assignments.append((decay_group[0], group_assignment))
656
657 if process.get('decay_chains') and not group_assignments:
658 return None
659
660
661
662
663 ids = [(l.get('id'),l.get('onshell')) for l in process.get('legs')]
664 core_groups = [(i, group) for (i, group) in \
665 enumerate(self.get('core_groups')) \
666 if ids in [[(l.get('id'),l.get('onshell')) for l in \
667 a.get('process').get('legs')] \
668 for a in group.get('amplitudes')] \
669 and process.get('id') == group.get('number')]
670
671 if not core_groups:
672 return None
673
674 assert len(core_groups) == 1
675
676 core_group = core_groups[0]
677
678 group_assignment = (core_group[0],
679 tuple([g for g in group_assignments]))
680
681 if not group_assignments:
682
683 return group_assignment
684
685 return group_assignment
686
687
688
689
690 @staticmethod
691 - def group_amplitudes(decay_chain_amps, criteria='madevent', matrix_elements_opts={}):
692 """Recursive function. Starting from a DecayChainAmplitude,
693 return a DecayChainSubProcessGroup with the core amplitudes
694 and decay chains divided into subprocess groups"""
695
696 assert isinstance(decay_chain_amps, diagram_generation.DecayChainAmplitudeList), \
697 "Argument to group_amplitudes must be DecayChainAmplitudeList"
698 if criteria in ['matrix', 'standalone','pythia8','standalone_cpp', False]:
699 criteria = 'madevent'
700 assert criteria in ['madevent', 'madweight']
701
702
703 amplitudes = diagram_generation.AmplitudeList()
704 for amp in decay_chain_amps:
705 amplitudes.extend(amp.get('amplitudes'))
706
707
708 core_groups = SubProcessGroup.group_amplitudes(amplitudes, criteria)
709
710 dc_subproc_group = DecayChainSubProcessGroup(\
711 {'core_groups': core_groups,
712 'decay_chain_amplitudes': decay_chain_amps})
713
714 decays = diagram_generation.DecayChainAmplitudeList()
715
716
717 for decay_chain_amp in decay_chain_amps:
718 decays.extend(decay_chain_amp.get('decay_chains'))
719
720 if decays:
721 dc_subproc_group.get('decay_groups').append(\
722 DecayChainSubProcessGroup.group_amplitudes(decays, criteria))
723
724 return dc_subproc_group
725
733 """List of DecayChainSubProcessGroup objects"""
734
739