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