1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Definitions of the objects needed for the implementation of MadFKS"""
17
18 import madgraph.core.base_objects as MG
19 import madgraph.core.helas_objects as helas_objects
20 import madgraph.core.diagram_generation as diagram_generation
21 import madgraph.core.color_amp as color_amp
22 import madgraph.core.color_algebra as color_algebra
23 import madgraph.loop.loop_diagram_generation as loop_diagram_generation
24 import madgraph.fks.fks_common as fks_common
25 import copy
26 import logging
27 import array
28
29 from madgraph import InvalidCmd
30
31 logger = logging.getLogger('madgraph.fks_base')
32
33
34
35
36
38 """A multi process class that contains informations on the born processes
39 and the reals.
40 """
41
48
50 """Return particle property names as a nicely sorted list."""
51 keys = super(FKSMultiProcess, self).get_sorted_keys()
52 keys += ['born_processes', 'real_amplitudes', 'real_pdgs', 'has_isr',
53 'has_fsr', 'OLP']
54 return keys
55
56 - def filter(self, name, value):
57 """Filter for valid leg property values."""
58
59 if name == 'born_processes':
60 if not isinstance(value, FKSProcessList):
61 raise self.PhysicsObjectError, \
62 "%s is not a valid list for born_processes " % str(value)
63
64 if name == 'real_amplitudes':
65 if not isinstance(value, diagram_generation.AmplitudeList):
66 raise self.PhysicsObjectError, \
67 "%s is not a valid list for real_amplitudes " % str(value)
68
69 if name == 'real_pdgs':
70 if not isinstance(value, list):
71 raise self.PhysicsObjectError, \
72 "%s is not a valid list for real_amplitudes " % str(value)
73
74 if name == 'OLP':
75 if not isinstance(value,str):
76 raise self.PhysicsObjectError, \
77 "%s is not a valid string for OLP " % str(value)
78
79 return super(FKSMultiProcess,self).filter(name, value)
80
81 - def __init__(self, *arguments, **options):
82 """Initializes the original multiprocess, then generates the amps for the
83 borns, then generate the born processes and the reals.
84 Real amplitudes are stored in real_amplitudes according on the pdgs of their
85 legs (stored in pdgs, so that they need to be generated only once and then reicycled
86 """
87
88
89 loggers_off = [logging.getLogger('madgraph.diagram_generation'),
90 logging.getLogger('madgraph.loop_diagram_generation')]
91 old_levels = [logg.level for logg in loggers_off]
92 for logg in loggers_off:
93 logg.setLevel(logging.WARNING)
94
95 self['real_amplitudes'] = diagram_generation.AmplitudeList()
96 self['pdgs'] = []
97
98 if 'OLP' in options.keys():
99 self['OLP']=options['OLP']
100 del options['OLP']
101
102 try:
103
104 super(FKSMultiProcess, self).__init__(*arguments,**options)
105 except InvalidCmd as error:
106
107 raise InvalidCmd, "Born diagrams could not be generated for the "+\
108 self['process_definitions'][0].nice_string().replace('Process',\
109 'process')+". Notice that aMC@NLO does not handle loop-induced"+\
110 " processes yet, but you can still use MadLoop if you want to "+\
111 "only generate them."+\
112 " For this, use the 'virt=' mode, without multiparticle labels."
113
114
115
116
117 perturbation = []
118 for procdef in self['process_definitions']:
119 soft_particles = []
120 for pert in procdef['perturbation_couplings']:
121 if pert not in perturbation:
122 perturbation.append(pert)
123 soft_particles.extend(\
124 fks_common.find_pert_particles_interactions(\
125 procdef['model'], pert)['soft_particles'])
126 soft_particles_string = ', '.join( \
127 [procdef['model'].get('particle_dict')[id][\
128 {True:'name', False:'antiname'}[id >0] ] \
129 for id in sorted(soft_particles, reverse=True)])
130 for leg in procdef['legs']:
131 if any([id in soft_particles for id in leg['ids']]) \
132 and sorted(leg['ids']) != soft_particles:
133 logger.warning(('%s can have real emission processes ' + \
134 'which are not finite.\nTo avoid this, please use multiparticles ' + \
135 'when generating the process and be sure to include all the following ' + \
136 'particles in the multiparticle definition:\n %s' ) \
137 % (procdef.nice_string(), soft_particles_string) )
138 break
139 for procdef in self['process_definitions']:
140 procdef.set('orders', diagram_generation.MultiProcess.find_optimal_process_orders(procdef))
141
142 amps = self.get('amplitudes')
143 for i, amp in enumerate(amps):
144 logger.info("Generating FKS-subtracted matrix elements for born process%s (%d / %d)" \
145 % (amp['process'].nice_string(print_weighted=False).replace(\
146 'Process', ''),
147 i + 1, len(amps)))
148
149 born = FKSProcess(amp)
150 self['born_processes'].append(born)
151 born.generate_reals(self['pdgs'], self['real_amplitudes'])
152
153 born_pdg_list = [[l['id'] for l in born.born_proc['legs']] \
154 for born in self['born_processes'] ]
155
156 for born in self['born_processes']:
157 for real in born.real_amps:
158 real.find_fks_j_from_i(born_pdg_list)
159
160
161 if amps:
162 if self['process_definitions'][0].get('NLO_mode') == 'all':
163 self.generate_virtuals()
164
165 elif not self['process_definitions'][0].get('NLO_mode') in ['all', 'real']:
166 raise fks_common.FKSProcessError(\
167 "Not a valid NLO_mode for a FKSMultiProcess: %s" % \
168 self['process_definitions'][0].get('NLO_mode'))
169
170
171 n_diag_born = sum([len(amp.get('diagrams'))
172 for amp in self.get_born_amplitudes()])
173 n_diag_real = sum([len(amp.get('diagrams'))
174 for amp in self.get_real_amplitudes()])
175 n_diag_virt = sum([len(amp.get('loop_diagrams'))
176 for amp in self.get_virt_amplitudes()])
177
178 if n_diag_virt == 0 and n_diag_real ==0:
179 raise fks_common.FKSProcessError(
180 'This process does not have any correction up to NLO in %s'\
181 %','.join(perturbation))
182
183 logger.info(('Generated %d subprocesses with %d real emission diagrams, ' + \
184 '%d born diagrams and %d virtual diagrams') % \
185 (len(self['born_processes']), n_diag_real, n_diag_born, n_diag_virt))
186
187 for i, logg in enumerate(loggers_off):
188 logg.setLevel(old_levels[i])
189
190 self['has_isr'] = any([proc.isr for proc in self['born_processes']])
191 self['has_fsr'] = any([proc.fsr for proc in self['born_processes']])
192
193 - def add(self, other):
194 """combines self and other, extending the lists of born/real amplitudes"""
195 self['born_processes'].extend(other['born_processes'])
196 self['real_amplitudes'].extend(other['real_amplitudes'])
197 self['pdgs'].extend(other['pdgs'])
198 self['has_isr'] = self['has_isr'] or other['has_isr']
199 self['has_fsr'] = self['has_fsr'] or other['has_fsr']
200 self['OLP'] = other['OLP']
201
203 """return an amplitudelist with the born amplitudes"""
204 return diagram_generation.AmplitudeList([born.born_amp \
205 for born in self['born_processes']])
206
208 """return an amplitudelist with the virt amplitudes"""
209 return diagram_generation.AmplitudeList([born.virt_amp \
210 for born in self['born_processes'] if born.virt_amp])
211
213 """return an amplitudelist with the real amplitudes"""
214 return self.get('real_amplitudes')
215
216
218 """For each process among the born_processes, creates the corresponding
219 virtual amplitude"""
220
221
222
223
224 if self['OLP']!='MadLoop':
225 logger.info("The loop matrix elements will be generated by "+\
226 '%s at the output stage only.'%self['OLP'])
227 return
228
229
230 loop_orders = {}
231 for born in self['born_processes']:
232 for coup, val in fks_common.find_orders(born.born_amp).items():
233 try:
234 loop_orders[coup] = max([loop_orders[coup], val])
235 except KeyError:
236 loop_orders[coup] = val
237
238 for i, born in enumerate(self['born_processes']):
239 logger.info('Generating virtual matrix elements using MadLoop:')
240 myproc = copy.copy(born.born_proc)
241
242 myproc['orders'] = loop_orders
243 myproc['legs'] = fks_common.to_legs(copy.copy(myproc['legs']))
244 logger.info('Generating virtual matrix element with MadLoop for process%s (%d / %d)' \
245 % (myproc.nice_string(print_weighted = False).replace(\
246 'Process', ''),
247 i + 1, len(self['born_processes'])))
248 myamp = loop_diagram_generation.LoopAmplitude(myproc)
249 if myamp.get('diagrams'):
250 born.virt_amp = myamp
251
252
254 """Contains information about a real process:
255 -- fks_infos (list containing the possible fks configs for a given process
256 -- amplitude
257 -- is_to_integrate
258 -- leg permutation<<REMOVED!.
259 """
260
261 - def __init__(self, born_proc, leglist, ij, ijglu,
262 perturbed_orders = ['QCD']):
263 """Initializes the real process based on born_proc and leglist.
264 Stores the fks informations into the list of dictionaries fks_infos
265 """
266 self.fks_infos = []
267 for leg in leglist:
268 if leg.get('fks') == 'i':
269 i_fks = leg.get('number')
270
271 need_color_links = leg.get('massless') \
272 and leg.get('spin') == 3 \
273 and leg.get('self_antipart')
274 if leg.get('fks') == 'j':
275 j_fks = leg.get('number')
276 self.fks_infos.append({'i' : i_fks,
277 'j' : j_fks,
278 'ij' : ij,
279 'ij_glu': ijglu,
280 'need_color_links' : need_color_links})
281
282 self.process = copy.copy(born_proc)
283 orders = copy.copy(born_proc.get('orders'))
284
285 if not 'WEIGHTED' in orders:
286 orders['WEIGHTED'] = sum([v * born_proc.get('model').get('order_hierarchy')[o] \
287 for o, v in orders.items()])
288
289 for order in perturbed_orders:
290 try:
291 orders[order] +=1
292 except KeyError:
293 pass
294 orders['WEIGHTED'] += born_proc.get('model').get('order_hierarchy')[order]
295
296 self.process.set('orders', orders)
297 legs = [(leg.get('id'), leg) for leg in leglist]
298 self.pdgs = array.array('i',[s[0] for s in legs])
299 if 'QCD' in perturbed_orders:
300 self.colors = [leg['color'] for leg in leglist]
301
302 self.charges = [0. for leg in leglist]
303 self.perturbation = 'QCD'
304 else:
305 self.colors = [leg['color'] for leg in leglist]
306 self.charges = [leg['charge'] for leg in leglist]
307 self.perturbation = 'QED'
308 self.process.set('legs', MG.LegList(leglist))
309 self.process.set('legs_with_decays', MG.LegList())
310 self.amplitude = diagram_generation.Amplitude()
311 self.is_to_integrate = True
312 self.is_nbody_only = False
313 self.fks_j_from_i = {}
314
315
317 """generates the real emission amplitude starting from self.process"""
318 self.amplitude = diagram_generation.Amplitude(self.process)
319 return self.amplitude
320
321
323 """Returns a dictionary with the entries i : [j_from_i], if the born pdgs are in
324 born_pdg_list"""
325 fks_j_from_i = {}
326 dict = {}
327 for i in self.process.get('legs'):
328 fks_j_from_i[i.get('number')] = []
329 if i.get('state'):
330 for j in [l for l in self.process.get('legs') if \
331 l.get('number') != i.get('number')]:
332 ijlist = fks_common.combine_ij(i, j, self.process.get('model'), dict,\
333 pert=self.perturbation)
334 for ij in ijlist:
335 born_leglist = fks_common.to_fks_legs(
336 copy.deepcopy(self.process.get('legs')),
337 self.process.get('model'))
338 born_leglist.remove(i)
339 born_leglist.remove(j)
340 born_leglist.insert(ij.get('number') - 1, ij)
341 born_leglist.sort(pert = self.perturbation)
342 if [l['id'] for l in born_leglist] in born_pdg_list:
343 fks_j_from_i[i.get('number')].append(\
344 j.get('number'))
345
346 self.fks_j_from_i = fks_j_from_i
347 return fks_j_from_i
348
349
351 """Returns leg corresponding to i fks.
352 An error is raised if the fks_infos list has more than one entry"""
353 if len(self.fks_infos) > 1:
354 raise fks_common.FKSProcessError(\
355 'get_leg_i should only be called before combining processes')
356 return self.process.get('legs')[self.fks_infos[0]['i'] - 1]
357
359 """Returns leg corresponding to j fks.
360 An error is raised if the fks_infos list has more than one entry"""
361 if len(self.fks_infos) > 1:
362 raise fks_common.FKSProcessError(\
363 'get_leg_j should only be called before combining processes')
364 return self.process.get('legs')[self.fks_infos[0]['j'] - 1]
365
366
368 """Class to handle lists of FKSProcesses."""
369
371 """Test if object obj is a valid FKSProcess for the list."""
372 return isinstance(obj, FKSProcess)
373
374
376 """The class for a FKS process. Starts from the born process and finds
377 all the possible splittings."""
378
379 - def __init__(self, start_proc = None, remove_reals = True):
380 """initialization: starts either from an amplitude or a process,
381 then init the needed variables.
382 remove_borns tells if the borns not needed for integration will be removed
383 from the born list (mainly used for testing)"""
384
385 self.splittings = {}
386 self.reals = []
387 self.fks_dirs = []
388 self.leglist = []
389 self.myorders = {}
390 self.pdg_codes = []
391 self.colors = []
392 self.charges = []
393 self.nlegs = 0
394 self.fks_ipos = []
395 self.fks_j_from_i = {}
396 self.real_amps = []
397 self.remove_reals = remove_reals
398 self.nincoming = 0
399 self.virt_amp = None
400 self.perturbation = 'QCD'
401
402 if not remove_reals in [True, False]:
403 raise fks_common.FKSProcessError(\
404 'Not valid type for remove_reals in FKSProcess')
405
406 if start_proc:
407 if isinstance(start_proc, MG.Process):
408 pertur = start_proc['perturbation_couplings']
409 if pertur:
410 self.perturbation = sorted(pertur)[0]
411 self.born_proc = fks_common.sort_proc(start_proc,pert = self.perturbation)
412
413 bornproc = copy.copy(self.born_proc)
414 assert bornproc==self.born_proc
415 self.born_amp = diagram_generation.Amplitude(bornproc)
416 elif isinstance(start_proc, diagram_generation.Amplitude):
417 pertur = start_proc.get('process')['perturbation_couplings']
418 if pertur:
419 self.perturbation = sorted(pertur)[0]
420 self.born_proc = fks_common.sort_proc(start_proc.get('process'),\
421 pert = self.perturbation)
422
423 bornproc = copy.copy(self.born_proc)
424 assert bornproc == self.born_proc
425 self.born_amp = diagram_generation.Amplitude(bornproc)
426 else:
427 raise fks_common.FKSProcessError(\
428 'Not valid start_proc in FKSProcess')
429 self.born_proc.set('legs_with_decays', MG.LegList())
430
431 self.leglist = fks_common.to_fks_legs(
432 self.born_proc['legs'], self.born_proc['model'])
433 self.nlegs = len(self.leglist)
434 self.pdg_codes = [leg.get('id') for leg in self.leglist]
435 if self.perturbation == 'QCD':
436 self.colors = [leg.get('color') for leg in self.leglist]
437
438 self.charges = [0. for leg in self.leglist]
439 color = 'color'
440 zero = 1
441 elif self.perturbation == 'QED':
442 self.colors = [leg.get('color') for leg in self.leglist]
443 self.charges = [leg.get('charge') for leg in self.leglist]
444 color = 'charge'
445 zero = 0.
446
447 self.isr = set([leg.get(color) for leg in self.leglist if not leg.get('state')]) != set([zero])
448 self.fsr = set([leg.get(color) for leg in self.leglist if leg.get('state')]) != set([zero])
449 for leg in self.leglist:
450 if not leg['state']:
451 self.nincoming += 1
452 self.orders = self.born_amp['process']['orders']
453
454 if sum(self.orders.values()) == 0:
455 self.orders = fks_common.find_orders(self.born_amp)
456
457 self.ndirs = 0
458 for order in self.born_proc.get('perturbation_couplings'):
459 self.find_reals(order)
460
461
463 """generates the real amplitudes for all the real emission processes, using pdgs and real_amps
464 to avoid multiple generation of the same amplitude"""
465
466 for amp in self.real_amps:
467 try:
468 amp.amplitude = real_amp_list[pdg_list.index(amp.pdgs)]
469 except ValueError:
470 pdg_list.append(amp.pdgs)
471 real_amp_list.append(amp.generate_real_amplitude())
472
473
475 """combines real emission processes if the pdgs are the same, combining the lists
476 of fks_infos"""
477 pdgs = []
478 real_amps = []
479 old_real_amps = copy.copy(self.real_amps)
480 for amp in old_real_amps:
481 try:
482 real_amps[pdgs.index(amp.pdgs)].fks_infos.extend(amp.fks_infos)
483 except ValueError:
484 real_amps.append(amp)
485 pdgs.append(amp.pdgs)
486
487 self.real_amps = real_amps
488
489
490
492 """For all the possible splittings, creates an FKSRealProcess.
493 It removes double counted configorations from the ones to integrates and
494 sets the one which includes the bosn (is_nbody_only).
495 if combine is true, FKS_real_processes having the same pdgs (i.e. real amplitude)
496 are combined together
497 """
498
499 born_proc = copy.copy(self.born_proc)
500 born_proc['orders'] = self.orders
501 for i, list in enumerate(self.reals):
502
503 if self.leglist[i]['massless'] and self.leglist[i]['spin'] == 3:
504 ijglu = i + 1
505 else:
506 ijglu = 0
507 for l in list:
508 ij = self.leglist[i].get('number')
509 self.real_amps.append(FKSRealProcess( \
510 born_proc, l, ij, ijglu,\
511 perturbed_orders = [self.perturbation]))
512 self.find_reals_to_integrate()
513 if combine:
514 self.combine_real_amplitudes()
515 self.generate_real_amplitudes(pdg_list, real_amp_list)
516 self.link_born_reals()
517
518
520 """create the rb_links in the real matrix element to find
521 which configuration in the real correspond to which in the born
522 """
523 for real in self.real_amps:
524 for info in real.fks_infos:
525 info['rb_links'] = fks_common.link_rb_configs(\
526 self.born_amp, real.amplitude,
527 info['i'], info['j'], info['ij'])
528
529
531 """finds the FKS real configurations for a given process"""
532 if range(len(self.leglist)) != [l['number']-1 for l in self.leglist]:
533 raise fks_common.FKSProcessError('Disordered numbers of leglist')
534 for i in self.leglist:
535 i_i = i['number'] - 1
536 self.reals.append([])
537 self.splittings[i_i] = fks_common.find_splittings(i, self.born_proc['model'], {}, pert_order)
538 for split in self.splittings[i_i]:
539 self.reals[i_i].append(
540 fks_common.insert_legs(self.leglist, i, split,pert=pert_order))
541
542
543
545 """Finds double countings in the real emission configurations, sets the
546 is_to_integrate variable and if "self.remove_reals" is True removes the
547 not needed ones from the born list.
548 """
549
550 ninit = len(self.real_amps)
551 remove = self.remove_reals
552
553 for m in range(ninit):
554 for n in range(m + 1, ninit):
555 real_m = self.real_amps[m]
556 real_n = self.real_amps[n]
557 if len(real_m.fks_infos) > 1 or len(real_m.fks_infos) > 1:
558 raise fks_common.FKSProcessError(\
559 'find_reals_to_integrate should only be called before combining processes')
560
561 i_m = real_m.fks_infos[0]['i']
562 j_m = real_m.fks_infos[0]['j']
563 i_n = real_n.fks_infos[0]['i']
564 j_n = real_n.fks_infos[0]['j']
565 if j_m > self.nincoming and j_n > self.nincoming:
566 if (real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] \
567 and \
568 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']) \
569 or \
570 (real_m.get_leg_i()['id'] == real_n.get_leg_j()['id'] \
571 and \
572 real_m.get_leg_j()['id'] == real_n.get_leg_i()['id']):
573 if i_m > i_n:
574 print real_m.get_leg_i()['id'], real_m.get_leg_j()['id']
575 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']:
576 self.real_amps[m].is_to_integrate = False
577 else:
578 self.real_amps[n].is_to_integrate = False
579 elif i_m == i_n and j_m > j_n:
580 print real_m.get_leg_i()['id'], real_m.get_leg_j()['id']
581 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']:
582 self.real_amps[m].is_to_integrate = False
583 else:
584 self.real_amps[n].is_to_integrate = False
585
586 elif i_m == i_n and j_m == j_n and \
587 not real_m.get_leg_j()['self_antipart'] and \
588 not real_m.get_leg_i()['self_antipart']:
589 if real_m.fks_infos[0]['ij'] > real_n.fks_infos[0]['ij']:
590 real_m.is_to_integrate = False
591 else:
592 real_n.is_to_integrate = False
593 else:
594 if real_m.get_leg_i()['id'] == -real_m.get_leg_j()['id']:
595 self.real_amps[n].is_to_integrate = False
596 else:
597 self.real_amps[m].is_to_integrate = False
598
599 elif j_m <= self.nincoming and j_n == j_m:
600 if real_m.get_leg_i()['id'] == real_n.get_leg_i()['id'] and \
601 real_m.get_leg_j()['id'] == real_n.get_leg_j()['id']:
602 if i_m > i_n:
603 self.real_amps[n].is_to_integrate = False
604 else:
605 self.real_amps[m].is_to_integrate = False
606 if remove:
607 newreal_amps = []
608 for real in self.real_amps:
609 if real.is_to_integrate:
610 newreal_amps.append(real)
611 self.real_amps = newreal_amps
612