Package models :: Module import_ufo
[hide private]
[frames] | no frames]

Source Code for Module models.import_ufo

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