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

Source Code for Module models.import_ufo

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