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 761 if self.old_new: 762 pattern = re.compile(r'\b(%s)\b' % '|'.join(list(self.old_new.keys()))) 763 def replace(matchobj): 764 return self.old_new[matchobj.group(0)]
765 coupling.value = pattern.sub(replace, coupling.value) 766 767 old_coupling = next((p for p in self.couplings if p.value==coupling.value), None) 768 769 if old_coupling: 770 coupling.replace = old_coupling #tag for replacement 771 else: 772 self.couplings.append(coupling) 773
774 - def add_coupling_order(self, coupling_order):
775 """adding a new coupling order inside the model""" 776 777 name = coupling_order.name 778 same_name = next((p for p in self.orders if p.name==name), None) 779 if same_name: 780 if coupling_order.hierarchy != same_name.hierarchy: 781 logger.warning('%s has different hierarchy use the minimal value (%s, %s) => %s' \ 782 % (name, same_name.hierarchy, coupling_order.hierarchy, 783 min(same_name.hierarchy, coupling_order.hierarchy))) 784 same_name.hierarchy = min(same_name.hierarchy, coupling_order.hierarchy) 785 if coupling_order.expansion_order != same_name.expansion_order: 786 logger.warning('%s has different expansion_order use the minimal value (%s, %s) => %s' \ 787 % (name, coupling_order.expansion_order, same_name.expansion_order, 788 min(same_name.expansion_order, coupling_order.expansion_order))) 789 same_name.expansion_order = min(same_name.expansion_order, coupling_order.expansion_order) 790 if hasattr(same_name, 'perturbative_expansion') and same_name.perturbative_expansion: 791 logger.info('%s will be forbidden to run at NLO' % same_name.name) 792 same_name.perturbative_expansion = 0 793 794 795 else: 796 self.orders.append(coupling_order)
797
798 - def add_lorentz(self, lorentz):
799 """add one coupling""" 800 801 # avoid name duplication 802 name = lorentz.name 803 same_name = next((p for p in self.lorentz if p.name==name), None) 804 if same_name: 805 lorentz.name = '%s%s' % (lorentz.name, self.addon) 806 807 if self.old_new: 808 pattern = re.compile(r'\b(%s)\b' % '|'.join(list(self.old_new.keys()))) 809 def replace(matchobj): 810 return self.old_new[matchobj.group(0)]
811 lorentz.structure = pattern.sub(replace, lorentz.structure) 812 813 old_lor = next((p for p in self.lorentz 814 if p.structure==lorentz.structure and p.spins == lorentz.spins), 815 None) 816 817 if old_lor: 818 lorentz.replace = old_lor #tag for replacement 819 else: 820 self.lorentz.append(lorentz) 821
822 - def add_interaction(self, interaction , model):
823 """Add one interaction to the model. This is UNCONDITIONAL! 824 if the same interaction is in the model this means that the interaction 825 will appear twice. This is now weaken if both interaction are exactly identical! 826 (EXACT same color/lorentz/coupling expression) 827 """ 828 829 interaction = interaction.__class__(**interaction.__dict__) 830 model.all_vertices.pop(-1) 831 832 #0. check name: 833 name = interaction.name 834 same_name = next((p for p in self.vertices if p.name==name), None) 835 if same_name: 836 interaction.name = '%s%s' % (interaction.name, self.addon) 837 838 #1. check particles translation 839 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles] 840 interaction.particles = particles 841 #2. check the lorentz structure 842 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz] 843 interaction.lorentz = lorentz 844 845 #3. check the couplings 846 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c) 847 for key, c in interaction.couplings.items()] 848 interaction.couplings = dict(couplings) 849 850 #4. Try to avoid duplication of interaction: 851 # A crash is raised if the same particles have already the some lorentz structure 852 # at the same coupling order: 853 get_pdg = lambda vertex: sorted([p.pdg_code for p in vertex.particles]) 854 id_part = get_pdg(interaction) 855 iden_vertex = [v for v in self.vertices if get_pdg(v) == id_part] 856 iden = False 857 nb_coupling = len(interaction.couplings) 858 keys = list(interaction.couplings.keys()) # to have a fixed order! 859 860 get_lor_and_color = lambda i: (interaction.lorentz[keys[i][1]].structure, 861 interaction.color[keys[i][0]]) 862 for v in iden_vertex: 863 if len(v.couplings) != nb_coupling: 864 continue 865 found = [] 866 for ((i,j), coup) in v.couplings.items(): 867 new_lorentz = v.lorentz[j].structure 868 new_color = v.color[i] 869 k=0 870 same = [k for k in range(nb_coupling) if k not in found and 871 get_lor_and_color(k) == (new_lorentz, new_color)] 872 if not same: 873 break 874 else: 875 for k in same: 876 if interaction.couplings[keys[k]] == coup: 877 found.append(k) 878 break 879 else: 880 # check only the coupling order 881 for k in same: 882 if interaction.couplings[keys[k]].order == coup.order: 883 found.append(k) 884 warning = """Did NOT add interaction %s since same particles/lorentz/color/coupling order 885 BUT did not manage to ensure that the coupling is the same. couplings expression: 886 base model: %s 887 addon model: %s 888 """ % (id_part, coup.value, interaction.couplings[keys[k]].value) 889 logger.warning(warning) 890 found.append(k) 891 break 892 else: 893 pass 894 # mat 895 else: 896 # all found one identical... 897 return 898 899 logger.info('Adding interaction for the following particles: %s' % id_part) 900 901 902 903 904 self.vertices.append(interaction)
905
906 - def add_CTinteraction(self, interaction):
907 """Add one interaction to the model. This is UNCONDITIONAL! 908 if the same interaction is in the model this means that the interaction 909 will appear twice.""" 910 911 #0. check name: 912 name = interaction.name 913 same_name = next((p for p in self.vertices if p.name==name), None) 914 if same_name: 915 interaction.name = '%s%s' % (interaction.name, self.addon) 916 917 #1. check particles translation 918 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles] 919 interaction.particles = particles 920 921 #2. check the lorentz structure 922 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz] 923 interaction.lorentz = lorentz 924 925 #3. check the couplings 926 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c) 927 for key, c in interaction.couplings.items()] 928 interaction.couplings = dict(couplings) 929 930 931 #4. check the loop_particles 932 loop_particles=[ [p.replace if hasattr(p, 'replace') else p for p in plist] 933 for plist in interaction.loop_particles] 934 interaction.loop_particles = loop_particles 935 self.CTvertices.append(interaction)
936 937
938 - def add_model(self, model=None, path=None, identify_particles=None):
939 """add another model in the current one""" 940 941 942 self.new_external = [] 943 if path: 944 model = ufomodels.load_model(path) 945 946 if not model: 947 raise USRMODERROR('Need a valid Model') 948 else: 949 path = model.__path__[0] 950 # Check the validity of the model. Too old UFO (before UFO 1.0) 951 if not hasattr(model, 'all_orders'): 952 raise USRMODERROR('Add-on Model doesn\'t follows UFO convention (no couplings_order information)\n' +\ 953 'MG5 is able to load such model but NOT to the add model feature.') 954 if isinstance(model.all_particles[0].mass, six.string_types): 955 raise USRMODERROR('Add-on Model doesn\'t follows UFO convention (Mass/Width of particles are string name, not object)\n' +\ 956 'MG5 is able to load such model but NOT to the add model feature.') 957 958 for order in model.all_orders: 959 if hasattr(order, 'perturbative_expansion') and order.perturbative_expansion: 960 raise USRMODERROR('Add-on model can not be loop model.') 961 962 for order in model.all_orders: 963 self.add_coupling_order(order) 964 965 # Adding automatically identification for anti-particle if needed 966 # + define identify_pid which keep tracks of the pdg_code identified 967 identify_pid = {} 968 if identify_particles: 969 for new, old in identify_particles.items(): 970 new_part = next((p for p in model.all_particles if p.name==new), None) 971 old_part = next((p for p in self.particles if p.name==old), None) 972 # secure agqinst lower/upper case problem 973 if not new_part: 974 first = True 975 for p in model.all_particles: 976 if p.name.lower() == new.lower(): 977 if not first: 978 raise Exception 979 else: 980 first =False 981 new_part = p 982 if not old_part: 983 first = True 984 for p in self.particles: 985 if p.name.lower() == old.lower(): 986 if not first: 987 raise Exception 988 else: 989 first =False 990 old_part = p 991 if not old_part: 992 # last possibility is that the model do not follow MG5 convention 993 # but that "old" does 994 defaultname = base_objects.Model.load_default_name() # id->name 995 for pdg, value in defaultname.items(): 996 if value == old: 997 old_part = self.particle_dict[pdg] 998 identify_particles[new] = old_part.name 999 break 1000 1001 # end for the case security 1002 identify_pid[new_part.pdg_code] = old_part.pdg_code 1003 if new_part is None: 1004 raise USRMODERROR("particle %s not in added model" % new) 1005 if old_part is None: 1006 raise USRMODERROR("particle %s not in original model" % old) 1007 if new_part.antiname not in identify_particles: 1008 new_anti = new_part.antiname 1009 old_anti = old_part.antiname 1010 if old_anti == old: 1011 raise USRMODERROR("failed identification (one particle is self-conjugate and not the other)") 1012 logger.info("adding identification for anti-particle: %s=%s" % (new_anti, old_anti)) 1013 identify_particles[new_anti] = old_anti 1014 1015 for parameter in model.all_parameters: 1016 self.add_parameter(parameter, identify_pid) 1017 for coupling in model.all_couplings: 1018 self.add_coupling(coupling) 1019 for lorentz in model.all_lorentz: 1020 self.add_lorentz(lorentz) 1021 for particle in model.all_particles: 1022 if particle.name in identify_particles: 1023 self.add_particle(particle, identify=identify_particles[particle.name]) 1024 else: 1025 self.add_particle(particle) 1026 for vertex in model.all_vertices: 1027 self.add_interaction(vertex, model) 1028 1029 self.all_path.append(path) 1030 1031 1032 return
1033 1034 # def add_particle_from_model(self, model, name): 1035 # """add the particles NAME from model model (either path or object) 1036 # names can be either the name of one particle or a list of particle name 1037 # """ 1038 # 1039 # if isinstance(model, basestring): 1040 # model = UFOModel(self.modelpath) 1041 # 1042 # 1043 # if isinstance(name, list): 1044 # [self.add_particles(self.modelpath, name) for name in names] 1045 # return 1046 # 1047 # # Check Validity 1048 # part = self.get_particle(name) 1049 # if self.particles_dict.has_key(part.pdg_code): 1050 # raise USRMODERROR, 'The model contains already a particle with pdg_code %s.' % part.pdg_code 1051 # 1052 # # Add the particles to model 1053 # self.particles.append(part) 1054 # self.particles_dict[part.pdg_code] = part 1055 # 1056 # # Loop over the interactions of the other model and add (if possible) the interactions 1057 # #associated to the new particles 1058 # possibility = [v for v in vertex if part in v.particles] 1059 # 1060 # for vertex in possibility: 1061 # # Check that all particles are define in the model 1062 # for particles in vertex.particles: 1063 # if particles.pdg_code not in self.particles_dict: 1064 # continue 1065 # # Add the interactions/lorentz structure/coupling 1066 # self.vertices.append(vertex) 1067 # # NEED WORK!!!!! 1068