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