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
1982 """ create a dict couplings_name -> vertex or (particle, counterterm_key) """
1983
1984 self.coupling_pos = {}
1985 for vertex in self['interactions']:
1986 for key, coupling in vertex['couplings'].items():
1987 if coupling.startswith('-'):
1988 coupling = coupling[1:]
1989 if coupling in self.coupling_pos:
1990 if vertex not in self.coupling_pos[coupling]:
1991 self.coupling_pos[coupling].append(vertex)
1992 else:
1993 self.coupling_pos[coupling] = [vertex]
1994
1995 for particle in self['particles']:
1996 for key, coupling_dict in particle['counterterm'].items():
1997 for LaurentOrder, coupling in coupling_dict.items():
1998 if coupling in self.coupling_pos:
1999 if (particle,key) not in self.coupling_pos[coupling]:
2000 self.coupling_pos[coupling].append((particle,key))
2001 else:
2002 self.coupling_pos[coupling] = [(particle,key)]
2003
2004 return self.coupling_pos
2005
2007 """return a list with the name of all vanishing couplings"""
2008
2009 dict_value_coupling = {}
2010 iden_key = set()
2011 zero_coupling = []
2012 iden_coupling = []
2013
2014
2015 keys = list(self['coupling_dict'].keys())
2016 keys.sort()
2017 for name in keys:
2018 value = self['coupling_dict'][name]
2019 if value == 0:
2020 zero_coupling.append(name)
2021 continue
2022 elif not strict_zero and abs(value) < 1e-13:
2023 logger.log(self.log_level, 'coupling with small value %s: %s treated as zero' %
2024 (name, value))
2025 zero_coupling.append(name)
2026 continue
2027 elif not strict_zero and abs(value) < 1e-10:
2028 return self.detect_identical_couplings(strict_zero=True)
2029
2030
2031 if value in dict_value_coupling or -1*value in dict_value_coupling:
2032 if value in dict_value_coupling:
2033 iden_key.add(value)
2034 dict_value_coupling[value].append((name,1))
2035 else:
2036 iden_key.add(-1*value)
2037 dict_value_coupling[-1*value].append((name,-1))
2038 else:
2039 dict_value_coupling[value] = [(name,1)]
2040 for key in iden_key:
2041 tmp = []
2042 if key in dict_value_coupling:
2043 tmp += dict_value_coupling[key]
2044 elif -1*key in dict_value_coupling:
2045 tmp += dict_value_coupling[-1*key]
2046 assert tmp
2047
2048
2049 ords = [self.get_coupling_order(k) for k,c in tmp]
2050 coup_by_ord = collections.defaultdict(list)
2051 for o,t in zip(ords, tmp):
2052 coup_by_ord[str(o)].append(t)
2053
2054 for tmp3 in coup_by_ord.values():
2055 if len(tmp3) > 1:
2056 if tmp3[0][1] == -1:
2057 tmp3 = [(t0,-t1) for t0, t1 in tmp3]
2058 iden_coupling.append(tmp3)
2059
2060
2061
2062
2063 return zero_coupling, iden_coupling
2064
2066 """return the coupling order associated to a coupling """
2067
2068 if cname in self.coupling_order_dict:
2069 return self.coupling_order_dict[cname]
2070
2071 for v in self['interactions']:
2072 for c in v['couplings'].values():
2073 self.coupling_order_dict[c] = v['orders']
2074
2075 if cname not in self.coupling_order_dict:
2076 self.coupling_order_dict[cname] = None
2077
2078
2079
2080 return self.coupling_order_dict[cname]
2081
2082
2083
2085 """ return the list of (name of) parameter which are zero """
2086
2087 null_parameters = []
2088 one_parameters = []
2089 for name, value in self['parameter_dict'].items():
2090 if value == 0 and name != 'ZERO':
2091 null_parameters.append(name)
2092 elif value == 1:
2093 one_parameters.append(name)
2094
2095 return null_parameters, one_parameters
2096
2099 """ Apply the conditional statement simplifications for parameters and
2100 couplings detected by 'simplify_conditional_statements'.
2101 modified_params (modified_couplings) are list of tuples (a,b) with a
2102 parameter (resp. coupling) instance and b is the simplified expression."""
2103
2104 if modified_params:
2105 logger.log(self.log_level, "Conditional expressions are simplified for parameters:")
2106 logger.log(self.log_level, ",".join("%s"%param[0].name for param in modified_params))
2107 for param, new_expr in modified_params:
2108 param.expr = new_expr
2109
2110 if modified_couplings:
2111 logger.log(self.log_level, "Conditional expressions are simplified for couplings:")
2112 logger.log(self.log_level, ",".join("%s"%coupl[0].name for coupl in modified_couplings))
2113 for coupl, new_expr in modified_couplings:
2114 coupl.expr = new_expr
2115
2118 """ Simplifies the 'if' statements in the pythonic UFO expressions
2119 of parameters using the default variables specified in the restrict card.
2120 It returns a list of objects (parameters or couplings) and the new
2121 expression that they should take. Model definitions include all definitons
2122 of the model functions and parameters."""
2123
2124 param_modifications = []
2125 coupl_modifications = []
2126 ifparser = parsers.UFOExpressionParserPythonIF(model_definitions)
2127
2128 start_param = time.time()
2129 if 'parameters' in objects:
2130 for dependences, param_list in self['parameters'].items():
2131 if 'external' in dependences:
2132 continue
2133 for param in param_list:
2134 new_expr, n_changes = ifparser.parse(param.expr)
2135 if n_changes > 0:
2136 param_modifications.append((param, new_expr))
2137
2138 end_param = time.time()
2139
2140 if 'couplings' in objects:
2141 for dependences, coupl_list in self['couplings'].items():
2142 for coupl in coupl_list:
2143 new_expr, n_changes = ifparser.parse(coupl.expr)
2144 if n_changes > 0:
2145 coupl_modifications.append((coupl, new_expr))
2146
2147 end_coupl = time.time()
2148
2149 tot_param_time = end_param-start_param
2150 tot_coupl_time = end_coupl-end_param
2151 if tot_param_time>5.0:
2152 logger.log(self.log_level, "Simplification of conditional statements"+\
2153 " in parameter expressions done in %s."%misc.format_time(tot_param_time))
2154 if tot_coupl_time>5.0:
2155 logger.log(self.log_level, "Simplification of conditional statements"+\
2156 " in couplings expressions done in %s."%misc.format_time(tot_coupl_time))
2157
2158 return param_modifications, coupl_modifications
2159
2161 """ return the list of tuple of name of parameter with the same
2162 input value """
2163
2164
2165 external_parameters = self['parameters'][('external',)]
2166
2167
2168 block_value_to_var={}
2169 mult_param = set([])
2170
2171
2172
2173 for param in external_parameters[:]:
2174 value = self['parameter_dict'][param.name]
2175 if value in [0,1,0.000001e-99,9.999999e-1]:
2176 continue
2177 if param.lhablock.lower() == 'decay':
2178 continue
2179 key = (param.lhablock, value)
2180 mkey = (param.lhablock, -value)
2181
2182 if key in block_value_to_var:
2183 block_value_to_var[key].append((param,1))
2184 mult_param.add(key)
2185 elif mkey in block_value_to_var:
2186 block_value_to_var[mkey].append((param,-1))
2187 mult_param.add(mkey)
2188 else:
2189 block_value_to_var[key] = [(param,1)]
2190
2191 output=[]
2192 for key in mult_param:
2193 output.append(block_value_to_var[key])
2194
2195 return output
2196
2197
2198 @staticmethod
2200 """ We have main == coeff * coupling
2201 coeff is only +1 or -1
2202 main can be either GC_X or -GC_X
2203 coupling can be either GC_Y or -GC_Y
2204 value is either GC_Y or -GC_Y
2205 the return is either GC_X or -GC_X
2206 such that we have value == OUTPUT
2207 """
2208 assert coeff in [-1,1]
2209 assert value == coupling or value == '-%s' % coupling or coupling == '-%s' % value
2210 assert isinstance(main, str)
2211 assert isinstance(coupling, str)
2212 assert isinstance(value, str)
2213 if coeff ==1:
2214 if value == coupling:
2215 return main
2216 else:
2217 if main.startswith('-'):
2218 return main[1:]
2219 else:
2220 return '-%s' % main
2221 else:
2222 if value == coupling:
2223 if main.startswith('-'):
2224 return main[1:]
2225 else:
2226 return '-%s' % main
2227 else:
2228 return main
2229
2230
2232 """merge the identical couplings in the interactions and particle
2233 counterterms"""
2234
2235
2236 logger_mod.log(self.log_level, ' Fuse the Following coupling (they have the same value): %s '% \
2237 ', '.join([str(obj) for obj in couplings]))
2238
2239 main = couplings[0][0]
2240 assert couplings[0][1] == 1
2241 self.del_coup += [c[0] for c in couplings[1:]]
2242
2243 for coupling, coeff in couplings[1:]:
2244
2245 if coupling not in self.coupling_pos:
2246 continue
2247
2248 vertices = [ vert for vert in self.coupling_pos[coupling] if
2249 isinstance(vert, base_objects.Interaction)]
2250 for vertex in vertices:
2251 for key, value in vertex['couplings'].items():
2252 if value == coupling or value == '-%s' % coupling or coupling == '-%s' % value:
2253 vertex['couplings'][key] = self.get_new_coupling_name(\
2254 main, coupling, value, coeff)
2255
2256
2257
2258
2259
2260 particles_ct = [ pct for pct in self.coupling_pos[coupling] if
2261 isinstance(pct, tuple)]
2262 for pct in particles_ct:
2263 for key, value in pct[0]['counterterm'][pct[1]].items():
2264 if value == coupling:
2265 pct[0]['counterterm'][pct[1]][key] = main
2266
2267
2268
2270 """return the list of block defined in the param_card"""
2271
2272 blocks = set([p.lhablock for p in self['parameters'][('external',)]])
2273 return blocks
2274
2276 """ merge the identical parameters given in argument.
2277 keep external force to keep the param_card untouched (up to comment)"""
2278
2279 logger_mod.log(self.log_level, 'Parameters set to identical values: %s '% \
2280 ', '.join(['%s*%s' % (f, obj.name.replace('mdl_','')) for (obj,f) in parameters]))
2281
2282
2283 external_parameters = self['parameters'][('external',)]
2284 for i, (obj, factor) in enumerate(parameters):
2285
2286 if i == 0:
2287 obj.info = 'set of param :' + \
2288 ', '.join([str(f)+'*'+param.name.replace('mdl_','')
2289 for (param, f) in parameters])
2290 expr = obj.name
2291 continue
2292
2293 if factor ==1:
2294 self.rule_card.add_identical(obj.lhablock.lower(), obj.lhacode,
2295 parameters[0][0].lhacode )
2296 else:
2297 self.rule_card.add_opposite(obj.lhablock.lower(), obj.lhacode,
2298 parameters[0][0].lhacode )
2299 obj_name = obj.name
2300
2301 if not keep_external:
2302 external_parameters.remove(obj)
2303 elif obj.lhablock.upper() in ['MASS','DECAY']:
2304 external_parameters.remove(obj)
2305 else:
2306 obj.name = ''
2307 obj.info = 'MG5 will not use this value use instead %s*%s' %(factor,expr)
2308
2309 new_param = base_objects.ModelVariable(obj_name, '%s*%s' %(factor, expr), 'real')
2310 self['parameters'][()].insert(0, new_param)
2311
2312
2313
2314 if parameters[0][0].lhablock in ['MASS','DECAY']:
2315 new_name = parameters[0][0].name
2316 if parameters[0][0].lhablock == 'MASS':
2317 arg = 'mass'
2318 else:
2319 arg = 'width'
2320 change_name = [p.name for (p,f) in parameters[1:]]
2321 factor_for_name = [f for (p,f) in parameters[1:]]
2322 [p.set(arg, new_name) for p in self['particle_dict'].values()
2323 if p[arg] in change_name and
2324 factor_for_name[change_name.index(p[arg])]==1]
2325
2327 """ remove the interactions and particle counterterms
2328 associated to couplings"""
2329
2330
2331 mod_vertex = []
2332 mod_particle_ct = []
2333 for coup in zero_couplings:
2334
2335 if coup not in self.coupling_pos:
2336 continue
2337
2338
2339
2340 vertices = [ vert for vert in self.coupling_pos[coup] if
2341 isinstance(vert, base_objects.Interaction) ]
2342 for vertex in vertices:
2343 modify = False
2344 for key, coupling in list(vertex['couplings'].items()):
2345 if coupling in zero_couplings:
2346 modify=True
2347 del vertex['couplings'][key]
2348 elif coupling.startswith('-'):
2349 coupling = coupling[1:]
2350 if coupling in zero_couplings:
2351 modify=True
2352 del vertex['couplings'][key]
2353
2354 if modify:
2355 mod_vertex.append(vertex)
2356
2357
2358 particles_ct = [ pct for pct in self.coupling_pos[coup] if
2359 isinstance(pct, tuple)]
2360 for pct in particles_ct:
2361 modify = False
2362 for key, coupling in list(pct[0]['counterterm'][pct[1]].items()):
2363 if coupling in zero_couplings:
2364 modify=True
2365 del pct[0]['counterterm'][pct[1]][key]
2366 if modify:
2367 mod_particle_ct.append(pct)
2368
2369
2370 for vertex in mod_vertex:
2371 part_name = [part['name'] for part in vertex['particles']]
2372 orders = ['%s=%s' % (order,value) for order,value in vertex['orders'].items()]
2373
2374 if not vertex['couplings']:
2375 logger_mod.log(self.log_level, 'remove interactions: %s at order: %s' % \
2376 (' '.join(part_name),', '.join(orders)))
2377 self['interactions'].remove(vertex)
2378 else:
2379 logger_mod.log(self.log_level, 'modify interactions: %s at order: %s' % \
2380 (' '.join(part_name),', '.join(orders)))
2381
2382
2383 for pct in mod_particle_ct:
2384 part_name = pct[0]['name']
2385 order = pct[1][0]
2386 loop_parts = ','.join(['('+','.join([\
2387 self.get_particle(p)['name'] for p in part])+')' \
2388 for part in pct[1][1]])
2389
2390 if not pct[0]['counterterm'][pct[1]]:
2391 logger_mod.log(self.log_level, 'remove counterterm of particle %s'%part_name+\
2392 ' with loop particles (%s)'%loop_parts+\
2393 ' perturbing order %s'%order)
2394 del pct[0]['counterterm'][pct[1]]
2395 else:
2396 logger_mod.log(self.log_level, 'Modify counterterm of particle %s'%part_name+\
2397 ' with loop particles (%s)'%loop_parts+\
2398 ' perturbing order %s'%order)
2399
2400 return
2401
2403
2404 for name, data in self['couplings'].items():
2405 for coupling in data[:]:
2406 if coupling.name in couplings:
2407 data.remove(coupling)
2408
2409
2410 - def fix_parameter_values(self, zero_parameters, one_parameters,
2411 simplify=True, keep_external=False):
2412 """ Remove all instance of the parameters in the model and replace it by
2413 zero when needed."""
2414
2415
2416
2417 for particle in self['particles']:
2418 if particle['mass'] in zero_parameters:
2419 particle['mass'] = 'ZERO'
2420 if particle['width'] in zero_parameters:
2421 particle['width'] = 'ZERO'
2422 if particle['width'] in one_parameters:
2423 one_parameters.remove(particle['width'])
2424 if particle['mass'] in one_parameters:
2425 one_parameters.remove(particle['mass'])
2426
2427 for pdg, particle in self['particle_dict'].items():
2428 if particle['mass'] in zero_parameters:
2429 particle['mass'] = 'ZERO'
2430 if particle['width'] in zero_parameters:
2431 particle['width'] = 'ZERO'
2432
2433
2434
2435 external_parameters = self['parameters'][('external',)]
2436 for param in external_parameters[:]:
2437 value = self['parameter_dict'][param.name]
2438 block = param.lhablock.lower()
2439 if value == 0:
2440 self.rule_card.add_zero(block, param.lhacode)
2441 elif value == 1:
2442 self.rule_card.add_one(block, param.lhacode)
2443
2444 special_parameters = zero_parameters + one_parameters
2445
2446
2447
2448 if simplify:
2449
2450 re_str = '|'.join(special_parameters)
2451 if len(re_str) > 25000:
2452 split = len(special_parameters) // 2
2453 re_str = ['|'.join(special_parameters[:split]),
2454 '|'.join(special_parameters[split:])]
2455 else:
2456 re_str = [ re_str ]
2457 used = set()
2458 for expr in re_str:
2459 re_pat = re.compile(r'''\b(%s)\b''' % expr)
2460
2461 for name, coupling_list in self['couplings'].items():
2462 for coupling in coupling_list:
2463 for use in re_pat.findall(coupling.expr):
2464 used.add(use)
2465
2466
2467 for lor in self['lorentz']:
2468 if hasattr(lor, 'formfactors') and lor.formfactors:
2469 for ff in lor.formfactors:
2470 for use in re_pat.findall(ff.value):
2471 used.add(use)
2472 else:
2473 used = set([i for i in special_parameters if i])
2474
2475
2476 re_str = '|'.join([param for param in special_parameters if param not in used])
2477 if len(re_str) > 25000:
2478 split = len(special_parameters) // 2
2479 re_str = ['|'.join(special_parameters[:split]),
2480 '|'.join(special_parameters[split:])]
2481 else:
2482 re_str = [ re_str ]
2483 for expr in re_str:
2484 re_pat = re.compile(r'''\b(%s)\b''' % expr)
2485
2486 param_info = {}
2487
2488 for dep, param_list in self['parameters'].items():
2489 for tag, parameter in enumerate(param_list):
2490
2491 if parameter.name in special_parameters:
2492 param_info[parameter.name]= {'dep': dep, 'tag': tag,
2493 'obj': parameter}
2494 continue
2495
2496
2497 if isinstance(parameter, base_objects.ParamCardVariable):
2498 continue
2499
2500 if simplify:
2501 for use in re_pat.findall(parameter.expr):
2502 used.add(use)
2503
2504
2505 for param in used:
2506 if not param:
2507 continue
2508 data = self['parameters'][param_info[param]['dep']]
2509 data.remove(param_info[param]['obj'])
2510 tag = param_info[param]['tag']
2511 data = self['parameters'][()]
2512 if param in zero_parameters:
2513 data.insert(0, base_objects.ModelVariable(param, '0.0', 'real'))
2514 else:
2515 data.insert(0, base_objects.ModelVariable(param, '1.0', 'real'))
2516
2517
2518 for param in special_parameters:
2519
2520 if param in used or \
2521 (keep_external and param_info[param]['dep'] == ('external',)):
2522 logger_mod.log(self.log_level, 'fix parameter value: %s' % param)
2523 continue
2524 logger_mod.log(self.log_level,'remove parameters: %s' % (param))
2525 data = self['parameters'][param_info[param]['dep']]
2526 data.remove(param_info[param]['obj'])
2527
2528
2530
2531
2532
2533 to_lor = {}
2534 for (color, lor), coup in interaction['couplings'].items():
2535 abscoup, coeff = (coup[1:],-1) if coup.startswith('-') else (coup, 1)
2536 key = (color, abscoup)
2537 if key in to_lor:
2538 to_lor[key].append((lor,coeff))
2539 else:
2540 to_lor[key] = [(lor,coeff)]
2541
2542 nb_reduce = []
2543 optimize = False
2544 for key in to_lor:
2545 if len(to_lor[key]) >1:
2546 nb_reduce.append(len(to_lor[key])-1)
2547 optimize = True
2548
2549 if not optimize:
2550 return
2551
2552 if not hasattr(self, 'defined_lorentz_expr'):
2553 self.defined_lorentz_expr = {}
2554 self.lorentz_info = {}
2555 self.lorentz_combine = {}
2556 for lor in self.get('lorentz'):
2557 self.defined_lorentz_expr[lor.get('structure')] = lor.get('name')
2558 self.lorentz_info[lor.get('name')] = lor
2559
2560 for key in to_lor:
2561 if len(to_lor[key]) == 1:
2562 continue
2563 names = ['u%s' % interaction['lorentz'][i[0]] if i[1] ==1 else \
2564 'd%s' % interaction['lorentz'][i[0]] for i in to_lor[key]]
2565
2566 names.sort()
2567
2568
2569 if tuple(names) in self.lorentz_combine:
2570
2571 new_name = self.lorentz_combine[tuple(names)]
2572 else:
2573 new_name = self.add_merge_lorentz(names)
2574
2575
2576 color, coup = key
2577 to_remove = [(color, lor[0]) for lor in to_lor[key]]
2578 for rm in to_remove:
2579 del interaction['couplings'][rm]
2580
2581
2582 if new_name not in [l for l in interaction.get('lorentz')]:
2583 interaction.get('lorentz').append(new_name)
2584
2585
2586 new_l = interaction.get('lorentz').index(new_name)
2587
2588 interaction['couplings'][(color, new_l)] = coup
2589
2590
2591
2593 """add a lorentz structure which is the sume of the list given above"""
2594
2595
2596 ii = len(names[0])
2597 while ii>1:
2598
2599 if not all(n[1:].startswith(names[0][1:ii]) for n in names[1:]):
2600 ii -=1
2601 else:
2602 base_name = names[0][1:ii]
2603 break
2604 else:
2605 base_name = 'LMER'
2606 i = 1
2607 while '%s%s' %(base_name, i) in self.lorentz_info:
2608 i +=1
2609 new_name = '%s%s' %(base_name, i)
2610 self.lorentz_combine[tuple(names)] = new_name
2611
2612
2613 new_struct = ' + '.join([self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('u')])
2614 if any( n.startswith('d') for n in names ):
2615 new_struct += '-' + ' - '.join(['1.*(%s)' %self.lorentz_info[n[1:]].get('structure') for n in names if n.startswith('d')])
2616 spins = self.lorentz_info[names[0][1:]].get('spins')
2617 formfact = sum([ self.lorentz_info[n[1:]].get('formfactors') for n in names \
2618 if hasattr(self.lorentz_info[n[1:]], 'formfactors') \
2619 and self.lorentz_info[n[1:]].get('formfactors') \
2620 ],[])
2621
2622
2623
2624
2625 new_lor = self.add_lorentz(new_name, spins, new_struct, formfact)
2626 self.lorentz_info[new_name] = new_lor
2627
2628 return new_name
2629
2630 - def add_lorentz(self, name, spin, struct, formfact=None):
2631 """adding lorentz structure to the current model"""
2632 new = self['lorentz'][0].__class__(name = name,
2633 spins = spin,
2634 structure = struct)
2635 if formfact:
2636 new.formfactors = formfact
2637 self['lorentz'].append(new)
2638 self.create_lorentz_dict()
2639
2640 return None
2641