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