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