Package madgraph :: Package iolibs :: Module import_v4
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.import_v4

  1  ################################################################################ 
  2  # 
  3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
  4  # 
  5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
  6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
  7  # high-energy processes in the Standard Model and beyond. 
  8  # 
  9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
 10  # distribution. 
 11  # 
 12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
 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') 
35 36 #=============================================================================== 37 # import_v4model 38 #=============================================================================== 39 -def import_model(model_path, mgme_dir = MG4DIR, absolute=True, web=True):
40 """create a model from a MG4 model directory.""" 41 42 # Check for a valid directory 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 # use pickle files if defined 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 # save in a pickle files to fasten future usage 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
100 101 -def find_model_path(model_path, mgme_dir, absolute=True):
102 """Find the path to the model, starting with path model_path.""" 103 104 # treat simple case (model_path is a valid path/ mgme_dir doesn't exist) 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 # Try to build the valid path 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 # No valid path found 129 raise InvalidCmd("Path %s is not a valid pathname" % model_path)
130
131 #=============================================================================== 132 # read_particles_v4 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 # stop scanning if old MULTIPARTICLES tag found 161 162 line = line.split("#", 2)[0] # remove any comment 163 line = line.strip() # makes the string clean 164 165 if line != "": 166 values = line.split() 167 if len(values) != 9: 168 # Not the right number tags on the line 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 #mypart.set("texname", values[7]) 204 mypart.set("pdg_code", int(values[8])) 205 206 mypart.set('charge', 0.) 207 #mypart.set('antitexname', mypart.get('texname')) 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
216 217 #=============================================================================== 218 # read_interactions_v4 219 #=============================================================================== 220 -def read_interactions_v4(fsock, ref_part_list):
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] # remove any comment 235 line = line.strip() # makes the string clean 236 237 if line != "": # skip blank 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 # Look at the total number of strings, stop if 246 # anyway not enough, required if a variable name 247 # corresponds to a particle! (eg G) 248 if len(values) >= 2 * len(part_list) + 1: 249 part_list.append(curr_part) 250 else: break 251 # also stops if string does not correspond to 252 # a particle name 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 # Flip part/antipart of first part for FFV, FFS, FFT vertices 260 # according to v4 convention 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 # Give color structure 269 # Order particles according to color 270 # Don't consider singlets 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 # Set color empty by default 279 myinter.set('color', []) 280 if not colors: 281 # All color singlets - set empty 282 pass 283 elif colors == [-3, 3]: 284 # triplet-triplet-singlet coupling 285 myinter.set('color', [color.ColorString(\ 286 [color.T(ind[1], ind[0])])]) 287 elif colors == [8, 8]: 288 # octet-octet-singlet coupling 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 # triplet-triplet-octet coupling 295 myinter.set('color', [color.ColorString(\ 296 [color.T(ind[2], ind[1], ind[0])])]) 297 elif colors == [8, 8, 8]: 298 # Triple glue coupling 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 # 4-glue coupling 311 cs1 = color.ColorString([color.f(0, 1, -1), 312 color.f(2, 3, -1)]) 313 #cs1.coeff = fractions.Fraction(-1) 314 cs2 = color.ColorString([color.f(2, 0, -1), 315 color.f(1, 3, -1)]) 316 #cs2.coeff = fractions.Fraction(-1) 317 cs3 = color.ColorString([color.f(1, 2, -1), 318 color.f(0, 3, -1)]) 319 #cs3.coeff = fractions.Fraction(-1) 320 myinter.set('color', [cs1, cs2, cs3]) 321 # The following line are expected to be correct but not physical validations 322 # have been performed. So we keep it commented for the moment. 323 # elif colors == [3, 3, 3]: 324 # my_color_string = color.ColorString(\ 325 # [color.Epsilon(ind[0], ind[1], ind[2])]) 326 # myinter.set('color', [my_color_string]) 327 # elif colors == [-3, -3, -3]: 328 # my_color_string = color.ColorString(\ 329 # [color.EpsilonBar(ind[0], ind[1], ind[2])]) 330 # myinter.set('color', [my_color_string]) 331 else: 332 logger.warning(\ 333 "Color combination %s not yet implemented." % \ 334 repr(colors)) 335 336 # Set the Lorentz structure. Default for 3-particle 337 # vertices is empty string, for 4-particle pair of 338 # empty strings 339 myinter.set('lorentz', ['']) 340 341 pdg_codes = sorted([part.get_pdg_code() for part in part_list]) 342 343 # WWWW and WWVV 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 # gggg 351 if pdg_codes == [21, 21, 21, 21]: 352 myinter.set('lorentz', ['gggg1', 'gggg2', 'gggg3']) 353 354 # go-go-g 355 # Using the special fvigox routine provides the minus 356 # sign necessary for octet Majorana-vector interactions 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 # If extra flag, add this to Lorentz 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 # Use the other strings to fill variable names and tags 369 370 # Couplings: special treatment for 4-vertices, where MG4 used 371 # two couplings, while MG5 only uses one (with the exception 372 # of the 4g vertex, which needs special treatment) 373 # DUM0 and DUM1 are used as placeholders by FR, corresponds to 1 374 if len(part_list) == 3 or \ 375 values[len(part_list) + 1] in ['DUM', 'DUM0', 'DUM1']: 376 # We can just use the first coupling, since the second 377 # is a dummy 378 myinter.set('couplings', {(0, 0):values[len(part_list)]}) 379 if myinter.get('lorentz')[0] == 'WWWWN': 380 # Should only use one Helas amplitude for electroweak 381 # 4-vector vertices with FR. I choose W3W3NX. 382 myinter.set('lorentz', ['WWVVN']) 383 elif values[len(part_list)] in ['DUM', 'DUM0', 'DUM1']: 384 # We can just use the second coupling, since the first 385 # is a dummy 386 myinter.set('couplings', {(0, 0):values[len(part_list)+1]}) 387 elif pdg_codes == [21, 21, 21, 21]: 388 # gggg 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 # Need special treatment of v4 SM WWWW couplings since 394 # MG5 can only have one coupling per Lorentz structure 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: #if myinter.get('lorentz')[0] == 'WWVV': 402 # Need special treatment of v4 SM WWVV couplings since 403 # MG5 can only have one coupling per Lorentz structure 404 myinter.set('couplings', {(0, 0):values[len(part_list)] + \ 405 '*' + \ 406 values[len(part_list) + 1]}) 407 #raise Interaction.PhysicsObjectError, \ 408 # "Only FR-style 4-vertices implemented." 409 410 # SPECIAL TREATMENT OF COLOR 411 # g g sq sq (two different color structures, same Lorentz) 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 # Coupling orders - needs to be fixed 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
440 #=============================================================================== 441 # read_proc_card.dat (mg4 format) 442 #=============================================================================== 443 -def read_proc_card_v4(fsock):
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
455 -class WrongFileFormat(InvalidCmd):
456 """A specific class error for wrong V4 proc_card""" 457 pass 458
459 -class ProcCardv4Reader(object):
460 """read a proc_card.dat in the mg4 format and creates the equivalent routine 461 for mg5""" 462 463 #tag in the proc_card.dat which split the proc_card content 464 465 # line pattern (remove comment at the end of the line) 466 pat_line = re.compile(r"""^\s*(?P<info>[^\#]*?)\s*(\#|$)""", re.DOTALL) 467
468 - def __init__(self, fsock):
469 """init the variable""" 470 471 self.process = [] # List of ProcessInfo 472 self.model = "" # name of the model 473 self.multipart = [] # list of the mg4 definition of multiparticle 474 self.particles_name = set() # set of authorize particle name 475 self.couplings_name = set() # set of mandatory couplings 476 self.process_path = os.path.realpath(os.path.join( 477 os.path.dirname(fsock.name), os.pardir)) 478 479 # Reading the files and store the information in string format. 480 self.analyze_v4_proc_card(fsock)
481 482
483 - def analyze_v4_proc_card(self, fsock):
484 """read the file and fullfill the variable with mg4 line""" 485 486 proc_card = fsock.read() 487 488 # store process information 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 # an 'end_coup' stop the current process, 516 # 'done' finish the list of process 517 analyze_line = self.pat_line.search(line) 518 if analyze_line: 519 data = analyze_line.group('info') #skip the comment 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') #skip the comment 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
545 - def extract_command_lines(self, model):
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 # extract useful information of the model 551 self.extract_info_from_model(model) 552 553 # use the model information for the splitting in particles of the mg4 554 #process line. 555 for process in self.process: 556 process.analyze_process(self.particles_name) 557 558 #Now we are in position to write the lines call 559 lines = [] 560 #first write the lines associate to the multiparticls definition 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 # secondly define the lines associate with diagram 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 #finally export the madevent output 579 lines.append('# Output processes to MadEvent directory') 580 lines.append('output -f') 581 582 return lines
583 584
585 - def extract_info_from_model(self, model):
586 """ creates the self.particles_name (list of all valid name) 587 and self.couplings_name (list of all couplings)""" 588 589 # add in self.particles_name (it contains normally the mulpart name 590 #already) all the valid name of particle of the model 591 for particle in model['particles']: 592 self.particles_name.add(particle['name']) 593 self.particles_name.add(particle['antiname']) 594 595 # add in self.couplings_name the couplings name of the model 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() # Particle name are not case sensitive 606 out = [] # list of the particles 607 # The procedure to find particles is the following 608 # - check if the combination of 4 string form a valid particle name 609 # if it is, move of 4 characters and check for the next particles. 610 # if not try with 3, 2, 1 611 # if still not -> exit. 612 613 pos = 0 # current starting position 614 old_pos = -1 # check that we don't have infinite loop 615 line += ' ' #add 4 blank for security 616 while pos < len(line) - 4: 617 #Check for infinite loop 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 # check for pointless character 625 if line[pos] in [' ', '\n', '\t']: 626 pos += 1 627 continue 628 629 # try to find a match at 4(then 3/2/1) characters 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
638 -class ProcessInfo(object):
639 """This is the basic object for storing process information""" 640
641 - def __init__(self, line):
642 """Initialize information""" 643 644 self.particles = [] # list tuple (level, particle) 645 self.couplings = {} # coupling -> max_order 646 self.decays = [] # ProcessInfo of the decays 647 self.tag = '' # tag of the process 648 self.s_forbid = [] # list of particles forbids in s channel 649 self.forbid = [] # list of particles forbids 650 self.line = line # initialization line 651 652 self.is_mg5_valid = False 653 #some shortcut 654 self.separate_particle = ProcCardv4Reader.separate_particle
655
656 - def analyze_process(self, particles_name):
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 #extract the tag 665 if '@' in line: 666 split = line.split('@') 667 line = split[0] 668 self.tag = split[1] 669 670 671 # check if we have a MG5 format 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 # extract (S-)forbidden particle 681 pos_forbid = line.find('/') 682 pos_sforbid = line.find('$') 683 684 # Select the restrictions (pos is -1 if not defined) 685 #and remove the restrictions from the line 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 # Same but if they are no S-forbidden particles 700 elif pos_forbid != -1: 701 self.forbid = self.separate_particle(line[pos_forbid + 1:], \ 702 particles_name) 703 line = line[:pos_forbid] 704 # Same but if they are no forbidden particles 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 # Deal with decay chains, returns lines whitout the decay (and treat 711 #the different decays. 712 if '(' in line: 713 line = self.treat_decay_chain(line, particles_name) 714 715 #define the level of each particle 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
723 - def treat_decay_chain(self, line, particles_name):
724 """Split the information of the decays into a tree of ProcessInfo.""" 725 726 level = 0 #depth of the decay chain 727 out_line = '' # core process 728 for character in line: 729 if character == '(': 730 level += 1 731 if level == 1: 732 decay_line = "" # initialize a new decay info 733 else: 734 decay_line += '(' 735 continue 736 elif character == ')': 737 level -= 1 738 if level == 0: #store the information 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
753 - def add_coupling(self, line):
754 """Add the coupling information to the process""" 755 data = line.split('=') 756 self.couplings[data[0]] = int(data[1])
757 758
759 - def add_restrictions(self, forbid, s_forbid, couplings):
760 """Associate some restriction to this diagram""" 761 762 self.forbid = forbid 763 self.s_forbid = s_forbid 764 self.couplings = couplings
765
766 - def mg5_process_line(self, model_coupling):
767 """Return a valid mg5 format for this process """ 768 769 if self.is_mg5_valid: 770 return self.line 771 772 text = '' 773 # Write the process 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 # Write the constraints 782 if self.s_forbid: 783 text += '$ ' + ' '.join(self.s_forbid) + ' ' 784 if self.forbid: 785 text += '/ ' + ' '.join(self.forbid) + ' ' 786 787 #treat decay_chains 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 # write the tag 796 if self.tag: 797 text += '@%s ' % self.tag 798 799 if self.couplings: 800 if not self.tag: 801 text += '@0 ' 802 #write the rules associate to the couplings 803 text += self.mg5_couplings_line(model_coupling, len(self.particles)) 804 805 return text.rstrip()
806
807 - def mg5_couplings_line(self, model_coupling, nb_part):
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 # Need coupling for all cases, since might be decay chain 814 out += '%s=%s ' % (coupling, self.couplings[coupling]) 815 else: 816 # if not define put to zero (mg4 default) 817 out += '%s=0 ' % coupling 818 819 return out
820