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