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   
  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 aloha.unitary_gauge: 816 particle.set(key, str(value[0])) 817 else: 818 particle.set(key, str(value[1])) 819 else: 820 particle.set(key, '') 821 else: 822 particle.set(key, value) 823 elif key == 'loop_particles': 824 loop_particles = value 825 elif key == 'counterterm': 826 counterterms = value 827 elif key.lower() not in ('ghostnumber','selfconjugate','goldstone', 828 'goldstoneboson','partial_widths', 829 'texname', 'antitexname', 'propagating', 'ghost' 830 ): 831 # add charge -we will check later if those are conserve 832 self.conservecharge.add(key) 833 particle.set(key,value, force=True) 834 835 if not hasattr(particle_info, 'propagator'): 836 nb_property += 1 837 if particle.get('spin') >= 3: 838 if particle.get('mass').lower() == 'zero': 839 particle.set('propagator', 0) 840 elif particle.get('spin') == 3 and not aloha.unitary_gauge: 841 particle.set('propagator', 0) 842 843 assert(10 == nb_property) #basic check that all the information is there 844 845 # Identify self conjugate particles 846 if particle_info.name == particle_info.antiname: 847 particle.set('self_antipart', True) 848 849 # Proceed only if we deal with a loop model and that this particle 850 # has wavefunction renormalization 851 if not self.perturbation_couplings or counterterms=={}: 852 self.particles.append(particle) 853 return 854 855 # Set here the 'counterterm' attribute to the particle. 856 # First we must change the couplings dictionary keys from the entry format 857 # (order1,order2,...,orderN,loop_particle#):LaurentSerie 858 # two a dictionary with format 859 # ('ORDER_OF_COUNTERTERM',((Particle_list_PDG))):{laurent_order:CTCouplingName} 860 particle_counterterms = {} 861 for key, counterterm in counterterms.items(): 862 # Makes sure this counterterm contributes at one-loop. 863 if len([1 for k in key[:-1] if k==1])==1 and \ 864 not any(k>1 for k in key[:-1]): 865 newParticleCountertermKey=[None,\ 866 # The line below is for loop UFO Model with the 'attribute' 867 # 'loop_particles' of the Particle objects to be defined with 868 # instances of the particle class. The new convention is to use 869 # pdg numbers instead. 870 # tuple([tuple([abs(part.pdg_code) for part in loop_parts]) for\ 871 tuple([tuple(loop_parts) for\ 872 loop_parts in loop_particles[key[-1]]])] 873 for i, order in enumerate(self.ufomodel.all_orders[:-1]): 874 if key[i]==1: 875 newParticleCountertermKey[0]=order.name 876 newCouplingName='UVWfct_'+particle_info.name+'_'+str(key[-1]) 877 particle_counterterms[tuple(newParticleCountertermKey)]=\ 878 dict([(key,newCouplingName+('' if key==0 else '_'+str(-key)+'eps'))\ 879 for key in counterterm]) 880 # We want to create the new coupling for this wavefunction 881 # renormalization. 882 self.ufomodel.object_library.Coupling(\ 883 name = newCouplingName, 884 value = counterterm, 885 order = {newParticleCountertermKey[0]:2}) 886 self.wavefunction_CT_couplings.append(self.ufomodel.all_couplings.pop()) 887 888 particle.set('counterterm',particle_counterterms) 889 self.particles.append(particle) 890 return
