Package models :: Module usermod
[hide private]
[frames] | no frames]

Source Code for Module models.usermod

  1  ################################################################################ 
  2  # 
  3  # Copyright (c) 2011 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  """ Set of Tool in order to modify a given UFO model. 
 16      (mainly by adding-suppressing interactions and allow to modify by text the  
 17      different part of the model. Check of consistency of the model are performed. 
 18      This produce a new valid UFO model in output. 
 19  """ 
 20   
 21  import glob 
 22  import logging 
 23  import os 
 24  import re 
 25  import sys 
 26   
 27  import madgraph.iolibs.files as files 
 28  import madgraph.various.misc as misc 
 29  import models as ufomodels 
 30  import models.import_ufo as import_ufo 
 31  import models.check_param_card as check_param_card 
 32   
 33  pjoin =os.path.join 
 34  logger = logging.getLogger('madgraph.model') 
 35   
36 -class USRMODERROR(Exception): pass
37
38 -class UFOModel(object):
39 """ The class storing the current status of the model """ 40
41 - def __init__(self, modelpath, addon='__1'):
42 """load the model from a valid UFO directory (otherwise keep everything 43 as empty.""" 44 45 self.modelpath = modelpath 46 model = ufomodels.load_model(modelpath) 47 48 # Check the validity of the model. Too old UFO (before UFO 1.0) 49 if not hasattr(model, 'all_orders'): 50 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention (no couplings_order information)\n' +\ 51 'MG5 is able to load such model but NOT to the add model feature.' 52 if isinstance(model.all_particles[0].mass, basestring): 53 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention (Mass/Width of particles are string name, not object)\n' +\ 54 'MG5 is able to load such model but NOT to the add model feature.' 55 56 57 self.particles = model.all_particles 58 if any(hasattr(p, 'loop_particles') for p in self.particles): 59 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention ' 60 self.vertices = model.all_vertices 61 self.couplings = model.all_couplings 62 self.lorentz = model.all_lorentz 63 self.parameters = model.all_parameters 64 self.Parameter = self.parameters[0].__class__ 65 self.orders = model.all_orders 66 67 self.functions = model.all_functions 68 self.new_external = [] 69 # UFO optional file 70 if hasattr(model, 'all_propagators'): 71 self.propagators = model.all_propagators 72 else: 73 self.propagators = [] 74 75 # UFO NLO extension 76 if hasattr(model, 'all_CTvertices'): 77 self.CTvertices = model.all_CTvertices 78 else: 79 self.CTvertices = [] 80 81 #translate for how to write the python file 82 if 'self.expr = expression' in open(pjoin(self.modelpath, 'object_library.py')).read(): 83 self.translate = {'expr': 'expression'} 84 else: 85 self.translate = {} 86 87 #translate for the expression of the UFO model 88 self.old_new = {} 89 self.addon = addon 90 91 # particle id -> object 92 self.particle_dict = {} 93 for particle in self.particles: 94 self.particle_dict[particle.pdg_code] = particle 95 96 # path to all model that should be used for the Fortran file. 97 self.all_path = [self.modelpath]
98
99 - def write(self, outputdir):
100 """ """ 101 if not os.path.exists(outputdir): 102 os.mkdir(outputdir) 103 files.cp(os.path.join(self.modelpath, '__init__.py'), outputdir) 104 files.cp(os.path.join(self.modelpath, 'object_library.py'), outputdir) 105 files.cp(os.path.join(self.modelpath, 'write_param_card.py'), outputdir) 106 107 self.write_particles(outputdir) 108 self.write_vertices(outputdir) 109 self.write_couplings(outputdir) 110 self.write_lorentz(outputdir) 111 self.write_parameters(outputdir) 112 self.write_orders(outputdir) 113 self.write_functions(outputdir) 114 self.write_propagators(outputdir) 115 self.write_ctvertices(outputdir) 116 117 self.write_external_files(outputdir) 118 self.write_restrict_card(outputdir)
119 120
121 - def write_restrict_card(self, outputdir):
122 """ propagate model restriction of the original model. """ 123 124 restrict_list = [l for l in os.listdir(self.modelpath) if l.startswith('restrict_')] 125 if not self.new_external: 126 # no new entry in the card => just copy the restrict_card.dat 127 for p in restrict_list: 128 files.cp(pjoin(self.modelpath, p), outputdir) 129 130 else: 131 # need to add the parameter and ensure that they will not be restricted! 132 for p in restrict_list: 133 param_card = check_param_card.ParamCard(pjoin(self.modelpath, p)) 134 for parameter in self.new_external: 135 block = parameter.lhablock 136 lhaid = parameter.lhacode 137 value = parameter.value 138 if value == 0: 139 value = 1e-99 140 elif value == 1: 141 value = 9.999999e-1 142 try: 143 param_card.add_param(block.lower(), lhaid, value, 'from addon') 144 except check_param_card.InvalidParamCard: 145 logger.warning("%s will not acting for %s %s" % (p, block, lhaid)) 146 param_card[block.lower()].get(lhaid).value = value 147 # all added -> write it 148 param_card.write(pjoin(outputdir, p))
149 150 151 152 153 154 155 156
157 - def format_param(self, param):
158 """convert param to string in order to have it written correctly for the 159 UFO file""" 160 161 if isinstance(param, basestring): 162 return "'%s'" % param.replace('\'', '\\\'').replace('\"', '\\\"') 163 elif isinstance(param, int) or isinstance(param, float) or \ 164 isinstance(param, complex): 165 return "%s" % param 166 elif isinstance(param, list): 167 return '[%s]' % ', '.join(self.format_param(p) for p in param) 168 elif isinstance(param, tuple): 169 if len(param) == 1: 170 return '(%s,)' % self.format_param(param[0]) 171 else: 172 return '(%s)' % ','.join([self.format_param(p) for p in param]) 173 elif isinstance(param, dict): 174 return '{%s}' % ','.join(['%s: %s' % (self.format_param(key), self.format_param(value)) for key, value in param.items()]) 175 elif param.__class__.__name__ == 'Parameter': 176 return 'Param.%s' % param.__repr__() 177 elif param.__class__.__name__ == 'Coupling': 178 return 'C.%s' % param.__repr__() 179 elif param.__class__.__name__ == 'Lorentz': 180 return 'L.%s' % param.__repr__() 181 elif param.__class__.__name__ == 'Particle': 182 return 'P.%s' % param.__repr__() 183 elif param is None: 184 return 'None' 185 else: 186 raise Exception, '%s unknow type for writting UFO' % param.__class__.__name__
187 188 189
190 - def create_data_text(self, obj):
191 """ create the data associate to the object""" 192 # Most of the object comes from the UFOBASECLASS 193 # BUT NOT ALL (some object) need to deal with both 194 195 nb_space = 0 196 if hasattr(obj, 'require_args_all'): 197 args = obj.require_args_all 198 elif hasattr(obj, 'require_args'): 199 args = obj.require_args 200 else: 201 args = [] 202 if args: 203 text = """%s = %s(""" % (obj.__repr__(), obj.__class__.__name__) 204 else: 205 text = """%s = %s(""" % (obj.name, obj.__class__.__name__) 206 207 208 for data in args: 209 if data in self.translate: 210 data = self.translate[data] 211 if not nb_space: 212 add_space = len(text) 213 else: 214 add_space = 0 215 216 try: 217 expr = getattr(obj, data) 218 except: 219 if data in ['counterterm', 'propagator', 'loop_particles']: 220 expr = None 221 setattr(obj, data, None) 222 else: 223 raise 224 name =str(data) 225 if name in self.translate: 226 name = self.translate[name] 227 #if data == 'lhablock': 228 # print data, type(self.format_param(getattr(obj, data))) 229 text += '%s%s = %s,\n' % (' ' * nb_space,name, self.format_param(getattr(obj, data))) 230 nb_space += add_space 231 232 if hasattr(obj, 'get_all'): 233 other_attr = [name for name in obj.get_all().keys() 234 if name not in args] 235 else: 236 other_attr = obj.__dict__.keys() 237 238 for data in other_attr: 239 name =str(data) 240 if name in ['partial_widths', 'loop_particles']: 241 continue 242 if name in self.translate: 243 name = self.translate[name] 244 if not nb_space: 245 add_space = len(text) 246 else: 247 add_space = 0 248 text += '%s%s = %s,\n' % (' ' * nb_space, name, self.format_param(getattr(obj, data))) 249 nb_space += add_space 250 251 text = text[:-2] + ')\n\n' 252 253 return text
254
255 - def create_file_content(self, datalist):
256 """ """ 257 return '\n'.join([self.create_data_text(obj) for obj in datalist])
258 259
260 - def write_particles(self, outputdir):
261 """ """ 262 text = """ 263 # This file was automatically created by The UFO_usermod 264 265 from __future__ import division 266 from object_library import all_particles, Particle 267 import parameters as Param 268 269 """ 270 text += self.create_file_content(self.particles) 271 ff = open(os.path.join(outputdir, 'particles.py'), 'w') 272 ff.writelines(text) 273 ff.close() 274 return
275
276 - def write_vertices(self, outputdir):
277 """ """ 278 text = """ 279 # This file was automatically created by The UFO_usermod 280 281 from object_library import all_vertices, Vertex 282 import particles as P 283 import couplings as C 284 import lorentz as L 285 286 """ 287 text += self.create_file_content(self.vertices) 288 ff = open(os.path.join(outputdir, 'vertices.py'), 'w') 289 ff.writelines(text) 290 ff.close() 291 return
292
293 - def write_ctvertices(self, outputdir):
294 """ """ 295 296 if not self.CTvertices: 297 return 298 299 text = """ 300 # This file was automatically created by The UFO_usermod 301 302 from object_library import all_vertices, all_CTvertices, Vertex, CTVertex 303 import particles as P 304 import couplings as C 305 import lorentz as L 306 307 """ 308 text += self.create_file_content(self.CTvertices) 309 ff = open(os.path.join(outputdir, 'CT_vertices.py'), 'w') 310 ff.writelines(text) 311 ff.close() 312 return
313 314
315 - def write_couplings(self, outputdir):
316 """ """ 317 text = """ 318 # This file was automatically created by The UFO_usermod 319 320 from object_library import all_couplings, Coupling 321 """ 322 text += self.create_file_content(self.couplings) 323 ff = open(os.path.join(outputdir, 'couplings.py'), 'w') 324 ff.writelines(text) 325 ff.close() 326 return
327
328 - def write_lorentz(self, outputdir):
329 """ """ 330 text = """ 331 # This file was automatically created by The UFO_usermod 332 333 from object_library import all_lorentz, Lorentz 334 """ 335 336 text += self.create_file_content(self.lorentz) 337 ff = open(os.path.join(outputdir, 'lorentz.py'), 'w') 338 ff.writelines(text) 339 ff.close() 340 return
341
342 - def write_parameters(self, outputdir):
343 """ """ 344 text = """ 345 # This file was automatically created by The UFO_usermod 346 347 from object_library import all_parameters, Parameter 348 """ 349 350 text += self.create_file_content(self.parameters) 351 ff = open(os.path.join(outputdir, 'parameters.py'), 'w') 352 ff.writelines(text) 353 ff.close() 354 return
355
356 - def write_orders(self, outputdir):
357 """ """ 358 text = """ 359 # This file was automatically created by The UFO_usermod 360 361 from object_library import all_orders, CouplingOrder 362 """ 363 364 text += self.create_file_content(self.orders) 365 ff = open(os.path.join(outputdir, 'coupling_orders.py'), 'w') 366 ff.writelines(text) 367 ff.close() 368 return
369
370 - def write_functions(self, outputdir):
371 """ """ 372 text = """ 373 # This file was automatically created by The UFO_usermod 374 375 import cmath 376 from object_library import all_functions, Function 377 378 """ 379 380 text += self.create_file_content(self.functions) 381 ff = open(os.path.join(outputdir, 'function_library.py'), 'w') 382 ff.writelines(text) 383 ff.close() 384 return
385
386 - def write_propagators(self, outputdir):
387 """ """ 388 389 text = """ 390 # This file was automatically created by The UFO_usermod 391 from object_library import all_propagators, Propagator 392 """ 393 394 text += self.create_file_content(self.propagators) 395 ff = open(os.path.join(outputdir, 'propagators.py'), 'w') 396 ff.writelines(text) 397 ff.close() 398 return
399
400 - def write_external_files(self, outputdir):
401 """Copy/merge the routines written in Fortran/C++/pyhton""" 402 403 #1. Special case for the formfactor written in Fortran 404 re_fct = re.compile('''^\s{7,70}[\w\s]*function (\w*)\(''',re.M+re.I) 405 present_fct = set() 406 for dirpath in self.all_path: 407 if os.path.exists(pjoin(dirpath, 'Fortran', 'functions.f')): 408 text = open(pjoin(dirpath, 'Fortran', 'functions.f')).read() 409 new_fct = re_fct.findall(text) 410 nb_old = len(present_fct) 411 nb_added = len(new_fct) 412 new_fct = set([f.lower() for f in new_fct]) 413 present_fct.update(new_fct) 414 if len(present_fct) < nb_old + nb_added: 415 logger.critical('''Some Functions in functions.f are define in more than one model. 416 This require AT LEAST manual modification of the resulting file. But more likely the 417 model need to be consider as un-physical! Use it very carefully.''') 418 419 if not os.path.exists(pjoin(outputdir, 'Fortran')): 420 os.mkdir(pjoin(outputdir, 'Fortran')) 421 fsock = open(pjoin(outputdir, 'Fortran','functions.f'),'a') 422 fsock.write(text) 423 fsock.close() 424 425 #2. Ohter files present in Fortran/Cpp/Python directory 426 # ASk user to handle it if any! 427 for dirpath in self.all_path: 428 for subdir in ['Fortran', 'CPP', 'Python']: 429 if os.path.exists(pjoin(dirpath, subdir)): 430 for filepath in os.listdir(pjoin(dirpath, subdir)): 431 if filepath == 'functions.f': 432 continue 433 if '.' not in filepath: 434 continue 435 logger.warning('Manual HELAS routine associated to the model. Those are not modified automaticaly!! So you need to manually checked them') 436 nb = 0 437 name, extension = filepath.rsplit('.', 1) 438 439 while 1: 440 filename = '%s%s%s' %(name, '.moved' * nb, extension) 441 if os.path.exists(pjoin(outputdir, subdir, filename)): 442 nb+=1 443 else: 444 break 445 if not os.path.exists(pjoin(outputdir, subdir)): 446 os.mkdir(pjoin(outputdir, subdir)) 447 files.cp(pjoin(dirpath, subdir, filepath), pjoin(outputdir, subdir, filename))
448
449 - def get_particle(self, name):
450 """ """ 451 for part in self.particles: 452 if part.name == name: 453 return part 454 455 raise USRMODERROR, 'no particle %s in the model' % name
456
457 - def add_parameter(self, parameter, identify_pid={}):
458 """wrapper to call the correct function""" 459 460 if parameter.nature == 'internal': 461 self.add_internal_parameter(parameter) 462 else: 463 self.add_external_parameter(parameter, identify_pid)
464
465 - def add_particle(self, particle, identify=None):
466 """Add a particle in a consistent way""" 467 468 name = particle.name 469 if identify: 470 name = identify 471 old_part = next((p for p in self.particles if p.name==name), None) 472 if old_part: 473 #Check if the two particles have the same pdgcode 474 if old_part.pdg_code == particle.pdg_code: 475 particle.replace = old_part 476 return self.check_mass_width_of_particle(old_part, particle) 477 elif identify: 478 if particle.spin != old_part.spin: 479 raise USRMODERROR, "identify particles should have the same spin" 480 elif particle.color != old_part.color: 481 raise USRMODERROR, "identify particles should have the same color" 482 483 particle.pdg_code = old_part.pdg_code 484 particle.replace = old_part 485 return self.check_mass_width_of_particle(old_part, particle) 486 else: 487 logger.warning('The particle name \'%s\' is present in both model with different pdg code' % name) 488 logger.warning('The particle coming from the plug-in model will be rename to \'%s%s\'' % (name, self.addon)) 489 particle.name = '%s%s' % (name, self.addon) 490 self.particles.append(particle) 491 return 492 elif identify: 493 raise USRMODERROR, "Particle %s is not in the model" % identify 494 495 pdg = particle.pdg_code 496 if pdg in self.particle_dict: 497 particle.replace = self.particle_dict[pdg] 498 return self.check_mass_width_of_particle(self.particle_dict[pdg], particle) 499 else: 500 self.particles.append(particle)
501 502
503 - def check_mass_width_of_particle(self, p_base, p_plugin):
504 505 # Check the mass 506 if p_base.mass.name != p_plugin.mass.name: 507 #different name but actually the same 508 if p_plugin.mass.name in self.old_new: 509 if self.old_new[p_plugin.mass.name] != p_base.mass.name: 510 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model: equivalent of %s is %s != %s ' % ( p_plugin.mass.name, self.old_new[p_plugin.mass.name], p_base.mass.name) 511 elif p_base.mass.name.lower() == 'zero': 512 p_base.mass = p_plugin.mass 513 elif p_plugin.mass.name.lower() == 'zero': 514 pass 515 else: 516 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model\n' + \ 517 ' Mass: %s and %s\n' %(p_base.mass.name, p_plugin.mass.name) + \ 518 ' conflict name %s\n' % self.old_new + \ 519 ' pdg_code: %s %s' % (p_base.pdg_code, p_plugin.pdg_code) 520 # Check the width 521 if p_base.width.name != p_plugin.width.name: 522 #different name but actually the same 523 if p_plugin.width.name in self.old_new: 524 if self.old_new[p_plugin.width.name] != p_base.width.name: 525 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model' 526 elif p_base.width.name.lower() == 'zero': 527 p_base.width = p_plugin.width 528 elif p_plugin.width.name.lower() == 'zero': 529 pass 530 else: 531 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model' 532 533 return
534
535 - def add_external_parameter(self, parameter, identify_pid):
536 """adding a param_card parameter inside the current model. 537 if the parameter block/lhcode already exists then just do nothing 538 (but if the name are different then keep the info for future translation) 539 If the name already exists in the model. raise an exception. 540 """ 541 542 name = parameter.name 543 # check if a parameter already has this name 544 old_param = next((p for p in self.parameters if p.name==name), None) 545 if old_param: 546 if old_param.lhablock == parameter.lhablock and \ 547 old_param.lhacode == parameter.lhacode: 548 return #Nothing to do! 549 else: 550 logger.info('The two model defines the parameter \'%s\'\n' % parameter.name + 551 ' the original model for %s :%s\n' %(old_param.lhablock, old_param.lhacode)+ 552 ' the plugin for %s :%s\n' %(parameter.lhablock,parameter.lhacode)+ 553 ' We will rename the one from the plugin to %s%s' % (parameter.name, self.addon)) 554 if old_param.nature == 'internal': 555 logger.warning('''The parameter %s is actually an internal parameter of the base model. 556 his value is given by %s. 557 If those two parameters are expected to be identical, you need to provide the value in the param_card according to this formula. 558 ''') 559 #add the parameter with a new name. 560 self.old_new[parameter.name] = '%s%s' % (parameter.name, self.addon) 561 parameter.name = '%s%s' % (parameter.name, self.addon) 562 # 563 #self.parameters.append(parameter) 564 #return 565 #check if a parameter already has this lhablock/code information 566 lhacode = parameter.lhacode 567 if parameter.lhablock.lower() in ['mass', 'decay']: 568 if int(parameter.lhacode[0]) in identify_pid: 569 lhacode = [identify_pid[int(parameter.lhacode[0])]] 570 571 old_param = next((p for p in self.parameters if p.lhacode==lhacode \ 572 and p.lhablock==parameter.lhablock), None) 573 if old_param: 574 logger.info('The two model defines the block \'%s\' with id \'%s\' with different parameter name \'%s\', \'%s\'\n'\ 575 % (old_param.lhablock, old_param.lhacode, parameter.name, old_param.name) + \ 576 ' We will merge those two parameters in a single one') 577 if parameter.name in self.old_new.values(): 578 key = [k for k in self.old_new if self.old_new[k] == parameter.name][0] 579 self.old_new[key] = old_param.name 580 self.old_new[parameter.name] = old_param.name 581 else: 582 self.old_new[parameter.name] = old_param.name 583 # self.add_internal_parameter(iden_param) 584 585 else: 586 #Just add the new parameter to the current list 587 self.parameters.append(parameter) 588 self.new_external.append(parameter)
589
590 - def add_internal_parameter(self, parameter):
591 """ add a parameter of type internal """ 592 593 name = parameter.name 594 # check if a parameter already has this name 595 old_param = next((p for p in self.parameters if p.name==name), None) 596 if old_param: 597 if old_param.value == parameter.value: 598 return #Nothing to do! 599 else: 600 if self.old_new: 601 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys())) 602 def replace(matchobj): 603 return self.old_new[matchobj.group(0)]
604 parameter.value = pattern.sub(replace, parameter.value) 605 self.old_new[parameter.name] = '%s%s' % (parameter.name, self.addon) 606 607 parameter.name = '%s%s' % (parameter.name, self.addon) 608 self.parameters.append(parameter) 609 return 610 611 # No name conflict: 612 if self.old_new: 613 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys())) 614 def replace(matchobj): 615 return self.old_new[matchobj.group(0)]
616 parameter.value = pattern.sub(replace, parameter.value) 617 618 self.parameters.append(parameter) 619 620 621 622
623 - def add_coupling(self, coupling):
624 """add one coupling""" 625 626 # avoid name duplication 627 name = coupling.name 628 same_name = next((p for p in self.couplings if p.name==name), None) 629 if same_name: 630 coupling.name = '%s%s' % (coupling.name, self.addon) 631 632 if self.old_new: 633 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys())) 634 def replace(matchobj): 635 return self.old_new[matchobj.group(0)]
636 coupling.value = pattern.sub(replace, coupling.value) 637 638 old_coupling = next((p for p in self.couplings if p.value==coupling.value), None) 639 640 if old_coupling: 641 coupling.replace = old_coupling #tag for replacement 642 else: 643 self.couplings.append(coupling) 644
645 - def add_coupling_order(self, coupling_order):
646 """adding a new coupling order inside the model""" 647 648 name = coupling_order.name 649 same_name = next((p for p in self.orders if p.name==name), None) 650 if same_name: 651 if coupling_order.hierarchy != same_name.hierarchy: 652 logger.warning('%s has different hierarchy use the minimal value (%s, %s) => %s' \ 653 % (name, same_name.hierarchy, coupling_order.hierarchy, 654 min(same_name.hierarchy, coupling_order.hierarchy))) 655 same_name.hierarchy = min(same_name.hierarchy, coupling_order.hierarchy) 656 if coupling_order.expansion_order != same_name.expansion_order: 657 logger.warning('%s has different expansion_order use the minimal value (%s, %s) => %s' \ 658 % (name, coupling_order.expansion_order, same_name.expansion_order, 659 min(same_name.expansion_order, coupling_order.expansion_order))) 660 same_name.expansion_order = min(same_name.expansion_order, coupling_order.expansion_order) 661 if hasattr(same_name, 'perturbative_expansion') and same_name.perturbative_expansion: 662 logger.info('%s will be forbidden to run at NLO' % same_name.name) 663 same_name.perturbative_expansion = 0 664 665 666 else: 667 self.orders.append(coupling_order)
668
669 - def add_lorentz(self, lorentz):
670 """add one coupling""" 671 672 # avoid name duplication 673 name = lorentz.name 674 same_name = next((p for p in self.lorentz if p.name==name), None) 675 if same_name: 676 lorentz.name = '%s%s' % (lorentz.name, self.addon) 677 678 if self.old_new: 679 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys())) 680 def replace(matchobj): 681 return self.old_new[matchobj.group(0)]
682 lorentz.structure = pattern.sub(replace, lorentz.structure) 683 684 old_lor = next((p for p in self.lorentz 685 if p.structure==lorentz.structure and p.spins == lorentz.spins), 686 None) 687 688 if old_lor: 689 lorentz.replace = old_lor #tag for replacement 690 else: 691 self.lorentz.append(lorentz) 692
693 - def add_interaction(self, interaction):
694 """Add one interaction to the model. This is UNCONDITIONAL! 695 if the same interaction is in the model this means that the interaction 696 will appear twice. This is now weaken if both interaction are exactly identical! 697 (EXACT same color/lorentz/coupling expression) 698 """ 699 700 #0. check name: 701 name = interaction.name 702 same_name = next((p for p in self.vertices if p.name==name), None) 703 if same_name: 704 interaction.name = '%s%s' % (interaction.name, self.addon) 705 706 #1. check particles translation 707 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles] 708 interaction.particles = particles 709 710 #2. check the lorentz structure 711 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz] 712 interaction.lorentz = lorentz 713 714 #3. check the couplings 715 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c) 716 for key, c in interaction.couplings.items()] 717 interaction.couplings = dict(couplings) 718 719 #4. Try to avoid duplication of interaction: 720 # A crash is raised if the same particles have already the some lorentz structure 721 # at the same coupling order: 722 get_pdg = lambda vertex: sorted([p.pdg_code for p in vertex.particles]) 723 id_part = get_pdg(interaction) 724 iden_vertex = [v for v in self.vertices if get_pdg(v) == id_part] 725 iden = False 726 nb_coupling = len(interaction.couplings) 727 keys = interaction.couplings.keys() # to have a fixed order! 728 729 get_lor_and_color = lambda i: (interaction.lorentz[keys[i][1]].structure, 730 interaction.color[keys[i][0]]) 731 for v in iden_vertex: 732 if len(v.couplings) != nb_coupling: 733 continue 734 found = [] 735 for ((i,j), coup) in v.couplings.items(): 736 new_lorentz = v.lorentz[j].structure 737 new_color = v.color[i] 738 k=0 739 same = [k for k in range(nb_coupling) if k not in found and 740 get_lor_and_color(k) == (new_lorentz, new_color)] 741 if not same: 742 break 743 else: 744 for k in same: 745 if interaction.couplings[keys[k]] == coup: 746 found.append(k) 747 break 748 else: 749 # check only the coupling order 750 for k in same: 751 if interaction.couplings[keys[k]].order == coup.order: 752 found.append(k) 753 warning = """Did NOT add interaction %s since same particles/lorentz/color/coupling order 754 BUT did not manage to ensure that the coupling is the same. couplings expression: 755 base model: %s 756 addon model: %s 757 """ % (id_part, coup.value, interaction.couplings[keys[k]].value) 758 logger.warning(warning) 759 found.append(k) 760 break 761 else: 762 pass 763 # mat 764 else: 765 # all found one identical... 766 return 767 768 logger.info('Adding interaction for the following particles: %s' % id_part) 769 770 771 772 773 self.vertices.append(interaction)
774
775 - def add_CTinteraction(self, interaction):
776 """Add one interaction to the model. This is UNCONDITIONAL! 777 if the same interaction is in the model this means that the interaction 778 will appear twice.""" 779 780 #0. check name: 781 name = interaction.name 782 same_name = next((p for p in self.vertices if p.name==name), None) 783 if same_name: 784 interaction.name = '%s%s' % (interaction.name, self.addon) 785 786 #1. check particles translation 787 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles] 788 interaction.particles = particles 789 790 #2. check the lorentz structure 791 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz] 792 interaction.lorentz = lorentz 793 794 #3. check the couplings 795 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c) 796 for key, c in interaction.couplings.items()] 797 interaction.couplings = dict(couplings) 798 799 800 #4. check the loop_particles 801 loop_particles=[ [p.replace if hasattr(p, 'replace') else p for p in plist] 802 for plist in interaction.loop_particles] 803 interaction.loop_particles = loop_particles 804 self.CTvertices.append(interaction)
805 806
807 - def add_model(self, model=None, path=None, identify_particles=None):
808 """add another model in the current one""" 809 810 self.new_external = [] 811 if path: 812 model = ufomodels.load_model(path) 813 814 if not model: 815 raise USRMODERROR, 'Need a valid Model' 816 else: 817 path = model.__path__[0] 818 # Check the validity of the model. Too old UFO (before UFO 1.0) 819 if not hasattr(model, 'all_orders'): 820 raise USRMODERROR, 'Add-on Model doesn\'t follows UFO convention (no couplings_order information)\n' +\ 821 'MG5 is able to load such model but NOT to the add model feature.' 822 if isinstance(model.all_particles[0].mass, basestring): 823 raise USRMODERROR, 'Add-on Model doesn\'t follows UFO convention (Mass/Width of particles are string name, not object)\n' +\ 824 'MG5 is able to load such model but NOT to the add model feature.' 825 826 for order in model.all_orders: 827 if hasattr(order, 'perturbative_expansion') and order.perturbative_expansion: 828 raise USRMODERROR, 'Add-on model can not be loop model.' 829 830 for order in model.all_orders: 831 self.add_coupling_order(order) 832 833 # Adding automatically identification for anti-particle if needed 834 # + define identify_pid which keep tracks of the pdg_code identified 835 if identify_particles: 836 identify_pid = {} 837 for new, old in identify_particles.items(): 838 new_part = next((p for p in model.all_particles if p.name==new), None) 839 old_part = next((p for p in self.particles if p.name==old), None) 840 identify_pid[new_part.pdg_code] = old_part.pdg_code 841 842 if new_part is None: 843 raise USRMODERROR, "particle %s not in added model" % new 844 if old_part is None: 845 raise USRMODERROR, "particle %s not in original model" % old 846 if new_part.antiname not in identify_particles: 847 new_anti = new_part.antiname 848 old_anti = old_part.antiname 849 misc.sprint(old, new, new_anti, old_anti, old_part.antiname) 850 if old_anti == old: 851 raise USRMODERROR, "failed identification (one particle is self-conjugate and not the other)" 852 logger.info("adding identification for anti-particle: %s=%s" % (new_anti, old_anti)) 853 identify_particles[new_anti] = old_anti 854 855 for parameter in model.all_parameters: 856 self.add_parameter(parameter, identify_pid) 857 for coupling in model.all_couplings: 858 self.add_coupling(coupling) 859 for lorentz in model.all_lorentz: 860 self.add_lorentz(lorentz) 861 for particle in model.all_particles: 862 if particle.name in identify_particles: 863 self.add_particle(particle, identify=identify_particles[particle.name]) 864 else: 865 self.add_particle(particle) 866 for vertex in model.all_vertices: 867 self.add_interaction(vertex) 868 869 self.all_path.append(path) 870 871 872 return
873 874 # def add_particle_from_model(self, model, name): 875 # """add the particles NAME from model model (either path or object) 876 # names can be either the name of one particle or a list of particle name 877 # """ 878 # 879 # if isinstance(model, basestring): 880 # model = UFOModel(self.modelpath) 881 # 882 # 883 # if isinstance(name, list): 884 # [self.add_particles(self.modelpath, name) for name in names] 885 # return 886 # 887 # # Check Validity 888 # part = self.get_particle(name) 889 # if self.particles_dict.has_key(part.pdg_code): 890 # raise USRMODERROR, 'The model contains already a particle with pdg_code %s.' % part.pdg_code 891 # 892 # # Add the particles to model 893 # self.particles.append(part) 894 # self.particles_dict[part.pdg_code] = part 895 # 896 # # Loop over the interactions of the other model and add (if possible) the interactions 897 # #associated to the new particles 898 # possibility = [v for v in vertex if part in v.particles] 899 # 900 # for vertex in possibility: 901 # # Check that all particles are define in the model 902 # for particles in vertex.particles: 903 # if particles.pdg_code not in self.particles_dict: 904 # continue 905 # # Add the interactions/lorentz structure/coupling 906 # self.vertices.append(vertex) 907 # # NEED WORK!!!!! 908