decaylanguage 0.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- decaylanguage/__init__.py +0 -0
- decaylanguage/decay/__init__.py +0 -0
- decaylanguage/decay/ampgen2goofit.py +77 -0
- decaylanguage/decay/ampline.py +79 -0
- decaylanguage/decay/amplitudechain.py +323 -0
- decaylanguage/decay/goofit.py +321 -0
- decaylanguage/decay/utilities.py +51 -0
- decaylanguage/goofit.py +23 -0
- decaylanguage/particle/MintDalitzSpecialParticles.csv +41 -0
- decaylanguage/particle/__init__.py +12 -0
- decaylanguage/particle/mass_width.csv +736 -0
- decaylanguage/particle/particle.py +435 -0
- decaylanguage/particle/pdgID_to_latex.txt +337 -0
- decaylanguage-0.1.0.dist-info/DESCRIPTION.rst +56 -0
- decaylanguage-0.1.0.dist-info/METADATA +91 -0
- decaylanguage-0.1.0.dist-info/RECORD +20 -0
- decaylanguage-0.1.0.dist-info/WHEEL +6 -0
- decaylanguage-0.1.0.dist-info/metadata.json +1 -0
- decaylanguage-0.1.0.dist-info/top_level.txt +1 -0
- decaylanguage-0.1.0.dist-info/zip-safe +1 -0
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
from __future__ import absolute_import
|
|
5
|
+
from __future__ import division
|
|
6
|
+
from __future__ import print_function
|
|
7
|
+
|
|
8
|
+
import datetime
|
|
9
|
+
from six import StringIO
|
|
10
|
+
|
|
11
|
+
from functools import partial
|
|
12
|
+
|
|
13
|
+
from plumbum import colors
|
|
14
|
+
|
|
15
|
+
from decaylanguage.decay.goofit import GooFitChain
|
|
16
|
+
from decaylanguage.decay.goofit import SF_4Body
|
|
17
|
+
from decaylanguage.particle import SpinType
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def ampgen2goofit(filename, ret_output=False):
|
|
21
|
+
if ret_output:
|
|
22
|
+
output = StringIO()
|
|
23
|
+
printer = partial(print, file=output)
|
|
24
|
+
else:
|
|
25
|
+
printer = print
|
|
26
|
+
|
|
27
|
+
lines, all_states = GooFitChain.read_AmpGen(str(filename))
|
|
28
|
+
|
|
29
|
+
printer(r'/* Autogenerated file by AmpGen2GooFit')
|
|
30
|
+
printer('Generated on ', datetime.datetime.now())
|
|
31
|
+
|
|
32
|
+
printer('\n')
|
|
33
|
+
for seen_factor in {p.spindetails() for p in lines}:
|
|
34
|
+
my_lines = [p for p in lines if p.spindetails() == seen_factor]
|
|
35
|
+
printer(colors.bold | seen_factor, ":", *my_lines[0].spinfactors)
|
|
36
|
+
for line in my_lines:
|
|
37
|
+
printer(' ', colors.blue | str(line))
|
|
38
|
+
|
|
39
|
+
printer('\n')
|
|
40
|
+
for spintype in SpinType:
|
|
41
|
+
ps = [format(str(p), '11')
|
|
42
|
+
for p in sorted(GooFitChain.all_particles) if p.spintype == spintype]
|
|
43
|
+
printer("{spintype.name:>12}:".format(spintype=spintype), *ps)
|
|
44
|
+
|
|
45
|
+
printer('\n')
|
|
46
|
+
for n, line in enumerate(lines):
|
|
47
|
+
printer('{n:2} {line!s:<70} spinfactors: {lensf} L: {line.L} [{Lr[0]}-{Lr[1]}]'
|
|
48
|
+
.format(n=n, line=line, lensf=len(line.spinfactors), Lr=line.L_range()))
|
|
49
|
+
|
|
50
|
+
# We can make the GooFit Intro code:
|
|
51
|
+
|
|
52
|
+
printer(colors.bold & colors.green | '\n\nAll discovered spin configurations:')
|
|
53
|
+
|
|
54
|
+
for line in sorted({line.spindetails() for line in lines}):
|
|
55
|
+
printer(colors.green | line)
|
|
56
|
+
|
|
57
|
+
printer(colors.bold & colors.blue | '\n\nAll known spin configurations:')
|
|
58
|
+
|
|
59
|
+
# TODO: 4 body only
|
|
60
|
+
for e in SF_4Body:
|
|
61
|
+
printer(colors.blue | e.name)
|
|
62
|
+
|
|
63
|
+
printer('\n*/\n\n // Intro')
|
|
64
|
+
printer(GooFitChain.make_intro(all_states))
|
|
65
|
+
|
|
66
|
+
printer('\n\n // Parameters')
|
|
67
|
+
printer(GooFitChain.make_pars())
|
|
68
|
+
|
|
69
|
+
# And the lines can be turned into code, as well:
|
|
70
|
+
|
|
71
|
+
printer('\n\n // Lines')
|
|
72
|
+
for n, line in enumerate(lines):
|
|
73
|
+
printer(' // Line', n)
|
|
74
|
+
printer(line.to_goofit(all_states[1:]), end='\n\n\n')
|
|
75
|
+
|
|
76
|
+
if(ret_output):
|
|
77
|
+
return output.getvalue()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'''
|
|
2
|
+
ampline.py
|
|
3
|
+
|
|
4
|
+
A collection of regular expressions that pick out information about line.
|
|
5
|
+
|
|
6
|
+
The regular expressions make named groups that you can access.
|
|
7
|
+
'''
|
|
8
|
+
|
|
9
|
+
from __future__ import absolute_import
|
|
10
|
+
from __future__ import division
|
|
11
|
+
from __future__ import print_function
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
inverted = re.compile(r'''
|
|
16
|
+
^ # Beginning of string
|
|
17
|
+
(?P<line> [^=]+ ) # All chars until =
|
|
18
|
+
= # Must have an equals sign
|
|
19
|
+
\s* (?P<factor> [0-9\-\.]* ) # Optional
|
|
20
|
+
\s* \*? # Eat a * sign if needed
|
|
21
|
+
\s* (?P<invline> [^=]+ ) # The inverted line
|
|
22
|
+
$ # End of string
|
|
23
|
+
''', re.VERBOSE)
|
|
24
|
+
|
|
25
|
+
dual = re.compile(r'''
|
|
26
|
+
^ # Beginning of string
|
|
27
|
+
(?P<name> [^\{^\[]+ ) # All chars until { or [
|
|
28
|
+
(?:\[ # Start of []
|
|
29
|
+
(?P<spinfactor> \w # Optional 1 char spin factor
|
|
30
|
+
(?= \W ) # - Look ahead for non-word
|
|
31
|
+
)? ;? # Remove ; if exists
|
|
32
|
+
(?P<lineshape> [^\]]* )? # The rest of the lineshape
|
|
33
|
+
\] )? # Done with []
|
|
34
|
+
(?:\{ (?P<daughters> .* ) \} )? # All daughters
|
|
35
|
+
\s+ (?P<fix_r> [02] ) # Real fix
|
|
36
|
+
\s+ (?P<A> [0-9\.\-]+ ) # Real value
|
|
37
|
+
\s+ (?P<dA> [0-9\.\-]+ ) # Real error
|
|
38
|
+
\s+ (?P<fix_i> [02] ) # Imag fix
|
|
39
|
+
\s+ (?P<theta> [0-9\.\-]+ ) # Imag value
|
|
40
|
+
\s+ (?P<dtheta> [0-9\.\-]+ ) # Imag error
|
|
41
|
+
$ # End of string
|
|
42
|
+
''', re.VERBOSE)
|
|
43
|
+
|
|
44
|
+
cartesian = re.compile(r'''
|
|
45
|
+
^ # Beginning of string
|
|
46
|
+
(?P<name> .+ ) # All chars of name
|
|
47
|
+
_ (?P<cart> Re|Im ) # Real or imaginary
|
|
48
|
+
\s+ (?P<fix> [02] ) # Value fix
|
|
49
|
+
\s+ (?P<amp> [0-9\.\-]+ ) # Value
|
|
50
|
+
\s+ (?P<err> [0-9\.\-]+ ) # Error
|
|
51
|
+
$ # End of string
|
|
52
|
+
''', re.VERBOSE)
|
|
53
|
+
|
|
54
|
+
partial = re.compile(r'''
|
|
55
|
+
^ # Beginning of string
|
|
56
|
+
(?P<name> [^\{^\[]+ ) # All chars until { or [
|
|
57
|
+
(?:\[ # Start of []
|
|
58
|
+
(?P<spinfactor> \w # Optional 1 char spin factor
|
|
59
|
+
(?= \W ) # - Look ahead for non-word
|
|
60
|
+
)? ;? # Remove ; if exists
|
|
61
|
+
(?P<lineshape> [^\]]* )? # The rest of the lineshape
|
|
62
|
+
\] )? # Done with []
|
|
63
|
+
(?:\{ (?P<daughters> .* ) \} )? # All daughters
|
|
64
|
+
$ # End of string
|
|
65
|
+
''', re.VERBOSE)
|
|
66
|
+
|
|
67
|
+
settings = re.compile(r'''
|
|
68
|
+
^ # Beginning of string
|
|
69
|
+
(?P<name> # Choice list
|
|
70
|
+
FastCoherentSum::UseCartesian
|
|
71
|
+
| output
|
|
72
|
+
| nEvents
|
|
73
|
+
| EventType
|
|
74
|
+
)
|
|
75
|
+
\s+ # White space
|
|
76
|
+
(?P<value> .*? ) # Everything else
|
|
77
|
+
\s* # Strip remainging whitespace
|
|
78
|
+
$ # End of string
|
|
79
|
+
''', re.VERBOSE)
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
from __future__ import division
|
|
3
|
+
from __future__ import print_function
|
|
4
|
+
|
|
5
|
+
from copy import copy
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from itertools import combinations
|
|
8
|
+
from itertools import product
|
|
9
|
+
|
|
10
|
+
import attr
|
|
11
|
+
import graphviz
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pandas as pd
|
|
14
|
+
|
|
15
|
+
from . import ampline
|
|
16
|
+
from ..particle import Particle
|
|
17
|
+
from .utilities import filter_lines
|
|
18
|
+
from .utilities import iter_flatten
|
|
19
|
+
from .utilities import split
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LS(Enum):
|
|
23
|
+
'Line shapes supported (currently)'
|
|
24
|
+
RBW = 0
|
|
25
|
+
GSpline = 1
|
|
26
|
+
kMatrix = 2
|
|
27
|
+
FOCUS = 3
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@attr.s(slots=True)
|
|
31
|
+
class AmplitudeChain(object):
|
|
32
|
+
particle = attr.ib()
|
|
33
|
+
daughters = attr.ib([], convert=lambda x: x if x else [])
|
|
34
|
+
lineshape = attr.ib(None)
|
|
35
|
+
spinfactor = attr.ib(None)
|
|
36
|
+
amp = attr.ib(1+0j, cmp=False, validator=attr.validators.instance_of(complex))
|
|
37
|
+
err = attr.ib(0+0j, cmp=False, validator=attr.validators.instance_of(complex))
|
|
38
|
+
fix = attr.ib(True, cmp=False, validator=attr.validators.instance_of(bool))
|
|
39
|
+
name = attr.ib(None)
|
|
40
|
+
|
|
41
|
+
def __attrs_post_init__(self):
|
|
42
|
+
if self.name is None:
|
|
43
|
+
self.name = self.particle.name
|
|
44
|
+
|
|
45
|
+
# Class members keep track of additions
|
|
46
|
+
all_particles = set()
|
|
47
|
+
final_particles = set()
|
|
48
|
+
|
|
49
|
+
# This is set class-wide, and only used when a line is made
|
|
50
|
+
cartesian = False
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_matched_line(cls, mat):
|
|
54
|
+
'''
|
|
55
|
+
This operates on an already-matched line.
|
|
56
|
+
|
|
57
|
+
:param mat: The groupdict output of a match
|
|
58
|
+
:return: A new amplitude chain instance
|
|
59
|
+
'''
|
|
60
|
+
mat['particle'] = Particle.from_AmpGen(mat['name'])
|
|
61
|
+
|
|
62
|
+
if mat['particle'] not in cls.all_particles:
|
|
63
|
+
cls.all_particles |= {mat['particle']}
|
|
64
|
+
|
|
65
|
+
if mat['daughters']:
|
|
66
|
+
mat['daughters'] = [cls.from_matched_line(ampline.partial.match(
|
|
67
|
+
x).groupdict()) for x in split(mat['daughters'])]
|
|
68
|
+
|
|
69
|
+
# This is only true if not a parital line
|
|
70
|
+
if 'fix_r' in mat:
|
|
71
|
+
# Slightly odd, since we use cartesian, so fixing theta or mag
|
|
72
|
+
# of amplitude only doesn't work
|
|
73
|
+
mat['fix'] = mat['fix_r'] == '2' and mat['fix_i'] == '2'
|
|
74
|
+
del mat['fix_r'], mat['fix_i']
|
|
75
|
+
|
|
76
|
+
if cls.cartesian:
|
|
77
|
+
mat['amp'] = float(mat['A']) + float(mat['theta'])*1j
|
|
78
|
+
mat['err'] = float(mat['theta']) + float(mat['dtheta'])*1j
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
A = float(mat['A'])
|
|
82
|
+
dA = float(mat['dA'])
|
|
83
|
+
theta = float(mat['theta'])
|
|
84
|
+
dtheta = float(mat['dtheta'])
|
|
85
|
+
mat['amp'] = A * np.exp(theta*1j)
|
|
86
|
+
|
|
87
|
+
mat['err'] = ((dA*np.cos(theta) + A*np.sin(dtheta))
|
|
88
|
+
+ (dA*np.sin(theta) + A*np.cos(dtheta))*1j)
|
|
89
|
+
|
|
90
|
+
del mat['A'], mat['dA'], mat['theta'], mat['dtheta']
|
|
91
|
+
|
|
92
|
+
return cls(**mat)
|
|
93
|
+
|
|
94
|
+
def expand_lines(self, linelist):
|
|
95
|
+
'''
|
|
96
|
+
Take a DecayTree -> list of DecayTrees with each dead-end daughter
|
|
97
|
+
expanded to every possible combination. (recursive)
|
|
98
|
+
|
|
99
|
+
'''
|
|
100
|
+
|
|
101
|
+
# If the Tree has daugthers, run on those
|
|
102
|
+
if self.daughters:
|
|
103
|
+
dlist = [d.expand_lines(linelist) for d in self.daughters]
|
|
104
|
+
retlist = []
|
|
105
|
+
for ds in product(*dlist):
|
|
106
|
+
newd = copy(self)
|
|
107
|
+
newd.daughters = ds
|
|
108
|
+
retlist.append(newd)
|
|
109
|
+
return retlist
|
|
110
|
+
|
|
111
|
+
# If the tree ends
|
|
112
|
+
new_trees = [l for line in linelist if line.name ==
|
|
113
|
+
self.name for l in line.expand_lines(linelist)]
|
|
114
|
+
if new_trees:
|
|
115
|
+
return new_trees
|
|
116
|
+
else:
|
|
117
|
+
self.__class__.final_particles |= {self.particle}
|
|
118
|
+
return [self]
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def ls_enum(self):
|
|
122
|
+
if not self.lineshape:
|
|
123
|
+
return LS.RBW
|
|
124
|
+
elif 'GSpline.EFF' == self.lineshape:
|
|
125
|
+
return LS.GSpline
|
|
126
|
+
elif self.lineshape.startswith('kMatrix'):
|
|
127
|
+
return LS.kMatrix
|
|
128
|
+
elif self.lineshape.startswith('FOCUS'):
|
|
129
|
+
return LS.FOCUS
|
|
130
|
+
else:
|
|
131
|
+
raise RuntimeError("Unimplemented lineshape {0}".format(self.lineshape))
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def full_amp(self):
|
|
135
|
+
amp = self.amp
|
|
136
|
+
for d in self.daughters:
|
|
137
|
+
amp *= d.full_amp
|
|
138
|
+
return amp
|
|
139
|
+
|
|
140
|
+
def is_vertex(self):
|
|
141
|
+
return len(self) == 2
|
|
142
|
+
|
|
143
|
+
def is_strong(self):
|
|
144
|
+
if not self.is_vertex():
|
|
145
|
+
return None
|
|
146
|
+
return set(self.particle.quarks) == set(self[0].particle.quarks).union(set(self[1].particle.quarks))
|
|
147
|
+
|
|
148
|
+
def L_range(self, conserveParity=False):
|
|
149
|
+
S = self.particle.J
|
|
150
|
+
s1 = self[0].particle.J
|
|
151
|
+
s2 = self[1].particle.J
|
|
152
|
+
min_spin = abs(S-s1-s2)
|
|
153
|
+
min_spin = min(abs(S+s1-s2), min_spin)
|
|
154
|
+
min_spin = min(abs(S-s1+s2), min_spin)
|
|
155
|
+
max_spin = S + s1 + s2
|
|
156
|
+
if not conserveParity:
|
|
157
|
+
return (min_spin, max_spin)
|
|
158
|
+
else:
|
|
159
|
+
raise RuntimeError("Strong decays not implemented yet")
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def L(self):
|
|
163
|
+
if self.spinfactor:
|
|
164
|
+
return 'S P D F'.split().index(self.spinfactor)
|
|
165
|
+
min_L, _ = self.L_range()
|
|
166
|
+
return min_L # Ground state unless specified
|
|
167
|
+
|
|
168
|
+
def __len__(self):
|
|
169
|
+
return len(self.daughters)
|
|
170
|
+
|
|
171
|
+
def __getitem__(self, item):
|
|
172
|
+
return self.daughters[item]
|
|
173
|
+
|
|
174
|
+
def _get_html(self):
|
|
175
|
+
name = self.particle.html_name
|
|
176
|
+
|
|
177
|
+
if self.spinfactor or self.lineshape:
|
|
178
|
+
name += '<br/><br/>'
|
|
179
|
+
if self.spinfactor:
|
|
180
|
+
name += '<font color="blue">[' + self.spinfactor + ']</font>'
|
|
181
|
+
if self.lineshape:
|
|
182
|
+
name += '<font color="red">[' + self.lineshape + ']</font>'
|
|
183
|
+
return name
|
|
184
|
+
|
|
185
|
+
def _add_nodes(self, drawing):
|
|
186
|
+
name = self._get_html()
|
|
187
|
+
drawing.node(str(id(self)), "<" + name + ">")
|
|
188
|
+
for p in self.daughters:
|
|
189
|
+
drawing.edge(str(id(self)), str(id(p)))
|
|
190
|
+
p._add_nodes(drawing)
|
|
191
|
+
|
|
192
|
+
def _make_graphvis(self):
|
|
193
|
+
d = graphviz.Digraph()
|
|
194
|
+
d.attr(labelloc='t', label=str(self))
|
|
195
|
+
self._add_nodes(d)
|
|
196
|
+
return d
|
|
197
|
+
|
|
198
|
+
def _repr_svg_(self):
|
|
199
|
+
return self._make_graphvis()._repr_svg_()
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def vertexes(self):
|
|
203
|
+
verts = []
|
|
204
|
+
for d in self.daughters:
|
|
205
|
+
if d.is_vertex():
|
|
206
|
+
verts.append(d)
|
|
207
|
+
verts += d.vertexes
|
|
208
|
+
return verts
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def structure(self):
|
|
212
|
+
'''
|
|
213
|
+
The structure of the decay chain, simplified to only final state particles
|
|
214
|
+
'''
|
|
215
|
+
if self.daughters:
|
|
216
|
+
return [d.structure for d in self.daughters]
|
|
217
|
+
else:
|
|
218
|
+
return self.particle
|
|
219
|
+
|
|
220
|
+
def list_structure(self, final_states):
|
|
221
|
+
'''
|
|
222
|
+
The structure in the form [(0,1,2,3)], where the dual-list is used
|
|
223
|
+
for permutations for bose symmatrization.
|
|
224
|
+
So for final_states=[a,b,c,c], [a,c,[c,b]] would be:
|
|
225
|
+
[(0,2,3,1),(0,3,2,1)]
|
|
226
|
+
'''
|
|
227
|
+
|
|
228
|
+
structure = list(iter_flatten(self.structure))
|
|
229
|
+
|
|
230
|
+
if set(structure) - set(final_states):
|
|
231
|
+
raise RuntimeError("The final states must encompass all particles in final states!")
|
|
232
|
+
|
|
233
|
+
possibilities = [[i for i, v in enumerate(final_states) if v == name] for name in structure]
|
|
234
|
+
return [a for a in product(*possibilities) if len(set(a)) == len(a)]
|
|
235
|
+
|
|
236
|
+
def __str__(self):
|
|
237
|
+
name = str(self.particle)
|
|
238
|
+
if self.lineshape and self.spinfactor:
|
|
239
|
+
name += '[' + self.spinfactor + ';' + self.lineshape + ']'
|
|
240
|
+
elif self.lineshape:
|
|
241
|
+
name += '[' + self.lineshape + ']'
|
|
242
|
+
elif self.spinfactor:
|
|
243
|
+
name += '[' + self.spinfactor + ']'
|
|
244
|
+
if self.daughters:
|
|
245
|
+
name += '{'+','.join(map(str, self.daughters))+'}'
|
|
246
|
+
return name
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
def read_AmpGen(cls, filename=None, text=None):
|
|
250
|
+
'''
|
|
251
|
+
Read in an ampgen file
|
|
252
|
+
|
|
253
|
+
:param filename: Filename
|
|
254
|
+
:return: array of AmplitudeChains, parameters, constants, event type
|
|
255
|
+
'''
|
|
256
|
+
|
|
257
|
+
# Read the file in, ignore empty lines and comments
|
|
258
|
+
if filename is not None:
|
|
259
|
+
with open(filename) as f:
|
|
260
|
+
valid_lines = [l.strip().rstrip(',') for l in f if l and not l.startswith('#')]
|
|
261
|
+
elif text is not None:
|
|
262
|
+
valid_lines = [l.strip().rstrip(',')
|
|
263
|
+
for l in text.splitlines() if l and not l.startswith('#')]
|
|
264
|
+
else:
|
|
265
|
+
raise RuntimeError("Must have filename or text")
|
|
266
|
+
|
|
267
|
+
# Collect known options
|
|
268
|
+
option_lines, valid_lines = filter_lines(ampline.settings, valid_lines)
|
|
269
|
+
|
|
270
|
+
# Collect lines with an = in them
|
|
271
|
+
relation_lines, valid_lines = filter_lines(ampline.inverted, valid_lines)
|
|
272
|
+
|
|
273
|
+
# Collect "normal" non-Cartesian lines
|
|
274
|
+
real_lines, valid_lines = filter_lines(ampline.dual, valid_lines)
|
|
275
|
+
|
|
276
|
+
# Collect Cartesian lines (need combining)
|
|
277
|
+
cart_lines, valid_lines = filter_lines(ampline.cartesian, valid_lines)
|
|
278
|
+
|
|
279
|
+
# The other lines do not need explicit filtering
|
|
280
|
+
variable_lines = [l for l in valid_lines if len(l.split()) == 4]
|
|
281
|
+
constant_lines = [l for l in valid_lines if len(l.split()) == 2]
|
|
282
|
+
|
|
283
|
+
# Process the options
|
|
284
|
+
all_states = None
|
|
285
|
+
for mat in option_lines:
|
|
286
|
+
if mat['name'] == 'EventType':
|
|
287
|
+
all_states = [Particle.from_AmpGen(name) for name in mat['value'].split()]
|
|
288
|
+
elif mat['name'] == 'FastCoherentSum::UseCartesian':
|
|
289
|
+
cls.cartesian = bool(mat['value'])
|
|
290
|
+
|
|
291
|
+
# Make sure this exists!
|
|
292
|
+
if all_states is None:
|
|
293
|
+
raise RuntimeError("EventType is missing! Cannot compute decay.")
|
|
294
|
+
|
|
295
|
+
# Combine dual line Cartesian lines into traditional cartesian lines
|
|
296
|
+
for a, b in combinations(cart_lines, 2):
|
|
297
|
+
if a['name'] == b['name']:
|
|
298
|
+
if a['cart'] == 'Re' and b['cart'] == 'Im':
|
|
299
|
+
pass
|
|
300
|
+
elif a['cart'] == 'Im' and b['cart'] == 'Re':
|
|
301
|
+
a, b = b, a
|
|
302
|
+
else:
|
|
303
|
+
raise RuntimeError("Can't process a line with *both* components Re or Im")
|
|
304
|
+
new_string = "{a[name]} {a[fix]} {a[amp]} {a[err]} {b[fix]} {b[amp]} {b[err]}".format(
|
|
305
|
+
a=a, b=b)
|
|
306
|
+
real_lines.append(ampline.dual.match(new_string).groupdict())
|
|
307
|
+
|
|
308
|
+
# Make the partial lines and constants as dataframes
|
|
309
|
+
parameters = pd.DataFrame(((v.strip() for v in p.split()) for p in variable_lines),
|
|
310
|
+
columns='name fix value error'.split()).set_index('name')
|
|
311
|
+
|
|
312
|
+
constants = pd.DataFrame(((v.strip() for v in p.split()) for p in constant_lines),
|
|
313
|
+
columns='name value'.split()).set_index('name')
|
|
314
|
+
|
|
315
|
+
# Convert the matches into AmplitudeChains
|
|
316
|
+
line_arr = [cls.from_matched_line(a) for a in real_lines]
|
|
317
|
+
|
|
318
|
+
# Expand partial lines into complete lines
|
|
319
|
+
new_line_arr = [l for line in line_arr if line.particle == all_states[0]
|
|
320
|
+
for l in line.expand_lines(line_arr)]
|
|
321
|
+
|
|
322
|
+
# Return
|
|
323
|
+
return new_line_arr, parameters, constants, all_states
|