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

Source Code for Module models.import_ufo

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15  """ How to import a UFO model to the MG5 format """ 
  16   
  17   
  18  import fractions 
  19  import logging 
  20  import os 
  21  import re 
  22  import sys 
  23  import time 
  24  import collections 
  25   
  26   
  27  from madgraph import MadGraph5Error, MG5DIR, ReadWrite 
  28  import madgraph.core.base_objects as base_objects 
  29  import madgraph.loop.loop_base_objects as loop_base_objects 
  30  import madgraph.core.color_algebra as color 
  31  import madgraph.iolibs.files as files 
  32  import madgraph.iolibs.save_load_object as save_load_object 
  33  from madgraph.core.color_algebra import * 
  34  import madgraph.various.misc as misc 
  35  import madgraph.iolibs.ufo_expression_parsers as parsers 
  36   
  37  import aloha 
  38  import aloha.create_aloha as create_aloha 
  39  import aloha.aloha_fct as aloha_fct 
  40   
  41  import models as ufomodels 
  42  import models.model_reader as model_reader 
  43  logger = logging.getLogger('madgraph.model') 
  44  logger_mod = logging.getLogger('madgraph.model') 
  45   
  46  root_path = os.path.dirname(os.path.realpath( __file__ )) 
  47  sys.path.append(root_path) 
  48   
  49  sys.path.append(os.path.join(root_path, os.path.pardir, 'Template', 'bin', 'internal')) 
  50  import check_param_card  
  51   
  52  pjoin = os.path.join 
  53   
  54  # Suffixes to employ for the various poles of CTparameters 
  55  pole_dict = {-2:'2EPS',-1:'1EPS',0:'FIN'} 
