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