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 particle.set('type', 'goldstone') 503 elif hasattr(particle_info, 'goldstone') and particle_info.goldstone: 504 particle.set('type', 'goldstone') 505 506 nb_property = 0 #basic check that the UFO information is complete 507 # Loop over the element defining the UFO particles 508 for key,value in particle_info.__dict__.items(): 509 # Check if we use it in the MG5 definition of a particles 510 if key in base_objects.Particle.sorted_keys and not key=='counterterm': 511 nb_property +=1 512 if key in ['name', 'antiname']: 513 if not self.model['case_sensitive']: 514 particle.set(key, value.lower()) 515 else: 516 particle.set(key, value) 517 elif key == 'charge': 518 particle.set(key, float(value)) 519 elif key in ['mass','width']: 520 particle.set(key, str(value)) 521 elif key == 'spin': 522 # MG5 internally treats ghost with positive spin for loop models and 523 # ignore them otherwise 524 particle.set(key,abs(value)) 525 if value<0: 526 particle.set('type','ghost') 527 elif key == 'propagating': 528 if not value: 529 particle.set('line', None) 530 elif key == 'line': 531 if particle.get('line') is None: 532 pass # This means that propagating is on False 533 else: 534 particle.set('line', value) 535 elif key == 'propagator': 536 if value: 537 if aloha.unitary_gauge: 538 particle.set(key, str(value[0])) 539 else: 540 particle.set(key, str(value[1])) 541 else: 542 particle.set(key, '') 543 else: 544 particle.set(key, value) 545 elif key == 'loop_particles': 546 loop_particles = value 547 elif key == 'counterterm': 548 counterterms = value 549 elif key.lower() not in ('ghostnumber','selfconjugate','goldstone', 550 'goldstoneboson','partial_widths', 551 'texname', 'antitexname', 'propagating', 'ghost' 552 ): 553 # add charge -we will check later if those are conserve 554 self.conservecharge.add(key) 555 particle.set(key,value, force=True) 556 557 if not hasattr(particle_info, 'propagator'): 558 nb_property += 1 559 if particle.get('spin') >= 3: 560 if particle.get('mass').lower() == 'zero': 561 particle.set('propagator', 0) 562 elif particle.get('spin') == 3 and not aloha.unitary_gauge: 563 particle.set('propagator', 0) 564 565 assert(10 == nb_property) #basic check that all the information is there 566 567 # Identify self conjugate particles 568 if particle_info.name == particle_info.antiname: 569 particle.set('self_antipart', True) 570 571 # Proceed only if we deal with a loop model and that this particle 572 # has wavefunction renormalization 573 if not self.perturbation_couplings or counterterms=={}: 574 self.particles.append(particle) 575 return 576 577 # Set here the 'counterterm' attribute to the particle. 578 # First we must change the couplings dictionary keys from the entry format 579 # (order1,order2,...,orderN,loop_particle#):LaurentSerie 580 # two a dictionary with format 581 # ('ORDER_OF_COUNTERTERM',((Particle_list_PDG))):{laurent_order:CTCouplingName} 582 particle_counterterms = {} 583 for key, counterterm in counterterms.items(): 584 # Makes sure this counterterm contributes at one-loop. 585 if len([1 for k in key[:-1] if k==1])==1 and \ 586 not any(k>1 for k in key[:-1]): 587 newParticleCountertermKey=[None,\ 588 # The line below is for loop UFO Model with the 'attribute' 589 # 'loop_particles' of the Particle objects to be defined with 590 # instances of the particle class. The new convention is to use 591 # pdg numbers instead. 592 # tuple([tuple([abs(part.pdg_code) for part in loop_parts]) for\ 593 tuple([tuple(loop_parts) for\ 594 loop_parts in loop_particles[key[-1]]])] 595 for i, order in enumerate(self.ufomodel.all_orders[:-1]): 596 if key[i]==1: 597 newParticleCountertermKey[0]=order.name 598 newCouplingName='UVWfct_'+particle_info.name+'_'+str(key[-1]) 599 particle_counterterms[tuple(newParticleCountertermKey)]=\ 600 dict([(key,newCouplingName+('' if key==0 else '_'+str(-key)+'eps'))\ 601 for key in counterterm]) 602 # We want to create the new coupling for this wavefunction 603 # renormalization. 604 self.ufomodel.object_library.Coupling(\ 605 name = newCouplingName, 606 value = counterterm, 607 order = {newParticleCountertermKey[0]:2}) 608 self.wavefunction_CT_couplings.append(self.ufomodel.all_couplings.pop()) 609 610 particle.set('counterterm',particle_counterterms) 611 self.particles.append(particle) 612 return
613
614 - def treat_couplings(self, couplings, all_CTparameters):
615 """ This function scan each coupling to see if it contains a CT parameter. 616 when it does, it changes its value to a dictionary with the CT parameter 617 changed to a new parameter for each pole and finite part. For instance, 618 the following coupling: 619 coupling.value = '2*(myCTParam1 + myParam*(myCTParam2 + myCTParam3)' 620 with CTparameters 621 myCTParam1 = {0: Something, -1: SomethingElse} 622 myCTParam2 = {0: OtherSomething } 623 myCTParam3 = {-1: YetOtherSomething } 624 would be turned into 625 coupling.value = {0: '2*(myCTParam1_FIN_ + myParam*(myCTParam2_FIN_ + ZERO)' 626 -1: '2*(myCTParam1_EPS_ + myParam*(ZERO + myCTParam2_EPS_)'} 627 628 all_CTParameter is the list of all CTParameters in the model""" 629 630 # First define a list of regular expressions for each CT parameter 631 # and put them in a dictionary whose keys are the CT parameter names 632 # and the values are a tuple with the substituting patter in the first 633 # entry and the list of substituting functions (one for each pole) 634 # as the second entry of this tuple. 635 CTparameter_patterns = {} 636 zero_substitution = lambda matchedObj: matchedObj.group('first')+\ 637 'ZERO'+matchedObj.group('second') 638 def function_factory(arg): 639 return lambda matchedObj: \ 640 matchedObj.group('first')+arg+matchedObj.group('second')
641 for CTparam in all_CTparameters: 642 pattern_finder = re.compile(r"(?P<first>\A|\*|\+|\-|\(|\s)(?P<name>"+ 643 CTparam.name+r")(?P<second>\Z|\*|\+|\-|\)|/|\\|\s)") 644 645 sub_functions = [None if CTparam.pole(pole)=='ZERO' else 646 function_factory('%s_%s_'%(CTparam.name,pole_dict[-pole])) 647 for pole in range(3)] 648 CTparameter_patterns[CTparam.name] = (pattern_finder,sub_functions) 649 650 times_zero = re.compile('\*\s*-?ZERO') 651 zero_times = re.compile('ZERO\s*(\*|\/)') 652 def is_expr_zero(expresson): 653 """ Checks whether a single term (involving only the operations 654 * or / is zero. """ 655 for term in expresson.split('-'): 656 for t in term.split('+'): 657 t = t.strip() 658 if t in ['ZERO','']: 659 continue 660 if not (times_zero.search(t) or zero_times.search(t)): 661 return False 662 return True
663 664 def find_parenthesis(expr): 665 end = expr.find(')') 666 if end == -1: 667 return None 668 start = expr.rfind('(',0,end+1) 669 if start ==-1: 670 raise InvalidModel,\ 671 'Parenthesis of expression %s are malformed'%expr 672 return [expr[:start],expr[start+1:end],expr[end+1:]] 673 674 start_parenthesis = re.compile(r".*\s*[\+\-\*\/\)\(]\s*$") 675 def is_value_zero(value): 676 """Check whether an expression like ((A+B)*ZERO+C)*ZERO is zero. 677 Only +,-,/,* operations are allowed and 'ZERO' is a tag for an 678 analytically zero quantity.""" 679 680 parenthesis = find_parenthesis(value) 681 if parenthesis: 682 # Allow the complexconjugate function 683 if parenthesis[0].endswith('complexconjugate'): 684 # Then simply remove it 685 parenthesis[0] = parenthesis[0][:-16] 686 if parenthesis[0]=='' or re.match(start_parenthesis, 687 parenthesis[0]): 688 if is_value_zero(parenthesis[1]): 689 new_parenthesis = 'ZERO' 690 else: 691 new_parenthesis = 'PARENTHESIS' 692 else: 693 new_parenthesis = '_FUNCTIONARGS' 694 new_value = parenthesis[0]+new_parenthesis+parenthesis[2] 695 return is_value_zero(new_value) 696 else: 697 return is_expr_zero(value) 698 699 def CTCoupling_pole(CTCoupling, pole): 700 """Compute the pole of the CTCoupling in two cases: 701 a) Its value is a dictionary, then just return the corresponding 702 entry in the dictionary. 703 b) It is expressed in terms of CTParameters which are themselves 704 dictionary so we want to substitute their expression to get 705 the value of the pole. In the current implementation, this is 706 just to see if the pole is zero or not. 707 """ 708 709 if isinstance(CTCoupling.value,dict): 710 if -pole in CTCoupling.value.keys(): 711 return CTCoupling.value[-pole], [], 0 712 else: 713 return 'ZERO', [], 0 714 715 new_expression = CTCoupling.value 716 CTparamNames = [] 717 n_CTparams = 0 718 for paramname, value in CTparameter_patterns.items(): 719 pattern = value[0] 720 # Keep track of which CT parameters enter in the definition of 721 # which coupling. 722 if not re.search(pattern,new_expression): 723 continue 724 n_CTparams += 1 725 # If the contribution of this CTparam to this pole is non 726 # zero then the substituting function is not None: 727 if not value[1][pole] is None: 728 CTparamNames.append('%s_%s_'%(paramname,pole_dict[-pole])) 729 730 substitute_function = zero_substitution if \ 731 value[1][pole] is None else value[1][pole] 732 new_expression = pattern.sub(substitute_function,new_expression) 733 734 # If no CTParam was found and we ask for a pole, then it can only 735 # be zero. 736 if pole!=0 and n_CTparams==0: 737 return 'ZERO', [], n_CTparams 738 739 # Check if resulting expression is analytically zero or not. 740 # Remember that when the value of a CT_coupling is not a dictionary 741 # then the only operators allowed in the definition are +,-,*,/ 742 # and each term added or subtracted must contain *exactly one* 743 # CTParameter and never at the denominator. 744 if n_CTparams > 0 and is_value_zero(new_expression): 745 return 'ZERO', [], n_CTparams 746 else: 747 return new_expression, CTparamNames, n_CTparams 748 749 # For each coupling we substitute its value if necessary 750 for coupl in couplings: 751 new_value = {} 752 for pole in range(0,3): 753 expression, CTparamNames, n_CTparams = CTCoupling_pole(coupl, pole) 754 # Make sure it uses CT parameters, otherwise do nothing 755 if n_CTparams == 0: 756 break 757 elif expression!='ZERO': 758 new_value[-pole] = expression 759 couplname = coupl.name 760 if pole!=0: 761 couplname += "_%deps"%pole 762 # Add the parameter dependency found to the dependency map 763 # of the model being built. In principle, since we should 764 # be building a loop model now, it should always have this 765 # attribute defined, but it is better to make sure. 766 if hasattr(self.model, 'map_CTcoup_CTparam'): 767 self.model.map_CTcoup_CTparam[couplname] = CTparamNames 768 769 # Finally modify the value of this CTCoupling so that it is no 770 # longer a string expression in terms of CTParameters but rather 771 # a dictionary with the CTparameters replaced by their _FIN_ and 772 # _EPS_ counterparts. 773 # This is useful for the addCT_interaction() step. I will be reverted 774 # right after the addCT_interaction() function so as to leave 775 # the UFO intact, as it should. 776 if new_value: 777 coupl.old_value = coupl.value 778 coupl.value = new_value 779
780 - def add_CTinteraction(self, interaction, color_info):
781 """ Split this interaction in order to call add_interaction for 782 interactions for each element of the loop_particles list. Also it 783 is necessary to unfold here the contributions to the different laurent 784 expansion orders of the couplings.""" 785 786 # Work on a local copy of the interaction provided 787 interaction_info=copy.copy(interaction) 788 789 intType='' 790 if interaction_info.type not in ['UV','UVloop','UVtree','UVmass','R2']: 791 raise MadGraph5Error, 'MG5 only supports the following types of'+\ 792 ' vertices, R2, UV and UVmass. %s is not in this list.'%interaction_info.type 793 else: 794 intType=interaction_info.type 795 # If not specified and simply set to UV, guess the appropriate type 796 if interaction_info.type=='UV': 797 if len(interaction_info.particles)==2 and interaction_info.\ 798 particles[0].name==interaction_info.particles[1].name: 799 intType='UVmass' 800 else: 801 intType='UVloop' 802 803 # Make sure that if it is a UV mass renromalization counterterm it is 804 # defined as such. 805 # if len(intType)>2 and intType[:2]=='UV' and len(interaction_info.particles)==2 \ 806 # and interaction_info.particles[0].name==interaction_info.particles[1].name: 807 # intType='UVmass' 808 809 # Now we create a couplings dictionary for each element of the loop_particles list 810 # and for each expansion order of the laurent serie in the coupling. 811 # Format is new_couplings[loop_particles][laurent_order] and each element 812 # is a couplings dictionary. 813 new_couplings=[[{} for j in range(0,3)] for i in \ 814 range(0,max(1,len(interaction_info.loop_particles)))] 815 # So sort all entries in the couplings dictionary to put them a the 816 # correct place in new_couplings. 817 for key, coupling in interaction_info.couplings.items(): 818 for poleOrder in range(0,3): 819 expression = coupling.pole(poleOrder) 820 if expression!='ZERO': 821 if poleOrder==2: 822 raise InvalidModel, """ 823 The CT coupling %s was found with a contribution to the double pole. 824 This is either an error in the model or a parsing error in the function 'is_value_zero'. 825 The expression of the non-zero double pole coupling is: 826 %s 827 """%(coupling.name,str(coupling.value)) 828 # It is actually safer that the new coupling associated to 829 # the interaction added is not a reference to an original 830 # coupling in the ufo model. So copy.copy is right here. 831 newCoupling = copy.copy(coupling) 832 if poleOrder!=0: 833 newCoupling.name=newCoupling.name+"_"+str(poleOrder)+"eps" 834 newCoupling.value = expression 835 # assign the CT parameter dependences 836 #if hasattr(coupling,'CTparam_dependence') and \ 837 # (-poleOrder in coupling.CTparam_dependence) and \ 838 # coupling.CTparam_dependence[-poleOrder]: 839 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 840 #elif hasattr(newCoupling,'CTparam_dependence'): 841 # delattr(newCoupling,"CTparam_dependence") 842 new_couplings[key[2]][poleOrder][(key[0],key[1])] = newCoupling 843 844 # Now we can add an interaction for each. 845 for i, all_couplings in enumerate(new_couplings): 846 loop_particles=[[]] 847 if len(interaction_info.loop_particles)>0: 848 loop_particles=[[part.pdg_code for part in loop_parts] \ 849 for loop_parts in interaction_info.loop_particles[i]] 850 for poleOrder in range(0,3): 851 if all_couplings[poleOrder]!={}: 852 interaction_info.couplings=all_couplings[poleOrder] 853 self.add_interaction(interaction_info, color_info,\ 854 (intType if poleOrder==0 else (intType+str(poleOrder)+\ 855 'eps')),loop_particles)
856 857
858 - def find_color_anti_color_rep(self, output=None):
859 """find which color are in the 3/3bar states""" 860 # method look at the 3 3bar 8 configuration. 861 # If the color is T(3,2,1) and the interaction F1 F2 V 862 # Then set F1 to anticolor (and F2 to color) 863 # if this is T(3,1,2) set the opposite 864 if not output: 865 output = {} 866 867 for interaction_info in self.ufomodel.all_vertices: 868 if len(interaction_info.particles) != 3: 869 continue 870 colors = [abs(p.color) for p in interaction_info.particles] 871 if colors[:2] == [3,3]: 872 if 'T(3,2,1)' in interaction_info.color: 873 color, anticolor, other = interaction_info.particles 874 elif 'T(3,1,2)' in interaction_info.color: 875 anticolor, color, _ = interaction_info.particles 876 elif 'Identity(1,2)' in interaction_info.color or \ 877 'Identity(2,1)' in interaction_info.color: 878 first, second, _ = interaction_info.particles 879 if first.pdg_code in output: 880 if output[first.pdg_code] == 3: 881 color, anticolor = first, second 882 else: 883 color, anticolor = second, first 884 elif second.pdg_code in output: 885 if output[second.pdg_code] == 3: 886 color, anticolor = second, first 887 else: 888 color, anticolor = first, second 889 else: 890 continue 891 else: 892 continue 893 elif colors[1:] == [3,3]: 894 if 'T(1,2,3)' in interaction_info.color: 895 other, anticolor, color = interaction_info.particles 896 elif 'T(1,3,2)' in interaction_info.color: 897 other, color, anticolor = interaction_info.particles 898 elif 'Identity(2,3)' in interaction_info.color or \ 899 'Identity(3,2)' in interaction_info.color: 900 _, first, second = interaction_info.particles 901 if first.pdg_code in output: 902 if output[first.pdg_code] == 3: 903 color, anticolor = first, second 904 else: 905 color, anticolor = second, first 906 elif second.pdg_code in output: 907 if output[second.pdg_code] == 3: 908 color, anticolor = second, first 909 else: 910 color, anticolor = first, second 911 else: 912 continue 913 else: 914 continue 915 916 elif colors.count(3) == 2: 917 if 'T(2,3,1)' in interaction_info.color: 918 color, other, anticolor = interaction_info.particles 919 elif 'T(2,1,3)' in interaction_info.color: 920 anticolor, other, color = interaction_info.particles 921 elif 'Identity(1,3)' in interaction_info.color or \ 922 'Identity(3,1)' in interaction_info.color: 923 first, _, second = interaction_info.particles 924 if first.pdg_code in output: 925 if output[first.pdg_code] == 3: 926 color, anticolor = first, second 927 else: 928 color, anticolor = second, first 929 elif second.pdg_code in output: 930 if output[second.pdg_code] == 3: 931 color, anticolor = second, first 932 else: 933 color, anticolor = first, second 934 else: 935 continue 936 else: 937 continue 938 else: 939 continue 940 941 # Check/assign for the color particle 942 if color.pdg_code in output: 943 if output[color.pdg_code] == -3: 944 raise InvalidModel, 'Particles %s is sometimes in the 3 and sometimes in the 3bar representations' \ 945 % color.name 946 else: 947 output[color.pdg_code] = 3 948 949 # Check/assign for the anticolor particle 950 if anticolor.pdg_code in output: 951 if output[anticolor.pdg_code] == 3: 952 raise InvalidModel, 'Particles %s is sometimes set as in the 3 and sometimes in the 3bar representations' \ 953 % anticolor.name 954 else: 955 output[anticolor.pdg_code] = -3 956 957 return output
958
959 - def detect_incoming_fermion(self):
960 """define which fermion should be incoming 961 for that we look at F F~ X interactions 962 """ 963 self.incoming = [] 964 self.outcoming = [] 965 for interaction_info in self.ufomodel.all_vertices: 966 # check if the interaction meet requirements: 967 pdg = [p.pdg_code for p in interaction_info.particles if p.spin in [2,4]] 968 if len(pdg) % 2: 969 raise InvalidModel, 'Odd number of fermion in vertex: %s' % [p.pdg_code for p in interaction_info.particles] 970 for i in range(0, len(pdg),2): 971 if pdg[i] == - pdg[i+1]: 972 if pdg[i] in self.outcoming: 973 raise InvalidModel, '%s has not coherent incoming/outcoming status between interactions' %\ 974 [p for p in interaction_info.particles if p.spin in [2,4]][i].name 975 976 elif not pdg[i] in self.incoming: 977 self.incoming.append(pdg[i]) 978 self.outcoming.append(pdg[i+1])
979
980 - def add_interaction(self, interaction_info, color_info, type='base', loop_particles=None):
981 """add an interaction in the MG5 model. interaction_info is the 982 UFO vertices information.""" 983 # Import particles content: 984 particles = [self.model.get_particle(particle.pdg_code) \ 985 for particle in interaction_info.particles] 986 if None in particles: 987 # Interaction with a ghost/goldstone 988 return 989 particles = base_objects.ParticleList(particles) 990 991 # Import Lorentz content: 992 lorentz = [helas for helas in interaction_info.lorentz] 993 994 # Check the coherence of the Fermion Flow 995 nb_fermion = sum([ 1 if p.is_fermion() else 0 for p in particles]) 996 try: 997 if nb_fermion == 2: 998 # Fermion Flow is suppose to be dealt by UFO 999 [aloha_fct.check_flow_validity(helas.structure, nb_fermion) \ 1000 for helas in interaction_info.lorentz 1001 if helas.name not in self.checked_lor] 1002 self.checked_lor.update(set([helas.name for helas in interaction_info.lorentz])) 1003 elif nb_fermion: 1004 if any(p.selfconjugate for p in interaction_info.particles if p.spin % 2 == 0): 1005 text = "Majorana can not be dealt in 4/6/... fermion interactions" 1006 raise InvalidModel, text 1007 except aloha_fct.WrongFermionFlow, error: 1008 text = 'Fermion Flow error for interactions %s: %s: %s\n %s' % \ 1009 (', '.join([p.name for p in interaction_info.particles]), 1010 helas.name, helas.structure, error) 1011 raise InvalidModel, text 1012 1013 1014 1015 # Now consider the name only 1016 lorentz = [helas.name for helas in lorentz] 1017 # Import color information: 1018 colors = [self.treat_color(color_obj, interaction_info, color_info) 1019 for color_obj in interaction_info.color] 1020 1021 1022 order_to_int={} 1023 1024 for key, couplings in interaction_info.couplings.items(): 1025 if not isinstance(couplings, list): 1026 couplings = [couplings] 1027 if interaction_info.lorentz[key[1]].name not in lorentz: 1028 continue 1029 # get the sign for the coupling (if we need to adapt the flow) 1030 if nb_fermion > 2: 1031 flow = aloha_fct.get_fermion_flow(interaction_info.lorentz[key[1]].structure, 1032 nb_fermion) 1033 coupling_sign = self.get_sign_flow(flow, nb_fermion) 1034 else: 1035 coupling_sign = '' 1036 for coupling in couplings: 1037 order = tuple(coupling.order.items()) 1038 if '1' in order: 1039 raise InvalidModel, '''Some couplings have \'1\' order. 1040 This is not allowed in MG. 1041 Please defines an additional coupling to your model''' 1042 if order in order_to_int: 1043 order_to_int[order].get('couplings')[key] = '%s%s' % \ 1044 (coupling_sign,coupling.name) 1045 else: 1046 # Initialize a new interaction with a new id tag 1047 interaction = base_objects.Interaction({'id':len(self.interactions)+1}) 1048 interaction.set('particles', particles) 1049 interaction.set('lorentz', lorentz) 1050 interaction.set('couplings', {key: 1051 '%s%s' %(coupling_sign,coupling.name)}) 1052 interaction.set('orders', coupling.order) 1053 interaction.set('color', colors) 1054 interaction.set('type', type) 1055 interaction.set('loop_particles', loop_particles) 1056 order_to_int[order] = interaction 1057 # add to the interactions 1058 self.interactions.append(interaction) 1059 1060 # check if this interaction conserve the charge defined 1061 # if type=='base': 1062 for charge in list(self.conservecharge): #duplicate to allow modification 1063 total = 0 1064 for part in interaction_info.particles: 1065 try: 1066 total += getattr(part, charge) 1067 except AttributeError: 1068 pass 1069 if abs(total) > 1e-12: 1070 logger.info('The model has interaction violating the charge: %s' % charge) 1071 self.conservecharge.discard(charge) 1072 1073
1074 - def get_sign_flow(self, flow, nb_fermion):
1075 """ensure that the flow of particles/lorentz are coherent with flow 1076 and return a correct version if needed""" 1077 1078 if not flow or nb_fermion < 4: 1079 return '' 1080 1081 expected = {} 1082 for i in range(nb_fermion//2): 1083 expected[i+1] = i+2 1084 1085 if flow == expected: 1086 return '' 1087 1088 switch = {} 1089 for i in range(1, nb_fermion+1): 1090 if not i in flow: 1091 continue 1092 switch[i] = len(switch) 1093 switch[flow[i]] = len(switch) 1094 1095 # compute the sign of the permutation 1096 sign = 1 1097 done = [] 1098 1099 # make a list of consecutive number which correspond to the new 1100 # order of the particles in the new list. 1101 new_order = [] 1102 for id in range(nb_fermion): # id is the position in the particles order (starts 0) 1103 nid = switch[id+1]-1 # nid is the position in the new_particles 1104 #order (starts 0) 1105 new_order.append(nid) 1106 1107 # compute the sign: 1108 sign =1 1109 for k in range(len(new_order)-1): 1110 for l in range(k+1,len(new_order)): 1111 if new_order[l] < new_order[k]: 1112 sign *= -1 1113 1114 return '' if sign ==1 else '-'
1115 1116 1117 1118
1119 - def add_lorentz(self, name, spins , expr):
1120 """ Add a Lorentz expression which is not present in the UFO """ 1121 1122 new = self.model['lorentz'][0].__class__(name = name, 1123 spins = spins, 1124 structure = expr) 1125 1126 self.model['lorentz'].append(new) 1127 self.model.create_lorentz_dict() 1128 return name
1129 1130 _pat_T = re.compile(r'T\((?P<first>\d*),(?P<second>\d*)\)') 1131 _pat_id = re.compile(r'Identity\((?P<first>\d*),(?P<second>\d*)\)') 1132
1133 - def treat_color(self, data_string, interaction_info, color_info):
1134 """ convert the string to ColorString""" 1135 1136 #original = copy.copy(data_string) 1137 #data_string = p.sub('color.T(\g<first>,\g<second>)', data_string) 1138 1139 1140 output = [] 1141 factor = 1 1142 for term in data_string.split('*'): 1143 pattern = self._pat_id.search(term) 1144 if pattern: 1145 particle = interaction_info.particles[int(pattern.group('first'))-1] 1146 particle2 = interaction_info.particles[int(pattern.group('second'))-1] 1147 if particle.color == particle2.color and particle.color in [-6, 6]: 1148 error_msg = 'UFO model have inconsistency in the format:\n' 1149 error_msg += 'interactions for particles %s has color information %s\n' 1150 error_msg += ' but both fermion are in the same representation %s' 1151 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1152 if particle.color == particle2.color and particle.color in [-3, 3]: 1153 if particle.pdg_code in color_info and particle2.pdg_code in color_info: 1154 if color_info[particle.pdg_code] == color_info[particle2.pdg_code]: 1155 error_msg = 'UFO model have inconsistency in the format:\n' 1156 error_msg += 'interactions for particles %s has color information %s\n' 1157 error_msg += ' but both fermion are in the same representation %s' 1158 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1159 elif particle.pdg_code in color_info: 1160 color_info[particle2.pdg_code] = -particle.pdg_code 1161 elif particle2.pdg_code in color_info: 1162 color_info[particle.pdg_code] = -particle2.pdg_code 1163 else: 1164 error_msg = 'UFO model have inconsistency in the format:\n' 1165 error_msg += 'interactions for particles %s has color information %s\n' 1166 error_msg += ' but both fermion are in the same representation %s' 1167 raise InvalidModel, error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color) 1168 1169 1170 if particle.color == 6: 1171 output.append(self._pat_id.sub('color.T6(\g<first>,\g<second>)', term)) 1172 elif particle.color == -6 : 1173 output.append(self._pat_id.sub('color.T6(\g<second>,\g<first>)', term)) 1174 elif particle.color == 8: 1175 output.append(self._pat_id.sub('color.Tr(\g<first>,\g<second>)', term)) 1176 factor *= 2 1177 elif particle.color in [-3,3]: 1178 if particle.pdg_code not in color_info: 1179 #try to find it one more time 3 -3 1 might help 1180 logger.debug('fail to find 3/3bar representation: Retry to find it') 1181 color_info = self.find_color_anti_color_rep(color_info) 1182 if particle.pdg_code not in color_info: 1183 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle.name) 1184 color_info[particle.pdg_code] = particle.color 1185 else: 1186 logger.debug('succeed') 1187 if particle2.pdg_code not in color_info: 1188 #try to find it one more time 3 -3 1 might help 1189 logger.debug('fail to find 3/3bar representation: Retry to find it') 1190 color_info = self.find_color_anti_color_rep(color_info) 1191 if particle2.pdg_code not in color_info: 1192 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle2.name) 1193 color_info[particle2.pdg_code] = particle2.color 1194 else: 1195 logger.debug('succeed') 1196 1197 if color_info[particle.pdg_code] == 3 : 1198 output.append(self._pat_id.sub('color.T(\g<second>,\g<first>)', term)) 1199 elif color_info[particle.pdg_code] == -3: 1200 output.append(self._pat_id.sub('color.T(\g<first>,\g<second>)', term)) 1201 else: 1202 raise MadGraph5Error, \ 1203 "Unknown use of Identity for particle with color %d" \ 1204 % particle.color 1205 else: 1206 output.append(term) 1207 data_string = '*'.join(output) 1208 1209 # Change convention for summed indices 1210 p = re.compile(r'\'\w(?P<number>\d+)\'') 1211 data_string = p.sub('-\g<number>', data_string) 1212 1213 # Shift indices by -1 1214 new_indices = {} 1215 new_indices = dict([(j,i) for (i,j) in \ 1216 enumerate(range(1, 1217 len(interaction_info.particles)+1))]) 1218 1219 1220 output = data_string.split('*') 1221 output = color.ColorString([eval(data) \ 1222 for data in output if data !='1']) 1223 output.coeff = fractions.Fraction(factor) 1224 for col_obj in output: 1225 col_obj.replace_indices(new_indices) 1226 1227 return output
1228
1229 -class OrganizeModelExpression:
1230 """Organize the couplings/parameters of a model""" 1231 1232 track_dependant = ['aS','aEWM1','MU_R'] # list of variable from which we track 1233 #dependencies those variables should be define 1234 #as external parameters 1235 1236 # regular expression to shorten the expressions 1237 complex_number = re.compile(r'''complex\((?P<real>[^,\(\)]+),(?P<imag>[^,\(\)]+)\)''') 1238 expo_expr = re.compile(r'''(?P<expr>[\w.]+)\s*\*\*\s*(?P<expo>[+-]?[\d.]+)''') 1239 cmath_expr = re.compile(r'''cmath.(?P<operation>\w+)\((?P<expr>\w+)\)''') 1240 #operation is usualy sqrt / sin / cos / tan 1241 conj_expr = re.compile(r'''complexconjugate\((?P<expr>\w+)\)''') 1242 1243 #RE expression for is_event_dependent 1244 separator = re.compile(r'''[+,\-*/()\s]*''') 1245 1246
1247 - def __init__(self, model):
1248 1249 self.model = model # UFOMODEL 1250 self.perturbation_couplings = {} 1251 try: 1252 for order in model.all_orders: # Check if it is a loop model or not 1253 if(order.perturbative_expansion>0): 1254 self.perturbation_couplings[order.name]=order.perturbative_expansion 1255 except AttributeError: 1256 pass 1257 self.params = {} # depend on -> ModelVariable 1258 self.couplings = {} # depend on -> ModelVariable 1259 self.all_expr = {} # variable_name -> ModelVariable
1260
1261 - def main(self, additional_couplings = []):
1262 """Launch the actual computation and return the associate 1263 params/couplings. Possibly consider additional_couplings in addition 1264 to those defined in the UFO model attribute all_couplings """ 1265 1266 additional_params = [] 1267 if hasattr(self.model,'all_CTparameters'): 1268 additional_params = self.get_additional_CTparameters() 1269 1270 self.analyze_parameters(additional_params = additional_params) 1271 self.analyze_couplings(additional_couplings = additional_couplings) 1272 1273 # Finally revert the possible modifications done by treat_couplings() 1274 if hasattr(self.model,'all_CTparameters'): 1275 self.revert_CTCoupling_modifications() 1276 1277 return self.params, self.couplings
1278
1280 """ Finally revert the possible modifications done by treat_couplings() 1281 in UFOMG5Converter which were useful for the add_CTinteraction() in 1282 particular. This modification consisted in expanding the value of a 1283 CTCoupling which consisted in an expression in terms of a CTParam to 1284 its corresponding dictionary (e.g 1285 CTCoupling.value = 2*CTParam -> 1286 CTCoupling.value = {-1: 2*CTParam_1EPS_, 0: 2*CTParam_FIN_} 1287 for example if CTParam had a non-zero finite and single pole.""" 1288 1289 for coupl in self.model.all_couplings: 1290 if hasattr(coupl,'old_value'): 1291 coupl.value = coupl.old_value 1292 del(coupl.old_value)
1293
1295 """ For each CTparameter split it into spimple parameter for each pole 1296 and the finite part if not zero.""" 1297 1298 additional_params = [] 1299 for CTparam in self.model.all_CTparameters: 1300 for pole in range(3): 1301 if CTparam.pole(pole) != 'ZERO': 1302 CTparam_piece = copy.copy(CTparam) 1303 CTparam_piece.name = '%s_%s_'%(CTparam.name,pole_dict[-pole]) 1304 CTparam_piece.nature = 'internal' 1305 CTparam_piece.type = CTparam.type 1306 CTparam_piece.value = CTparam.pole(pole) 1307 CTparam_piece.texname = '%s_{%s}'%\ 1308 (CTparam.texname,pole_dict[-pole]) 1309 additional_params.append(CTparam_piece) 1310 return additional_params
1311
1312 - def analyze_parameters(self, additional_params=[]):
1313 """ separate the parameters needed to be recomputed events by events and 1314 the others""" 1315 # in order to match in Gmu scheme 1316 # test whether aEWM1 is the external or not 1317 # if not, take Gf as the track_dependant variable 1318 present_aEWM1 = any(param.name == 'aEWM1' for param in 1319 self.model.all_parameters if param.nature == 'external') 1320 1321 if not present_aEWM1: 1322 self.track_dependant = ['aS','Gf','MU_R'] 1323 1324 for param in self.model.all_parameters+additional_params: 1325 if param.nature == 'external': 1326 parameter = base_objects.ParamCardVariable(param.name, param.value, \ 1327 param.lhablock, param.lhacode) 1328 1329 else: 1330 expr = self.shorten_expr(param.value) 1331 depend_on = self.find_dependencies(expr) 1332 parameter = base_objects.ModelVariable(param.name, expr, param.type, depend_on) 1333 1334 self.add_parameter(parameter)
1335
1336 - def add_parameter(self, parameter):
1337 """ add consistently the parameter in params and all_expr. 1338 avoid duplication """ 1339 1340 assert isinstance(parameter, base_objects.ModelVariable) 1341 1342 if parameter.name in self.all_expr: 1343 return 1344 1345 self.all_expr[parameter.name] = parameter 1346 try: 1347 self.params[parameter.depend].append(parameter) 1348 except: 1349 self.params[parameter.depend] = [parameter]
1350
1351 - def add_coupling(self, coupling):
1352 """ add consistently the coupling in couplings and all_expr. 1353 avoid duplication """ 1354 1355 assert isinstance(coupling, base_objects.ModelVariable) 1356 1357 if coupling.name in self.all_expr: 1358 return 1359 self.all_expr[coupling.value] = coupling 1360 try: 1361 self.coupling[coupling.depend].append(coupling) 1362 except: 1363 self.coupling[coupling.depend] = [coupling]
1364
1365 - def analyze_couplings(self,additional_couplings=[]):
1366 """creates the shortcut for all special function/parameter 1367 separate the couplings dependent of track variables of the others""" 1368 1369 # For loop models, make sure that all couplings with dictionary values 1370 # are turned into set of couplings, one for each pole and finite part. 1371 if self.perturbation_couplings: 1372 couplings_list=[] 1373 for coupling in self.model.all_couplings + additional_couplings: 1374 if not isinstance(coupling.value,dict): 1375 couplings_list.append(coupling) 1376 else: 1377 for poleOrder in range(0,3): 1378 if coupling.pole(poleOrder)!='ZERO': 1379 newCoupling=copy.copy(coupling) 1380 if poleOrder!=0: 1381 newCoupling.name += "_%deps"%poleOrder 1382 newCoupling.value=coupling.pole(poleOrder) 1383 # assign the CT parameter dependences 1384 # if hasattr(coupling,'CTparam_dependence') and \ 1385 # (-poleOrder in coupling.CTparam_dependence) and \ 1386 # coupling.CTparam_dependence[-poleOrder]: 1387 # newCoupling.CTparam_dependence = coupling.CTparam_dependence[-poleOrder] 1388 # elif hasattr(newCoupling,'CTparam_dependence'): 1389 # delattr(newCoupling,"CTparam_dependence") 1390 couplings_list.append(newCoupling) 1391 else: 1392 couplings_list = self.model.all_couplings + additional_couplings 1393 couplings_list = [c for c in couplings_list if not isinstance(c.value, dict)] 1394 1395 for coupling in couplings_list: 1396 # shorten expression, find dependencies, create short object 1397 expr = self.shorten_expr(coupling.value) 1398 depend_on = self.find_dependencies(expr) 1399 parameter = base_objects.ModelVariable(coupling.name, expr, 'complex', depend_on) 1400 # Add consistently in the couplings/all_expr 1401 try: 1402 self.couplings[depend_on].append(parameter) 1403 except KeyError: 1404 self.couplings[depend_on] = [parameter] 1405 self.all_expr[coupling.value] = parameter
1406
1407 - def find_dependencies(self, expr):
1408 """check if an expression should be evaluated points by points or not 1409 """ 1410 depend_on = set() 1411 1412 # Treat predefined result 1413 #if name in self.track_dependant: 1414 # return tuple() 1415 1416 # Split the different part of the expression in order to say if a 1417 #subexpression is dependent of one of tracked variable 1418 expr = self.separator.split(expr) 1419 1420 # look for each subexpression 1421 for subexpr in expr: 1422 if subexpr in self.track_dependant: 1423 depend_on.add(subexpr) 1424 1425 elif subexpr in self.all_expr and self.all_expr[subexpr].depend: 1426 [depend_on.add(value) for value in self.all_expr[subexpr].depend 1427 if self.all_expr[subexpr].depend != ('external',)] 1428 if depend_on: 1429 return tuple(depend_on) 1430 else: 1431 return tuple()
1432 1433
1434 - def shorten_expr(self, expr):
1435 """ apply the rules of contraction and fullfill 1436 self.params with dependent part""" 1437 try: 1438 expr = self.complex_number.sub(self.shorten_complex, expr) 1439 expr = self.expo_expr.sub(self.shorten_expo, expr) 1440 expr = self.cmath_expr.sub(self.shorten_cmath, expr) 1441 expr = self.conj_expr.sub(self.shorten_conjugate, expr) 1442 except Exception: 1443 logger.critical("fail to handle expression: %s, type()=%s", expr,type(expr)) 1444 raise 1445 return expr
1446 1447
1448 - def shorten_complex(self, matchobj):
1449 """add the short expression, and return the nice string associate""" 1450 1451 float_real = float(eval(matchobj.group('real'))) 1452 float_imag = float(eval(matchobj.group('imag'))) 1453 if float_real == 0 and float_imag ==1: 1454 new_param = base_objects.ModelVariable('complexi', 'complex(0,1)', 'complex') 1455 self.add_parameter(new_param) 1456 return 'complexi' 1457 else: 1458 return 'complex(%s, %s)' % (matchobj.group('real'), matchobj.group('imag'))
1459 1460
1461 - def shorten_expo(self, matchobj):
1462 """add the short expression, and return the nice string associate""" 1463 1464 expr = matchobj.group('expr') 1465 exponent = matchobj.group('expo') 1466 new_exponent = exponent.replace('.','_').replace('+','').replace('-','_m_') 1467 output = '%s__exp__%s' % (expr, new_exponent) 1468 old_expr = '%s**%s' % (expr,exponent) 1469 1470 if expr.startswith('cmath'): 1471 return old_expr 1472 1473 if expr.isdigit(): 1474 output = 'nb__' + output #prevent to start with a number 1475 new_param = base_objects.ModelVariable(output, old_expr,'real') 1476 else: 1477 depend_on = self.find_dependencies(expr) 1478 type = self.search_type(expr) 1479 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1480 self.add_parameter(new_param) 1481 return output
1482
1483 - def shorten_cmath(self, matchobj):
1484 """add the short expression, and return the nice string associate""" 1485 1486 expr = matchobj.group('expr') 1487 operation = matchobj.group('operation') 1488 output = '%s__%s' % (operation, expr) 1489 old_expr = ' cmath.%s(%s) ' % (operation, expr) 1490 if expr.isdigit(): 1491 new_param = base_objects.ModelVariable(output, old_expr , 'real') 1492 else: 1493 depend_on = self.find_dependencies(expr) 1494 type = self.search_type(expr) 1495 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1496 self.add_parameter(new_param) 1497 1498 return output
1499
1500 - def shorten_conjugate(self, matchobj):
1501 """add the short expression, and retrun the nice string associate""" 1502 1503 expr = matchobj.group('expr') 1504 output = 'conjg__%s' % (expr) 1505 old_expr = ' complexconjugate(%s) ' % expr 1506 depend_on = self.find_dependencies(expr) 1507 type = 'complex' 1508 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on) 1509 self.add_parameter(new_param) 1510 1511 return output
1512 1513 1514
1515 - def search_type(self, expr, dep=''):
1516 """return the type associate to the expression if define""" 1517 1518 try: 1519 return self.all_expr[expr].type 1520 except: 1521 return 'complex'
1522
1523 -class RestrictModel(model_reader.ModelReader):
1524 """ A class for restricting a model for a given param_card. 1525 rules applied: 1526 - Vertex with zero couplings are throw away 1527 - external parameter with zero/one input are changed into internal parameter. 1528 - identical coupling/mass/width are replace in the model by a unique one 1529 """ 1530
1531 - def default_setup(self):
1532 """define default value""" 1533 self.del_coup = [] 1534 super(RestrictModel, self).default_setup() 1535 self.rule_card = check_param_card.ParamCardRule() 1536 self.restrict_card = None
1537
1538 - def restrict_model(self, param_card, rm_parameter=True, keep_external=False, 1539 complex_mass_scheme=None):
1540 """apply the model restriction following param_card. 1541 rm_parameter defines if the Zero/one parameter are removed or not from 1542 the model. 1543 keep_external if the param_card need to be kept intact 1544 """ 1545 1546 if self.get('name') == "mssm" and not keep_external: 1547 raise Exception 1548 self.restrict_card = param_card 1549 # Reset particle dict to ensure synchronized particles and interactions 1550 self.set('particles', self.get('particles')) 1551 1552 # compute the value of all parameters 1553 # Get the list of definition of model functions, parameter values. 1554 self.set_parameters_and_couplings(param_card, 1555 complex_mass_scheme=complex_mass_scheme) 1556 1557 1558 # Keep the list of definition of model functions, parameter values. 1559 model_definitions = self.set_parameters_and_couplings(param_card) 1560 1561 # Simplify conditional statements 1562 logger.debug('Simplifying conditional expressions') 1563 modified_params, modified_couplings = \ 1564 self.detect_conditional_statements_simplifications(model_definitions) 1565 1566 # Apply simplifications 1567 self.apply_conditional_simplifications(modified_params, modified_couplings) 1568 1569 # associate to each couplings the associated vertex: def self.coupling_pos 1570 self.locate_coupling() 1571 # deal with couplings 1572 zero_couplings, iden_couplings = self.detect_identical_couplings() 1573 1574 # remove the out-dated interactions 1575 self.remove_interactions(zero_couplings) 1576 1577 # replace in interactions identical couplings 1578 for iden_coups in iden_couplings: 1579 self.merge_iden_couplings(iden_coups) 1580 1581 # remove zero couplings and other pointless couplings 1582 self.del_coup += zero_couplings 1583 self.remove_couplings(self.del_coup) 1584 1585 # deal with parameters 1586 parameters = self.detect_special_parameters() 1587 self.fix_parameter_values(*parameters, simplify=rm_parameter, 1588 keep_external=keep_external) 1589 1590 # deal with identical parameters 1591 if not keep_external: 1592 iden_parameters = self.detect_identical_parameters() 1593 for iden_param in iden_parameters: 1594 self.merge_iden_parameters(iden_param) 1595 1596 iden_parameters = self.detect_identical_parameters() 1597 for iden_param in iden_parameters: 1598 self.merge_iden_parameters(iden_param, keep_external) 1599 1600 # change value of default parameter if they have special value: 1601 # 9.999999e-1 -> 1.0 1602 # 0.000001e-99 -> 0 Those value are used to avoid restriction 1603 for name, value in self['parameter_dict'].items(): 1604 if value == 9.999999e-1: 1605 self['parameter_dict'][name] = 1 1606 elif value == 0.000001e-99: 1607 self['parameter_dict'][name] = 0
1608 1609
1610 - def locate_coupling(self):
1611 """ create a dict couplings_name -> vertex or (particle, counterterm_key) """ 1612 1613 self.coupling_pos = {} 1614 for vertex in self['interactions']: 1615 for key, coupling in vertex['couplings'].items(): 1616 if coupling in self.coupling_pos: 1617 if vertex not in self.coupling_pos[coupling]: 1618 self.coupling_pos[coupling].append(vertex) 1619 else: 1620 self.coupling_pos[coupling] = [vertex] 1621 1622 for particle in self['particles']: 1623 for key, coupling_dict in particle['counterterm'].items(): 1624 for LaurentOrder, coupling in coupling_dict.items(): 1625 if coupling in self.coupling_pos: 1626 if (particle,key) not in self.coupling_pos[coupling]: 1627 self.coupling_pos[coupling].append((particle,key)) 1628 else: 1629 self.coupling_pos[coupling] = [(particle,key)] 1630 1631 return self.coupling_pos
1632
1633 - def detect_identical_couplings(self, strict_zero=False):
1634 """return a list with the name of all vanishing couplings""" 1635 1636 dict_value_coupling = {} 1637 iden_key = set() 1638 zero_coupling = [] 1639 iden_coupling = [] 1640 1641 for name, value in self['coupling_dict'].items(): 1642 if value == 0: 1643 zero_coupling.append(name) 1644 continue 1645 elif not strict_zero and abs(value) < 1e-13: 1646 logger.debug('coupling with small value %s: %s treated as zero' % 1647 (name, value)) 1648 zero_coupling.append(name) 1649 elif not strict_zero and abs(value) < 1e-10: 1650 return self.detect_identical_couplings(strict_zero=True) 1651 1652 1653 if value in dict_value_coupling: 1654 iden_key.add(value) 1655 dict_value_coupling[value].append(name) 1656 else: 1657 dict_value_coupling[value] = [name] 1658 1659 for key in iden_key: 1660 iden_coupling.append(dict_value_coupling[key]) 1661 1662 return zero_coupling, iden_coupling
1663 1664
1665 - def detect_special_parameters(self):
1666 """ return the list of (name of) parameter which are zero """ 1667 1668 null_parameters = [] 1669 one_parameters = [] 1670 for name, value in self['parameter_dict'].items(): 1671 if value == 0 and name != 'ZERO': 1672 null_parameters.append(name) 1673 elif value == 1: 1674 one_parameters.append(name) 1675 1676 return null_parameters, one_parameters
1677
1678 - def apply_conditional_simplifications(self, modified_params, 1679 modified_couplings):
1680 """ Apply the conditional statement simplifications for parameters and 1681 couplings detected by 'simplify_conditional_statements'. 1682 modified_params (modified_couplings) are list of tuples (a,b) with a 1683 parameter (resp. coupling) instance and b is the simplified expression.""" 1684 1685 if modified_params: 1686 logger.debug("Conditional expressions are simplified for parameters:") 1687 logger.debug(",".join("%s"%param[0].name for param in modified_params)) 1688 for param, new_expr in modified_params: 1689 param.expr = new_expr 1690 1691 if modified_couplings: 1692 logger.debug("Conditional expressions are simplified for couplings:") 1693 logger.debug(",".join("%s"%coupl[0].name for coupl in modified_couplings)) 1694 for coupl, new_expr in modified_couplings: 1695 coupl.expr = new_expr
1696
1697 - def detect_conditional_statements_simplifications(self, model_definitions, 1698 objects=['couplings','parameters']):
1699 """ Simplifies the 'if' statements in the pythonic UFO expressions 1700 of parameters using the default variables specified in the restrict card. 1701 It returns a list of objects (parameters or couplings) and the new 1702 expression that they should take. Model definitions include all definitons 1703 of the model functions and parameters.""" 1704 1705 param_modifications = [] 1706 coupl_modifications = [] 1707 ifparser = parsers.UFOExpressionParserPythonIF(model_definitions) 1708 1709 start_param = time.time() 1710 if 'parameters' in objects: 1711 for dependences, param_list in self['parameters'].items(): 1712 if 'external' in dependences: 1713 continue 1714 for param in param_list: 1715 new_expr, n_changes = ifparser.parse(param.expr) 1716 if n_changes > 0: 1717 param_modifications.append((param, new_expr)) 1718 1719 end_param = time.time() 1720 1721 if 'couplings' in objects: 1722 for dependences, coupl_list in self['couplings'].items(): 1723 for coupl in coupl_list: 1724 new_expr, n_changes = ifparser.parse(coupl.expr) 1725 if n_changes > 0: 1726 coupl_modifications.append((coupl, new_expr)) 1727 1728 end_coupl = time.time() 1729 1730 tot_param_time = end_param-start_param 1731 tot_coupl_time = end_coupl-end_param 1732 if tot_param_time>5.0: 1733 logger.debug("Simplification of conditional statements"+\ 1734 " in parameter expressions done in %s."%misc.format_time(tot_param_time)) 1735 if tot_coupl_time>5.0: 1736 logger.debug("Simplification of conditional statements"+\ 1737 " in couplings expressions done in %s."%misc.format_time(tot_coupl_time)) 1738 1739 return param_modifications, coupl_modifications
1740
1742 """ return the list of tuple of name of parameter with the same 1743 input value """ 1744 1745 # Extract external parameters 1746 external_parameters = self['parameters'][('external',)] 1747 1748 # define usefull variable to detect identical input 1749 block_value_to_var={} #(lhablok, value): list_of_var 1750 mult_param = set([]) # key of the previous dict with more than one 1751 #parameter. 1752 1753 #detect identical parameter and remove the duplicate parameter 1754 for param in external_parameters[:]: 1755 value = self['parameter_dict'][param.name] 1756 if value in [0,1,0.000001e-99,9.999999e-1]: 1757 continue 1758 if param.lhablock.lower() == 'decay': 1759 continue 1760 1761 key = (param.lhablock, value) 1762 mkey = (param.lhablock, -value) 1763 if key in block_value_to_var: 1764 block_value_to_var[key].append((param,1)) 1765 mult_param.add(key) 1766 elif mkey in block_value_to_var: 1767 block_value_to_var[mkey].append((param,-1)) 1768 mult_param.add(mkey) 1769 else: 1770 block_value_to_var[key] = [(param,1)] 1771 1772 output=[] 1773 for key in mult_param: 1774 output.append(block_value_to_var[key]) 1775 1776 return output
1777 1778
1779 - def merge_iden_couplings(self, couplings):
1780 """merge the identical couplings in the interactions and particle 1781 counterterms""" 1782 1783 1784 logger_mod.debug(' Fuse the Following coupling (they have the same value): %s '% \ 1785 ', '.join([obj for obj in couplings])) 1786 1787 main = couplings[0] 1788 self.del_coup += couplings[1:] # add the other coupl to the suppress list 1789 1790 for coupling in couplings[1:]: 1791 # check if param is linked to an interaction 1792 if coupling not in self.coupling_pos: 1793 continue 1794 # replace the coupling, by checking all coupling of the interaction 1795 vertices = [ vert for vert in self.coupling_pos[coupling] if 1796 isinstance(vert, base_objects.Interaction)] 1797 for vertex in vertices: 1798 for key, value in vertex['couplings'].items(): 1799 if value == coupling: 1800 vertex['couplings'][key] = main 1801 1802 # replace the coupling appearing in the particle counterterm 1803 particles_ct = [ pct for pct in self.coupling_pos[coupling] if 1804 isinstance(pct, tuple)] 1805 for pct in particles_ct: 1806 for key, value in pct[0]['counterterm'][pct[1]].items(): 1807 if value == coupling: 1808 pct[0]['counterterm'][pct[1]][key] = main
1809 1810
1811 - def merge_iden_parameters(self, parameters, keep_external=False):
1812 """ merge the identical parameters given in argument. 1813 keep external force to keep the param_card untouched (up to comment)""" 1814 1815 logger_mod.debug('Parameters set to identical values: %s '% \ 1816 ', '.join(['%s*%s' % (f, obj.name.replace('mdl_','')) for (obj,f) in parameters])) 1817 1818 # Extract external parameters 1819 external_parameters = self['parameters'][('external',)] 1820 for i, (obj, factor) in enumerate(parameters): 1821 # Keeped intact the first one and store information 1822 if i == 0: 1823 obj.info = 'set of param :' + \ 1824 ', '.join([str(f)+'*'+param.name.replace('mdl_','') 1825 for (param, f) in parameters]) 1826 expr = obj.name 1827 continue 1828 # Add a Rule linked to the param_card 1829 if factor ==1: 1830 self.rule_card.add_identical(obj.lhablock.lower(), obj.lhacode, 1831 parameters[0][0].lhacode ) 1832 else: 1833 self.rule_card.add_opposite(obj.lhablock.lower(), obj.lhacode, 1834 parameters[0][0].lhacode ) 1835 obj_name = obj.name 1836 # delete the old parameters 1837 if not keep_external: 1838 external_parameters.remove(obj) 1839 elif obj.lhablock.upper() in ['MASS','DECAY']: 1840 external_parameters.remove(obj) 1841 else: 1842 obj.name = '' 1843 obj.info = 'MG5 will not use this value use instead %s*%s' %(factor,expr) 1844 # replace by the new one pointing of the first obj of the class 1845 new_param = base_objects.ModelVariable(obj_name, '%s*%s' %(factor, expr), 'real') 1846 self['parameters'][()].insert(0, new_param) 1847 1848 # For Mass-Width, we need also to replace the mass-width in the particles 1849 #This allows some optimization for multi-process. 1850 if parameters[0][0].lhablock in ['MASS','DECAY']: 1851 new_name = parameters[0][0].name 1852 if parameters[0][0].lhablock == 'MASS': 1853 arg = 'mass' 1854 else: 1855 arg = 'width' 1856 change_name = [p.name for (p,f) in parameters[1:]] 1857 [p.set(arg, new_name) for p in self['particle_dict'].values() 1858 if p[arg] in change_name]
1859
1860 - def remove_interactions(self, zero_couplings):
1861 """ remove the interactions and particle counterterms 1862 associated to couplings""" 1863 1864 1865 mod_vertex = [] 1866 mod_particle_ct = [] 1867 for coup in zero_couplings: 1868 # some coupling might be not related to any interactions 1869 if coup not in self.coupling_pos: 1870 continue 1871 1872 # Remove the corresponding interactions. 1873 1874 vertices = [ vert for vert in self.coupling_pos[coup] if 1875 isinstance(vert, base_objects.Interaction) ] 1876 for vertex in vertices: 1877 modify = False 1878 for key, coupling in vertex['couplings'].items(): 1879 if coupling in zero_couplings: 1880 modify=True 1881 del vertex['couplings'][key] 1882 if modify: 1883 mod_vertex.append(vertex) 1884 1885 # Remove the corresponding particle counterterm 1886 particles_ct = [ pct for pct in self.coupling_pos[coup] if 1887 isinstance(pct, tuple)] 1888 for pct in particles_ct: 1889 modify = False 1890 for key, coupling in pct[0]['counterterm'][pct[1]].items(): 1891 if coupling in zero_couplings: 1892 modify=True 1893 del pct[0]['counterterm'][pct[1]][key] 1894 if modify: 1895 mod_particle_ct.append(pct) 1896 1897 # print useful log and clean the empty interaction 1898 for vertex in mod_vertex: 1899 part_name = [part['name'] for part in vertex['particles']] 1900 orders = ['%s=%s' % (order,value) for order,value in vertex['orders'].items()] 1901 1902 if not vertex['couplings']: 1903 logger_mod.debug('remove interactions: %s at order: %s' % \ 1904 (' '.join(part_name),', '.join(orders))) 1905 self['interactions'].remove(vertex) 1906 else: 1907 logger_mod.debug('modify interactions: %s at order: %s' % \ 1908 (' '.join(part_name),', '.join(orders))) 1909 1910 # print useful log and clean the empty counterterm values 1911 for pct in mod_particle_ct: 1912 part_name = pct[0]['name'] 1913 order = pct[1][0] 1914 loop_parts = ','.join(['('+','.join([\ 1915 self.get_particle(p)['name'] for p in part])+')' \ 1916 for part in pct[1][1]]) 1917 1918 if not pct[0]['counterterm'][pct[1]]: 1919 logger_mod.debug('remove counterterm of particle %s'%part_name+\ 1920 ' with loop particles (%s)'%loop_parts+\ 1921 ' perturbing order %s'%order) 1922 del pct[0]['counterterm'][pct[1]] 1923 else: 1924 logger_mod.debug('Modify counterterm of particle %s'%part_name+\ 1925 ' with loop particles (%s)'%loop_parts+\ 1926 ' perturbing order %s'%order) 1927 1928 return
1929
1930 - def remove_couplings(self, couplings):
1931 #clean the coupling list: 1932 for name, data in self['couplings'].items(): 1933 for coupling in data[:]: 1934 if coupling.name in couplings: 1935 data.remove(coupling)
1936 1937
1938 - def fix_parameter_values(self, zero_parameters, one_parameters, 1939 simplify=True, keep_external=False):
1940 """ Remove all instance of the parameters in the model and replace it by 1941 zero when needed.""" 1942 1943 1944 # treat specific cases for masses and width 1945 for particle in self['particles']: 1946 if particle['mass'] in zero_parameters: 1947 particle['mass'] = 'ZERO' 1948 if particle['width'] in zero_parameters: 1949 particle['width'] = 'ZERO' 1950 if particle['width'] in one_parameters: 1951 one_parameters.remove(particle['width']) 1952 1953 for pdg, particle in self['particle_dict'].items(): 1954 if particle['mass'] in zero_parameters: 1955 particle['mass'] = 'ZERO' 1956 if particle['width'] in zero_parameters: 1957 particle['width'] = 'ZERO' 1958 1959 1960 # Add a rule for zero/one parameter 1961 external_parameters = self['parameters'][('external',)] 1962 for param in external_parameters[:]: 1963 value = self['parameter_dict'][param.name] 1964 block = param.lhablock.lower() 1965 if value == 0: 1966 self.rule_card.add_zero(block, param.lhacode) 1967 elif value == 1: 1968 self.rule_card.add_one(block, param.lhacode) 1969 1970 special_parameters = zero_parameters + one_parameters 1971 1972 1973 1974 if simplify: 1975 # check if the parameters is still useful: 1976 re_str = '|'.join(special_parameters) 1977 if len(re_str) > 25000: # size limit on mac 1978 split = len(special_parameters) // 2 1979 re_str = ['|'.join(special_parameters[:split]), 1980 '|'.join(special_parameters[split:])] 1981 else: 1982 re_str = [ re_str ] 1983 used = set() 1984 for expr in re_str: 1985 re_pat = re.compile(r'''\b(%s)\b''' % expr) 1986 # check in coupling 1987 for name, coupling_list in self['couplings'].items(): 1988 for coupling in coupling_list: 1989 for use in re_pat.findall(coupling.expr): 1990 used.add(use) 1991 else: 1992 used = set([i for i in special_parameters if i]) 1993 1994 # simplify the regular expression 1995 re_str = '|'.join([param for param in special_parameters if param not in used]) 1996 if len(re_str) > 25000: # size limit on mac 1997 split = len(special_parameters) // 2 1998 re_str = ['|'.join(special_parameters[:split]), 1999 '|'.join(special_parameters[split:])] 2000 else: 2001 re_str = [ re_str ] 2002 for expr in re_str: 2003 re_pat = re.compile(r'''\b(%s)\b''' % expr) 2004 2005 param_info = {} 2006 # check in parameters 2007 for dep, param_list in self['parameters'].items(): 2008 for tag, parameter in enumerate(param_list): 2009 # update information concerning zero/one parameters 2010 if parameter.name in special_parameters: 2011 param_info[parameter.name]= {'dep': dep, 'tag': tag, 2012 'obj': parameter} 2013 continue 2014 2015 # Bypass all external parameter 2016 if isinstance(parameter, base_objects.ParamCardVariable): 2017 continue 2018 2019 if simplify: 2020 for use in re_pat.findall(parameter.expr): 2021 used.add(use) 2022 2023 # modify the object for those which are still used 2024 for param in used: 2025 if not param: 2026 continue 2027 data = self['parameters'][param_info[param]['dep']] 2028 data.remove(param_info[param]['obj']) 2029 tag = param_info[param]['tag'] 2030 data = self['parameters'][()] 2031 if param in zero_parameters: 2032 data.insert(0, base_objects.ModelVariable(param, '0.0', 'real')) 2033 else: 2034 data.insert(0, base_objects.ModelVariable(param, '1.0', 'real')) 2035 2036 # remove completely useless parameters 2037 for param in special_parameters: 2038 #by pass parameter still in use 2039 if param in used or \ 2040 (keep_external and param_info[param]['dep'] == ('external',)): 2041 logger_mod.debug('fix parameter value: %s' % param) 2042 continue 2043 logger_mod.debug('remove parameters: %s' % (param)) 2044 data = self['parameters'][param_info[param]['dep']] 2045 data.remove(param_info[param]['obj'])
2046