56 57 -class UFOImportError(MadGraph5Error):
58 """ a error class for wrong import of UFO model"""
59
60 -class InvalidModel(MadGraph5Error):
61 """ a class for invalid Model """
62 63 last_model_path =''
64 -def find_ufo_path(model_name, web_search=True):
65 """ find the path to a model """ 66 67 global last_model_path 68 69 # Check for a valid directory 70 if model_name.startswith(('./','../')) and os.path.isdir(model_name): 71 return model_name 72 elif os.path.isdir(os.path.join(MG5DIR, 'models', model_name)): 73 return os.path.join(MG5DIR, 'models', model_name) 74 elif 'PYTHONPATH' in os.environ: 75 for p in os.environ['PYTHONPATH'].split(':'): 76 if os.path.isdir(os.path.join(MG5DIR, p, model_name)): 77 if last_model_path != os.path.join(MG5DIR, p, model_name): 78 logger.info("model loaded from PYTHONPATH: %s", os.path.join(MG5DIR, p, model_name)) 79 last_model_path = os.path.join(MG5DIR, p, model_name) 80 return os.path.join(MG5DIR, p, model_name) 81 if os.path.isdir(model_name): 82 logger.warning('No model %s found in default path. Did you mean \'import model ./%s\'', 83 model_name, model_name) 84 if os.path.sep in model_name: 85 raise UFOImportError("Path %s is not a valid pathname" % model_name) 86 elif web_search and '-' not in model_name: 87 found = import_model_from_db(model_name) 88 if found: 89 return find_ufo_path(model_name, web_search=False) 90 else: 91 raise UFOImportError("Path %s is not a valid pathname" % model_name) 92 else: 93 raise UFOImportError("Path %s is not a valid pathname" % model_name) 94 95 raise UFOImportError("Path %s is not a valid pathname" % model_name) 96 return
97
98 99 -def get_model_db():
100 """return the file with the online model database""" 101 102 data_path = ['http://madgraph.phys.ucl.ac.be/models_db.dat', 103 'http://madgraph.physics.illinois.edu/models_db.dat'] 104 import random 105 import urllib 106 r = random.randint(0,1) 107 r = [r, (1-r)] 108 109 for index in r: 110 cluster_path = data_path[index] 111 try: 112 data = urllib.urlopen(cluster_path) 113 except Exception: 114 continue 115 break 116 else: 117 raise MadGraph5Error, '''Model not found locally and Impossible to connect any of us servers. 118 Please check your internet connection or retry later''' 119 return data
120
121 -def import_model_from_db(model_name, local_dir=False):
122 """ import the model with a given name """ 123 124 data =get_model_db() 125 link = None 126 for line in data: 127 split = line.split() 128 if model_name == split[0]: 129 link = split[1] 130 break 131 else: 132 logger.debug('no model with that name found online') 133 return False 134 135 #get target directory 136 # 1. PYTHONPATH containing UFO 137 # 2. models directory 138 target = None 139 if 'PYTHONPATH' in os.environ and not local_dir: 140 for directory in os.environ['PYTHONPATH'].split(':'): 141 if 'UFO' in os.path.basename(directory) and os.path.exists(directory): 142 target= directory 143 if target is None: 144 target = pjoin(MG5DIR, 'models') 145 try: 146 os.remove(pjoin(target, 'tmp.tgz')) 147 except Exception: 148 pass 149 logger.info("download model from %s to the following directory: %s", link, target, '$MG:color:BLACK') 150 misc.wget(link, 'tmp.tgz', cwd=target) 151 152 #untar the file. 153 # .tgz 154 if link.endswith(('.tgz','.tar.gz','.tar')): 155 try: 156 proc = misc.call('tar -xzpvf tmp.tgz', shell=True, cwd=target)#, stdout=devnull, stderr=devnull) 157 if proc: raise Exception 158 except: 159 proc = misc.call('tar -xpvf tmp.tgz', shell=True, cwd=target)#, stdout=devnull, stderr=devnull) 160 # .zip 161 if link.endswith(('.zip')): 162 try: 163 proc = misc.call('unzip tmp.tgz', shell=True, cwd=target)#, stdout=devnull, stderr=devnull) 164 if proc: raise Exception 165 except: 166 proc = misc.call('tar -xzpvf tmp.tgz', shell=True, cwd=target)#, stdout=devnull, stderr=devnull) 167 if proc: 168 raise Exception, "Impossible to unpack the model. Please install it manually" 169 return True
170
171 -def import_model(model_name, decay=False, restrict=True, prefix='mdl_', 172 complex_mass_scheme = None):
173 """ a practical and efficient way to import a model""" 174 175 # check if this is a valid path or if this include restriction file 176 try: 177 model_path = find_ufo_path(model_name) 178 except UFOImportError: 179 if '-' not in model_name: 180 if model_name == "mssm": 181 logger.error("mssm model has been replaced by MSSM_SLHA2 model.\n The new model require SLHA2 format. You can use the \"update to_slha2\" command to convert your slha1 param_card.\n That option is available at the time of the edition of the cards.") 182 raise 183 split = model_name.split('-') 184 model_name = '-'.join([text for text in split[:-1]]) 185 try: 186 model_path = find_ufo_path(model_name) 187 except UFOImportError: 188 if model_name == "mssm": 189 logger.error("mssm model has been replaced by MSSM_SLHA2 model.\n The new model require SLHA2 format. You can use the \"update to_slha2\" command to convert your slha1 param_card.\n That option is available at the time of the edition of the cards.") 190 raise 191 restrict_name = split[-1] 192 193 restrict_file = os.path.join(model_path, 'restrict_%s.dat'% restrict_name) 194 195 #if restriction is full, then we by pass restriction (avoid default) 196 if split[-1] == 'full': 197 restrict_file = None 198 else: 199 # Check if by default we need some restrictions 200 restrict_name = "" 201 if restrict and os.path.exists(os.path.join(model_path,'restrict_default.dat')): 202 restrict_file = os.path.join(model_path,'restrict_default.dat') 203 else: 204 restrict_file = None 205 206 if isinstance(restrict, str): 207 if os.path.exists(os.path.join(model_path, restrict)): 208 restrict_file = os.path.join(model_path, restrict) 209 elif os.path.exists(restrict): 210 restrict_file = restrict 211 else: 212 raise Exception, "%s is not a valid path for restrict file" % restrict 213 214 #import the FULL model 215 model = import_full_model(model_path, decay, prefix) 216 217 if os.path.exists(pjoin(model_path, "README")): 218 logger.info("Please read carefully the README of the model file for instructions/restrictions of the model.",'$MG:color:BLACK') 219 # restore the model name 220 if restrict_name: 221 model["name"] += '-' + restrict_name 222 223 # Decide whether complex mass scheme is on or not 224 useCMS = (complex_mass_scheme is None and aloha.complex_mass) or \ 225 complex_mass_scheme==True 226 #restrict it if needed 227 if restrict_file: 228 try: 229 logger.info('Restrict model %s with file %s .' % (model_name, os.path.relpath(restrict_file))) 230 except OSError: 231 # sometimes has trouble with relative path 232 logger.info('Restrict model %s with file %s .' % (model_name, restrict_file)) 233 234 if logger_mod.getEffectiveLevel() > 10: 235 logger.info('Run \"set stdout_level DEBUG\" before import for more information.') 236 # Modify the mother class of the object in order to allow restriction 237 model = RestrictModel(model) 238 239 # Change to complex mass scheme if necessary. This must be done BEFORE 240 # the restriction. 241 if useCMS: 242 # We read the param_card a first time so that the function 243 # change_mass_to_complex_scheme can know if a particle is to 244 # be considered massive or not and with zero width or not. 245 # So we read the restrict card a first time, with the CMS set to 246 # False because we haven't changed the model yet. 247 model.set_parameters_and_couplings(param_card = restrict_file, 248 complex_mass_scheme=False) 249 model.change_mass_to_complex_scheme(toCMS=True) 250 else: 251 # Make sure that the parameter 'CMSParam' of the model is set to 0.0 252 # as it should in order to have the correct NWA renormalization condition. 253 # It might be that the default of the model is CMS. 254 model.change_mass_to_complex_scheme(toCMS=False) 255 256 blocks = model.get_param_block() 257 if model_name == 'mssm' or os.path.basename(model_name) == 'mssm': 258 keep_external=True 259 elif all( b in blocks for b in ['USQMIX', 'SL2', 'MSOFT', 'YE', 'NMIX', 'TU','MSE2','UPMNS']): 260 keep_external=True 261 elif model_name == 'MSSM_SLHA2' or os.path.basename(model_name) == 'MSSM_SLHA2': 262 keep_external=True 263 else: 264 keep_external=False 265 if keep_external: 266 logger.info('Detect SLHA2 format. keeping restricted parameter in the param_card') 267 268 model.restrict_model(restrict_file, rm_parameter=not decay, 269 keep_external=keep_external, complex_mass_scheme=complex_mass_scheme) 270 model.path = model_path 271 else: 272 # Change to complex mass scheme if necessary 273 if useCMS: 274 model.change_mass_to_complex_scheme(toCMS=True) 275 else: 276 # It might be that the default of the model (i.e. 'CMSParam') is CMS. 277 model.change_mass_to_complex_scheme(toCMS=False) 278 279 return model
280 281 282 _import_once = []
283 -def import_full_model(model_path, decay=False, prefix=''):
284 """ a practical and efficient way to import one of those models 285 (no restriction file use)""" 286 287 assert model_path == find_ufo_path(model_path) 288 289 if prefix is True: 290 prefix='mdl_' 291 292 # Check the validity of the model 293 files_list_prov = ['couplings.py','lorentz.py','parameters.py', 294 'particles.py', 'vertices.py', 'function_library.py', 295 'propagators.py', 'coupling_orders.py'] 296 297 if decay: 298 files_list_prov.append('decays.py') 299 300 files_list = [] 301 for filename in files_list_prov: 302 filepath = os.path.join(model_path, filename) 303 if not os.path.isfile(filepath): 304 if filename not in ['propagators.py', 'decays.py', 'coupling_orders.py']: 305 raise UFOImportError, "%s directory is not a valid UFO model: \n %s is missing" % \ 306 (model_path, filename) 307 files_list.append(filepath) 308 # use pickle files if defined and up-to-date 309 if aloha.unitary_gauge: 310 pickle_name = 'model.pkl' 311 else: 312 pickle_name = 'model_Feynman.pkl' 313 if decay: 314 pickle_name = 'dec_%s' % pickle_name 315 316 allow_reload = False 317 if files.is_uptodate(os.path.join(model_path, pickle_name), files_list): 318 allow_reload = True 319 try: 320 model = save_load_object.load_from_file( \ 321 os.path.join(model_path, pickle_name)) 322 except Exception, error: 323 logger.info('failed to load model from pickle file. Try importing UFO from File') 324 else: 325 # We don't care about the restrict_card for this comparison 326 if model.has_key('version_tag') and not model.get('version_tag') is None and \ 327 model.get('version_tag').startswith(os.path.realpath(model_path)) and \ 328 model.get('version_tag').endswith('##' + str(misc.get_pkg_info())): 329 #check if the prefix is correct one. 330 for key in model.get('parameters'): 331 for param in model['parameters'][key]: 332 value = param.name.lower() 333 if value in ['as','mu_r', 'zero','aewm1']: 334 continue 335 if prefix: 336 if value.startswith(prefix): 337 _import_once.append((model_path, aloha.unitary_gauge, prefix, decay)) 338 return model 339 else: 340 logger.info('reload from .py file') 341 break 342 else: 343 if value.startswith('mdl_'): 344 logger.info('reload from .py file') 345 break 346 else: 347 _import_once.append((model_path, aloha.unitary_gauge, prefix, decay)) 348 return model 349 else: 350 continue 351 break 352 else: 353 logger.info('reload from .py file') 354 355 if (model_path, aloha.unitary_gauge, prefix, decay) in _import_once and not allow_reload: 356 raise MadGraph5Error, 'This model %s is modified on disk. To reload it you need to quit/relaunch MG5_aMC ' % model_path 357 358 # Load basic information 359 ufo_model = ufomodels.load_model(model_path, decay) 360 ufo2mg5_converter = UFOMG5Converter(ufo_model) 361 model = ufo2mg5_converter.load_model() 362 if model_path[-1] == '/': model_path = model_path[:-1] #avoid empty name 363 model.set('name', os.path.split(model_path)[-1]) 364 365 # Load the Parameter/Coupling in a convenient format. 366 parameters, couplings = OrganizeModelExpression(ufo_model).main(\ 367 additional_couplings =(ufo2mg5_converter.wavefunction_CT_couplings 368 if ufo2mg5_converter.perturbation_couplings else [])) 369 370 model.set('parameters', parameters) 371 model.set('couplings', couplings) 372 model.set('functions', ufo_model.all_functions) 373 374 # Optional UFO part: decay_width information 375 376 377 if decay and hasattr(ufo_model, 'all_decays') and ufo_model.all_decays: 378 start = time.time() 379 for ufo_part in ufo_model.all_particles: 380 name = ufo_part.name 381 if not model['case_sensitive']: 382 name = name.lower() 383 p = model['particles'].find_name(name) 384 if hasattr(ufo_part, 'partial_widths'): 385 p.partial_widths = ufo_part.partial_widths 386 elif p and not hasattr(p, 'partial_widths'): 387 p.partial_widths = {} 388 # might be None for ghost 389 logger.debug("load width takes %s", time.time()-start) 390 391 if prefix: 392 start = time.time() 393 model.change_parameter_name_with_prefix() 394 logger.debug("model prefixing takes %s", time.time()-start) 395 396 path = os.path.dirname(os.path.realpath(model_path)) 397 path = os.path.join(path, model.get('name')) 398 model.set('version_tag', os.path.realpath(path) +'##'+ str(misc.get_pkg_info())) 399 400 # save in a pickle files to fasten future usage 401 if ReadWrite: 402 save_load_object.save_to_file(os.path.join(model_path, pickle_name), 403 model, log=False) 404 405 #if default and os.path.exists(os.path.join(model_path, 'restrict_default.dat')): 406 # restrict_file = os.path.join(model_path, 'restrict_default.dat') 407 # model = import_ufo.RestrictModel(model) 408 # model.restrict_model(restrict_file) 409 410 return model
411
412 -class UFOMG5Converter(object):
413 """Convert a UFO model to the MG5 format""" 414
415 - def __init__(self, model, auto=False):
416 """ initialize empty list for particles/interactions """ 417 418 if hasattr(model, '__arxiv__'): 419 logger.info('Please cite %s when using this model', model.__arxiv__, '$MG:color:BLACK') 420 421 self.particles = base_objects.ParticleList() 422 self.interactions = base_objects.InteractionList() 423 self.wavefunction_CT_couplings = [] 424 425 # Check here if we can extract the couplings perturbed in this model 426 # which indicate a loop model or if this model is only meant for 427 # tree-level computations 428 self.perturbation_couplings = {} 429 try: 430 for order in model.all_orders: 431 if(order.perturbative_expansion>0): 432 self.perturbation_couplings[order.name]=order.perturbative_expansion 433 except AttributeError,error: 434 pass 435 436 if self.perturbation_couplings!={}: 437 self.model = loop_base_objects.LoopModel({'perturbation_couplings':\ 438 self.perturbation_couplings.keys()}) 439 else: 440 self.model = base_objects.Model() 441 self.model.set('particles', self.particles) 442 self.model.set('interactions', self.interactions) 443 self.conservecharge = set(['charge']) 444 445 self.ufomodel = model 446 self.checked_lor = set() 447 448 if auto: 449 self.load_model()
450
451 - def load_model(self):
452 """load the different of the model first particles then interactions""" 453 454 # Check the validity of the model 455 # 1) check that all lhablock are single word. 456 def_name = [] 457 for param in self.ufomodel.all_parameters: 458 if param.nature == "external": 459 if len(param.lhablock.split())>1: 460 raise InvalidModel, '''LHABlock should be single word which is not the case for 461 \'%s\' parameter with lhablock \'%s\' ''' % (param.name, param.lhablock) 462 if param.name in def_name: 463 raise InvalidModel, "name %s define multiple time. Please correct the UFO model!" \ 464 % (param.name) 465 else: 466 def_name.append(param.name) 467 468 # For each CTParameter, check that there is no name conflict with the 469 # set of re-defined CTParameters with EPS and FIN suffixes. 470 if hasattr(self.ufomodel,'all_CTparameters'): 471 for CTparam in self.ufomodel.all_CTparameters: 472 for pole in pole_dict: 473 if CTparam.pole(pole)!='ZERO': 474 new_param_name = '%s_%s_'%(CTparam.name,pole_dict[pole]) 475 if new_param_name in def_name: 476 raise InvalidModel, "CT name %s"% (new_param_name)+\ 477 " the model. Please change its name." 478 479 if hasattr(self.ufomodel, 'gauge'): 480 self.model.set('gauge', self.ufomodel.gauge) 481 logger.info('load particles') 482 # Check if multiple particles have the same name but different case. 483 # Otherwise, we can use lowercase particle names. 484 if len(set([p.name for p in self.ufomodel.all_particles] + \ 485 [p.antiname for p in self.ufomodel.all_particles])) == \ 486 len(set([p.name.lower() for p in self.ufomodel.all_particles] + \ 487 [p.antiname.lower() for p in self.ufomodel.all_particles])): 488 self.model['case_sensitive'] = False 489 490 491 # check which of the fermion/anti-fermion should be set as incoming 492 self.detect_incoming_fermion() 493 494 for particle_info in self.ufomodel.all_particles: 495 self.add_particle(particle_info) 496 497 # Find which particles is in the 3/3bar color states (retrun {id: 3/-3}) 498 color_info = self.find_color_anti_color_rep() 499 500 # load the lorentz structure. 501 self.model.set('lorentz', list(self.ufomodel.all_lorentz)) 502 503 # Substitute the expression of CT couplings which include CTparameters 504 # in their definition with the corresponding dictionaries, e.g. 505 # CTCoupling.value = 2*CTParam -> 506 # CTCoupling.value = {-1: 2*CTParam_1EPS_, 0: 2*CTParam_FIN_} 507 # for example if CTParam had a non-zero finite and single pole. 508 # This change affects directly the UFO model and it will be reverted in 509 # OrganizeModelExpression only, so that the main() function of this class 510 # *must* be run on the UFO to have this change reverted. 511 if hasattr(self.ufomodel,'all_CTparameters'): 512 logger.debug('Handling couplings defined with CTparameters...') 513 start_treat_coupling = time.time() 514 self.treat_couplings(self.ufomodel.all_couplings, 515 self.ufomodel.all_CTparameters) 516 tot_time = time.time()-start_treat_coupling 517 if tot_time>5.0: 518 logger.debug('... done in %s'%misc.format_time(tot_time)) 519 520 logger.info('load vertices') 521 for interaction_info in self.ufomodel.all_vertices: 522 self.add_interaction(interaction_info, color_info) 523 524 if self.perturbation_couplings: 525 try: 526 self.ufomodel.add_NLO() 527 except Exception, error: 528 pass 529 530 for interaction_info in self.ufomodel.all_CTvertices: 531 self.add_CTinteraction(interaction_info, color_info) 532 533 534 for interaction in self.interactions: 535 self.optimise_interaction(interaction) 536 537 538 self.model.set('conserved_charge', self.conservecharge) 539 540 # If we deal with a Loop model here, the order hierarchy MUST be 541 # defined in the file coupling_orders.py and we import it from 542 # there. 543 all_orders = [] 544 try: 545 all_orders = self.ufomodel.all_orders 546 except AttributeError: 547 if self.perturbation_couplings: 548 raise MadGraph5Error, "The loop model MG5 attemps to import does not specify the attribute 'all_order'." 549 else: 550 pass 551 552 hierarchy={} 553 try: 554 for order in all_orders: 555 hierarchy[order.name]=order.hierarchy 556 except AttributeError: 557 if self.perturbation_couplings: 558 raise MadGraph5Error, 'The loop model MG5 attemps to import does not specify an order hierarchy.' 559 else: 560 pass 561 else: 562 self.model.set('order_hierarchy', hierarchy) 563 564 # Also set expansion_order, i.e., maximum coupling order per process 565 expansion_order={} 566 # And finally the UVCT coupling order counterterms 567 coupling_order_counterterms={} 568 try: 569 for order in all_orders: 570 expansion_order[order.name]=order.expansion_order 571 coupling_order_counterterms[order.name]=order.expansion_order 572 except AttributeError: 573 if self.perturbation_couplings: 574 raise MadGraph5Error, 'The loop model MG5 attemps to import does not specify an expansion_order for all coupling orders.' 575 else: 576 pass 577 else: 578 self.model.set('expansion_order', expansion_order) 579 self.model.set('expansion_order', expansion_order) 580 581 #clean memory 582 del self.checked_lor 583 584 return self.model
585
586 - def optimise_interaction(self, interaction):
587 588 # we want to check if the same coupling is used for two lorentz strucutre 589 # for the same color structure. 590 to_lor = {} 591 for (color, lor), coup in interaction['couplings'].items(): 592 key = (color, coup) 593 if key in to_lor: 594 to_lor[key].append(lor) 595 else: 596 to_lor[key] = [lor] 597 598 nb_reduce = [] 599 optimize = False 600 for key in to_lor: 601 if len(to_lor[key]) >1: 602 nb_reduce.append(len(to_lor[key])-1) 603 optimize = True 604 605 if not optimize: 606 return 607 608 if not hasattr(self, 'defined_lorentz_expr'): 609 self.defined_lorentz_expr = {} 610 self.lorentz_info = {} 611 self.lorentz_combine = {} 612 for lor in self.model['lorentz']: 613 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name') 614 self.lorentz_info[lor.get('name')] = lor #(lor.get('structure'), lor.get('spins')) 615 616 for key in to_lor: 617 if len(to_lor[key]) == 1: 618 continue 619 names = [interaction['lorentz'][i] for i in to_lor[key]] 620 names.sort() 621 622 # get name of the new lorentz 623 if tuple(names) in self.lorentz_combine: 624 # already created new loretnz 625 new_name = self.lorentz_combine[tuple(names)] 626 else: 627 new_name = self.add_merge_lorentz(names) 628 629 # remove the old couplings 630 color, coup = key 631 to_remove = [(color, lor) for lor in to_lor[key]] 632 for rm in to_remove: 633 del interaction['couplings'][rm] 634 635 #add the lorentz structure to the interaction 636 if new_name not in [l for l in interaction.get('lorentz')]: 637 interaction.get('lorentz').append(new_name) 638 639 #find the associate index 640 new_l = interaction.get('lorentz').index(new_name) 641 # adding the new combination (color,lor) associate to this sum of structure 642 interaction['couplings'][(color, new_l)] = coup
643 644
645 - def add_merge_lorentz(self, names):
646 """add a lorentz structure which is the sume of the list given above""" 647 648 649 #create new_name 650 ii = len(names[0]) 651 while ii>0: 652 if not all(n.startswith(names[0][:ii]) for n in names[1:]): 653 ii -=1 654 else: 655 base_name = names[0][:ii] 656 break 657 else: 658 base_name = 'LMER' 659 660 i = 1 661 while '%s%s' %(base_name, i) in self.lorentz_info: 662 i +=1 663 new_name = '%s%s' %(base_name, i) 664 self.lorentz_combine[tuple(names)] = new_name 665 assert new_name not in self.lorentz_info 666 assert new_name not in [l.name for l in self.model['lorentz']] 667 668 # load the associate lorentz expression 669 new_struct = ' + '.join([self.lorentz_info[n].get('structure') for n in names]) 670 spins = self.lorentz_info[names[0]].get('spins') 671 new_lor = self.add_lorentz(new_name, spins, new_struct) 672 self.lorentz_info[new_name] = new_lor 673 674 return new_name
675 676 # We also have to create the new lorentz 677 678 679 680 681
682 - def add_particle(self, particle_info):
683 """ convert and add a particle in the particle list """ 684 685 loop_particles = [[[]]] 686 counterterms = {} 687 688 # MG5 have only one entry for particle and anti particles. 689 #UFO has two. use the color to avoid duplictions 690 pdg = particle_info.pdg_code 691 if pdg in self.incoming or (pdg not in self.outcoming and pdg <0): 692 return 693 694 # MG5 doesn't use ghost for tree models: physical sum on the polarization 695 if not self.perturbation_couplings and particle_info.spin < 0: 696 return 697 698 if (aloha.unitary_gauge and 0 in self.model['gauge']) \ 699 or (1 not in self.model['gauge']): 700 701 # MG5 doesn't use goldstone boson 702 if hasattr(particle_info, 'GoldstoneBoson') and particle_info.GoldstoneBoson: 703 return 704 elif hasattr(particle_info, 'goldstone') and particle_info.goldstone: 705 return 706 # Initialize a particles 707 particle = base_objects.Particle() 708 709 # MG5 doesn't use goldstone boson 710 if (hasattr(particle_info, 'GoldstoneBoson') and particle_info.GoldstoneBoson) \ 711 or (hasattr(particle_info, 'goldstoneboson') and particle_info.goldstoneboson): 712 particle.set('type', 'goldstone') 713 elif hasattr(particle_info, 'goldstone') and particle_info.goldstone: 714 particle.set('type', 'goldstone') 715 716 nb_property = 0 #basic check that the UFO information is complete 717 # Loop over the element defining the UFO particles 718 for key,value in particle_info.__dict__.items(): 719 # Check if we use it in the MG5 definition of a particles 720 if key in base_objects.Particle.sorted_keys and not key=='counterterm': 721 nb_property +=1 722 if key in ['name', 'antiname']: 723 if not self.model['case_sensitive']: 724 particle.set(key, value.lower()) 725 else: 726 particle.set(key, value) 727 elif key == 'charge': 728 particle.set(key, float(value)) 729 elif key in ['mass','width']: 730 particle.set(key, str(value)) 731 elif key == 'spin': 732 # MG5 internally treats ghost with positive spin for loop models and 733 # ignore them otherwise 734 particle.set(key,abs(value)) 735 if value<0: 736 particle.set('type','ghost') 737 elif key == 'propagating': 738 if not value: 739 particle.set('line', None) 740 elif key == 'line': 741 if particle.get('line') is None: 742 pass # This means that propagating is on False 743 else: 744 particle.set('line', value) 745 elif key == 'propagator': 746 if value: 747 if aloha.unitary_gauge: 748 particle.set(key, str(value[0])) 749 else: 750 particle.set(key, str(value[1])) 751 else: 752 particle.set(key, '') 753 else: 754 particle.set(key, value) 755 elif key == 'loop_particles': 756 loop_particles = value 757 elif key == 'counterterm': 758 counterterms = value 759 elif key.lower() not in ('ghostnumber','selfconjugate','goldstone', 760 'goldstoneboson','partial_widths', 761 'texname', 'antitexname', 'propagating', 'ghost' 762 ): 763 # add charge -we will check later if those are conserve 764 self.conservecharge.add(key) 765 particle.set(key,value, force=True) 766 767 if not hasattr(particle_info, 'propagator'): 768 nb_property += 1 769 if particle.get('spin') >= 3: 770 if particle.get('mass').lower() == 'zero': 771 particle.set('propagator', 0) 772 elif particle.get('spin') == 3 and not aloha.unitary_gauge: 773 particle.set('propagator', 0) 774 775 assert(10 == nb_property) #basic check that all the information is there 776 777 # Identify self conjugate particles 778 if particle_info.name == particle_info.antiname: 779 particle.set('self_antipart', True) 780 781 # Proceed only if we deal with a loop model and that this particle 782 # has wavefunction renormalization 783 if not self.perturbation_couplings or counterterms=={}: 784 self.particles.append(particle) 785 return 786 787 # Set here the 'counterterm' attribute to the particle. 788 # First we must change the couplings dictionary keys from the entry format 789 # (order1,order2,...,orderN,loop_particle#):LaurentSerie 790 # two a dictionary with format 791 # ('ORDER_OF_COUNTERTERM',((Particle_list_PDG))):{laurent_order:CTCouplingName} 792 particle_counterterms = {} 793 for key, counterterm in counterterms.items(): 794 # Makes sure this counterterm contributes at one-loop. 795 if len([1 for k in key[:-1] if k==1])==1 and \ 796 not any(k>1 for k in key[:-1]): 797 newParticleCountertermKey=[None,\ 798 # The line below is for loop UFO Model with the 'attribute' 799 # 'loop_particles' of the Particle objects to be defined with 800 # instances of the particle class. The new convention is to use 801 # pdg numbers instead. 802 # tuple([tuple([abs(part.pdg_code) for part in loop_parts]) for\ 803 tuple([tuple(loop_parts) for\ 804 loop_parts in loop_particles[key[-1]]])] 805 for i, order in enumerate(self.ufomodel.all_orders[:-1]): 806 if key[i]==1: 807 newParticleCountertermKey[0]=order.name 808 newCouplingName='UVWfct_'+particle_info.name+'_'+str(key[-1]) 809 particle_counterterms[tuple(newParticleCountertermKey)]=\ 810 dict([(key,newCouplingName+('' if key==0 else '_'+str(-key)+'eps'))\ 811 for key in counterterm]) 812 # We want to create the new coupling for this wavefunction 813 # renormalization. 814 self.ufomodel.object_library.Coupling(\ 815 name = newCouplingName, 816 value = counterterm, 817 order = {newParticleCountertermKey[0]:2}) 818 self.wavefunction_CT_couplings.append(self.ufomodel.all_couplings.pop()) 819 820 particle.set('counterterm',particle_counterterms) 821 self.particles.append(particle) 822 return
823
824 - def treat_couplings(self, couplings, all_CTparameters):
825 """ This function scan each coupling to see if it contains a CT parameter. 826 when it does, it changes its value to a dictionary with the CT parameter 827 changed to a new parameter for each pole and finite part. For instance, 828 the following coupling: 829 coupling.value = '2*(myCTParam1 + myParam*(myCTParam2 + myCTParam3)' 830 with CTparameters 831 myCTParam1 = {0: Something, -1: SomethingElse} 832 myCTParam2 = {0: OtherSomething } 833 myCTParam3 = {-1: YetOtherSomething } 834 would be turned into 835 coupling.value = {0: '2*(myCTParam1_FIN_ + myParam*(myCTParam2_FIN_ + ZERO)' 836 -1: '2*(myCTParam1_EPS_ + myParam*(ZERO + myCTParam2_EPS_)'} 837 838 all_CTParameter is the list of all CTParameters in the model""" 839 840 # First define a list of regular expressions for each CT parameter 841 # and put them in a dictionary whose keys are the CT parameter names 842 # and the values are a tuple with the substituting patter in the first 843 # entry and the list of substituting functions (one for each pole) 844 # as the second entry of this tuple. 845 CTparameter_patterns = {} 846 zero_substitution = lambda matchedObj: matchedObj.group('first')+\ 847 'ZERO'+matchedObj.group('second') 848 def function_factory(arg): 849 return lambda matchedObj: \ 850 matchedObj.group('first')+arg+matchedObj.group('second')
851 for CTparam in all_CTparameters: 852 pattern_finder = re.compile(r"(?P<first>\A|\*|\+|\-|\(|\s)(?P<name>"+ 853 CTparam.name+r")(?P<second>\Z|\*|\+|\-|\)|/|\\|\s)") 854 855 sub_functions = [None if CTparam.pole(pole)=='ZERO' else 856 function_factory('%s_%s_'%(CTparam.name,pole_dict[-pole])) 857 for pole in range(3)] 858 CTparameter_patterns[CTparam.name] = (pattern_finder,sub_functions) 859 860 times_zero = re.compile('\*\s*-?ZERO') 861 zero_times = re.compile('ZERO\s*(\*|\/)') 862 def is_expr_zero(expresson): 863 """ Checks whether a single term (involving only the operations 864 * or / is zero. """ 865 for term in expresson.split('-'): 866 for t in term.split('+'): 867 t = t.strip() 868 if t in ['ZERO','']: 869 continue 870 if not (times_zero.search(t) or zero_times.search(t)): 871 return False 872 return True
873 874 def find_parenthesis(expr): 875 end = expr.find(')') 876 if end == -1: 877 return None 878 start = expr.rfind('(',0,end+1) 879 if start ==-1: 880 raise InvalidModel,\ 881 'Parenthesis of expression %s are malformed'%expr 882 return [expr[:start],expr[start+1:end],expr[end+1:]] 883 884 start_parenthesis = re.compile(r".*\s*[\+\-\*\/\)\(]\s*$") 885 886 def is_value_zero(value): 887 """Check whether an expression like ((A+B)*ZERO+C)*ZERO is zero. 888 Only +,-,/,* operations are allowed and 'ZERO' is a tag for an 889 analytically zero quantity.""" 890 891 curr_value = value 892 parenthesis = find_parenthesis(curr_value) 893 while parenthesis: 894 # Allow the complexconjugate function 895 if parenthesis[0].endswith('complexconjugate'): 896 # Then simply remove it 897 parenthesis[0] = parenthesis[0][:-16] 898 if parenthesis[0]=='' or re.match(start_parenthesis, 899 parenthesis[0]): 900 if is_value_zero(parenthesis[1]): 901 new_parenthesis = 'ZERO' 902 else: 903 new_parenthesis = 'PARENTHESIS' 904 else: 905 new_parenthesis = '_FUNCTIONARGS' 906 curr_value = parenthesis[0]+new_parenthesis+parenthesis[2] 907 parenthesis = find_parenthesis(curr_value) 908 return is_expr_zero(curr_value) 909 910 def CTCoupling_pole(CTCoupling, pole): 911 """Compute the pole of the CTCoupling in two cases: 912 a) Its value is a dictionary, then just return the corresponding 913 entry in the dictionary. 914 b) It is expressed in terms of CTParameters which are themselves 915 dictionary so we want to substitute their expression to get 916 the value of the pole. In the current implementation, this is 917 just to see if the pole is zero or not. 918 """ 919 920 if isinstance(CTCoupling.value,dict): 921 if -pole in CTCoupling.value.keys(): 922 return CTCoupling.value[-pole], [], 0 923 else: 924 return 'ZERO', [], 0 925 926 new_expression = CTCoupling.value 927 CTparamNames = [] 928 n_CTparams = 0 929 for paramname, value in CTparameter_patterns.items(): 930 pattern = value[0] 931 # Keep track of which CT parameters enter in the definition of 932 # which coupling. 933 if not re.search(pattern,new_expression): 934 continue 935 n_CTparams += 1 936 # If the contribution of this CTparam to this pole is non 937 # zero then the substituting function is not None: 938 if not value[1][pole] is None: 939 CTparamNames.append('%s_%s_'%(paramname,pole_dict[-pole])) 940 941 substitute_function = zero_substitution if \ 942 value[1][pole] is None else value[1][pole] 943 new_expression = pattern.sub(substitute_function,new_expression) 944 945 # If no CTParam was found and we ask for a pole, then it can only 946 # be zero. 947 if pole!=0 and n_CTparams==0: 948 return 'ZERO', [], n_CTparams 949 950 # Check if resulting expression is analytically zero or not. 951 # Remember that when the value of a CT_coupling is not a dictionary 952 # then the only operators allowed in the definition are +,-,*,/ 953 # and each term added or subtracted must contain *exactly one* 954 # CTParameter and never at the denominator. 955 if n_CTparams > 0 and is_value_zero(new_expression): 956 return 'ZERO', [], n_CTparams 957 else: 958 return new_expression, CTparamNames, n_CTparams 959 960 # For each coupling we substitute its value if necessary 961 for coupl in couplings: 962 new_value = {} 963 for pole in range(0,3): 964 expression, CTparamNames, n_CTparams = CTCoupling_pole(coupl, pole) 965 # Make sure it uses CT parameters, otherwise do nothing 966 if n_CTparams == 0: 967 break 968 elif expression!='ZERO': 969 new_value[-pole] = expression 970 couplname = coupl.name 971 if pole!=0: 972 couplname += "_%deps"%pole 973 # Add the parameter dependency found to the dependency map 974 # of the model being built. In principle, since we should 975 # be building a loop model now, it should always have this 976 # attribute defined, but it is better to make sure. 977 if hasattr(self.model, 'map_CTcoup_CTparam'): 978 self.model.map_CTcoup_CTparam[couplname] = CTparamNames 979 980 # Finally modify the value of this CTCoupling so that it is no 981 # longer a string expression in terms of CTParameters but rather 982 # a dictionary with the CTparameters replaced by their _FIN_ and 983 # _EPS_ counterparts. 984 # This is useful for the addCT_interaction() step. I will be reverted 985 # right after the addCT_interaction() function so as to leave 986 # the UFO intact, as it should. 987 if new_value: 988 coupl.old_value = coupl.value 989 coupl.value = new_value 990
991 - def add_CTinteraction(self, interaction, color_info):
992 """ Split this interaction in order to call add_interaction for 993 interactions for each element of the loop_particles list. Also it 994 is necessary to unfold here the contributions to the different laurent 995 expansion orders of the couplings.""" 996 997 # Work on a local copy of the interaction provided 998 interaction_info=copy.copy(interaction) 999 1000 intType='' 1001 if interaction_info.type not in ['UV','UVloop','UVtree','UVmass','R2']: 1002 raise MadGraph5Error, 'MG5 only supports the following types of'+\ 1003 ' vertices, R2, UV and UVmass. %s is not in this list.'%interaction_info.type 1004 else: 1005 intType=interaction_info.type 1006 # If not specified and simply set to UV, guess the appropriate type 1007 if interaction_info.type=='UV': 1008 if len(interaction_info.particles)==2 and interaction_info.\ 1009 particles[0].name==interaction_info.particles[1].name: 1010 intType='UVmass' 1011 else: 1012 intType='UVloop' 1013 1014 # Make sure that if it is a UV mass renromalization counterterm it is 1015 # defined as such. 1016 # if len(intType)>2 and intType[:2]=='UV' and len(interaction_info.particles)==2 \ 1017 # and interaction_info.particles[0].name==interaction_info.particles[1].name: 1018 # intType='UVmass' 1019 1020 # Now we create a couplings dictionary for each element of the loop_particles list 1021 # and for each expansion order of the laurent serie in the coupling. 1022 # and for each coupling order 1023 # Format is new_couplings[loop_particles][laurent_order] and each element 1024 # is a couplings dictionary. 1025 order_to_interactions= {} 1026 # will contains the new coupling of form 1027 #new_couplings=[[{} for j in range(0,3)] for i in \ 1028 # range(0,max(1,len(interaction_info.loop_particles)))] 1029 # So sort all entries in the couplings dictionary to put them a the 1030 # correct place in new_couplings. 1031 for key, couplings in interaction_info.couplings.items(): 1032 if not isinstance(couplings, list): 1033 couplings = [couplings] 1034 for coupling in couplings: 1035 order = tuple(coupling.order.items()) 1036 if order not in order_to_interactions: 1037 order_to_interactions[order] = [ 1038 [{} for j in range(0,3)] for i in \ 1039 range(0,max(1,len(interaction_info.loop_particles)))] 1040 new_couplings = order_to_interactions[order] 1041 else: 1042 new_couplings = order_to_interactions[order] 1043 1044 for poleOrder in range(0,3): 1045 expression = coupling.pole(poleOrder) 1046 if expression!='ZERO': 1047 if poleOrder==2: 1048 raise InvalidModel, """ 1049 The CT coupling %s was found with a contribution to the double pole. 1050 This is either an error in the model or a parsing error in the function 'is_value_zero'. 1051 The expression of the non-zero double pole coupling is: 1052 %s 1053 """%(coupling.name,str(coupling.value)) 1054 # It is actually safer that the new coupling associated to 1055 # the interaction added is not a reference to an original 1056 # coupling in the ufo model. So copy.copy is right here. 1057 newCoupling = copy.copy(coupling) 1058 if poleOrder!=0: 1059 newCoupling.name=newCoupling.name+"_"+str(poleOrder)+"eps" 1060 newCoupling.value = expression 1061 # assign the CT parameter dependences 1062 #if hasattr(coupling,'CTparam_dependence') and \ 1063 # (-poleOrder in coupling.CTparam_dependence) and \ 1064 # coupling.CTparam_dependence[-poleOrder]: 1065 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 1066 #elif hasattr(newCoupling,'CTparam_dependence'): 1067 # delattr(newCoupling,"CTparam_dependence") 1068 new_couplings[key[2]][poleOrder][(key[0],key[1])] = newCoupling 1069 1070 for new_couplings in order_to_interactions.values(): 1071 # Now we can add an interaction for each. 1072 for i, all_couplings in enumerate(new_couplings): 1073 loop_particles=[[]] 1074 if len(interaction_info.loop_particles)>0: 1075 loop_particles=[[part.pdg_code for part in loop_parts] \ 1076 for loop_parts in interaction_info.loop_particles[i]] 1077 for poleOrder in range(0,3): 1078 if all_couplings[poleOrder]!={}: 1079 interaction_info.couplings=all_couplings[poleOrder] 1080 self.add_interaction(interaction_info, color_info,\ 1081 (intType if poleOrder==0 else (intType+str(poleOrder)+\ 1082 'eps')),loop_particles)
1083
1084 - def find_color_anti_color_rep(self, output=None):
1085 """find which color are in the 3/3bar states""" 1086 # method look at the 3 3bar 8 configuration. 1087 # If the color is T(3,2,1) and the interaction F1 F2 V 1088 # Then set F1 to anticolor (and F2 to color) 1089 # if this is T(3,1,2) set the opposite 1090 if not output: 1091 output = {} 1092 1093 for interaction_info in self.ufomodel.all_vertices: 1094 if len(interaction_info.particles) != 3: 1095 continue 1096 colors = [abs(p.color) for p in interaction_info.particles] 1097 if colors[:2] == [3,3]: 1098 if 'T(3,2,1)' in interaction_info.color: 1099 color, anticolor, other = interaction_info.particles 1100 elif 'T(3,1,2)' in interaction_info.color: 1101 anticolor, color, _ = interaction_info.particles 1102 elif 'Identity(1,2)' in interaction_info.color or \ 1103 'Identity(2,1)' in interaction_info.color: 1104 first, second, _ = interaction_info.particles 1105 if first.pdg_code in output: 1106 if output[first.pdg_code] == 3: 1107 color, anticolor = first, second 1108 else: 1109 color, anticolor = second, first 1110 elif second.pdg_code in output: 1111 if output[second.pdg_code] == 3: 1112 color, anticolor = second, first 1113 else: 1114 color, anticolor = first, second 1115 else: 1116 continue 1117 else: 1118 continue 1119 elif colors[1:] == [3,3]: 1120 if 'T(1,2,3)' in interaction_info.color: 1121 other, anticolor, color = interaction_info.particles 1122 elif 'T(1,3,2)' in interaction_info.color: 1123 other, color, anticolor = interaction_info.particles 1124 elif 'Identity(2,3)' in interaction_info.color or \ 1125 'Identity(3,2)' in interaction_info.color: 1126 _, first, second = interaction_info.particles 1127 if first.pdg_code in output: 1128 if output[first.pdg_code] == 3: 1129 color, anticolor = first, second 1130 else: 1131 color, anticolor = second, first 1132 elif second.pdg_code in output: 1133 if output[second.pdg_code] == 3: 1134 color, anticolor = second, first 1135 else: 1136 color, anticolor = first, second 1137 else: 1138 continue 1139 else: 1140 continue 1141 1142 elif colors.count(3) == 2: 1143 if 'T(2,3,1)' in interaction_info.color: 1144 color, other, anticolor = interaction_info.particles 1145 elif 'T(2,1,3)' in interaction_info.color: 1146 anticolor, other, color = interaction_info.particles 1147 elif 'Identity(1,3)' in interaction_info.color or \ 1148 'Identity(3,1)' in interaction_info.color: 1149 first, _, second = interaction_info.particles 1150 if first.pdg_code in output: 1151 if output[first.pdg_code] == 3: 1152 color, anticolor = first, second 1153 else: 1154 color, anticolor = second, first 1155 elif second.pdg_code in output: 1156 if output[second.pdg_code] == 3: 1157 color, anticolor = second, first 1158 else: 1159 color, anticolor = first, second 1160 else: 1161 continue 1162 else: 1163 continue 1164 else: 1165 continue 1166 1167 # Check/assign for the color particle 1168 if color.pdg_code in output: 1169 if output[color.pdg_code] == -3: 1170 raise InvalidModel, 'Particles %s is sometimes in the 3 and sometimes in the 3bar representations' \ 1171 % color.name 1172 else: 1173 output[color.pdg_code] = 3 1174 1175 # Check/assign for the anticolor particle 1176 if anticolor.pdg_code in output: 1177 if output[anticolor.pdg_code] == 3: 1178 raise InvalidModel, 'Particles %s is sometimes set as in the 3 and sometimes in the 3bar representations' \ 1179 % anticolor.name 1180 else: 1181 output[anticolor.pdg_code] = -3 1182 1183 return output
1184
1185 - def detect_incoming_fermion(self):
1186 """define which fermion should be incoming 1187 for that we look at F F~ X interactions 1188 """ 1189 self.incoming = [] 1190 self.outcoming = [] 1191 for interaction_info in self.ufomodel.all_vertices: 1192 # check if the interaction meet requirements: 1193 pdg = [p.pdg_code for p in interaction_info.particles if p.spin in [2,4]] 1194 if len(pdg) % 2: 1195 raise InvalidModel, 'Odd number of fermion in vertex: %s' % [p.pdg_code for p in interaction_info.particles] 1196 for i in range(0, len(pdg),2): 1197 if pdg[i] == - pdg[i+1]: 1198 if pdg[i] in self.outcoming: 1199 raise InvalidModel, '%s has not coherent incoming/outcoming status between interactions' %\ 1200 [p for p in interaction_info.particles if p.spin in [2,4]][i].name 1201 1202 elif not pdg[i] in self.incoming: 1203 self.incoming.append(pdg[i]) 1204 self.outcoming.append(pdg[i+1])
1205
1206 - def add_interaction(self, interaction_info, color_info, type='base', loop_particles=None):
1207 """add an interaction in the MG5 model. interaction_info is the 1208 UFO vertices information.""" 1209 # Import particles content: 1210 particles = [self.model.get_particle(particle.pdg_code) \ 1211 for particle in interaction_info.particles] 1212 if None in particles: 1213 # Interaction with a ghost/goldstone 1214 return 1215 particles = base_objects.ParticleList(particles) 1216 1217 # Import Lorentz content: 1218 lorentz = [helas for helas in interaction_info.lorentz] 1219 1220 # Check the coherence of the Fermion Flow 1221 nb_fermion = sum([ 1 if p.is_fermion() else 0 for p in particles]) 1222 try: 1223 if nb_fermion == 2: 1224 # Fermion Flow is suppose to be dealt by UFO 1225 [aloha_fct.check_flow_validity(helas.structure, nb_fermion) \ 1226 for helas in interaction_info.lorentz 1227 if helas.name not in self.checked_lor] 1228 self.checked_lor.update(set([helas.name for helas in interaction_info.lorentz])) 1229 elif nb_fermion: 1230 if any(p.selfconjugate for p in interaction_info.particles if p.spin % 2 == 0): 1231 text = "Majorana can not be dealt in 4/6/... fermion interactions" 1232 raise InvalidModel, text 1233 except aloha_fct.WrongFermionFlow, error: 1234 text = 'Fermion Flow error for interactions %s: %s: %s\n %s' % \ 1235 (', '.join([p.name for p in interaction_info.particles]), 1236 helas.name, helas.structure, error) 1237 raise InvalidModel, text 1238 1239 1240 1241 # Now consider the name only 1242 lorentz = [helas.name for helas in lorentz] 1243 # Import color information: 1244 colors = [self.treat_color(color_obj, interaction_info, color_info) 1245 for color_obj in interaction_info.color] 1246 1247 1248 order_to_int={} 1249 1250 for key, couplings in interaction_info.couplings.items(): 1251 if not isinstance(couplings, list): 1252 couplings = [couplings] 1253 if interaction_info.lorentz[key[1]].name not in lorentz: 1254 continue 1255 # get the sign for the coupling (if we need to adapt the flow) 1256 if nb_fermion > 2: 1257 flow = aloha_fct.get_fermion_flow(interaction_info.lorentz[key[1]].structure, 1258 nb_fermion) 1259 coupling_sign = self.get_sign_flow(flow, nb_fermion) 1260 else: 1261 coupling_sign = '' 1262 for coupling in couplings: 1263 order = tuple(coupling.order.items()) 1264 if '1' in order: 1265 raise InvalidModel, '''Some couplings have \'1\' order. 1266 This is not allowed in MG. 1267 Please defines an additional coupling to your model''' 1268 if order in order_to_int: 1269 order_to_int[order].get('couplings')[key] = '%s%s' % \ 1270 (coupling_sign,coupling.name) 1271 else: 1272 # Initialize a new interaction with a new id tag 1273 interaction = base_objects.Interaction({'id':len(self.interactions)+1}) 1274 interaction.set('particles', particles) 1275 interaction.set('lorentz', lorentz) 1276 interaction.set('couplings', {key: 1277 '%s%s' %(coupling_sign,coupling.name)}) 1278 interaction.set('orders', coupling.order) 1279 interaction.set('color', colors) 1280 interaction.set('type', type) 1281 interaction.set('loop_particles', loop_particles) 1282 order_to_int[order] = interaction 1283 # add to the interactions 1284 self.interactions.append(interaction) 1285 1286 # check if this interaction conserve the charge defined 1287 # if type=='base': 1288 for charge in list(self.conservecharge): #duplicate to allow modification 1289 total = 0 1290 for part in interaction_info.particles: 1291 try: 1292 total += getattr(part, charge) 1293 except AttributeError: 1294 pass 1295 if abs(total) > 1e-12: 1296 logger.info('The model has interaction violating the charge: %s' % charge) 1297 self.conservecharge.discard(charge) 1298 1299 1300
1301 - def get_sign_flow(self, flow, nb_fermion):
1302 """ensure that the flow of particles/lorentz are coherent with flow 1303 and return a correct version if needed""" 1304 1305 if not flow or nb_fermion < 4: 1306 return '' 1307 1308 expected = {} 1309 for i in range(nb_fermion//2): 1310 expected[i+1] = i+2 1311 1312 if flow == expected: 1313 return '' 1314 1315 switch = {} 1316 for i in range(1, nb_fermion+1): 1317 if not i in flow: 1318 continue 1319 switch[i] = len(switch) 1320 switch[flow[i]] = len(switch) 1321 1322 # compute the sign of the permutation 1323 sign = 1 1324 done = [] 1325 1326 # make a list of consecutive number which correspond to the new 1327 # order of the particles in the new list. 1328 new_order = [] 1329 for id in range(nb_fermion): # id is the position in the particles order (starts 0) 1330 nid = switch[id+1]-1 # nid is the position in the new_particles 1331 #order (starts 0) 1332 new_order.append(nid) 1333 1334 # compute the sign: 1335 sign =1 1336 for k in range(len(new_order)-1): 1337 for l in range(k+1,len(new_order)): 1338 if new_order[l] < new_order[k]: 1339 sign *= -1 1340 1341 return '' if sign ==1 else '-'
1342
1343 - def add_lorentz(self, name, spins , expr):
1344 """ Add a Lorentz expression which is not present in the UFO """ 1345 1346 assert name not in [l.name for l in self.model['lorentz']] 1347 with misc.TMP_variable(self.ufomodel.object_library, 'all_lorentz', 1348 self.model['lorentz']): 1349 new = self.model['lorentz'][0].__class__(name = name, 1350 spins = spins, 1351 structure = expr) 1352 assert name in [l.name for l in self.model['lorentz']] 1353 assert name not in [l.name for l in self.ufomodel.all_lorentz] 1354 #self.model['lorentz'].append(new) # already done by above command 1355 self.model.create_lorentz_dict() 1356 return new
1357 1358 _pat_T = re.compile(r'T\((?P<first>\d*),(?P<second>\d*)\)') 1359 _pat_id = re.compile(r'Identity\((?P<first>\d*),(?P<second>\d*)\)') 1360
1361 - def treat_color(self, data_string, interaction_info, color_info):
1362 """ convert the string to ColorString""" 1363 1364 #original = copy.copy(data_string) 1365 #data_string = p.sub('color.T(\g<first>,\g<second>)', data_string) 1366 1367 1368 output = [] 1369 factor = 1 1370 for term in data_string.split('*'): 1371 pattern = self._pat_id.search(term) 1372 if pattern: 1373 particle = interaction_info.particles[int(pattern.group('first'))-1] 1374 particle2 = interaction_info.particles[int(pattern.group('second'))-1] 1375 if particle.color == particle2.color and particle.color in [-6, 6]: 1376 error_msg = 'UFO model have inconsistency in the format:\n' 1377 error_msg += 'interactions for particles %s has color information %s\n' 1378 error_msg += ' but both fermion are in the same representation %s' 1379 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1380 if particle.color == particle2.color and particle.color in [-3, 3]: 1381 if particle.pdg_code in color_info and particle2.pdg_code in color_info: 1382 if color_info[particle.pdg_code] == color_info[particle2.pdg_code]: 1383 error_msg = 'UFO model have inconsistency in the format:\n' 1384 error_msg += 'interactions for particles %s has color information %s\n' 1385 error_msg += ' but both fermion are in the same representation %s' 1386 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1387 elif particle.pdg_code in color_info: 1388 color_info[particle2.pdg_code] = -particle.pdg_code 1389 elif particle2.pdg_code in color_info: 1390 color_info[particle.pdg_code] = -particle2.pdg_code 1391 else: 1392 error_msg = 'UFO model have inconsistency in the format:\n' 1393 error_msg += 'interactions for particles %s has color information %s\n' 1394 error_msg += ' but both fermion are in the same representation %s' 1395 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1396 1397 1398 if particle.color == 6: 1399 output.append(self._pat_id.sub('color.T6(\g<first>,\g<second>)', term)) 1400 elif particle.color == -6 : 1401 output.append(self._pat_id.sub('color.T6(\g<second>,\g<first>)', term)) 1402 elif particle.color == 8: 1403 output.append(self._pat_id.sub('color.Tr(\g<first>,\g<second>)', term)) 1404 factor *= 2 1405 elif particle.color in [-3,3]: 1406 if particle.pdg_code not in color_info: 1407 #try to find it one more time 3 -3 1 might help 1408 logger.debug('fail to find 3/3bar representation: Retry to find it') 1409 color_info = self.find_color_anti_color_rep(color_info) 1410 if particle.pdg_code not in color_info: 1411 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle.name) 1412 color_info[particle.pdg_code] = particle.color 1413 else: 1414 logger.debug('succeed') 1415 if particle2.pdg_code not in color_info: 1416 #try to find it one more time 3 -3 1 might help 1417 logger.debug('fail to find 3/3bar representation: Retry to find it') 1418 color_info = self.find_color_anti_color_rep(color_info) 1419 if particle2.pdg_code not in color_info: 1420 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle2.name) 1421 color_info[particle2.pdg_code] = particle2.color 1422 else: 1423 logger.debug('succeed') 1424 1425 if color_info[particle.pdg_code] == 3 : 1426 output.append(self._pat_id.sub('color.T(\g<second>,\g<first>)', term)) 1427 elif color_info[particle.pdg_code] == -3: 1428 output.append(self._pat_id.sub('color.T(\g<first>,\g<second>)', term)) 1429 else: 1430 raise MadGraph5Error, \ 1431 "Unknown use of Identity for particle with color %d" \ 1432 % particle.color 1433 else: 1434 output.append(term) 1435 data_string = '*'.join(output) 1436 1437 # Change convention for summed indices 1438 p = re.compile(r'\'\w(?P<number>\d+)\'') 1439 data_string = p.sub('-\g<number>', data_string) 1440 1441 # Shift indices by -1 1442 new_indices = {} 1443 new_indices = dict([(j,i) for (i,j) in \ 1444 enumerate(range(1, 1445 len(interaction_info.particles)+1))]) 1446 1447 1448 output = data_string.split('*') 1449 output = color.ColorString([eval(data) \ 1450 for data in output if data !='1']) 1451 output.coeff = fractions.Fraction(factor) 1452 for col_obj in output: 1453 col_obj.replace_indices(new_indices) 1454 1455 return output
1456
1457 -class OrganizeModelExpression:
1458 """Organize the couplings/parameters of a model""" 1459 1460 track_dependant = ['aS','aEWM1','MU_R'] # list of variable from which we track 1461 #dependencies those variables should be define 1462 #as external parameters 1463 1464 # regular expression to shorten the expressions 1465 complex_number = re.compile(r'''complex\((?P<real>[^,\(\)]+),(?P<imag>[^,\(\)]+)\)''') 1466 expo_expr = re.compile(r'''(?P<expr>[\w.]+)\s*\*\*\s*(?P<expo>[+-]?[\d.]+)''') 1467 cmath_expr = re.compile(r'''cmath.(?P<operation>\w+)\((?P<expr>\w+)\)''') 1468 #operation is usualy sqrt / sin / cos / tan 1469 conj_expr = re.compile(r'''complexconjugate\((?P<expr>\w+)\)''') 1470 1471 #RE expression for is_event_dependent 1472 separator = re.compile(r'''[+,\-*/()\s]*''') 1473 1474
1475 - def __init__(self, model):
1476 1477 self.model = model # UFOMODEL 1478 self.perturbation_couplings = {} 1479 try: 1480 for order in model.all_orders: # Check if it is a loop model or not 1481 if(order.perturbative_expansion>0): 1482 self.perturbation_couplings[order.name]=order.perturbative_expansion 1483 except AttributeError: 1484 pass 1485 self.params = {} # depend on -> ModelVariable 1486 self.couplings = {} # depend on -> ModelVariable 1487 self.all_expr = {} # variable_name -> ModelVariable
1488
1489 - def main(self, additional_couplings = []):
1490 """Launch the actual computation and return the associate 1491 params/couplings. Possibly consider additional_couplings in addition 1492 to those defined in the UFO model attribute all_couplings """ 1493 1494 additional_params = [] 1495 if hasattr(self.model,'all_CTparameters'): 1496 additional_params = self.get_additional_CTparameters() 1497 1498 self.analyze_parameters(additional_params = additional_params) 1499 self.analyze_couplings(additional_couplings = additional_couplings) 1500 1501 # Finally revert the possible modifications done by treat_couplings() 1502 if hasattr(self.model,'all_CTparameters'): 1503 self.revert_CTCoupling_modifications() 1504 1505 return self.params, self.couplings
1506
1508 """ Finally revert the possible modifications done by treat_couplings() 1509 in UFOMG5Converter which were useful for the add_CTinteraction() in 1510 particular. This modification consisted in expanding the value of a 1511 CTCoupling which consisted in an expression in terms of a CTParam to 1512 its corresponding dictionary (e.g 1513 CTCoupling.value = 2*CTParam -> 1514 CTCoupling.value = {-1: 2*CTParam_1EPS_, 0: 2*CTParam_FIN_} 1515 for example if CTParam had a non-zero finite and single pole.""" 1516 1517 for coupl in self.model.all_couplings: 1518 if hasattr(coupl,'old_value'): 1519 coupl.value = coupl.old_value 1520 del(coupl.old_value)
1521
1523 """ For each CTparameter split it into spimple parameter for each pole 1524 and the finite part if not zero.""" 1525 1526 additional_params = [] 1527 for CTparam in self.model.all_CTparameters: 1528 for pole in range(3): 1529 if CTparam.pole(pole) != 'ZERO': 1530 CTparam_piece = copy.copy(CTparam) 1531 CTparam_piece.name = '%s_%s_'%(CTparam.name,pole_dict[-pole]) 1532 CTparam_piece.nature = 'internal' 1533 CTparam_piece.type = CTparam.type 1534 CTparam_piece.value = CTparam.pole(pole) 1535 CTparam_piece.texname = '%s_{%s}'%\ 1536 (CTparam.texname,pole_dict[-pole]) 1537 additional_params.append(CTparam_piece) 1538 return additional_params
1539
1540 - def analyze_parameters(self, additional_params=[]):
1541 """ separate the parameters needed to be recomputed events by events and 1542 the others""" 1543 # in order to match in Gmu scheme 1544 # test whether aEWM1 is the external or not 1545 # if not, take Gf as the track_dependant variable 1546 present_aEWM1 = any(param.name == 'aEWM1' for param in 1547 self.model.all_parameters if param.nature == 'external') 1548 1549 if not present_aEWM1: 1550 self.track_dependant = ['aS','Gf','MU_R'] 1551 1552 for param in self.model.all_parameters+additional_params: 1553 if param.nature == 'external': 1554 parameter = base_objects.ParamCardVariable(param.name, param.value, \ 1555 param.lhablock, param.lhacode) 1556 1557 else: 1558 expr = self.shorten_expr(param.value) 1559 depend_on = self.find_dependencies(expr) 1560 parameter = base_objects.ModelVariable(param.name, expr, param.type, depend_on) 1561 1562 self.add_parameter(parameter)
1563
1564 - def add_parameter(self, parameter):
1565 """ add consistently the parameter in params and all_expr. 1566 avoid duplication """ 1567 1568 assert isinstance(parameter, base_objects.ModelVariable) 1569 1570 if parameter.name in self.all_expr: 1571 return 1572 1573 self.all_expr[parameter.name] = parameter 1574 try: 1575 self.params[parameter.depend].append(parameter) 1576 except: 1577 self.params[parameter.depend] = [parameter]
1578
1579 - def add_coupling(self, coupling):
1580 """ add consistently the coupling in couplings and all_expr. 1581 avoid duplication """ 1582 1583 assert isinstance(coupling, base_objects.ModelVariable) 1584 1585 if coupling.name in self.all_expr: 1586 return 1587 self.all_expr[coupling.value] = coupling 1588 try: 1589 self.coupling[coupling.depend].append(coupling) 1590 except: 1591 self.coupling[coupling.depend] = [coupling]
1592
1593 - def analyze_couplings(self,additional_couplings=[]):
1594 """creates the shortcut for all special function/parameter 1595 separate the couplings dependent of track variables of the others""" 1596 1597 # For loop models, make sure that all couplings with dictionary values 1598 # are turned into set of couplings, one for each pole and finite part. 1599 if self.perturbation_couplings: 1600 couplings_list=[] 1601 for coupling in self.model.all_couplings + additional_couplings: 1602 if not isinstance(coupling.value,dict): 1603 couplings_list.append(coupling) 1604 else: 1605 for poleOrder in range(0,3): 1606 if coupling.pole(poleOrder)!='ZERO': 1607 newCoupling=copy.copy(coupling) 1608 if poleOrder!=0: 1609 newCoupling.name += "_%deps"%poleOrder 1610 newCoupling.value=coupling.pole(poleOrder) 1611 # assign the CT parameter dependences 1612 # if hasattr(coupling,'CTparam_dependence') and \ 1613 # (-poleOrder in coupling.CTparam_dependence) and \ 1614 # coupling.CTparam_dependence[-poleOrder]: 1615 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 1616 # elif hasattr(newCoupling,'CTparam_dependence'): 1617 # delattr(newCoupling,"CTparam_dependence") 1618 couplings_list.append(newCoupling) 1619 else: 1620 couplings_list = self.model.all_couplings + additional_couplings 1621 couplings_list = [c for c in couplings_list if not isinstance(c.value, dict)] 1622 1623 for coupling in couplings_list: 1624 # shorten expression, find dependencies, create short object 1625 expr = self.shorten_expr(coupling.value) 1626 depend_on = self.find_dependencies(expr) 1627 parameter = base_objects.ModelVariable(coupling.name, expr, 'complex', depend_on) 1628 # Add consistently in the couplings/all_expr 1629 try: 1630 self.couplings[depend_on].append(parameter) 1631 except KeyError: 1632 self.couplings[depend_on] = [parameter] 1633 self.all_expr[coupling.value] = parameter
1634
1635 - def find_dependencies(self, expr):
1636 """check if an expression should be evaluated points by points or not 1637 """ 1638 depend_on = set() 1639 1640 # Treat predefined result 1641 #if name in self.track_dependant: 1642 # return tuple() 1643 1644 # Split the different part of the expression in order to say if a 1645 #subexpression is dependent of one of tracked variable 1646 expr = self.separator.split(expr) 1647 1648 # look for each subexpression 1649 for subexpr in expr: 1650 if subexpr in self.track_dependant: 1651 depend_on.add(subexpr) 1652 1653 elif subexpr in self.all_expr and self.all_expr[subexpr].depend: 1654 [depend_on.add(value) for value in self.all_expr[subexpr].depend 1655 if self.all_expr[subexpr].depend != ('external',)] 1656 if depend_on: 1657 return tuple(depend_on) 1658 else: 1659 return tuple()
1660 1661
1662 - def shorten_expr(self, expr):
1663 """ apply the rules of contraction and fullfill 1664 self.params with dependent part""" 1665 try: 1666 expr = self.complex_number.sub(self.shorten_complex, expr) 1667 expr = self.expo_expr.sub(self.shorten_expo, expr) 1668 expr = self.cmath_expr.sub(self.shorten_cmath, expr) 1669 expr = self.conj_expr.sub(self.shorten_conjugate, expr) 1670 except Exception: 1671 logger.critical("fail to handle expression: %s, type()=%s", expr,type(expr)) 1672 raise 1673 return expr
1674 1675
1676 - def shorten_complex(self, matchobj):
1677 """add the short expression, and return the nice string associate""" 1678 1679 float_real = float(eval(matchobj.group('real'))) 1680 float_imag = float(eval(matchobj.group('imag'))) 1681 if float_real == 0 and float_imag ==1: 1682 new_param = base_objects.ModelVariable('complexi', 'complex(0,1)', 'complex') 1683 self.add_parameter(new_param) 1684 return 'complexi' 1685 else: 1686 return 'complex(%s, %s)' % (matchobj.group('real'), matchobj.group('imag'))
1687 1688
1689 - def shorten_expo(self, matchobj):
1690 """add the short expression, and return the nice string associate""" 1691 1692 expr = matchobj.group('expr') 1693 exponent = matchobj.group('expo') 1694 new_exponent = exponent.replace('.','_').replace('+','').replace('-','_m_') 1695 output = '%s__exp__%s' % (expr, new_exponent) 1696 old_expr = '%s**%s' % (expr,exponent) 1697 1698 if expr.startswith('cmath'): 1699 return old_expr 1700 1701 if expr.isdigit(): 1702 output = 'nb__' + output #prevent to start with a number 1703 new_param = base_objects.ModelVariable(output, old_expr,'real') 1704 else: 1705 depend_on = self.find_dependencies(expr) 1706 type = self.search_type(expr) 1707 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1708 self.add_parameter(new_param) 1709 return output
1710
1711 - def shorten_cmath(self, matchobj):
1712 """add the short expression, and return the nice string associate""" 1713 1714 expr = matchobj.group('expr') 1715 operation = matchobj.group('operation') 1716 output = '%s__%s' % (operation, expr) 1717 old_expr = ' cmath.%s(%s) ' % (operation, expr) 1718 if expr.isdigit(): 1719 new_param = base_objects.ModelVariable(output, old_expr , 'real') 1720 else: 1721 depend_on = self.find_dependencies(expr) 1722 type = self.search_type(expr) 1723 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1724 self.add_parameter(new_param) 1725 1726 return output
1727
1728 - def shorten_conjugate(self, matchobj):
1729 """add the short expression, and retrun the nice string associate""" 1730 1731 expr = matchobj.group('expr') 1732 output = 'conjg__%s' % (expr) 1733 old_expr = ' complexconjugate(%s) ' % expr 1734 depend_on = self.find_dependencies(expr) 1735 type = 'complex' 1736 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1737 self.add_parameter(new_param) 1738 1739 return output
1740 1741 1742
1743 - def search_type(self, expr, dep=''):
1744 """return the type associate to the expression if define""" 1745 1746 try: 1747 return self.all_expr[expr].type 1748 except: 1749 return 'complex'
1750
1751 -class RestrictModel(model_reader.ModelReader):
1752 """ A class for restricting a model for a given param_card. 1753 rules applied: 1754 - Vertex with zero couplings are throw away 1755 - external parameter with zero/one input are changed into internal parameter. 1756 - identical coupling/mass/width are replace in the model by a unique one 1757 """ 1758
1759 - def default_setup(self):
1760 """define default value""" 1761 self.del_coup = [] 1762 super(RestrictModel, self).default_setup() 1763 self.rule_card = check_param_card.ParamCardRule() 1764 self.restrict_card = None 1765 self.coupling_order_dict ={}
1766
1767 - def restrict_model(self, param_card, rm_parameter=True, keep_external=False, 1768 complex_mass_scheme=None):
1769 """apply the model restriction following param_card. 1770 rm_parameter defines if the Zero/one parameter are removed or not from 1771 the model. 1772 keep_external if the param_card need to be kept intact 1773 """ 1774 1775 if self.get('name') == "mssm" and not keep_external: 1776 raise Exception 1777 1778 self.restrict_card = param_card 1779 # Reset particle dict to ensure synchronized particles and interactions 1780 self.set('particles', self.get('particles')) 1781 1782 # compute the value of all parameters 1783 # Get the list of definition of model functions, parameter values. 1784 model_definitions = self.set_parameters_and_couplings(param_card, 1785 complex_mass_scheme=complex_mass_scheme) 1786 1787 # Simplify conditional statements 1788 logger.debug('Simplifying conditional expressions') 1789 modified_params, modified_couplings = \ 1790 self.detect_conditional_statements_simplifications(model_definitions) 1791 1792 # Apply simplifications 1793 self.apply_conditional_simplifications(modified_params, modified_couplings) 1794 1795 # associate to each couplings the associated vertex: def self.coupling_pos 1796 self.locate_coupling() 1797 # deal with couplings 1798 zero_couplings, iden_couplings = self.detect_identical_couplings() 1799 1800 # remove the out-dated interactions 1801 self.remove_interactions(zero_couplings) 1802 1803 # replace in interactions identical couplings 1804 for iden_coups in iden_couplings: 1805 self.merge_iden_couplings(iden_coups) 1806 1807 # remove zero couplings and other pointless couplings 1808 self.del_coup += zero_couplings 1809 self.remove_couplings(self.del_coup) 1810 1811 # modify interaction to avoid to have identical coupling with different lorentz 1812 for interaction in self.get('interactions'): 1813 self.optimise_interaction(interaction) 1814 1815 # deal with parameters 1816 parameters = self.detect_special_parameters() 1817 self.fix_parameter_values(*parameters, simplify=rm_parameter, 1818 keep_external=keep_external) 1819 1820 # deal with identical parameters 1821 if not keep_external: 1822 iden_parameters = self.detect_identical_parameters() 1823 for iden_param in iden_parameters: 1824 self.merge_iden_parameters(iden_param) 1825 1826 iden_parameters = self.detect_identical_parameters() 1827 for iden_param in iden_parameters: 1828 self.merge_iden_parameters(iden_param, keep_external) 1829 1830 # change value of default parameter if they have special value: 1831 # 9.999999e-1 -> 1.0 1832 # 0.000001e-99 -> 0 Those value are used to avoid restriction 1833 for name, value in self['parameter_dict'].items(): 1834 if value == 9.999999e-1: 1835 self['parameter_dict'][name] = 1 1836 elif value == 0.000001e-99: 1837 self['parameter_dict'][name] = 0
1838 1839
1840 - def locate_coupling(self):
1841 """ create a dict couplings_name -> vertex or (particle, counterterm_key) """ 1842 1843 self.coupling_pos = {} 1844 for vertex in self['interactions']: 1845 for key, coupling in vertex['couplings'].items(): 1846 if coupling.startswith('-'): 1847 coupling = coupling[1:] 1848 if coupling in self.coupling_pos: 1849 if vertex not in self.coupling_pos[coupling]: 1850 self.coupling_pos[coupling].append(vertex) 1851 else: 1852 self.coupling_pos[coupling] = [vertex] 1853 1854 for particle in self['particles']: 1855 for key, coupling_dict in particle['counterterm'].items(): 1856 for LaurentOrder, coupling in coupling_dict.items(): 1857 if coupling in self.coupling_pos: 1858 if (particle,key) not in self.coupling_pos[coupling]: 1859 self.coupling_pos[coupling].append((particle,key)) 1860 else: 1861 self.coupling_pos[coupling] = [(particle,key)] 1862 1863 return self.coupling_pos
1864
1865 - def detect_identical_couplings(self, strict_zero=False):
1866 """return a list with the name of all vanishing couplings""" 1867 1868 dict_value_coupling = {} 1869 iden_key = set() 1870 zero_coupling = [] 1871 iden_coupling = [] 1872 1873 for name, value in self['coupling_dict'].items(): 1874 if value == 0: 1875 zero_coupling.append(name) 1876 continue 1877 elif not strict_zero and abs(value) < 1e-13: 1878 logger.debug('coupling with small value %s: %s treated as zero' % 1879 (name, value)) 1880 zero_coupling.append(name) 1881 elif not strict_zero and abs(value) < 1e-10: 1882 return self.detect_identical_couplings(strict_zero=True) 1883 1884 1885 if value in dict_value_coupling or -1*value in dict_value_coupling: 1886 if value in dict_value_coupling: 1887 iden_key.add(value) 1888 dict_value_coupling[value].append((name,1)) 1889 else: 1890 iden_key.add(-1*value) 1891 dict_value_coupling[-1*value].append((name,-1)) 1892 else: 1893 dict_value_coupling[value] = [(name,1)] 1894 for key in iden_key: 1895 tmp = [] 1896 if key in dict_value_coupling: 1897 tmp += dict_value_coupling[key] 1898 elif -1*key in dict_value_coupling: 1899 tmp += dict_value_coupling[-1*key] 1900 assert tmp 1901 1902 #ensure that all coupling have the same coupling order. 1903 ords = [self.get_coupling_order(k) for k,c in tmp] 1904 coup_by_ord = collections.defaultdict(list) 1905 for o,t in zip(ords, tmp): 1906 coup_by_ord[str(o)].append(t) 1907 # add the remaining identical 1908 for tmp3 in coup_by_ord.values(): 1909 if len(tmp3) > 1: 1910 if tmp3[0][1] == -1: #ensure that the first coupling has positif value 1911 tmp3 = [(t0,-t1) for t0, t1 in tmp3] 1912 iden_coupling.append(tmp3) 1913 1914 1915 1916 1917 return zero_coupling, iden_coupling
1918
1919 - def get_coupling_order(self, cname):
1920 """return the coupling order associated to a coupling """ 1921 1922 if cname in self.coupling_order_dict: 1923 return self.coupling_order_dict[cname] 1924 1925 for v in self['interactions']: 1926 for c in v['couplings'].values(): 1927 self.coupling_order_dict[c] = v['orders'] 1928 1929 if cname not in self.coupling_order_dict: 1930 self.coupling_order_dict[cname] = None 1931 #can happen when some vertex are discarded due to ghost/... 1932 1933 1934 return self.coupling_order_dict[cname]
1935 1936 1937
1938 - def detect_special_parameters(self):
1939 """ return the list of (name of) parameter which are zero """ 1940 1941 null_parameters = [] 1942 one_parameters = [] 1943 for name, value in self['parameter_dict'].items(): 1944 if value == 0 and name != 'ZERO': 1945 null_parameters.append(name) 1946 elif value == 1: 1947 one_parameters.append(name) 1948 1949 return null_parameters, one_parameters
1950
1951 - def apply_conditional_simplifications(self, modified_params, 1952 modified_couplings):
1953 """ Apply the conditional statement simplifications for parameters and 1954 couplings detected by 'simplify_conditional_statements'. 1955 modified_params (modified_couplings) are list of tuples (a,b) with a 1956 parameter (resp. coupling) instance and b is the simplified expression.""" 1957 1958 if modified_params: 1959 logger.debug("Conditional expressions are simplified for parameters:") 1960 logger.debug(",".join("%s"%param[0].name for param in modified_params)) 1961 for param, new_expr in modified_params: 1962 param.expr = new_expr 1963 1964 if modified_couplings: 1965 logger.debug("Conditional expressions are simplified for couplings:") 1966 logger.debug(",".join("%s"%coupl[0].name for coupl in modified_couplings)) 1967 for coupl, new_expr in modified_couplings: 1968 coupl.expr = new_expr
1969
1970 - def detect_conditional_statements_simplifications(self, model_definitions, 1971 objects=['couplings','parameters']):
1972 """ Simplifies the 'if' statements in the pythonic UFO expressions 1973 of parameters using the default variables specified in the restrict card. 1974 It returns a list of objects (parameters or couplings) and the new 1975 expression that they should take. Model definitions include all definitons 1976 of the model functions and parameters.""" 1977 1978 param_modifications = [] 1979 coupl_modifications = [] 1980 ifparser = parsers.UFOExpressionParserPythonIF(model_definitions) 1981 1982 start_param = time.time() 1983 if 'parameters' in objects: 1984 for dependences, param_list in self['parameters'].items(): 1985 if 'external' in dependences: 1986 continue 1987 for param in param_list: 1988 new_expr, n_changes = ifparser.parse(param.expr) 1989 if n_changes > 0: 1990 param_modifications.append((param, new_expr)) 1991 1992 end_param = time.time() 1993 1994 if 'couplings' in objects: 1995 for dependences, coupl_list in self['couplings'].items(): 1996 for coupl in coupl_list: 1997 new_expr, n_changes = ifparser.parse(coupl.expr) 1998 if n_changes > 0: 1999 coupl_modifications.append((coupl, new_expr)) 2000 2001 end_coupl = time.time() 2002 2003 tot_param_time = end_param-start_param 2004 tot_coupl_time = end_coupl-end_param 2005 if tot_param_time>5.0: 2006 logger.debug("Simplification of conditional statements"+\ 2007 " in parameter expressions done in %s."%misc.format_time(tot_param_time)) 2008 if tot_coupl_time>5.0: 2009 logger.debug("Simplification of conditional statements"+\ 2010 " in couplings expressions done in %s."%misc.format_time(tot_coupl_time)) 2011 2012 return param_modifications, coupl_modifications
2013
2015 """ return the list of tuple of name of parameter with the same 2016 input value """ 2017 2018 # Extract external parameters 2019 external_parameters = self['parameters'][('external',)] 2020 2021 # define usefull variable to detect identical input 2022 block_value_to_var={} #(lhablok, value): list_of_var 2023 mult_param = set([]) # key of the previous dict with more than one 2024 #parameter. 2025 2026 #detect identical parameter and remove the duplicate parameter 2027 for param in external_parameters[:]: 2028 value = self['parameter_dict'][param.name] 2029 if value in [0,1,0.000001e-99,9.999999e-1]: 2030 continue 2031 if param.lhablock.lower() == 'decay': 2032 continue 2033 key = (param.lhablock, value) 2034 mkey = (param.lhablock, -value) 2035 2036 if key in block_value_to_var: 2037 block_value_to_var[key].append((param,1)) 2038 mult_param.add(key) 2039 elif mkey in block_value_to_var: 2040 block_value_to_var[mkey].append((param,-1)) 2041 mult_param.add(mkey) 2042 else: 2043 block_value_to_var[key] = [(param,1)] 2044 2045 output=[] 2046 for key in mult_param: 2047 output.append(block_value_to_var[key]) 2048 2049 return output
2050 2051 2052 @staticmethod
2053 - def get_new_coupling_name(main, coupling, value, coeff):
2054 """ We have main == coeff * coupling 2055 coeff is only +1 or -1 2056 main can be either GC_X or -GC_X 2057 coupling can be either GC_Y or -GC_Y 2058 value is either GC_Y or -GC_Y 2059 the return is either GC_X or -GC_X 2060 such that we have value == OUTPUT 2061 """ 2062 assert coeff in [-1,1] 2063 assert value == coupling or value == '-%s' % coupling or coupling == '-%s' % value 2064 assert isinstance(main, str) 2065 assert isinstance(coupling, str) 2066 assert isinstance(value, str) 2067 if coeff ==1: 2068 if value == coupling: 2069 return main # 4/4 2070 else: 2071 if main.startswith('-'): 2072 return main[1:] # 2/2 2073 else: 2074 return '-%s' % main # 2/2 2075 else: 2076 if value == coupling: 2077 if main.startswith('-'): 2078 return main[1:] # 2/2 2079 else: 2080 return '-%s' % main # 2/2 2081 else: 2082 return main # 4/4
2083 2084
2085 - def merge_iden_couplings(self, couplings):
2086 """merge the identical couplings in the interactions and particle 2087 counterterms""" 2088 2089 2090 logger_mod.debug(' Fuse the Following coupling (they have the same value): %s '% \ 2091 ', '.join([str(obj) for obj in couplings])) 2092 2093 main = couplings[0][0] 2094 assert couplings[0][1] == 1 2095 self.del_coup += [c[0] for c in couplings[1:]] # add the other coupl to the suppress list 2096 2097 for coupling, coeff in couplings[1:]: 2098 # check if param is linked to an interaction 2099 if coupling not in self.coupling_pos: 2100 continue 2101 # replace the coupling, by checking all coupling of the interaction 2102 vertices = [ vert for vert in self.coupling_pos[coupling] if 2103 isinstance(vert, base_objects.Interaction)] 2104 for vertex in vertices: 2105 for key, value in vertex['couplings'].items(): 2106 if value == coupling or value == '-%s' % coupling or coupling == '-%s' % value: 2107 vertex['couplings'][key] = self.get_new_coupling_name(\ 2108 main, coupling, value, coeff) 2109 2110 2111 2112 2113 # replace the coupling appearing in the particle counterterm 2114 particles_ct = [ pct for pct in self.coupling_pos[coupling] if 2115 isinstance(pct, tuple)] 2116 for pct in particles_ct: 2117 for key, value in pct[0]['counterterm'][pct[1]].items(): 2118 if value == coupling: 2119 pct[0]['counterterm'][pct[1]][key] = main
2120 2121 2122
2123 - def get_param_block(self):
2124 """return the list of block defined in the param_card""" 2125 2126 blocks = set([p.lhablock for p in self['parameters'][('external',)]]) 2127 return blocks
2128
2129 - def merge_iden_parameters(self, parameters, keep_external=False):
2130 """ merge the identical parameters given in argument. 2131 keep external force to keep the param_card untouched (up to comment)""" 2132 2133 logger_mod.debug('Parameters set to identical values: %s '% \ 2134 ', '.join(['%s*%s' % (f, obj.name.replace('mdl_','')) for (obj,f) in parameters])) 2135 2136 # Extract external parameters 2137 external_parameters = self['parameters'][('external',)] 2138 for i, (obj, factor) in enumerate(parameters): 2139 # Keeped intact the first one and store information 2140 if i == 0: 2141 obj.info = 'set of param :' + \ 2142 ', '.join([str(f)+'*'+param.name.replace('mdl_','') 2143 for (param, f) in parameters]) 2144 expr = obj.name 2145 continue 2146 # Add a Rule linked to the param_card 2147 if factor ==1: 2148 self.rule_card.add_identical(obj.lhablock.lower(), obj.lhacode, 2149 parameters[0][0].lhacode ) 2150 else: 2151 self.rule_card.add_opposite(obj.lhablock.lower(), obj.lhacode, 2152 parameters[0][0].lhacode ) 2153 obj_name = obj.name 2154 # delete the old parameters 2155 if not keep_external: 2156 external_parameters.remove(obj) 2157 elif obj.lhablock.upper() in ['MASS','DECAY']: 2158 external_parameters.remove(obj) 2159 else: 2160 obj.name = '' 2161 obj.info = 'MG5 will not use this value use instead %s*%s' %(factor,expr) 2162 # replace by the new one pointing of the first obj of the class 2163 new_param = base_objects.ModelVariable(obj_name, '%s*%s' %(factor, expr), 'real') 2164 self['parameters'][()].insert(0, new_param) 2165 2166 # For Mass-Width, we need also to replace the mass-width in the particles 2167 #This allows some optimization for multi-process. 2168 if parameters[0][0].lhablock in ['MASS','DECAY']: 2169 new_name = parameters[0][0].name 2170 if parameters[0][0].lhablock == 'MASS': 2171 arg = 'mass' 2172 else: 2173 arg = 'width' 2174 change_name = [p.name for (p,f) in parameters[1:]] 2175 [p.set(arg, new_name) for p in self['particle_dict'].values() 2176 if p[arg] in change_name]
2177
2178 - def remove_interactions(self, zero_couplings):
2179 """ remove the interactions and particle counterterms 2180 associated to couplings""" 2181 2182 2183 mod_vertex = [] 2184 mod_particle_ct = [] 2185 for coup in zero_couplings: 2186 # some coupling might be not related to any interactions 2187 if coup not in self.coupling_pos: 2188 continue 2189 2190 # Remove the corresponding interactions. 2191 2192 vertices = [ vert for vert in self.coupling_pos[coup] if 2193 isinstance(vert, base_objects.Interaction) ] 2194 for vertex in vertices: 2195 modify = False 2196 for key, coupling in vertex['couplings'].items(): 2197 if coupling in zero_couplings: 2198 modify=True 2199 del vertex['couplings'][key] 2200 elif coupling.startswith('-'): 2201 coupling = coupling[1:] 2202 if coupling in zero_couplings: 2203 modify=True 2204 del vertex['couplings'][key] 2205 2206 if modify: 2207 mod_vertex.append(vertex) 2208 2209 # Remove the corresponding particle counterterm 2210 particles_ct = [ pct for pct in self.coupling_pos[coup] if 2211 isinstance(pct, tuple)] 2212 for pct in particles_ct: 2213 modify = False 2214 for key, coupling in pct[0]['counterterm'][pct[1]].items(): 2215 if coupling in zero_couplings: 2216 modify=True 2217 del pct[0]['counterterm'][pct[1]][key] 2218 if modify: 2219 mod_particle_ct.append(pct) 2220 2221 # print useful log and clean the empty interaction 2222 for vertex in mod_vertex: 2223 part_name = [part['name'] for part in vertex['particles']] 2224 orders = ['%s=%s' % (order,value) for order,value in vertex['orders'].items()] 2225 2226 if not vertex['couplings']: 2227 logger_mod.debug('remove interactions: %s at order: %s' % \ 2228 (' '.join(part_name),', '.join(orders))) 2229 self['interactions'].remove(vertex) 2230 else: 2231 logger_mod.debug('modify interactions: %s at order: %s' % \ 2232 (' '.join(part_name),', '.join(orders))) 2233 2234 # print useful log and clean the empty counterterm values 2235 for pct in mod_particle_ct: 2236 part_name = pct[0]['name'] 2237 order = pct[1][0] 2238 loop_parts = ','.join(['('+','.join([\ 2239 self.get_particle(p)['name'] for p in part])+')' \ 2240 for part in pct[1][1]]) 2241 2242 if not pct[0]['counterterm'][pct[1]]: 2243 logger_mod.debug('remove counterterm of particle %s'%part_name+\ 2244 ' with loop particles (%s)'%loop_parts+\ 2245 ' perturbing order %s'%order) 2246 del pct[0]['counterterm'][pct[1]] 2247 else: 2248 logger_mod.debug('Modify counterterm of particle %s'%part_name+\ 2249 ' with loop particles (%s)'%loop_parts+\ 2250 ' perturbing order %s'%order) 2251 2252 return
2253
2254 - def remove_couplings(self, couplings):
2255 #clean the coupling list: 2256 for name, data in self['couplings'].items(): 2257 for coupling in data[:]: 2258 if coupling.name in couplings: 2259 data.remove(coupling)
2260 2261
2262 - def fix_parameter_values(self, zero_parameters, one_parameters, 2263 simplify=True, keep_external=False):
2264 """ Remove all instance of the parameters in the model and replace it by 2265 zero when needed.""" 2266 2267 2268 # treat specific cases for masses and width 2269 for particle in self['particles']: 2270 if particle['mass'] in zero_parameters: 2271 particle['mass'] = 'ZERO' 2272 if particle['width'] in zero_parameters: 2273 particle['width'] = 'ZERO' 2274 if particle['width'] in one_parameters: 2275 one_parameters.remove(particle['width']) 2276 2277 for pdg, particle in self['particle_dict'].items(): 2278 if particle['mass'] in zero_parameters: 2279 particle['mass'] = 'ZERO' 2280 if particle['width'] in zero_parameters: 2281 particle['width'] = 'ZERO' 2282 2283 2284 # Add a rule for zero/one parameter 2285 external_parameters = self['parameters'][('external',)] 2286 for param in external_parameters[:]: 2287 value = self['parameter_dict'][param.name] 2288 block = param.lhablock.lower() 2289 if value == 0: 2290 self.rule_card.add_zero(block, param.lhacode) 2291 elif value == 1: 2292 self.rule_card.add_one(block, param.lhacode) 2293 2294 special_parameters = zero_parameters + one_parameters 2295 2296 2297 2298 if simplify: 2299 # check if the parameters is still useful: 2300 re_str = '|'.join(special_parameters) 2301 if len(re_str) > 25000: # size limit on mac 2302 split = len(special_parameters) // 2 2303 re_str = ['|'.join(special_parameters[:split]), 2304 '|'.join(special_parameters[split:])] 2305 else: 2306 re_str = [ re_str ] 2307 used = set() 2308 for expr in re_str: 2309 re_pat = re.compile(r'''\b(%s)\b''' % expr) 2310 # check in coupling 2311 for name, coupling_list in self['couplings'].items(): 2312 for coupling in coupling_list: 2313 for use in re_pat.findall(coupling.expr): 2314 used.add(use) 2315 else: 2316 used = set([i for i in special_parameters if i]) 2317 2318 # simplify the regular expression 2319 re_str = '|'.join([param for param in special_parameters if param not in used]) 2320 if len(re_str) > 25000: # size limit on mac 2321 split = len(special_parameters) // 2 2322 re_str = ['|'.join(special_parameters[:split]), 2323 '|'.join(special_parameters[split:])] 2324 else: 2325 re_str = [ re_str ] 2326 for expr in re_str: 2327 re_pat = re.compile(r'''\b(%s)\b''' % expr) 2328 2329 param_info = {} 2330 # check in parameters 2331 for dep, param_list in self['parameters'].items(): 2332 for tag, parameter in enumerate(param_list): 2333 # update information concerning zero/one parameters 2334 if parameter.name in special_parameters: 2335 param_info[parameter.name]= {'dep': dep, 'tag': tag, 2336 'obj': parameter} 2337 continue 2338 2339 # Bypass all external parameter 2340 if isinstance(parameter, base_objects.ParamCardVariable): 2341 continue 2342 2343 if simplify: 2344 for use in re_pat.findall(parameter.expr): 2345 used.add(use) 2346 2347 # modify the object for those which are still used 2348 for param in used: 2349 if not param: 2350 continue 2351 data = self['parameters'][param_info[param]['dep']] 2352 data.remove(param_info[param]['obj']) 2353 tag = param_info[param]['tag'] 2354 data = self['parameters'][()] 2355 if param in zero_parameters: 2356 data.insert(0, base_objects.ModelVariable(param, '0.0', 'real')) 2357 else: 2358 data.insert(0, base_objects.ModelVariable(param, '1.0', 'real')) 2359 2360 # remove completely useless parameters 2361 for param in special_parameters: 2362 #by pass parameter still in use 2363 if param in used or \ 2364 (keep_external and param_info[param]['dep'] == ('external',)): 2365 logger_mod.debug('fix parameter value: %s' % param) 2366 continue 2367 logger_mod.debug('remove parameters: %s' % (param)) 2368 data = self['parameters'][param_info[param]['dep']] 2369 data.remove(param_info[param]['obj'])
2370
2371 - def optimise_interaction(self, interaction):
2372 2373 # we want to check if the same coupling (up to the sign) is used for two lorentz structure 2374 # for the same color structure. 2375 to_lor = {} 2376 for (color, lor), coup in interaction['couplings'].items(): 2377 abscoup, coeff = (coup[1:],-1) if coup.startswith('-') else (coup, 1) 2378 key = (color, abscoup) 2379 if key in to_lor: 2380 to_lor[key].append((lor,coeff)) 2381 else: 2382 to_lor[key] = [(lor,coeff)] 2383 2384 nb_reduce = [] 2385 optimize = False 2386 for key in to_lor: 2387 if len(to_lor[key]) >1: 2388 nb_reduce.append(len(to_lor[key])-1) 2389 optimize = True 2390 2391 if not optimize: 2392 return 2393 2394 if not hasattr(self, 'defined_lorentz_expr'): 2395 self.defined_lorentz_expr = {} 2396 self.lorentz_info = {} 2397 self.lorentz_combine = {} 2398 for lor in self.get('lorentz'): 2399 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name') 2400 self.lorentz_info[lor.get('name')] = lor #(lor.get('structure'), lor.get('spins')) 2401 2402 for key in to_lor: 2403 if len(to_lor[key]) == 1: 2404 continue 2405 names = ['u%s' % interaction['lorentz'][i[0]] if i[1] ==1 else \ 2406 'd%s' % interaction['lorentz'][i[0]] for i in to_lor[key]] 2407 2408 names.sort() 2409 2410 # get name of the new lorentz 2411 if tuple(names) in self.lorentz_combine: 2412 # already created new loretnz 2413 new_name = self.lorentz_combine[tuple(names)] 2414 else: 2415 new_name = self.add_merge_lorentz(names) 2416 2417 # remove the old couplings 2418 color, coup = key 2419 to_remove = [(color, lor[0]) for lor in to_lor[key]] 2420 for rm in to_remove: 2421 del interaction['couplings'][rm] 2422 2423 #add the lorentz structure to the interaction 2424 if new_name not in [l for l in interaction.get('lorentz')]: 2425 interaction.get('lorentz').append(new_name) 2426 2427 #find the associate index 2428 new_l = interaction.get('lorentz').index(new_name) 2429 # adding the new combination (color,lor) associate to this sum of structure 2430 interaction['couplings'][(color, new_l)] = coup
2431 2432 2433
2434 - def add_merge_lorentz(self, names):
2435 """add a lorentz structure which is the sume of the list given above""" 2436 2437 #create new_name 2438 ii = len(names[0]) 2439 while ii>1: 2440 #do not count the initial "u/d letter whcih indicates the sign" 2441 if not all(n[1:].startswith(names[0][1:ii]) for n in names[1:]): 2442 ii -=1 2443 else: 2444 base_name = names[0][1:ii] 2445 break 2446 else: 2447 base_name = 'LMER' 2448 i = 1 2449 while '%s%s' %(base_name, i) in self.lorentz_info: 2450 i +=1 2451 new_name = '%s%s' %(base_name, i) 2452 self.lorentz_combine[tuple(names)] = new_name 2453 2454 # load the associate lorentz expression 2455 new_struct = ' + '.join([self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('u')]) 2456 if any( n.startswith('d') for n in names ): 2457 new_struct += '-' + ' - '.join(['1.*(%s)' %self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('d')]) 2458 spins = self.lorentz_info[names[0][1:]].get('spins') 2459 new_lor = self.add_lorentz(new_name, spins, new_struct) 2460 self.lorentz_info[new_name] = new_lor 2461 2462 return new_name
2463
2464 - def add_lorentz(self, name, spin, struct):
2465 """adding lorentz structure to the current model""" 2466 new = self['lorentz'][0].__class__(name = name, 2467 spins = spin, 2468 structure = struct) 2469 self['lorentz'].append(new) 2470 self.create_lorentz_dict() 2471 2472 return None
2473