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.sm.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 matrix
59 elements: ((leg numer, state, spin, self_antipart, mass,
60 width, color, decay and is_part), number)."""
61
62 part = model.get_particle(leg.get('id'))
63
64 return [((leg.get('number'),
65 part.get('mass'), part.get('width'), part.get('color')),
66 leg.get('number'))]
67
68 @staticmethod
70 """Returns the info needed to identify matrix elements:
71 interaction color, lorentz, coupling, and wavefunction
72 spin, self_antipart, mass, width, color, decay and
73 is_part. Note that is_part needs to be flipped if we move the
74 final vertex around."""
75
76 inter = model.get_interaction(vertex.get('id'))
77 ret_list = 0
78
79 if last_vertex:
80 return (ret_list,)
81 else:
82 part = model.get_particle(vertex.get('legs')[-1].get('id'))
83 return ((part.get('color'),
84 part.get('mass'), part.get('width')),
85 ret_list)
86
87 @staticmethod
89 """Move the wavefunction part of vertex id appropriately"""
90
91 if len(new_vertex) == 1 and len(old_vertex) == 2:
92
93 return (old_vertex[0],new_vertex[0])
94 elif len(new_vertex) == 2 and len(old_vertex) == 1:
95
96 return (new_vertex[1],)
97
98 raise diagram_generation.DiagramTag.DiagramTagError, \
99 "Error in IdentifyConfigTag, wrong setup of vertices in link."
100
106 """Class to group a number of amplitudes with same initial states
107 into a subprocess group"""
108
110 """Define object and give default values"""
111
112 self['number'] = 0
113 self['name'] = ""
114 self['amplitudes'] = diagram_generation.AmplitudeList()
115 self['matrix_elements'] = helas_objects.HelasMatrixElementList()
116 self['mapping_diagrams'] = []
117 self['diagram_maps'] = {}
118 self['diagrams_for_configs'] = []
119 self['amplitude_map'] = {}
120
121 - def filter(self, name, value):
122 """Filter for valid property values."""
123
124 if name == 'number':
125 if not isinstance(value, int):
126 raise self.PhysicsObjectError, \
127 "%s is not a valid int object" % str(value)
128 if name == 'name':
129 if not isinstance(value, str):
130 raise self.PhysicsObjectError, \
131 "%s is not a valid str object" % str(value)
132 if name == 'amplitudes':
133 if not isinstance(value, diagram_generation.AmplitudeList):
134 raise self.PhysicsObjectError, \
135 "%s is not a valid amplitudelist" % str(value)
136 if name in ['mapping_diagrams', 'diagrams_for_configs']:
137 if not isinstance(value, list):
138 raise self.PhysicsObjectError, \
139 "%s is not a valid list" % str(value)
140 if name == 'diagram_maps':
141 if not isinstance(value, dict):
142 raise self.PhysicsObjectError, \
143 "%s is not a valid dict" % str(value)
144 if name == 'matrix_elements':
145 if not isinstance(value, helas_objects.HelasMatrixElementList):
146 raise self.PhysicsObjectError, \
147 "%s is not a valid HelasMatrixElementList" % str(value)
148
149 if name == 'amplitude_map':
150 if not isinstance(value, dict):
151 raise self.PhysicsObjectError, \
152 "%s is not a valid dict object" % str(value)
153
154 return True
155
157 """Return diagram property names as a nicely sorted list."""
158
159 return ['number', 'name', 'amplitudes', 'mapping_diagrams',
160 'diagram_maps', 'matrix_elements', 'amplitude_map']
161
162
163 - def get(self, name):
176
178 """Set mapping_diagrams and diagram_maps, to prepare for
179 generation of the super-config.inc files."""
180
181
182 mapping_diagrams, diagram_maps = \
183 self.find_mapping_diagrams()
184
185 self.set('mapping_diagrams', mapping_diagrams)
186 self.set('diagram_maps', diagram_maps)
187
188
189
190
206
208 """Generate a convenient name for the group, based on and
209 masses"""
210
211 beam = [l.get('id') for l in process.get('legs') if not l.get('state')]
212 fs = [l.get('id') for l in process.get('legs') if l.get('state')]
213 name = ""
214 for beam in beam:
215 part = process.get('model').get_particle(beam)
216 if part.get('mass').lower() == 'zero' and part.is_fermion() and \
217 part.get('color') != 1:
218 name += "q"
219 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
220 part.get('color') == 1 and part.get('pdg_code') % 2 == 1:
221 name += "l"
222 elif part.get('mass').lower() == 'zero' and part.is_fermion() and \
223 part.get('color') == 1 and part.get('pdg_code') % 2 == 0:
224 name += "vl"
225 else:
226 name += part.get_name().replace('~', 'x').\
227 replace('+', 'p').replace('-', 'm')
228 name += "_"
229 for fs_part in fs:
230 part = process.get('model').get_particle(fs_part)
231 if part.get('mass').lower() == 'zero' and part.get('color') != 1 \
232 and part.get('spin') == 2:
233 name += "q"
234 elif part.get('mass').lower() == 'zero' and part.get('color') == 1 \
235 and part.get('spin') == 2:
236 if part.get('charge') == 0:
237 name += "vl"
238 else:
239 name += "l"
240 else:
241 name += part.get_name().replace('~', 'x').\
242 replace('+', 'p').replace('-', 'm')
243
244 for dc in process.get('decay_chains'):
245 name += "_" + self.generate_name(dc)
246
247 return name
248
250 """Get number of external and initial particles for this group"""
251
252 assert self.get('matrix_elements'), \
253 "Need matrix element to call get_nexternal_ninitial"
254
255 return self.get('matrix_elements')[0].\
256 get_nexternal_ninitial()
257
259 """Get number of configs for this group"""
260
261 model = self.get('matrix_elements')[0].get('processes')[0].\
262 get('model')
263
264 next, nini = self.get_nexternal_ninitial()
265
266 return sum([md.get_num_configs(model, nini) for md in
267 self.get('mapping_diagrams')])
268
270 """Find all unique diagrams for all processes in this
271 process class, and the mapping of their diagrams unto this
272 unique diagram."""
273
274 assert self.get('matrix_elements'), \
275 "Need matrix elements to run find_mapping_diagrams"
276
277 matrix_elements = self.get('matrix_elements')
278 model = matrix_elements[0].get('processes')[0].get('model')
279
280
281 mapping_diagrams = []
282
283
284 equiv_diagrams = []
285
286
287
288 diagram_maps = {}
289 masswidth_to_pdg = {}
290
291 for ime, me in enumerate(matrix_elements):
292 diagrams = me.get('base_amplitude').get('diagrams')
293
294
295 max_legs = min([max([len(v.get('legs')) for v in \
296 d.get('vertices') if v.get('id') > 0]) \
297 for d in diagrams])
298 diagram_maps[ime] = []
299 for diagram in diagrams:
300
301 if any([len(v.get('legs')) > max_legs \
302 for v in diagram.get('vertices') if v.get('id') > 0]):
303 diagram_maps[ime].append(0)
304 continue
305
306
307
308 equiv_diag = IdentifyConfigTag(diagram, model)
309 try:
310 diagram_maps[ime].append(equiv_diagrams.index(\
311 equiv_diag) + 1)
312 except ValueError:
313 equiv_diagrams.append(equiv_diag)
314 mapping_diagrams.append(diagram)
315 diagram_maps[ime].append(equiv_diagrams.index(\
316 equiv_diag) + 1)
317 return mapping_diagrams, diagram_maps
318
320 """Find the diagrams (number + 1) for all subprocesses
321 corresponding to config number iconfig. Return 0 for subprocesses
322 without corresponding diagram. Note that the iconfig should
323 start at 0."""
324
325 assert self.get('diagram_maps'), \
326 "Need diagram_maps to run get_subproc_diagrams_for_config"
327
328 subproc_diagrams = []
329 for iproc in \
330 range(len(self.get('matrix_elements'))):
331 try:
332 subproc_diagrams.append(self.get('diagram_maps')[iproc].\
333 index(iconfig + 1) + 1)
334 except ValueError:
335 subproc_diagrams.append(0)
336
337 return subproc_diagrams
338
340 """Get a list of all diagrams_for_configs"""
341
342 subproc_diagrams_for_config = []
343 for iconf in range(len(self.get('mapping_diagrams'))):
344 subproc_diagrams_for_config.append(\
345 self.get_subproc_diagrams_for_config(iconf))
346
347 self['diagrams_for_configs'] = subproc_diagrams_for_config
348
349
350
351
352 @staticmethod
354 """Return a SubProcessGroupList with the amplitudes divided
355 into subprocess groups"""
356
357 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
358 "Argument to group_amplitudes must be AmplitudeList"
359
360 logger.info("Organizing processes into subprocess groups")
361
362 process_classes = SubProcessGroup.find_process_classes(amplitudes)
363 ret_list = SubProcessGroupList()
364 process_class_numbers = sorted(list(set(process_classes.values())))
365 for num in process_class_numbers:
366 amp_nums = [key for (key, val) in process_classes.items() if \
367 val == num]
368 group = SubProcessGroup()
369 group.set('amplitudes',
370 diagram_generation.AmplitudeList([amplitudes[i] for i in \
371 amp_nums]))
372 group.set('number', group.get('amplitudes')[0].get('process').\
373 get('id'))
374 group.set('name', group.generate_name(\
375 group.get('amplitudes')[0].get('process')))
376 ret_list.append(group)
377
378 return ret_list
379
380 @staticmethod
382 """Find all different process classes, classified according to
383 initial state and final state. For initial state, we
384 differentiate fermions, antifermions, gluons, and masses. For
385 final state, only masses."""
386
387 assert isinstance(amplitudes, diagram_generation.AmplitudeList), \
388 "Argument to find_process_classes must be AmplitudeList"
389 assert amplitudes
390
391 model = amplitudes[0].get('process').get('model')
392 proc_classes = []
393 amplitude_classes = {}
394
395 for iamp, amplitude in enumerate(amplitudes):
396 process = amplitude.get('process')
397 is_parts = [model.get_particle(l.get('id')) for l in \
398 process.get('legs') if not \
399 l.get('state')]
400 fs_parts = [model.get_particle(l.get('id')) for l in \
401 process.get('legs') if l.get('state')]
402 diagrams = amplitude.get('diagrams')
403
404
405
406
407
408
409 proc_class = [ [(p.is_fermion(), ) \
410 for p in is_parts],
411 [(p.get('mass'), p.get('spin'),
412 abs(p.get('color')),l.get('onshell')) for (p, l) \
413 in zip(is_parts + fs_parts, process.get('legs'))],
414 amplitude.get('process').get('id'),
415 process.get('id')]
416 try:
417 amplitude_classes[iamp] = proc_classes.index(proc_class)
418 except ValueError:
419 proc_classes.append(proc_class)
420 amplitude_classes[iamp] = proc_classes.index(proc_class)
421
422 return amplitude_classes
423
428 """List of SubProcessGroup objects"""
429
431 """Test if object obj is a valid element."""
432
433 return isinstance(obj, SubProcessGroup)
434
439
445
451
457 """Class to keep track of subprocess groups from a list of decay chains"""
458
466
467 - def filter(self, name, value):
483
485 """Return diagram property names as a nicely sorted list."""
486
487 return ['core_groups', 'decay_groups', 'decay_chain_amplitudes']
488
490 """Returns a nicely formatted string of the content."""
491
492 mystr = ""
493 for igroup, group in enumerate(self.get('core_groups')):
494 mystr += " " * indent + "Group %d:\n" % (igroup + 1)
495 for amplitude in group.get('amplitudes'):
496 mystr = mystr + amplitude.nice_string(indent + 2) + "\n"
497
498 if self.get('decay_groups'):
499 mystr += " " * indent + "Decay groups:\n"
500 for dec in self.get('decay_groups'):
501 mystr = mystr + dec.nice_string(indent + 2) + "\n"
502
503 return mystr[:-1]
504
505
506
507
547
549 """Recursively identify which group process belongs to."""
550
551
552
553
554
555
556 group_assignments = []
557
558 for decay in process.get('decay_chains'):
559
560 ids = [l.get('id') for l in decay.get('legs')]
561 decay_groups = [(i, group) for (i, group) in \
562 enumerate(self.get('decay_groups')) \
563 if any([ids in [[l.get('id') for l in \
564 a.get('process').get('legs')] \
565 for a in g.get('amplitudes')] \
566 for g in group.get('core_groups')])]
567
568 for decay_group in decay_groups:
569
570 group_assignment = \
571 decay_group[1].assign_group_to_decay_process(decay)
572
573 if group_assignment:
574 group_assignments.append((decay_group[0], group_assignment))
575
576 if process.get('decay_chains') and not group_assignments:
577 return None
578
579
580
581
582 ids = [(l.get('id'),l.get('onshell')) for l in process.get('legs')]
583 core_groups = [(i, group) for (i, group) in \
584 enumerate(self.get('core_groups')) \
585 if ids in [[(l.get('id'),l.get('onshell')) for l in \
586 a.get('process').get('legs')] \
587 for a in group.get('amplitudes')] \
588 and process.get('id') == group.get('number')]
589
590 if not core_groups:
591 return None
592
593 assert len(core_groups) == 1
594
595 core_group = core_groups[0]
596
597 group_assignment = (core_group[0],
598 tuple([g for g in group_assignments]))
599
600 if not group_assignments:
601
602 return group_assignment
603
604 return group_assignment
605
606
607
608
609 @staticmethod
641
649 """List of DecayChainSubProcessGroup objects"""
650
655