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