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