1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 from madgraph.core import base_objects
16 """Methods and classes to import v4 format model files."""
17
18 import fractions
19 import logging
20 import os
21 import re
22
23 from madgraph import InvalidCmd, MG4DIR, ReadWrite
24
25 import madgraph.core.color_algebra as color
26 import madgraph.iolibs.files as files
27 import madgraph.iolibs.save_load_object as save_load_object
28
29 import madgraph.various.misc as misc
30
31 from madgraph.core.base_objects import Particle, ParticleList
32 from madgraph.core.base_objects import Interaction, InteractionList
33
34 logger = logging.getLogger('madgraph.import_v4')
40 """create a model from a MG4 model directory."""
41
42
43 model_path_old = model_path
44 model_path = find_model_path(model_path, mgme_dir, absolute)
45
46 files_list = [os.path.join(model_path, 'particles.dat'),\
47 os.path.join(model_path, 'interactions.dat')]
48
49 for filepath in files_list:
50 if not os.path.isfile(filepath):
51 if not absolute:
52 raise InvalidCmd, "%s directory is not a valid v4 model" % \
53 (model_path)
54 else:
55 return import_model(model_path_old, mgme_dir, False)
56
57
58 if files.is_uptodate(os.path.join(model_path, 'model.pkl'), files_list):
59 model = save_load_object.load_from_file( \
60 os.path.join(model_path, 'model.pkl'))
61 if model.has_key('version_tag') and model.get('version_tag') == os.path.realpath(model_path) + str(misc.get_pkg_info()):
62 return model, model_path
63
64 model = base_objects.Model()
65 model.set('particles',files.read_from_file( \
66 os.path.join(model_path, 'particles.dat'),
67 read_particles_v4))
68
69 model.set('interactions',files.read_from_file( \
70 os.path.join(model_path, 'interactions.dat'),
71 read_interactions_v4,
72 model['particles']))
73
74 model.set('name', os.path.split(model_path)[-1])
75
76
77 if ReadWrite:
78 try:
79 save_load_object.save_to_file(os.path.join(model_path, 'model.pkl'), model)
80 except Exception:
81 logger.warning("fail to write %s. This is perfectly fine will just prevent speed boost in future load of this model" %\
82 os.path.join(model_path, 'model.pkl'))
83 return model, model_path
84
87 """Find the path to the model, starting with path model_path."""
88
89
90 if os.path.isdir(model_path) and absolute:
91 return model_path
92 elif mgme_dir and os.path.isdir(os.path.join(mgme_dir, 'models',
93 model_path + "_v4")):
94 model_path = os.path.join(mgme_dir, 'models', model_path + "_v4")
95 elif mgme_dir and os.path.isdir(os.path.join(mgme_dir, 'Models', model_path)):
96 model_path = os.path.join(mgme_dir, 'Models', model_path)
97 elif not mgme_dir:
98 error_text = "Path %s is not a valid pathname\n" % model_path
99 error_text += "and no MG_ME installation detected in order to search in Models"
100 raise InvalidCmd(error_text)
101
102
103 path_possibilities = [os.path.join(mgme_dir, 'Models', model_path),
104 os.path.join(mgme_dir, 'models', model_path + "_v4"),
105 os.path.join(mgme_dir, 'models', model_path)
106 ]
107
108 for path in path_possibilities:
109 if os.path.exists(path) and \
110 not os.path.exists(os.path.join(path, 'particles.py')):
111 return path
112
113
114 raise InvalidCmd("Path %s is not a valid pathname" % model_path)
115
116
117
118
119 -def read_particles_v4(fsock):
120 """Read a list of particle from stream fsock, using the old v4 format"""
121
122 spin_equiv = {'s': 1,
123 'f': 2,
124 'v': 3,
125 't': 5}
126
127 color_equiv = {'s': 1,
128 't': 3,
129 '6': 6,
130 'o': 8}
131
132 line_equiv = {'d': 'dashed',
133 's': 'straight',
134 'w': 'wavy',
135 'c': 'curly'}
136
137 logger.info('load particles')
138
139 mypartlist = ParticleList()
140
141 for line in fsock:
142 mypart = Particle()
143
144 if line.find("MULTIPARTICLES") != -1:
145 break
146
147 line = line.split("#", 2)[0]
148 line = line.strip()
149
150 if line != "":
151 values = line.split()
152 if len(values) != 9:
153
154 raise ValueError, \
155 "Unvalid initialization string:" + line
156 else:
157 try:
158 mypart.set('name', values[0].lower())
159 mypart.set('antiname', values[1].lower())
160
161 if mypart['name'] == mypart['antiname']:
162 mypart['self_antipart'] = True
163
164 if values[2].lower() in spin_equiv.keys():
165 mypart.set('spin',
166 spin_equiv[values[2].lower()])
167 else:
168 raise ValueError, "Invalid spin %s" % \
169 values[2]
170
171 if values[3].lower() in line_equiv.keys():
172 mypart.set('line',
173 line_equiv[values[3].lower()])
174 else:
175 raise ValueError, \
176 "Invalid line type %s" % values[3]
177
178 mypart.set("mass", values[4])
179 mypart.set("width", values[5])
180
181 if values[6].lower() in color_equiv.keys():
182 mypart.set('color',
183 color_equiv[values[6].lower()])
184 else:
185 raise ValueError, \
186 "Invalid color rep %s" % values[6]
187
188
189 mypart.set("pdg_code", int(values[8]))
190
191 mypart.set('charge', 0.)
192
193
194 except (Particle.PhysicsObjectError, ValueError), why:
195 logger.warning("Warning: %s, particle ignored" % why)
196 else:
197 mypartlist.append(mypart)
198
199 return mypartlist
200
206 """Read a list of interactions from stream fsock, using the old v4 format.
207 Requires a ParticleList object as an input to recognize particle names."""
208
209 logger.info('load interactions')
210 myinterlist = InteractionList()
211
212 if not isinstance(ref_part_list, ParticleList):
213 raise ValueError, \
214 "Object %s is not a valid ParticleList" % repr(ref_part_list)
215
216 for line in fsock:
217 myinter = Interaction()
218
219 line = line.split("#", 2)[0]
220 line = line.strip()
221
222 if line != "":
223 values = line.split()
224 part_list = ParticleList()
225
226 try:
227 for str_name in values:
228 curr_part = ref_part_list.get_copy(str_name.lower())
229 if isinstance(curr_part, Particle):
230
231
232
233 if len(values) >= 2 * len(part_list) + 1:
234 part_list.append(curr_part)
235 else: break
236
237
238 else: break
239
240 if len(part_list) < 3:
241 raise Interaction.PhysicsObjectError, \
242 "Vertex with less than 3 known particles found."
243
244
245
246 spin_array = [part['spin'] for part in part_list]
247 if spin_array[:2] == [2, 2] and \
248 not part_list[0].get('self_antipart'):
249 part_list[0]['is_part'] = not part_list[0]['is_part']
250
251 myinter.set('particles', part_list)
252
253
254
255
256 color_parts = sorted(enumerate(part_list), lambda p1, p2:\
257 p1[1].get_color() - p2[1].get_color())
258 color_ind = [(i, part.get_color()) for i, part in \
259 color_parts if part.get_color() !=1]
260 colors = [c for i,c in color_ind]
261 ind = [i for i,c in color_ind]
262
263
264 myinter.set('color', [])
265 if not colors:
266
267 pass
268 elif colors == [-3, 3]:
269
270 myinter.set('color', [color.ColorString(\
271 [color.T(ind[1], ind[0])])])
272 elif colors == [8, 8]:
273
274 my_cs = color.ColorString(\
275 [color.Tr(ind[0], ind[1])])
276 my_cs.coeff = fractions.Fraction(2)
277 myinter.set('color', [my_cs])
278 elif colors == [-3, 3, 8]:
279
280 myinter.set('color', [color.ColorString(\
281 [color.T(ind[2], ind[1], ind[0])])])
282 elif colors == [8, 8, 8]:
283
284 my_color_string = color.ColorString(\
285 [color.f(ind[0], ind[1], ind[2])])
286 my_color_string.is_imaginary = True
287 myinter.set('color', [my_color_string])
288 elif colors == [-3, 3, 8, 8]:
289 my_cs1 = color.ColorString(\
290 [color.T(ind[2], ind[3], ind[1], ind[0])])
291 my_cs2 = color.ColorString(\
292 [color.T(ind[3], ind[2], ind[1], ind[0])])
293 myinter.set('color', [my_cs1, my_cs2])
294 elif colors == [8, 8, 8, 8]:
295
296 cs1 = color.ColorString([color.f(0, 1, -1),
297 color.f(2, 3, -1)])
298
299 cs2 = color.ColorString([color.f(2, 0, -1),
300 color.f(1, 3, -1)])
301
302 cs3 = color.ColorString([color.f(1, 2, -1),
303 color.f(0, 3, -1)])
304
305 myinter.set('color', [cs1, cs2, cs3])
306
307
308
309
310
311
312
313
314
315
316 else:
317 logger.warning(\
318 "Color combination %s not yet implemented." % \
319 repr(colors))
320
321
322
323
324 myinter.set('lorentz', [''])
325
326 pdg_codes = sorted([part.get_pdg_code() for part in part_list])
327
328
329 if pdg_codes == [-24, -24, 24, 24]:
330 myinter.set('lorentz', ['WWWW'])
331 elif spin_array == [3, 3, 3, 3] and \
332 24 in pdg_codes and - 24 in pdg_codes:
333 myinter.set('lorentz', ['WWVV'])
334
335
336 if pdg_codes == [21, 21, 21, 21]:
337 myinter.set('lorentz', ['gggg1', 'gggg2', 'gggg3'])
338
339
340
341
342 if spin_array == [2, 2, 3] and colors == [8, 8, 8] and \
343 part_list[0].get('self_antipart') and \
344 part_list[1].get('self_antipart'):
345 myinter.set('lorentz', ['go'])
346
347
348 if len(values) > 3 * len(part_list) - 4:
349 myinter.get('lorentz')[0] = \
350 myinter.get('lorentz')[0]\
351 + values[3 * len(part_list) - 4].upper()
352
353
354
355
356
357
358
359 if len(part_list) == 3 or \
360 values[len(part_list) + 1] in ['DUM', 'DUM0', 'DUM1']:
361
362
363 myinter.set('couplings', {(0, 0):values[len(part_list)]})
364 if myinter.get('lorentz')[0] == 'WWWWN':
365
366
367 myinter.set('lorentz', ['WWVVN'])
368 elif values[len(part_list)] in ['DUM', 'DUM0', 'DUM1']:
369
370
371 myinter.set('couplings', {(0, 0):values[len(part_list)+1]})
372 elif pdg_codes == [21, 21, 21, 21]:
373
374 myinter.set('couplings', {(0, 0):values[len(part_list)],
375 (1, 1):values[len(part_list)],
376 (2, 2):values[len(part_list)]})
377 elif myinter.get('lorentz')[0] == 'WWWW':
378
379
380 myinter.set('couplings', {(0, 0):\
381 'sqrt(' +
382 values[len(part_list)] + \
383 '**2+' + \
384 values[len(part_list) + 1] + \
385 '**2)'})
386 else:
387
388
389 myinter.set('couplings', {(0, 0):values[len(part_list)] + \
390 '*' + \
391 values[len(part_list) + 1]})
392
393
394
395
396
397 if spin_array == [3, 3, 1, 1] and colors == [-3, 3, 8, 8]:
398 myinter.set('couplings', {(0, 0):values[len(part_list)],
399 (1, 0):values[len(part_list)]})
400
401
402 order_list = values[2 * len(part_list) - 2: \
403 3 * len(part_list) - 4]
404
405 def count_duplicates_in_list(dupedlist):
406 """return a dictionary with key the element of dupeList and
407 with value the number of times that they are in this list"""
408 unique_set = set(item for item in dupedlist)
409 ret_dict = {}
410 for item in unique_set:
411 ret_dict[item] = dupedlist.count(item)
412 return ret_dict
413
414 myinter.set('orders', count_duplicates_in_list(order_list))
415
416 myinter.set('id', len(myinterlist) + 1)
417
418 myinterlist.append(myinter)
419
420 except Interaction.PhysicsObjectError, why:
421 logger.error("Interaction ignored: %s" % why)
422
423 return myinterlist
424
429 """A simple function reading the files in fsock and returning a
430 ProcCardv4Reader object. This function authorize to have the same syntax as
431 for the other files treatment"""
432
433 reader = ProcCardv4Reader(fsock)
434 return reader
435
436 -class ParticleError(InvalidCmd):
437 """ A class to carch the error"""
438 pass
439
441 """A specific class error for wrong V4 proc_card"""
442 pass
443
445 """read a proc_card.dat in the mg4 format and creates the equivalent routine
446 for mg5"""
447
448
449
450
451 pat_line = re.compile(r"""^\s*(?P<info>[^\#]*?)\s*(\#|$)""", re.DOTALL)
452
454 """init the variable"""
455
456 self.process = []
457 self.model = ""
458 self.multipart = []
459 self.particles_name = set()
460 self.couplings_name = set()
461 self.process_path = os.path.realpath(os.path.join(
462 os.path.dirname(fsock.name), os.pardir))
463
464
465 self.analyze_v4_proc_card(fsock)
466
467
469 """read the file and fullfill the variable with mg4 line"""
470
471 proc_card = fsock.read()
472
473
474 process_open = False
475
476 process_re = re.search(\
477 r"^# Begin\s+PROCESS.*?^(?P<process>.*)^# End\s+PROCESS",
478 proc_card, re.MULTILINE|re.DOTALL)
479
480 if not process_re:
481 raise WrongFileFormat('No valid Begin...End PROCESS tags')
482
483 model_re = re.search(\
484 r"^# Begin\s+MODEL.*?^(?P<model>.+?)(\s+|$)^# End\s+MODEL",
485 proc_card, re.MULTILINE|re.DOTALL)
486
487 if not model_re:
488 raise WrongFileFormat('No valid Begin...End MODEL tags')
489
490 multiparticles_re = re.search(\
491 r"^# Begin\s+MULTIPARTICLES.*?^(?P<multiparticles>.*)^# End\s+MULTIPARTICLES",
492 proc_card, re.MULTILINE|re.DOTALL)
493
494 if not multiparticles_re:
495 raise WrongFileFormat('No valid Begin...End MULTIPARTICLES tags')
496
497 process_lines = process_re.group('process').split('\n')
498
499 for line in process_lines:
500
501
502 analyze_line = self.pat_line.search(line)
503 if analyze_line:
504 data = analyze_line.group('info')
505 if not data:
506 continue
507 if not process_open and 'done' not in data:
508 process_open = True
509 self.process.append(ProcessInfo(data))
510 elif 'end_coup' in data:
511 process_open = False
512 elif 'done' not in data:
513 self.process[-1].add_coupling(data)
514
515 self.model = model_re.group('model')
516
517 multiparticles_lines = multiparticles_re.group('multiparticles').split('\n')
518
519 for line in multiparticles_lines:
520 analyze_line = self.pat_line.search(line)
521 if analyze_line:
522 line = analyze_line.group('info')
523 if not line:
524 continue
525 data = line.split()
526 self.particles_name.add(data[0].lower())
527 self.multipart.append(line)
528
529
531 """Return the MG5 command line corresponding to this proc_card
532 the MG5 command import model is skipped (since the model should be
533 loaded -it is one of the argument-)"""
534
535
536 self.extract_info_from_model(model)
537
538
539
540 for process in self.process:
541 process.analyze_process(self.particles_name)
542
543
544 lines = []
545
546 if self.multipart:
547 lines.append('# Define multiparticle labels')
548 for multipart in self.multipart:
549 data = self.separate_particle(multipart, self.particles_name)
550 lines.append('define ' + ' '.join(data))
551
552
553 if self.process:
554 lines.append('# Specify process(es) to run')
555 for i, process in enumerate(self.process):
556 if i == 0:
557 lines.append('generate %s' % \
558 process.mg5_process_line(self.couplings_name))
559 else:
560 lines.append('add process %s' % \
561 process.mg5_process_line(self.couplings_name))
562
563
564 lines.append('# Output processes to MadEvent directory')
565 lines.append('output -f')
566
567 return lines
568
569
571 """ creates the self.particles_name (list of all valid name)
572 and self.couplings_name (list of all couplings)"""
573
574
575
576 for particle in model['particles']:
577 self.particles_name.add(particle['name'])
578 self.particles_name.add(particle['antiname'])
579
580
581 for interaction in model['interactions']:
582 for coupling in interaction['orders'].keys():
583 self.couplings_name.add(coupling)
584
585
586 @staticmethod
587 - def separate_particle(line, possible_str):
588 """ for a list of concatanate variable return a list of particle name"""
589
590 line = line.lower()
591 out = []
592
593
594
595
596
597
598 pos = 0
599 old_pos = -1
600 line += ' '
601 while pos < len(line) - 4:
602
603 if pos == old_pos:
604 logging.error('Invalid particle name: %s' % \
605 line[pos:pos + 4].rstrip())
606 raise ParticleError('Invalid particle name %s' %
607 line[pos:pos + 4].rstrip())
608 old_pos = pos
609
610 if line[pos] in [' ', '\n', '\t']:
611 pos += 1
612 continue
613
614
615 for i in range(4, 0, -1):
616 if line[pos:pos + i] in possible_str:
617 out.append(line[pos:pos + i])
618 pos = pos + i
619 break
620
621 return out
622
624 """This is the basic object for storing process information"""
625
627 """Initialize information"""
628
629 self.particles = []
630 self.couplings = {}
631 self.decays = []
632 self.tag = ''
633 self.s_forbid = []
634 self.forbid = []
635 self.line = line
636
637 self.is_mg5_valid = False
638
639 self.separate_particle = ProcCardv4Reader.separate_particle
640
642 """Add a line information
643 two format are possible (decay chains or not)
644 pp>h>WWj /a $u @3
645 pp>(h>WW)j /a $u @3
646 """
647
648 line = self.line
649
650 if '@' in line:
651 split = line.split('@')
652 line = split[0]
653 self.tag = split[1]
654
655
656
657 if '/mg5/' in line:
658 self.line = line.replace('/mg5/','')
659 self.is_mg5_valid = True
660 return
661 if ',' in line or '=' in line:
662 self.is_mg5_valid = True
663 return
664
665
666 pos_forbid = line.find('/')
667 pos_sforbid = line.find('$')
668
669
670
671 if pos_forbid != -1 and pos_sforbid != -1:
672 if pos_forbid > pos_sforbid :
673 self.forbid = self.separate_particle(line[pos_forbid + 1:], \
674 particles_name)
675 self.s_forbid = self.separate_particle(\
676 line[pos_sforbid + 1:pos_forbid], particles_name)
677 line = line[:min(pos_forbid, pos_sforbid)]
678 else:
679 self.forbid = self.separate_particle(\
680 line[pos_forbid + 1:pos_sforbid], particles_name)
681 self.s_forbid = self.separate_particle(line[pos_sforbid + 1:], \
682 particles_name)
683 line = line[:min(pos_forbid, pos_sforbid)]
684
685 elif pos_forbid != -1:
686 self.forbid = self.separate_particle(line[pos_forbid + 1:], \
687 particles_name)
688 line = line[:pos_forbid]
689
690 elif pos_sforbid != -1:
691 self.s_forbid = self.separate_particle(line[pos_sforbid + 1:], \
692 particles_name)
693 line = line[:pos_sforbid]
694
695
696
697 if '(' in line:
698 line = self.treat_decay_chain(line, particles_name)
699
700
701 level_content = line.split('>')
702 for level, data in enumerate(level_content):
703 particles = self.separate_particle(data, particles_name)
704 if particles:
705 [self.particles.append((level, name)) for name in particles]
706
707
709 """Split the information of the decays into a tree of ProcessInfo."""
710
711 level = 0
712 out_line = ''
713 for character in line:
714 if character == '(':
715 level += 1
716 if level == 1:
717 decay_line = ""
718 else:
719 decay_line += '('
720 continue
721 elif character == ')':
722 level -= 1
723 if level == 0:
724 self.decays.append(ProcessInfo(decay_line))
725 self.decays[-1].add_restrictions(self.forbid, self.s_forbid,
726 None)
727 self.decays[-1].analyze_process(particles_name)
728 out_line += decay_line[:decay_line.find('>')]
729 else:
730 decay_line += ')'
731 continue
732 elif level:
733 decay_line += character
734 else:
735 out_line += character
736 return out_line
737
739 """Add the coupling information to the process"""
740 data = line.split('=')
741 self.couplings[data[0]] = int(data[1])
742
743
745 """Associate some restriction to this diagram"""
746
747 self.forbid = forbid
748 self.s_forbid = s_forbid
749 self.couplings = couplings
750
752 """Return a valid mg5 format for this process """
753
754 if self.is_mg5_valid:
755 return self.line
756
757 text = ''
758
759 cur_level = 0
760 for level, particle in self.particles:
761 if level > cur_level:
762 text += '> '
763 cur_level += 1
764 text += '%s ' % particle
765
766
767 if self.s_forbid:
768 text += '$ ' + ' '.join(self.s_forbid) + ' '
769 if self.forbid:
770 text += '/ ' + ' '.join(self.forbid) + ' '
771
772
773 for decay in self.decays:
774 decay_text = decay.mg5_process_line(model_coupling)
775 if ',' in decay_text:
776 text = text.rstrip() + ', (%s) ' % decay_text.strip()
777 else:
778 text = text.rstrip() + ', %s ' % decay_text.strip()
779
780
781 if self.tag:
782 text += '@%s ' % self.tag
783
784 if self.couplings:
785 if not self.tag:
786 text += '@0 '
787
788 text += self.mg5_couplings_line(model_coupling, len(self.particles))
789
790 return text.rstrip()
791
793 """Return the assignment of coupling for this process"""
794
795 out = ''
796 for coupling in model_coupling:
797 if self.couplings.has_key(coupling):
798
799 out += '%s=%s ' % (coupling, self.couplings[coupling])
800 else:
801
802 out += '%s=0 ' % coupling
803
804 return out
805