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