891
892 - def treat_couplings(self, couplings, all_CTparameters):
893 """ This function scan each coupling to see if it contains a CT parameter. 894 when it does, it changes its value to a dictionary with the CT parameter 895 changed to a new parameter for each pole and finite part. For instance, 896 the following coupling: 897 coupling.value = '2*(myCTParam1 + myParam*(myCTParam2 + myCTParam3)' 898 with CTparameters 899 myCTParam1 = {0: Something, -1: SomethingElse} 900 myCTParam2 = {0: OtherSomething } 901 myCTParam3 = {-1: YetOtherSomething } 902 would be turned into 903 coupling.value = {0: '2*(myCTParam1_FIN_ + myParam*(myCTParam2_FIN_ + ZERO)' 904 -1: '2*(myCTParam1_EPS_ + myParam*(ZERO + myCTParam2_EPS_)'} 905 906 all_CTParameter is the list of all CTParameters in the model""" 907 908 # First define a list of regular expressions for each CT parameter 909 # and put them in a dictionary whose keys are the CT parameter names 910 # and the values are a tuple with the substituting patter in the first 911 # entry and the list of substituting functions (one for each pole) 912 # as the second entry of this tuple. 913 CTparameter_patterns = {} 914 zero_substitution = lambda matchedObj: matchedObj.group('first')+\ 915 'ZERO'+matchedObj.group('second') 916 def function_factory(arg): 917 return lambda matchedObj: \ 918 matchedObj.group('first')+arg+matchedObj.group('second')
919 for CTparam in all_CTparameters: 920 pattern_finder = re.compile(r"(?P<first>\A|\*|\+|\-|\(|\s)(?P<name>"+ 921 CTparam.name+r")(?P<second>\Z|\*|\+|\-|\)|/|\\|\s)") 922 923 sub_functions = [None if CTparam.pole(pole)=='ZERO' else 924 function_factory('%s_%s_'%(CTparam.name,pole_dict[-pole])) 925 for pole in range(3)] 926 CTparameter_patterns[CTparam.name] = (pattern_finder,sub_functions) 927 928 times_zero = re.compile('\*\s*-?ZERO') 929 zero_times = re.compile('ZERO\s*(\*|\/)') 930 def is_expr_zero(expresson): 931 """ Checks whether a single term (involving only the operations 932 * or / is zero. """ 933 for term in expresson.split('-'): 934 for t in term.split('+'): 935 t = t.strip() 936 if t in ['ZERO','']: 937 continue 938 if not (times_zero.search(t) or zero_times.search(t)): 939 return False 940 return True
941 942 def find_parenthesis(expr): 943 end = expr.find(')') 944 if end == -1: 945 return None 946 start = expr.rfind('(',0,end+1) 947 if start ==-1: 948 raise InvalidModel,\ 949 'Parenthesis of expression %s are malformed'%expr 950 return [expr[:start],expr[start+1:end],expr[end+1:]] 951 952 start_parenthesis = re.compile(r".*\s*[\+\-\*\/\)\(]\s*$") 953 954 def is_value_zero(value): 955 """Check whether an expression like ((A+B)*ZERO+C)*ZERO is zero. 956 Only +,-,/,* operations are allowed and 'ZERO' is a tag for an 957 analytically zero quantity.""" 958 959 curr_value = value 960 parenthesis = find_parenthesis(curr_value) 961 while parenthesis: 962 # Allow the complexconjugate function 963 if parenthesis[0].endswith('complexconjugate'): 964 # Then simply remove it 965 parenthesis[0] = parenthesis[0][:-16] 966 if parenthesis[0]=='' or re.match(start_parenthesis, 967 parenthesis[0]): 968 if is_value_zero(parenthesis[1]): 969 new_parenthesis = 'ZERO' 970 else: 971 new_parenthesis = 'PARENTHESIS' 972 else: 973 new_parenthesis = '_FUNCTIONARGS' 974 curr_value = parenthesis[0]+new_parenthesis+parenthesis[2] 975 parenthesis = find_parenthesis(curr_value) 976 return is_expr_zero(curr_value) 977 978 def CTCoupling_pole(CTCoupling, pole): 979 """Compute the pole of the CTCoupling in two cases: 980 a) Its value is a dictionary, then just return the corresponding 981 entry in the dictionary. 982 b) It is expressed in terms of CTParameters which are themselves 983 dictionary so we want to substitute their expression to get 984 the value of the pole. In the current implementation, this is 985 just to see if the pole is zero or not. 986 """ 987 988 if isinstance(CTCoupling.value,dict): 989 if -pole in CTCoupling.value.keys(): 990 return CTCoupling.value[-pole], [], 0 991 else: 992 return 'ZERO', [], 0 993 994 new_expression = CTCoupling.value 995 CTparamNames = [] 996 n_CTparams = 0 997 for paramname, value in CTparameter_patterns.items(): 998 pattern = value[0] 999 # Keep track of which CT parameters enter in the definition of 1000 # which coupling. 1001 if not re.search(pattern,new_expression): 1002 continue 1003 n_CTparams += 1 1004 # If the contribution of this CTparam to this pole is non 1005 # zero then the substituting function is not None: 1006 if not value[1][pole] is None: 1007 CTparamNames.append('%s_%s_'%(paramname,pole_dict[-pole])) 1008 1009 substitute_function = zero_substitution if \ 1010 value[1][pole] is None else value[1][pole] 1011 new_expression = pattern.sub(substitute_function,new_expression) 1012 1013 # If no CTParam was found and we ask for a pole, then it can only 1014 # be zero. 1015 if pole!=0 and n_CTparams==0: 1016 return 'ZERO', [], n_CTparams 1017 1018 # Check if resulting expression is analytically zero or not. 1019 # Remember that when the value of a CT_coupling is not a dictionary 1020 # then the only operators allowed in the definition are +,-,*,/ 1021 # and each term added or subtracted must contain *exactly one* 1022 # CTParameter and never at the denominator. 1023 if n_CTparams > 0 and is_value_zero(new_expression): 1024 return 'ZERO', [], n_CTparams 1025 else: 1026 return new_expression, CTparamNames, n_CTparams 1027 1028 # For each coupling we substitute its value if necessary 1029 for coupl in couplings: 1030 new_value = {} 1031 for pole in range(0,3): 1032 expression, CTparamNames, n_CTparams = CTCoupling_pole(coupl, pole) 1033 # Make sure it uses CT parameters, otherwise do nothing 1034 if n_CTparams == 0: 1035 break 1036 elif expression!='ZERO': 1037 new_value[-pole] = expression 1038 couplname = coupl.name 1039 if pole!=0: 1040 couplname += "_%deps"%pole 1041 # Add the parameter dependency found to the dependency map 1042 # of the model being built. In principle, since we should 1043 # be building a loop model now, it should always have this 1044 # attribute defined, but it is better to make sure. 1045 if hasattr(self.model, 'map_CTcoup_CTparam'): 1046 self.model.map_CTcoup_CTparam[couplname] = CTparamNames 1047 1048 # Finally modify the value of this CTCoupling so that it is no 1049 # longer a string expression in terms of CTParameters but rather 1050 # a dictionary with the CTparameters replaced by their _FIN_ and 1051 # _EPS_ counterparts. 1052 # This is useful for the addCT_interaction() step. I will be reverted 1053 # right after the addCT_interaction() function so as to leave 1054 # the UFO intact, as it should. 1055 if new_value: 1056 coupl.old_value = coupl.value 1057 coupl.value = new_value 1058
1059 - def add_CTinteraction(self, interaction, color_info):
1060 """ Split this interaction in order to call add_interaction for 1061 interactions for each element of the loop_particles list. Also it 1062 is necessary to unfold here the contributions to the different laurent 1063 expansion orders of the couplings.""" 1064 1065 # Work on a local copy of the interaction provided 1066 interaction_info=copy.copy(interaction) 1067 1068 intType='' 1069 if interaction_info.type not in ['UV','UVloop','UVtree','UVmass','R2']: 1070 raise MadGraph5Error, 'MG5 only supports the following types of'+\ 1071 ' vertices, R2, UV and UVmass. %s is not in this list.'%interaction_info.type 1072 else: 1073 intType=interaction_info.type 1074 # If not specified and simply set to UV, guess the appropriate type 1075 if interaction_info.type=='UV': 1076 if len(interaction_info.particles)==2 and interaction_info.\ 1077 particles[0].name==interaction_info.particles[1].name: 1078 intType='UVmass' 1079 else: 1080 intType='UVloop' 1081 1082 # Make sure that if it is a UV mass renromalization counterterm it is 1083 # defined as such. 1084 # if len(intType)>2 and intType[:2]=='UV' and len(interaction_info.particles)==2 \ 1085 # and interaction_info.particles[0].name==interaction_info.particles[1].name: 1086 # intType='UVmass' 1087 1088 # Now we create a couplings dictionary for each element of the loop_particles list 1089 # and for each expansion order of the laurent serie in the coupling. 1090 # and for each coupling order 1091 # Format is new_couplings[loop_particles][laurent_order] and each element 1092 # is a couplings dictionary. 1093 order_to_interactions= {} 1094 # will contains the new coupling of form 1095 #new_couplings=[[{} for j in range(0,3)] for i in \ 1096 # range(0,max(1,len(interaction_info.loop_particles)))] 1097 # So sort all entries in the couplings dictionary to put them a the 1098 # correct place in new_couplings. 1099 for key, couplings in interaction_info.couplings.items(): 1100 if not isinstance(couplings, list): 1101 couplings = [couplings] 1102 for coupling in couplings: 1103 order = tuple(coupling.order.items()) 1104 if order not in order_to_interactions: 1105 order_to_interactions[order] = [ 1106 [{} for j in range(0,3)] for i in \ 1107 range(0,max(1,len(interaction_info.loop_particles)))] 1108 new_couplings = order_to_interactions[order] 1109 else: 1110 new_couplings = order_to_interactions[order] 1111 1112 for poleOrder in range(0,3): 1113 expression = coupling.pole(poleOrder) 1114 if expression!='ZERO': 1115 if poleOrder==2: 1116 raise InvalidModel, """ 1117 The CT coupling %s was found with a contribution to the double pole. 1118 This is either an error in the model or a parsing error in the function 'is_value_zero'. 1119 The expression of the non-zero double pole coupling is: 1120 %s 1121 """%(coupling.name,str(coupling.value)) 1122 # It is actually safer that the new coupling associated to 1123 # the interaction added is not a reference to an original 1124 # coupling in the ufo model. So copy.copy is right here. 1125 newCoupling = copy.copy(coupling) 1126 if poleOrder!=0: 1127 newCoupling.name=newCoupling.name+"_"+str(poleOrder)+"eps" 1128 newCoupling.value = expression 1129 # assign the CT parameter dependences 1130 #if hasattr(coupling,'CTparam_dependence') and \ 1131 # (-poleOrder in coupling.CTparam_dependence) and \ 1132 # coupling.CTparam_dependence[-poleOrder]: 1133 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 1134 #elif hasattr(newCoupling,'CTparam_dependence'): 1135 # delattr(newCoupling,"CTparam_dependence") 1136 new_couplings[key[2]][poleOrder][(key[0],key[1])] = newCoupling 1137 1138 for new_couplings in order_to_interactions.values(): 1139 # Now we can add an interaction for each. 1140 for i, all_couplings in enumerate(new_couplings): 1141 loop_particles=[[]] 1142 if len(interaction_info.loop_particles)>0: 1143 loop_particles=[[part.pdg_code for part in loop_parts] \ 1144 for loop_parts in interaction_info.loop_particles[i]] 1145 for poleOrder in range(0,3): 1146 if all_couplings[poleOrder]!={}: 1147 interaction_info.couplings=all_couplings[poleOrder] 1148 self.add_interaction(interaction_info, color_info,\ 1149 (intType if poleOrder==0 else (intType+str(poleOrder)+\ 1150 'eps')),loop_particles)
1151
1152 - def find_color_anti_color_rep(self, output=None):
1153 """find which color are in the 3/3bar states""" 1154 # method look at the 3 3bar 8 configuration. 1155 # If the color is T(3,2,1) and the interaction F1 F2 V 1156 # Then set F1 to anticolor (and F2 to color) 1157 # if this is T(3,1,2) set the opposite 1158 if not output: 1159 output = {} 1160 1161 for interaction_info in self.ufomodel.all_vertices: 1162 if len(interaction_info.particles) != 3: 1163 continue 1164 colors = [abs(p.color) for p in interaction_info.particles] 1165 if colors[:2] == [3,3]: 1166 if 'T(3,2,1)' in interaction_info.color: 1167 color, anticolor, other = interaction_info.particles 1168 elif 'T(3,1,2)' in interaction_info.color: 1169 anticolor, color, _ = interaction_info.particles 1170 elif 'Identity(1,2)' in interaction_info.color or \ 1171 'Identity(2,1)' in interaction_info.color: 1172 first, second, _ = interaction_info.particles 1173 if first.pdg_code in output: 1174 if output[first.pdg_code] == 3: 1175 color, anticolor = first, second 1176 else: 1177 color, anticolor = second, first 1178 elif second.pdg_code in output: 1179 if output[second.pdg_code] == 3: 1180 color, anticolor = second, first 1181 else: 1182 color, anticolor = first, second 1183 else: 1184 continue 1185 else: 1186 continue 1187 elif colors[1:] == [3,3]: 1188 if 'T(1,2,3)' in interaction_info.color: 1189 other, anticolor, color = interaction_info.particles 1190 elif 'T(1,3,2)' in interaction_info.color: 1191 other, color, anticolor = interaction_info.particles 1192 elif 'Identity(2,3)' in interaction_info.color or \ 1193 'Identity(3,2)' in interaction_info.color: 1194 _, first, second = interaction_info.particles 1195 if first.pdg_code in output: 1196 if output[first.pdg_code] == 3: 1197 color, anticolor = first, second 1198 else: 1199 color, anticolor = second, first 1200 elif second.pdg_code in output: 1201 if output[second.pdg_code] == 3: 1202 color, anticolor = second, first 1203 else: 1204 color, anticolor = first, second 1205 else: 1206 continue 1207 else: 1208 continue 1209 1210 elif colors.count(3) == 2: 1211 if 'T(2,3,1)' in interaction_info.color: 1212 color, other, anticolor = interaction_info.particles 1213 elif 'T(2,1,3)' in interaction_info.color: 1214 anticolor, other, color = interaction_info.particles 1215 elif 'Identity(1,3)' in interaction_info.color or \ 1216 'Identity(3,1)' in interaction_info.color: 1217 first, _, second = interaction_info.particles 1218 if first.pdg_code in output: 1219 if output[first.pdg_code] == 3: 1220 color, anticolor = first, second 1221 else: 1222 color, anticolor = second, first 1223 elif second.pdg_code in output: 1224 if output[second.pdg_code] == 3: 1225 color, anticolor = second, first 1226 else: 1227 color, anticolor = first, second 1228 else: 1229 continue 1230 else: 1231 continue 1232 else: 1233 continue 1234 1235 # Check/assign for the color particle 1236 if color.pdg_code in output: 1237 if output[color.pdg_code] == -3: 1238 raise InvalidModel, 'Particles %s is sometimes in the 3 and sometimes in the 3bar representations' \ 1239 % color.name 1240 else: 1241 output[color.pdg_code] = 3 1242 1243 # Check/assign for the anticolor particle 1244 if anticolor.pdg_code in output: 1245 if output[anticolor.pdg_code] == 3: 1246 raise InvalidModel, 'Particles %s is sometimes set as in the 3 and sometimes in the 3bar representations' \ 1247 % anticolor.name 1248 else: 1249 output[anticolor.pdg_code] = -3 1250 1251 return output
1252
1253 - def detect_incoming_fermion(self):
1254 """define which fermion should be incoming 1255 for that we look at F F~ X interactions 1256 """ 1257 self.incoming = [] 1258 self.outcoming = [] 1259 for interaction_info in self.ufomodel.all_vertices: 1260 # check if the interaction meet requirements: 1261 pdg = [p.pdg_code for p in interaction_info.particles if p.spin in [2,4]] 1262 if len(pdg) % 2: 1263 raise InvalidModel, 'Odd number of fermion in vertex: %s' % [p.pdg_code for p in interaction_info.particles] 1264 for i in range(0, len(pdg),2): 1265 if pdg[i] == - pdg[i+1]: 1266 if pdg[i] in self.outcoming: 1267 raise InvalidModel, '%s has not coherent incoming/outcoming status between interactions' %\ 1268 [p for p in interaction_info.particles if p.spin in [2,4]][i].name 1269 1270 elif not pdg[i] in self.incoming: 1271 self.incoming.append(pdg[i]) 1272 self.outcoming.append(pdg[i+1])
1273
1274 - def add_interaction(self, interaction_info, color_info, type='base', loop_particles=None):
1275 """add an interaction in the MG5 model. interaction_info is the 1276 UFO vertices information.""" 1277 # Import particles content: 1278 particles = [self.model.get_particle(particle.pdg_code) \ 1279 for particle in interaction_info.particles] 1280 if None in particles: 1281 # Interaction with a ghost/goldstone 1282 return 1283 particles = base_objects.ParticleList(particles) 1284 1285 # Import Lorentz content: 1286 lorentz = [helas for helas in interaction_info.lorentz] 1287 1288 # Check the coherence of the Fermion Flow 1289 nb_fermion = sum([ 1 if p.is_fermion() else 0 for p in particles]) 1290 try: 1291 if nb_fermion == 2: 1292 # Fermion Flow is suppose to be dealt by UFO 1293 [aloha_fct.check_flow_validity(helas.structure, nb_fermion) \ 1294 for helas in interaction_info.lorentz 1295 if helas.name not in self.checked_lor] 1296 self.checked_lor.update(set([helas.name for helas in interaction_info.lorentz])) 1297 elif nb_fermion: 1298 if any(p.selfconjugate for p in interaction_info.particles if p.spin % 2 == 0): 1299 text = "Majorana can not be dealt in 4/6/... fermion interactions" 1300 raise InvalidModel, text 1301 except aloha_fct.WrongFermionFlow, error: 1302 text = 'Fermion Flow error for interactions %s: %s: %s\n %s' % \ 1303 (', '.join([p.name for p in interaction_info.particles]), 1304 helas.name, helas.structure, error) 1305 raise InvalidModel, text 1306 1307 1308 1309 1310 # Now consider the name only 1311 lorentz = [helas.name for helas in lorentz] 1312 # Import color information: 1313 colors = [self.treat_color(color_obj, interaction_info, color_info) 1314 for color_obj in interaction_info.color] 1315 1316 1317 order_to_int={} 1318 1319 for key, couplings in interaction_info.couplings.items(): 1320 if not isinstance(couplings, list): 1321 couplings = [couplings] 1322 if interaction_info.lorentz[key[1]].name not in lorentz: 1323 continue 1324 # get the sign for the coupling (if we need to adapt the flow) 1325 if nb_fermion > 2: 1326 flow = aloha_fct.get_fermion_flow(interaction_info.lorentz[key[1]].structure, 1327 nb_fermion) 1328 coupling_sign = self.get_sign_flow(flow, nb_fermion) 1329 else: 1330 coupling_sign = '' 1331 for coupling in couplings: 1332 order = tuple(coupling.order.items()) 1333 1334 if '1' in coupling.order: 1335 raise InvalidModel, '''Some couplings have \'1\' order. 1336 This is not allowed in MG. 1337 Please defines an additional coupling to your model''' 1338 # check that gluon emission from quark are QCD tagged 1339 if 21 in [particle.pdg_code for particle in interaction_info.particles] and\ 1340 'QCD' not in coupling.order: 1341 col = [par.get('color') for par in particles] 1342 if 1 not in col: 1343 self.non_qcd_gluon_emission +=1 1344 1345 if order in order_to_int: 1346 order_to_int[order].get('couplings')[key] = '%s%s' % \ 1347 (coupling_sign,coupling.name) 1348 else: 1349 # Initialize a new interaction with a new id tag 1350 interaction = base_objects.Interaction({'id':len(self.interactions)+1}) 1351 interaction.set('particles', particles) 1352 interaction.set('lorentz', lorentz) 1353 interaction.set('couplings', {key: 1354 '%s%s' %(coupling_sign,coupling.name)}) 1355 interaction.set('orders', coupling.order) 1356 interaction.set('color', colors) 1357 interaction.set('type', type) 1358 interaction.set('loop_particles', loop_particles) 1359 order_to_int[order] = interaction 1360 # add to the interactions 1361 self.interactions.append(interaction) 1362 1363 1364 # check if this interaction conserve the charge defined 1365 # if type=='base': 1366 for charge in list(self.conservecharge): #duplicate to allow modification 1367 total = 0 1368 for part in interaction_info.particles: 1369 try: 1370 total += getattr(part, charge) 1371 except AttributeError: 1372 pass 1373 if abs(total) > 1e-12: 1374 logger.info('The model has interaction violating the charge: %s' % charge) 1375 self.conservecharge.discard(charge) 1376 1377 1378
1379 - def get_sign_flow(self, flow, nb_fermion):
1380 """ensure that the flow of particles/lorentz are coherent with flow 1381 and return a correct version if needed""" 1382 1383 if not flow or nb_fermion < 4: 1384 return '' 1385 1386 expected = {} 1387 for i in range(nb_fermion//2): 1388 expected[i+1] = i+2 1389 1390 if flow == expected: 1391 return '' 1392 1393 switch = {} 1394 for i in range(1, nb_fermion+1): 1395 if not i in flow: 1396 continue 1397 switch[i] = len(switch) 1398 switch[flow[i]] = len(switch) 1399 1400 # compute the sign of the permutation 1401 sign = 1 1402 done = [] 1403 1404 # make a list of consecutive number which correspond to the new 1405 # order of the particles in the new list. 1406 new_order = [] 1407 for id in range(nb_fermion): # id is the position in the particles order (starts 0) 1408 nid = switch[id+1]-1 # nid is the position in the new_particles 1409 #order (starts 0) 1410 new_order.append(nid) 1411 1412 # compute the sign: 1413 sign =1 1414 for k in range(len(new_order)-1): 1415 for l in range(k+1,len(new_order)): 1416 if new_order[l] < new_order[k]: 1417 sign *= -1 1418 1419 return '' if sign ==1 else '-'
1420
1421 - def add_lorentz(self, name, spins , expr, formfact=None):
1422 """ Add a Lorentz expression which is not present in the UFO """ 1423 1424 logger.debug('MG5 converter defines %s to %s', name, expr) 1425 assert name not in [l.name for l in self.model['lorentz']] 1426 with misc.TMP_variable(self.ufomodel.object_library, 'all_lorentz', 1427 self.model['lorentz']): 1428 new = self.model['lorentz'][0].__class__(name = name, 1429 spins = spins, 1430 structure = expr) 1431 if formfact: 1432 new.formfactors = formfact 1433 1434 assert name in [l.name for l in self.model['lorentz']] 1435 assert name not in [l.name for l in self.ufomodel.all_lorentz] 1436 #self.model['lorentz'].append(new) # already done by above command 1437 self.model.create_lorentz_dict() 1438 return new
1439 1440 _pat_T = re.compile(r'T\((?P<first>\d*),(?P<second>\d*)\)') 1441 _pat_id = re.compile(r'Identity\((?P<first>\d*),(?P<second>\d*)\)') 1442
1443 - def treat_color(self, data_string, interaction_info, color_info):
1444 """ convert the string to ColorString""" 1445 1446 #original = copy.copy(data_string) 1447 #data_string = p.sub('color.T(\g<first>,\g<second>)', data_string) 1448 1449 1450 output = [] 1451 factor = 1 1452 for term in data_string.split('*'): 1453 pattern = self._pat_id.search(term) 1454 if pattern: 1455 particle = interaction_info.particles[int(pattern.group('first'))-1] 1456 particle2 = interaction_info.particles[int(pattern.group('second'))-1] 1457 if particle.color == particle2.color and particle.color in [-6, 6]: 1458 error_msg = 'UFO model have inconsistency in the format:\n' 1459 error_msg += 'interactions for particles %s has color information %s\n' 1460 error_msg += ' but both fermion are in the same representation %s' 1461 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1462 if particle.color == particle2.color and particle.color in [-3, 3]: 1463 if particle.pdg_code in color_info and particle2.pdg_code in color_info: 1464 if color_info[particle.pdg_code] == color_info[particle2.pdg_code]: 1465 error_msg = 'UFO model have inconsistency in the format:\n' 1466 error_msg += 'interactions for particles %s has color information %s\n' 1467 error_msg += ' but both fermion are in the same representation %s' 1468 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1469 elif particle.pdg_code in color_info: 1470 color_info[particle2.pdg_code] = -particle.pdg_code 1471 elif particle2.pdg_code in color_info: 1472 color_info[particle.pdg_code] = -particle2.pdg_code 1473 else: 1474 error_msg = 'UFO model have inconsistency in the format:\n' 1475 error_msg += 'interactions for particles %s has color information %s\n' 1476 error_msg += ' but both fermion are in the same representation %s' 1477 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1478 1479 1480 if particle.color == 6: 1481 output.append(self._pat_id.sub('color.T6(\g<first>,\g<second>)', term)) 1482 elif particle.color == -6 : 1483 output.append(self._pat_id.sub('color.T6(\g<second>,\g<first>)', term)) 1484 elif particle.color == 8: 1485 output.append(self._pat_id.sub('color.Tr(\g<first>,\g<second>)', term)) 1486 factor *= 2 1487 elif particle.color in [-3,3]: 1488 if particle.pdg_code not in color_info: 1489 #try to find it one more time 3 -3 1 might help 1490 logger.debug('fail to find 3/3bar representation: Retry to find it') 1491 color_info = self.find_color_anti_color_rep(color_info) 1492 if particle.pdg_code not in color_info: 1493 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle.name) 1494 color_info[particle.pdg_code] = particle.color 1495 else: 1496 logger.debug('succeed') 1497 if particle2.pdg_code not in color_info: 1498 #try to find it one more time 3 -3 1 might help 1499 logger.debug('fail to find 3/3bar representation: Retry to find it') 1500 color_info = self.find_color_anti_color_rep(color_info) 1501 if particle2.pdg_code not in color_info: 1502 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle2.name) 1503 color_info[particle2.pdg_code] = particle2.color 1504 else: 1505 logger.debug('succeed') 1506 1507 if color_info[particle.pdg_code] == 3 : 1508 output.append(self._pat_id.sub('color.T(\g<second>,\g<first>)', term)) 1509 elif color_info[particle.pdg_code] == -3: 1510 output.append(self._pat_id.sub('color.T(\g<first>,\g<second>)', term)) 1511 else: 1512 raise MadGraph5Error, \ 1513 "Unknown use of Identity for particle with color %d" \ 1514 % particle.color 1515 else: 1516 output.append(term) 1517 data_string = '*'.join(output) 1518 1519 # Change convention for summed indices 1520 p = re.compile(r'\'\w(?P<number>\d+)\'') 1521 data_string = p.sub('-\g<number>', data_string) 1522 1523 # Shift indices by -1 1524 new_indices = {} 1525 new_indices = dict([(j,i) for (i,j) in \ 1526 enumerate(range(1, 1527 len(interaction_info.particles)+1))]) 1528 1529 1530 output = data_string.split('*') 1531 output = color.ColorString([eval(data) \ 1532 for data in output if data !='1']) 1533 output.coeff = fractions.Fraction(factor) 1534 for col_obj in output: 1535 col_obj.replace_indices(new_indices) 1536 1537 return output
1538
1539 -class OrganizeModelExpression:
1540 """Organize the couplings/parameters of a model""" 1541 1542 track_dependant = ['aS','aEWM1','MU_R'] # list of variable from which we track 1543 #dependencies those variables should be define 1544 #as external parameters 1545 1546 # regular expression to shorten the expressions 1547 complex_number = re.compile(r'''complex\((?P<real>[^,\(\)]+),(?P<imag>[^,\(\)]+)\)''') 1548 expo_expr = re.compile(r'''(?P<expr>[\w.]+)\s*\*\*\s*(?P<expo>[+-]?[\d.]+)''') 1549 cmath_expr = re.compile(r'''cmath.(?P<operation>\w+)\((?P<expr>\w+)\)''') 1550 #operation is usualy sqrt / sin / cos / tan 1551 conj_expr = re.compile(r'''complexconjugate\((?P<expr>\w+)\)''') 1552 1553 #RE expression for is_event_dependent 1554 separator = re.compile(r'''[+,\-*/()\s]*''') 1555 1556
1557 - def __init__(self, model):
1558 1559 self.model = model # UFOMODEL 1560 self.perturbation_couplings = {} 1561 try: 1562 for order in model.all_orders: # Check if it is a loop model or not 1563 if(order.perturbative_expansion>0): 1564 self.perturbation_couplings[order.name]=order.perturbative_expansion 1565 except AttributeError: 1566 pass 1567 self.params = {} # depend on -> ModelVariable 1568 self.couplings = {} # depend on -> ModelVariable 1569 self.all_expr = {} # variable_name -> ModelVariable
1570
1571 - def main(self, additional_couplings = []):
1572 """Launch the actual computation and return the associate 1573 params/couplings. Possibly consider additional_couplings in addition 1574 to those defined in the UFO model attribute all_couplings """ 1575 1576 additional_params = [] 1577 if hasattr(self.model,'all_CTparameters'): 1578 additional_params = self.get_additional_CTparameters() 1579 1580 self.analyze_parameters(additional_params = additional_params) 1581 self.analyze_couplings(additional_couplings = additional_couplings) 1582 1583 # Finally revert the possible modifications done by treat_couplings() 1584 if hasattr(self.model,'all_CTparameters'): 1585 self.revert_CTCoupling_modifications() 1586 1587 return self.params, self.couplings
1588
1590 """ Finally revert the possible modifications done by treat_couplings() 1591 in UFOMG5Converter which were useful for the add_CTinteraction() in 1592 particular. This modification consisted in expanding the value of a 1593 CTCoupling which consisted in an expression in terms of a CTParam to 1594 its corresponding dictionary (e.g 1595 CTCoupling.value = 2*CTParam -> 1596 CTCoupling.value = {-1: 2*CTParam_1EPS_, 0: 2*CTParam_FIN_} 1597 for example if CTParam had a non-zero finite and single pole.""" 1598 1599 for coupl in self.model.all_couplings: 1600 if hasattr(coupl,'old_value'): 1601 coupl.value = coupl.old_value 1602 del(coupl.old_value)
1603
1605 """ For each CTparameter split it into spimple parameter for each pole 1606 and the finite part if not zero.""" 1607 1608 additional_params = [] 1609 for CTparam in self.model.all_CTparameters: 1610 for pole in range(3): 1611 if CTparam.pole(pole) != 'ZERO': 1612 CTparam_piece = copy.copy(CTparam) 1613 CTparam_piece.name = '%s_%s_'%(CTparam.name,pole_dict[-pole]) 1614 CTparam_piece.nature = 'internal' 1615 CTparam_piece.type = CTparam.type 1616 CTparam_piece.value = CTparam.pole(pole) 1617 CTparam_piece.texname = '%s_{%s}'%\ 1618 (CTparam.texname,pole_dict[-pole]) 1619 additional_params.append(CTparam_piece) 1620 return additional_params
1621
1622 - def analyze_parameters(self, additional_params=[]):
1623 """ separate the parameters needed to be recomputed events by events and 1624 the others""" 1625 # in order to match in Gmu scheme 1626 # test whether aEWM1 is the external or not 1627 # if not, take Gf as the track_dependant variable 1628 present_aEWM1 = any(param.name == 'aEWM1' for param in 1629 self.model.all_parameters if param.nature == 'external') 1630 1631 if not present_aEWM1: 1632 self.track_dependant = ['aS','Gf','MU_R'] 1633 1634 for param in self.model.all_parameters+additional_params: 1635 if param.nature == 'external': 1636 parameter = base_objects.ParamCardVariable(param.name, param.value, \ 1637 param.lhablock, param.lhacode) 1638 1639 else: 1640 expr = self.shorten_expr(param.value) 1641 depend_on = self.find_dependencies(expr) 1642 parameter = base_objects.ModelVariable(param.name, expr, param.type, depend_on) 1643 1644 self.add_parameter(parameter)
1645
1646 - def add_parameter(self, parameter):
1647 """ add consistently the parameter in params and all_expr. 1648 avoid duplication """ 1649 1650 assert isinstance(parameter, base_objects.ModelVariable) 1651 1652 if parameter.name in self.all_expr: 1653 return 1654 1655 self.all_expr[parameter.name] = parameter 1656 try: 1657 self.params[parameter.depend].append(parameter) 1658 except: 1659 self.params[parameter.depend] = [parameter]
1660
1661 - def add_coupling(self, coupling):
1662 """ add consistently the coupling in couplings and all_expr. 1663 avoid duplication """ 1664 1665 assert isinstance(coupling, base_objects.ModelVariable) 1666 1667 if coupling.name in self.all_expr: 1668 return 1669 self.all_expr[coupling.value] = coupling 1670 try: 1671 self.coupling[coupling.depend].append(coupling) 1672 except: 1673 self.coupling[coupling.depend] = [coupling]
1674
1675 - def analyze_couplings(self,additional_couplings=[]):
1676 """creates the shortcut for all special function/parameter 1677 separate the couplings dependent of track variables of the others""" 1678 1679 # For loop models, make sure that all couplings with dictionary values 1680 # are turned into set of couplings, one for each pole and finite part. 1681 if self.perturbation_couplings: 1682 couplings_list=[] 1683 for coupling in self.model.all_couplings + additional_couplings: 1684 if not isinstance(coupling.value,dict): 1685 couplings_list.append(coupling) 1686 else: 1687 for poleOrder in range(0,3): 1688 if coupling.pole(poleOrder)!='ZERO': 1689 newCoupling=copy.copy(coupling) 1690 if poleOrder!=0: 1691 newCoupling.name += "_%deps"%poleOrder 1692 newCoupling.value=coupling.pole(poleOrder) 1693 # assign the CT parameter dependences 1694 # if hasattr(coupling,'CTparam_dependence') and \ 1695 # (-poleOrder in coupling.CTparam_dependence) and \ 1696 # coupling.CTparam_dependence[-poleOrder]: 1697 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 1698 # elif hasattr(newCoupling,'CTparam_dependence'): 1699 # delattr(newCoupling,"CTparam_dependence") 1700 couplings_list.append(newCoupling) 1701 else: 1702 couplings_list = self.model.all_couplings + additional_couplings 1703 couplings_list = [c for c in couplings_list if not isinstance(c.value, dict)] 1704 1705 for coupling in couplings_list: 1706 # shorten expression, find dependencies, create short object 1707 expr = self.shorten_expr(coupling.value) 1708 depend_on = self.find_dependencies(expr) 1709 parameter = base_objects.ModelVariable(coupling.name, expr, 'complex', depend_on) 1710 # Add consistently in the couplings/all_expr 1711 try: 1712 self.couplings[depend_on].append(parameter) 1713 except KeyError: 1714 self.couplings[depend_on] = [parameter] 1715 self.all_expr[coupling.value] = parameter
1716
1717 - def find_dependencies(self, expr):
1718 """check if an expression should be evaluated points by points or not 1719 """ 1720 depend_on = set() 1721 1722 # Treat predefined result 1723 #if name in self.track_dependant: 1724 # return tuple() 1725 1726 # Split the different part of the expression in order to say if a 1727 #subexpression is dependent of one of tracked variable 1728 expr = self.separator.split(expr) 1729 1730 # look for each subexpression 1731 for subexpr in expr: 1732 if subexpr in self.track_dependant: 1733 depend_on.add(subexpr) 1734 1735 elif subexpr in self.all_expr and self.all_expr[subexpr].depend: 1736 [depend_on.add(value) for value in self.all_expr[subexpr].depend 1737 if self.all_expr[subexpr].depend != ('external',)] 1738 if depend_on: 1739 return tuple(depend_on) 1740 else: 1741 return tuple()
1742 1743
1744 - def shorten_expr(self, expr):
1745 """ apply the rules of contraction and fullfill 1746 self.params with dependent part""" 1747 try: 1748 expr = self.complex_number.sub(self.shorten_complex, expr) 1749 expr = self.expo_expr.sub(self.shorten_expo, expr) 1750 expr = self.cmath_expr.sub(self.shorten_cmath, expr) 1751 expr = self.conj_expr.sub(self.shorten_conjugate, expr) 1752 except Exception: 1753 logger.critical("fail to handle expression: %s, type()=%s", expr,type(expr)) 1754 raise 1755 return expr
1756 1757
1758 - def shorten_complex(self, matchobj):
1759 """add the short expression, and return the nice string associate""" 1760 1761 float_real = float(eval(matchobj.group('real'))) 1762 float_imag = float(eval(matchobj.group('imag'))) 1763 if float_real == 0 and float_imag ==1: 1764 new_param = base_objects.ModelVariable('complexi', 'complex(0,1)', 'complex') 1765 self.add_parameter(new_param) 1766 return 'complexi' 1767 else: 1768 return 'complex(%s, %s)' % (matchobj.group('real'), matchobj.group('imag'))
1769 1770
1771 - def shorten_expo(self, matchobj):
1772 """add the short expression, and return the nice string associate""" 1773 1774 expr = matchobj.group('expr') 1775 exponent = matchobj.group('expo') 1776 new_exponent = exponent.replace('.','_').replace('+','').replace('-','_m_') 1777 output = '%s__exp__%s' % (expr, new_exponent) 1778 old_expr = '%s**%s' % (expr,exponent) 1779 1780 if expr.startswith('cmath'): 1781 return old_expr 1782 1783 if expr.isdigit(): 1784 output = 'nb__' + output #prevent to start with a number 1785 new_param = base_objects.ModelVariable(output, old_expr,'real') 1786 else: 1787 depend_on = self.find_dependencies(expr) 1788 type = self.search_type(expr) 1789 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1790 self.add_parameter(new_param) 1791 return output
1792
1793 - def shorten_cmath(self, matchobj):
1794 """add the short expression, and return the nice string associate""" 1795 1796 expr = matchobj.group('expr') 1797 operation = matchobj.group('operation') 1798 output = '%s__%s' % (operation, expr) 1799 old_expr = ' cmath.%s(%s) ' % (operation, expr) 1800 if expr.isdigit(): 1801 new_param = base_objects.ModelVariable(output, old_expr , 'real') 1802 else: 1803 depend_on = self.find_dependencies(expr) 1804 type = self.search_type(expr) 1805 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1806 self.add_parameter(new_param) 1807 1808 return output
1809
1810 - def shorten_conjugate(self, matchobj):
1811 """add the short expression, and retrun the nice string associate""" 1812 1813 expr = matchobj.group('expr') 1814 output = 'conjg__%s' % (expr) 1815 old_expr = ' complexconjugate(%s) ' % expr 1816 depend_on = self.find_dependencies(expr) 1817 type = 'complex' 1818 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1819 self.add_parameter(new_param) 1820 1821 return output
1822 1823 1824
1825 - def search_type(self, expr, dep=''):
1826 """return the type associate to the expression if define""" 1827 1828 try: 1829 return self.all_expr[expr].type 1830 except: 1831 return 'complex'
1832
1833 -class RestrictModel(model_reader.ModelReader):
1834 """ A class for restricting a model for a given param_card. 1835 rules applied: 1836 - Vertex with zero couplings are throw away 1837 - external parameter with zero/one input are changed into internal parameter. 1838 - identical coupling/mass/width are replace in the model by a unique one 1839 """ 1840
1841 - def default_setup(self):
1842 """define default value""" 1843 self.del_coup = [] 1844 super(RestrictModel, self).default_setup() 1845 self.rule_card = check_param_card.ParamCardRule() 1846 self.restrict_card = None 1847 self.coupling_order_dict ={}
1848
1849 - def restrict_model(self, param_card, rm_parameter=True, keep_external=False, 1850 complex_mass_scheme=None):
1851 """apply the model restriction following param_card. 1852 rm_parameter defines if the Zero/one parameter are removed or not from 1853 the model. 1854 keep_external if the param_card need to be kept intact 1855 """ 1856 1857 if self.get('name') == "mssm" and not keep_external: 1858 raise Exception 1859 1860 self.restrict_card = param_card 1861 # Reset particle dict to ensure synchronized particles and interactions 1862 self.set('particles', self.get('particles')) 1863 1864 # compute the value of all parameters 1865 # Get the list of definition of model functions, parameter values. 1866 model_definitions = self.set_parameters_and_couplings(param_card, 1867 complex_mass_scheme=complex_mass_scheme) 1868 1869 # Simplify conditional statements 1870 logger.debug('Simplifying conditional expressions') 1871 modified_params, modified_couplings = \ 1872 self.detect_conditional_statements_simplifications(model_definitions) 1873 1874 # Apply simplifications 1875 self.apply_conditional_simplifications(modified_params, modified_couplings) 1876 1877 # associate to each couplings the associated vertex: def self.coupling_pos 1878 self.locate_coupling() 1879 # deal with couplings 1880 zero_couplings, iden_couplings = self.detect_identical_couplings() 1881 1882 # remove the out-dated interactions 1883 self.remove_interactions(zero_couplings) 1884 1885 # replace in interactions identical couplings 1886 for iden_coups in iden_couplings: 1887 self.merge_iden_couplings(iden_coups) 1888 1889 # remove zero couplings and other pointless couplings 1890 self.del_coup += zero_couplings 1891 self.remove_couplings(self.del_coup) 1892 1893 # modify interaction to avoid to have identical coupling with different lorentz 1894 for interaction in list(self.get('interactions')): 1895 self.optimise_interaction(interaction) 1896 1897 # deal with parameters 1898 parameters = self.detect_special_parameters() 1899 self.fix_parameter_values(*parameters, simplify=rm_parameter, 1900 keep_external=keep_external) 1901 1902 # deal with identical parameters 1903 if not keep_external: 1904 iden_parameters = self.detect_identical_parameters() 1905 for iden_param in iden_parameters: 1906 self.merge_iden_parameters(iden_param) 1907 1908 iden_parameters = self.detect_identical_parameters() 1909 for iden_param in iden_parameters: 1910 self.merge_iden_parameters(iden_param, keep_external) 1911 1912 # change value of default parameter if they have special value: 1913 # 9.999999e-1 -> 1.0 1914 # 0.000001e-99 -> 0 Those value are used to avoid restriction 1915 for name, value in self['parameter_dict'].items(): 1916 if value == 9.999999e-1: 1917 self['parameter_dict'][name] = 1 1918 elif value == 0.000001e-99: 1919 self['parameter_dict'][name] = 0
1920 1921
1922 - def locate_coupling(self):
1923 """ create a dict couplings_name -> vertex or (particle, counterterm_key) """ 1924 1925 self.coupling_pos = {} 1926 for vertex in self['interactions']: 1927 for key, coupling in vertex['couplings'].items(): 1928 if coupling.startswith('-'): 1929 coupling = coupling[1:] 1930 if coupling in self.coupling_pos: 1931 if vertex not in self.coupling_pos[coupling]: 1932 self.coupling_pos[coupling].append(vertex) 1933 else: 1934 self.coupling_pos[coupling] = [vertex] 1935 1936 for particle in self['particles']: 1937 for key, coupling_dict in particle['counterterm'].items(): 1938 for LaurentOrder, coupling in coupling_dict.items(): 1939 if coupling in self.coupling_pos: 1940 if (particle,key) not in self.coupling_pos[coupling]: 1941 self.coupling_pos[coupling].append((particle,key)) 1942 else: 1943 self.coupling_pos[coupling] = [(particle,key)] 1944 1945 return self.coupling_pos
1946
1947 - def detect_identical_couplings(self, strict_zero=False):
1948 """return a list with the name of all vanishing couplings""" 1949 1950 dict_value_coupling = {} 1951 iden_key = set() 1952 zero_coupling = [] 1953 iden_coupling = [] 1954 1955 for name, value in self['coupling_dict'].items(): 1956 if value == 0: 1957 zero_coupling.append(name) 1958 continue 1959 elif not strict_zero and abs(value) < 1e-13: 1960 logger.debug('coupling with small value %s: %s treated as zero' % 1961 (name, value)) 1962 zero_coupling.append(name) 1963 continue 1964 elif not strict_zero and abs(value) < 1e-10: 1965 return self.detect_identical_couplings(strict_zero=True) 1966 1967 1968 if value in dict_value_coupling or -1*value in dict_value_coupling: 1969 if value in dict_value_coupling: 1970 iden_key.add(value) 1971 dict_value_coupling[value].append((name,1)) 1972 else: 1973 iden_key.add(-1*value) 1974 dict_value_coupling[-1*value].append((name,-1)) 1975 else: 1976 dict_value_coupling[value] = [(name,1)] 1977 for key in iden_key: 1978 tmp = [] 1979 if key in dict_value_coupling: 1980 tmp += dict_value_coupling[key] 1981 elif -1*key in dict_value_coupling: 1982 tmp += dict_value_coupling[-1*key] 1983 assert tmp 1984 1985 #ensure that all coupling have the same coupling order. 1986 ords = [self.get_coupling_order(k) for k,c in tmp] 1987 coup_by_ord = collections.defaultdict(list) 1988 for o,t in zip(ords, tmp): 1989 coup_by_ord[str(o)].append(t) 1990 # add the remaining identical 1991 for tmp3 in coup_by_ord.values(): 1992 if len(tmp3) > 1: 1993 if tmp3[0][1] == -1: #ensure that the first coupling has positif value 1994 tmp3 = [(t0,-t1) for t0, t1 in tmp3] 1995 iden_coupling.append(tmp3) 1996 1997 1998 1999 2000 return zero_coupling, iden_coupling
2001
2002 - def get_coupling_order(self, cname):
2003 """return the coupling order associated to a coupling """ 2004 2005 if cname in self.coupling_order_dict: 2006 return self.coupling_order_dict[cname] 2007 2008 for v in self['interactions']: 2009 for c in v['couplings'].values(): 2010 self.coupling_order_dict[c] = v['orders'] 2011 2012 if cname not in self.coupling_order_dict: 2013 self.coupling_order_dict[cname] = None 2014 #can happen when some vertex are discarded due to ghost/... 2015 2016 2017 return self.coupling_order_dict[cname]
2018 2019 2020
2021 - def detect_special_parameters(self):
2022 """ return the list of (name of) parameter which are zero """ 2023 2024 null_parameters = [] 2025 one_parameters = [] 2026 for name, value in self['parameter_dict'].items(): 2027 if value == 0 and name != 'ZERO': 2028 null_parameters.append(name) 2029 elif value == 1: 2030 one_parameters.append(name) 2031 2032 return null_parameters, one_parameters
2033
2034 - def apply_conditional_simplifications(self, modified_params, 2035 modified_couplings):
2036 """ Apply the conditional statement simplifications for parameters and 2037 couplings detected by 'simplify_conditional_statements'. 2038 modified_params (modified_couplings) are list of tuples (a,b) with a 2039 parameter (resp. coupling) instance and b is the simplified expression.""" 2040 2041 if modified_params: 2042 logger.debug("Conditional expressions are simplified for parameters:") 2043 logger.debug(",".join("%s"%param[0].name for param in modified_params)) 2044 for param, new_expr in modified_params: 2045 param.expr = new_expr 2046 2047 if modified_couplings: 2048 logger.debug("Conditional expressions are simplified for couplings:") 2049 logger.debug(",".join("%s"%coupl[0].name for coupl in modified_couplings)) 2050 for coupl, new_expr in modified_couplings: 2051 coupl.expr = new_expr
2052
2053 - def detect_conditional_statements_simplifications(self, model_definitions, 2054 objects=['couplings','parameters']):
2055 """ Simplifies the 'if' statements in the pythonic UFO expressions 2056 of parameters using the default variables specified in the restrict card. 2057 It returns a list of objects (parameters or couplings) and the new 2058 expression that they should take. Model definitions include all definitons 2059 of the model functions and parameters.""" 2060 2061 param_modifications = [] 2062 coupl_modifications = [] 2063 ifparser = parsers.UFOExpressionParserPythonIF(model_definitions) 2064 2065 start_param = time.time() 2066 if 'parameters' in objects: 2067 for dependences, param_list in self['parameters'].items(): 2068 if 'external' in dependences: 2069 continue 2070 for param in param_list: 2071 new_expr, n_changes = ifparser.parse(param.expr) 2072 if n_changes > 0: 2073 param_modifications.append((param, new_expr)) 2074 2075 end_param = time.time() 2076 2077 if 'couplings' in objects: 2078 for dependences, coupl_list in self['couplings'].items(): 2079 for coupl in coupl_list: 2080 new_expr, n_changes = ifparser.parse(coupl.expr) 2081 if n_changes > 0: 2082 coupl_modifications.append((coupl, new_expr)) 2083 2084 end_coupl = time.time() 2085 2086 tot_param_time = end_param-start_param 2087 tot_coupl_time = end_coupl-end_param 2088 if tot_param_time>5.0: 2089 logger.debug("Simplification of conditional statements"+\ 2090 " in parameter expressions done in %s."%misc.format_time(tot_param_time)) 2091 if tot_coupl_time>5.0: 2092 logger.debug("Simplification of conditional statements"+\ 2093 " in couplings expressions done in %s."%misc.format_time(tot_coupl_time)) 2094 2095 return param_modifications, coupl_modifications
2096
2098 """ return the list of tuple of name of parameter with the same 2099 input value """ 2100 2101 # Extract external parameters 2102 external_parameters = self['parameters'][('external',)] 2103 2104 # define usefull variable to detect identical input 2105 block_value_to_var={} #(lhablok, value): list_of_var 2106 mult_param = set([]) # key of the previous dict with more than one 2107 #parameter. 2108 2109 #detect identical parameter and remove the duplicate parameter 2110 for param in external_parameters[:]: 2111 value = self['parameter_dict'][param.name] 2112 if value in [0,1,0.000001e-99,9.999999e-1]: 2113 continue 2114 if param.lhablock.lower() == 'decay': 2115 continue 2116 key = (param.lhablock, value) 2117 mkey = (param.lhablock, -value) 2118 2119 if key in block_value_to_var: 2120 block_value_to_var[key].append((param,1)) 2121 mult_param.add(key) 2122 elif mkey in block_value_to_var: 2123 block_value_to_var[mkey].append((param,-1)) 2124 mult_param.add(mkey) 2125 else: 2126 block_value_to_var[key] = [(param,1)] 2127 2128 output=[] 2129 for key in mult_param: 2130 output.append(block_value_to_var[key]) 2131 2132 return output
2133 2134 2135 @staticmethod
2136 - def get_new_coupling_name(main, coupling, value, coeff):
2137 """ We have main == coeff * coupling 2138 coeff is only +1 or -1 2139 main can be either GC_X or -GC_X 2140 coupling can be either GC_Y or -GC_Y 2141 value is either GC_Y or -GC_Y 2142 the return is either GC_X or -GC_X 2143 such that we have value == OUTPUT 2144 """ 2145 assert coeff in [-1,1] 2146 assert value == coupling or value == '-%s' % coupling or coupling == '-%s' % value 2147 assert isinstance(main, str) 2148 assert isinstance(coupling, str) 2149 assert isinstance(value, str) 2150 if coeff ==1: 2151 if value == coupling: 2152 return main # 4/4 2153 else: 2154 if main.startswith('-'): 2155 return main[1:] # 2/2 2156 else: 2157 return '-%s' % main # 2/2 2158 else: 2159 if value == coupling: 2160 if main.startswith('-'): 2161 return main[1:] # 2/2 2162 else: 2163 return '-%s' % main # 2/2 2164 else: 2165 return main # 4/4
2166 2167
2168 - def merge_iden_couplings(self, couplings):
2169 """merge the identical couplings in the interactions and particle 2170 counterterms""" 2171 2172 2173 logger_mod.debug(' Fuse the Following coupling (they have the same value): %s '% \ 2174 ', '.join([str(obj) for obj in couplings])) 2175 2176 main = couplings[0][0] 2177 assert couplings[0][1] == 1 2178 self.del_coup += [c[0] for c in couplings[1:]] # add the other coupl to the suppress list 2179 2180 for coupling, coeff in couplings[1:]: 2181 # check if param is linked to an interaction 2182 if coupling not in self.coupling_pos: 2183 continue 2184 # replace the coupling, by checking all coupling of the interaction 2185 vertices = [ vert for vert in self.coupling_pos[coupling] if 2186 isinstance(vert, base_objects.Interaction)] 2187 for vertex in vertices: 2188 for key, value in vertex['couplings'].items(): 2189 if value == coupling or value == '-%s' % coupling or coupling == '-%s' % value: 2190 vertex['couplings'][key] = self.get_new_coupling_name(\ 2191 main, coupling, value, coeff) 2192 2193 2194 2195 2196 # replace the coupling appearing in the particle counterterm 2197 particles_ct = [ pct for pct in self.coupling_pos[coupling] if 2198 isinstance(pct, tuple)] 2199 for pct in particles_ct: 2200 for key, value in pct[0]['counterterm'][pct[1]].items(): 2201 if value == coupling: 2202 pct[0]['counterterm'][pct[1]][key] = main
2203 2204 2205
2206 - def get_param_block(self):
2207 """return the list of block defined in the param_card""" 2208 2209 blocks = set([p.lhablock for p in self['parameters'][('external',)]]) 2210 return blocks
2211
2212 - def merge_iden_parameters(self, parameters, keep_external=False):
2213 """ merge the identical parameters given in argument. 2214 keep external force to keep the param_card untouched (up to comment)""" 2215 2216 logger_mod.debug('Parameters set to identical values: %s '% \ 2217 ', '.join(['%s*%s' % (f, obj.name.replace('mdl_','')) for (obj,f) in parameters])) 2218 2219 # Extract external parameters 2220 external_parameters = self['parameters'][('external',)] 2221 for i, (obj, factor) in enumerate(parameters): 2222 # Keeped intact the first one and store information 2223 if i == 0: 2224 obj.info = 'set of param :' + \ 2225 ', '.join([str(f)+'*'+param.name.replace('mdl_','') 2226 for (param, f) in parameters]) 2227 expr = obj.name 2228 continue 2229 # Add a Rule linked to the param_card 2230 if factor ==1: 2231 self.rule_card.add_identical(obj.lhablock.lower(), obj.lhacode, 2232 parameters[0][0].lhacode ) 2233 else: 2234 self.rule_card.add_opposite(obj.lhablock.lower(), obj.lhacode, 2235 parameters[0][0].lhacode ) 2236 obj_name = obj.name 2237 # delete the old parameters 2238 if not keep_external: 2239 external_parameters.remove(obj) 2240 elif obj.lhablock.upper() in ['MASS','DECAY']: 2241 external_parameters.remove(obj) 2242 else: 2243 obj.name = '' 2244 obj.info = 'MG5 will not use this value use instead %s*%s' %(factor,expr) 2245 # replace by the new one pointing of the first obj of the class 2246 new_param = base_objects.ModelVariable(obj_name, '%s*%s' %(factor, expr), 'real') 2247 self['parameters'][()].insert(0, new_param) 2248 2249 # For Mass-Width, we need also to replace the mass-width in the particles 2250 #This allows some optimization for multi-process. 2251 if parameters[0][0].lhablock in ['MASS','DECAY']: 2252 new_name = parameters[0][0].name 2253 if parameters[0][0].lhablock == 'MASS': 2254 arg = 'mass' 2255 else: 2256 arg = 'width' 2257 change_name = [p.name for (p,f) in parameters[1:]] 2258 factor_for_name = [f for (p,f) in parameters[1:]] 2259 [p.set(arg, new_name) for p in self['particle_dict'].values() 2260 if p[arg] in change_name and 2261 factor_for_name[change_name.index(p[arg])]==1]
2262
2263 - def remove_interactions(self, zero_couplings):
2264 """ remove the interactions and particle counterterms 2265 associated to couplings""" 2266 2267 2268 mod_vertex = [] 2269 mod_particle_ct = [] 2270 for coup in zero_couplings: 2271 # some coupling might be not related to any interactions 2272 if coup not in self.coupling_pos: 2273 continue 2274 2275 # Remove the corresponding interactions. 2276 2277 vertices = [ vert for vert in self.coupling_pos[coup] if 2278 isinstance(vert, base_objects.Interaction) ] 2279 for vertex in vertices: 2280 modify = False 2281 for key, coupling in vertex['couplings'].items(): 2282 if coupling in zero_couplings: 2283 modify=True 2284 del vertex['couplings'][key] 2285 elif coupling.startswith('-'): 2286 coupling = coupling[1:] 2287 if coupling in zero_couplings: 2288 modify=True 2289 del vertex['couplings'][key] 2290 2291 if modify: 2292 mod_vertex.append(vertex) 2293 2294 # Remove the corresponding particle counterterm 2295 particles_ct = [ pct for pct in self.coupling_pos[coup] if 2296 isinstance(pct, tuple)] 2297 for pct in particles_ct: 2298 modify = False 2299 for key, coupling in pct[0]['counterterm'][pct[1]].items(): 2300 if coupling in zero_couplings: 2301 modify=True 2302 del pct[0]['counterterm'][pct[1]][key] 2303 if modify: 2304 mod_particle_ct.append(pct) 2305 2306 # print useful log and clean the empty interaction 2307 for vertex in mod_vertex: 2308 part_name = [part['name'] for part in vertex['particles']] 2309 orders = ['%s=%s' % (order,value) for order,value in vertex['orders'].items()] 2310 2311 if not vertex['couplings']: 2312 logger_mod.debug('remove interactions: %s at order: %s' % \ 2313 (' '.join(part_name),', '.join(orders))) 2314 self['interactions'].remove(vertex) 2315 else: 2316 logger_mod.debug('modify interactions: %s at order: %s' % \ 2317 (' '.join(part_name),', '.join(orders))) 2318 2319 # print useful log and clean the empty counterterm values 2320 for pct in mod_particle_ct: 2321 part_name = pct[0]['name'] 2322 order = pct[1][0] 2323 loop_parts = ','.join(['('+','.join([\ 2324 self.get_particle(p)['name'] for p in part])+')' \ 2325 for part in pct[1][1]]) 2326 2327 if not pct[0]['counterterm'][pct[1]]: 2328 logger_mod.debug('remove counterterm of particle %s'%part_name+\ 2329 ' with loop particles (%s)'%loop_parts+\ 2330 ' perturbing order %s'%order) 2331 del pct[0]['counterterm'][pct[1]] 2332 else: 2333 logger_mod.debug('Modify counterterm of particle %s'%part_name+\ 2334 ' with loop particles (%s)'%loop_parts+\ 2335 ' perturbing order %s'%order) 2336 2337 return
2338
2339 - def remove_couplings(self, couplings):
2340 #clean the coupling list: 2341 for name, data in self['couplings'].items(): 2342 for coupling in data[:]: 2343 if coupling.name in couplings: 2344 data.remove(coupling)
2345 2346
2347 - def fix_parameter_values(self, zero_parameters, one_parameters, 2348 simplify=True, keep_external=False):
2349 """ Remove all instance of the parameters in the model and replace it by 2350 zero when needed.""" 2351 2352 2353 # treat specific cases for masses and width 2354 for particle in self['particles']: 2355 if particle['mass'] in zero_parameters: 2356 particle['mass'] = 'ZERO' 2357 if particle['width'] in zero_parameters: 2358 particle['width'] = 'ZERO' 2359 if particle['width'] in one_parameters: 2360 one_parameters.remove(particle['width']) 2361 if particle['mass'] in one_parameters: 2362 one_parameters.remove(particle['mass']) 2363 2364 for pdg, particle in self['particle_dict'].items(): 2365 if particle['mass'] in zero_parameters: 2366 particle['mass'] = 'ZERO' 2367 if particle['width'] in zero_parameters: 2368 particle['width'] = 'ZERO' 2369 2370 2371 # Add a rule for zero/one parameter 2372 external_parameters = self['parameters'][('external',)] 2373 for param in external_parameters[:]: 2374 value = self['parameter_dict'][param.name] 2375 block = param.lhablock.lower() 2376 if value == 0: 2377 self.rule_card.add_zero(block, param.lhacode) 2378 elif value == 1: 2379 self.rule_card.add_one(block, param.lhacode) 2380 2381 special_parameters = zero_parameters + one_parameters 2382 2383 2384 2385 if simplify: 2386 # check if the parameters is still useful: 2387 re_str = '|'.join(special_parameters) 2388 if len(re_str) > 25000: # size limit on mac 2389 split = len(special_parameters) // 2 2390 re_str = ['|'.join(special_parameters[:split]), 2391 '|'.join(special_parameters[split:])] 2392 else: 2393 re_str = [ re_str ] 2394 used = set() 2395 for expr in re_str: 2396 re_pat = re.compile(r'''\b(%s)\b''' % expr) 2397 # check in coupling 2398 for name, coupling_list in self['couplings'].items(): 2399 for coupling in coupling_list: 2400 for use in re_pat.findall(coupling.expr): 2401 used.add(use) 2402 2403 # check in form-factor 2404 for lor in self['lorentz']: 2405 if hasattr(lor, 'formfactors') and lor.formfactors: 2406 for ff in lor.formfactors: 2407 for use in re_pat.findall(ff.value): 2408 used.add(use) 2409 else: 2410 used = set([i for i in special_parameters if i]) 2411 2412 # simplify the regular expression 2413 re_str = '|'.join([param for param in special_parameters if param not in used]) 2414 if len(re_str) > 25000: # size limit on mac 2415 split = len(special_parameters) // 2 2416 re_str = ['|'.join(special_parameters[:split]), 2417 '|'.join(special_parameters[split:])] 2418 else: 2419 re_str = [ re_str ] 2420 for expr in re_str: 2421 re_pat = re.compile(r'''\b(%s)\b''' % expr) 2422 2423 param_info = {} 2424 # check in parameters 2425 for dep, param_list in self['parameters'].items(): 2426 for tag, parameter in enumerate(param_list): 2427 # update information concerning zero/one parameters 2428 if parameter.name in special_parameters: 2429 param_info[parameter.name]= {'dep': dep, 'tag': tag, 2430 'obj': parameter} 2431 continue 2432 2433 # Bypass all external parameter 2434 if isinstance(parameter, base_objects.ParamCardVariable): 2435 continue 2436 2437 if simplify: 2438 for use in re_pat.findall(parameter.expr): 2439 used.add(use) 2440 2441 # modify the object for those which are still used 2442 for param in used: 2443 if not param: 2444 continue 2445 data = self['parameters'][param_info[param]['dep']] 2446 data.remove(param_info[param]['obj']) 2447 tag = param_info[param]['tag'] 2448 data = self['parameters'][()] 2449 if param in zero_parameters: 2450 data.insert(0, base_objects.ModelVariable(param, '0.0', 'real')) 2451 else: 2452 data.insert(0, base_objects.ModelVariable(param, '1.0', 'real')) 2453 2454 # remove completely useless parameters 2455 for param in special_parameters: 2456 #by pass parameter still in use 2457 if param in used or \ 2458 (keep_external and param_info[param]['dep'] == ('external',)): 2459 logger_mod.debug('fix parameter value: %s' % param) 2460 continue 2461 logger_mod.debug('remove parameters: %s' % (param)) 2462 data = self['parameters'][param_info[param]['dep']] 2463 data.remove(param_info[param]['obj'])
2464
2465 - def optimise_interaction(self, interaction):
2466 2467 # we want to check if the same coupling (up to the sign) is used for two lorentz structure 2468 # for the same color structure. 2469 to_lor = {} 2470 for (color, lor), coup in interaction['couplings'].items(): 2471 abscoup, coeff = (coup[1:],-1) if coup.startswith('-') else (coup, 1) 2472 key = (color, abscoup) 2473 if key in to_lor: 2474 to_lor[key].append((lor,coeff)) 2475 else: 2476 to_lor[key] = [(lor,coeff)] 2477 2478 nb_reduce = [] 2479 optimize = False 2480 for key in to_lor: 2481 if len(to_lor[key]) >1: 2482 nb_reduce.append(len(to_lor[key])-1) 2483 optimize = True 2484 2485 if not optimize: 2486 return 2487 2488 if not hasattr(self, 'defined_lorentz_expr'): 2489 self.defined_lorentz_expr = {} 2490 self.lorentz_info = {} 2491 self.lorentz_combine = {} 2492 for lor in self.get('lorentz'): 2493 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name') 2494 self.lorentz_info[lor.get('name')] = lor #(lor.get('structure'), lor.get('spins')) 2495 2496 for key in to_lor: 2497 if len(to_lor[key]) == 1: 2498 continue 2499 names = ['u%s' % interaction['lorentz'][i[0]] if i[1] ==1 else \ 2500 'd%s' % interaction['lorentz'][i[0]] for i in to_lor[key]] 2501 2502 names.sort() 2503 2504 # get name of the new lorentz 2505 if tuple(names) in self.lorentz_combine: 2506 # already created new loretnz 2507 new_name = self.lorentz_combine[tuple(names)] 2508 else: 2509 new_name = self.add_merge_lorentz(names) 2510 2511 # remove the old couplings 2512 color, coup = key 2513 to_remove = [(color, lor[0]) for lor in to_lor[key]] 2514 for rm in to_remove: 2515 del interaction['couplings'][rm] 2516 2517 #add the lorentz structure to the interaction 2518 if new_name not in [l for l in interaction.get('lorentz')]: 2519 interaction.get('lorentz').append(new_name) 2520 2521 #find the associate index 2522 new_l = interaction.get('lorentz').index(new_name) 2523 # adding the new combination (color,lor) associate to this sum of structure 2524 interaction['couplings'][(color, new_l)] = coup
2525 2526 2527
2528 - def add_merge_lorentz(self, names):
2529 """add a lorentz structure which is the sume of the list given above""" 2530 2531 #create new_name 2532 ii = len(names[0]) 2533 while ii>1: 2534 #do not count the initial "u/d letter whcih indicates the sign" 2535 if not all(n[1:].startswith(names[0][1:ii]) for n in names[1:]): 2536 ii -=1 2537 else: 2538 base_name = names[0][1:ii] 2539 break 2540 else: 2541 base_name = 'LMER' 2542 i = 1 2543 while '%s%s' %(base_name, i) in self.lorentz_info: 2544 i +=1 2545 new_name = '%s%s' %(base_name, i) 2546 self.lorentz_combine[tuple(names)] = new_name 2547 2548 # load the associate lorentz expression 2549 new_struct = ' + '.join([self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('u')]) 2550 if any( n.startswith('d') for n in names ): 2551 new_struct += '-' + ' - '.join(['1.*(%s)' %self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('d')]) 2552 spins = self.lorentz_info[names[0][1:]].get('spins') 2553 formfact = sum([ self.lorentz_info[n[1:]].get('formfactors') for n in names \ 2554 if hasattr(self.lorentz_info[n[1:]], 'formfactors') \ 2555 and self.lorentz_info[n[1:]].get('formfactors') \ 2556 ],[]) 2557 2558 2559 2560 2561 new_lor = self.add_lorentz(new_name, spins, new_struct, formfact) 2562 self.lorentz_info[new_name] = new_lor 2563 2564 return new_name
2565
2566 - def add_lorentz(self, name, spin, struct, formfact=None):
2567 """adding lorentz structure to the current model""" 2568 new = self['lorentz'][0].__class__(name = name, 2569 spins = spin, 2570 structure = struct) 2571 if formfact: 2572 new.formfactors = formfact 2573 self['lorentz'].append(new) 2574 self.create_lorentz_dict() 2575 2576 return None
2577