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