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.core.diagram_generation as diagram_generation
31 import madgraph.core.helas_objects as helas_objects
32 import madgraph.iolibs.drawing_eps as draw
33 import madgraph.iolibs.files as files
34 import madgraph.iolibs.file_writers as writers
35 import madgraph.iolibs.template_files as template_files
36 import madgraph.iolibs.ufo_expression_parsers as parsers
37
38 import madgraph.various.misc as misc
39
40 import aloha.create_aloha as create_aloha
41
42 import models.write_param_card as write_param_card
43 from madgraph import MG5DIR
44 from madgraph.iolibs.files import cp, ln, mv
45 _file_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] + '/'
46 logger = logging.getLogger('madgraph.group_subprocs')
53 """DiagramTag daughter class to identify diagrams giving the same
54 config. Need to compare leg number, mass, width, and color."""
55
56 @staticmethod
58 """Returns the end link for a leg needed to identify configs:
59 ((leg numer, spin, mass, width, color), number)."""
60
61 part = model.get_particle(leg.get('id'))
62
63 return [((leg.get('number'), part.get('spin'),
64 part.get('mass'), part.get('width'), part.get('color')),
65 leg.get('number'))]
66
67 @staticmethod
69 """Returns the info needed to identify configs:
70 interaction color, mass, width."""
71
72 inter = model.get_interaction(vertex.get('id'))
73
74 if last_vertex:
75 return ((0,),)
76 else:
77 part = model.get_particle(vertex.get('legs')[-1].get('id'))
78 return ((part.get('color'),
79 part.get('mass'), part.get('width')),
80 0)
81
82 @staticmethod
84 """Move the wavefunction part of propagator id appropriately"""
85
86 if len(new_vertex[0]) == 1 and len(old_vertex[0]) > 1:
87
88 return (old_vertex[0], new_vertex[0][0])
89 elif len(new_vertex[0]) > 1 and len(old_vertex[0]) == 1:
90
91 return (old_vertex[0],)
92
93 raise diagram_generation.DiagramTag.DiagramTagError, \
94 "Error in IdentifyConfigTag, wrong setup of vertices in link."
95
101 """Class to group a number of amplitudes with same initial states
102 into a subprocess group"""
103
105 """Define object and give default values"""
106
107 self['number'] = 0
108 self['name'] = ""
109 self['amplitudes'] = diagram_generation.AmplitudeList()
110 self['matrix_elements'] = helas_objects.HelasMatrixElementList()
111 self['mapping_diagrams'] = []
112 self['diagram_maps'] = {}
113 self['diagrams_for_configs'] = []
114 self['amplitude_map'] = {}
115
116
117 - def filter(self, name, value):
118 """Filter for valid property values."""
119
120 if name == 'number':
121 if not isinstance(value, int):
122 raise self.PhysicsObjectError, \
123 "%s is not a valid int object" % str(value)
124 if name == 'name':
125 if not isinstance(value, str):
126 raise self.PhysicsObjectError, \
127 "%s is not a valid str object" % str(value)
128 if name == 'amplitudes':
129 if not isinstance(value, diagram_generation.AmplitudeList):
130 raise self.PhysicsObjectError, \
131 "%s is not a valid amplitudelist" % str(value)
132 if name in ['mapping_diagrams', 'diagrams_for_configs']:
133 if not isinstance(value, list):
134 raise self.PhysicsObjectError, \
135 "%s is not a valid list" % str(value)
136 if name == 'diagram_maps':
137 if not isinstance(value, dict):
138 raise self.PhysicsObjectError, \
139 "%s is not a valid dict" % str(value)
140 if name == 'matrix_elements':
141 if not isinstance(value, helas_objects.HelasMatrixElementList):
142 raise self.PhysicsObjectError, \
143 "%s is not a valid HelasMatrixElementList" % str(value)
144
145 if name == 'amplitude_map':
146 if not isinstance(value, dict):
147 raise self.PhysicsObjectError, \
148 "%s is not a valid dict object" % str(value)
149
150 return True
151
153 """Return diagram property names as a nicely sorted list."""
154
155 return ['number', 'name', 'amplitudes', 'mapping_diagrams',
156 'diagram_maps', 'matrix_elements', 'amplitude_map']
157
158
159 - def get(self, name):
172
174 """Set mapping_diagrams and diagram_maps, to prepare for
175 generation of the super-config.inc files."""
176
177
178 mapping_diagrams, diagram_maps = \
179 self.find_mapping_diagrams()
180
181 self.set('mapping_diagrams', mapping_diagrams)
182 self.set('diagram_maps', diagram_maps)
183
184
185
186
202
204 """Generate a convenient name for the group, based on and
205 masses"""
206
207 beam = [l.get('id') for l in process.get('legs') if not l.get('state')]
208 fs = [l.get('id') for l in process.get('legs') if l.get('state')]
209 name = ""
210 for beam in beam:
211 part = process.get('model').get_particle(beam)
212 if part.get('mass').lower() == 'zero' and part.is_fermion() and \
213 part.get('color') != 1:
214 name += "q"
215 elif criteria == 'madweight':
216 name += part.get_name().replace('~', 'x').\
217 replace('+', 'p').replace('-', 'm')
218 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
219 part.get('color') == 1 and part.get('pdg_code') % 2 == 1:
220 name += "l"
221 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
222 part.get('color') == 1 and part.get('pdg_code') % 2 == 0:
223 name += "vl"
224 else:
225 name += part.get_name().replace('~', 'x').\
226 replace('+', 'p').replace('-', 'm')
227 name += "_"
228 for fs_part in fs:
229 part = process.get('model').get_particle(fs_part)
230 if part.get('mass').lower() == 'zero' and part.get('color') != 1 \
231 and part.get('spin') == 2:
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.get('color') == 1 \
237 and part.get('spin') == 2:
238 if part.get('charge') == 0:
239 name += "vl"
240 else:
241 name += "l"
242 else:
243 name += part.get_name().replace('~', 'x').\
244 replace('+', 'p').replace('-', 'm')
245
246 for dc in process.get('decay_chains'):
247 name += "_" + self.generate_name(dc, criteria)
248
249 return name
250
252 """Get number of external and initial particles for this group"""
253
254 assert self.get('matrix_elements'), \
255 "Need matrix element to call get_nexternal_ninitial"
256
257 return self.get('matrix_elements')[0].\
258 get_nexternal_ninitial()
259
261 """Get number of configs for this group"""
262
263 model = self.get('matrix_elements')[0].get('processes')[0].\
264 get('model')
265
266 next, nini = self.get_nexternal_ninitial()
267
268 return sum([md.get_num_configs(model, nini) for md in
269 self.get('mapping_diagrams')])
270
272 """Find all unique diagrams for all processes in this
273 process class, and the mapping of their diagrams unto this
274 unique diagram."""
275
276 assert self.get('matrix_elements'), \
277 "Need matrix elements to run find_mapping_diagrams"
278
279 matrix_elements = self.get('matrix_elements')
280 model = matrix_elements[0].get('processes')[0].get('model')
281
282
283 mapping_diagrams = []
284
285
286 equiv_diagrams = []
287
288
289
290 diagram_maps = {}
291 masswidth_to_pdg = {}
292
293 for ime, me in enumerate(matrix_elements):
294 diagrams = me.get('base_amplitude').get('diagrams')
295
296
297 max_legs = min([max([len(v.get('legs')) for v in \
298 d.get('vertices') if v.get('id') > 0]) \
299 for d in diagrams])
300 diagram_maps[ime] = []
301 for diagram in diagrams:
302
303 if any([len(v.get('legs')) > max_legs \
304 for v in diagram.get('vertices') if v.get('id') > 0]):
305 diagram_maps[ime].append(0)
306 continue
307
308
309
310 equiv_diag = IdentifyConfigTag(diagram, model)
311 try:
312 diagram_maps[ime].append(equiv_diagrams.index(\
313 equiv_diag) + 1)
314 except ValueError:
315 equiv_diagrams.append(equiv_diag)
316 mapping_diagrams.append(diagram)
317 diagram_maps[ime].append(equiv_diagrams.index(\
318 equiv_diag) + 1)
319 return mapping_diagrams, diagram_maps
320
322 """Find the diagrams (number + 1) for all subprocesses
323 corresponding to config number iconfig. Return 0 for subprocesses
324 without corresponding diagram. Note that the iconfig should
325 start at 0."""
326
327 assert self.get('diagram_maps'), \
328 "Need diagram_maps to run get_subproc_diagrams_for_config"
329
330 subproc_diagrams = []
331 for iproc in \
332 range(len(self.get('matrix_elements'))):
333 try:
334 subproc_diagrams.append(self.get('diagram_maps')[iproc].\
335 index(iconfig + 1) + 1)
336 except ValueError:
337 subproc_diagrams.append(0)
338
339 return subproc_diagrams
340
342 """Get a list of all diagrams_for_configs"""
343
344 subproc_diagrams_for_config = []
345 for iconf in range(len(self.get('mapping_diagrams'))):
346 subproc_diagrams_for_config.append(\
347 self.get_subproc_diagrams_for_config(iconf))
348
349 self['diagrams_for_configs'] = subproc_diagrams_for_config
350
351
352
353
354 @staticmethod
356 """Return a SubProcessGroupList with the amplitudes divided
357 into subprocess groups"""
358
359 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
360 "Argument to group_amplitudes must be AmplitudeList"
361
362 if criteria in ['matrix', 'standalone','pythia8','standalone_cpp']:
363 criteria = 'madevent'
364 assert criteria in ['madevent', 'madweight']
365
366 logger.info("Organizing processes into subprocess groups")
367
368 process_classes = SubProcessGroup.find_process_classes(amplitudes,criteria)
369 ret_list = SubProcessGroupList()
370 process_class_numbers = sorted(list(set(process_classes.values())))
371 for num in process_class_numbers:
372 amp_nums = [key for (key, val) in process_classes.items() if \
373 val == num]
374 group = SubProcessGroup()
375 group.set('amplitudes',
376 diagram_generation.AmplitudeList([amplitudes[i] for i in \
377 amp_nums]))
378 group.set('number', group.get('amplitudes')[0].get('process').\
379 get('id'))
380 group.set('name', group.generate_name(\
381 group.get('amplitudes')[0].get('process'),
382 criteria=criteria))
383 ret_list.append(group)
384
385 return ret_list
386
387 @staticmethod
389 """Find all different process classes, classified according to
390 initial state and final state. For initial state, we
391 differentiate fermions, antifermions, gluons, and masses. For
392 final state, only masses."""
393
394 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
395 "Argument to find_process_classes must be AmplitudeList"
396 assert amplitudes
397 assert criteria in ['madevent','madweight']
398
399 model = amplitudes[0].get('process').get('model')
400 proc_classes = []
401 amplitude_classes = {}
402
403 for iamp, amplitude in enumerate(amplitudes):
404 process = amplitude.get('process')
405 is_parts = [model.get_particle(l.get('id')) for l in \
406 process.get('legs') if not \
407 l.get('state')]
408 fs_parts = [model.get_particle(l.get('id')) for l in \
409 process.get('legs') if l.get('state')]
410 diagrams = amplitude.get('diagrams')
411
412
413
414
415
416
417 if (criteria=="madevent"):
418 proc_class = [ [(p.is_fermion(), ) \
419 for p in is_parts],
420 [(p.get('mass'), p.get('spin'),
421 abs(p.get('color')),l.get('onshell')) for (p, l) \
422 in zip(is_parts + fs_parts, process.get('legs'))],
423 amplitude.get('process').get('id'),
424 process.get('id')]
425 if (criteria=="madweight"):
426 proc_class = [ [(abs(p.get('pdg_code'))==5, abs(p.get('pdg_code'))==11,
427 abs(p.get('pdg_code'))==13, abs(p.get('pdg_code'))==15) for p in \
428 fs_parts],
429 amplitude.get('process').get('id')]
430
431 try:
432 amplitude_classes[iamp] = proc_classes.index(proc_class)
433 except ValueError:
434 proc_classes.append(proc_class)
435 amplitude_classes[iamp] = proc_classes.index(proc_class)
436
437 return amplitude_classes
438
443 """List of SubProcessGroup objects"""
444
446 """Test if object obj is a valid element."""
447
448 return isinstance(obj, SubProcessGroup)
449
454
460
466
468 """Return a list of grouping where they are no groupoing over the leptons."""
469
470 output = SubProcessGroupList()
471 for group in self:
472 new_mes = {}
473 for me in group['matrix_elements']:
474 tags = {}
475 for proc in me['processes']:
476 ids = proc.get_final_ids_after_decay()
477 ids = tuple([t if abs(t) in [11, 13,15] else 0 for t in ids])
478 if ids not in tags:
479 tags[ids] = base_objects.ProcessList()
480 tags[ids].append(proc)
481 for tag in tags:
482 new_me = copy.copy(me)
483 new_me['processes'] = tags[tag]
484 if tag not in new_mes:
485 new_mes[tag] = helas_objects.HelasMatrixElementList()
486 new_mes[tag].append(new_me)
487 for tag in tags:
488 new_group = copy.copy(group)
489 new_group['matrix_elements'] = new_mes[tag]
490 new_group.set('name', new_group.generate_name(\
491 new_group['matrix_elements'][0]['processes'][0],
492 criteria='madweight'))
493 output.append(new_group)
494 return output
495
503 """Class to keep track of subprocess groups from a list of decay chains"""
504
512
513 - def filter(self, name, value):
529
531 """Return diagram property names as a nicely sorted list."""
532
533 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
534
536 """Returns a nicely formatted string of the content."""
537
538 mystr = ""
539 for igroup, group in enumerate(self.get('core_groups')):
540 mystr += " " * indent + "Group %d:\n" % (igroup + 1)
541 for amplitude in group.get('amplitudes'):
542 mystr = mystr + amplitude.nice_string(indent + 2) + "\n"
543
544 if self.get('decay_groups'):
545 mystr += " " * indent + "Decay groups:\n"
546 for dec in self.get('decay_groups'):
547 mystr = mystr + dec.nice_string(indent + 2) + "\n"
548
549 return mystr[:-1]
550
551
552
553
594
596 """Recursively identify which group process belongs to."""
597
598
599
600
601
602
603 group_assignments = []
604
605 for decay in process.get('decay_chains'):
606
607 ids = [l.get('id') for l in decay.get('legs')]
608 decay_groups = [(i, group) for (i, group) in \
609 enumerate(self.get('decay_groups')) \
610 if any([ids in [[l.get('id') for l in \
611 a.get('process').get('legs')] \
612 for a in g.get('amplitudes')] \
613 for g in group.get('core_groups')])]
614
615 for decay_group in decay_groups:
616
617 group_assignment = \
618 decay_group[1].assign_group_to_decay_process(decay)
619
620 if group_assignment:
621 group_assignments.append((decay_group[0], group_assignment))
622
623 if process.get('decay_chains') and not group_assignments:
624 return None
625
626
627
628
629 ids = [(l.get('id'),l.get('onshell')) for l in process.get('legs')]
630 core_groups = [(i, group) for (i, group) in \
631 enumerate(self.get('core_groups')) \
632 if ids in [[(l.get('id'),l.get('onshell')) for l in \
633 a.get('process').get('legs')] \
634 for a in group.get('amplitudes')] \
635 and process.get('id') == group.get('number')]
636
637 if not core_groups:
638 return None
639
640 assert len(core_groups) == 1
641
642 core_group = core_groups[0]
643
644 group_assignment = (core_group[0],
645 tuple([g for g in group_assignments]))
646
647 if not group_assignments:
648
649 return group_assignment
650
651 return group_assignment
652
653
654
655
656 @staticmethod
658 """Recursive function. Starting from a DecayChainAmplitude,
659 return a DecayChainSubProcessGroup with the core amplitudes
660 and decay chains divided into subprocess groups"""
661
662 assert isinstance(decay_chain_amps, diagram_generation.DecayChainAmplitudeList), \
663 "Argument to group_amplitudes must be DecayChainAmplitudeList"
664 if criteria in ['matrix', 'standalone','pythia8','standalone_cpp']:
665 criteria = 'madevent'
666 assert criteria in ['madevent', 'madweight']
667
668
669 amplitudes = diagram_generation.AmplitudeList()
670 for amp in decay_chain_amps:
671 amplitudes.extend(amp.get('amplitudes'))
672
673
674 core_groups = SubProcessGroup.group_amplitudes(amplitudes, criteria)
675
676 dc_subproc_group = DecayChainSubProcessGroup(\
677 {'core_groups': core_groups,
678 'decay_chain_amplitudes': decay_chain_amps})
679
680 decays = diagram_generation.DecayChainAmplitudeList()
681
682
683 for decay_chain_amp in decay_chain_amps:
684 decays.extend(decay_chain_amp.get('decay_chains'))
685
686 if decays:
687 dc_subproc_group.get('decay_groups').append(\
688 DecayChainSubProcessGroup.group_amplitudes(decays, criteria))
689
690 return dc_subproc_group
691
699 """List of DecayChainSubProcessGroup objects"""
700
705