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 abs(p.get('color')),l.get('onshell')) for (p, l) \
455 in zip(is_parts + fs_parts, process.get('legs'))],
456 amplitude.get('process').get('id'),
457 process.get('id')]
458 if (criteria=="madweight"):
459 proc_class = [ [(abs(p.get('pdg_code'))==5, abs(p.get('pdg_code'))==11,
460 abs(p.get('pdg_code'))==13, abs(p.get('pdg_code'))==15) for p in \
461 fs_parts],
462 amplitude.get('process').get('id')]
463
464 try:
465 amplitude_classes[iamp] = proc_classes.index(proc_class)
466 except ValueError:
467 proc_classes.append(proc_class)
468 amplitude_classes[iamp] = len(proc_classes)-1
469
470 return amplitude_classes
471
476 """List of SubProcessGroup objects"""
477
479 """Test if object obj is a valid element."""
480
481 return isinstance(obj, SubProcessGroup)
482
487
493
499
501 """Return a list of grouping where they are no groupoing over the leptons."""
502
503 output = SubProcessGroupList()
504 for group in self:
505 new_mes = {}
506 for me in group['matrix_elements']:
507 tags = {}
508 for proc in me['processes']:
509 ids = proc.get_final_ids_after_decay()
510 ids = tuple([t if abs(t) in [11, 13,15] else 0 for t in ids])
511 if ids not in tags:
512 tags[ids] = base_objects.ProcessList()
513 tags[ids].append(proc)
514 for tag in tags:
515 new_me = copy.copy(me)
516 new_me['processes'] = tags[tag]
517 if tag not in new_mes:
518 new_mes[tag] = helas_objects.HelasMatrixElementList()
519 new_mes[tag].append(new_me)
520 for tag in tags:
521 new_group = copy.copy(group)
522 new_group['matrix_elements'] = new_mes[tag]
523 new_group.set('name', new_group.generate_name(\
524 new_group['matrix_elements'][0]['processes'][0],
525 criteria='madweight'))
526 output.append(new_group)
527 return output
528
536 """Class to keep track of subprocess groups from a list of decay chains"""
537
545
546 - def filter(self, name, value):
562
564 """Return diagram property names as a nicely sorted list."""
565
566 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
567
569 """Returns a nicely formatted string of the content."""
570
571 mystr = ""
572 for igroup, group in enumerate(self.get('core_groups')):
573 mystr += " " * indent + "Group %d:\n" % (igroup + 1)
574 for amplitude in group.get('amplitudes'):
575 mystr = mystr + amplitude.nice_string(indent + 2) + "\n"
576
577 if self.get('decay_groups'):
578 mystr += " " * indent + "Decay groups:\n"
579 for dec in self.get('decay_groups'):
580 mystr = mystr + dec.nice_string(indent + 2) + "\n"
581
582 return mystr[:-1]
583
584
585
586
627
629 """Recursively identify which group process belongs to."""
630
631
632
633
634
635
636 group_assignments = []
637
638 for decay in process.get('decay_chains'):
639
640 ids = [l.get('id') for l in decay.get('legs')]
641 decay_groups = [(i, group) for (i, group) in \
642 enumerate(self.get('decay_groups')) \
643 if any([ids in [[l.get('id') for l in \
644 a.get('process').get('legs')] \
645 for a in g.get('amplitudes')] \
646 for g in group.get('core_groups')])]
647
648 for decay_group in decay_groups:
649
650 group_assignment = \
651 decay_group[1].assign_group_to_decay_process(decay)
652
653 if group_assignment:
654 group_assignments.append((decay_group[0], group_assignment))
655
656 if process.get('decay_chains') and not group_assignments:
657 return None
658
659
660
661
662 ids = [(l.get('id'),l.get('onshell')) for l in process.get('legs')]
663 core_groups = [(i, group) for (i, group) in \
664 enumerate(self.get('core_groups')) \
665 if ids in [[(l.get('id'),l.get('onshell')) for l in \
666 a.get('process').get('legs')] \
667 for a in group.get('amplitudes')] \
668 and process.get('id') == group.get('number')]
669
670 if not core_groups:
671 return None
672
673 assert len(core_groups) == 1
674
675 core_group = core_groups[0]
676
677 group_assignment = (core_group[0],
678 tuple([g for g in group_assignments]))
679
680 if not group_assignments:
681
682 return group_assignment
683
684 return group_assignment
685
686
687
688
689 @staticmethod
690 - def group_amplitudes(decay_chain_amps, criteria='madevent', matrix_elements_opts={}):
691 """Recursive function. Starting from a DecayChainAmplitude,
692 return a DecayChainSubProcessGroup with the core amplitudes
693 and decay chains divided into subprocess groups"""
694
695 assert isinstance(decay_chain_amps, diagram_generation.DecayChainAmplitudeList), \
696 "Argument to group_amplitudes must be DecayChainAmplitudeList"
697 if criteria in ['matrix', 'standalone','pythia8','standalone_cpp', False]:
698 criteria = 'madevent'
699 assert criteria in ['madevent', 'madweight']
700
701
702 amplitudes = diagram_generation.AmplitudeList()
703 for amp in decay_chain_amps:
704 amplitudes.extend(amp.get('amplitudes'))
705
706
707 core_groups = SubProcessGroup.group_amplitudes(amplitudes, criteria)
708
709 dc_subproc_group = DecayChainSubProcessGroup(\
710 {'core_groups': core_groups,
711 'decay_chain_amplitudes': decay_chain_amps})
712
713 decays = diagram_generation.DecayChainAmplitudeList()
714
715
716 for decay_chain_amp in decay_chain_amps:
717 decays.extend(decay_chain_amp.get('decay_chains'))
718
719 if decays:
720 dc_subproc_group.get('decay_groups').append(\
721 DecayChainSubProcessGroup.group_amplitudes(decays, criteria))
722
723 return dc_subproc_group
724
732 """List of DecayChainSubProcessGroup objects"""
733
738