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 MadGraph Development team and Contributors 
  4  # 
  5  # This file is a part of the MadGraph 5 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 MadGraph license which should accompany this  
 10  # distribution. 
 11  # 
 12  # For more information, please visit: http://madgraph.phys.ucl.ac.be 
 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') 
35 36 #=============================================================================== 37 # import_v4model 38 #=============================================================================== 39 -def import_model(model_path, mgme_dir = MG4DIR):
40 """create a model from a MG4 model directory.""" 41 42 # Check for a valid directory 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 # use pickle files if defined 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 # save in a pickle files to fasten future usage 73 save_load_object.save_to_file(os.path.join(model_path, 'model.pkl'), model) 74 75 return model, model_path
76
77 78 -def find_model_path(model_path, mgme_dir):
79 """Find the path to the model, starting with path model_path.""" 80 81 # treat simple case (model_path is a valid path/ mgme_dir doesn't exist) 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 # Try to build the valid path 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 # No valid path found 106 raise InvalidCmd("Path %s is not a valid pathname" % model_path)
107
108 #=============================================================================== 109 # read_particles_v4 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 # stop scanning if old MULTIPARTICLES tag found 138 139 line = line.split("#", 2)[0] # remove any comment 140 line = line.strip() # makes the string clean 141 142 if line != "": 143 values = line.split() 144 if len(values) != 9: 145 # Not the right number tags on the line 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
193 194 #=============================================================================== 195 # read_interactions_v4 196 #=============================================================================== 197 -def read_interactions_v4(fsock, ref_part_list):
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] # remove any comment 212 line = line.strip() # makes the string clean 213 214 if line != "": # skip blank 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 # Look at the total number of strings, stop if 223 # anyway not enough, required if a variable name 224 # corresponds to a particle! (eg G) 225 if len(values) >= 2 * len(part_list) + 1: 226 part_list.append(curr_part) 227 else: break 228 # also stops if string does not correspond to 229 # a particle name 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 # Flip part/antipart of first part for FFV, FFS, FFT vertices 237 # according to v4 convention 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 # Give color structure 246 # Order particles according to color 247 # Don't consider singlets 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 # Set color empty by default 256 myinter.set('color', []) 257 if not colors: 258 # All color singlets - set empty 259 pass 260 elif colors == [-3, 3]: 261 # triplet-triplet-singlet coupling 262 myinter.set('color', [color.ColorString(\ 263 [color.T(ind[1], ind[0])])]) 264 elif colors == [8, 8]: 265 # octet-octet-singlet coupling 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 # triplet-triplet-octet coupling 272 myinter.set('color', [color.ColorString(\ 273 [color.T(ind[2], ind[1], ind[0])])]) 274 elif colors == [8, 8, 8]: 275 # Triple glue coupling 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 # 4-glue coupling 288 cs1 = color.ColorString([color.f(0, 1, -1), 289 color.f(2, 3, -1)]) 290 #cs1.coeff = fractions.Fraction(-1) 291 cs2 = color.ColorString([color.f(2, 0, -1), 292 color.f(1, 3, -1)]) 293 #cs2.coeff = fractions.Fraction(-1) 294 cs3 = color.ColorString([color.f(1, 2, -1), 295 color.f(0, 3, -1)]) 296 #cs3.coeff = fractions.Fraction(-1) 297 myinter.set('color', [cs1, cs2, cs3]) 298 else: 299 logger.warning(\ 300 "Color combination %s not yet implemented." % \ 301 repr(colors)) 302 303 # Set the Lorentz structure. Default for 3-particle 304 # vertices is empty string, for 4-particle pair of 305 # empty strings 306 myinter.set('lorentz', ['']) 307 308 pdg_codes = sorted([part.get_pdg_code() for part in part_list]) 309 310 # WWWW and WWVV 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 # gggg 318 if pdg_codes == [21, 21, 21, 21]: 319 myinter.set('lorentz', ['gggg1', 'gggg2', 'gggg3']) 320 321 # go-go-g 322 # Using the special fvigox routine provides the minus 323 # sign necessary for octet Majorana-vector interactions 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 # If extra flag, add this to Lorentz 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 # Use the other strings to fill variable names and tags 336 337 # Couplings: special treatment for 4-vertices, where MG4 used 338 # two couplings, while MG5 only uses one (with the exception 339 # of the 4g vertex, which needs special treatment) 340 # DUM0 and DUM1 are used as placeholders by FR, corresponds to 1 341 if len(part_list) == 3 or \ 342 values[len(part_list) + 1] in ['DUM', 'DUM0', 'DUM1']: 343 # We can just use the first coupling, since the second 344 # is a dummy 345 myinter.set('couplings', {(0, 0):values[len(part_list)]}) 346 if myinter.get('lorentz')[0] == 'WWWWN': 347 # Should only use one Helas amplitude for electroweak 348 # 4-vector vertices with FR. I choose W3W3NX. 349 myinter.set('lorentz', ['WWVVN']) 350 elif values[len(part_list)] in ['DUM', 'DUM0', 'DUM1']: 351 # We can just use the second coupling, since the first 352 # is a dummy 353 myinter.set('couplings', {(0, 0):values[len(part_list)+1]}) 354 elif pdg_codes == [21, 21, 21, 21]: 355 # gggg 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 # Need special treatment of v4 SM WWWW couplings since 361 # MG5 can only have one coupling per Lorentz structure 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: #if myinter.get('lorentz')[0] == 'WWVV': 369 # Need special treatment of v4 SM WWVV couplings since 370 # MG5 can only have one coupling per Lorentz structure 371 myinter.set('couplings', {(0, 0):values[len(part_list)] + \ 372 '*' + \ 373 values[len(part_list) + 1]}) 374 #raise Interaction.PhysicsObjectError, \ 375 # "Only FR-style 4-vertices implemented." 376 377 # SPECIAL TREATMENT OF COLOR 378 # g g sq sq (two different color structures, same Lorentz) 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 # Coupling orders - needs to be fixed 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
407 #=============================================================================== 408 # read_proc_card.dat (mg4 format) 409 #=============================================================================== 410 -def read_proc_card_v4(fsock):
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
422 -class WrongFileFormat(InvalidCmd):
423 """A specific class error for wrong V4 proc_card""" 424 pass 425
426 -class ProcCardv4Reader(object):
427 """read a proc_card.dat in the mg4 format and creates the equivalent routine 428 for mg5""" 429 430 #tag in the proc_card.dat which split the proc_card content 431 432 # line pattern (remove comment at the end of the line) 433 pat_line = re.compile(r"""^\s*(?P<info>[^\#]*?)\s*(\#|$)""", re.DOTALL) 434
435 - def __init__(self, fsock):
436 """init the variable""" 437 438 self.process = [] # List of ProcessInfo 439 self.model = "" # name of the model 440 self.multipart = [] # list of the mg4 definition of multiparticle 441 self.particles_name = set() # set of authorize particle name 442 self.couplings_name = set() # set of mandatory couplings 443 self.process_path = os.path.realpath(os.path.join( 444 os.path.dirname(fsock.name), os.pardir)) 445 446 # Reading the files and store the information in string format. 447 self.analyze_v4_proc_card(fsock)
448 449
450 - def analyze_v4_proc_card(self, fsock):
451 """read the file and fullfill the variable with mg4 line""" 452 453 proc_card = fsock.read() 454 455 # store process information 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 # an 'end_coup' stop the current process, 483 # 'done' finish the list of process 484 analyze_line = self.pat_line.search(line) 485 if analyze_line: 486 data = analyze_line.group('info') #skip the comment 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') #skip the comment 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
512 - def extract_command_lines(self, model):
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 # extract useful information of the model 518 self.extract_info_from_model(model) 519 520 # use the model information for the splitting in particles of the mg4 521 #process line. 522 for process in self.process: 523 process.analyze_process(self.particles_name) 524 525 #Now we are in position to write the lines call 526 lines = [] 527 #first write the lines associate to the multiparticls definition 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 # secondly define the lines associate with diagram 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 #finally export the madevent output 546 lines.append('# Output processes to MadEvent directory') 547 lines.append('output -f') 548 549 return lines
550 551
552 - def extract_info_from_model(self, model):
553 """ creates the self.particles_name (list of all valid name) 554 and self.couplings_name (list of all couplings)""" 555 556 # add in self.particles_name (it contains normally the mulpart name 557 #already) all the valid name of particle of the model 558 for particle in model['particles']: 559 self.particles_name.add(particle['name']) 560 self.particles_name.add(particle['antiname']) 561 562 # add in self.couplings_name the couplings name of the model 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() # Particle name are not case sensitive 573 out = [] # list of the particles 574 # The procedure to find particles is the following 575 # - check if the combination of 4 string form a valid particle name 576 # if it is, move of 4 characters and check for the next particles. 577 # if not try with 3, 2, 1 578 # if still not -> exit. 579 580 pos = 0 # current starting position 581 old_pos = -1 # check that we don't have infinite loop 582 line += ' ' #add 4 blank for security 583 while pos < len(line) - 4: 584 #Check for infinite loop 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 # check for pointless character 592 if line[pos] in [' ', '\n', '\t']: 593 pos += 1 594 continue 595 596 # try to find a match at 4(then 3/2/1) characters 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
605 -class ProcessInfo(object):
606 """This is the basic object for storing process information""" 607
608 - def __init__(self, line):
609 """Initialize information""" 610 611 self.particles = [] # list tuple (level, particle) 612 self.couplings = {} # coupling -> max_order 613 self.decays = [] # ProcessInfo of the decays 614 self.tag = '' # tag of the process 615 self.s_forbid = [] # list of particles forbids in s channel 616 self.forbid = [] # list of particles forbids 617 self.line = line # initialization line 618 619 self.is_mg5_valid = False 620 #some shortcut 621 self.separate_particle = ProcCardv4Reader.separate_particle
622
623 - def analyze_process(self, particles_name):
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 #extract the tag 632 if '@' in line: 633 split = line.split('@') 634 line = split[0] 635 self.tag = split[1] 636 637 638 # check if we have a MG5 format 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 # extract (S-)forbidden particle 648 pos_forbid = line.find('/') 649 pos_sforbid = line.find('$') 650 651 # Select the restrictions (pos is -1 if not defined) 652 #and remove the restrictions from the line 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 # Same but if they are no S-forbidden particles 667 elif pos_forbid != -1: 668 self.forbid = self.separate_particle(line[pos_forbid + 1:], \ 669 particles_name) 670 line = line[:pos_forbid] 671 # Same but if they are no forbidden particles 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 # Deal with decay chains, returns lines whitout the decay (and treat 678 #the different decays. 679 if '(' in line: 680 line = self.treat_decay_chain(line, particles_name) 681 682 #define the level of each particle 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
690 - def treat_decay_chain(self, line, particles_name):
691 """Split the information of the decays into a tree of ProcessInfo.""" 692 693 level = 0 #depth of the decay chain 694 out_line = '' # core process 695 for character in line: 696 if character == '(': 697 level += 1 698 if level == 1: 699 decay_line = "" # initialize a new decay info 700 else: 701 decay_line += '(' 702 continue 703 elif character == ')': 704 level -= 1 705 if level == 0: #store the information 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
720 - def add_coupling(self, line):
721 """Add the coupling information to the process""" 722 data = line.split('=') 723 self.couplings[data[0]] = int(data[1])
724 725
726 - def add_restrictions(self, forbid, s_forbid, couplings):
727 """Associate some restriction to this diagram""" 728 729 self.forbid = forbid 730 self.s_forbid = s_forbid 731 self.couplings = couplings
732
733 - def mg5_process_line(self, model_coupling):
734 """Return a valid mg5 format for this process """ 735 736 if self.is_mg5_valid: 737 return self.line 738 739 text = '' 740 # Write the process 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 # Write the constraints 749 if self.s_forbid: 750 text += '$ ' + ' '.join(self.s_forbid) + ' ' 751 if self.forbid: 752 text += '/ ' + ' '.join(self.forbid) + ' ' 753 754 #treat decay_chains 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 # write the tag 763 if self.tag: 764 text += '@%s ' % self.tag 765 766 if self.couplings: 767 if not self.tag: 768 text += '@0 ' 769 #write the rules associate to the couplings 770 text += self.mg5_couplings_line(model_coupling, len(self.particles)) 771 772 return text.rstrip()
773
774 - def mg5_couplings_line(self, model_coupling, nb_part):
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 # Need coupling for all cases, since might be decay chain 781 out += '%s=%s ' % (coupling, self.couplings[coupling]) 782 else: 783 # if not define put to zero (mg4 default) 784 out += '%s=0 ' % coupling 785 786 return out
787