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 from __future__ import absolute_import
18 import collections
19 import fractions
20 import logging
21 import math
22 import os
23 import re
24 import sys
25 import time
26 import collections
27
28 import madgraph
29 from madgraph import MadGraph5Error, MG5DIR, ReadWrite
30 import madgraph.core.base_objects as base_objects
31 import madgraph.loop.loop_base_objects as loop_base_objects
32 import madgraph.core.color_algebra as color
33 import madgraph.iolibs.files as files
34 import madgraph.iolibs.save_load_object as save_load_object
35 from madgraph.core.color_algebra import *
36 import madgraph.various.misc as misc
37 import madgraph.iolibs.ufo_expression_parsers as parsers
38
39 import aloha
40 import aloha.create_aloha as create_aloha
41 import aloha.aloha_fct as aloha_fct
42
43 import models as ufomodels
44 import models.model_reader as model_reader
45 import six
46 from six.moves import range
47 from six.moves import zip
48 logger = logging.getLogger('madgraph.model')
49 logger_mod = logging.getLogger('madgraph.model')
50
51 root_path = os.path.dirname(os.path.realpath( __file__ ))
52 sys.path.append(root_path)
53
54 sys.path.append(os.path.join(root_path, os.path.pardir, 'Template', 'bin', 'internal'))
55 from . import check_param_card
56
57 pjoin = os.path.join
58
59
60 pole_dict = {-2:'2EPS',-1:'1EPS',0:'FIN'}
63 """ a error class for wrong import of UFO model"""
64
66 """ a class for invalid Model """
67
68 last_model_path =''
70 """ find the path to a model """
71
72 global last_model_path
73
74
75 if model_name.startswith(('./','../')) and os.path.isdir(model_name):
76 return model_name
77 elif os.path.isdir(os.path.join(MG5DIR, 'models', model_name)):
78 return os.path.join(MG5DIR, 'models', model_name)
79 elif 'PYTHONPATH' in os.environ:
80 for p in os.environ['PYTHONPATH'].split(':'):
81 if os.path.isdir(os.path.join(MG5DIR, p, model_name)):
82 if last_model_path != os.path.join(MG5DIR, p, model_name):
83 logger.info("model loaded from PYTHONPATH: %s", os.path.join(MG5DIR, p, model_name))
84 last_model_path = os.path.join(MG5DIR, p, model_name)
85 return os.path.join(MG5DIR, p, model_name)
86 if os.path.isdir(model_name):
87 logger.warning('No model %s found in default path. Did you mean \'import model ./%s\'',
88 model_name, model_name)
89 if os.path.sep in model_name:
90 raise UFOImportError("Path %s is not a valid pathname" % model_name)
91 elif web_search and '-' not in model_name:
92 found = import_model_from_db(model_name)
93 if found:
94 return find_ufo_path(model_name, web_search=False)
95 else:
96 raise UFOImportError("Path %s is not a valid pathname" % model_name)
97 else:
98 raise UFOImportError("Path %s is not a valid pathname" % model_name)
99
100 raise UFOImportError("Path %s is not a valid pathname" % model_name)
101 return
102
105 """return the file with the online model database"""
106
107 data_path = ['http://madgraph.phys.ucl.ac.be/models_db.dat',
108 'http://madgraph.physics.illinois.edu/models_db.dat']
109 import random
110 import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
111 r = random.randint(0,1)
112 r = [r, (1-r)]
113
114 if 'MG5aMC_WWW' in os.environ and os.environ['MG5aMC_WWW']:
115 data_path.append(os.environ['MG5aMC_WWW']+'/models_db.dat')
116 r.insert(0, 2)
117
118
119 for index in r:
120 cluster_path = data_path[index]
121 try:
122 data = six.moves.urllib.request.urlopen(cluster_path)
123 except Exception:
124 continue
125 if data.getcode() != 200:
126 continue
127 break
128 else:
129 raise MadGraph5Error('''Model not found locally and Impossible to connect any of us servers.
130 Please check your internet connection or retry later''')
131
132 return data
133
135 """ import the model with a given name """
136
137 if os.path.sep in model_name and os.path.exists(os.path.dirname(model_name)):
138 target = os.path.dirname(model_name)
139 model_name = os.path.basename(model_name)
140 else:
141 target = None
142 data =get_model_db()
143 link = None
144 for line in data:
145 split = line.decode().split()
146 if model_name == split[0]:
147 link = split[1]
148 break
149 else:
150 logger.debug('no model with that name (%s) found online', model_name)
151 return False
152
153
154
155
156
157 username = ''
158 if not target:
159 try:
160 import pwd
161 username =pwd.getpwuid( os.getuid() )[ 0 ]
162 except Exception as error:
163 misc.sprint(str(error))
164 username = ''
165 if username in ['omatt', 'mattelaer', 'olivier', 'omattelaer'] and target is None and \
166 'PYTHONPATH' in os.environ and not local_dir:
167 for directory in os.environ['PYTHONPATH'].split(':'):
168
169 if 'UFOMODEL' == os.path.basename(directory) and os.path.exists(directory) and\
170 misc.glob('*/couplings.py', path=directory) and 'matt' in directory:
171 target= directory
172
173 if target is None:
174 target = pjoin(MG5DIR, 'models')
175 try:
176 os.remove(pjoin(target, 'tmp.tgz'))
177 except Exception:
178 pass
179 logger.info("download model from %s to the following directory: %s", link, target, '$MG:color:BLACK')
180 misc.wget(link, 'tmp.tgz', cwd=target)
181
182
183
184 if link.endswith(('.tgz','.tar.gz','.tar')):
185 try:
186 proc = misc.call('tar -xzpvf tmp.tgz', shell=True, cwd=target)
187 if proc: raise Exception
188 except:
189 proc = misc.call('tar -xpvf tmp.tgz', shell=True, cwd=target)
190
191 if link.endswith(('.zip')):
192 try:
193 proc = misc.call('unzip tmp.tgz', shell=True, cwd=target)
194 if proc: raise Exception
195 except:
196 proc = misc.call('tar -xzpvf tmp.tgz', shell=True, cwd=target)
197 if proc:
198 raise Exception("Impossible to unpack the model. Please install it manually")
199 return True
200
202
203 try:
204 model_path = find_ufo_path(model_name)
205 except UFOImportError:
206 if '-' not in model_name:
207 if model_name == "mssm":
208 logger.error("mssm model has been replaced by MSSM_SLHA2 model.\n The new model require SLHA2 format. You can use the \"update to_slha2\" command to convert your slha1 param_card.\n That option is available at the time of the edition of the cards.")
209 raise
210 split = model_name.split('-')
211 model_name = '-'.join([text for text in split[:-1]])
212 try:
213 model_path = find_ufo_path(model_name)
214 except UFOImportError:
215 if model_name == "mssm":
216 logger.error("mssm model has been replaced by MSSM_SLHA2 model.\n The new model require SLHA2 format. You can use the \"update to_slha2\" command to convert your slha1 param_card.\n That option is available at the time of the edition of the cards.")
217 raise
218 restrict_name = split[-1]
219
220 restrict_file = os.path.join(model_path, 'restrict_%s.dat'% restrict_name)
221
222
223 if split[-1] == 'full':
224 restrict_file = None
225 else:
226
227 restrict_name = ""
228 if restrict and os.path.exists(os.path.join(model_path,'restrict_default.dat')):
229 restrict_file = os.path.join(model_path,'restrict_default.dat')
230 else:
231 restrict_file = None
232
233 if isinstance(restrict, str):
234 if os.path.exists(os.path.join(model_path, restrict)):
235 restrict_file = os.path.join(model_path, restrict)
236 elif os.path.exists(restrict):
237 restrict_file = restrict
238 else:
239 raise Exception("%s is not a valid path for restrict file" % restrict)
240
241 return model_path, restrict_file, restrict_name
242
243 -def import_model(model_name, decay=False, restrict=True, prefix='mdl_',
244 complex_mass_scheme = None):
245 """ a practical and efficient way to import a model"""
246
247 model_path, restrict_file, restrict_name = get_path_restrict(model_name, restrict)
248
249
250 model = import_full_model(model_path, decay, prefix)
251
252 if os.path.exists(pjoin(model_path, "README")):
253 logger.info("Please read carefully the README of the model file for instructions/restrictions of the model.",'$MG:color:BLACK')
254
255 if restrict_name:
256 model["name"] += '-' + restrict_name
257
258
259 useCMS = (complex_mass_scheme is None and aloha.complex_mass) or \
260 complex_mass_scheme==True
261
262 if restrict_file:
263 try:
264 logger.info('Restrict model %s with file %s .' % (model_name, os.path.relpath(restrict_file)))
265 except OSError:
266
267 logger.info('Restrict model %s with file %s .' % (model_name, restrict_file))
268
269 if logger_mod.getEffectiveLevel() > 10:
270 logger.info('Run \"set stdout_level DEBUG\" before import for more information.')
271
272 model = RestrictModel(model)
273
274
275
276 if useCMS:
277
278
279
280
281
282 model.set_parameters_and_couplings(param_card = restrict_file,
283 complex_mass_scheme=False)
284 model.change_mass_to_complex_scheme(toCMS=True)
285 else:
286
287
288
289 model.change_mass_to_complex_scheme(toCMS=False)
290
291 blocks = model.get_param_block()
292 if model_name == 'mssm' or os.path.basename(model_name) == 'mssm':
293 keep_external=True
294 elif all( b in blocks for b in ['USQMIX', 'SL2', 'MSOFT', 'YE', 'NMIX', 'TU','MSE2','UPMNS']):
295 keep_external=True
296 elif model_name == 'MSSM_SLHA2' or os.path.basename(model_name) == 'MSSM_SLHA2':
297 keep_external=True
298 else:
299 keep_external=False
300 if keep_external:
301 logger.info('Detect SLHA2 format. keeping restricted parameter in the param_card')
302
303 model.restrict_model(restrict_file, rm_parameter=not decay,
304 keep_external=keep_external, complex_mass_scheme=complex_mass_scheme)
305 model.path = model_path
306 else:
307
308 if useCMS:
309 model.change_mass_to_complex_scheme(toCMS=True)
310 else:
311
312 model.change_mass_to_complex_scheme(toCMS=False)
313
314 return model
315
316
317 _import_once = []
319 """ a practical and efficient way to import one of those models
320 (no restriction file use)"""
321
322 assert model_path == find_ufo_path(model_path)
323
324 if prefix is True:
325 prefix='mdl_'
326
327
328 files_list_prov = ['couplings.py','lorentz.py','parameters.py',
329 'particles.py', 'vertices.py', 'function_library.py',
330 'propagators.py', 'coupling_orders.py']
331
332 if decay:
333 files_list_prov.append('decays.py')
334
335 files_list = []
336 for filename in files_list_prov:
337 filepath = os.path.join(model_path, filename)
338 if not os.path.isfile(filepath):
339 if filename not in ['propagators.py', 'decays.py', 'coupling_orders.py']:
340 raise UFOImportError("%s directory is not a valid UFO model: \n %s is missing" % \
341 (model_path, filename))
342 files_list.append(filepath)
343
344 if aloha.unitary_gauge:
345 pickle_name = 'model.pkl'
346 else:
347 pickle_name = 'model_Feynman.pkl'
348 if decay:
349 pickle_name = 'dec_%s' % pickle_name
350 if six.PY3:
351 pickle_name = 'py3_%s' % pickle_name
352
353 allow_reload = False
354 if files.is_uptodate(os.path.join(model_path, pickle_name), files_list):
355 allow_reload = True
356 try:
357 model = save_load_object.load_from_file( \
358 os.path.join(model_path, pickle_name))
359 except Exception as error:
360 logger.info('failed to load model from pickle file. Try importing UFO from File')
361 else:
362
363 if 'version_tag' in model and not model.get('version_tag') is None and \
364 model.get('version_tag').startswith(os.path.realpath(model_path)) and \
365 model.get('version_tag').endswith('##' + str(misc.get_pkg_info())):
366
367 for key in model.get('parameters'):
368 for param in model['parameters'][key]:
369 value = param.name.lower()
370 if value in ['as','mu_r', 'zero','aewm1']:
371 continue
372 if prefix:
373 if value.startswith(prefix):
374 _import_once.append((model_path, aloha.unitary_gauge, prefix, decay))
375 return model
376 else:
377 logger.info('reload from .py file')
378 break
379 else:
380 if value.startswith('mdl_'):
381 logger.info('reload from .py file')
382 break
383 else:
384 _import_once.append((model_path, aloha.unitary_gauge, prefix, decay))
385 return model
386 else:
387 continue
388 break
389 else:
390 logger.info('reload from .py file')
391
392 if (model_path, aloha.unitary_gauge, prefix, decay) in _import_once and not allow_reload:
393 raise MadGraph5Error('This model %s is modified on disk. To reload it you need to quit/relaunch MG5_aMC ' % model_path)
394
395
396 ufo_model = ufomodels.load_model(model_path, decay)
397 ufo2mg5_converter = UFOMG5Converter(ufo_model)
398 model = ufo2mg5_converter.load_model()
399 if model_path[-1] == '/': model_path = model_path[:-1]
400 model.set('name', os.path.split(model_path)[-1])
401
402
403 parameters, couplings = OrganizeModelExpression(ufo_model).main(\
404 additional_couplings =(ufo2mg5_converter.wavefunction_CT_couplings
405 if ufo2mg5_converter.perturbation_couplings else []))
406
407 model.set('parameters', parameters)
408 model.set('couplings', couplings)
409 model.set('functions', ufo_model.all_functions)
410
411
412
413
414 if decay and hasattr(ufo_model, 'all_decays') and ufo_model.all_decays:
415 start = time.time()
416 for ufo_part in ufo_model.all_particles:
417 name = ufo_part.name
418 if not model['case_sensitive']:
419 name = name.lower()
420 p = model['particles'].find_name(name)
421 if hasattr(ufo_part, 'partial_widths'):
422 p.partial_widths = ufo_part.partial_widths
423 elif p and not hasattr(p, 'partial_widths'):
424 p.partial_widths = {}
425
426 logger.debug("load width takes %s", time.time()-start)
427
428 if prefix:
429 start = time.time()
430 model.change_parameter_name_with_prefix()
431 logger.debug("model prefixing takes %s", time.time()-start)
432
433 path = os.path.dirname(os.path.realpath(model_path))
434 path = os.path.join(path, model.get('name'))
435 model.set('version_tag', os.path.realpath(path) +'##'+ str(misc.get_pkg_info()))
436
437
438 if ReadWrite and model['allow_pickle']:
439 save_load_object.save_to_file(os.path.join(model_path, pickle_name),
440 model, log=False, allow_fail=True)
441
442
443
444
445
446
447 return model
448
450 """Convert a UFO model to the MG5 format"""
451
453 """ initialize empty list for particles/interactions """
454
455 if hasattr(model, '__header__'):
456 header = model.__header__
457 if len(header) > 500 or header.count('\n') > 5:
458 logger.debug("Too long header")
459 else:
460 logger.info("\n"+header)
461 else:
462 f =collections.defaultdict(lambda : 'n/a')
463 for key in ['author', 'version', 'email', 'arxiv']:
464 if hasattr(model, '__%s__' % key):
465 val = getattr(model, '__%s__' % key)
466 if 'Duhr' in val:
467 continue
468 f[key] = getattr(model, '__%s__' % key)
469
470 if len(f)>2:
471 logger.info("This model [version %(version)s] is provided by %(author)s (email: %(email)s). Please cite %(arxiv)s" % f, '$MG:color:BLACK')
472 elif hasattr(model, '__arxiv__'):
473 logger.info('Please cite %s when using this model', model.__arxiv__, '$MG:color:BLACK')
474
475 self.particles = base_objects.ParticleList()
476 self.interactions = base_objects.InteractionList()
477 self.non_qcd_gluon_emission = 0
478
479 self.wavefunction_CT_couplings = []
480
481
482
483
484 self.perturbation_couplings = {}
485 try:
486 for order in model.all_orders:
487 if(order.perturbative_expansion>0):
488 self.perturbation_couplings[order.name]=order.perturbative_expansion
489 except AttributeError as error:
490 pass
491
492 if self.perturbation_couplings!={}:
493 self.model = loop_base_objects.LoopModel({'perturbation_couplings':\
494 list(self.perturbation_couplings.keys())})
495 else:
496 self.model = base_objects.Model()
497 self.model.set('particles', self.particles)
498 self.model.set('interactions', self.interactions)
499 self.conservecharge = set(['charge'])
500
501 self.ufomodel = model
502 self.checked_lor = set()
503
504 if auto:
505 self.load_model()
506
508 """load the different of the model first particles then interactions"""
509
510
511
512 def_name = []
513 for param in self.ufomodel.all_parameters:
514 if param.nature == "external":
515 if len(param.lhablock.split())>1:
516 raise InvalidModel('''LHABlock should be single word which is not the case for
517 \'%s\' parameter with lhablock \'%s\' ''' % (param.name, param.lhablock))
518 if param.name in def_name:
519 raise InvalidModel("name %s define multiple time. Please correct the UFO model!" \
520 % (param.name))
521 else:
522 def_name.append(param.name)
523
524
525
526 if hasattr(self.ufomodel,'all_CTparameters'):
527 for CTparam in self.ufomodel.all_CTparameters:
528 for pole in pole_dict:
529 if CTparam.pole(pole)!='ZERO':
530 new_param_name = '%s_%s_'%(CTparam.name,pole_dict[pole])
531 if new_param_name in def_name:
532 raise InvalidModel("CT name %s"% (new_param_name)+\
533 " the model. Please change its name.")
534
535 if hasattr(self.ufomodel, 'gauge'):
536 self.model.set('gauge', self.ufomodel.gauge)
537 logger.info('load particles')
538
539
540 if len(set([p.name for p in self.ufomodel.all_particles] + \
541 [p.antiname for p in self.ufomodel.all_particles])) == \
542 len(set([p.name.lower() for p in self.ufomodel.all_particles] + \
543 [p.antiname.lower() for p in self.ufomodel.all_particles])):
544 self.model['case_sensitive'] = False
545
546
547
548 self.detect_incoming_fermion()
549
550 for particle_info in self.ufomodel.all_particles:
551 self.add_particle(particle_info)
552
553
554 color_info = self.find_color_anti_color_rep()
555
556
557 self.model.set('lorentz', list(self.ufomodel.all_lorentz))
558
559
560
561
562
563
564
565
566
567 if hasattr(self.ufomodel,'all_CTparameters'):
568 logger.debug('Handling couplings defined with CTparameters...')
569 start_treat_coupling = time.time()
570 self.treat_couplings(self.ufomodel.all_couplings,
571 self.ufomodel.all_CTparameters)
572 tot_time = time.time()-start_treat_coupling
573 if tot_time>5.0:
574 logger.debug('... done in %s'%misc.format_time(tot_time))
575
576 logger.info('load vertices')
577 for interaction_info in self.ufomodel.all_vertices:
578 self.add_interaction(interaction_info, color_info)
579
580 if self.non_qcd_gluon_emission:
581 logger.critical("Model with non QCD emission of gluon (found %i of those).\n This type of model is not fully supported within MG5aMC.\n"+\
582 " Restriction on LO dynamical scale and MLM matching/merging can occur for some processes.\n"+\
583 " Use such features with care.", self.non_qcd_gluon_emission)
584
585 self.model['allow_pickle'] = False
586 self.model['limitations'].append('MLM')
587
588 if self.perturbation_couplings:
589 try:
590 self.ufomodel.add_NLO()
591 except Exception as error:
592 pass
593
594 for interaction_info in self.ufomodel.all_CTvertices:
595 self.add_CTinteraction(interaction_info, color_info)
596
597
598 for interaction in list(self.interactions):
599 self.optimise_interaction(interaction)
600 if not interaction['couplings']:
601 self.interactions.remove(interaction)
602
603
604 self.model.set('conserved_charge', self.conservecharge)
605
606
607
608
609 all_orders = []
610 try:
611 all_orders = self.ufomodel.all_orders
612 except AttributeError:
613 if self.perturbation_couplings:
614 raise MadGraph5Error("The loop model MG5 attemps to import does not specify the attribute 'all_order'.")
615 else:
616 pass
617
618 hierarchy={}
619 try:
620 for order in all_orders:
621 hierarchy[order.name]=order.hierarchy
622 except AttributeError:
623 if self.perturbation_couplings:
624 raise MadGraph5Error('The loop model MG5 attemps to import does not specify an order hierarchy.')
625 else:
626 pass
627 else:
628 self.model.set('order_hierarchy', hierarchy)
629
630
631 expansion_order={}
632
633 coupling_order_counterterms={}
634 try:
635 for order in all_orders:
636 expansion_order[order.name]=order.expansion_order
637 coupling_order_counterterms[order.name]=order.expansion_order
638 except AttributeError:
639 if self.perturbation_couplings:
640 raise MadGraph5Error('The loop model MG5 attemps to import does not specify an expansion_order for all coupling orders.')
641 else:
642 pass
643 else:
644 self.model.set('expansion_order', expansion_order)
645 self.model.set('expansion_order', expansion_order)
646
647
648 del self.checked_lor
649
650 return self.model
651
653
654
655
656
657 if not hasattr(self, 'iden_couplings'):
658 coups = collections.defaultdict(list)
659 coups['0'].append('ZERO')
660 for coupling in self.ufomodel.all_couplings:
661
662 coups[str(coupling.value)].append( coupling.name)
663
664 self.iden_couplings = {}
665 for idens in [c for c in coups.values() if len(c)>1]:
666 for i in range(1, len(idens)):
667 self.iden_couplings[idens[i]] = idens[0]
668
669
670 for key, coup in list(interaction['couplings'].items()):
671 if coup in self.iden_couplings:
672 interaction['couplings'][key] = self.iden_couplings[coup]
673 if interaction['couplings'][key] == 'ZERO':
674 del interaction['couplings'][key]
675
676
677
678
679
680
681
682 to_lor = {}
683 for (color, lor), coup in interaction['couplings'].items():
684 key = (color, coup)
685 if key in to_lor:
686 to_lor[key].append(lor)
687 else:
688 to_lor[key] = [lor]
689
690 nb_reduce = []
691 optimize = False
692 for key in to_lor:
693 if len(to_lor[key]) >1:
694 nb_reduce.append(len(to_lor[key])-1)
695 optimize = True
696
697 if not optimize:
698 return
699
700 if not hasattr(self, 'defined_lorentz_expr'):
701 self.defined_lorentz_expr = {}
702 self.lorentz_info = {}
703 self.lorentz_combine = {}
704 for lor in self.model['lorentz']:
705 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name')
706 self.lorentz_info[lor.get('name')] = lor
707
708 for key in to_lor:
709 if len(to_lor[key]) == 1:
710 continue
711 names = [interaction['lorentz'][i] for i in to_lor[key]]
712 names.sort()
713 if self.lorentz_info[names[0]].get('structure') == 'external':
714 continue
715
716 if tuple(names) in self.lorentz_combine:
717
718 new_name = self.lorentz_combine[tuple(names)]
719 else:
720 new_name = self.add_merge_lorentz(names)
721
722
723 color, coup = key
724 to_remove = [(color, lor) for lor in to_lor[key]]
725 for rm in to_remove:
726 del interaction['couplings'][rm]
727
728
729 if new_name not in [l for l in interaction.get('lorentz')]:
730 interaction.get('lorentz').append(new_name)
731
732
733 new_l = interaction.get('lorentz').index(new_name)
734
735 interaction['couplings'][(color, new_l)] = coup
736
737
739 """add a lorentz structure which is the sume of the list given above"""
740
741
742
743 ii = len(names[0])
744 while ii>0:
745 if not all(n.startswith(names[0][:ii]) for n in names[1:]):
746 ii -=1
747 else:
748 base_name = names[0][:ii]
749 break
750 else:
751 base_name = 'LMER'
752
753 i = 1
754 while '%s%s' %(base_name, i) in self.lorentz_info:
755 i +=1
756 new_name = '%s%s' %(base_name, i)
757 self.lorentz_combine[tuple(names)] = new_name
758 assert new_name not in self.lorentz_info
759 assert new_name not in [l.name for l in self.model['lorentz']]
760
761
762 new_struct = ' + '.join([self.lorentz_info[n].get('structure') for n in names])
763 spins = self.lorentz_info[names[0]].get('spins')
764 formfactors = sum([ self.lorentz_info[n].get('formfactors') for n in names \
765 if hasattr(self.lorentz_info[n], 'formfactors') \
766 and self.lorentz_info[n].get('formfactors') \
767 ],[])
768
769 new_lor = self.add_lorentz(new_name, spins, new_struct, formfactors)
770 self.lorentz_info[new_name] = new_lor
771
772 return new_name
773
774
775
776
777
778
779
780 - def add_particle(self, particle_info):
781 """ convert and add a particle in the particle list """
782
783 loop_particles = [[[]]]
784 counterterms = {}
785
786
787
788 pdg = particle_info.pdg_code
789 if pdg in self.incoming or (pdg not in self.outcoming and pdg <0):
790 return
791
792
793 if not self.perturbation_couplings and particle_info.spin < 0:
794 return
795
796 if (aloha.unitary_gauge and 0 in self.model['gauge']) \
797 or (1 not in self.model['gauge']):
798
799
800 if hasattr(particle_info, 'GoldstoneBoson') and particle_info.GoldstoneBoson:
801 return
802 elif hasattr(particle_info, 'goldstone') and particle_info.goldstone:
803 return
804
805 particle = base_objects.Particle()
806
807
808 if (hasattr(particle_info, 'GoldstoneBoson') and particle_info.GoldstoneBoson) \
809 or (hasattr(particle_info, 'goldstoneboson') and particle_info.goldstoneboson):
810 particle.set('type', 'goldstone')
811 elif hasattr(particle_info, 'goldstone') and particle_info.goldstone:
812 particle.set('type', 'goldstone')
813
814 nb_property = 0
815
816 for key,value in particle_info.__dict__.items():
817
818 if key in base_objects.Particle.sorted_keys and not key=='counterterm':
819 nb_property +=1
820 if key in ['name', 'antiname']:
821 if not self.model['case_sensitive']:
822 particle.set(key, value.lower())
823 else:
824 particle.set(key, value)
825 elif key == 'charge':
826 particle.set(key, float(value))
827 elif key in ['mass','width']:
828 particle.set(key, str(value))
829 elif key == 'spin':
830
831
832 particle.set(key,abs(value))
833 if value<0:
834 particle.set('type','ghost')
835 elif key == 'propagating':
836 if not value:
837 particle.set('line', None)
838 elif key == 'line':
839 if particle.get('line') is None:
840 pass
841 else:
842 particle.set('line', value)
843 elif key == 'propagator':
844 if value:
845 if isinstance(value, (list,dict)):
846 if aloha.unitary_gauge:
847 particle.set(key, str(value[0]))
848 else:
849 particle.set(key, str(value[1]))
850 else:
851 particle.set(key, str(value))
852 else:
853 particle.set(key, '')
854 else:
855 particle.set(key, value)
856 elif key == 'loop_particles':
857 loop_particles = value
858 elif key == 'counterterm':
859 counterterms = value
860 elif key.lower() not in ('ghostnumber','selfconjugate','goldstone',
861 'goldstoneboson','partial_widths',
862 'texname', 'antitexname', 'propagating', 'ghost'
863 ):
864
865 self.conservecharge.add(key)
866 particle.set(key,value, force=True)
867
868 if not hasattr(particle_info, 'propagator'):
869 nb_property += 1
870 if particle.get('spin') >= 3:
871 if particle.get('mass').lower() == 'zero':
872 particle.set('propagator', 0)
873 elif particle.get('spin') == 3 and not aloha.unitary_gauge:
874 particle.set('propagator', 0)
875
876 assert(10 == nb_property)
877
878
879 if particle_info.name == particle_info.antiname:
880 particle.set('self_antipart', True)
881
882
883
884 if not self.perturbation_couplings or counterterms=={}:
885 self.particles.append(particle)
886 return
887
888
889
890
891
892
893 particle_counterterms = {}
894 for key, counterterm in counterterms.items():
895
896 if len([1 for k in key[:-1] if k==1])==1 and \
897 not any(k>1 for k in key[:-1]):
898 newParticleCountertermKey=[None,\
899
900
901
902
903
904 tuple([tuple(loop_parts) for\
905 loop_parts in loop_particles[key[-1]]])]
906 for i, order in enumerate(self.ufomodel.all_orders[:-1]):
907 if key[i]==1:
908 newParticleCountertermKey[0]=order.name
909 newCouplingName='UVWfct_'+particle_info.name+'_'+str(key[-1])
910 particle_counterterms[tuple(newParticleCountertermKey)]=\
911 dict([(key,newCouplingName+('' if key==0 else '_'+str(-key)+'eps'))\
912 for key in counterterm])
913
914
915 self.ufomodel.object_library.Coupling(\
916 name = newCouplingName,
917 value = counterterm,
918 order = {newParticleCountertermKey[0]:2})
919 self.wavefunction_CT_couplings.append(self.ufomodel.all_couplings.pop())
920
921 particle.set('counterterm',particle_counterterms)
922 self.particles.append(particle)
923 return
924
926 """ This function scan each coupling to see if it contains a CT parameter.
927 when it does, it changes its value to a dictionary with the CT parameter
928 changed to a new parameter for each pole and finite part. For instance,
929 the following coupling:
930 coupling.value = '2*(myCTParam1 + myParam*(myCTParam2 + myCTParam3)'
931 with CTparameters
932 myCTParam1 = {0: Something, -1: SomethingElse}
933 myCTParam2 = {0: OtherSomething }
934 myCTParam3 = {-1: YetOtherSomething }
935 would be turned into
936 coupling.value = {0: '2*(myCTParam1_FIN_ + myParam*(myCTParam2_FIN_ + ZERO)'
937 -1: '2*(myCTParam1_EPS_ + myParam*(ZERO + myCTParam2_EPS_)'}
938
939 all_CTParameter is the list of all CTParameters in the model"""
940
941
942
943
944
945
946 CTparameter_patterns = {}
947 zero_substitution = lambda matchedObj: matchedObj.group('first')+\
948 'ZERO'+matchedObj.group('second')
949 def function_factory(arg):
950 return lambda matchedObj: \
951 matchedObj.group('first')+arg+matchedObj.group('second')
952 for CTparam in all_CTparameters:
953 pattern_finder = re.compile(r"(?P<first>\A|\*|\+|\-|\(|\s)(?P<name>"+
954 CTparam.name+r")(?P<second>\Z|\*|\+|\-|\)|/|\\|\s)")
955
956 sub_functions = [None if CTparam.pole(pole)=='ZERO' else
957 function_factory('%s_%s_'%(CTparam.name,pole_dict[-pole]))
958 for pole in range(3)]
959 CTparameter_patterns[CTparam.name] = (pattern_finder,sub_functions)
960
961 times_zero = re.compile('\*\s*-?ZERO')
962 zero_times = re.compile('ZERO\s*(\*|\/)')
963 def is_expr_zero(expresson):
964 """ Checks whether a single term (involving only the operations
965 * or / is zero. """
966 for term in expresson.split('-'):
967 for t in term.split('+'):
968 t = t.strip()
969 if t in ['ZERO','']:
970 continue
971 if not (times_zero.search(t) or zero_times.search(t)):
972 return False
973 return True
974
975 def find_parenthesis(expr):
976 end = expr.find(')')
977 if end == -1:
978 return None
979 start = expr.rfind('(',0,end+1)
980 if start ==-1:
981 raise InvalidModel('Parenthesis of expression %s are malformed'%expr)
982 return [expr[:start],expr[start+1:end],expr[end+1:]]
983
984 start_parenthesis = re.compile(r".*\s*[\+\-\*\/\)\(]\s*$")
985
986 def is_value_zero(value):
987 """Check whether an expression like ((A+B)*ZERO+C)*ZERO is zero.
988 Only +,-,/,* operations are allowed and 'ZERO' is a tag for an
989 analytically zero quantity."""
990
991 curr_value = value
992 parenthesis = find_parenthesis(curr_value)
993 while parenthesis:
994
995 if parenthesis[0].endswith('complexconjugate'):
996
997 parenthesis[0] = parenthesis[0][:-16]
998 if parenthesis[0]=='' or re.match(start_parenthesis,
999 parenthesis[0]):
1000 if is_value_zero(parenthesis[1]):
1001 new_parenthesis = 'ZERO'
1002 else:
1003 new_parenthesis = 'PARENTHESIS'
1004 else:
1005 new_parenthesis = '_FUNCTIONARGS'
1006 curr_value = parenthesis[0]+new_parenthesis+parenthesis[2]
1007 parenthesis = find_parenthesis(curr_value)
1008 return is_expr_zero(curr_value)
1009
1010 def CTCoupling_pole(CTCoupling, pole):
1011 """Compute the pole of the CTCoupling in two cases:
1012 a) Its value is a dictionary, then just return the corresponding
1013 entry in the dictionary.
1014 b) It is expressed in terms of CTParameters which are themselves
1015 dictionary so we want to substitute their expression to get
1016 the value of the pole. In the current implementation, this is
1017 just to see if the pole is zero or not.
1018 """
1019
1020 if isinstance(CTCoupling.value,dict):
1021 if -pole in list(CTCoupling.value.keys()):
1022 return CTCoupling.value[-pole], [], 0
1023 else:
1024 return 'ZERO', [], 0
1025
1026 new_expression = CTCoupling.value
1027 CTparamNames = []
1028 n_CTparams = 0
1029 for paramname, value in CTparameter_patterns.items():
1030 pattern = value[0]
1031
1032
1033 if not re.search(pattern,new_expression):
1034 continue
1035 n_CTparams += 1
1036
1037
1038 if not value[1][pole] is None:
1039 CTparamNames.append('%s_%s_'%(paramname,pole_dict[-pole]))
1040
1041 substitute_function = zero_substitution if \
1042 value[1][pole] is None else value[1][pole]
1043 new_expression = pattern.sub(substitute_function,new_expression)
1044
1045
1046
1047 if pole!=0 and n_CTparams==0:
1048 return 'ZERO', [], n_CTparams
1049
1050
1051
1052
1053
1054
1055 if n_CTparams > 0 and is_value_zero(new_expression):
1056 return 'ZERO', [], n_CTparams
1057 else:
1058 return new_expression, CTparamNames, n_CTparams
1059
1060
1061 for coupl in couplings:
1062 new_value = {}
1063 for pole in range(0,3):
1064 expression, CTparamNames, n_CTparams = CTCoupling_pole(coupl, pole)
1065
1066 if n_CTparams == 0:
1067 break
1068 elif expression!='ZERO':
1069 new_value[-pole] = expression
1070 couplname = coupl.name
1071 if pole!=0:
1072 couplname += "_%deps"%pole
1073
1074
1075
1076
1077 if hasattr(self.model, 'map_CTcoup_CTparam'):
1078 self.model.map_CTcoup_CTparam[couplname] = CTparamNames
1079
1080
1081
1082
1083
1084
1085
1086
1087 if new_value:
1088 coupl.old_value = coupl.value
1089 coupl.value = new_value
1090
1092 """ Split this interaction in order to call add_interaction for
1093 interactions for each element of the loop_particles list. Also it
1094 is necessary to unfold here the contributions to the different laurent
1095 expansion orders of the couplings."""
1096
1097
1098 interaction_info=copy.copy(interaction)
1099
1100 intType=''
1101 if interaction_info.type not in ['UV','UVloop','UVtree','UVmass','R2']:
1102 raise MadGraph5Error('MG5 only supports the following types of'+\
1103 ' vertices, R2, UV and UVmass. %s is not in this list.'%interaction_info.type)
1104 else:
1105 intType=interaction_info.type
1106
1107 if interaction_info.type=='UV':
1108 if len(interaction_info.particles)==2 and interaction_info.\
1109 particles[0].name==interaction_info.particles[1].name:
1110 intType='UVmass'
1111 else:
1112 intType='UVloop'
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125 order_to_interactions= {}
1126
1127
1128
1129
1130
1131 for key, couplings in interaction_info.couplings.items():
1132 if not isinstance(couplings, list):
1133 couplings = [couplings]
1134 for coupling in couplings:
1135 order = tuple(coupling.order.items())
1136 if order not in order_to_interactions:
1137 order_to_interactions[order] = [
1138 [{} for j in range(0,3)] for i in \
1139 range(0,max(1,len(interaction_info.loop_particles)))]
1140 new_couplings = order_to_interactions[order]
1141 else:
1142 new_couplings = order_to_interactions[order]
1143
1144 for poleOrder in range(0,3):
1145 expression = coupling.pole(poleOrder)
1146 if expression!='ZERO':
1147 if poleOrder==2:
1148 raise InvalidModel("""
1149 The CT coupling %s was found with a contribution to the double pole.
1150 This is either an error in the model or a parsing error in the function 'is_value_zero'.
1151 The expression of the non-zero double pole coupling is:
1152 %s
1153 """%(coupling.name,str(coupling.value)))
1154
1155
1156
1157 newCoupling = copy.copy(coupling)
1158 if poleOrder!=0:
1159 newCoupling.name=newCoupling.name+"_"+str(poleOrder)+"eps"
1160 newCoupling.value = expression
1161
1162
1163
1164
1165
1166
1167
1168 new_couplings[key[2]][poleOrder][(key[0],key[1])] = newCoupling
1169
1170 for new_couplings in order_to_interactions.values():
1171
1172 for i, all_couplings in enumerate(new_couplings):
1173 loop_particles=[[]]
1174 if len(interaction_info.loop_particles)>0:
1175 loop_particles=[[part.pdg_code for part in loop_parts] \
1176 for loop_parts in interaction_info.loop_particles[i]]
1177 for poleOrder in range(0,3):
1178 if all_couplings[poleOrder]!={}:
1179 interaction_info.couplings=all_couplings[poleOrder]
1180 self.add_interaction(interaction_info, color_info,\
1181 (intType if poleOrder==0 else (intType+str(poleOrder)+\
1182 'eps')),loop_particles)
1183
1185 """find which color are in the 3/3bar states"""
1186
1187
1188
1189
1190 if not output:
1191 output = {}
1192
1193 for interaction_info in self.ufomodel.all_vertices:
1194 if len(interaction_info.particles) != 3:
1195 continue
1196 colors = [abs(p.color) for p in interaction_info.particles]
1197 if colors[:2] == [3,3]:
1198 if 'T(3,2,1)' in interaction_info.color:
1199 color, anticolor, other = interaction_info.particles
1200 elif 'T(3,1,2)' in interaction_info.color:
1201 anticolor, color, _ = interaction_info.particles
1202 elif 'Identity(1,2)' in interaction_info.color or \
1203 'Identity(2,1)' in interaction_info.color:
1204 first, second, _ = interaction_info.particles
1205 if first.pdg_code in output:
1206 if output[first.pdg_code] == 3:
1207 color, anticolor = first, second
1208 else:
1209 color, anticolor = second, first
1210 elif second.pdg_code in output:
1211 if output[second.pdg_code] == 3:
1212 color, anticolor = second, first
1213 else:
1214 color, anticolor = first, second
1215 else:
1216 continue
1217 else:
1218 continue
1219 elif colors[1:] == [3,3]:
1220 if 'T(1,2,3)' in interaction_info.color:
1221 other, anticolor, color = interaction_info.particles
1222 elif 'T(1,3,2)' in interaction_info.color:
1223 other, color, anticolor = interaction_info.particles
1224 elif 'Identity(2,3)' in interaction_info.color or \
1225 'Identity(3,2)' in interaction_info.color:
1226 _, first, second = interaction_info.particles
1227 if first.pdg_code in output:
1228 if output[first.pdg_code] == 3:
1229 color, anticolor = first, second
1230 else:
1231 color, anticolor = second, first
1232 elif second.pdg_code in output:
1233 if output[second.pdg_code] == 3:
1234 color, anticolor = second, first
1235 else:
1236 color, anticolor = first, second
1237 else:
1238 continue
1239 else:
1240 continue
1241
1242 elif colors.count(3) == 2:
1243 if 'T(2,3,1)' in interaction_info.color:
1244 color, other, anticolor = interaction_info.particles
1245 elif 'T(2,1,3)' in interaction_info.color:
1246 anticolor, other, color = interaction_info.particles
1247 elif 'Identity(1,3)' in interaction_info.color or \
1248 'Identity(3,1)' in interaction_info.color:
1249 first, _, second = interaction_info.particles
1250 if first.pdg_code in output:
1251 if output[first.pdg_code] == 3:
1252 color, anticolor = first, second
1253 else:
1254 color, anticolor = second, first
1255 elif second.pdg_code in output:
1256 if output[second.pdg_code] == 3:
1257 color, anticolor = second, first
1258 else:
1259 color, anticolor = first, second
1260 else:
1261 continue
1262 else:
1263 continue
1264 else:
1265 continue
1266
1267
1268 if color.pdg_code in output:
1269 if output[color.pdg_code] == -3:
1270 raise InvalidModel('Particles %s is sometimes in the 3 and sometimes in the 3bar representations' \
1271 % color.name)
1272 else:
1273 output[color.pdg_code] = 3
1274
1275
1276 if anticolor.pdg_code in output:
1277 if output[anticolor.pdg_code] == 3:
1278 raise InvalidModel('Particles %s is sometimes set as in the 3 and sometimes in the 3bar representations' \
1279 % anticolor.name)
1280 else:
1281 output[anticolor.pdg_code] = -3
1282
1283 return output
1284
1286 """define which fermion should be incoming
1287 for that we look at F F~ X interactions
1288 """
1289 self.incoming = []
1290 self.outcoming = []
1291 for interaction_info in self.ufomodel.all_vertices:
1292
1293 pdg = [p.pdg_code for p in interaction_info.particles if p.spin in [2,4]]
1294 if len(pdg) % 2:
1295 raise InvalidModel('Odd number of fermion in vertex: %s' % [p.pdg_code for p in interaction_info.particles])
1296 for i in range(0, len(pdg),2):
1297 if pdg[i] == - pdg[i+1]:
1298 if pdg[i] in self.outcoming:
1299 raise InvalidModel('%s has not coherent incoming/outcoming status between interactions' %\
1300 [p for p in interaction_info.particles if p.spin in [2,4]][i].name)
1301
1302 elif not pdg[i] in self.incoming:
1303 self.incoming.append(pdg[i])
1304 self.outcoming.append(pdg[i+1])
1305
1306 - def add_interaction(self, interaction_info, color_info, type='base', loop_particles=None):
1307 """add an interaction in the MG5 model. interaction_info is the
1308 UFO vertices information."""
1309
1310 particles = [self.model.get_particle(particle.pdg_code) \
1311 for particle in interaction_info.particles]
1312 if None in particles:
1313
1314 return
1315 particles = base_objects.ParticleList(particles)
1316
1317
1318 lorentz = [helas for helas in interaction_info.lorentz]
1319
1320
1321 nb_fermion = sum([ 1 if p.is_fermion() else 0 for p in particles])
1322 try:
1323 if nb_fermion == 2:
1324
1325 [aloha_fct.check_flow_validity(helas.structure, nb_fermion) \
1326 for helas in interaction_info.lorentz
1327 if helas.name not in self.checked_lor]
1328 self.checked_lor.update(set([helas.name for helas in interaction_info.lorentz]))
1329 elif nb_fermion:
1330 if any(p.selfconjugate for p in interaction_info.particles if p.spin % 2 == 0):
1331 text = "Majorana can not be dealt in 4/6/... fermion interactions"
1332 raise InvalidModel(text)
1333 except aloha_fct.WrongFermionFlow as error:
1334 text = 'Fermion Flow error for interactions %s: %s: %s\n %s' % \
1335 (', '.join([p.name for p in interaction_info.particles]),
1336 helas.name, helas.structure, error)
1337 raise InvalidModel(text)
1338
1339
1340
1341
1342
1343 lorentz = [helas.name for helas in lorentz]
1344
1345 colors = [self.treat_color(color_obj, interaction_info, color_info)
1346 for color_obj in interaction_info.color]
1347
1348
1349 order_to_int={}
1350
1351 for key, couplings in interaction_info.couplings.items():
1352 if not isinstance(couplings, list):
1353 couplings = [couplings]
1354 if interaction_info.lorentz[key[1]].name not in lorentz:
1355 continue
1356
1357 if nb_fermion > 2:
1358 flow = aloha_fct.get_fermion_flow(interaction_info.lorentz[key[1]].structure,
1359 nb_fermion)
1360 coupling_sign = self.get_sign_flow(flow, nb_fermion)
1361 else:
1362 coupling_sign = ''
1363 for coupling in couplings:
1364 order = tuple(coupling.order.items())
1365 if '1' in coupling.order:
1366 raise InvalidModel('''Some couplings have \'1\' order.
1367 This is not allowed in MG.
1368 Please defines an additional coupling to your model''')
1369
1370
1371 if 21 in [particle.pdg_code for particle in interaction_info.particles] and\
1372 'QCD' not in coupling.order:
1373 col = [par.get('color') for par in particles]
1374 if 1 not in col:
1375 self.non_qcd_gluon_emission +=1
1376
1377 if order in order_to_int:
1378 order_to_int[order].get('couplings')[key] = '%s%s' % \
1379 (coupling_sign,coupling.name)
1380 else:
1381
1382 interaction = base_objects.Interaction({'id':len(self.interactions)+1})
1383 interaction.set('particles', particles)
1384 interaction.set('lorentz', lorentz)
1385 interaction.set('couplings', {key:
1386 '%s%s' %(coupling_sign,coupling.name)})
1387 interaction.set('orders', coupling.order)
1388 interaction.set('color', colors)
1389 interaction.set('type', type)
1390 interaction.set('loop_particles', loop_particles)
1391 order_to_int[order] = interaction
1392
1393 self.interactions.append(interaction)
1394
1395
1396
1397
1398 for charge in list(self.conservecharge):
1399 total = 0
1400 for part in interaction_info.particles:
1401 try:
1402 total += getattr(part, charge)
1403 except AttributeError:
1404 pass
1405 if abs(total) > 1e-12:
1406 logger.info('The model has interaction violating the charge: %s' % charge)
1407 self.conservecharge.discard(charge)
1408
1409
1410
1412 """ensure that the flow of particles/lorentz are coherent with flow
1413 and return a correct version if needed"""
1414
1415 if not flow or nb_fermion < 4:
1416 return ''
1417
1418 expected = {}
1419 for i in range(nb_fermion//2):
1420 expected[i+1] = i+2
1421
1422 if flow == expected:
1423 return ''
1424
1425 switch = {}
1426 for i in range(1, nb_fermion+1):
1427 if not i in flow:
1428 continue
1429 switch[i] = len(switch)
1430 switch[flow[i]] = len(switch)
1431
1432
1433 sign = 1
1434 done = []
1435
1436
1437
1438 new_order = []
1439 for id in range(nb_fermion):
1440 nid = switch[id+1]-1
1441
1442 new_order.append(nid)
1443
1444
1445 sign =1
1446 for k in range(len(new_order)-1):
1447 for l in range(k+1,len(new_order)):
1448 if new_order[l] < new_order[k]:
1449 sign *= -1
1450
1451 return '' if sign ==1 else '-'
1452
1453 - def add_lorentz(self, name, spins , expr, formfact=None):
1454 """ Add a Lorentz expression which is not present in the UFO """
1455
1456 logger.debug('MG5 converter defines %s to %s', name, expr)
1457 assert name not in [l.name for l in self.model['lorentz']]
1458 with misc.TMP_variable(self.ufomodel.object_library, 'all_lorentz',
1459 self.model['lorentz']):
1460 new = self.model['lorentz'][0].__class__(name = name,
1461 spins = spins,
1462 structure = expr)
1463 if formfact:
1464 new.formfactors = formfact
1465 if self.model['lorentz'][-1].name != name:
1466 self.model['lorentz'].append(new)
1467 if name in [l.name for l in self.ufomodel.all_lorentz]:
1468 self.ufomodel.all_lorentz.remove(new)
1469
1470 assert name in [l.name for l in self.model['lorentz']]
1471 assert name not in [l.name for l in self.ufomodel.all_lorentz]
1472
1473 self.model.create_lorentz_dict()
1474 return new
1475
1476 _pat_T = re.compile(r'T\((?P<first>\d*),(?P<second>\d*)\)')
1477 _pat_id = re.compile(r'Identity\((?P<first>\d*),(?P<second>\d*)\)')
1478
1479 - def treat_color(self, data_string, interaction_info, color_info):
1480 """ convert the string to ColorString"""
1481
1482
1483
1484
1485
1486 output = []
1487 factor = 1
1488 for term in data_string.split('*'):
1489 pattern = self._pat_id.search(term)
1490 if pattern:
1491 particle = interaction_info.particles[int(pattern.group('first'))-1]
1492 particle2 = interaction_info.particles[int(pattern.group('second'))-1]
1493 if particle.color == particle2.color and particle.color in [-6, 6]:
1494 error_msg = 'UFO model have inconsistency in the format:\n'
1495 error_msg += 'interactions for particles %s has color information %s\n'
1496 error_msg += ' but both fermion are in the same representation %s'
1497 raise InvalidModel(error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color))
1498 if particle.color == particle2.color and particle.color in [-3, 3]:
1499 if particle.pdg_code in color_info and particle2.pdg_code in color_info:
1500 if color_info[particle.pdg_code] == color_info[particle2.pdg_code]:
1501 error_msg = 'UFO model have inconsistency in the format:\n'
1502 error_msg += 'interactions for particles %s has color information %s\n'
1503 error_msg += ' but both fermion are in the same representation %s'
1504 raise InvalidModel(error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color))
1505 elif particle.pdg_code in color_info:
1506 color_info[particle2.pdg_code] = -particle.pdg_code
1507 elif particle2.pdg_code in color_info:
1508 color_info[particle.pdg_code] = -particle2.pdg_code
1509 else:
1510 error_msg = 'UFO model have inconsistency in the format:\n'
1511 error_msg += 'interactions for particles %s has color information %s\n'
1512 error_msg += ' but both fermion are in the same representation %s'
1513 raise InvalidModel(error_msg % (', '.join([p.name for p in interaction_info.particles]),data_string, particle.color))
1514
1515
1516 if particle.color == 6:
1517 output.append(self._pat_id.sub('color.T6(\g<first>,\g<second>)', term))
1518 elif particle.color == -6 :
1519 output.append(self._pat_id.sub('color.T6(\g<second>,\g<first>)', term))
1520 elif particle.color == 8:
1521 output.append(self._pat_id.sub('color.Tr(\g<first>,\g<second>)', term))
1522 factor *= 2
1523 elif particle.color in [-3,3]:
1524 if particle.pdg_code not in color_info:
1525
1526 logger.debug('fail to find 3/3bar representation: Retry to find it')
1527 color_info = self.find_color_anti_color_rep(color_info)
1528 if particle.pdg_code not in color_info:
1529 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle.name)
1530 color_info[particle.pdg_code] = particle.color
1531 else:
1532 logger.debug('succeed')
1533 if particle2.pdg_code not in color_info:
1534
1535 logger.debug('fail to find 3/3bar representation: Retry to find it')
1536 color_info = self.find_color_anti_color_rep(color_info)
1537 if particle2.pdg_code not in color_info:
1538 logger.debug('Not able to find the 3/3bar rep from the interactions for particle %s' % particle2.name)
1539 color_info[particle2.pdg_code] = particle2.color
1540 else:
1541 logger.debug('succeed')
1542
1543 if color_info[particle.pdg_code] == 3 :
1544 output.append(self._pat_id.sub('color.T(\g<second>,\g<first>)', term))
1545 elif color_info[particle.pdg_code] == -3:
1546 output.append(self._pat_id.sub('color.T(\g<first>,\g<second>)', term))
1547 else:
1548 raise MadGraph5Error("Unknown use of Identity for particle with color %d" \
1549 % particle.color)
1550 else:
1551 output.append(term)
1552 data_string = '*'.join(output)
1553
1554
1555 p = re.compile(r'\'\w(?P<number>\d+)\'')
1556 data_string = p.sub('-\g<number>', data_string)
1557
1558
1559 new_indices = {}
1560 new_indices = dict([(j,i) for (i,j) in \
1561 enumerate(range(1,
1562 len(interaction_info.particles)+1))])
1563
1564
1565 output = data_string.split('*')
1566 output = color.ColorString([eval(data) \
1567 for data in output if data !='1'])
1568 output.coeff = fractions.Fraction(factor)
1569 for col_obj in output:
1570 col_obj.replace_indices(new_indices)
1571
1572 return output
1573
1575 """Organize the couplings/parameters of a model"""
1576
1577 track_dependant = ['aS','aEWM1','MU_R']
1578
1579
1580
1581
1582 complex_number = re.compile(r'''complex\((?P<real>[^,\(\)]+),(?P<imag>[^,\(\)]+)\)''')
1583 expo_expr = re.compile(r'''(?P<expr>[\w.]+)\s*\*\*\s*(?P<expo>[+-]?[\d.]+)''')
1584 cmath_expr = re.compile(r'''cmath.(?P<operation>\w+)\((?P<expr>\w+)\)''')
1585
1586 conj_expr = re.compile(r'''complexconjugate\((?P<expr>\w+)\)''')
1587
1588
1589 separator = re.compile(r'''[+,\-*/()\s]+''')
1590
1591
1593
1594 self.model = model
1595 self.perturbation_couplings = {}
1596 try:
1597 for order in model.all_orders:
1598 if(order.perturbative_expansion>0):
1599 self.perturbation_couplings[order.name]=order.perturbative_expansion
1600 except AttributeError:
1601 pass
1602 self.params = {}
1603 self.couplings = {}
1604 self.all_expr = {}
1605
1606 - def main(self, additional_couplings = []):
1607 """Launch the actual computation and return the associate
1608 params/couplings. Possibly consider additional_couplings in addition
1609 to those defined in the UFO model attribute all_couplings """
1610
1611 additional_params = []
1612 if hasattr(self.model,'all_CTparameters'):
1613 additional_params = self.get_additional_CTparameters()
1614
1615 self.analyze_parameters(additional_params = additional_params)
1616 self.analyze_couplings(additional_couplings = additional_couplings)
1617
1618
1619 if hasattr(self.model,'all_CTparameters'):
1620 self.revert_CTCoupling_modifications()
1621
1622 return self.params, self.couplings
1623
1625 """ Finally revert the possible modifications done by treat_couplings()
1626 in UFOMG5Converter which were useful for the add_CTinteraction() in
1627 particular. This modification consisted in expanding the value of a
1628 CTCoupling which consisted in an expression in terms of a CTParam to
1629 its corresponding dictionary (e.g
1630 CTCoupling.value = 2*CTParam ->
1631 CTCoupling.value = {-1: 2*CTParam_1EPS_, 0: 2*CTParam_FIN_}
1632 for example if CTParam had a non-zero finite and single pole."""
1633
1634 for coupl in self.model.all_couplings:
1635 if hasattr(coupl,'old_value'):
1636 coupl.value = coupl.old_value
1637 del(coupl.old_value)
1638
1640 """ For each CTparameter split it into spimple parameter for each pole
1641 and the finite part if not zero."""
1642
1643 additional_params = []
1644 for CTparam in self.model.all_CTparameters:
1645 for pole in range(3):
1646 if CTparam.pole(pole) != 'ZERO':
1647 CTparam_piece = copy.copy(CTparam)
1648 CTparam_piece.name = '%s_%s_'%(CTparam.name,pole_dict[-pole])
1649 CTparam_piece.nature = 'internal'
1650 CTparam_piece.type = CTparam.type
1651 CTparam_piece.value = CTparam.pole(pole)
1652 CTparam_piece.texname = '%s_{%s}'%\
1653 (CTparam.texname,pole_dict[-pole])
1654 additional_params.append(CTparam_piece)
1655 return additional_params
1656
1658 """ separate the parameters needed to be recomputed events by events and
1659 the others"""
1660
1661
1662
1663 present_aEWM1 = any(param.name == 'aEWM1' for param in
1664 self.model.all_parameters if param.nature == 'external')
1665
1666 if not present_aEWM1:
1667 self.track_dependant = ['aS','Gf','MU_R']
1668
1669 for param in self.model.all_parameters+additional_params:
1670 if param.nature == 'external':
1671 parameter = base_objects.ParamCardVariable(param.name, param.value, \
1672 param.lhablock, param.lhacode)
1673
1674 else:
1675 expr = self.shorten_expr(param.value)
1676 depend_on = self.find_dependencies(expr)
1677 parameter = base_objects.ModelVariable(param.name, expr, param.type, depend_on)
1678
1679 self.add_parameter(parameter)
1680
1682 """ add consistently the parameter in params and all_expr.
1683 avoid duplication """
1684
1685 assert isinstance(parameter, base_objects.ModelVariable)
1686
1687 if parameter.name in self.all_expr:
1688 return
1689
1690 self.all_expr[parameter.name] = parameter
1691 try:
1692 self.params[parameter.depend].append(parameter)
1693 except:
1694 self.params[parameter.depend] = [parameter]
1695
1697 """ add consistently the coupling in couplings and all_expr.
1698 avoid duplication """
1699
1700 assert isinstance(coupling, base_objects.ModelVariable)
1701
1702 if coupling.name in self.all_expr:
1703 return
1704 self.all_expr[coupling.value] = coupling
1705 try:
1706 self.coupling[coupling.depend].append(coupling)
1707 except:
1708 self.coupling[coupling.depend] = [coupling]
1709
1711 """creates the shortcut for all special function/parameter
1712 separate the couplings dependent of track variables of the others"""
1713
1714
1715
1716 if self.perturbation_couplings:
1717 couplings_list=[]
1718 for coupling in self.model.all_couplings + additional_couplings:
1719 if not isinstance(coupling.value,dict):
1720 couplings_list.append(coupling)
1721 else:
1722 for poleOrder in range(0,3):
1723 if coupling.pole(poleOrder)!='ZERO':
1724 newCoupling=copy.copy(coupling)
1725 if poleOrder!=0:
1726 newCoupling.name += "_%deps"%poleOrder
1727 newCoupling.value=coupling.pole(poleOrder)
1728
1729
1730
1731
1732
1733
1734
1735 couplings_list.append(newCoupling)
1736 else:
1737 couplings_list = self.model.all_couplings + additional_couplings
1738 couplings_list = [c for c in couplings_list if not isinstance(c.value, dict)]
1739
1740 for coupling in couplings_list:
1741
1742 expr = self.shorten_expr(coupling.value)
1743 depend_on = self.find_dependencies(expr)
1744 parameter = base_objects.ModelVariable(coupling.name, expr, 'complex', depend_on)
1745
1746 try:
1747 self.couplings[depend_on].append(parameter)
1748 except KeyError:
1749 self.couplings[depend_on] = [parameter]
1750 self.all_expr[coupling.value] = parameter
1751
1753 """check if an expression should be evaluated points by points or not
1754 """
1755 depend_on = set()
1756
1757
1758
1759
1760
1761
1762
1763 expr = self.separator.split(expr)
1764
1765 for subexpr in expr:
1766 if subexpr in self.track_dependant:
1767 depend_on.add(subexpr)
1768
1769 elif subexpr in self.all_expr and self.all_expr[subexpr].depend:
1770 [depend_on.add(value) for value in self.all_expr[subexpr].depend
1771 if self.all_expr[subexpr].depend != ('external',)]
1772 if depend_on:
1773 return tuple(depend_on)
1774 else:
1775 return tuple()
1776
1777
1790
1791
1793 """add the short expression, and return the nice string associate"""
1794
1795 float_real = float(eval(matchobj.group('real')))
1796 float_imag = float(eval(matchobj.group('imag')))
1797 if float_real == 0 and float_imag ==1:
1798 new_param = base_objects.ModelVariable('complexi', 'complex(0,1)', 'complex')
1799 self.add_parameter(new_param)
1800 return 'complexi'
1801 else:
1802 return 'complex(%s, %s)' % (matchobj.group('real'), matchobj.group('imag'))
1803
1804
1806 """add the short expression, and return the nice string associate"""
1807
1808 expr = matchobj.group('expr')
1809 exponent = matchobj.group('expo')
1810 new_exponent = exponent.replace('.','_').replace('+','').replace('-','_m_')
1811 output = '%s__exp__%s' % (expr, new_exponent)
1812 old_expr = '%s**%s' % (expr,exponent)
1813
1814 if expr.startswith('cmath'):
1815 return old_expr
1816
1817 if expr.isdigit():
1818 output = 'nb__' + output
1819 new_param = base_objects.ModelVariable(output, old_expr,'real')
1820 else:
1821 depend_on = self.find_dependencies(expr)
1822 type = self.search_type(expr)
1823 new_param = base_objects.ModelVariable(output, old_expr, type, depend_on)
1824 self.add_parameter(new_param)
1825 return output
1826
1843
1856
1857
1858
1860 """return the type associate to the expression if define"""
1861
1862 try:
1863 return self.all_expr[expr].type
1864 except:
1865 return 'complex'
1866
1868 """ A class for restricting a model for a given param_card.
1869 rules applied:
1870 - Vertex with zero couplings are throw away
1871 - external parameter with zero/one input are changed into internal parameter.
1872 - identical coupling/mass/width are replace in the model by a unique one
1873 """
1874
1875 log_level = 10
1876 if madgraph.ADMIN_DEBUG:
1877 log_level = 5
1878
1887
1889 self.autowidth.append([int(id[0])])
1890 return math.log10(2*len(self.autowidth))
1891
1892 - def restrict_model(self, param_card, rm_parameter=True, keep_external=False,
1893 complex_mass_scheme=None):
1894 """apply the model restriction following param_card.
1895 rm_parameter defines if the Zero/one parameter are removed or not from
1896 the model.
1897 keep_external if the param_card need to be kept intact
1898 """
1899
1900 if self.get('name') == "mssm" and not keep_external:
1901 raise Exception
1902
1903 self.restrict_card = param_card
1904
1905 self.set('particles', self.get('particles'))
1906
1907
1908
1909 model_definitions = self.set_parameters_and_couplings(param_card,
1910 complex_mass_scheme=complex_mass_scheme,
1911 auto_width=self.modify_autowidth)
1912
1913
1914 logger.log(self.log_level, 'Simplifying conditional expressions')
1915 modified_params, modified_couplings = \
1916 self.detect_conditional_statements_simplifications(model_definitions)
1917
1918
1919 self.apply_conditional_simplifications(modified_params, modified_couplings)
1920
1921
1922 self.locate_coupling()
1923
1924 zero_couplings, iden_couplings = self.detect_identical_couplings()
1925
1926
1927 self.remove_interactions(zero_couplings)
1928
1929
1930 for iden_coups in iden_couplings:
1931 self.merge_iden_couplings(iden_coups)
1932
1933
1934 self.del_coup += zero_couplings
1935 self.remove_couplings(self.del_coup)
1936
1937
1938 for interaction in list(self.get('interactions')):
1939 self.optimise_interaction(interaction)
1940
1941
1942 parameters = self.detect_special_parameters()
1943 self.fix_parameter_values(*parameters, simplify=rm_parameter,
1944 keep_external=keep_external)
1945
1946
1947 if not keep_external:
1948 iden_parameters = self.detect_identical_parameters()
1949 for iden_param in iden_parameters:
1950 self.merge_iden_parameters(iden_param)
1951
1952 iden_parameters = self.detect_identical_parameters()
1953 for iden_param in iden_parameters:
1954 self.merge_iden_parameters(iden_param, keep_external)
1955
1956
1957
1958
1959 for name, value in self['parameter_dict'].items():
1960 if value == 9.999999e-1:
1961 self['parameter_dict'][name] = 1
1962 elif value == 0.000001e-99:
1963 self['parameter_dict'][name] = 0
1964
1965
1966
1967
1968
1969 for parameter in self['parameters'][('external',)]:
1970 if parameter.lhablock.lower() == 'decay' and parameter.lhacode in self.autowidth:
1971 parameter.value = 'auto'
1972 if parameter.name in self['parameter_dict']:
1973 self['parameter_dict'][parameter.name] = 'auto'
1974 elif parameter.name.startswith('mdl_'):
1975 self['parameter_dict'][parameter.name[4:]] = 'auto'
1976 else:
1977 raise Exception
1978
1979
1980 old_order = self['coupling_orders']
1981 self['coupling_orders'] = None
1982 if old_order and old_order != self.get('coupling_orders'):
1983 removed = set(old_order).difference(set(self.get('coupling_orders')))
1984 logger.warning("Some coupling order do not have any coupling associated to them: %s", list(removed))
1985 logger.warning("Those coupling order will not be valid anymore for this model")
1986
1987 self['order_hierarchy'] = {}
1988 self['expansion_order'] = None
1989
1990 self.get('order_hierarchy')
1991 self.get('expansion_order')
1992
1993
1994
1995
1997 """ create a dict couplings_name -> vertex or (particle, counterterm_key) """
1998
1999 self.coupling_pos = {}
2000 for vertex in self['interactions']:
2001 for key, coupling in vertex['couplings'].items():
2002 if coupling.startswith('-'):
2003 coupling = coupling[1:]
2004 if coupling in self.coupling_pos:
2005 if vertex not in self.coupling_pos[coupling]:
2006 self.coupling_pos[coupling].append(vertex)
2007 else:
2008 self.coupling_pos[coupling] = [vertex]
2009
2010 for particle in self['particles']:
2011 for key, coupling_dict in particle['counterterm'].items():
2012 for LaurentOrder, coupling in coupling_dict.items():
2013 if coupling in self.coupling_pos:
2014 if (particle,key) not in self.coupling_pos[coupling]:
2015 self.coupling_pos[coupling].append((particle,key))
2016 else:
2017 self.coupling_pos[coupling] = [(particle,key)]
2018
2019 return self.coupling_pos
2020
2022 """return a list with the name of all vanishing couplings"""
2023
2024 dict_value_coupling = {}
2025 iden_key = set()
2026 zero_coupling = []
2027 iden_coupling = []
2028
2029
2030 keys = list(self['coupling_dict'].keys())
2031 keys.sort()
2032 for name in keys:
2033 value = self['coupling_dict'][name]
2034 if value == 0:
2035 zero_coupling.append(name)
2036 continue
2037 elif not strict_zero and abs(value) < 1e-13:
2038 logger.log(self.log_level, 'coupling with small value %s: %s treated as zero' %
2039 (name, value))
2040 zero_coupling.append(name)
2041 continue
2042 elif not strict_zero and abs(value) < 1e-10:
2043 return self.detect_identical_couplings(strict_zero=True)
2044
2045
2046 if value in dict_value_coupling or -1*value in dict_value_coupling:
2047 if value in dict_value_coupling:
2048 iden_key.add(value)
2049 dict_value_coupling[value].append((name,1))
2050 else:
2051 iden_key.add(-1*value)
2052 dict_value_coupling[-1*value].append((name,-1))
2053 else:
2054 dict_value_coupling[value] = [(name,1)]
2055 for key in iden_key:
2056 tmp = []
2057 if key in dict_value_coupling:
2058 tmp += dict_value_coupling[key]
2059 elif -1*key in dict_value_coupling:
2060 tmp += dict_value_coupling[-1*key]
2061 assert tmp
2062
2063
2064 ords = [self.get_coupling_order(k) for k,c in tmp]
2065 coup_by_ord = collections.defaultdict(list)
2066 for o,t in zip(ords, tmp):
2067 coup_by_ord[str(o)].append(t)
2068
2069 for tmp3 in coup_by_ord.values():
2070 if len(tmp3) > 1:
2071 if tmp3[0][1] == -1:
2072 tmp3 = [(t0,-t1) for t0, t1 in tmp3]
2073 iden_coupling.append(tmp3)
2074
2075
2076
2077
2078 return zero_coupling, iden_coupling
2079
2081 """return the coupling order associated to a coupling """
2082
2083 if cname in self.coupling_order_dict:
2084 return self.coupling_order_dict[cname]
2085
2086 for v in self['interactions']:
2087 for c in v['couplings'].values():
2088 self.coupling_order_dict[c] = v['orders']
2089
2090 if cname not in self.coupling_order_dict:
2091 self.coupling_order_dict[cname] = None
2092
2093
2094
2095 return self.coupling_order_dict[cname]
2096
2097
2098
2100 """ return the list of (name of) parameter which are zero """
2101
2102 null_parameters = []
2103 one_parameters = []
2104 for name, value in self['parameter_dict'].items():
2105 if value == 0 and name != 'ZERO':
2106 null_parameters.append(name)
2107 elif value == 1:
2108 one_parameters.append(name)
2109
2110 return null_parameters, one_parameters
2111
2114 """ Apply the conditional statement simplifications for parameters and
2115 couplings detected by 'simplify_conditional_statements'.
2116 modified_params (modified_couplings) are list of tuples (a,b) with a
2117 parameter (resp. coupling) instance and b is the simplified expression."""
2118
2119 if modified_params:
2120 logger.log(self.log_level, "Conditional expressions are simplified for parameters:")
2121 logger.log(self.log_level, ",".join("%s"%param[0].name for param in modified_params))
2122 for param, new_expr in modified_params:
2123 param.expr = new_expr
2124
2125 if modified_couplings:
2126 logger.log(self.log_level, "Conditional expressions are simplified for couplings:")
2127 logger.log(self.log_level, ",".join("%s"%coupl[0].name for coupl in modified_couplings))
2128 for coupl, new_expr in modified_couplings:
2129 coupl.expr = new_expr
2130
2133 """ Simplifies the 'if' statements in the pythonic UFO expressions
2134 of parameters using the default variables specified in the restrict card.
2135 It returns a list of objects (parameters or couplings) and the new
2136 expression that they should take. Model definitions include all definitons
2137 of the model functions and parameters."""
2138
2139 param_modifications = []
2140 coupl_modifications = []
2141 ifparser = parsers.UFOExpressionParserPythonIF(model_definitions)
2142
2143 start_param = time.time()
2144 if 'parameters' in objects:
2145 for dependences, param_list in self['parameters'].items():
2146 if 'external' in dependences:
2147 continue
2148 for param in param_list:
2149 new_expr, n_changes = ifparser.parse(param.expr)
2150 if n_changes > 0:
2151 param_modifications.append((param, new_expr))
2152
2153 end_param = time.time()
2154
2155 if 'couplings' in objects:
2156 for dependences, coupl_list in self['couplings'].items():
2157 for coupl in coupl_list:
2158 new_expr, n_changes = ifparser.parse(coupl.expr)
2159 if n_changes > 0:
2160 coupl_modifications.append((coupl, new_expr))
2161
2162 end_coupl = time.time()
2163
2164 tot_param_time = end_param-start_param
2165 tot_coupl_time = end_coupl-end_param
2166 if tot_param_time>5.0:
2167 logger.log(self.log_level, "Simplification of conditional statements"+\
2168 " in parameter expressions done in %s."%misc.format_time(tot_param_time))
2169 if tot_coupl_time>5.0:
2170 logger.log(self.log_level, "Simplification of conditional statements"+\
2171 " in couplings expressions done in %s."%misc.format_time(tot_coupl_time))
2172
2173 return param_modifications, coupl_modifications
2174
2176 """ return the list of tuple of name of parameter with the same
2177 input value """
2178
2179
2180 external_parameters = self['parameters'][('external',)]
2181
2182
2183 block_value_to_var={}
2184 mult_param = set([])
2185
2186
2187
2188 for param in external_parameters[:]:
2189 value = self['parameter_dict'][param.name]
2190 if value in [0,1,0.000001e-99,9.999999e-1]:
2191 continue
2192 if param.lhablock.lower() == 'decay':
2193 continue
2194 key = (param.lhablock, value)
2195 mkey = (param.lhablock, -value)
2196
2197 if key in block_value_to_var:
2198 block_value_to_var[key].append((param,1))
2199 mult_param.add(key)
2200 elif mkey in block_value_to_var:
2201 block_value_to_var[mkey].append((param,-1))
2202 mult_param.add(mkey)
2203 else:
2204 block_value_to_var[key] = [(param,1)]
2205
2206 output=[]
2207 for key in mult_param:
2208 output.append(block_value_to_var[key])
2209
2210 return output
2211
2212
2213 @staticmethod
2215 """ We have main == coeff * coupling
2216 coeff is only +1 or -1
2217 main can be either GC_X or -GC_X
2218 coupling can be either GC_Y or -GC_Y
2219 value is either GC_Y or -GC_Y
2220 the return is either GC_X or -GC_X
2221 such that we have value == OUTPUT
2222 """
2223 assert coeff in [-1,1]
2224 assert value == coupling or value == '-%s' % coupling or coupling == '-%s' % value
2225 assert isinstance(main, str)
2226 assert isinstance(coupling, str)
2227 assert isinstance(value, str)
2228 if coeff ==1:
2229 if value == coupling:
2230 return main
2231 else:
2232 if main.startswith('-'):
2233 return main[1:]
2234 else:
2235 return '-%s' % main
2236 else:
2237 if value == coupling:
2238 if main.startswith('-'):
2239 return main[1:]
2240 else:
2241 return '-%s' % main
2242 else:
2243 return main
2244
2245
2247 """merge the identical couplings in the interactions and particle
2248 counterterms"""
2249
2250
2251 logger_mod.log(self.log_level, ' Fuse the Following coupling (they have the same value): %s '% \
2252 ', '.join([str(obj) for obj in couplings]))
2253
2254 main = couplings[0][0]
2255 assert couplings[0][1] == 1
2256 self.del_coup += [c[0] for c in couplings[1:]]
2257
2258 for coupling, coeff in couplings[1:]:
2259
2260 if coupling not in self.coupling_pos:
2261 continue
2262
2263 vertices = [ vert for vert in self.coupling_pos[coupling] if
2264 isinstance(vert, base_objects.Interaction)]
2265 for vertex in vertices:
2266 for key, value in vertex['couplings'].items():
2267 if value == coupling or value == '-%s' % coupling or coupling == '-%s' % value:
2268 vertex['couplings'][key] = self.get_new_coupling_name(\
2269 main, coupling, value, coeff)
2270
2271
2272
2273
2274
2275 particles_ct = [ pct for pct in self.coupling_pos[coupling] if
2276 isinstance(pct, tuple)]
2277 for pct in particles_ct:
2278 for key, value in pct[0]['counterterm'][pct[1]].items():
2279 if value == coupling:
2280 pct[0]['counterterm'][pct[1]][key] = main
2281
2282
2283
2285 """return the list of block defined in the param_card"""
2286
2287 blocks = set([p.lhablock for p in self['parameters'][('external',)]])
2288 return blocks
2289
2291 """ merge the identical parameters given in argument.
2292 keep external force to keep the param_card untouched (up to comment)"""
2293
2294 logger_mod.log(self.log_level, 'Parameters set to identical values: %s '% \
2295 ', '.join(['%s*%s' % (f, obj.name.replace('mdl_','')) for (obj,f) in parameters]))
2296
2297
2298 external_parameters = self['parameters'][('external',)]
2299 for i, (obj, factor) in enumerate(parameters):
2300
2301 if i == 0:
2302 obj.info = 'set of param :' + \
2303 ', '.join([str(f)+'*'+param.name.replace('mdl_','')
2304 for (param, f) in parameters])
2305 expr = obj.name
2306 continue
2307
2308 if factor ==1:
2309 self.rule_card.add_identical(obj.lhablock.lower(), obj.lhacode,
2310 parameters[0][0].lhacode )
2311 else:
2312 self.rule_card.add_opposite(obj.lhablock.lower(), obj.lhacode,
2313 parameters[0][0].lhacode )
2314 obj_name = obj.name
2315
2316 if not keep_external:
2317 external_parameters.remove(obj)
2318 elif obj.lhablock.upper() in ['MASS','DECAY']:
2319 external_parameters.remove(obj)
2320 else:
2321 obj.name = ''
2322 obj.info = 'MG5 will not use this value use instead %s*%s' %(factor,expr)
2323
2324 new_param = base_objects.ModelVariable(obj_name, '%s*%s' %(factor, expr), 'real')
2325 self['parameters'][()].insert(0, new_param)
2326
2327
2328
2329 if parameters[0][0].lhablock in ['MASS','DECAY']:
2330 new_name = parameters[0][0].name
2331 if parameters[0][0].lhablock == 'MASS':
2332 arg = 'mass'
2333 else:
2334 arg = 'width'
2335 change_name = [p.name for (p,f) in parameters[1:]]
2336 factor_for_name = [f for (p,f) in parameters[1:]]
2337 [p.set(arg, new_name) for p in self['particle_dict'].values()
2338 if p[arg] in change_name and
2339 factor_for_name[change_name.index(p[arg])]==1]
2340
2342 """ remove the interactions and particle counterterms
2343 associated to couplings"""
2344
2345
2346 mod_vertex = []
2347 mod_particle_ct = []
2348 for coup in zero_couplings:
2349
2350 if coup not in self.coupling_pos:
2351 continue
2352
2353
2354
2355 vertices = [ vert for vert in self.coupling_pos[coup] if
2356 isinstance(vert, base_objects.Interaction) ]
2357 for vertex in vertices:
2358 modify = False
2359 for key, coupling in list(vertex['couplings'].items()):
2360 if coupling in zero_couplings:
2361 modify=True
2362 del vertex['couplings'][key]
2363 elif coupling.startswith('-'):
2364 coupling = coupling[1:]
2365 if coupling in zero_couplings:
2366 modify=True
2367 del vertex['couplings'][key]
2368
2369 if modify:
2370 mod_vertex.append(vertex)
2371
2372
2373 particles_ct = [ pct for pct in self.coupling_pos[coup] if
2374 isinstance(pct, tuple)]
2375 for pct in particles_ct:
2376 modify = False
2377 for key, coupling in list(pct[0]['counterterm'][pct[1]].items()):
2378 if coupling in zero_couplings:
2379 modify=True
2380 del pct[0]['counterterm'][pct[1]][key]
2381 if modify:
2382 mod_particle_ct.append(pct)
2383
2384
2385 for vertex in mod_vertex:
2386 part_name = [part['name'] for part in vertex['particles']]
2387 orders = ['%s=%s' % (order,value) for order,value in vertex['orders'].items()]
2388
2389 if not vertex['couplings']:
2390 logger_mod.log(self.log_level, 'remove interactions: %s at order: %s' % \
2391 (' '.join(part_name),', '.join(orders)))
2392 self['interactions'].remove(vertex)
2393 else:
2394 logger_mod.log(self.log_level, 'modify interactions: %s at order: %s' % \
2395 (' '.join(part_name),', '.join(orders)))
2396
2397
2398 for pct in mod_particle_ct:
2399 part_name = pct[0]['name']
2400 order = pct[1][0]
2401 loop_parts = ','.join(['('+','.join([\
2402 self.get_particle(p)['name'] for p in part])+')' \
2403 for part in pct[1][1]])
2404
2405 if not pct[0]['counterterm'][pct[1]]:
2406 logger_mod.log(self.log_level, 'remove counterterm of particle %s'%part_name+\
2407 ' with loop particles (%s)'%loop_parts+\
2408 ' perturbing order %s'%order)
2409 del pct[0]['counterterm'][pct[1]]
2410 else:
2411 logger_mod.log(self.log_level, 'Modify counterterm of particle %s'%part_name+\
2412 ' with loop particles (%s)'%loop_parts+\
2413 ' perturbing order %s'%order)
2414
2415 return
2416
2418
2419 for name, data in self['couplings'].items():
2420 for coupling in data[:]:
2421 if coupling.name in couplings:
2422 data.remove(coupling)
2423
2424
2425 - def fix_parameter_values(self, zero_parameters, one_parameters,
2426 simplify=True, keep_external=False):
2427 """ Remove all instance of the parameters in the model and replace it by
2428 zero when needed."""
2429
2430
2431
2432 for particle in self['particles']:
2433 if particle['mass'] in zero_parameters:
2434 particle['mass'] = 'ZERO'
2435 if particle['width'] in zero_parameters:
2436 particle['width'] = 'ZERO'
2437 if particle['width'] in one_parameters:
2438 one_parameters.remove(particle['width'])
2439 if particle['mass'] in one_parameters:
2440 one_parameters.remove(particle['mass'])
2441
2442 for pdg, particle in self['particle_dict'].items():
2443 if particle['mass'] in zero_parameters:
2444 particle['mass'] = 'ZERO'
2445 if particle['width'] in zero_parameters:
2446 particle['width'] = 'ZERO'
2447
2448
2449
2450 external_parameters = self['parameters'][('external',)]
2451 for param in external_parameters[:]:
2452 value = self['parameter_dict'][param.name]
2453 block = param.lhablock.lower()
2454 if value == 0:
2455 self.rule_card.add_zero(block, param.lhacode)
2456 elif value == 1:
2457 self.rule_card.add_one(block, param.lhacode)
2458
2459 special_parameters = zero_parameters + one_parameters
2460
2461
2462
2463 if simplify:
2464
2465 re_str = '|'.join(special_parameters)
2466 if len(re_str) > 25000:
2467 split = len(special_parameters) // 2
2468 re_str = ['|'.join(special_parameters[:split]),
2469 '|'.join(special_parameters[split:])]
2470 else:
2471 re_str = [ re_str ]
2472 used = set()
2473 for expr in re_str:
2474 re_pat = re.compile(r'''\b(%s)\b''' % expr)
2475
2476 for name, coupling_list in self['couplings'].items():
2477 for coupling in coupling_list:
2478 for use in re_pat.findall(coupling.expr):
2479 used.add(use)
2480
2481
2482 for lor in self['lorentz']:
2483 if hasattr(lor, 'formfactors') and lor.formfactors:
2484 for ff in lor.formfactors:
2485 for use in re_pat.findall(ff.value):
2486 used.add(use)
2487 else:
2488 used = set([i for i in special_parameters if i])
2489
2490
2491 re_str = '|'.join([param for param in special_parameters if param not in used])
2492 if len(re_str) > 25000:
2493 split = len(special_parameters) // 2
2494 re_str = ['|'.join(special_parameters[:split]),
2495 '|'.join(special_parameters[split:])]
2496 else:
2497 re_str = [ re_str ]
2498 for expr in re_str:
2499 re_pat = re.compile(r'''\b(%s)\b''' % expr)
2500
2501 param_info = {}
2502
2503 for dep, param_list in self['parameters'].items():
2504 for tag, parameter in enumerate(param_list):
2505
2506 if parameter.name in special_parameters:
2507 param_info[parameter.name]= {'dep': dep, 'tag': tag,
2508 'obj': parameter}
2509 continue
2510
2511
2512 if isinstance(parameter, base_objects.ParamCardVariable):
2513 continue
2514
2515 if simplify:
2516 for use in re_pat.findall(parameter.expr):
2517 used.add(use)
2518
2519
2520 for param in used:
2521 if not param:
2522 continue
2523 data = self['parameters'][param_info[param]['dep']]
2524 data.remove(param_info[param]['obj'])
2525 tag = param_info[param]['tag']
2526 data = self['parameters'][()]
2527 if param in zero_parameters:
2528 data.insert(0, base_objects.ModelVariable(param, '0.0', 'real'))
2529 else:
2530 data.insert(0, base_objects.ModelVariable(param, '1.0', 'real'))
2531
2532
2533 for param in special_parameters:
2534
2535 if param in used or \
2536 (keep_external and param_info[param]['dep'] == ('external',)):
2537 logger_mod.log(self.log_level, 'fix parameter value: %s' % param)
2538 continue
2539 logger_mod.log(self.log_level,'remove parameters: %s' % (param))
2540 data = self['parameters'][param_info[param]['dep']]
2541 data.remove(param_info[param]['obj'])
2542
2543
2545
2546
2547
2548 to_lor = {}
2549 for (color, lor), coup in interaction['couplings'].items():
2550 abscoup, coeff = (coup[1:],-1) if coup.startswith('-') else (coup, 1)
2551 key = (color, abscoup)
2552 if key in to_lor:
2553 to_lor[key].append((lor,coeff))
2554 else:
2555 to_lor[key] = [(lor,coeff)]
2556
2557 nb_reduce = []
2558 optimize = False
2559 for key in to_lor:
2560 if len(to_lor[key]) >1:
2561 nb_reduce.append(len(to_lor[key])-1)
2562 optimize = True
2563
2564 if not optimize:
2565 return
2566
2567 if not hasattr(self, 'defined_lorentz_expr'):
2568 self.defined_lorentz_expr = {}
2569 self.lorentz_info = {}
2570 self.lorentz_combine = {}
2571 for lor in self.get('lorentz'):
2572 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name')
2573 self.lorentz_info[lor.get('name')] = lor
2574
2575 for key in to_lor:
2576 if len(to_lor[key]) == 1:
2577 continue
2578 names = ['u%s' % interaction['lorentz'][i[0]] if i[1] ==1 else \
2579 'd%s' % interaction['lorentz'][i[0]] for i in to_lor[key]]
2580
2581 names.sort()
2582
2583
2584 if tuple(names) in self.lorentz_combine:
2585
2586 new_name = self.lorentz_combine[tuple(names)]
2587 else:
2588 new_name = self.add_merge_lorentz(names)
2589
2590
2591 color, coup = key
2592 to_remove = [(color, lor[0]) for lor in to_lor[key]]
2593 for rm in to_remove:
2594 del interaction['couplings'][rm]
2595
2596
2597 if new_name not in [l for l in interaction.get('lorentz')]:
2598 interaction.get('lorentz').append(new_name)
2599
2600
2601 new_l = interaction.get('lorentz').index(new_name)
2602
2603 interaction['couplings'][(color, new_l)] = coup
2604
2605
2606
2608 """add a lorentz structure which is the sume of the list given above"""
2609
2610
2611 ii = len(names[0])
2612 while ii>1:
2613
2614 if not all(n[1:].startswith(names[0][1:ii]) for n in names[1:]):
2615 ii -=1
2616 else:
2617 base_name = names[0][1:ii]
2618 break
2619 else:
2620 base_name = 'LMER'
2621 i = 1
2622 while '%s%s' %(base_name, i) in self.lorentz_info:
2623 i +=1
2624 new_name = '%s%s' %(base_name, i)
2625 self.lorentz_combine[tuple(names)] = new_name
2626
2627
2628 new_struct = ' + '.join([self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('u')])
2629 if any( n.startswith('d') for n in names ):
2630 new_struct += '-' + ' - '.join(['1.*(%s)' %self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('d')])
2631 spins = self.lorentz_info[names[0][1:]].get('spins')
2632 formfact = sum([ self.lorentz_info[n[1:]].get('formfactors') for n in names \
2633 if hasattr(self.lorentz_info[n[1:]], 'formfactors') \
2634 and self.lorentz_info[n[1:]].get('formfactors') \
2635 ],[])
2636
2637
2638
2639
2640 new_lor = self.add_lorentz(new_name, spins, new_struct, formfact)
2641 self.lorentz_info[new_name] = new_lor
2642
2643 return new_name
2644
2645 - def add_lorentz(self, name, spin, struct, formfact=None):
2646 """adding lorentz structure to the current model"""
2647 new = self['lorentz'][0].__class__(name = name,
2648 spins = spin,
2649 structure = struct)
2650 if formfact:
2651 new.formfactors = formfact
2652 self['lorentz'].append(new)
2653 self.create_lorentz_dict()
2654
2655 return None
2656