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