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