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