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