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 __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') 
37 38 #=============================================================================== 39 # import_v4model 40 #=============================================================================== 41 -def import_model(model_path, mgme_dir = MG4DIR, absolute=True, web=True):
42 """create a model from a MG4 model directory.""" 43 44 # Check for a valid directory 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 # use pickle files if defined 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 # save in a pickle files to fasten future usage 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
103 104 -def find_model_path(model_path, mgme_dir, absolute=True):
105 """Find the path to the model, starting with path model_path.""" 106 107 # treat simple case (model_path is a valid path/ mgme_dir doesn't exist) 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 # Try to build the valid path 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 # No valid path found 132 raise InvalidCmd("Path %s is not a valid pathname" % model_path)
133
134 #=============================================================================== 135 # read_particles_v4 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 # stop scanning if old MULTIPARTICLES tag found 164 165 line = line.split("#", 2)[0] # remove any comment 166 line = line.strip() # makes the string clean 167 168 if line != "": 169 values = line.split() 170 if len(values) != 9: 171 # Not the right number tags on the line 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 #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) as 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("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] # remove any comment 234 line = line.strip() # makes the string clean 235 236 if line != "": # skip blank 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 # Look at the total number of strings, stop if 245 # anyway not enough, required if a variable name 246 # corresponds to a particle! (eg G) 247 if len(values) >= 2 * len(part_list) + 1: 248 part_list.append(curr_part) 249 else: break 250 # also stops if string does not correspond to 251 # a particle name 252 else: break 253 254 if len(part_list) < 3: 255 raise Interaction.PhysicsObjectError("Vertex with less than 3 known particles found.") 256 257 # Flip part/antipart of first part for FFV, FFS, FFT vertices 258 # according to v4 convention 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 # Give color structure 267 # Order particles according to color 268 # Don't consider singlets 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 # Set color empty by default 277 myinter.set('color', []) 278 if not colors: 279 # All color singlets - set empty 280 pass 281 elif colors == [-3, 3]: 282 # triplet-triplet-singlet coupling 283 myinter.set('color', [color.ColorString(\ 284 [color.T(ind[1], ind[0])])]) 285 elif colors == [8, 8]: 286 # octet-octet-singlet coupling 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 # triplet-triplet-octet coupling 293 myinter.set('color', [color.ColorString(\ 294 [color.T(ind[2], ind[1], ind[0])])]) 295 elif colors == [8, 8, 8]: 296 # Triple glue coupling 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 # 4-glue coupling 309 cs1 = color.ColorString([color.f(0, 1, -1), 310 color.f(2, 3, -1)]) 311 #cs1.coeff = fractions.Fraction(-1) 312 cs2 = color.ColorString([color.f(2, 0, -1), 313 color.f(1, 3, -1)]) 314 #cs2.coeff = fractions.Fraction(-1) 315 cs3 = color.ColorString([color.f(1, 2, -1), 316 color.f(0, 3, -1)]) 317 #cs3.coeff = fractions.Fraction(-1) 318 myinter.set('color', [cs1, cs2, cs3]) 319 # The following line are expected to be correct but not physical validations 320 # have been performed. So we keep it commented for the moment. 321 # elif colors == [3, 3, 3]: 322 # my_color_string = color.ColorString(\ 323 # [color.Epsilon(ind[0], ind[1], ind[2])]) 324 # myinter.set('color', [my_color_string]) 325 # elif colors == [-3, -3, -3]: 326 # my_color_string = color.ColorString(\ 327 # [color.EpsilonBar(ind[0], ind[1], ind[2])]) 328 # myinter.set('color', [my_color_string]) 329 else: 330 logger.warning(\ 331 "Color combination %s not yet implemented." % \ 332 repr(colors)) 333 334 # Set the Lorentz structure. Default for 3-particle 335 # vertices is empty string, for 4-particle pair of 336 # empty strings 337 myinter.set('lorentz', ['']) 338 339 pdg_codes = sorted([part.get_pdg_code() for part in part_list]) 340 341 # WWWW and WWVV 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 # gggg 349 if pdg_codes == [21, 21, 21, 21]: 350 myinter.set('lorentz', ['gggg1', 'gggg2', 'gggg3']) 351 352 # go-go-g 353 # Using the special fvigox routine provides the minus 354 # sign necessary for octet Majorana-vector interactions 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 # If extra flag, add this to Lorentz 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 # Use the other strings to fill variable names and tags 367 368 # Couplings: special treatment for 4-vertices, where MG4 used 369 # two couplings, while MG5 only uses one (with the exception 370 # of the 4g vertex, which needs special treatment) 371 # DUM0 and DUM1 are used as placeholders by FR, corresponds to 1 372 if len(part_list) == 3 or \ 373 values[len(part_list) + 1] in ['DUM', 'DUM0', 'DUM1']: 374 # We can just use the first coupling, since the second 375 # is a dummy 376 myinter.set('couplings', {(0, 0):values[len(part_list)]}) 377 if myinter.get('lorentz')[0] == 'WWWWN': 378 # Should only use one Helas amplitude for electroweak 379 # 4-vector vertices with FR. I choose W3W3NX. 380 myinter.set('lorentz', ['WWVVN']) 381 elif values[len(part_list)] in ['DUM', 'DUM0', 'DUM1']: 382 # We can just use the second coupling, since the first 383 # is a dummy 384 myinter.set('couplings', {(0, 0):values[len(part_list)+1]}) 385 elif pdg_codes == [21, 21, 21, 21]: 386 # gggg 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 # Need special treatment of v4 SM WWWW couplings since 392 # MG5 can only have one coupling per Lorentz structure 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: #if myinter.get('lorentz')[0] == 'WWVV': 400 # Need special treatment of v4 SM WWVV couplings since 401 # MG5 can only have one coupling per Lorentz structure 402 myinter.set('couplings', {(0, 0):values[len(part_list)] + \ 403 '*' + \ 404 values[len(part_list) + 1]}) 405 #raise Interaction.PhysicsObjectError, \ 406 # "Only FR-style 4-vertices implemented." 407 408 # SPECIAL TREATMENT OF COLOR 409 # g g sq sq (two different color structures, same Lorentz) 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 # Coupling orders - needs to be fixed 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
438 #=============================================================================== 439 # read_proc_card.dat (mg4 format) 440 #=============================================================================== 441 -def read_proc_card_v4(fsock):
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
453 -class WrongFileFormat(InvalidCmd):
454 """A specific class error for wrong V4 proc_card""" 455 pass 456
457 -class ProcCardv4Reader(object):
458 """read a proc_card.dat in the mg4 format and creates the equivalent routine 459 for mg5""" 460 461 #tag in the proc_card.dat which split the proc_card content 462 463 # line pattern (remove comment at the end of the line) 464 pat_line = re.compile(r"""^\s*(?P<info>[^\#]*?)\s*(\#|$)""", re.DOTALL) 465
466 - def __init__(self, fsock):
467 """init the variable""" 468 469 self.process = [] # List of ProcessInfo 470 self.model = "" # name of the model 471 self.multipart = [] # list of the mg4 definition of multiparticle 472 self.particles_name = set() # set of authorize particle name 473 self.couplings_name = set() # set of mandatory couplings 474 self.process_path = os.path.realpath(os.path.join( 475 os.path.dirname(fsock.name), os.pardir)) 476 477 # Reading the files and store the information in string format. 478 self.analyze_v4_proc_card(fsock)
479 480
481 - def analyze_v4_proc_card(self, fsock):
482 """read the file and fullfill the variable with mg4 line""" 483 484 proc_card = fsock.read() 485 486 # store process information 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 # an 'end_coup' stop the current process, 514 # 'done' finish the list of process 515 analyze_line = self.pat_line.search(line) 516 if analyze_line: 517 data = analyze_line.group('info') #skip the comment 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') #skip the comment 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
543 - def extract_command_lines(self, model):
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 # extract useful information of the model 549 self.extract_info_from_model(model) 550 551 # use the model information for the splitting in particles of the mg4 552 #process line. 553 for process in self.process: 554 process.analyze_process(self.particles_name) 555 556 #Now we are in position to write the lines call 557 lines = [] 558 #first write the lines associate to the multiparticls definition 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 # secondly define the lines associate with diagram 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 #finally export the madevent output 577 lines.append('# Output processes to MadEvent directory') 578 lines.append('output -f') 579 580 return lines
581 582
583 - def extract_info_from_model(self, model):
584 """ creates the self.particles_name (list of all valid name) 585 and self.couplings_name (list of all couplings)""" 586 587 # add in self.particles_name (it contains normally the mulpart name 588 #already) all the valid name of particle of the model 589 for particle in model['particles']: 590 self.particles_name.add(particle['name']) 591 self.particles_name.add(particle['antiname']) 592 593 # add in self.couplings_name the couplings name of the model 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() # Particle name are not case sensitive 604 out = [] # list of the particles 605 # The procedure to find particles is the following 606 # - check if the combination of 4 string form a valid particle name 607 # if it is, move of 4 characters and check for the next particles. 608 # if not try with 3, 2, 1 609 # if still not -> exit. 610 611 pos = 0 # current starting position 612 old_pos = -1 # check that we don't have infinite loop 613 line += ' ' #add 4 blank for security 614 while pos < len(line) - 4: 615 #Check for infinite loop 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 # check for pointless character 623 if line[pos] in [' ', '\n', '\t']: 624 pos += 1 625 continue 626 627 # try to find a match at 4(then 3/2/1) characters 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
636 -class ProcessInfo(object):
637 """This is the basic object for storing process information""" 638
639 - def __init__(self, line):
640 """Initialize information""" 641 642 self.particles = [] # list tuple (level, particle) 643 self.couplings = {} # coupling -> max_order 644 self.decays = [] # ProcessInfo of the decays 645 self.tag = '' # tag of the process 646 self.s_forbid = [] # list of particles forbids in s channel 647 self.forbid = [] # list of particles forbids 648 self.line = line # initialization line 649 650 self.is_mg5_valid = False 651 #some shortcut 652 self.separate_particle = ProcCardv4Reader.separate_particle
653
654 - def analyze_process(self, particles_name):
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 #extract the tag 663 if '@' in line: 664 split = line.split('@') 665 line = split[0] 666 self.tag = split[1] 667 668 669 # check if we have a MG5 format 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 # extract (S-)forbidden particle 679 pos_forbid = line.find('/') 680 pos_sforbid = line.find('$') 681 682 # Select the restrictions (pos is -1 if not defined) 683 #and remove the restrictions from the line 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 # Same but if they are no S-forbidden particles 698 elif pos_forbid != -1: 699 self.forbid = self.separate_particle(line[pos_forbid + 1:], \ 700 particles_name) 701 line = line[:pos_forbid] 702 # Same but if they are no forbidden particles 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 # Deal with decay chains, returns lines whitout the decay (and treat 709 #the different decays. 710 if '(' in line: 711 line = self.treat_decay_chain(line, particles_name) 712 713 #define the level of each particle 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
721 - def treat_decay_chain(self, line, particles_name):
722 """Split the information of the decays into a tree of ProcessInfo.""" 723 724 level = 0 #depth of the decay chain 725 out_line = '' # core process 726 for character in line: 727 if character == '(': 728 level += 1 729 if level == 1: 730 decay_line = "" # initialize a new decay info 731 else: 732 decay_line += '(' 733 continue 734 elif character == ')': 735 level -= 1 736 if level == 0: #store the information 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
751 - def add_coupling(self, line):
752 """Add the coupling information to the process""" 753 data = line.split('=') 754 self.couplings[data[0]] = int(data[1])
755 756
757 - def add_restrictions(self, forbid, s_forbid, couplings):
758 """Associate some restriction to this diagram""" 759 760 self.forbid = forbid 761 self.s_forbid = s_forbid 762 self.couplings = couplings
763
764 - def mg5_process_line(self, model_coupling):
765 """Return a valid mg5 format for this process """ 766 767 if self.is_mg5_valid: 768 return self.line 769 770 text = '' 771 # Write the process 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 # Write the constraints 780 if self.s_forbid: 781 text += '$ ' + ' '.join(self.s_forbid) + ' ' 782 if self.forbid: 783 text += '/ ' + ' '.join(self.forbid) + ' ' 784 785 #treat decay_chains 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 # write the tag 794 if self.tag: 795 text += '@%s ' % self.tag 796 797 if self.couplings: 798 if not self.tag: 799 text += '@0 ' 800 #write the rules associate to the couplings 801 text += self.mg5_couplings_line(model_coupling, len(self.particles)) 802 803 return text.rstrip()
804
805 - def mg5_couplings_line(self, model_coupling, nb_part):
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 # Need coupling for all cases, since might be decay chain 812 out += '%s=%s ' % (coupling, self.couplings[coupling]) 813 else: 814 # if not define put to zero (mg4 default) 815 out += '%s=0 ' % coupling 816 817 return out
818