Skip to content

Commit f939cc3

Browse files
committed
outp: fixed 1tally 8 nps parsing
1 parent 2ec9663 commit f939cc3

File tree

9 files changed

+656
-15
lines changed

9 files changed

+656
-15
lines changed

docs/source/pymcnp/outp.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,20 @@ These AST class have methods for translating between PyMCNP and OUTP:
120120

121121
[tally subpackage](outp/tally)
122122

123-
### `Tally_8` Class
123+
### `Tally_8A` Class
124124

125125
```{eval-rst}
126-
.. autoclass:: pymcnp.outp.Tally_8
126+
.. autoclass:: pymcnp.outp.Tally_8A
127+
:members:
128+
:inherited-members:
129+
```
130+
131+
[tally subpackage](outp/tally)
132+
133+
### `Tally_8B` Class
134+
135+
```{eval-rst}
136+
.. autoclass:: pymcnp.outp.Tally_8B
127137
:members:
128138
:inherited-members:
129139
```

src/pymcnp/Outp.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,27 @@ def from_mcnp(source: str):
6161

6262
if len(tokens) > 2:
6363
header = outp.Header.from_mcnp(tokens[0] + '\n')
64-
tokens[1] = '1' + ''.join(filter(bool, tokens[1:]))
64+
tokens[1] = ''.join(filter(bool, tokens[1:]))
6565
else:
6666
header = outp.Header.from_mcnp(tokens[0])
6767
tokens.append('')
6868

6969
blocks = []
70-
for subsource in outp.Block._REGEX.finditer(tokens[1]):
70+
71+
for subsource in re.split(r'\n1', tokens[1]):
72+
if not subsource:
73+
continue
74+
else:
75+
subsource = '1' + subsource
76+
7177
for subclass in outp.Block.__subclasses__():
7278
try:
73-
if block := subclass.from_mcnp(subsource[0]):
79+
if block := subclass.from_mcnp(subsource):
7480
break
7581
except Exception:
7682
continue
83+
else:
84+
continue
7785

7886
blocks.append(block)
7987

