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