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