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