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