Package madgraph :: Package iolibs :: Module group_subprocs
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.group_subprocs

  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.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') 
47 48 #=============================================================================== 49 # DiagramTag class to identify diagrams giving the same config 50 #=============================================================================== 51 52 -class IdentifyConfigTag(diagram_generation.DiagramTag):
53 """DiagramTag daughter class to identify diagrams giving the same 54 config. Need to compare leg number, mass, width, and color.""" 55 56 @staticmethod 66 67 @staticmethod
68 - def vertex_id_from_vertex(vertex, last_vertex, model, ninitial):
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
83 - def flip_vertex(new_vertex, old_vertex, links):
84 """Move the wavefunction part of propagator id appropriately""" 85 86 if len(new_vertex[0]) == 1 and len(old_vertex[0]) > 1: 87 # We go from a last link to next-to-last link - 88 return (old_vertex[0], new_vertex[0][0]) 89 elif len(new_vertex[0]) > 1 and len(old_vertex[0]) == 1: 90 # We go from next-to-last link to last link - remove propagator info 91 return (old_vertex[0],) 92 # We should not get here 93 raise diagram_generation.DiagramTag.DiagramTagError, \ 94 "Error in IdentifyConfigTag, wrong setup of vertices in link."
95
96 #=============================================================================== 97 # SubProcessGroup 98 #=============================================================================== 99 100 -class SubProcessGroup(base_objects.PhysicsObject):
101 """Class to group a number of amplitudes with same initial states 102 into a subprocess group""" 103
104 - def default_setup(self):
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
152 - def get_sorted_keys(self):
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 # Enhanced get function
159 - def get(self, name):
160 """Get the value of the property name.""" 161 162 if name == 'matrix_elements' and not self[name]: 163 self.generate_matrix_elements() 164 165 if name in ['mapping_diagrams', 'diagram_maps'] and not self[name]: 166 self.set_mapping_diagrams() 167 168 if name in ['diagrams_for_configs'] and not self[name]: 169 self.set_diagrams_for_configs() 170 171 return super(SubProcessGroup, self).get(name)
172
173 - def set_mapping_diagrams(self):
174 """Set mapping_diagrams and diagram_maps, to prepare for 175 generation of the super-config.inc files.""" 176 177 # Find the mapping diagrams 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 # generate_matrix_elements 186 #===========================================================================
187 - def generate_matrix_elements(self):
188 """Create a HelasMultiProcess corresponding to the amplitudes 189 in self""" 190 191 if not self.get('amplitudes'): 192 raise self.PhysicsObjectError, \ 193 "Need amplitudes to generate matrix_elements" 194 195 amplitudes = copy.copy(self.get('amplitudes')) 196 197 self.set('matrix_elements', 198 helas_objects.HelasMultiProcess.\ 199 generate_matrix_elements(amplitudes)) 200 201 self.set('amplitudes', diagram_generation.AmplitudeList())
202
203 - def generate_name(self, process, criteria='madevent'):
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" # "j" 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
251 - def get_nexternal_ninitial(self):
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
260 - def get_num_configs(self):
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
271 - def find_mapping_diagrams(self):
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 # mapping_diagrams: The configurations for the non-reducable 282 # diagram topologies 283 mapping_diagrams = [] 284 # equiv_diags: Tags identifying diagrams that correspond to 285 # the same configuration 286 equiv_diagrams = [] 287 # diagram_maps: A dict from amplitude number to list of 288 # diagram maps, pointing to the mapping_diagrams (starting at 289 # 1). Diagrams with multi-particle vertices will have 0. 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 # Check the minimal number of legs we need to include in order 296 # to make sure we'll have some valid configurations 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 # Only use diagrams with all vertices == min_legs 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 # Create the equivalent diagram, in the format 308 # [[((ext_number1, mass_width_id1), ..., )], 309 # ...] (for each vertex) 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
321 - def get_subproc_diagrams_for_config(self, iconfig):
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
341 - def set_diagrams_for_configs(self):
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 # group_amplitudes 353 #=========================================================================== 354 @staticmethod
355 - def group_amplitudes(amplitudes, criteria='madevent'):
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
388 - def find_process_classes(amplitudes, criteria):
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 # This is where the requirements for which particles to 413 # combine are defined. Include p.get('is_part') in 414 # is_parts selection to distinguish between q and qbar, 415 # remove p.get('spin') from fs_parts selection to combine 416 # q and g into "j" 417 if (criteria=="madevent"): 418 proc_class = [ [(p.is_fermion(), ) \ 419 for p in is_parts], # p.get('is_part') 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
439 #=============================================================================== 440 # SubProcessGroupList 441 #=============================================================================== 442 -class SubProcessGroupList(base_objects.PhysicsObjectList):
443 """List of SubProcessGroup objects""" 444
445 - def is_valid_element(self, obj):
446 """Test if object obj is a valid element.""" 447 448 return isinstance(obj, SubProcessGroup)
449
450 - def get_matrix_elements(self):
451 """Extract the list of matrix elements""" 452 return helas_objects.HelasMatrixElementList(\ 453 sum([group.get('matrix_elements') for group in self], []))
454
455 - def get_used_lorentz(self):
456 """Return the list of ALOHA routines used in these matrix elements""" 457 458 return helas_objects.HelasMultiProcess( 459 {'matrix_elements': self.get_matrix_elements()}).get_used_lorentz()
460
461 - def get_used_couplings(self):
462 """Return the list of ALOHA routines used in these matrix elements""" 463 464 return helas_objects.HelasMultiProcess( 465 {'matrix_elements': self.get_matrix_elements()}).get_used_couplings()
466
467 - def split_lepton_grouping(self):
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
496 497 498 #=============================================================================== 499 # DecayChainSubProcessGroup 500 #=============================================================================== 501 502 -class DecayChainSubProcessGroup(SubProcessGroup):
503 """Class to keep track of subprocess groups from a list of decay chains""" 504
505 - def default_setup(self):
506 """Define object and give default values""" 507 508 self['core_groups'] = SubProcessGroupList() 509 self['decay_groups'] = DecayChainSubProcessGroupList() 510 # decay_chain_amplitudes is the original DecayChainAmplitudeList 511 self['decay_chain_amplitudes'] = diagram_generation.DecayChainAmplitudeList()
512
513 - def filter(self, name, value):
514 """Filter for valid property values.""" 515 516 if name == 'core_groups': 517 if not isinstance(value, SubProcessGroupList): 518 raise self.PhysicsObjectError, \ 519 "%s is not a valid core_groups" % str(value) 520 if name == 'decay_groups': 521 if not isinstance(value, DecayChainSubProcessGroupList): 522 raise self.PhysicsObjectError, \ 523 "%s is not a valid decay_groups" % str(value) 524 if name == 'decay_chain_amplitudes': 525 if not isinstance(value, diagram_generation.DecayChainAmplitudeList): 526 raise self.PhysicsObjectError, \ 527 "%s is not a valid DecayChainAmplitudeList" % str(value) 528 return True
529
530 - def get_sorted_keys(self):
531 """Return diagram property names as a nicely sorted list.""" 532 533 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
534
535 - def nice_string(self, indent = 0):
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 # generate_helas_decay_chain_subproc_groups 553 #===========================================================================
555 """Combine core_groups and decay_groups to give 556 HelasDecayChainProcesses and new diagram_maps. 557 """ 558 559 # Combine decays 560 matrix_elements = \ 561 helas_objects.HelasMultiProcess.generate_matrix_elements(\ 562 diagram_generation.AmplitudeList(\ 563 self.get('decay_chain_amplitudes'))) 564 565 566 # For each matrix element, check which group it should go into and 567 # calculate diagram_maps 568 me_assignments = {} 569 for me in matrix_elements: 570 group_assignment = self.assign_group_to_decay_process(\ 571 me.get('processes')[0]) 572 assert group_assignment 573 try: 574 me_assignments[group_assignment].append(me) 575 except KeyError: 576 me_assignments[group_assignment] = [me] 577 578 # Create subprocess groups corresponding to the different 579 # group_assignments 580 581 subproc_groups = SubProcessGroupList() 582 for key in sorted(me_assignments.keys()): 583 group = SubProcessGroup() 584 group.set('matrix_elements', helas_objects.HelasMatrixElementList(\ 585 me_assignments[key])) 586 group.set('number', group.get('matrix_elements')[0].\ 587 get('processes')[0].get('id')) 588 group.set('name', group.generate_name(\ 589 group.get('matrix_elements')[0].\ 590 get('processes')[0])) 591 subproc_groups.append(group) 592 593 return subproc_groups
594
595 - def assign_group_to_decay_process(self, process):
596 """Recursively identify which group process belongs to.""" 597 598 # Determine properties for the decay chains 599 # The entries of group_assignments are: 600 # [(decay_index, (decay_group_index, ...)), 601 # diagram_map (updated), len(mapping_diagrams)] 602 603 group_assignments = [] 604 605 for decay in process.get('decay_chains'): 606 # Find decay group that has this decay in it 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 # Now calculate the corresponding properties for process 627 628 # Find core process group 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 # This is the first return argument - the chain of group indices 644 group_assignment = (core_group[0], 645 tuple([g for g in group_assignments])) 646 647 if not group_assignments: 648 # No decays - return the values for this process 649 return group_assignment 650 651 return group_assignment
652 653 #=========================================================================== 654 # group_amplitudes 655 #=========================================================================== 656 @staticmethod
657 - def group_amplitudes(decay_chain_amps, criteria='madevent'):
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 # Collect all amplitudes 669 amplitudes = diagram_generation.AmplitudeList() 670 for amp in decay_chain_amps: 671 amplitudes.extend(amp.get('amplitudes')) 672 673 # Determine core process groups 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 # Recursively determine decay chain groups 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
692 693 694 695 #=============================================================================== 696 # DecayChainSubProcessGroupList 697 #=============================================================================== 698 -class DecayChainSubProcessGroupList(base_objects.PhysicsObjectList):
699 """List of DecayChainSubProcessGroup objects""" 700
701 - def is_valid_element(self, obj):
702 """Test if object obj is a valid element.""" 703 704 return isinstance(obj, DecayChainSubProcessGroup)
705