| Trees | Indices | Help |
|---|
|
|
1 ################################################################################
2 #
3 # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors
4 #
5 # This file is a part of the MadGraph5_aMC@NLO project, an application which
6 # automatically generates Feynman diagrams and matrix elements for arbitrary
7 # high-energy processes in the Standard Model and beyond.
8 #
9 # It is subject to the MadGraph5_aMC@NLO license which should accompany this
10 # distribution.
11 #
12 # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch
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')
50
51 #===============================================================================
52 # DiagramTag class to identify diagrams giving the same config
53 #===============================================================================
54
55 -class IdentifyConfigTag(diagram_generation.DiagramTag):
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 # We go from a last link to next-to-last link -
91 return (old_vertex[0], new_vertex[0][0])
92 elif len(new_vertex[0]) > 1 and len(old_vertex[0]) == 1:
93 # We go from next-to-last link to last link - remove propagator info
94 return (old_vertex[0],)
95 # We should not get here
96 raise diagram_generation.DiagramTag.DiagramTagError, \
97 "Error in IdentifyConfigTag, wrong setup of vertices in link."
98
99 #===============================================================================
100 # SubProcessGroup
101 #===============================================================================
102
103 -class SubProcessGroup(base_objects.PhysicsObject):
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
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 # Enhanced get function
168 """Get the value of the property name."""
169
170 if name == 'matrix_elements' and not self[name]:
171 self.generate_matrix_elements()
172
173 if name in ['mapping_diagrams', 'diagram_maps'] and not self[name]:
174 self.set_mapping_diagrams()
175
176 if name in ['diagrams_for_configs'] and not self[name]:
177 self.set_diagrams_for_configs()
178
179 return super(SubProcessGroup, self).get(name)
180
182 """Set mapping_diagrams and diagram_maps, to prepare for
183 generation of the super-config.inc files."""
184
185 # Find the mapping diagrams
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 # generate_matrix_elements
194 #===========================================================================
196 """Create a HelasMultiProcess corresponding to the amplitudes
197 in self"""
198
199 if not self.get('amplitudes'):
200 raise self.PhysicsObjectError, \
201 "Need amplitudes to generate matrix_elements"
202
203 amplitudes = copy.copy(self.get('amplitudes'))
204
205 # The conditional statement tests whether we are dealing with a
206 # loop induced process. We must set compute_loop_nc to True here
207 # since the knowledge of the power of Nc coming from potential
208 # loop color trace is necessary for the loop induced output with MadEvent
209 if isinstance(amplitudes[0], loop_diagram_generation.LoopAmplitude):
210 self.set('matrix_elements',
211 loop_helas_objects.LoopHelasProcess.generate_matrix_elements(
212 amplitudes, compute_loop_nc=True,
213 matrix_element_opts = self['matrix_element_opts']))
214 else:
215 self.set('matrix_elements',
216 helas_objects.HelasMultiProcess.\
217 generate_matrix_elements(amplitudes))
218
219 self.set('amplitudes', diagram_generation.AmplitudeList())
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" # "j"
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 # mapping_diagrams: The configurations for the non-reducable
300 # diagram topologies
301 mapping_diagrams = []
302 # equiv_diags: Tags identifying diagrams that correspond to
303 # the same configuration
304 equiv_diagrams = []
305 # diagram_maps: A dict from amplitude number to list of
306 # diagram maps, pointing to the mapping_diagrams (starting at
307 # 1). Diagrams with multi-particle vertices will have 0.
308 diagram_maps = {}
309
310 for ime, me in enumerate(matrix_elements):
311 # Define here a FDStructure repository which will be used for the
312 # tagging all the diagrams in get_contracted_loop_diagram. Remember
313 # the the tagging of each loop updates the FDStructre repository
314 # with the new structures identified.
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 # Check the minimal number of legs we need to include in order
325 # to make sure we'll have some valid configurations
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 # Only use diagrams with all vertices == min_legs, but do not
334 # consider artificial vertices, such as those coming from a
335 # contracted loop for example, which should be considered as new
336 # topologies (the contracted vertex has id == -2.)
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 # Create the equivalent diagram, in the format
342 # [[((ext_number1, mass_width_id1), ..., )],
343 # ...] (for each vertex)
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 # group_amplitudes
387 #===========================================================================
388 @staticmethod
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 # This is where the requirements for which particles to
446 # combine are defined. Include p.get('is_part') in
447 # is_parts selection to distinguish between q and qbar,
448 # remove p.get('spin') from fs_parts selection to combine
449 # q and g into "j"
450 if (criteria=="madevent"):
451 proc_class = [ [(p.is_fermion(), ) \
452 for p in is_parts], # p.get('is_part')
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
472 #===============================================================================
473 # SubProcessGroupList
474 #===============================================================================
475 -class SubProcessGroupList(base_objects.PhysicsObjectList):
476 """List of SubProcessGroup objects"""
477
482
484 """Extract the list of matrix elements"""
485 return helas_objects.HelasMatrixElementList(\
486 sum([group.get('matrix_elements') for group in self], []))
487
489 """Return the list of ALOHA routines used in these matrix elements"""
490
491 return helas_objects.HelasMultiProcess(
492 {'matrix_elements': self.get_matrix_elements()}).get_used_lorentz()
493
495 """Return the list of ALOHA routines used in these matrix elements"""
496
497 return helas_objects.HelasMultiProcess(
498 {'matrix_elements': self.get_matrix_elements()}).get_used_couplings()
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
529
530
531 #===============================================================================
532 # DecayChainSubProcessGroup
533 #===============================================================================
534
535 -class DecayChainSubProcessGroup(SubProcessGroup):
536 """Class to keep track of subprocess groups from a list of decay chains"""
537
539 """Define object and give default values"""
540
541 self['core_groups'] = SubProcessGroupList()
542 self['decay_groups'] = DecayChainSubProcessGroupList()
543 # decay_chain_amplitudes is the original DecayChainAmplitudeList
544 self['decay_chain_amplitudes'] = diagram_generation.DecayChainAmplitudeList()
545
547 """Filter for valid property values."""
548
549 if name == 'core_groups':
550 if not isinstance(value, SubProcessGroupList):
551 raise self.PhysicsObjectError, \
552 "%s is not a valid core_groups" % str(value)
553 if name == 'decay_groups':
554 if not isinstance(value, DecayChainSubProcessGroupList):
555 raise self.PhysicsObjectError, \
556 "%s is not a valid decay_groups" % str(value)
557 if name == 'decay_chain_amplitudes':
558 if not isinstance(value, diagram_generation.DecayChainAmplitudeList):
559 raise self.PhysicsObjectError, \
560 "%s is not a valid DecayChainAmplitudeList" % str(value)
561 return True
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 # generate_helas_decay_chain_subproc_groups
586 #===========================================================================
588 """Combine core_groups and decay_groups to give
589 HelasDecayChainProcesses and new diagram_maps.
590 """
591
592 # Combine decays
593 matrix_elements = \
594 helas_objects.HelasMultiProcess.generate_matrix_elements(\
595 diagram_generation.AmplitudeList(\
596 self.get('decay_chain_amplitudes')))
597
598
599 # For each matrix element, check which group it should go into and
600 # calculate diagram_maps
601 me_assignments = {}
602 for me in matrix_elements:
603 group_assignment = self.assign_group_to_decay_process(\
604 me.get('processes')[0])
605 assert group_assignment
606 try:
607 me_assignments[group_assignment].append(me)
608 except KeyError:
609 me_assignments[group_assignment] = [me]
610
611 # Create subprocess groups corresponding to the different
612 # group_assignments
613
614 subproc_groups = SubProcessGroupList()
615 for key in sorted(me_assignments.keys()):
616 group = SubProcessGroup()
617 group.set('matrix_elements', helas_objects.HelasMatrixElementList(\
618 me_assignments[key]))
619 group.set('number', group.get('matrix_elements')[0].\
620 get('processes')[0].get('id'))
621 group.set('name', group.generate_name(\
622 group.get('matrix_elements')[0].\
623 get('processes')[0]))
624 subproc_groups.append(group)
625
626 return subproc_groups
627
629 """Recursively identify which group process belongs to."""
630
631 # Determine properties for the decay chains
632 # The entries of group_assignments are:
633 # [(decay_index, (decay_group_index, ...)),
634 # diagram_map (updated), len(mapping_diagrams)]
635
636 group_assignments = []
637
638 for decay in process.get('decay_chains'):
639 # Find decay group that has this decay in it
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 # Now calculate the corresponding properties for process
660
661 # Find core process group
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 # This is the first return argument - the chain of group indices
677 group_assignment = (core_group[0],
678 tuple([g for g in group_assignments]))
679
680 if not group_assignments:
681 # No decays - return the values for this process
682 return group_assignment
683
684 return group_assignment
685
686 #===========================================================================
687 # group_amplitudes
688 #===========================================================================
689 @staticmethod
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 # Collect all amplitudes
702 amplitudes = diagram_generation.AmplitudeList()
703 for amp in decay_chain_amps:
704 amplitudes.extend(amp.get('amplitudes'))
705
706 # Determine core process groups
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 # Recursively determine decay chain groups
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
725
726
727
728 #===============================================================================
729 # DecayChainSubProcessGroupList
730 #===============================================================================
731 -class DecayChainSubProcessGroupList(base_objects.PhysicsObjectList):
732 """List of DecayChainSubProcessGroup objects"""
733
735 """Test if object obj is a valid element."""
736
737 return isinstance(obj, DecayChainSubProcessGroup)
738
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Thu Aug 17 00:27:04 2017 | http://epydoc.sourceforge.net |