1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """ Set of Tool in order to modify a given UFO model.
16 (mainly by adding-suppressing interactions and allow to modify by text the
17 different part of the model. Check of consistency of the model are performed.
18 This produce a new valid UFO model in output.
19 """
20
21 import glob
22 import logging
23 import os
24 import re
25 import sys
26
27 import madgraph.iolibs.files as files
28 import madgraph.various.misc as misc
29 import models as ufomodels
30 import models.import_ufo as import_ufo
31 import models.check_param_card as check_param_card
32
33 pjoin =os.path.join
34 logger = logging.getLogger('madgraph.model')
35
37
39 """ The class storing the current status of the model """
40
41 - def __init__(self, modelpath, addon='__1'):
42 """load the model from a valid UFO directory (otherwise keep everything
43 as empty."""
44
45 self.modelpath = modelpath
46 model = ufomodels.load_model(modelpath)
47
48
49 if not hasattr(model, 'all_orders'):
50 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention (no couplings_order information)\n' +\
51 'MG5 is able to load such model but NOT to the add model feature.'
52 if isinstance(model.all_particles[0].mass, basestring):
53 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention (Mass/Width of particles are string name, not object)\n' +\
54 'MG5 is able to load such model but NOT to the add model feature.'
55
56
57 self.particles = model.all_particles
58 if any(hasattr(p, 'loop_particles') for p in self.particles):
59 raise USRMODERROR, 'Base Model doesn\'t follows UFO convention '
60 self.vertices = model.all_vertices
61 self.couplings = model.all_couplings
62 self.lorentz = model.all_lorentz
63 self.parameters = model.all_parameters
64 self.Parameter = self.parameters[0].__class__
65 self.orders = model.all_orders
66
67 self.functions = model.all_functions
68 self.new_external = []
69
70 if hasattr(model, 'all_propagators'):
71 self.propagators = model.all_propagators
72 else:
73 self.propagators = []
74
75
76 if hasattr(model, 'all_CTvertices'):
77 self.CTvertices = model.all_CTvertices
78 else:
79 self.CTvertices = []
80
81
82 if 'self.expr = expression' in open(pjoin(self.modelpath, 'object_library.py')).read():
83 self.translate = {'expr': 'expression'}
84 else:
85 self.translate = {}
86
87
88 self.old_new = {}
89 self.addon = addon
90
91
92 self.particle_dict = {}
93 for particle in self.particles:
94 self.particle_dict[particle.pdg_code] = particle
95
96
97 self.all_path = [self.modelpath]
98
99 - def write(self, outputdir):
119
120
122 """ propagate model restriction of the original model. """
123
124 restrict_list = [l for l in os.listdir(self.modelpath) if l.startswith('restrict_')]
125 if not self.new_external:
126
127 for p in restrict_list:
128 files.cp(pjoin(self.modelpath, p), outputdir)
129
130 else:
131
132 for p in restrict_list:
133 param_card = check_param_card.ParamCard(pjoin(self.modelpath, p))
134 for parameter in self.new_external:
135 block = parameter.lhablock
136 lhaid = parameter.lhacode
137 value = parameter.value
138 if value == 0:
139 value = 1e-99
140 elif value == 1:
141 value = 9.999999e-1
142 try:
143 param_card.add_param(block.lower(), lhaid, value, 'from addon')
144 except check_param_card.InvalidParamCard:
145 logger.warning("%s will not acting for %s %s" % (p, block, lhaid))
146 param_card[block.lower()].get(lhaid).value = value
147
148 param_card.write(pjoin(outputdir, p))
149
150
151
152
153
154
155
156
187
188
189
190 - def create_data_text(self, obj):
191 """ create the data associate to the object"""
192
193
194
195 nb_space = 0
196 if hasattr(obj, 'require_args_all'):
197 args = obj.require_args_all
198 elif hasattr(obj, 'require_args'):
199 args = obj.require_args
200 else:
201 args = []
202 if args:
203 text = """%s = %s(""" % (obj.__repr__(), obj.__class__.__name__)
204 else:
205 text = """%s = %s(""" % (obj.name, obj.__class__.__name__)
206
207
208 for data in args:
209 if data in self.translate:
210 data = self.translate[data]
211 if not nb_space:
212 add_space = len(text)
213 else:
214 add_space = 0
215
216 try:
217 expr = getattr(obj, data)
218 except:
219 if data in ['counterterm', 'propagator', 'loop_particles']:
220 expr = None
221 setattr(obj, data, None)
222 else:
223 raise
224 name =str(data)
225 if name in self.translate:
226 name = self.translate[name]
227
228
229 text += '%s%s = %s,\n' % (' ' * nb_space,name, self.format_param(getattr(obj, data)))
230 nb_space += add_space
231
232 if hasattr(obj, 'get_all'):
233 other_attr = [name for name in obj.get_all().keys()
234 if name not in args]
235 else:
236 other_attr = obj.__dict__.keys()
237
238 for data in other_attr:
239 name =str(data)
240 if name in ['partial_widths', 'loop_particles']:
241 continue
242 if name in self.translate:
243 name = self.translate[name]
244 if not nb_space:
245 add_space = len(text)
246 else:
247 add_space = 0
248 text += '%s%s = %s,\n' % (' ' * nb_space, name, self.format_param(getattr(obj, data)))
249 nb_space += add_space
250
251 text = text[:-2] + ')\n\n'
252
253 return text
254
255 - def create_file_content(self, datalist):
256 """ """
257 return '\n'.join([self.create_data_text(obj) for obj in datalist])
258
259
260 - def write_particles(self, outputdir):
261 """ """
262 text = """
263 # This file was automatically created by The UFO_usermod
264
265 from __future__ import division
266 from object_library import all_particles, Particle
267 import parameters as Param
268
269 """
270 text += self.create_file_content(self.particles)
271 ff = open(os.path.join(outputdir, 'particles.py'), 'w')
272 ff.writelines(text)
273 ff.close()
274 return
275
277 """ """
278 text = """
279 # This file was automatically created by The UFO_usermod
280
281 from object_library import all_vertices, Vertex
282 import particles as P
283 import couplings as C
284 import lorentz as L
285
286 """
287 text += self.create_file_content(self.vertices)
288 ff = open(os.path.join(outputdir, 'vertices.py'), 'w')
289 ff.writelines(text)
290 ff.close()
291 return
292
294 """ """
295
296 if not self.CTvertices:
297 return
298
299 text = """
300 # This file was automatically created by The UFO_usermod
301
302 from object_library import all_vertices, all_CTvertices, Vertex, CTVertex
303 import particles as P
304 import couplings as C
305 import lorentz as L
306
307 """
308 text += self.create_file_content(self.CTvertices)
309 ff = open(os.path.join(outputdir, 'CT_vertices.py'), 'w')
310 ff.writelines(text)
311 ff.close()
312 return
313
314
316 """ """
317 text = """
318 # This file was automatically created by The UFO_usermod
319
320 from object_library import all_couplings, Coupling
321 """
322 text += self.create_file_content(self.couplings)
323 ff = open(os.path.join(outputdir, 'couplings.py'), 'w')
324 ff.writelines(text)
325 ff.close()
326 return
327
329 """ """
330 text = """
331 # This file was automatically created by The UFO_usermod
332
333 from object_library import all_lorentz, Lorentz
334 """
335
336 text += self.create_file_content(self.lorentz)
337 ff = open(os.path.join(outputdir, 'lorentz.py'), 'w')
338 ff.writelines(text)
339 ff.close()
340 return
341
343 """ """
344 text = """
345 # This file was automatically created by The UFO_usermod
346
347 from object_library import all_parameters, Parameter
348 """
349
350 text += self.create_file_content(self.parameters)
351 ff = open(os.path.join(outputdir, 'parameters.py'), 'w')
352 ff.writelines(text)
353 ff.close()
354 return
355
357 """ """
358 text = """
359 # This file was automatically created by The UFO_usermod
360
361 from object_library import all_orders, CouplingOrder
362 """
363
364 text += self.create_file_content(self.orders)
365 ff = open(os.path.join(outputdir, 'coupling_orders.py'), 'w')
366 ff.writelines(text)
367 ff.close()
368 return
369
371 """ """
372 text = """
373 # This file was automatically created by The UFO_usermod
374
375 import cmath
376 from object_library import all_functions, Function
377
378 """
379
380 text += self.create_file_content(self.functions)
381 ff = open(os.path.join(outputdir, 'function_library.py'), 'w')
382 ff.writelines(text)
383 ff.close()
384 return
385
387 """ """
388
389 text = """
390 # This file was automatically created by The UFO_usermod
391 from object_library import all_propagators, Propagator
392 """
393
394 text += self.create_file_content(self.propagators)
395 ff = open(os.path.join(outputdir, 'propagators.py'), 'w')
396 ff.writelines(text)
397 ff.close()
398 return
399
401 """Copy/merge the routines written in Fortran/C++/pyhton"""
402
403
404 re_fct = re.compile('''^\s{7,70}[\w\s]*function (\w*)\(''',re.M+re.I)
405 present_fct = set()
406 for dirpath in self.all_path:
407 if os.path.exists(pjoin(dirpath, 'Fortran', 'functions.f')):
408 text = open(pjoin(dirpath, 'Fortran', 'functions.f')).read()
409 new_fct = re_fct.findall(text)
410 nb_old = len(present_fct)
411 nb_added = len(new_fct)
412 new_fct = set([f.lower() for f in new_fct])
413 present_fct.update(new_fct)
414 if len(present_fct) < nb_old + nb_added:
415 logger.critical('''Some Functions in functions.f are define in more than one model.
416 This require AT LEAST manual modification of the resulting file. But more likely the
417 model need to be consider as un-physical! Use it very carefully.''')
418
419 if not os.path.exists(pjoin(outputdir, 'Fortran')):
420 os.mkdir(pjoin(outputdir, 'Fortran'))
421 fsock = open(pjoin(outputdir, 'Fortran','functions.f'),'a')
422 fsock.write(text)
423 fsock.close()
424
425
426
427 for dirpath in self.all_path:
428 for subdir in ['Fortran', 'CPP', 'Python']:
429 if os.path.exists(pjoin(dirpath, subdir)):
430 for filepath in os.listdir(pjoin(dirpath, subdir)):
431 if filepath == 'functions.f':
432 continue
433 if '.' not in filepath:
434 continue
435 logger.warning('Manual HELAS routine associated to the model. Those are not modified automaticaly!! So you need to manually checked them')
436 nb = 0
437 name, extension = filepath.rsplit('.', 1)
438
439 while 1:
440 filename = '%s%s%s' %(name, '.moved' * nb, extension)
441 if os.path.exists(pjoin(outputdir, subdir, filename)):
442 nb+=1
443 else:
444 break
445 if not os.path.exists(pjoin(outputdir, subdir)):
446 os.mkdir(pjoin(outputdir, subdir))
447 files.cp(pjoin(dirpath, subdir, filepath), pjoin(outputdir, subdir, filename))
448
449 - def get_particle(self, name):
450 """ """
451 for part in self.particles:
452 if part.name == name:
453 return part
454
455 raise USRMODERROR, 'no particle %s in the model' % name
456
464
465 - def add_particle(self, particle, identify=None):
466 """Add a particle in a consistent way"""
467
468 name = particle.name
469 if identify:
470 name = identify
471 old_part = next((p for p in self.particles if p.name==name), None)
472 if old_part:
473
474 if old_part.pdg_code == particle.pdg_code:
475 particle.replace = old_part
476 return self.check_mass_width_of_particle(old_part, particle)
477 elif identify:
478 if particle.spin != old_part.spin:
479 raise USRMODERROR, "identify particles should have the same spin"
480 elif particle.color != old_part.color:
481 raise USRMODERROR, "identify particles should have the same color"
482
483 particle.pdg_code = old_part.pdg_code
484 particle.replace = old_part
485 return self.check_mass_width_of_particle(old_part, particle)
486 else:
487 logger.warning('The particle name \'%s\' is present in both model with different pdg code' % name)
488 logger.warning('The particle coming from the plug-in model will be rename to \'%s%s\'' % (name, self.addon))
489 particle.name = '%s%s' % (name, self.addon)
490 self.particles.append(particle)
491 return
492 elif identify:
493 raise USRMODERROR, "Particle %s is not in the model" % identify
494
495 pdg = particle.pdg_code
496 if pdg in self.particle_dict:
497 particle.replace = self.particle_dict[pdg]
498 return self.check_mass_width_of_particle(self.particle_dict[pdg], particle)
499 else:
500 self.particles.append(particle)
501
502
503 - def check_mass_width_of_particle(self, p_base, p_plugin):
504
505
506 if p_base.mass.name != p_plugin.mass.name:
507
508 if p_plugin.mass.name in self.old_new:
509 if self.old_new[p_plugin.mass.name] != p_base.mass.name:
510 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model: equivalent of %s is %s != %s ' % ( p_plugin.mass.name, self.old_new[p_plugin.mass.name], p_base.mass.name)
511 elif p_base.mass.name.lower() == 'zero':
512 p_base.mass = p_plugin.mass
513 elif p_plugin.mass.name.lower() == 'zero':
514 pass
515 else:
516 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model\n' + \
517 ' Mass: %s and %s\n' %(p_base.mass.name, p_plugin.mass.name) + \
518 ' conflict name %s\n' % self.old_new + \
519 ' pdg_code: %s %s' % (p_base.pdg_code, p_plugin.pdg_code)
520
521 if p_base.width.name != p_plugin.width.name:
522
523 if p_plugin.width.name in self.old_new:
524 if self.old_new[p_plugin.width.name] != p_base.width.name:
525 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model'
526 elif p_base.width.name.lower() == 'zero':
527 p_base.width = p_plugin.width
528 elif p_plugin.width.name.lower() == 'zero':
529 pass
530 else:
531 raise USRMODERROR, 'Some inconsistency in the mass assignment in the model'
532
533 return
534
536 """adding a param_card parameter inside the current model.
537 if the parameter block/lhcode already exists then just do nothing
538 (but if the name are different then keep the info for future translation)
539 If the name already exists in the model. raise an exception.
540 """
541
542 name = parameter.name
543
544 old_param = next((p for p in self.parameters if p.name==name), None)
545 if old_param:
546 if old_param.lhablock == parameter.lhablock and \
547 old_param.lhacode == parameter.lhacode:
548 return
549 else:
550 logger.info('The two model defines the parameter \'%s\'\n' % parameter.name +
551 ' the original model for %s :%s\n' %(old_param.lhablock, old_param.lhacode)+
552 ' the plugin for %s :%s\n' %(parameter.lhablock,parameter.lhacode)+
553 ' We will rename the one from the plugin to %s%s' % (parameter.name, self.addon))
554 if old_param.nature == 'internal':
555 logger.warning('''The parameter %s is actually an internal parameter of the base model.
556 his value is given by %s.
557 If those two parameters are expected to be identical, you need to provide the value in the param_card according to this formula.
558 ''')
559
560 self.old_new[parameter.name] = '%s%s' % (parameter.name, self.addon)
561 parameter.name = '%s%s' % (parameter.name, self.addon)
562
563
564
565
566 lhacode = parameter.lhacode
567 if parameter.lhablock.lower() in ['mass', 'decay']:
568 if int(parameter.lhacode[0]) in identify_pid:
569 lhacode = [identify_pid[int(parameter.lhacode[0])]]
570
571 old_param = next((p for p in self.parameters if p.lhacode==lhacode \
572 and p.lhablock==parameter.lhablock), None)
573 if old_param:
574 logger.info('The two model defines the block \'%s\' with id \'%s\' with different parameter name \'%s\', \'%s\'\n'\
575 % (old_param.lhablock, old_param.lhacode, parameter.name, old_param.name) + \
576 ' We will merge those two parameters in a single one')
577 if parameter.name in self.old_new.values():
578 key = [k for k in self.old_new if self.old_new[k] == parameter.name][0]
579 self.old_new[key] = old_param.name
580 self.old_new[parameter.name] = old_param.name
581 else:
582 self.old_new[parameter.name] = old_param.name
583
584
585 else:
586
587 self.parameters.append(parameter)
588 self.new_external.append(parameter)
589
591 """ add a parameter of type internal """
592
593 name = parameter.name
594
595 old_param = next((p for p in self.parameters if p.name==name), None)
596 if old_param:
597 if old_param.value == parameter.value:
598 return
599 else:
600 if self.old_new:
601 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys()))
602 def replace(matchobj):
603 return self.old_new[matchobj.group(0)]
604 parameter.value = pattern.sub(replace, parameter.value)
605 self.old_new[parameter.name] = '%s%s' % (parameter.name, self.addon)
606
607 parameter.name = '%s%s' % (parameter.name, self.addon)
608 self.parameters.append(parameter)
609 return
610
611
612 if self.old_new:
613 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys()))
614 def replace(matchobj):
615 return self.old_new[matchobj.group(0)]
616 parameter.value = pattern.sub(replace, parameter.value)
617
618 self.parameters.append(parameter)
619
620
621
622
624 """add one coupling"""
625
626
627 name = coupling.name
628 same_name = next((p for p in self.couplings if p.name==name), None)
629 if same_name:
630 coupling.name = '%s%s' % (coupling.name, self.addon)
631
632 if self.old_new:
633 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys()))
634 def replace(matchobj):
635 return self.old_new[matchobj.group(0)]
636 coupling.value = pattern.sub(replace, coupling.value)
637
638 old_coupling = next((p for p in self.couplings if p.value==coupling.value), None)
639
640 if old_coupling:
641 coupling.replace = old_coupling
642 else:
643 self.couplings.append(coupling)
644
646 """adding a new coupling order inside the model"""
647
648 name = coupling_order.name
649 same_name = next((p for p in self.orders if p.name==name), None)
650 if same_name:
651 if coupling_order.hierarchy != same_name.hierarchy:
652 logger.warning('%s has different hierarchy use the minimal value (%s, %s) => %s' \
653 % (name, same_name.hierarchy, coupling_order.hierarchy,
654 min(same_name.hierarchy, coupling_order.hierarchy)))
655 same_name.hierarchy = min(same_name.hierarchy, coupling_order.hierarchy)
656 if coupling_order.expansion_order != same_name.expansion_order:
657 logger.warning('%s has different expansion_order use the minimal value (%s, %s) => %s' \
658 % (name, coupling_order.expansion_order, same_name.expansion_order,
659 min(same_name.expansion_order, coupling_order.expansion_order)))
660 same_name.expansion_order = min(same_name.expansion_order, coupling_order.expansion_order)
661 if hasattr(same_name, 'perturbative_expansion') and same_name.perturbative_expansion:
662 logger.info('%s will be forbidden to run at NLO' % same_name.name)
663 same_name.perturbative_expansion = 0
664
665
666 else:
667 self.orders.append(coupling_order)
668
670 """add one coupling"""
671
672
673 name = lorentz.name
674 same_name = next((p for p in self.lorentz if p.name==name), None)
675 if same_name:
676 lorentz.name = '%s%s' % (lorentz.name, self.addon)
677
678 if self.old_new:
679 pattern = re.compile(r'\b(%s)\b' % '|'.join(self.old_new.keys()))
680 def replace(matchobj):
681 return self.old_new[matchobj.group(0)]
682 lorentz.structure = pattern.sub(replace, lorentz.structure)
683
684 old_lor = next((p for p in self.lorentz
685 if p.structure==lorentz.structure and p.spins == lorentz.spins),
686 None)
687
688 if old_lor:
689 lorentz.replace = old_lor
690 else:
691 self.lorentz.append(lorentz)
692
694 """Add one interaction to the model. This is UNCONDITIONAL!
695 if the same interaction is in the model this means that the interaction
696 will appear twice. This is now weaken if both interaction are exactly identical!
697 (EXACT same color/lorentz/coupling expression)
698 """
699
700
701 name = interaction.name
702 same_name = next((p for p in self.vertices if p.name==name), None)
703 if same_name:
704 interaction.name = '%s%s' % (interaction.name, self.addon)
705
706
707 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles]
708 interaction.particles = particles
709
710
711 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz]
712 interaction.lorentz = lorentz
713
714
715 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c)
716 for key, c in interaction.couplings.items()]
717 interaction.couplings = dict(couplings)
718
719
720
721
722 get_pdg = lambda vertex: sorted([p.pdg_code for p in vertex.particles])
723 id_part = get_pdg(interaction)
724 iden_vertex = [v for v in self.vertices if get_pdg(v) == id_part]
725 iden = False
726 nb_coupling = len(interaction.couplings)
727 keys = interaction.couplings.keys()
728
729 get_lor_and_color = lambda i: (interaction.lorentz[keys[i][1]].structure,
730 interaction.color[keys[i][0]])
731 for v in iden_vertex:
732 if len(v.couplings) != nb_coupling:
733 continue
734 found = []
735 for ((i,j), coup) in v.couplings.items():
736 new_lorentz = v.lorentz[j].structure
737 new_color = v.color[i]
738 k=0
739 same = [k for k in range(nb_coupling) if k not in found and
740 get_lor_and_color(k) == (new_lorentz, new_color)]
741 if not same:
742 break
743 else:
744 for k in same:
745 if interaction.couplings[keys[k]] == coup:
746 found.append(k)
747 break
748 else:
749
750 for k in same:
751 if interaction.couplings[keys[k]].order == coup.order:
752 found.append(k)
753 warning = """Did NOT add interaction %s since same particles/lorentz/color/coupling order
754 BUT did not manage to ensure that the coupling is the same. couplings expression:
755 base model: %s
756 addon model: %s
757 """ % (id_part, coup.value, interaction.couplings[keys[k]].value)
758 logger.warning(warning)
759 found.append(k)
760 break
761 else:
762 pass
763
764 else:
765
766 return
767
768 logger.info('Adding interaction for the following particles: %s' % id_part)
769
770
771
772
773 self.vertices.append(interaction)
774
776 """Add one interaction to the model. This is UNCONDITIONAL!
777 if the same interaction is in the model this means that the interaction
778 will appear twice."""
779
780
781 name = interaction.name
782 same_name = next((p for p in self.vertices if p.name==name), None)
783 if same_name:
784 interaction.name = '%s%s' % (interaction.name, self.addon)
785
786
787 particles = [p.replace if hasattr(p, 'replace') else p for p in interaction.particles]
788 interaction.particles = particles
789
790
791 lorentz = [l.replace if hasattr(l, 'replace') else l for l in interaction.lorentz]
792 interaction.lorentz = lorentz
793
794
795 couplings = [(key, c.replace) if hasattr(c, 'replace') else (key, c)
796 for key, c in interaction.couplings.items()]
797 interaction.couplings = dict(couplings)
798
799
800
801 loop_particles=[ [p.replace if hasattr(p, 'replace') else p for p in plist]
802 for plist in interaction.loop_particles]
803 interaction.loop_particles = loop_particles
804 self.CTvertices.append(interaction)
805
806
807 - def add_model(self, model=None, path=None, identify_particles=None):
808 """add another model in the current one"""
809
810 self.new_external = []
811 if path:
812 model = ufomodels.load_model(path)
813
814 if not model:
815 raise USRMODERROR, 'Need a valid Model'
816 else:
817 path = model.__path__[0]
818
819 if not hasattr(model, 'all_orders'):
820 raise USRMODERROR, 'Add-on Model doesn\'t follows UFO convention (no couplings_order information)\n' +\
821 'MG5 is able to load such model but NOT to the add model feature.'
822 if isinstance(model.all_particles[0].mass, basestring):
823 raise USRMODERROR, 'Add-on Model doesn\'t follows UFO convention (Mass/Width of particles are string name, not object)\n' +\
824 'MG5 is able to load such model but NOT to the add model feature.'
825
826 for order in model.all_orders:
827 if hasattr(order, 'perturbative_expansion') and order.perturbative_expansion:
828 raise USRMODERROR, 'Add-on model can not be loop model.'
829
830 for order in model.all_orders:
831 self.add_coupling_order(order)
832
833
834
835 identify_pid = {}
836 if identify_particles:
837 for new, old in identify_particles.items():
838 new_part = next((p for p in model.all_particles if p.name==new), None)
839 old_part = next((p for p in self.particles if p.name==old), None)
840 identify_pid[new_part.pdg_code] = old_part.pdg_code
841
842 if new_part is None:
843 raise USRMODERROR, "particle %s not in added model" % new
844 if old_part is None:
845 raise USRMODERROR, "particle %s not in original model" % old
846 if new_part.antiname not in identify_particles:
847 new_anti = new_part.antiname
848 old_anti = old_part.antiname
849 misc.sprint(old, new, new_anti, old_anti, old_part.antiname)
850 if old_anti == old:
851 raise USRMODERROR, "failed identification (one particle is self-conjugate and not the other)"
852 logger.info("adding identification for anti-particle: %s=%s" % (new_anti, old_anti))
853 identify_particles[new_anti] = old_anti
854
855 for parameter in model.all_parameters:
856 self.add_parameter(parameter, identify_pid)
857 for coupling in model.all_couplings:
858 self.add_coupling(coupling)
859 for lorentz in model.all_lorentz:
860 self.add_lorentz(lorentz)
861 for particle in model.all_particles:
862 if particle.name in identify_particles:
863 self.add_particle(particle, identify=identify_particles[particle.name])
864 else:
865 self.add_particle(particle)
866 for vertex in model.all_vertices:
867 self.add_interaction(vertex)
868
869 self.all_path.append(path)
870
871
872 return
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908