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