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