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