Merge lp:~madteam/mg5amcnlo/new_color into lp:~madteam/mg5amcnlo/trunk
- new_color
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~madteam/mg5amcnlo/new_color |
Merge into: | lp:~madteam/mg5amcnlo/trunk |
Diff against target: |
2772 lines (+2381/-38) 17 files modified
.project (+4/-0) .pydevproject (+1/-1) madgraph/VERSION (+2/-2) madgraph/core/base_objects.py (+41/-7) madgraph/core/color_algebra.py (+607/-0) madgraph/core/color_amp.py (+439/-0) madgraph/interface/cmd_interface.py (+41/-0) madgraph/iolibs/files.py (+24/-0) madgraph/iolibs/import_model_v4.py (+5/-0) madgraph/iolibs/save_model.py (+89/-0) tests/test_manager.py (+5/-1) tests/unit_tests/core/test_base_objects.py (+11/-9) tests/unit_tests/core/test_color_algebra.py (+352/-0) tests/unit_tests/core/test_color_amp.py (+600/-0) tests/unit_tests/core/test_diagram_generation.py (+10/-10) tests/unit_tests/iolibs/test_import_model_v4.py (+23/-8) tests/unit_tests/iolibs/test_save_model.py (+127/-0) |
To merge this branch: | bzr merge lp:~madteam/mg5amcnlo/new_color |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Johan Alwall (community) | Approve | ||
Olivier Mattelaer | Approve | ||
Tim Stelzer | Approve | ||
Review via email: mp+17008@code.launchpad.net |
Commit message
Description of the change
Michel Herquet (herquet) wrote : | # |
Tim Stelzer (tstelzer) wrote : | # |
Very nice, and very clear. Only part that took me a bit extra to understand were canonical and immutable representations. So perhaps in definition could add couple of lines explaining their purpose.
Olivier Mattelaer (olivier-mattelaer) wrote : | # |
Trully nice, quite clear.
The only point who trully trouble me is ColorString which is anything but a string. Could you think of rename this class and the instance associate to this one.
Event if it's trully well written, my feeling is that we can sometimes split functions in subfunction to increase intention and readability.
for example:
-> color_square l.90: put all the except part in a function
-> color_amp l.36: the routine colorize can be splitted in three.
but this is not mandatory at all.
and to finish some pointless details:
-> color_algebra l.448: For readability, it's not better to put a space after the lambda (nothing is specified in PEP08 on this specific case)
-> color_amp line 158: the code content in the try is too much larger. it calls two different functions. such that the try can hidden a keyError produces inside those functions.
At least simplify() should be in the part else:
->color square l.85: potentially same type of problem. In this cas 3 different KeyError are potentially raised. Are you 200% that only the first can create KeyError?
-> color square l.67: unused argument contrib_list1 (and two line after contrib_list2)
why not use a .keys() instead of .enumerate()
Very good work!!!
- 69. By Michel Herquet
-
New version to adress Tim and Olivier's comments
Olivier Mattelaer (olivier-mattelaer) wrote : | # |
thanks for those modifications!!
Johan Alwall (johan-alwall) wrote : | # |
Comments for color:
* Overall:
- Very nicely commented and readable code
- Apparently highly (maximally?) optimized
- Extremely neat and clear solutions
* Why create three modules instead of just one bigger module?
Do you foresee uses of color_algebra without the others?
Do you suggest that we should break up other models as well?
Is there a problem that there are parts of a module that are not used?
I would have expected that we only want to separate a module into
components if they belong to different parts of the directory
structure, such as core and io. I think it is important that we follow
the same philosophy, and this should be fixed before the merge if possible.
color_amp.py:
* line 87+: I think we need to make sure we order interactions in the
same way (between helas calls and color). I order according to cyclic
order in interaction and leg number. But we can take that after the merge.
color_square.py:
* line 43: "Be careful that" -> "Note that" (the formulation is a bit
confusing)
color_algebra.py:
* No comments, the code seems great!
- 70. By Michel Herquet
-
Fix Johan's comment
Michel Herquet (herquet) wrote : | # |
On Jan 15, 2010, at 3:59 PM, Johan Alwall wrote:
> Review: Needs Information
> Comments for color:
>
> * Overall:
> - Very nicely commented and readable code
> - Apparently highly (maximally?) optimized
> - Extremely neat and clear solutions
Thanks!
> * Why create three modules instead of just one bigger module?
> Do you foresee uses of color_algebra without the others?
> Do you suggest that we should break up other models as well?
> Is there a problem that there are parts of a module that are not used?
> I would have expected that we only want to separate a module into
> components if they belong to different parts of the directory
> structure, such as core and io. I think it is important that we follow
> the same philosophy, and this should be fixed before the merge if possible.
I agree, let's discuss this as a first step in the merge procedure. My current
POV is that we should give a good equilibrium between structure
clarity and readability (long files are hard to deal with). Also color_algebra
can indeed be used independently of v5, while it's not the case for the two
other modules. But maybe we can merge those...
> color_amp.py:
> * line 87+: I think we need to make sure we order interactions in the
> same way (between helas calls and color). I order according to cyclic
> order in interaction and leg number. But we can take that after the merge.
Right.
> color_square.py:
> * line 43: "Be careful that" -> "Note that" (the formulation is a bit
> confusing)
Fixed.
> color_algebra.py:
> * No comments, the code seems great!
Thank for your time reviewing it!
>
> --
> https:/
> You proposed lp:~madteam/madgraph5/new_color for merging.
Michel Herquet
-------
Postdoctoral researcher in Particle Physics Phenomenology
NIKHEF - Theory group E-mail: <email address hidden>
Science Park 105 Tel: +31 20 592 51 48
1098 XG Amsterdam Skype: mherquet
The Netherlands G-chat: <email address hidden>
Johan Alwall (johan-alwall) wrote : | # |
Thanks, great job!
- 71. By Michel Herquet
-
Merge color_square and color_amp
- 72. By Michel Herquet
-
Last revision before merge
Preview Diff
1 | === modified file '.project' |
2 | --- .project 2009-12-17 12:45:20 +0000 |
3 | +++ .project 2010-01-21 09:19:15 +0000 |
4 | @@ -1,6 +1,10 @@ |
5 | <?xml version="1.0" encoding="UTF-8"?> |
6 | <projectDescription> |
7 | +<<<<<<< TREE |
8 | <name>MG-process-devel</name> |
9 | +======= |
10 | + <name>MG-new_color</name> |
11 | +>>>>>>> MERGE-SOURCE |
12 | <comment></comment> |
13 | <projects> |
14 | </projects> |
15 | |
16 | === modified file '.pydevproject' |
17 | --- .pydevproject 2010-01-21 09:12:43 +0000 |
18 | +++ .pydevproject 2010-01-21 09:19:15 +0000 |
19 | @@ -5,6 +5,6 @@ |
20 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property> |
21 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> |
22 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> |
23 | -<path>/MG-trunk</path> |
24 | +<path>/MG-new_color</path> |
25 | </pydev_pathproperty> |
26 | </pydev_project> |
27 | |
28 | === modified file 'madgraph/VERSION' |
29 | --- madgraph/VERSION 2009-10-21 14:20:40 +0000 |
30 | +++ madgraph/VERSION 2010-01-21 09:19:15 +0000 |
31 | @@ -1,2 +1,2 @@ |
32 | -version = 0.2.0 |
33 | -date = 2009-11-05 |
34 | \ No newline at end of file |
35 | +version = 0.3.0/color |
36 | +date = 2010-01-11 |
37 | \ No newline at end of file |
38 | |
39 | === modified file 'madgraph/core/base_objects.py' |
40 | --- madgraph/core/base_objects.py 2010-01-20 03:15:48 +0000 |
41 | +++ madgraph/core/base_objects.py 2010-01-21 09:19:15 +0000 |
42 | @@ -21,6 +21,8 @@ |
43 | import logging |
44 | import re |
45 | |
46 | +import madgraph.core.color_algebra as color |
47 | + |
48 | #=============================================================================== |
49 | # PhysicsObject |
50 | #=============================================================================== |
51 | @@ -461,8 +463,18 @@ |
52 | raise self.PhysicsObjectError, \ |
53 | "%s is not a valid integer" % str(value[order]) |
54 | |
55 | - if name in ['color', 'lorentz']: |
56 | - #Should be a list of strings |
57 | + if name in ['color']: |
58 | + #Should be a list of list strings |
59 | + if not isinstance(value, list): |
60 | + raise self.PhysicsObjectError, \ |
61 | + "%s is not a valid list of Color Strings" % str(value) |
62 | + for mycolstring in value: |
63 | + if not isinstance(mycolstring, color.ColorString): |
64 | + raise self.PhysicsObjectError, \ |
65 | + "%s is not a valid list of Color Strings" % str(value) |
66 | + |
67 | + if name in ['lorentz']: |
68 | + #Should be a list of list strings |
69 | if not isinstance(value, list): |
70 | raise self.PhysicsObjectError, \ |
71 | "%s is not a valid list of strings" % str(value) |
72 | @@ -478,11 +490,6 @@ |
73 | "%s is not a valid dictionary for couplings" % \ |
74 | str(value) |
75 | |
76 | - if len(value) != len(self['color']) * len(self['lorentz']): |
77 | - raise self.PhysicsObjectError, \ |
78 | - "Dictionary " + str(value) + \ |
79 | - " for couplings has not the right number of entry" |
80 | - |
81 | for key in value.keys(): |
82 | if not isinstance(key, tuple): |
83 | raise self.PhysicsObjectError, \ |
84 | @@ -542,6 +549,29 @@ |
85 | else: |
86 | ref_dict_to1[pdg_tuple] = [(pdg_part, self['id'])] |
87 | |
88 | + def __str__(self): |
89 | + """String representation of an interaction. Outputs valid Python |
90 | + with improved format. Overrides the PhysicsObject __str__ to only |
91 | + display PDG code of involved particles.""" |
92 | + |
93 | + mystr = '{\n' |
94 | + |
95 | + for prop in self.get_sorted_keys(): |
96 | + if isinstance(self[prop], str): |
97 | + mystr = mystr + ' \'' + prop + '\': \'' + \ |
98 | + self[prop] + '\',\n' |
99 | + elif isinstance(self[prop], float): |
100 | + mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop] |
101 | + elif isinstance(self[prop], ParticleList): |
102 | + mystr = mystr + ' \'' + prop + '\': [%s],\n' % \ |
103 | + ','.join([str(part.get_pdg_code()) for part in self[prop]]) |
104 | + else: |
105 | + mystr = mystr + ' \'' + prop + '\': ' + \ |
106 | + repr(self[prop]) + ',\n' |
107 | + mystr = mystr.rstrip(',\n') |
108 | + mystr = mystr + '\n}' |
109 | + |
110 | + return mystr |
111 | |
112 | #=============================================================================== |
113 | # InteractionList |
114 | @@ -1067,7 +1097,11 @@ |
115 | for vert in self['vertices']: |
116 | mystr = mystr + '(' |
117 | for leg in vert['legs'][:-1]: |
118 | +<<<<<<< TREE |
119 | mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ',' |
120 | +======= |
121 | + mystr = mystr + str(leg['number']) + ',' |
122 | +>>>>>>> MERGE-SOURCE |
123 | if self['vertices'].index(vert) < len(self['vertices']) - 1: |
124 | # Do not want ">" in the last vertex |
125 | mystr = mystr[:-1] + '>' |
126 | |
127 | === added file 'madgraph/core/color_algebra.py' |
128 | --- madgraph/core/color_algebra.py 1970-01-01 00:00:00 +0000 |
129 | +++ madgraph/core/color_algebra.py 2010-01-21 09:19:15 +0000 |
130 | @@ -0,0 +1,607 @@ |
131 | +################################################################################ |
132 | +# |
133 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
134 | +# |
135 | +# This file is a part of the MadGraph 5 project, an application which |
136 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
137 | +# high-energy processes in the Standard Model and beyond. |
138 | +# |
139 | +# It is subject to the MadGraph license which should accompany this |
140 | +# distribution. |
141 | +# |
142 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
143 | +# |
144 | +################################################################################ |
145 | + |
146 | +"""Classes and methods required for all calculations related to SU(N) color |
147 | +algebra.""" |
148 | + |
149 | +import array |
150 | +import copy |
151 | +import fractions |
152 | +import itertools |
153 | + |
154 | +#=============================================================================== |
155 | +# ColorObject |
156 | +#=============================================================================== |
157 | +class ColorObject(array.array): |
158 | + """Parent class for all color objects like T, Tr, f, d, ... Any new color |
159 | + object MUST inherit from this class!""" |
160 | + |
161 | + def __new__(self, *args): |
162 | + """Create a new ColorObject, assuming an integer array""" |
163 | + |
164 | + return super(ColorObject, self).__new__(self, 'i', args) |
165 | + |
166 | + def __str__(self): |
167 | + """Returns a standard string representation.""" |
168 | + |
169 | + return '%s(%s)' % (self.__class__.__name__, |
170 | + ','.join([str(i) for i in self])) |
171 | + |
172 | + __repr__ = __str__ |
173 | + |
174 | + def simplify(self): |
175 | + """Simplification rules, to be overwritten for each new color object! |
176 | + Should return a color factor or None if no simplification is possible""" |
177 | + return None |
178 | + |
179 | + def pair_simplify(self, other): |
180 | + """Pair simplification rules, to be overwritten for each new color |
181 | + object! Should return a color factor or None if no simplification |
182 | + is possible""" |
183 | + return None |
184 | + |
185 | + def complex_conjugate(self): |
186 | + """Complex conjugation. By default, the ordering of color index is |
187 | + reversed. Can be overwritten for specific color objects like T,...""" |
188 | + |
189 | + self.reverse() |
190 | + |
191 | + def replace_indices(self, repl_dict): |
192 | + """Replace current indices following the rules listed in the replacement |
193 | + dictionary written as {old_index:new_index,...}. Deals correctly with |
194 | + the replacement by allowing only one single replacement.""" |
195 | + |
196 | + for i, index in enumerate(self): |
197 | + try: |
198 | + self[i] = repl_dict[index] |
199 | + except KeyError: |
200 | + continue |
201 | + |
202 | + def create_copy(self): |
203 | + """Return a real copy of the current object.""" |
204 | + return globals()[self.__class__.__name__](*self) |
205 | + |
206 | + __copy__ = create_copy |
207 | + |
208 | +#=============================================================================== |
209 | +# Tr |
210 | +#=============================================================================== |
211 | +class Tr(ColorObject): |
212 | + """The trace color object""" |
213 | + |
214 | + def simplify(self): |
215 | + """Implement simple trace simplifications and cyclicity, and |
216 | + Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))""" |
217 | + |
218 | + # Tr(a)=0 |
219 | + if len(self) == 1: |
220 | + col_str = ColorString() |
221 | + col_str.coeff = fractions.Fraction(0, 1) |
222 | + return ColorFactor([col_str]) |
223 | + |
224 | + # Tr()=Nc |
225 | + if len(self) == 0: |
226 | + col_str = ColorString() |
227 | + col_str.Nc_power = 1 |
228 | + return ColorFactor([col_str]) |
229 | + |
230 | + # Always order starting from smallest index |
231 | + if self[0] != min(self): |
232 | + pos = self.index(min(self)) |
233 | + new = self[pos:] + self[:pos] |
234 | + return ColorFactor([ColorString([Tr(*new)])]) |
235 | + |
236 | + # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c)) |
237 | + for i1, index1 in enumerate(self): |
238 | + for i2, index2 in enumerate(self[i1 + 1:]): |
239 | + if index1 == index2: |
240 | + a = self[:i1] |
241 | + b = self[i1 + 1:i1 + i2 + 1] |
242 | + c = self[i1 + i2 + 2:] |
243 | + col_str1 = ColorString([Tr(*(a + c)), Tr(*b)]) |
244 | + col_str2 = ColorString([Tr(*(a + b + c))]) |
245 | + col_str1.coeff = fractions.Fraction(1, 2) |
246 | + col_str2.coeff = fractions.Fraction(-1, 2) |
247 | + col_str2.Nc_power = -1 |
248 | + return ColorFactor([col_str1, col_str2]) |
249 | + |
250 | + return None |
251 | + |
252 | + def pair_simplify(self, col_obj): |
253 | + """Implement Tr product simplification: |
254 | + Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) and |
255 | + Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j))""" |
256 | + |
257 | + # Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) |
258 | + if isinstance(col_obj, Tr): |
259 | + for i1, index1 in enumerate(self): |
260 | + for i2, index2 in enumerate(col_obj): |
261 | + if index1 == index2: |
262 | + a = self[:i1] |
263 | + b = self[i1 + 1:] |
264 | + c = col_obj[:i2] |
265 | + d = col_obj[i2 + 1:] |
266 | + col_str1 = ColorString([Tr(*(a + d + c + b))]) |
267 | + col_str2 = ColorString([Tr(*(a + b)), Tr(*(c + d))]) |
268 | + col_str1.coeff = fractions.Fraction(1, 2) |
269 | + col_str2.coeff = fractions.Fraction(-1, 2) |
270 | + col_str2.Nc_power = -1 |
271 | + return ColorFactor([col_str1, col_str2]) |
272 | + |
273 | + # Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j)) |
274 | + if isinstance(col_obj, T): |
275 | + for i1, index1 in enumerate(self): |
276 | + for i2, index2 in enumerate(col_obj[:-2]): |
277 | + if index1 == index2: |
278 | + a = self[:i1] |
279 | + b = self[i1 + 1:] |
280 | + c = col_obj[:i2] |
281 | + d = col_obj[i2 + 1:-2] |
282 | + ij = col_obj[-2:] |
283 | + col_str1 = ColorString([T(*(c + b + a + d + ij))]) |
284 | + col_str2 = ColorString([Tr(*(a + b)), T(*(c + d) + ij)]) |
285 | + col_str1.coeff = fractions.Fraction(1, 2) |
286 | + col_str2.coeff = fractions.Fraction(-1, 2) |
287 | + col_str2.Nc_power = -1 |
288 | + return ColorFactor([col_str1, col_str2]) |
289 | + |
290 | + return None |
291 | + |
292 | +#=============================================================================== |
293 | +# T |
294 | +#=============================================================================== |
295 | +class T(ColorObject): |
296 | + """The T color object. Last two indices have a special meaning""" |
297 | + |
298 | + def __init__(self, *args): |
299 | + """Check for at least two indices""" |
300 | + |
301 | + super(T, self).__init__() |
302 | + if len(args) < 2: |
303 | + raise ValueError, \ |
304 | + "T objects must have at least two indices!" |
305 | + |
306 | + def simplify(self): |
307 | + """Implement T(a,b,c,...,i,i) = Tr(a,b,c,...) and |
308 | + T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j))""" |
309 | + |
310 | + # T(a,b,c,...,i,i) = Tr(a,b,c,...) |
311 | + if self[-2] == self[-1]: |
312 | + return ColorFactor([ColorString([Tr(*self[:-2])])]) |
313 | + |
314 | + # T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j)) |
315 | + for i1, index1 in enumerate(self[:-2]): |
316 | + for i2, index2 in enumerate(self[i1 + 1:-2]): |
317 | + if index1 == index2: |
318 | + a = self[:i1] |
319 | + b = self[i1 + 1:i1 + i2 + 1] |
320 | + c = self[i1 + i2 + 2:-2] |
321 | + ij = self[-2:] |
322 | + col_str1 = ColorString([T(*(a + c + ij)), Tr(*b)]) |
323 | + col_str2 = ColorString([T(*(a + b + c + ij))]) |
324 | + col_str1.coeff = fractions.Fraction(1, 2) |
325 | + col_str2.coeff = fractions.Fraction(-1, 2) |
326 | + col_str2.Nc_power = -1 |
327 | + return ColorFactor([col_str1, col_str2]) |
328 | + |
329 | + return None |
330 | + |
331 | + def pair_simplify(self, col_obj, simplify_T_product=False): |
332 | + """Implement T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) |
333 | + and T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) |
334 | + -1/Nc T(a,b,i,j)T(c,d,k,l)) |
335 | + but only if the simplify_T_product tag is True.""" |
336 | + |
337 | + if isinstance(col_obj, T): |
338 | + ij1 = self[-2:] |
339 | + ij2 = col_obj[-2:] |
340 | + |
341 | + # T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) |
342 | + if ij1[1] == ij2[0]: |
343 | + return ColorFactor([ColorString([T(*(self[:-2] + \ |
344 | + col_obj[:-2] + \ |
345 | + array.array('i', [ij1[0], |
346 | + ij2[1]])))])]) |
347 | + # T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) |
348 | + # -1/Nc T(a,b,i,j)T(c,d,k,l)) |
349 | + if simplify_T_product: |
350 | + for i1, index1 in enumerate(self[:-2]): |
351 | + for i2, index2 in enumerate(col_obj[:-2]): |
352 | + if index1 == index2: |
353 | + a = self[:i1] |
354 | + b = self[i1 + 1:-2] |
355 | + c = col_obj[:i2] |
356 | + d = col_obj[i2 + 1:-2] |
357 | + col_str1 = ColorString([T(*(a + d + \ |
358 | + array.array('i', |
359 | + [ij1[0], ij2[1]]))), |
360 | + T(*(c + b + \ |
361 | + array.array('i', |
362 | + [ij2[0], ij1[1]])))]) |
363 | + col_str2 = ColorString([T(*(a + b + \ |
364 | + array.array('i', |
365 | + [ij1[0], ij1[1]]))), |
366 | + T(*(c + d + \ |
367 | + array.array('i', |
368 | + [ij2[0], ij2[1]])))]) |
369 | + col_str1.coeff = fractions.Fraction(1, 2) |
370 | + col_str2.coeff = fractions.Fraction(-1, 2) |
371 | + col_str2.Nc_power = -1 |
372 | + return ColorFactor([col_str1, col_str2]) |
373 | + |
374 | + def complex_conjugate(self): |
375 | + """Complex conjugation. Overwritten here because the two last indices |
376 | + should be treated differently""" |
377 | + |
378 | + # T(a,b,c,i,j)* = T(c,b,a,j,i) |
379 | + l1 = self[:-2] |
380 | + l1.reverse() |
381 | + l2 = self[-2:] |
382 | + l2.reverse() |
383 | + self[:] = l1 + l2 |
384 | + |
385 | +#=============================================================================== |
386 | +# f |
387 | +#=============================================================================== |
388 | +class f(ColorObject): |
389 | + """The f color object""" |
390 | + |
391 | + def __init__(self, *args): |
392 | + """Ensure f and d objects have strictly 3 indices""" |
393 | + |
394 | + super(f, self).__init__() |
395 | + if len(args) != 3: |
396 | + raise ValueError, \ |
397 | + "f and d objects must have three indices!" |
398 | + |
399 | + def simplify(self): |
400 | + """Implement only the replacement rule |
401 | + f(a,b,c)=-2ITr(a,b,c)+2ITr(c,b,a)""" |
402 | + |
403 | + indices = self[:] |
404 | + col_str1 = ColorString([Tr(*indices)]) |
405 | + indices.reverse() |
406 | + col_str2 = ColorString([Tr(*indices)]) |
407 | + |
408 | + col_str1.coeff = fractions.Fraction(-2, 1) |
409 | + col_str2.coeff = fractions.Fraction(2, 1) |
410 | + |
411 | + col_str1.is_imaginary = True |
412 | + col_str2.is_imaginary = True |
413 | + |
414 | + return ColorFactor([col_str1, col_str2]) |
415 | + |
416 | +#=============================================================================== |
417 | +# d |
418 | +#=============================================================================== |
419 | +class d(f): |
420 | + """The d color object""" |
421 | + |
422 | + def simplify(self): |
423 | + """Implement only the replacement rule |
424 | + d(a,b,c)=2Tr(a,b,c)+2Tr(c,b,a)""" |
425 | + |
426 | + indices = self[:] |
427 | + col_str1 = ColorString([Tr(*indices)]) |
428 | + indices.reverse() |
429 | + col_str2 = ColorString([Tr(*indices)]) |
430 | + |
431 | + col_str1.coeff = fractions.Fraction(2, 1) |
432 | + col_str2.coeff = fractions.Fraction(2, 1) |
433 | + |
434 | + return ColorFactor([col_str1, col_str2]) |
435 | + |
436 | +#=============================================================================== |
437 | +# ColorString |
438 | +#=============================================================================== |
439 | +class ColorString(list): |
440 | + """A list of ColorObjects with an implicit multiplication between, |
441 | + together with a Fraction coefficient and a tag |
442 | + to indicate if the coefficient is real or imaginary. ColorStrings can be |
443 | + simplified, by simplifying their elements.""" |
444 | + |
445 | + coeff = fractions.Fraction(1, 1) |
446 | + is_imaginary = False |
447 | + Nc_power = 0 |
448 | + |
449 | + def __init__(self, init_list=[], |
450 | + coeff=fractions.Fraction(1, 1), |
451 | + is_imaginary=False, Nc_power=0): |
452 | + """Overrides norm list constructor to implement easy modification |
453 | + of coeff, is_imaginary and Nc_power""" |
454 | + |
455 | + if init_list: |
456 | + self.extend(init_list) |
457 | + self.coeff = coeff |
458 | + self.is_imaginary = is_imaginary |
459 | + self.Nc_power = Nc_power |
460 | + |
461 | + def __str__(self): |
462 | + """Returns a standard string representation based on color object |
463 | + representations""" |
464 | + |
465 | + coeff_str = str(self.coeff) |
466 | + if self.is_imaginary: |
467 | + coeff_str += ' I' |
468 | + if self.Nc_power > 0: |
469 | + coeff_str += ' Nc^%i' % self.Nc_power |
470 | + elif self.Nc_power < 0: |
471 | + coeff_str += ' 1/Nc^%i' % abs(self.Nc_power) |
472 | + return '%s %s' % (coeff_str, |
473 | + ' '.join([str(col_obj) for col_obj in self])) |
474 | + |
475 | + __repr__ = __str__ |
476 | + |
477 | + def product(self, other): |
478 | + """Multiply self with other.""" |
479 | + |
480 | + self.coeff = self.coeff * other.coeff |
481 | + |
482 | + self.Nc_power = self.Nc_power + other.Nc_power |
483 | + |
484 | + # Complex algebra |
485 | + if self.is_imaginary and other.is_imaginary: |
486 | + self.is_imaginary = False |
487 | + self.coeff = -self.coeff |
488 | + elif self.is_imaginary or other.is_imaginary: |
489 | + self.is_imaginary = True |
490 | + |
491 | + self.extend(other) |
492 | + |
493 | + def simplify(self): |
494 | + """Simplify the current ColorString by applying simplify rules on |
495 | + each element and building a new ColorFactor to return if necessary""" |
496 | + |
497 | + # First, try sto simplify element by element |
498 | + for i1, col_obj1 in enumerate(self): |
499 | + res = col_obj1.simplify() |
500 | + # If a simplification possibility is found... |
501 | + if res: |
502 | + # Create a color factor to store the answer... |
503 | + res_col_factor = ColorFactor() |
504 | + # Obtained my multiplying the initial string minus the color |
505 | + # object to simplify with all color strings in the result |
506 | + for second_col_str in res: |
507 | + first_col_str = copy.copy(self) |
508 | + del first_col_str[i1] |
509 | + first_col_str.product(second_col_str) |
510 | + # This sort is necessary to ensure ordering of ColorObjects |
511 | + # remains the same for comparison |
512 | + first_col_str.sort() |
513 | + res_col_factor.append(first_col_str) |
514 | + return res_col_factor |
515 | + |
516 | + # Second, try to simplify pairs |
517 | + for i1, col_obj1 in enumerate(self): |
518 | + |
519 | + for i2, col_obj2 in enumerate(self[i1 + 1:]): |
520 | + res = col_obj1.pair_simplify(col_obj2) |
521 | + # Try both pairing |
522 | + if not res: |
523 | + res = col_obj2.pair_simplify(col_obj1) |
524 | + if res: |
525 | + res_col_factor = ColorFactor() |
526 | + for second_col_str in res: |
527 | + first_col_str = copy.copy(self) |
528 | + del first_col_str[i1] |
529 | + del first_col_str[i1 + i2] |
530 | + first_col_str.product(second_col_str) |
531 | + first_col_str.sort() |
532 | + res_col_factor.append(first_col_str) |
533 | + return res_col_factor |
534 | + |
535 | + return None |
536 | + |
537 | + def add(self, other): |
538 | + """Add string other to current string. ONLY USE WITH SIMILAR STRINGS!""" |
539 | + |
540 | + self.coeff = self.coeff + other.coeff |
541 | + |
542 | + def complex_conjugate(self): |
543 | + """Returns the complex conjugate of the current color string""" |
544 | + |
545 | + compl_conj_str = copy.copy(self) |
546 | + for col_obj in compl_conj_str: |
547 | + col_obj.complex_conjugate() |
548 | + if compl_conj_str.is_imaginary: |
549 | + compl_conj_str.coeff = -compl_conj_str.coeff |
550 | + |
551 | + return compl_conj_str |
552 | + |
553 | + def to_immutable(self): |
554 | + """Returns an immutable object summarizing the color structure of the |
555 | + current color string. Format is ((name1,indices1),...) where name is the |
556 | + class name of the color object and indices a tuple corresponding to its |
557 | + indices. An immutable object, in Python, is built on tuples, strings and |
558 | + numbers, i.e. objects which cannot be modified. Their crucial property |
559 | + is that they can be used as dictionary keys!""" |
560 | + |
561 | + ret_list = [(col_obj.__class__.__name__, tuple(col_obj)) \ |
562 | + for col_obj in self] |
563 | + ret_list.sort() |
564 | + return tuple(ret_list) |
565 | + |
566 | + def from_immutable(self, immutable_rep): |
567 | + """Fill the current object with Color Objects created using an immutable |
568 | + representation.""" |
569 | + |
570 | + del self[:] |
571 | + |
572 | + for col_tuple in immutable_rep: |
573 | + self.append(globals()[col_tuple[0]](*col_tuple[1])) |
574 | + |
575 | + def replace_indices(self, repl_dict): |
576 | + """Replace current indices following the rules listed in the replacement |
577 | + dictionary written as {old_index:new_index,...}, does that for ALL |
578 | + color objects.""" |
579 | + |
580 | + map(lambda col_obj: col_obj.replace_indices(repl_dict), self) |
581 | + |
582 | + def create_copy(self): |
583 | + """Returns a real copy of self, non trivial because bug in |
584 | + copy.deepcopy""" |
585 | + |
586 | + res = ColorString() |
587 | + for col_obj in self: |
588 | + res.append(col_obj.create_copy()) |
589 | + res.coeff = self.coeff |
590 | + res.is_imaginary = self.is_imaginary |
591 | + res.Nc_power = self.Nc_power |
592 | + |
593 | + return res |
594 | + |
595 | + __copy__ = create_copy |
596 | + |
597 | + def set_Nc(self, Nc=3): |
598 | + """Returns a tuple, with the first entry being the string coefficient |
599 | + with Nc replaced (by default by 3), and the second one being True |
600 | + or False if the coefficient is imaginary or not. Raise an error if there |
601 | + are still non trivial color objects.""" |
602 | + |
603 | + if self: |
604 | + raise ValueError, \ |
605 | + "String %s cannot be simplified to a number!" % str(self) |
606 | + |
607 | + if self.Nc_power >= 0: |
608 | + return (self.coeff * fractions.Fraction(\ |
609 | + int(Nc ** self.Nc_power), 1), |
610 | + self.is_imaginary) |
611 | + else: |
612 | + return (self.coeff * fractions.Fraction(\ |
613 | + 1, int(Nc ** abs(self.Nc_power))), |
614 | + self.is_imaginary) |
615 | + |
616 | + def to_canonical(self, immutable=None): |
617 | + """Returns the canonical representation of the immutable representation |
618 | + (i.e., first index is 1, ...). This allow for an easy comparison of |
619 | + two color strings, i.e. independently of the actual index names (only |
620 | + relative positions matter). Also returns the conversion dictionary. |
621 | + If no immutable representation is given, use the one build from self.""" |
622 | + |
623 | + if not immutable: |
624 | + immutable = self.to_immutable() |
625 | + |
626 | + replaced_indices = {} |
627 | + curr_ind = 1 |
628 | + return_list = [] |
629 | + |
630 | + for elem in immutable: |
631 | + can_elem = [elem[0], []] |
632 | + for index in elem[1]: |
633 | + try: |
634 | + new_index = replaced_indices[index] |
635 | + except KeyError: |
636 | + new_index = curr_ind |
637 | + curr_ind += 1 |
638 | + replaced_indices[index] = new_index |
639 | + can_elem[1].append(new_index) |
640 | + return_list.append((can_elem[0], tuple(can_elem[1]))) |
641 | + |
642 | + return_list.sort() |
643 | + |
644 | + return (tuple(return_list), replaced_indices) |
645 | +#=============================================================================== |
646 | +# ColorFactor |
647 | +#=============================================================================== |
648 | +class ColorFactor(list): |
649 | + """ColorFactor objects are list of ColorString with an implicit summation. |
650 | + They can be simplified by simplifying all their elements.""" |
651 | + |
652 | + def __str__(self): |
653 | + """Returns a nice string for print""" |
654 | + |
655 | + return '+'.join(['(%s)' % str(col_str) for col_str in self]) |
656 | + |
657 | + def append_str(self, new_str): |
658 | + """Special append taking care of adding new string to strings already |
659 | + existing with the same structure.""" |
660 | + |
661 | + for col_str in self: |
662 | + # Check if strings are similar, this IS the optimal way of doing |
663 | + # it. Note that first line only compare the lists, not the |
664 | + # properties associated |
665 | + if col_str == new_str and \ |
666 | + col_str.Nc_power == new_str.Nc_power and \ |
667 | + col_str.is_imaginary == new_str.is_imaginary: |
668 | + # Add them |
669 | + col_str.add(new_str) |
670 | + return True |
671 | + |
672 | + # If no correspondence is found, append anyway |
673 | + self.append(new_str) |
674 | + return False |
675 | + |
676 | + def extend_str(self, new_col_fact): |
677 | + """Special extend taking care of adding new strings to strings already |
678 | + existing with the same structure.""" |
679 | + |
680 | + for col_str in new_col_fact: |
681 | + self.append_str(col_str) |
682 | + |
683 | + def simplify(self): |
684 | + """Returns a new color factor where each color string has been |
685 | + simplified once and similar strings have been added.""" |
686 | + |
687 | + new_col_factor = ColorFactor() |
688 | + # Simplify |
689 | + for col_str in self: |
690 | + res = col_str.simplify() |
691 | + if res: |
692 | + new_col_factor.extend_str(res) |
693 | + else: |
694 | + new_col_factor.append_str(col_str) |
695 | + |
696 | + # Only returns non zero elements |
697 | + return ColorFactor([col_str for col_str in \ |
698 | + new_col_factor if col_str.coeff != 0]) |
699 | + |
700 | + def full_simplify(self): |
701 | + """Simplify the current color factor until the result is stable""" |
702 | + |
703 | + result = copy.copy(self) |
704 | + while(True): |
705 | + ref = copy.copy(result) |
706 | + result = result.simplify() |
707 | + if result == ref: |
708 | + return result |
709 | + |
710 | + def set_Nc(self, Nc=3): |
711 | + """Returns a tuple containing real and imaginary parts of the current |
712 | + color factor, when Nc is replaced (3 by default).""" |
713 | + |
714 | + return (sum([cs.set_Nc(Nc)[0] for cs in self if not cs.is_imaginary]), |
715 | + sum([cs.set_Nc(Nc)[0] for cs in self if cs.is_imaginary])) |
716 | + |
717 | + |
718 | + def replace_indices(self, repl_dict): |
719 | + """Replace current indices following the rules listed in the replacement |
720 | + dictionary written as {old_index:new_index,...}, does that for ALL |
721 | + color strings.""" |
722 | + |
723 | + map(lambda col_str:col_str.replace_indices(repl_dict), self) |
724 | + |
725 | + def create_copy(self): |
726 | + """Returns a real copy of self, non trivial because bug in |
727 | + copy.deepcopy""" |
728 | + |
729 | + res = ColorFactor() |
730 | + for col_str in self: |
731 | + res.append(col_str.create_copy()) |
732 | + |
733 | + return res |
734 | + |
735 | + __copy__ = create_copy |
736 | + |
737 | + |
738 | |
739 | === added file 'madgraph/core/color_amp.py' |
740 | --- madgraph/core/color_amp.py 1970-01-01 00:00:00 +0000 |
741 | +++ madgraph/core/color_amp.py 2010-01-21 09:19:15 +0000 |
742 | @@ -0,0 +1,439 @@ |
743 | +################################################################################ |
744 | +# |
745 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
746 | +# |
747 | +# This file is a part of the MadGraph 5 project, an application which |
748 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
749 | +# high-energy processes in the Standard Model and beyond. |
750 | +# |
751 | +# It is subject to the MadGraph license which should accompany this |
752 | +# distribution. |
753 | +# |
754 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
755 | +# |
756 | +################################################################################ |
757 | + |
758 | +import copy |
759 | +import operator |
760 | +import re |
761 | + |
762 | +import madgraph.core.color_algebra as color_algebra |
763 | + |
764 | +"""Classes, methods and functions required to write QCD color information |
765 | +for a diagram and build a color basis, and to square a QCD color string for |
766 | +squared diagrams and interference terms.""" |
767 | + |
768 | +#=============================================================================== |
769 | +# ColorBasis |
770 | +#=============================================================================== |
771 | +class ColorBasis(dict): |
772 | + """The ColorBasis object is a dictionary created from an amplitude. Keys |
773 | + are the different color structures present in the amplitude. Values have |
774 | + the format (diag,(index c1, index c2,...), coeff, is_imaginary, Nc_power) |
775 | + where diag is the diagram index, (index c1, index c2,...) the list of |
776 | + indices corresponding to the chose color parts for each vertex in the |
777 | + diagram, coeff the corresponding coefficient (a fraction), is_imaginary |
778 | + if this contribution is real or complex, and Nc_power the Nc power.""" |
779 | + |
780 | + # Dictionary to save simplifications already done in a canonical form |
781 | + _canonical_dict = {} |
782 | + |
783 | + def colorize(self, diagram, model): |
784 | + """Takes a diagram and a model and outputs a dictionary with keys being |
785 | + color coefficient index tuples and values a color string (before |
786 | + simplification).""" |
787 | + |
788 | + # The smallest value used to create new summed indices |
789 | + min_index = -1000 |
790 | + # The dictionary to be output |
791 | + res_dict = {} |
792 | + # The dictionary for book keeping of replaced indices |
793 | + repl_dict = {} |
794 | + |
795 | + for vertex in diagram.get('vertices'): |
796 | + |
797 | + # SPECIAL VERTEX WITH ID = 0 ------------------------------------------- |
798 | + |
799 | + if vertex['id'] == 0: |
800 | + self.add_vertex_id_0(vertex, repl_dict, res_dict) |
801 | + # Return since this must be the last vertex |
802 | + return res_dict |
803 | + |
804 | + # NORMAL VERTICES WITH ID != 0 ----------------------------------------- |
805 | + min_index, res_dict = self.add_vertex(vertex, diagram, model, |
806 | + repl_dict, res_dict, min_index) |
807 | + |
808 | + return res_dict |
809 | + |
810 | + def add_vertex_id_0(self, vertex, repl_dict, res_dict): |
811 | + """Update the repl_dict and res_dict when vertex has id=0, i.e. for |
812 | + the special case of an identity vertex.""" |
813 | + |
814 | + # For vertex (i1,i2), replace all i2 by i1 |
815 | + old_num = vertex.get('legs')[1].get('number') |
816 | + new_num = vertex.get('legs')[0].get('number') |
817 | + # Be careful i1 or i2 might have been replaced themselves |
818 | + try: |
819 | + old_num = repl_dict[old_num] |
820 | + except KeyError: |
821 | + pass |
822 | + try: |
823 | + new_num = repl_dict[new_num] |
824 | + except KeyError: |
825 | + pass |
826 | + # Do the replacement |
827 | + for (ind_chain, col_str_chain) in res_dict.items(): |
828 | + col_str_chain.replace_indices({old_num:new_num}) |
829 | + |
830 | + def add_vertex(self, vertex, diagram, model, |
831 | + repl_dict, res_dict, min_index): |
832 | + """Update repl_dict, res_dict and min_index for normal vertices. |
833 | + Returns the min_index reached and the result dictionary in a tuple.""" |
834 | + |
835 | + # Create a list of pdg codes entering the vertex ordered as in |
836 | + # interactions.py |
837 | + list_pdg = [part.get_pdg_code() for part in \ |
838 | + model.get_interaction(vertex.get('id')).get('particles')] |
839 | + |
840 | + # Create a dictionary pdg code --> leg(s) |
841 | + dict_pdg_leg = {} |
842 | + for index, leg in enumerate(vertex.get('legs')): |
843 | + curr_num = leg.get('number') |
844 | + curr_pdg = leg.get('id') |
845 | + # If this is the last leg and not the last vertex, |
846 | + # flip part/antipart, and replace last index by a new |
847 | + # summed index |
848 | + if index == len(vertex.get('legs')) - 1 and \ |
849 | + vertex != diagram.get('vertices')[-1]: |
850 | + part = model.get('particle_dict')[curr_pdg] |
851 | + curr_pdg = \ |
852 | + model.get('particle_dict')[curr_pdg].get_anti_pdg_code() |
853 | + repl_dict[curr_num] = min_index |
854 | + min_index = min_index - 1 |
855 | + try: |
856 | + curr_num = repl_dict[curr_num] |
857 | + except KeyError: |
858 | + pass |
859 | + try: |
860 | + dict_pdg_leg[curr_pdg].append(curr_num) |
861 | + except KeyError: |
862 | + dict_pdg_leg[curr_pdg] = [curr_num] |
863 | + |
864 | + # Create a list of associated leg number following the same order |
865 | + list_numbers = [] |
866 | + for pdg_code in list_pdg: |
867 | + list_numbers.append(dict_pdg_leg[pdg_code].pop()) |
868 | + # ... and the associated dictionary for replacement |
869 | + match_dict = dict(enumerate(list_numbers)) |
870 | + |
871 | + # Update the result dict using the current vertex ColorString object |
872 | + # If more than one, create different entries |
873 | + new_res_dict = {} |
874 | + for i, col_str in \ |
875 | + enumerate(model.get_interaction(vertex['id'])['color']): |
876 | + # Build the new element |
877 | + mod_col_str = col_str.create_copy() |
878 | + |
879 | + # Replace summed (negative) internal indices |
880 | + list_neg = [] |
881 | + for col_obj in mod_col_str: |
882 | + list_neg.extend([ind for ind in col_obj if ind < 0]) |
883 | + internal_indices_dict = {} |
884 | + # This notation is to remove duplicates |
885 | + for index in list(set(list_neg)): |
886 | + internal_indices_dict[index] = min_index |
887 | + min_index = min_index - 1 |
888 | + mod_col_str.replace_indices(internal_indices_dict) |
889 | + |
890 | + # Replace other (positive) indices using the match_dic |
891 | + mod_col_str.replace_indices(match_dict) |
892 | + # If we are considering the first vertex, simply create |
893 | + # new entries |
894 | + if not res_dict: |
895 | + new_res_dict[tuple([i])] = mod_col_str |
896 | + #... otherwise, loop over existing elements and multiply |
897 | + # the color strings |
898 | + else: |
899 | + for ind_chain, col_str_chain in res_dict.items(): |
900 | + new_col_str_chain = col_str_chain.create_copy() |
901 | + new_col_str_chain.product(mod_col_str) |
902 | + new_res_dict[tuple(list(ind_chain) + [i])] = \ |
903 | + new_col_str_chain |
904 | + |
905 | + return (min_index, new_res_dict) |
906 | + |
907 | + |
908 | + def update_color_basis(self, colorize_dict, index): |
909 | + """Update the current color basis by adding information from |
910 | + the colorize dictionary (produced by the colorize routine) |
911 | + associated to diagram with index index. Keep track of simplification |
912 | + results for maximal optimization.""" |
913 | + |
914 | + # loop over possible color chains |
915 | + for col_chain, col_str in colorize_dict.items(): |
916 | + |
917 | + # Create a canonical immutable representation of the the string |
918 | + canonical_rep, rep_dict = col_str.to_canonical() |
919 | + try: |
920 | + # If this representation has already been considered, |
921 | + # recycle the result. |
922 | + col_fact = copy.copy(self._canonical_dict[canonical_rep]) |
923 | + |
924 | + except KeyError: |
925 | + # If the representation is really new |
926 | + |
927 | + # Create and simplify a color factor for the considered chain |
928 | + col_fact = color_algebra.ColorFactor([col_str]) |
929 | + col_fact = col_fact.full_simplify() |
930 | + |
931 | + # Save the result for further use |
932 | + canonical_col_fact = copy.copy(col_fact) |
933 | + canonical_col_fact.replace_indices(rep_dict) |
934 | + self._canonical_dict[canonical_rep] = canonical_col_fact |
935 | + |
936 | + else: |
937 | + # If this representation has already been considered, |
938 | + # adapt the result |
939 | + # Note that we have to replace back |
940 | + # the indices to match the initial convention. |
941 | + col_fact.replace_indices(self._invert_dict(rep_dict)) |
942 | + # Must simplify once to put traces in a canonical ordering |
943 | + col_fact = col_fact.simplify() |
944 | + |
945 | + # loop over color strings in the resulting color factor |
946 | + for col_str in col_fact: |
947 | + immutable_col_str = col_str.to_immutable() |
948 | + # if the color structure is already present in the present basis |
949 | + # update it |
950 | + basis_entry = (index, |
951 | + col_chain, |
952 | + col_str.coeff, |
953 | + col_str.is_imaginary, |
954 | + col_str.Nc_power) |
955 | + try: |
956 | + self[immutable_col_str].append(basis_entry) |
957 | + except KeyError: |
958 | + self[immutable_col_str] = [basis_entry] |
959 | + |
960 | + def build(self, amplitude, model): |
961 | + """Build the a color basis object using information contained in |
962 | + amplitude and model""" |
963 | + |
964 | + for index, diagram in enumerate(amplitude['diagrams']): |
965 | + colorize_dict = self.colorize(diagram, model) |
966 | + self.update_color_basis(colorize_dict, index) |
967 | + |
968 | + def __init__(self, *args): |
969 | + """Initialize a new color basis object, either empty or filled (0 |
970 | + or 2 arguments). If two arguments are given, the first one is |
971 | + interpreted as the amplitude and the second one as the model.""" |
972 | + |
973 | + if len(args) not in (0, 2): |
974 | + raise ValueError, \ |
975 | + "Object ColorBasis must be initialized with 0 or 2 arguments" |
976 | + |
977 | + if len(args) == 2: |
978 | + self.build(*args) |
979 | + |
980 | + def __str__(self): |
981 | + """Returns a nicely formatted string for display""" |
982 | + |
983 | + my_str = "" |
984 | + for k, v in self.items(): |
985 | + for name, indices in k: |
986 | + my_str = my_str + name + str(indices) |
987 | + my_str = my_str + ': ' |
988 | + for contrib in v: |
989 | + imag_str = '' |
990 | + if contrib[3]: |
991 | + imag_str = 'I' |
992 | + my_str = my_str + '(diag:%i, chain:%s, coeff:%s%s, Nc:%i) ' % \ |
993 | + (contrib[0], contrib[1], contrib[2], |
994 | + imag_str, contrib[4]) |
995 | + my_str = my_str + '\n' |
996 | + return my_str |
997 | + |
998 | + def _invert_dict(self, mydict): |
999 | + """Helper method to invert dictionary dict""" |
1000 | + |
1001 | + return dict([v, k] for k, v in mydict.items()) |
1002 | + |
1003 | +#=============================================================================== |
1004 | +# ColorMatrix |
1005 | +#=============================================================================== |
1006 | +class ColorMatrix(dict): |
1007 | + """A color matrix, i.e. a dictionary with pairs (i,j) as keys where i |
1008 | + and j refer to elements of color basis objects. Values are Color Factor |
1009 | + objects. Also contains two additional dictonaries, one with the fixed Nc |
1010 | + representation of the matrix, and the other one with the "inverted" matrix, |
1011 | + i.e. a dictionary where keys are values of the color matrix.""" |
1012 | + |
1013 | + _col_basis1 = None |
1014 | + _col_basis2 = None |
1015 | + col_matrix_fixed_Nc = {} |
1016 | + inverted_col_matrix = {} |
1017 | + |
1018 | + def __init__(self, col_basis, col_basis2=None, |
1019 | + Nc=3, Nc_power_min=None, Nc_power_max=None): |
1020 | + """Initialize a color matrix with one or two color basis objects. If |
1021 | + only one color basis is given, the other one is assumed to be equal. |
1022 | + As options, any value of Nc and minimal/maximal power of Nc can also be |
1023 | + provided. Note that the min/max power constraint is applied |
1024 | + only at the end, so that it does NOT speed up the calculation.""" |
1025 | + |
1026 | + self._col_basis1 = col_basis |
1027 | + if col_basis2: |
1028 | + self._col_basis2 = col_basis2 |
1029 | + self.build_matrix(Nc, Nc_power_min, Nc_power_max) |
1030 | + else: |
1031 | + self._col_basis2 = col_basis |
1032 | + # If the two color basis are equal, assumes the color matrix is |
1033 | + # symmetric |
1034 | + self.build_matrix(Nc, Nc_power_min, Nc_power_max, is_symmetric=True) |
1035 | + |
1036 | + def build_matrix(self, Nc=3, |
1037 | + Nc_power_min=None, |
1038 | + Nc_power_max=None, |
1039 | + is_symmetric=False): |
1040 | + """Create the matrix using internal color basis objects. Use the stored |
1041 | + color basis objects and takes Nc and Nc_min/max parameters as __init__. |
1042 | + If is_isymmetric is True, build only half of the matrix which is assumed |
1043 | + to be symmetric.""" |
1044 | + |
1045 | + canonical_dict = {} |
1046 | + |
1047 | + for i1, struct1 in \ |
1048 | + enumerate(self._col_basis1.keys()): |
1049 | + for i2, struct2 in \ |
1050 | + enumerate(self._col_basis2.keys()): |
1051 | + |
1052 | + # Only scan upper right triangle if symmetric |
1053 | + if is_symmetric and i2 < i1: |
1054 | + continue |
1055 | + |
1056 | + # Fix indices in struct2 knowing summed indices in struct1 |
1057 | + # to avoid duplicates |
1058 | + new_struct2 = self.fix_summed_indices(struct1, struct2) |
1059 | + |
1060 | + # Build a canonical representation of the two immutable struct |
1061 | + canonical_entry, dummy = \ |
1062 | + color_algebra.ColorString().to_canonical(struct1 + \ |
1063 | + new_struct2) |
1064 | + |
1065 | + try: |
1066 | + # If this has already been calculated, use the result |
1067 | + result, result_fixed_Nc = canonical_dict[canonical_entry] |
1068 | + |
1069 | + except KeyError: |
1070 | + # Otherwise calculate the result |
1071 | + result, result_fixed_Nc = \ |
1072 | + self.create_new_entry(struct1, |
1073 | + new_struct2, |
1074 | + Nc_power_min, |
1075 | + Nc_power_max, |
1076 | + Nc) |
1077 | + # Store both results |
1078 | + canonical_dict[canonical_entry] = (result, result_fixed_Nc) |
1079 | + |
1080 | + # Store the full result... |
1081 | + self[(i1, i2)] = result |
1082 | + if is_symmetric: |
1083 | + self[(i2, i1)] = result |
1084 | + |
1085 | + # the fixed Nc one ... |
1086 | + self.col_matrix_fixed_Nc[(i1, i2)] = result_fixed_Nc |
1087 | + if is_symmetric: |
1088 | + self.col_matrix_fixed_Nc[(i2, i1)] = result_fixed_Nc |
1089 | + # and update the inverted dict |
1090 | + if result_fixed_Nc in self.inverted_col_matrix.keys(): |
1091 | + self.inverted_col_matrix[result_fixed_Nc].append((i1, |
1092 | + i2)) |
1093 | + if is_symmetric: |
1094 | + self.inverted_col_matrix[result_fixed_Nc].append((i2, |
1095 | + i1)) |
1096 | + else: |
1097 | + self.inverted_col_matrix[result_fixed_Nc] = [(i1, i2)] |
1098 | + if is_symmetric: |
1099 | + self.inverted_col_matrix[result_fixed_Nc] = [(i2, i1)] |
1100 | + |
1101 | + def create_new_entry(self, struct1, struct2, |
1102 | + Nc_power_min, Nc_power_max, Nc): |
1103 | + """ Create a new product result, and result with fixed Nc for two color |
1104 | + basis entries. Implement Nc power limits.""" |
1105 | + |
1106 | + # Create color string objects corresponding to color basis |
1107 | + # keys |
1108 | + col_str = color_algebra.ColorString() |
1109 | + col_str.from_immutable(struct1) |
1110 | + |
1111 | + col_str2 = color_algebra.ColorString() |
1112 | + col_str2.from_immutable(struct2) |
1113 | + |
1114 | + # Complex conjugate the second one and multiply the two |
1115 | + col_str.product(col_str2.complex_conjugate()) |
1116 | + |
1117 | + # Create a color factor to store the result and simplify it |
1118 | + # taking into account the limit on Nc |
1119 | + col_fact = color_algebra.ColorFactor([col_str]) |
1120 | + result = col_fact.full_simplify() |
1121 | + |
1122 | + # Keep only terms with Nc_max >= Nc power >= Nc_min |
1123 | + if Nc_power_min is not None: |
1124 | + result[:] = [col_str for col_str in result \ |
1125 | + if col_str.Nc_power >= Nc_power_min] |
1126 | + if Nc_power_max is not None: |
1127 | + result[:] = [col_str for col_str in result \ |
1128 | + if col_str.Nc_power <= Nc_power_max] |
1129 | + |
1130 | + # Calculate the fixed Nc representation |
1131 | + result_fixed_Nc = result.set_Nc(Nc) |
1132 | + |
1133 | + return result, result_fixed_Nc |
1134 | + |
1135 | + def __str__(self): |
1136 | + """Returns a nicely formatted string with the fixed Nc representation |
1137 | + of the current matrix (only the real part)""" |
1138 | + |
1139 | + mystr = '\n\t' + '\t'.join([str(i) for i in \ |
1140 | + range(len(self._col_basis2))]) |
1141 | + |
1142 | + for i1 in range(len(self._col_basis1)): |
1143 | + mystr = mystr + '\n' + str(i1) + '\t' |
1144 | + mystr = mystr + '\t'.join(['%i/%i' % \ |
1145 | + (self.col_matrix_fixed_Nc[(i1, i2)][0].numerator, |
1146 | + self.col_matrix_fixed_Nc[(i1, i2)][0].denominator) \ |
1147 | + for i2 in range(len(self._col_basis2))]) |
1148 | + |
1149 | + return mystr |
1150 | + |
1151 | + @classmethod |
1152 | + def fix_summed_indices(self, struct1, struct2): |
1153 | + """Returns a copy of the immutable Color String representation struct2 |
1154 | + where summed indices are modified to avoid duplicates with those |
1155 | + appearing in struct1. Assumes internal summed indices are negative.""" |
1156 | + |
1157 | + # First, determines what is the smallest index appearing in struct1 |
1158 | + min_index = min(reduce(operator.add, |
1159 | + [list(elem[1]) for elem in struct1])) - 1 |
1160 | + # Second, determines the summed indices in struct2 and create a |
1161 | + # replacement dictionary |
1162 | + repl_dict = {} |
1163 | + list2 = reduce(operator.add, |
1164 | + [list(elem[1]) for elem in struct1]) |
1165 | + for summed_index in list(set([i for i in list2 \ |
1166 | + if list2.count(i) == 2])): |
1167 | + repl_dict[summed_index] = min_index |
1168 | + min_index -= 1 |
1169 | + |
1170 | + # Three, create a new immutable struct by doing replacements in struct2 |
1171 | + return_list = [] |
1172 | + for elem in struct2: |
1173 | + fix_elem = [elem[0], []] |
1174 | + for index in elem[1]: |
1175 | + try: |
1176 | + fix_elem[1].append(repl_dict[index]) |
1177 | + except: |
1178 | + fix_elem[1].append(index) |
1179 | + return_list.append((elem[0], tuple(fix_elem[1]))) |
1180 | + |
1181 | + return tuple(return_list) |
1182 | |
1183 | === modified file 'madgraph/interface/cmd_interface.py' |
1184 | --- madgraph/interface/cmd_interface.py 2010-01-04 06:44:15 +0000 |
1185 | +++ madgraph/interface/cmd_interface.py 2010-01-21 09:19:15 +0000 |
1186 | @@ -28,8 +28,13 @@ |
1187 | |
1188 | import madgraph.iolibs.misc as misc |
1189 | import madgraph.iolibs.files as files |
1190 | +<<<<<<< TREE |
1191 | import madgraph.iolibs.import_v4 as import_v4 |
1192 | import madgraph.iolibs.export_v4 as export_v4 |
1193 | +======= |
1194 | +import madgraph.iolibs.import_model_v4 as import_v4 |
1195 | +import madgraph.iolibs.save_model as save_model |
1196 | +>>>>>>> MERGE-SOURCE |
1197 | |
1198 | import madgraph.core.base_objects as base_objects |
1199 | import madgraph.core.diagram_generation as diagram_generation |
1200 | @@ -204,6 +209,37 @@ |
1201 | base_dir=\ |
1202 | self.split_arg(line[0:begidx])[2]) |
1203 | |
1204 | + def do_save(self, line): |
1205 | + """Save information to file""" |
1206 | + |
1207 | + args = self.split_arg(line) |
1208 | + if len(args) != 2: |
1209 | + self.help_save() |
1210 | + return False |
1211 | + |
1212 | + if args[0] == 'model': |
1213 | + if self.__curr_model: |
1214 | + save_model.save_model(args[1], self.__curr_model) |
1215 | + else: |
1216 | + print 'No model to save!' |
1217 | + |
1218 | + def complete_save(self, text, line, begidx, endidx): |
1219 | + "Complete the save command" |
1220 | + |
1221 | + # Format |
1222 | + if len(self.split_arg(line[0:begidx])) == 1: |
1223 | + return self.list_completion(text, ['model']) |
1224 | + |
1225 | + # Filename if directory is not given |
1226 | + if len(self.split_arg(line[0:begidx])) == 2: |
1227 | + return self.path_completion(text) |
1228 | + |
1229 | + # Filename if directory is given |
1230 | + if len(self.split_arg(line[0:begidx])) == 3: |
1231 | + return self.path_completion(text, |
1232 | + base_dir=\ |
1233 | + self.split_arg(line[0:begidx])[2]) |
1234 | + |
1235 | # Display |
1236 | def do_display(self, line): |
1237 | """Display current internal status""" |
1238 | @@ -535,6 +571,11 @@ |
1239 | sys.exit(1) |
1240 | |
1241 | # In-line help |
1242 | + def help_save(self): |
1243 | + print "syntax: save model|... PATH" |
1244 | + print "-- save information as files in PATH" |
1245 | + |
1246 | + # In-line help |
1247 | def help_import(self): |
1248 | print "syntax: import " + "|".join(self.__import_formats) + \ |
1249 | " FILENAME" |
1250 | |
1251 | === modified file 'madgraph/iolibs/files.py' |
1252 | --- madgraph/iolibs/files.py 2009-12-15 06:14:57 +0000 |
1253 | +++ madgraph/iolibs/files.py 2010-01-21 09:19:15 +0000 |
1254 | @@ -37,6 +37,7 @@ |
1255 | return None |
1256 | |
1257 | return ret_value |
1258 | +<<<<<<< TREE |
1259 | |
1260 | #=============================================================================== |
1261 | # write_to_file |
1262 | @@ -58,3 +59,26 @@ |
1263 | return None |
1264 | |
1265 | return ret_value |
1266 | +======= |
1267 | + |
1268 | +#=============================================================================== |
1269 | +# write_to_file |
1270 | +#=============================================================================== |
1271 | +def write_to_file(filename, myfunct, *args): |
1272 | + """Open a file for writing, apply the function myfunct (with sock as an arg) |
1273 | + on its content and return the result. Deals properly with errors and |
1274 | + returns None if something goes wrong. |
1275 | + """ |
1276 | + |
1277 | + try: |
1278 | + sock = open(filename, 'w') |
1279 | + try: |
1280 | + ret_value = myfunct(sock, *args) |
1281 | + finally: |
1282 | + sock.close() |
1283 | + except IOError, (errno, strerror): |
1284 | + logging.error("I/O error (%s): %s" % (errno, strerror)) |
1285 | + return None |
1286 | + |
1287 | + return ret_value |
1288 | +>>>>>>> MERGE-SOURCE |
1289 | |
1290 | === renamed file 'madgraph/iolibs/import_v4.py' => 'madgraph/iolibs/import_model_v4.py' |
1291 | --- madgraph/iolibs/import_v4.py 2009-12-12 08:42:16 +0000 |
1292 | +++ madgraph/iolibs/import_model_v4.py 2010-01-21 09:19:15 +0000 |
1293 | @@ -158,6 +158,7 @@ |
1294 | # Give a dummy 'guess' values for color and Lorentz structures |
1295 | # Those will have to be replaced by a proper guess! |
1296 | |
1297 | +<<<<<<< TREE |
1298 | myinter.set('color', ['guess']) |
1299 | |
1300 | # Set the Lorentz structure. Default for 3-particle |
1301 | @@ -180,6 +181,10 @@ |
1302 | myinter.get('lorentz')[0] =\ |
1303 | myinter.get('lorentz')[0]\ |
1304 | + values[3 * len(part_list) - 4].upper() |
1305 | +======= |
1306 | + myinter.set('color', [['C1']]) |
1307 | + myinter.set('lorentz', ['L1']) |
1308 | +>>>>>>> MERGE-SOURCE |
1309 | |
1310 | # Use the other strings to fill variable names and tags |
1311 | if len(part_list) == 3: |
1312 | |
1313 | === added file 'madgraph/iolibs/save_model.py' |
1314 | --- madgraph/iolibs/save_model.py 1970-01-01 00:00:00 +0000 |
1315 | +++ madgraph/iolibs/save_model.py 2010-01-21 09:19:15 +0000 |
1316 | @@ -0,0 +1,89 @@ |
1317 | +################################################################################ |
1318 | +# |
1319 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
1320 | +# |
1321 | +# This file is a part of the MadGraph 5 project, an application which |
1322 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
1323 | +# high-energy processes in the Standard Model and beyond. |
1324 | +# |
1325 | +# It is subject to the MadGraph license which should accompany this |
1326 | +# distribution. |
1327 | +# |
1328 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
1329 | +# |
1330 | +################################################################################ |
1331 | + |
1332 | +"""Function to save model files.""" |
1333 | + |
1334 | +import logging |
1335 | +import os |
1336 | + |
1337 | +import madgraph.iolibs.files as files |
1338 | +import madgraph.core.base_objects as base_objects |
1339 | + |
1340 | +def save_particles(fsock, part_list): |
1341 | + """Save particle objects contained in part_list in the stream fsock""" |
1342 | + |
1343 | + if not isinstance(part_list, base_objects.ParticleList): |
1344 | + raise ValueError, \ |
1345 | + "Object %s is not a valid ParticleList" % repr(part_list) |
1346 | + |
1347 | + fsock.write("particles = [\n") |
1348 | + |
1349 | + for part in part_list: |
1350 | + if part_list.index(part) != len(part_list) - 1: |
1351 | + fsock.write(str(part) + ',') |
1352 | + else: |
1353 | + fsock.write(str(part)) |
1354 | + |
1355 | + fsock.write("]") |
1356 | + |
1357 | +def save_interactions(fsock, inter_list): |
1358 | + """Save interaction objects contained in inter_list in the stream fsock""" |
1359 | + |
1360 | + if not isinstance(inter_list, base_objects.InteractionList): |
1361 | + raise ValueError, \ |
1362 | + "Object %s is not a valid InteractionList" % repr(inter_list) |
1363 | + |
1364 | + fsock.write("interactions = [\n") |
1365 | + |
1366 | + for inter in inter_list: |
1367 | + if inter_list.index(inter) != len(inter_list) - 1: |
1368 | + fsock.write(str(inter) + ',') |
1369 | + else: |
1370 | + fsock.write(str(inter)) |
1371 | + |
1372 | + fsock.write("]") |
1373 | + |
1374 | +def save_model(path, model): |
1375 | + """Save a full model in directory path (try to create if necessary).""" |
1376 | + |
1377 | + if not isinstance(model, base_objects.Model): |
1378 | + raise ValueError, \ |
1379 | + "Object %s is not a valid Model" % repr(model) |
1380 | + |
1381 | + if not isinstance(path, str): |
1382 | + raise ValueError, \ |
1383 | + "Object %s is not a path string" % repr(path) |
1384 | + |
1385 | + if not os.path.isdir(path): |
1386 | + logging.warning("Path %s does not exist, try to make it..." % str(path)) |
1387 | + try: |
1388 | + os.mkdir(path) |
1389 | + except IOError, (errno, strerror): |
1390 | + logging.error("I/O error (%s): %s" % (errno, strerror)) |
1391 | + return None |
1392 | + |
1393 | + print "Saving particles...", |
1394 | + files.write_to_file(os.path.join(path, 'particles.py'), |
1395 | + save_particles, |
1396 | + model['particles']) |
1397 | + print "%i particles saved to %s" % (len(model['particles']), |
1398 | + os.path.join(path, 'particles.py')) |
1399 | + |
1400 | + print "Saving interactions...", |
1401 | + files.write_to_file(os.path.join(path, 'interactions.py'), |
1402 | + save_interactions, |
1403 | + model['interactions']) |
1404 | + print "%i interactions saved to %s" % (len(model['interactions']), |
1405 | + os.path.join(path, 'interactions.py')) |
1406 | |
1407 | === modified file 'tests/test_manager.py' |
1408 | --- tests/test_manager.py 2009-10-07 11:56:20 +0000 |
1409 | +++ tests/test_manager.py 2010-01-21 09:19:15 +0000 |
1410 | @@ -37,9 +37,13 @@ |
1411 | import unittest |
1412 | |
1413 | #Add the ROOT dir to the current PYTHONPATH |
1414 | + |
1415 | +# Only for profiling with -m cProfile! |
1416 | +#root_path = os.path.split(os.path.dirname(os.path.realpath(sys.argv[0])))[0] |
1417 | +#sys.path.append(root_path) |
1418 | + |
1419 | root_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] |
1420 | sys.path.append(root_path) |
1421 | - |
1422 | #=============================================================================== |
1423 | # run |
1424 | #=============================================================================== |
1425 | |
1426 | === modified file 'tests/unit_tests/core/test_base_objects.py' |
1427 | --- tests/unit_tests/core/test_base_objects.py 2010-01-01 07:58:00 +0000 |
1428 | +++ tests/unit_tests/core/test_base_objects.py 2010-01-21 09:19:15 +0000 |
1429 | @@ -19,6 +19,7 @@ |
1430 | import unittest |
1431 | |
1432 | import madgraph.core.base_objects as base_objects |
1433 | +import madgraph.core.color_algebra as color |
1434 | |
1435 | #=============================================================================== |
1436 | # ParticleTest |
1437 | @@ -261,7 +262,8 @@ |
1438 | |
1439 | self.mydict = {'id': 1, |
1440 | 'particles': base_objects.ParticleList([self.mypart] * 4), |
1441 | - 'color': ['C1', 'C2'], |
1442 | + 'color': [color.ColorString([color.f(1, 2, 3)]), |
1443 | + color.ColorString([color.d(1, 2, 3)])], |
1444 | 'lorentz':['L1', 'L2'], |
1445 | 'couplings':{(0, 0):'g00', |
1446 | (0, 1):'g01', |
1447 | @@ -329,7 +331,8 @@ |
1448 | base_objects.ParticleList([self.mypart] * 3)], |
1449 | 'wrong_list':[1, 'x ', [self.mypart, 1], [1, 2]]}, |
1450 | {'prop':'color', |
1451 | - 'right_list':[[], ['C1'], ['C1', 'C2']], |
1452 | + 'right_list':[[], [color.ColorString([color.f(1, 2, 3)]), |
1453 | + color.ColorString([color.f(1, 2, 3)])]], |
1454 | 'wrong_list':[1, 'a', ['a', 1]]}, |
1455 | {'prop':'lorentz', |
1456 | 'right_list':[[], ['L1'], ['L1', 'L2']], |
1457 | @@ -346,9 +349,7 @@ |
1458 | 'wrong_list':[{(0):'g00', (0, 1):'g01', |
1459 | (1, 0):'g10', (1, 2):'g11'}, |
1460 | {(0, 0):'g00', (0, 1):'g01', |
1461 | - (1, 0):'g10', (1, 2):'g11'}, |
1462 | - {(0, 0):'g00', (0, 1):'g01', |
1463 | - (1, 0):'g10'}]} |
1464 | + (1, 0):'g10', (1, 2):'g11'}]} |
1465 | ] |
1466 | |
1467 | mytestinter = self.myinter |
1468 | @@ -364,9 +365,9 @@ |
1469 | |
1470 | goal = "{\n" |
1471 | goal = goal + " \'id\': %d,\n" % self.myinter['id'] |
1472 | - goal = goal + " \'particles\': %s,\n" % \ |
1473 | - repr(base_objects.ParticleList([self.mypart] * 4)) |
1474 | - goal = goal + " \'color\': [\'C1\', \'C2\'],\n" |
1475 | + goal = goal + " \'particles\': [%s],\n" % \ |
1476 | + ','.join([str(self.mypart.get_pdg_code())]*4) |
1477 | + goal = goal + " \'color\': [1 f(1,2,3), 1 d(1,2,3)],\n" |
1478 | goal = goal + " \'lorentz\': [\'L1\', \'L2\'],\n" |
1479 | goal = goal + " \'couplings\': %s,\n" % \ |
1480 | repr(self.myinter['couplings']) |
1481 | @@ -539,7 +540,8 @@ |
1482 | [self.mypartlist[0], \ |
1483 | antit, \ |
1484 | self.mypartlist[1]]), |
1485 | - 'color': ['C1'], |
1486 | + 'color': [color.ColorString([color.f(1, 2, 3), |
1487 | + color.d(1, 2, 3)])], |
1488 | 'lorentz':['L1'], |
1489 | 'couplings':{(0, 0):'GQQ'}, |
1490 | 'orders':{'QCD':1}})) |
1491 | |
1492 | === added file 'tests/unit_tests/core/test_color_algebra.py' |
1493 | --- tests/unit_tests/core/test_color_algebra.py 1970-01-01 00:00:00 +0000 |
1494 | +++ tests/unit_tests/core/test_color_algebra.py 2010-01-21 09:19:15 +0000 |
1495 | @@ -0,0 +1,352 @@ |
1496 | +################################################################################ |
1497 | +# |
1498 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
1499 | +# |
1500 | +# This file is a part of the MadGraph 5 project, an application which |
1501 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
1502 | +# high-energy processes in the Standard Model and beyond. |
1503 | +# |
1504 | +# It is subject to the MadGraph license which should accompany this |
1505 | +# distribution. |
1506 | +# |
1507 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
1508 | +# |
1509 | +################################################################################ |
1510 | + |
1511 | +"""Unit test library for the color algebra related routines |
1512 | +in the core library""" |
1513 | + |
1514 | +import copy |
1515 | +import fractions |
1516 | +import unittest |
1517 | + |
1518 | +import madgraph.core.color_algebra as color |
1519 | +# |
1520 | +class ColorObjectTest(unittest.TestCase): |
1521 | + """Test class for the ColorObject objects""" |
1522 | + |
1523 | + def test_standard(self): |
1524 | + """Test the standard routines of ColorObject""" |
1525 | + |
1526 | + my_color_object = color.ColorObject(-1, 2, 3) |
1527 | + my_color_object.append(4) |
1528 | + |
1529 | + self.assertEqual('ColorObject(-1,2,3,4)', str(my_color_object)) |
1530 | + |
1531 | + |
1532 | + def test_Tr_simplify(self): |
1533 | + """Test simplification of trace objects""" |
1534 | + |
1535 | + # Test Tr(a)=0 |
1536 | + self.assertEqual(color.Tr(-1).simplify(), |
1537 | + color.ColorFactor([color.ColorString(coeff=0)])) |
1538 | + |
1539 | + # Test Tr()=Nc |
1540 | + col_str = color.ColorString() |
1541 | + col_str.Nc_power = 1 |
1542 | + self.assertEqual(color.Tr().simplify(), color.ColorFactor([col_str])) |
1543 | + |
1544 | + # Test cyclicity |
1545 | + col_str = color.ColorString([color.Tr(1, 2, 3, 4, 5)]) |
1546 | + self.assertEqual(color.Tr(3, 4, 5, 1, 2).simplify(), |
1547 | + color.ColorFactor([col_str])) |
1548 | + |
1549 | + # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c)) |
1550 | + col_str1 = color.ColorString([color.Tr(1, 2, 4), color.Tr(3)]) |
1551 | + col_str2 = color.ColorString([color.Tr(1, 2, 3, 4)]) |
1552 | + col_str1.coeff = fractions.Fraction(1, 2) |
1553 | + col_str2.coeff = fractions.Fraction(-1, 2) |
1554 | + col_str2.Nc_power = -1 |
1555 | + my_tr = color.Tr(1, 2, 100, 3, 100, 4) |
1556 | + self.assertEqual(my_tr.simplify(), |
1557 | + color.ColorFactor([col_str1, col_str2])) |
1558 | + |
1559 | + my_tr = color.Tr(1, 2, 100, 100, 4) |
1560 | + |
1561 | + col_str1 = color.ColorString([color.Tr(1, 2, 4), color.Tr()]) |
1562 | + col_str2 = color.ColorString([color.Tr(1, 2, 4)]) |
1563 | + self.assertEqual(my_tr.simplify(), |
1564 | + color.ColorFactor([col_str1, col_str2])) |
1565 | + |
1566 | + my_tr = color.Tr(100, 100) |
1567 | + col_str1 = color.ColorString([color.Tr(), color.Tr()]) |
1568 | + col_str2 = color.ColorString([color.Tr()]) |
1569 | + self.assertEqual(my_tr.simplify(), |
1570 | + color.ColorFactor([col_str1, col_str2])) |
1571 | + |
1572 | + def test_Tr_pair_simplify(self): |
1573 | + """Test Tr object product simplification""" |
1574 | + |
1575 | + my_Tr1 = color.Tr(1, 2, 3) |
1576 | + my_Tr2 = color.Tr(4, 2, 5) |
1577 | + my_T = color.T(4, 2, 5, 101, 102) |
1578 | + |
1579 | + col_str1 = color.ColorString([color.Tr(1, 5, 4, 3)]) |
1580 | + col_str2 = color.ColorString([color.Tr(1, 3), color.Tr(4, 5)]) |
1581 | + col_str1.coeff = fractions.Fraction(1, 2) |
1582 | + col_str2.coeff = fractions.Fraction(-1, 2) |
1583 | + col_str2.Nc_power = -1 |
1584 | + self.assertEqual(my_Tr1.pair_simplify(my_Tr2), |
1585 | + color.ColorFactor([col_str1, col_str2])) |
1586 | + |
1587 | + col_str1 = color.ColorString([color.T(4, 3, 1, 5, 101, 102)]) |
1588 | + col_str2 = color.ColorString([color.Tr(1, 3), color.T(4, 5, 101, 102)]) |
1589 | + self.assertEqual(my_Tr1.pair_simplify(my_T), |
1590 | + color.ColorFactor([col_str1, col_str2])) |
1591 | + |
1592 | + |
1593 | + def test_T_simplify(self): |
1594 | + """Test T simplify""" |
1595 | + |
1596 | + # T(a,b,c,...,i,i) = Tr(a,b,c,...) |
1597 | + self.assertEqual(color.T(1, 2, 3, 100, 100).simplify(), |
1598 | + color.ColorFactor([\ |
1599 | + color.ColorString([color.Tr(1, 2, 3)])])) |
1600 | + |
1601 | + # T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j)) |
1602 | + my_T = color.T(1, 2, 100, 3, 100, 4, 101, 102) |
1603 | + col_str1 = color.ColorString([color.T(1, 2, 4, 101, 102), color.Tr(3)]) |
1604 | + col_str2 = color.ColorString([color.T(1, 2, 3, 4, 101, 102)]) |
1605 | + col_str1.coeff = fractions.Fraction(1, 2) |
1606 | + col_str2.coeff = fractions.Fraction(-1, 2) |
1607 | + col_str2.Nc_power = -1 |
1608 | + self.assertEqual(my_T.simplify(), |
1609 | + color.ColorFactor([col_str1, col_str2])) |
1610 | + self.assertEqual(my_T.simplify(), |
1611 | + color.ColorFactor([col_str1, col_str2])) |
1612 | + def test_T_pair_simplify(self): |
1613 | + """Test T object products simplifications""" |
1614 | + |
1615 | + my_T1 = color.T(1, 2, 3, 101, 102) |
1616 | + my_T2 = color.T(4, 5, 102, 103) |
1617 | + self.assertEqual(my_T1.pair_simplify(my_T2), |
1618 | + color.ColorFactor([color.ColorString([\ |
1619 | + color.T(1, 2, 3, 4, 5, 101, 103)])])) |
1620 | + |
1621 | + my_T3 = color.T(4, 2, 5, 103, 104) |
1622 | + col_str1 = color.ColorString([color.T(1, 5, 101, 104), |
1623 | + color.T(4, 3, 103, 102)]) |
1624 | + col_str2 = color.ColorString([color.T(1, 3, 101, 102), |
1625 | + color.T(4, 5, 103, 104)]) |
1626 | + col_str1.coeff = fractions.Fraction(1, 2) |
1627 | + col_str2.coeff = fractions.Fraction(-1, 2) |
1628 | + col_str2.Nc_power = -1 |
1629 | + self.assertEqual(my_T1.pair_simplify(my_T3, simplify_T_product=True), |
1630 | + color.ColorFactor([col_str1, col_str2])) |
1631 | + |
1632 | + def test_f_object(self): |
1633 | + """Test the f color object""" |
1634 | + # T should have exactly 3 indices! |
1635 | + self.assertRaises(ValueError, |
1636 | + color.f, |
1637 | + 1, 2, 3, 4) |
1638 | + |
1639 | + # Simplify should always return the same ColorFactor |
1640 | + my_f = color.f(1, 2, 3) |
1641 | + col_str1 = color.ColorString([color.Tr(1, 2, 3)]) |
1642 | + col_str2 = color.ColorString([color.Tr(3, 2, 1)]) |
1643 | + col_str1.coeff = fractions.Fraction(-2, 1) |
1644 | + col_str2.coeff = fractions.Fraction(2, 1) |
1645 | + col_str1.is_imaginary = True |
1646 | + col_str2.is_imaginary = True |
1647 | + |
1648 | + self.assertEqual(my_f.simplify(), |
1649 | + color.ColorFactor([col_str1, col_str2])) |
1650 | + |
1651 | + def test_d_object(self): |
1652 | + """Test the d color object""" |
1653 | + # T should have exactly 3 indices! |
1654 | + self.assertRaises(ValueError, |
1655 | + color.d, |
1656 | + 1, 2) |
1657 | + |
1658 | + # Simplify should always return the same ColorFactor |
1659 | + my_d = color.d(1, 2, 3) |
1660 | + col_str1 = color.ColorString([color.Tr(1, 2, 3)]) |
1661 | + col_str2 = color.ColorString([color.Tr(3, 2, 1)]) |
1662 | + col_str1.coeff = fractions.Fraction(2, 1) |
1663 | + col_str2.coeff = fractions.Fraction(2, 1) |
1664 | + |
1665 | + self.assertEqual(my_d.simplify(), |
1666 | + color.ColorFactor([col_str1, col_str2])) |
1667 | + |
1668 | + |
1669 | +class ColorStringTest(unittest.TestCase): |
1670 | + """Test class for the ColorString objects""" |
1671 | + |
1672 | + my_col_string = color.ColorString() |
1673 | + |
1674 | + def setUp(self): |
1675 | + """Initialize the ColorString test""" |
1676 | + # Create a test color string |
1677 | + |
1678 | + test_f = color.f(1, 2, 3) |
1679 | + test_d = color.d(4, 5, 6) |
1680 | + |
1681 | + self.my_col_string = color.ColorString([test_f, test_d], |
1682 | + coeff=fractions.Fraction(2, 3), |
1683 | + Nc_power= -2, |
1684 | + is_imaginary=True) |
1685 | + |
1686 | + def test_representation(self): |
1687 | + """Test ColorString representation""" |
1688 | + |
1689 | + self.assertEqual(str(self.my_col_string), |
1690 | + "2/3 I 1/Nc^2 f(1,2,3) d(4,5,6)") |
1691 | + |
1692 | + def test_product(self): |
1693 | + """Test the product of two color strings""" |
1694 | + test = copy.copy(self.my_col_string) |
1695 | + test.product(self.my_col_string) |
1696 | + self.assertEqual(str(test), |
1697 | + "-4/9 1/Nc^4 f(1,2,3) d(4,5,6) f(1,2,3) d(4,5,6)") |
1698 | + |
1699 | + |
1700 | + def test_simplify(self): |
1701 | + """Test the simplification of a string""" |
1702 | + |
1703 | + # Simplification of one term |
1704 | + self.assertEqual(str(self.my_col_string.simplify()), |
1705 | + '(4/3 1/Nc^2 Tr(1,2,3) d(4,5,6))+(-4/3 1/Nc^2 Tr(3,2,1) d(4,5,6))') |
1706 | + |
1707 | + def test_complex_conjugate(self): |
1708 | + """Test the complex conjugation of a color string""" |
1709 | + |
1710 | + my_color_string = color.ColorString([color.T(3, 4, 102, 103), |
1711 | + color.Tr(1, 2, 3)]) |
1712 | + my_color_string.is_imaginary = True |
1713 | + |
1714 | + self.assertEqual(str(my_color_string.complex_conjugate()), |
1715 | + '-1 I T(4,3,103,102) Tr(3,2,1)') |
1716 | + |
1717 | + def test_to_immutable(self): |
1718 | + """Test the immutable representation of a color string structure""" |
1719 | + |
1720 | + self.assertEqual(self.my_col_string.to_immutable(), |
1721 | + (('d', (4, 5, 6)), ('f', (1, 2, 3)))) |
1722 | + |
1723 | + def test_from_immutable(self): |
1724 | + """Test the creation of a color string using its immutable rep""" |
1725 | + |
1726 | + test_str = copy.copy(self.my_col_string) |
1727 | + test_str.from_immutable((('f', (1, 2, 3)), ('d', (4, 5, 6)))) |
1728 | + |
1729 | + self.assertEqual(test_str, self.my_col_string) |
1730 | + |
1731 | + def test_replace_indices(self): |
1732 | + """Test indices replacement""" |
1733 | + |
1734 | + repl_dict = {1:2, 2:3, 3:1} |
1735 | + |
1736 | + my_color_string = color.ColorString([color.T(1, 2, 3, 4), |
1737 | + color.Tr(3, 2, 1)]) |
1738 | + |
1739 | + my_color_string.replace_indices(repl_dict) |
1740 | + self.assertEqual(str(my_color_string), |
1741 | + '1 T(2,3,1,4) Tr(1,3,2)') |
1742 | + |
1743 | + def test_color_string_canonical(self): |
1744 | + """Test the canonical representation of a immutable color string""" |
1745 | + |
1746 | + immutable1 = (('f', (2, 3, 4)), ('T', (4, 2, 5))) |
1747 | + immutable2 = (('T', (3, 5)),) |
1748 | + |
1749 | + self.assertEqual(color.ColorString().to_canonical(immutable1 + \ |
1750 | + immutable2)[0], |
1751 | + (('T', (2, 4)), ('T', (3, 1, 4)), ('f', (1, 2, 3)))) |
1752 | + |
1753 | +class ColorFactorTest(unittest.TestCase): |
1754 | + """Test class for the ColorFactor objects""" |
1755 | + |
1756 | + def test_f_d_sum(self): |
1757 | + """Test f and d sum with the right weights giving a Tr""" |
1758 | + |
1759 | + col_str1 = color.ColorString([color.d(1, 2, 3)]) |
1760 | + col_str1.coeff = fractions.Fraction(1, 4) |
1761 | + col_str2 = color.ColorString([color.f(1, 2, 3)]) |
1762 | + col_str2.coeff = fractions.Fraction(1, 4) |
1763 | + col_str2.is_imaginary = True |
1764 | + |
1765 | + my_color_factor = color.ColorFactor([col_str1, col_str2]) |
1766 | + |
1767 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1768 | + '(1 Tr(1,2,3))') |
1769 | + |
1770 | + def test_f_product(self): |
1771 | + """Test the fully contracted product of two f's""" |
1772 | + |
1773 | + my_color_factor = color.ColorFactor([\ |
1774 | + color.ColorString([color.f(1, 2, 3), color.f(1, 2, 3)])]) |
1775 | + |
1776 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1777 | + '(-1 Nc^1 )+(1 Nc^3 )') |
1778 | + |
1779 | + |
1780 | + def test_d_product(self): |
1781 | + """Test the fully contracted product of two d's""" |
1782 | + |
1783 | + my_color_factor = color.ColorFactor([\ |
1784 | + color.ColorString([color.d(1, 2, 3), color.d(1, 2, 3)])]) |
1785 | + |
1786 | + |
1787 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1788 | + '(-5 Nc^1 )+(4 1/Nc^1 )+(1 Nc^3 )') |
1789 | + |
1790 | + def test_f_d_product(self): |
1791 | + """Test the fully contracted product of f and d""" |
1792 | + |
1793 | + my_color_factor = color.ColorFactor([\ |
1794 | + color.ColorString([color.f(1, 2, 3), color.d(1, 2, 3)])]) |
1795 | + |
1796 | + |
1797 | + self.assertEqual(str(my_color_factor.full_simplify()), '') |
1798 | + |
1799 | + def test_three_f_chain(self): |
1800 | + """Test a chain of three f's""" |
1801 | + |
1802 | + my_color_factor = color.ColorFactor([\ |
1803 | + color.ColorString([color.f(1, 2, -1), |
1804 | + color.f(-1, 3, -2), |
1805 | + color.f(-2, 4, 5)])]) |
1806 | + |
1807 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1808 | + "(2 I Tr(1,2,3,4,5))+(-2 I Tr(1,2,4,5,3))+(-2 I Tr(1,2,3,5,4))" + \ |
1809 | + "+(2 I Tr(1,2,5,4,3))+(-2 I Tr(1,3,4,5,2))+(2 I Tr(1,4,5,3,2))" + \ |
1810 | + "+(2 I Tr(1,3,5,4,2))+(-2 I Tr(1,5,4,3,2))") |
1811 | + |
1812 | + def test_Tr_product(self): |
1813 | + """Test a non trivial product of two traces""" |
1814 | + |
1815 | + my_color_factor = color.ColorFactor([\ |
1816 | + color.ColorString([color.Tr(1, 2, 3, 4, 5, 6, 7), |
1817 | + color.Tr(1, 7, 6, 5, 4, 3, 2)])]) |
1818 | + |
1819 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1820 | + "(1/128 Nc^7 )+(-7/128 Nc^5 )+(21/128 Nc^3 )+(-35/128 Nc^1 )" + \ |
1821 | + "+(35/128 1/Nc^1 )+(-21/128 1/Nc^3 )+(3/64 1/Nc^5 )") |
1822 | + |
1823 | + def test_T_f_product(self): |
1824 | + """Test a non trivial T f f product""" |
1825 | + |
1826 | + my_color_factor = color.ColorFactor([\ |
1827 | + color.ColorString([color.T(-1000, 1, 2), |
1828 | + color.f(-1, -1000, 5), |
1829 | + color.f(-1, 4, 3)])]) |
1830 | + |
1831 | + self.assertEqual(str(my_color_factor.full_simplify()), |
1832 | + "(-1 T(5,4,3,1,2))+(1 T(5,3,4,1,2))+(1 T(4,3,5,1,2))+(-1 T(3,4,5,1,2))") |
1833 | + |
1834 | + |
1835 | + def test_gluons(self): |
1836 | + """Test simplification of chains of f""" |
1837 | + |
1838 | + my_col_fact = color.ColorFactor([color.ColorString([color.f(-3, 1, 2), |
1839 | + color.f(-1, 3, 4), |
1840 | + color.f(-1, 5, -3) |
1841 | + ])]) |
1842 | + |
1843 | + self.assertEqual(str(my_col_fact.full_simplify()), |
1844 | + '(2 I Tr(1,2,3,4,5))+(-2 I Tr(1,2,5,3,4))+(-2 I Tr(1,2,4,3,5))+' + \ |
1845 | + '(2 I Tr(1,2,5,4,3))+(-2 I Tr(1,3,4,5,2))+(2 I Tr(1,5,3,4,2))+' + \ |
1846 | + '(2 I Tr(1,4,3,5,2))+(-2 I Tr(1,5,4,3,2))') |
1847 | + |
1848 | |
1849 | === added file 'tests/unit_tests/core/test_color_amp.py' |
1850 | --- tests/unit_tests/core/test_color_amp.py 1970-01-01 00:00:00 +0000 |
1851 | +++ tests/unit_tests/core/test_color_amp.py 2010-01-21 09:19:15 +0000 |
1852 | @@ -0,0 +1,600 @@ |
1853 | +################################################################################ |
1854 | +# |
1855 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
1856 | +# |
1857 | +# This file is a part of the MadGraph 5 project, an application which |
1858 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
1859 | +# high-energy processes in the Standard Model and beyond. |
1860 | +# |
1861 | +# It is subject to the MadGraph license which should accompany this |
1862 | +# distribution. |
1863 | +# |
1864 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
1865 | +# |
1866 | +################################################################################ |
1867 | + |
1868 | +"""Unit test library for the routines of the core library related to writing |
1869 | +color information for diagrams.""" |
1870 | + |
1871 | +import copy |
1872 | +import fractions |
1873 | +import unittest |
1874 | + |
1875 | +import madgraph.core.base_objects as base_objects |
1876 | +import madgraph.core.diagram_generation as diagram_generation |
1877 | + |
1878 | +import madgraph.core.color_amp as color_amp |
1879 | +import madgraph.core.color_algebra as color |
1880 | + |
1881 | +class ColorAmpTest(unittest.TestCase): |
1882 | + """Test class for the color_amp module""" |
1883 | + |
1884 | + mypartlist = base_objects.ParticleList() |
1885 | + myinterlist = base_objects.InteractionList() |
1886 | + mymodel = base_objects.Model() |
1887 | + |
1888 | + def setUp(self): |
1889 | + # A gluon |
1890 | + self.mypartlist.append(base_objects.Particle({'name':'g', |
1891 | + 'antiname':'g', |
1892 | + 'spin':3, |
1893 | + 'color':8, |
1894 | + 'mass':'zero', |
1895 | + 'width':'zero', |
1896 | + 'texname':'g', |
1897 | + 'antitexname':'g', |
1898 | + 'line':'curly', |
1899 | + 'charge':0., |
1900 | + 'pdg_code':21, |
1901 | + 'propagating':True, |
1902 | + 'is_part':True, |
1903 | + 'self_antipart':True})) |
1904 | + |
1905 | + # A quark U and its antiparticle |
1906 | + self.mypartlist.append(base_objects.Particle({'name':'u', |
1907 | + 'antiname':'u~', |
1908 | + 'spin':2, |
1909 | + 'color':3, |
1910 | + 'mass':'zero', |
1911 | + 'width':'zero', |
1912 | + 'texname':'u', |
1913 | + 'antitexname':'\bar u', |
1914 | + 'line':'straight', |
1915 | + 'charge':2. / 3., |
1916 | + 'pdg_code':2, |
1917 | + 'propagating':True, |
1918 | + 'is_part':True, |
1919 | + 'self_antipart':False})) |
1920 | + antiu = copy.copy(self.mypartlist[1]) |
1921 | + antiu.set('is_part', False) |
1922 | + |
1923 | + # A quark D and its antiparticle |
1924 | + self.mypartlist.append(base_objects.Particle({'name':'d', |
1925 | + 'antiname':'d~', |
1926 | + 'spin':2, |
1927 | + 'color':3, |
1928 | + 'mass':'zero', |
1929 | + 'width':'zero', |
1930 | + 'texname':'u', |
1931 | + 'antitexname':'\bar u', |
1932 | + 'line':'straight', |
1933 | + 'charge':-1. / 3., |
1934 | + 'pdg_code':1, |
1935 | + 'propagating':True, |
1936 | + 'is_part':True, |
1937 | + 'self_antipart':False})) |
1938 | + antid = copy.copy(self.mypartlist[2]) |
1939 | + antid.set('is_part', False) |
1940 | + |
1941 | + # A photon |
1942 | + self.mypartlist.append(base_objects.Particle({'name':'a', |
1943 | + 'antiname':'a', |
1944 | + 'spin':3, |
1945 | + 'color':1, |
1946 | + 'mass':'zero', |
1947 | + 'width':'zero', |
1948 | + 'texname':'\gamma', |
1949 | + 'antitexname':'\gamma', |
1950 | + 'line':'wavy', |
1951 | + 'charge':0., |
1952 | + 'pdg_code':22, |
1953 | + 'propagating':True, |
1954 | + 'is_part':True, |
1955 | + 'self_antipart':True})) |
1956 | + |
1957 | + # A Higgs |
1958 | + self.mypartlist.append(base_objects.Particle({'name':'h', |
1959 | + 'antiname':'h', |
1960 | + 'spin':1, |
1961 | + 'color':1, |
1962 | + 'mass':'mh', |
1963 | + 'width':'wh', |
1964 | + 'texname':'h', |
1965 | + 'antitexname':'h', |
1966 | + 'line':'dashed', |
1967 | + 'charge':0., |
1968 | + 'pdg_code':25, |
1969 | + 'propagating':True, |
1970 | + 'is_part':True, |
1971 | + 'self_antipart':True})) |
1972 | + |
1973 | + # 3 gluon vertiex |
1974 | + self.myinterlist.append(base_objects.Interaction({ |
1975 | + 'id': 1, |
1976 | + 'particles': base_objects.ParticleList(\ |
1977 | + [self.mypartlist[0]] * 3), |
1978 | + 'color': [color.ColorString([color.f(0, 1, 2)])], |
1979 | + 'lorentz':['L1'], |
1980 | + 'couplings':{(0, 0):'G'}, |
1981 | + 'orders':{'QCD':1}})) |
1982 | + |
1983 | + # 4 gluon vertex |
1984 | + self.myinterlist.append(base_objects.Interaction({ |
1985 | + 'id': 2, |
1986 | + 'particles': base_objects.ParticleList(\ |
1987 | + [self.mypartlist[0]] * 4), |
1988 | + 'color': [color.ColorString([color.f(-1, 0, 2), |
1989 | + color.f(-1, 1, 3)]), |
1990 | + color.ColorString([color.f(-1, 0, 3), |
1991 | + color.f(-1, 1, 2)]), |
1992 | + color.ColorString([color.f(-1, 0, 1), |
1993 | + color.f(-1, 2, 3)])], |
1994 | + 'lorentz':['L(p1,p2,p3)', 'L(p2,p3,p1)', 'L3'], |
1995 | + 'couplings':{(0, 0):'G^2', |
1996 | + (1, 1):'G^2', |
1997 | + (2, 2):'G^2'}, |
1998 | + 'orders':{'QCD':2}})) |
1999 | + |
2000 | + # Gluon couplings to up and down quarks |
2001 | + self.myinterlist.append(base_objects.Interaction({ |
2002 | + 'id': 3, |
2003 | + 'particles': base_objects.ParticleList(\ |
2004 | + [self.mypartlist[1], \ |
2005 | + antiu, \ |
2006 | + self.mypartlist[0]]), |
2007 | + 'color': [color.ColorString([color.T(2, 0, 1)])], |
2008 | + 'lorentz':['L1'], |
2009 | + 'couplings':{(0, 0):'GQQ'}, |
2010 | + 'orders':{'QCD':1}})) |
2011 | + |
2012 | + self.myinterlist.append(base_objects.Interaction({ |
2013 | + 'id': 4, |
2014 | + 'particles': base_objects.ParticleList(\ |
2015 | + [self.mypartlist[2], \ |
2016 | + antid, \ |
2017 | + self.mypartlist[0]]), |
2018 | + 'color': [color.ColorString([color.T(2, 0, 1)])], |
2019 | + 'lorentz':['L1'], |
2020 | + 'couplings':{(0, 0):'GQQ'}, |
2021 | + 'orders':{'QCD':1}})) |
2022 | + |
2023 | + # Photon coupling to up |
2024 | + self.myinterlist.append(base_objects.Interaction({ |
2025 | + 'id': 5, |
2026 | + 'particles': base_objects.ParticleList(\ |
2027 | + [self.mypartlist[1], \ |
2028 | + antiu, \ |
2029 | + self.mypartlist[3]]), |
2030 | + 'color': [color.ColorString([color.T(0, 1)])], |
2031 | + 'lorentz':['L1'], |
2032 | + 'couplings':{(0, 0):'GQED'}, |
2033 | + 'orders':{'QED':1}})) |
2034 | + |
2035 | + self.mymodel.set('particles', self.mypartlist) |
2036 | + self.mymodel.set('interactions', self.myinterlist) |
2037 | + |
2038 | + def test_colorize_uu_gg(self): |
2039 | + """Test the colorize function for uu~ > gg""" |
2040 | + |
2041 | + myleglist = base_objects.LegList() |
2042 | + |
2043 | + myleglist.append(base_objects.Leg({'id':-2, |
2044 | + 'state':'initial'})) |
2045 | + myleglist.append(base_objects.Leg({'id':2, |
2046 | + 'state':'initial'})) |
2047 | + |
2048 | + myleglist.extend([base_objects.Leg({'id':21, |
2049 | + 'state':'final'})] * 2) |
2050 | + |
2051 | + myprocess = base_objects.Process({'legs':myleglist, |
2052 | + 'model':self.mymodel}) |
2053 | + |
2054 | + myamplitude = diagram_generation.Amplitude() |
2055 | + |
2056 | + myamplitude.set('process', myprocess) |
2057 | + |
2058 | + myamplitude.generate_diagrams() |
2059 | + |
2060 | + my_col_basis = color_amp.ColorBasis() |
2061 | + |
2062 | + # S channel |
2063 | + col_dict = my_col_basis.colorize(myamplitude['diagrams'][0], |
2064 | + self.mymodel) |
2065 | + |
2066 | + goal_dict = {(0, 0):color.ColorString([color.T(-1000, 1, 2), |
2067 | + color.f(-1000, 4, 3)])} |
2068 | + |
2069 | + self.assertEqual(col_dict, goal_dict) |
2070 | + |
2071 | + # T channel |
2072 | + col_dict = my_col_basis.colorize(myamplitude['diagrams'][1], |
2073 | + self.mymodel) |
2074 | + |
2075 | + goal_dict = {(0, 0):color.ColorString([color.T(3, 1, -1000), |
2076 | + color.T(4, -1000, 2)])} |
2077 | + |
2078 | + self.assertEqual(col_dict, goal_dict) |
2079 | + |
2080 | + # U channel |
2081 | + col_dict = my_col_basis.colorize(myamplitude['diagrams'][2], |
2082 | + self.mymodel) |
2083 | + |
2084 | + goal_dict = {(0, 0):color.ColorString([color.T(4, 1, -1000), |
2085 | + color.T(3, -1000, 2)])} |
2086 | + |
2087 | + self.assertEqual(col_dict, goal_dict) |
2088 | + |
2089 | + def test_colorize_uux_ggg(self): |
2090 | + """Test the colorize function for uu~ > ggg""" |
2091 | + |
2092 | + myleglist = base_objects.LegList() |
2093 | + |
2094 | + myleglist.append(base_objects.Leg({'id':-2, |
2095 | + 'state':'initial'})) |
2096 | + myleglist.append(base_objects.Leg({'id':2, |
2097 | + 'state':'initial'})) |
2098 | + |
2099 | + myleglist.extend([base_objects.Leg({'id':21, |
2100 | + 'state':'final'})] * 3) |
2101 | + |
2102 | + myprocess = base_objects.Process({'legs':myleglist, |
2103 | + 'model':self.mymodel}) |
2104 | + |
2105 | + myamplitude = diagram_generation.Amplitude() |
2106 | + |
2107 | + myamplitude.set('process', myprocess) |
2108 | + |
2109 | + myamplitude.generate_diagrams() |
2110 | + |
2111 | + my_col_basis = color_amp.ColorBasis() |
2112 | + |
2113 | + # First diagram with two 3-gluon vertices |
2114 | + col_dict = my_col_basis.colorize(myamplitude['diagrams'][0], |
2115 | + self.mymodel) |
2116 | + |
2117 | + goal_dict = {(0, 0, 0):color.ColorString([color.T(-1000, 1, 2), |
2118 | + color.f(-1001, 4, 3), |
2119 | + color.f(5, -1001, -1000)])} |
2120 | + |
2121 | + self.assertEqual(col_dict, goal_dict) |
2122 | + |
2123 | + # Diagram with one 4-gluon vertex |
2124 | + col_dict = my_col_basis.colorize(myamplitude['diagrams'][3], |
2125 | + self.mymodel) |
2126 | + |
2127 | + goal_dict = {(0, 0):color.ColorString([color.T(-1000, 1, 2), |
2128 | + color.f(-1002, -1000, 4), |
2129 | + color.f(-1002, 5, 3)]), |
2130 | + (0, 1):color.ColorString([color.T(-1000, 1, 2), |
2131 | + color.f(-1003, -1000, 3), |
2132 | + color.f(-1003, 5, 4)]), |
2133 | + (0, 2):color.ColorString([color.T(-1000, 1, 2), |
2134 | + color.f(-1004, -1000, 5), |
2135 | + color.f(-1004, 4, 3)])} |
2136 | + |
2137 | + self.assertEqual(col_dict, goal_dict) |
2138 | + |
2139 | + def test_color_basis_uux_aggg(self): |
2140 | + """Test the color basis building for uu~ > aggg (3! elements)""" |
2141 | + |
2142 | + myleglist = base_objects.LegList() |
2143 | + |
2144 | + myleglist.append(base_objects.Leg({'id':2, |
2145 | + 'state':'initial'})) |
2146 | + myleglist.append(base_objects.Leg({'id':-2, |
2147 | + 'state':'initial'})) |
2148 | + |
2149 | + myleglist.append(base_objects.Leg({'id':22, |
2150 | + 'state':'final'})) |
2151 | + myleglist.extend([base_objects.Leg({'id':21, |
2152 | + 'state':'final'})] * 3) |
2153 | + |
2154 | + myprocess = base_objects.Process({'legs':myleglist, |
2155 | + 'model':self.mymodel}) |
2156 | + |
2157 | + myamplitude = diagram_generation.Amplitude() |
2158 | + |
2159 | + myamplitude.set('process', myprocess) |
2160 | + |
2161 | + myamplitude.generate_diagrams() |
2162 | + |
2163 | + new_col_basis = color_amp.ColorBasis(myamplitude, self.mymodel) |
2164 | + |
2165 | + self.assertEqual(len(new_col_basis), 6) |
2166 | + |
2167 | +class ColorSquareTest(unittest.TestCase): |
2168 | + """Test class for the color_amp module""" |
2169 | + |
2170 | + mypartlist = base_objects.ParticleList() |
2171 | + myinterlist = base_objects.InteractionList() |
2172 | + mymodel = base_objects.Model() |
2173 | + |
2174 | + def setUp(self): |
2175 | + # A gluon |
2176 | + self.mypartlist.append(base_objects.Particle({'name':'g', |
2177 | + 'antiname':'g', |
2178 | + 'spin':3, |
2179 | + 'color':8, |
2180 | + 'mass':'zero', |
2181 | + 'width':'zero', |
2182 | + 'texname':'g', |
2183 | + 'antitexname':'g', |
2184 | + 'line':'curly', |
2185 | + 'charge':0., |
2186 | + 'pdg_code':21, |
2187 | + 'propagating':True, |
2188 | + 'is_part':True, |
2189 | + 'self_antipart':True})) |
2190 | + |
2191 | + # A quark U and its antiparticle |
2192 | + self.mypartlist.append(base_objects.Particle({'name':'u', |
2193 | + 'antiname':'u~', |
2194 | + 'spin':2, |
2195 | + 'color':3, |
2196 | + 'mass':'zero', |
2197 | + 'width':'zero', |
2198 | + 'texname':'u', |
2199 | + 'antitexname':'\bar u', |
2200 | + 'line':'straight', |
2201 | + 'charge':2. / 3., |
2202 | + 'pdg_code':2, |
2203 | + 'propagating':True, |
2204 | + 'is_part':True, |
2205 | + 'self_antipart':False})) |
2206 | + antiu = copy.copy(self.mypartlist[1]) |
2207 | + antiu.set('is_part', False) |
2208 | + |
2209 | + # A quark D and its antiparticle |
2210 | + self.mypartlist.append(base_objects.Particle({'name':'d', |
2211 | + 'antiname':'d~', |
2212 | + 'spin':2, |
2213 | + 'color':3, |
2214 | + 'mass':'zero', |
2215 | + 'width':'zero', |
2216 | + 'texname':'u', |
2217 | + 'antitexname':'\bar u', |
2218 | + 'line':'straight', |
2219 | + 'charge':-1. / 3., |
2220 | + 'pdg_code':1, |
2221 | + 'propagating':True, |
2222 | + 'is_part':True, |
2223 | + 'self_antipart':False})) |
2224 | + antid = copy.copy(self.mypartlist[2]) |
2225 | + antid.set('is_part', False) |
2226 | + |
2227 | + # 3 gluon vertiex |
2228 | + self.myinterlist.append(base_objects.Interaction({ |
2229 | + 'id': 1, |
2230 | + 'particles': base_objects.ParticleList(\ |
2231 | + [self.mypartlist[0]] * 3), |
2232 | + 'color': [color.ColorString([color.f(0, 1, 2)])], |
2233 | + 'lorentz':['L1'], |
2234 | + 'couplings':{(0, 0):'G'}, |
2235 | + 'orders':{'QCD':1}})) |
2236 | + |
2237 | + # 4 gluon vertex |
2238 | + self.myinterlist.append(base_objects.Interaction({ |
2239 | + 'id': 2, |
2240 | + 'particles': base_objects.ParticleList(\ |
2241 | + [self.mypartlist[0]] * 4), |
2242 | + 'color': [color.ColorString([color.f(-1, 0, 2), |
2243 | + color.f(-1, 1, 3)]), |
2244 | + color.ColorString([color.f(-1, 0, 3), |
2245 | + color.f(-1, 1, 2)]), |
2246 | + color.ColorString([color.f(-1, 0, 1), |
2247 | + color.f(-1, 2, 3)])], |
2248 | + 'lorentz':['L(p1,p2,p3)', 'L(p2,p3,p1)', 'L3'], |
2249 | + 'couplings':{(0, 0):'G^2', |
2250 | + (1, 1):'G^2', |
2251 | + (2, 2):'G^2'}, |
2252 | + 'orders':{'QCD':2}})) |
2253 | + |
2254 | + # Gluon couplings to up and down quarks |
2255 | + self.myinterlist.append(base_objects.Interaction({ |
2256 | + 'id': 3, |
2257 | + 'particles': base_objects.ParticleList(\ |
2258 | + [self.mypartlist[1], \ |
2259 | + antiu, \ |
2260 | + self.mypartlist[0]]), |
2261 | + 'color': [color.ColorString([color.T(2, 0, 1)])], |
2262 | + 'lorentz':['L1'], |
2263 | + 'couplings':{(0, 0):'GQQ'}, |
2264 | + 'orders':{'QCD':1}})) |
2265 | + |
2266 | + self.myinterlist.append(base_objects.Interaction({ |
2267 | + 'id': 4, |
2268 | + 'particles': base_objects.ParticleList(\ |
2269 | + [self.mypartlist[2], \ |
2270 | + antid, \ |
2271 | + self.mypartlist[0]]), |
2272 | + 'color': [color.ColorString([color.T(2, 0, 1)])], |
2273 | + 'lorentz':['L1'], |
2274 | + 'couplings':{(0, 0):'GQQ'}, |
2275 | + 'orders':{'QCD':1}})) |
2276 | + |
2277 | + |
2278 | + self.mymodel.set('particles', self.mypartlist) |
2279 | + self.mymodel.set('interactions', self.myinterlist) |
2280 | + |
2281 | + def test_color_matrix_multi_gluons(self): |
2282 | + """Test the color matrix building for gg > n*g with n up to 3""" |
2283 | + |
2284 | + goal = [fractions.Fraction(7, 3), |
2285 | + fractions.Fraction(19, 6), |
2286 | + fractions.Fraction(455, 108), |
2287 | + fractions.Fraction(3641, 648)] |
2288 | + |
2289 | + goal_line1 = [(fractions.Fraction(7, 3), fractions.Fraction(-2, 3)), |
2290 | + (fractions.Fraction(19, 6), fractions.Fraction(-1, 3), |
2291 | + fractions.Fraction(-1, 3), fractions.Fraction(-1, 3), |
2292 | + fractions.Fraction(2, 3), fractions.Fraction(-1, 3)), |
2293 | + (fractions.Fraction(455, 108), fractions.Fraction(-29, 54), |
2294 | + fractions.Fraction(17, 27), fractions.Fraction(7, 54), |
2295 | + fractions.Fraction(-1, 27), fractions.Fraction(17, 27), |
2296 | + fractions.Fraction(5, 108), fractions.Fraction(7, 54), |
2297 | + fractions.Fraction(7, 54), fractions.Fraction(-1, 27), |
2298 | + fractions.Fraction(7, 54), fractions.Fraction(5, 108), |
2299 | + fractions.Fraction(-10, 27), fractions.Fraction(-29, 54), |
2300 | + fractions.Fraction(-29, 54), fractions.Fraction(-29, 54), |
2301 | + fractions.Fraction(-29, 54), fractions.Fraction(7, 54), |
2302 | + fractions.Fraction(17, 27), fractions.Fraction(-1, 27), |
2303 | + fractions.Fraction(-1, 27), fractions.Fraction(17, 27), |
2304 | + fractions.Fraction(-1, 27), fractions.Fraction(17, 27))] |
2305 | + |
2306 | + for n in range(3): |
2307 | + myleglist = base_objects.LegList() |
2308 | + |
2309 | + myleglist.append(base_objects.Leg({'id':21, |
2310 | + 'state':'initial'})) |
2311 | + myleglist.append(base_objects.Leg({'id':21, |
2312 | + 'state':'initial'})) |
2313 | + |
2314 | + myleglist.extend([base_objects.Leg({'id':21, |
2315 | + 'state':'final'})] * (n + 1)) |
2316 | + |
2317 | + myprocess = base_objects.Process({'legs':myleglist, |
2318 | + 'model':self.mymodel}) |
2319 | + |
2320 | + myamplitude = diagram_generation.Amplitude() |
2321 | + |
2322 | + myamplitude.set('process', myprocess) |
2323 | + |
2324 | + myamplitude.generate_diagrams() |
2325 | + |
2326 | + col_basis = color_amp.ColorBasis(myamplitude, self.mymodel) |
2327 | + |
2328 | + col_matrix = color_amp.ColorMatrix(col_basis, Nc=3) |
2329 | + |
2330 | + # Check diagonal |
2331 | + for i in range(len(col_basis.items())): |
2332 | + self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)], |
2333 | + (goal[n], 0)) |
2334 | + |
2335 | + # Check first line |
2336 | + for i in range(len(col_basis.items())): |
2337 | + self.assertEqual(col_matrix.col_matrix_fixed_Nc[(0, i)], |
2338 | + (goal_line1[n][i], 0)) |
2339 | + |
2340 | + def test_color_matrix_multi_quarks(self): |
2341 | + """Test the color matrix building for qq~ > n*(qq~) with n up to 2""" |
2342 | + |
2343 | + goal = [fractions.Fraction(2, 1), |
2344 | + fractions.Fraction(72, 54)] |
2345 | + |
2346 | + |
2347 | + |
2348 | + goal_line1 = [(fractions.Fraction(2, 1), fractions.Fraction(-2, 3)), |
2349 | + (fractions.Fraction(4, 3), fractions.Fraction(-5, 27), |
2350 | + fractions.Fraction(7, 6), fractions.Fraction(-1, 3), |
2351 | + fractions.Fraction(1, 9), fractions.Fraction(-7, 18), |
2352 | + fractions.Fraction(-5, 27), fractions.Fraction(-7, 18), |
2353 | + fractions.Fraction(1, 18), fractions.Fraction(31, 27), |
2354 | + fractions.Fraction(-5, 27), fractions.Fraction(-7, 18), |
2355 | + fractions.Fraction(-1, 3), fractions.Fraction(31, 27), |
2356 | + fractions.Fraction(-1, 3), fractions.Fraction(-4, 9), |
2357 | + fractions.Fraction(1, 9), fractions.Fraction(7, 6), |
2358 | + fractions.Fraction(-1, 6), fractions.Fraction(-5, 27), |
2359 | + fractions.Fraction(-1, 54), fractions.Fraction(1, 9), |
2360 | + fractions.Fraction(1, 18), fractions.Fraction(1, 18), |
2361 | + fractions.Fraction(4, 27), fractions.Fraction(-1, 54), |
2362 | + fractions.Fraction(5, 9), fractions.Fraction(1, 9), |
2363 | + fractions.Fraction(-4, 9), fractions.Fraction(-1, 54), |
2364 | + fractions.Fraction(71, 54), fractions.Fraction(1, 18), |
2365 | + fractions.Fraction(-7, 18), fractions.Fraction(-1, 3), |
2366 | + fractions.Fraction(-1, 54), fractions.Fraction(5, 9)) |
2367 | + ] |
2368 | + |
2369 | + for n in range(2): |
2370 | + myleglist = base_objects.LegList() |
2371 | + |
2372 | + myleglist.append(base_objects.Leg({'id':2, |
2373 | + 'state':'initial'})) |
2374 | + myleglist.append(base_objects.Leg({'id':-2, |
2375 | + 'state':'initial'})) |
2376 | + |
2377 | + myleglist.extend([base_objects.Leg({'id':2, |
2378 | + 'state':'final'}), |
2379 | + base_objects.Leg({'id':-2, |
2380 | + 'state':'final'})] * (n + 1)) |
2381 | + |
2382 | + myprocess = base_objects.Process({'legs':myleglist, |
2383 | + 'model':self.mymodel}) |
2384 | + |
2385 | + myamplitude = diagram_generation.Amplitude() |
2386 | + |
2387 | + myamplitude.set('process', myprocess) |
2388 | + |
2389 | + myamplitude.generate_diagrams() |
2390 | + |
2391 | + col_basis = color_amp.ColorBasis(myamplitude, self.mymodel) |
2392 | + |
2393 | + col_matrix = color_amp.ColorMatrix(col_basis, Nc=3) |
2394 | + |
2395 | + # Check diagonal |
2396 | + for i in range(len(col_basis.items())): |
2397 | + self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)], |
2398 | + (goal[n], 0)) |
2399 | + |
2400 | + # Check first line |
2401 | + for i in range(len(col_basis.items())): |
2402 | + self.assertEqual(col_matrix.col_matrix_fixed_Nc[(0, i)], |
2403 | + (goal_line1[n][i], 0)) |
2404 | + |
2405 | + def test_color_matrix_Nc_restrictions(self): |
2406 | + """Test the Nc power restriction during color basis building """ |
2407 | + |
2408 | + goal = [fractions.Fraction(3, 8), |
2409 | + fractions.Fraction(-9, 4), |
2410 | + fractions.Fraction(45, 16)] |
2411 | + |
2412 | + for n in range(3): |
2413 | + myleglist = base_objects.LegList() |
2414 | + |
2415 | + myleglist.append(base_objects.Leg({'id':21, |
2416 | + 'state':'initial'})) |
2417 | + myleglist.append(base_objects.Leg({'id':21, |
2418 | + 'state':'initial'})) |
2419 | + |
2420 | + myleglist.extend([base_objects.Leg({'id':21, |
2421 | + 'state':'final'})] * 2) |
2422 | + |
2423 | + myprocess = base_objects.Process({'legs':myleglist, |
2424 | + 'model':self.mymodel}) |
2425 | + |
2426 | + myamplitude = diagram_generation.Amplitude() |
2427 | + |
2428 | + myamplitude.set('process', myprocess) |
2429 | + |
2430 | + myamplitude.generate_diagrams() |
2431 | + |
2432 | + col_basis = color_amp.ColorBasis(myamplitude, self.mymodel) |
2433 | + |
2434 | + col_matrix = color_amp.ColorMatrix(col_basis, Nc=3, |
2435 | + Nc_power_min=n, |
2436 | + Nc_power_max=2 * n) |
2437 | + |
2438 | + for i in range(len(col_basis.items())): |
2439 | + self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)], |
2440 | + (goal[n], 0)) |
2441 | + |
2442 | + |
2443 | + def test_color_matrix_fixed_indices(self): |
2444 | + """Test index fixing for immutable color string""" |
2445 | + |
2446 | + immutable1 = (('f', (1, -1, -3)), ('T', (-1, -3, 4))) |
2447 | + immutable2 = (('d', (1, -2, -1)), ('T', (-1, -2, 4))) |
2448 | + |
2449 | + self.assertEqual(color_amp.ColorMatrix.fix_summed_indices(immutable1, |
2450 | + immutable2), |
2451 | + (('d', (1, -2, -5)), ('T', (-5, -2, 4)))) |
2452 | + |
2453 | |
2454 | === modified file 'tests/unit_tests/core/test_diagram_generation.py' |
2455 | --- tests/unit_tests/core/test_diagram_generation.py 2010-01-04 06:44:15 +0000 |
2456 | +++ tests/unit_tests/core/test_diagram_generation.py 2010-01-21 09:19:15 +0000 |
2457 | @@ -231,7 +231,7 @@ |
2458 | 'id': 1, |
2459 | 'particles': base_objects.ParticleList(\ |
2460 | [self.mypartlist[0]] * 3), |
2461 | - 'color': ['C1'], |
2462 | + 'color': [['C1']], |
2463 | 'lorentz':['L1'], |
2464 | 'couplings':{(0, 0):'G'}, |
2465 | 'orders':{'QCD':1}})) |
2466 | @@ -241,7 +241,7 @@ |
2467 | 'id': 2, |
2468 | 'particles': base_objects.ParticleList(\ |
2469 | [self.mypartlist[0]] * 4), |
2470 | - 'color': ['C1'], |
2471 | + 'color': [['C1']], |
2472 | 'lorentz':['L1'], |
2473 | 'couplings':{(0, 0):'G^2'}, |
2474 | 'orders':{'QCD':2}})) |
2475 | @@ -253,7 +253,7 @@ |
2476 | [self.mypartlist[1], \ |
2477 | antiu, \ |
2478 | self.mypartlist[0]]), |
2479 | - 'color': ['C1'], |
2480 | + 'color': [['C1']], |
2481 | 'lorentz':['L1'], |
2482 | 'couplings':{(0, 0):'GQQ'}, |
2483 | 'orders':{'QCD':1}})) |
2484 | @@ -264,7 +264,7 @@ |
2485 | [self.mypartlist[1], \ |
2486 | antiu, \ |
2487 | self.mypartlist[3]]), |
2488 | - 'color': ['C1'], |
2489 | + 'color': [['C1']], |
2490 | 'lorentz':['L1'], |
2491 | 'couplings':{(0, 0):'GQED'}, |
2492 | 'orders':{'QED':1}})) |
2493 | @@ -275,7 +275,7 @@ |
2494 | [self.mypartlist[2], \ |
2495 | antid, \ |
2496 | self.mypartlist[0]]), |
2497 | - 'color': ['C1'], |
2498 | + 'color': [['C1']], |
2499 | 'lorentz':['L1'], |
2500 | 'couplings':{(0, 0):'GQQ'}, |
2501 | 'orders':{'QCD':1}})) |
2502 | @@ -286,7 +286,7 @@ |
2503 | [self.mypartlist[2], \ |
2504 | antid, \ |
2505 | self.mypartlist[3]]), |
2506 | - 'color': ['C1'], |
2507 | + 'color': [['C1']], |
2508 | 'lorentz':['L1'], |
2509 | 'couplings':{(0, 0):'GQED'}, |
2510 | 'orders':{'QED':1}})) |
2511 | @@ -299,7 +299,7 @@ |
2512 | [self.mypartlist[4], \ |
2513 | antie, \ |
2514 | self.mypartlist[3]]), |
2515 | - 'color': ['C1'], |
2516 | + 'color': [['C1']], |
2517 | 'lorentz':['L1'], |
2518 | 'couplings':{(0, 0):'GQED'}, |
2519 | 'orders':{'QED':1}})) |
2520 | @@ -1019,7 +1019,7 @@ |
2521 | [antid, \ |
2522 | u, \ |
2523 | wminus]), |
2524 | - 'color': ['C1'], |
2525 | + 'color': [['C1']], |
2526 | 'lorentz':['L1'], |
2527 | 'couplings':{(0, 0):'GQED'}, |
2528 | 'orders':{'QED':1}})) |
2529 | @@ -1032,7 +1032,7 @@ |
2530 | [antiu, \ |
2531 | d, \ |
2532 | wplus]), |
2533 | - 'color': ['C1'], |
2534 | + 'color': [['C1']], |
2535 | 'lorentz':['L1'], |
2536 | 'couplings':{(0, 0):'GQED'}, |
2537 | 'orders':{'QED':1}})) |
2538 | @@ -1045,7 +1045,7 @@ |
2539 | [nuebar, \ |
2540 | eminus, \ |
2541 | wplus]), |
2542 | - 'color': ['C1'], |
2543 | + 'color': [['C1']], |
2544 | 'lorentz':['L1'], |
2545 | 'couplings':{(0, 0):'GQED'}, |
2546 | 'orders':{'QED':1}})) |
2547 | |
2548 | === renamed file 'tests/unit_tests/iolibs/test_import_v4.py' => 'tests/unit_tests/iolibs/test_import_model_v4.py' |
2549 | --- tests/unit_tests/iolibs/test_import_v4.py 2009-12-12 08:42:16 +0000 |
2550 | +++ tests/unit_tests/iolibs/test_import_model_v4.py 2010-01-21 09:19:15 +0000 |
2551 | @@ -19,7 +19,7 @@ |
2552 | import unittest |
2553 | import copy |
2554 | |
2555 | -import madgraph.iolibs.import_v4 as import_v4 |
2556 | +import madgraph.iolibs.import_model_v4 as import_v4 |
2557 | import madgraph.core.base_objects as base_objects |
2558 | |
2559 | #=============================================================================== |
2560 | @@ -28,12 +28,6 @@ |
2561 | class IOImportV4Test(unittest.TestCase): |
2562 | """Test class for the import v4 module""" |
2563 | |
2564 | - def setUp(self): |
2565 | - pass |
2566 | - |
2567 | - def tearDown(self): |
2568 | - pass |
2569 | - |
2570 | def test_read_particles(self): |
2571 | """Test the output of import particles.dat file""" |
2572 | |
2573 | @@ -136,7 +130,7 @@ |
2574 | wmin = copy.copy(myparts[14]) |
2575 | wmin.set('is_part', False) |
2576 | eplus = copy.copy(myparts[3]) |
2577 | - eplus.set('is_part',False) |
2578 | + eplus.set('is_part', False) |
2579 | enu = copy.copy(myparts[0]) |
2580 | photon = copy.copy(myparts[12]) |
2581 | gluon = copy.copy(myparts[15]) |
2582 | @@ -149,8 +143,13 @@ |
2583 | wplus, |
2584 | wmin, |
2585 | photon]), |
2586 | +<<<<<<< TREE |
2587 | 'color':['guess'], |
2588 | 'lorentz':[''], |
2589 | +======= |
2590 | + 'color':[['C1']], |
2591 | + 'lorentz':['L1'], |
2592 | +>>>>>>> MERGE-SOURCE |
2593 | 'couplings':{(0, 0):'MGVX3'}, |
2594 | 'orders':{'QED':1}}), |
2595 | base_objects.Interaction( |
2596 | @@ -159,8 +158,13 @@ |
2597 | gluon, |
2598 | gluon, |
2599 | t1]), |
2600 | +<<<<<<< TREE |
2601 | 'color':['guess'], |
2602 | 'lorentz':['A'], |
2603 | +======= |
2604 | + 'color':[['C1']], |
2605 | + 'lorentz':['L1'], |
2606 | +>>>>>>> MERGE-SOURCE |
2607 | 'couplings':{(0, 0):'MGVX2'}, |
2608 | 'orders':{'QCD':1}}), |
2609 | base_objects.Interaction( |
2610 | @@ -170,10 +174,16 @@ |
2611 | wmin, |
2612 | wplus, |
2613 | wmin]), |
2614 | +<<<<<<< TREE |
2615 | 'color':['guess'], |
2616 | 'lorentz':['WWWWN', ''], |
2617 | 'couplings':{(0, 0):'MGVX6', |
2618 | (0, 1):'DUM0'}, |
2619 | +======= |
2620 | + 'color':[['C1']], |
2621 | + 'lorentz':['L1'], |
2622 | + 'couplings':{(0, 0):'MGVX6'}, |
2623 | +>>>>>>> MERGE-SOURCE |
2624 | 'orders':{'QED':2}}), |
2625 | |
2626 | base_objects.Interaction( |
2627 | @@ -182,8 +192,13 @@ |
2628 | eplus, |
2629 | enu, |
2630 | wmin]), |
2631 | +<<<<<<< TREE |
2632 | 'color':['guess'], |
2633 | 'lorentz':[''], |
2634 | +======= |
2635 | + 'color':[['C1']], |
2636 | + 'lorentz':['L1'], |
2637 | +>>>>>>> MERGE-SOURCE |
2638 | 'couplings':{(0, 0):'MGVX24'}, |
2639 | 'orders':{'QED':1}})]) |
2640 | |
2641 | |
2642 | === added file 'tests/unit_tests/iolibs/test_save_model.py' |
2643 | --- tests/unit_tests/iolibs/test_save_model.py 1970-01-01 00:00:00 +0000 |
2644 | +++ tests/unit_tests/iolibs/test_save_model.py 2010-01-21 09:19:15 +0000 |
2645 | @@ -0,0 +1,127 @@ |
2646 | +################################################################################ |
2647 | +# |
2648 | +# Copyright (c) 2009 The MadGraph Development team and Contributors |
2649 | +# |
2650 | +# This file is a part of the MadGraph 5 project, an application which |
2651 | +# automatically generates Feynman diagrams and matrix elements for arbitrary |
2652 | +# high-energy processes in the Standard Model and beyond. |
2653 | +# |
2654 | +# It is subject to the MadGraph license which should accompany this |
2655 | +# distribution. |
2656 | +# |
2657 | +# For more information, please visit: http://madgraph.phys.ucl.ac.be |
2658 | +# |
2659 | +################################################################################ |
2660 | + |
2661 | +"""Unit test library for the save model routines""" |
2662 | + |
2663 | +import StringIO |
2664 | +import unittest |
2665 | + |
2666 | +import madgraph.core.base_objects as base_objects |
2667 | + |
2668 | +import madgraph.iolibs.files as files |
2669 | +import madgraph.iolibs.save_model as save_model |
2670 | + |
2671 | +class IOSaveModel(unittest.TestCase): |
2672 | + """Unit test class for the save model routines""" |
2673 | + |
2674 | + def test_error_particle_save(self): |
2675 | + """Test error raising for particle save""" |
2676 | + |
2677 | + fsock = StringIO.StringIO('') |
2678 | + |
2679 | + my_part_list = "This is not a particle list" |
2680 | + |
2681 | + self.assertRaises(ValueError, |
2682 | + save_model.save_particles, |
2683 | + fsock, my_part_list) |
2684 | + |
2685 | + def test_particle_save(self): |
2686 | + """Test the particle save routine""" |
2687 | + |
2688 | + mypartlist = \ |
2689 | + base_objects.ParticleList(\ |
2690 | + [base_objects.Particle({'name':'ve', |
2691 | + 'antiname':'ve~', |
2692 | + 'spin':2, |
2693 | + 'color':1, |
2694 | + 'mass':'ZERO', |
2695 | + 'width':'ZERO', |
2696 | + 'texname':'ve', |
2697 | + 'antitexname':'ve', |
2698 | + 'line':'straight', |
2699 | + 'charge': 0., |
2700 | + 'pdg_code':12, |
2701 | + 'propagating':True, |
2702 | + 'is_part':True, |
2703 | + 'self_antipart':False}), |
2704 | + base_objects.Particle({'name':'w+', |
2705 | + 'antiname':'w-', |
2706 | + 'spin':3, |
2707 | + 'color':1, |
2708 | + 'mass':'MW', |
2709 | + 'width':'WW', |
2710 | + 'texname':'W', |
2711 | + 'antitexname':'W', |
2712 | + 'line':'wavy', |
2713 | + 'charge':0., |
2714 | + 'pdg_code':24, |
2715 | + 'propagating':True, |
2716 | + 'is_part':True, |
2717 | + 'self_antipart':False})]) |
2718 | + |
2719 | + fsock = StringIO.StringIO() |
2720 | + save_model.save_particles(fsock, mypartlist) |
2721 | + |
2722 | + goal_str = "particles = [\n%s,%s]" % (str(mypartlist[0]), |
2723 | + str(mypartlist[1])) |
2724 | + |
2725 | + self.assertEqual(fsock.getvalue(), goal_str) |
2726 | + |
2727 | + def test_error_interaction_save(self): |
2728 | + """Test error raising for interaction save""" |
2729 | + |
2730 | + fsock = StringIO.StringIO('') |
2731 | + |
2732 | + my_inter_list = "This is not an interaction list" |
2733 | + |
2734 | + self.assertRaises(ValueError, |
2735 | + save_model.save_interactions, |
2736 | + fsock, my_inter_list) |
2737 | + |
2738 | + def test_interaction_save(self): |
2739 | + """Test the interaction save routine""" |
2740 | + |
2741 | + mypart = base_objects.Particle({'name':'t', |
2742 | + 'antiname':'t~', |
2743 | + 'spin':2, |
2744 | + 'color':3, |
2745 | + 'mass':'mt', |
2746 | + 'width':'wt', |
2747 | + 'texname':'t', |
2748 | + 'antitexname':'\\overline{t}', |
2749 | + 'line':'straight', |
2750 | + 'charge':2. / 3., |
2751 | + 'pdg_code':6, |
2752 | + 'propagating':True, |
2753 | + 'is_part':True}) |
2754 | + |
2755 | + myinterlist = base_objects.InteractionList([\ |
2756 | + base_objects.Interaction({'id': 1, |
2757 | + 'particles': base_objects.ParticleList([mypart] * 4), |
2758 | + 'color': ['C1', 'C2'], |
2759 | + 'lorentz':['L1', 'L2'], |
2760 | + 'couplings':{(0, 0):'g00', |
2761 | + (0, 1):'g01', |
2762 | + (1, 0):'g10', |
2763 | + (1, 1):'g11'}, |
2764 | + 'orders':{'QCD':1, 'QED':1}})]) |
2765 | + |
2766 | + fsock = StringIO.StringIO() |
2767 | + save_model.save_interactions(fsock, myinterlist) |
2768 | + |
2769 | + goal_str = "interactions = [\n%s]" % str(myinterlist[0]) |
2770 | + |
2771 | + self.assertEqual(fsock.getvalue(), goal_str) |
2772 | + |
The color modules:
color_algebra: where the base classes ColorObjects (and children T, Tr, f and d) are defined
color_amp: where the ColorBasis is built
color_square: where the ColorMatrix is built
Important points to review: clarity/ comments
-) Proposal for new tests
-) Code structure/