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