src/pymcnp/outp/Tally_8A.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import re
2+
import typing
3+
4+
import pandas
5+
6+
from . import _block
7+
from . import tally
8+
from .. import types
9+
from .. import errors
10+
11+
12+
class Tally_8A(_block.Block):
13+
"""
14+
Represents OUTP `1tally 8 nps` blocks.
15+
16+
Attributes:
17+
number: Tally number.
18+
nps: Tally nps.
19+
tally_type: Tally type.
20+
particles: Tally particles.
21+
message: Tally modification message.
22+
subtallies: Subtallies.
23+
stats_desired: Statistical checks desired.
24+
stats_observed: Statistical checks observed.
25+
stats_passed: Statistical checks passed.
26+
asymmetric_interval: Estimated asymmetric confidence interval.
27+
symmetric_interval: Estimated symmetric condifence interval.
28+
"""
29+
30+
_REGEX = re.compile(
31+
r'\A1tally (.{8}) nps = (.{11})\n'
32+
r' tally type (.+)\n'
33+
r' particle[(]s[)]: (.+)(\n.*)?\n \n'
34+
rf'((?:{tally.Subtally_4._REGEX.pattern[2:-2]})+)\n'
35+
r' ===================================================================================================================================\n\n'
36+
r' results of 10 statistical checks for the estimated answer for the tally fluctuation chart [(]tfc[)] bin of tally .{8}\n\n'
37+
r' tfc bin --mean-- ---------relative error--------- ----variance of the variance---- --figure of merit-- -pdf-\n'
38+
r' behavior behavior value decrease decrease rate value decrease decrease rate value behavior slope\n\n'
39+
r'( .+)\n'
40+
r'( .+)\n'
41+
r'( .+)\n\n'
42+
r' ===================================================================================================================================\n\n\n'
43+
r' this tally meets the statistical criteria used to form confidence intervals: check the tally fluctuation chart to verify[.]\n'
44+
r' the results in other bins associated with this tally may not meet these statistical criteria[.]\n\n'
45+
r' ----- estimated confidence intervals: -----\n\n'
46+
r' estimated asymmetric confidence interval[(]1,2,3 sigma[)]: (.+)\n'
47+
r' estimated symmetric confidence interval[(]1,2,3 sigma[)]: (.+)\n\Z'
48+
)
49+
50+
def __init__(
51+
self,
52+
number: types.String,
53+
nps: types.String,
54+
tally_type: types.String,
55+
particles: types.String,
56+
message: types.String,
57+
subtallies: types.Tuple(tally.Subtally_1),
58+
stats_desired: types.String,
59+
stats_observed: types.String,
60+
stats_passed: types.String,
61+
asymmetric_interval: types.String,
62+
symmetric_interval: types.String,
63+
):
64+
"""
65+
Initializes `Tally_8A`.
66+
67+
Parameters:
68+
number: Tally number.
69+
nps: Tally nps.
70+
tally_type: Tally type.
71+
particles: Tally particles.
72+
message: Tally modification message.
73+
subtallies: Subtallies.
74+
stats_desired: Statistical checks desired.
75+
stats_observed: Statistical checks observed.
76+
stats_passed: Statistical checks passed.
77+
asymmetric_interval: Estimated asymmetric confidence interval.
78+
symmetric_interval: Estimated symmetric condifence interval.
79+
80+
Raises:
81+
OutpError: SEMANTICS_TABLE.
82+
"""
83+
84+
if number is None:
85+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, number)
86+
if nps is None:
87+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, nps)
88+
if tally_type is None:
89+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, tally_type)
90+
if particles is None:
91+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, particles)
92+
if subtallies is None:
93+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, subtallies)
94+
if stats_desired is None:
95+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, stats_desired)
96+
if stats_observed is None:
97+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, stats_observed)
98+
if stats_passed is None:
99+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, stats_passed)
100+
if asymmetric_interval is None:
101+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, asymmetric_interval)
102+
if symmetric_interval is None:
103+
raise errors.OutpError(errors.OutpCode.SEMANTICS_TABLE, symmetric_interval)
104+
105+
self.number: typing.Final[types.String] = number
106+
self.nps: typing.Final[types.String] = nps
107+
self.tally_type: typing.Final[types.String] = tally_type
108+
self.particles: typing.Final[types.String] = particles
109+
self.message: typing.Final[types.String] = message
110+
self.subtallies: typing.Final[types.Tuple(tally.Subtally_1)] = subtallies
111+
self.stats_desired: typing.Final[types.String] = stats_desired
112+
self.stats_observed: typing.Final[types.String] = stats_observed
113+
self.stats_passed: typing.Final[types.String] = stats_passed
114+
self.asymmetric_interval: typing.Final[types.String] = asymmetric_interval
115+
self.symmetric_interval: typing.Final[types.String] = symmetric_interval
116+
117+
@staticmethod
118+
def from_mcnp(source: str):
119+
"""
120+
Generates `Tally_8A` from OUTP.
121+
122+
Parameters:
123+
source: OUTP for `Tally_8A`.
124+
125+
Returns:
126+
`Tally_8A`.
127+
"""
128+
129+
tokens = Tally_8A._REGEX.match(source)
130+
131+
if not tokens:
132+
raise errors.OutpError(errors.OutpCode.SYNTAX_TABLE, source)
133+
134+
number = types.String.from_mcnp(tokens[1])
135+
nps = types.String.from_mcnp(tokens[2])
136+
tally_type = types.String.from_mcnp(tokens[3])
137+
particles = types.String.from_mcnp(tokens[4])
138+
message = types.String.from_mcnp(tokens[5]) if tokens[5] else None
139+
subtallies = types.Tuple(tally.Subtally_4).from_mcnp(tokens[6])
140+
offset = tally.Subtally_4._REGEX.groups
141+
stats_desired = types.String.from_mcnp(tokens[7 + offset])
142+
stats_observed = types.String.from_mcnp(tokens[8 + offset])
143+
stats_passed = types.String.from_mcnp(tokens[9 + offset])
144+
asymmetric_interval = types.String.from_mcnp(tokens[10 + offset])
145+
symmetric_interval = types.String.from_mcnp(tokens[11 + offset])
146+
147+
return Tally_8A(
148+
number,
149+
nps,
150+
tally_type,
151+
particles,
152+
message,
153+
subtallies,
154+
stats_desired,
155+
stats_observed,
156+
stats_passed,
157+
asymmetric_interval,
158+
symmetric_interval,
159+
)
160+
161+
def to_mcnp(self):
162+
"""
163+
Generates OUTP from `Tally_8A`.
164+
165+
Returns:
166+
OUTP for `Tally_8A`.
167+
"""
168+
169+
message = '\n ' + str(self.message) + '\n' if self.message else ''
170+
171+
return f"""
172+
1tally {self.number} nps = {self.nps}
173+
tally type {self.tally_type}
174+
particle(s): {self.particles}{message}
175+
176+
{''.join(map(str, self.subtallies))}
177+
===================================================================================================================================
178+
179+
results of 10 statistical checks for the estimated answer for the tally fluctuation chart (tfc) bin of tally {self.number}
180+
181+
tfc bin --mean-- ---------relative error--------- ----variance of the variance---- --figure of merit-- -pdf-
182+
behavior behavior value decrease decrease rate value decrease decrease rate value behavior slope
183+
184+
{self.stats_desired}
185+
{self.stats_observed}
186+
{self.stats_passed}
187+
188+
===================================================================================================================================
189+
190+
191+
this tally meets the statistical criteria used to form confidence intervals: check the tally fluctuation chart to verify.
192+
the results in other bins associated with this tally may not meet these statistical criteria.
193+
194+
----- estimated confidence intervals: -----
195+
196+
estimated asymmetric confidence interval(1,2,3 sigma): {self.asymmetric_interval}
197+
estimated symmetric confidence interval(1,2,3 sigma): {self.symmetric_interval}
198+
"""[1:]
199+
200+
def to_dataframe(self):
201+
"""
202+
Generates `pandas.DataFrame` from `Tally_8A`.
203+
204+
Returns:
205+
`pandas.DataFrame`.
206+
"""
207+
208+
df = pandas.concat((subtally.to_dataframe() for subtally in self.subtallies), ignore_index=True)
209+
df['number'] = self.number.value.strip()
210+
df['type'] = self.tally_type.value.strip()
211+
df['particles'] = self.particles.value.strip()
212+
df['nps'] = self.nps.value.strip()
213+
return df

0 commit comments

Comments
 (0)