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