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