Skip to content

Commit 6883e2b

Browse files
committed
meshtal: added to_mcnp methods
1 parent 9371789 commit 6883e2b

File tree

3 files changed

+95
-52
lines changed

3 files changed

+95
-52
lines changed

src/pymcnp/Meshtal.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
import typing
23

34
from . import meshtal
@@ -14,6 +15,8 @@ class Meshtal(_object.McnpFile_):
1415
tallies: MESTHAL tallies.
1516
"""
1617

18+
_REGEX = re.compile(rf'({meshtal.Header._REGEX.pattern})((?:{meshtal.Tally._REGEX.pattern})+)')
19+
1720
def __init__(
1821
self, header: meshtal.Header, tallies: typing.Generator[meshtal.Tally, None, None]
1922
):
@@ -55,15 +58,16 @@ def from_mcnp(source: str):
5558
``Meshtal``.
5659
"""
5760

58-
header, lines = meshtal.Header.from_mcnp(source)
61+
tokens = Meshtal._REGEX.match(source)
5962

60-
def tallies(lines):
61-
while lines:
62-
tally, lines = meshtal.Tally.from_mcnp(lines, header)
63-
yield tally
64-
return
63+
if not tokens:
64+
raise errors.MeshtalError(errors.MeshtalCode.SYNTAX_MESHTAL, source)
6565

66-
tallies = tallies(lines)
66+
header = meshtal.Header.from_mcnp(tokens[1])
67+
tallies = (
68+
meshtal.Tally.from_mcnp(match[0], header)
69+
for match in meshtal.Tally._REGEX.finditer(tokens[12])
70+
)
6771

6872
return Meshtal(header, tallies)
6973

@@ -75,4 +79,4 @@ def to_mcnp(self):
7579
INP for ``Meshtal``.
7680
"""
7781

78-
return header.to_mcnp() + '\n'.join(tallies.to_mcnp())
82+
return self.header.to_mcnp() + ' ' + '\n '.join(tally.to_mcnp() for tally in self.tallies)

src/pymcnp/meshtal/Header.py

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Header(_block.Block):
2929
"""
3030

3131
_REGEX = re.compile(
32-
r'(.{7})version (.{6})ld=(.{10})probid =(.{20})\n\s(.{80})\n\sNumber of histories used for normalizing tallies =(.{17})\n\n\sMesh Tally Number(.{10})\n\s(.{8}) mesh tally[.]\n This mesh tally is modified by a dose response function[.]\n\n Tally bin boundaries:\n((?: (?:X direction|Y direction|Z direction|Energy bin boundaries|Time bin boundaries):.+\n)+)\n(.+)\n([\s\S]+)'
32+
r'(.{7})version (.{6})ld=(.{10})probid =(.{20})\n\s(.{80})\n\sNumber of histories used for normalizing tallies =(.{17})\n\n\sMesh Tally Number(.{10})\n\s(.{8}) mesh tally[.]\n This mesh tally is modified by a dose response function[.]\n\n Tally bin boundaries:\n((?: .+\n)+)\n(.+)\n'
3333
)
3434

3535
def __init__(
@@ -103,6 +103,7 @@ def __init__(
103103
self.version: typing.Final[types.String] = version
104104
self.ld: typing.Final[types.String] = ld
105105
self.probid: typing.Final[types.String] = probid
106+
self.title: typing.Final[types.String] = title
106107
self.histories: typing.Final[types.Integer] = histories
107108
self.number: typing.Final[types.Integer] = number
108109
self.particle: typing.Final[types.String] = particle
@@ -114,7 +115,7 @@ def __init__(
114115
self.columns: typing.Final[types.String] = columns
115116

116117
@staticmethod
117-
def from_mcnp(source: str) -> tuple[Header, str]:
118+
def from_mcnp(source: str):
118119
"""
119120
Generates ``Header`` from MESHTAL.
120121
@@ -157,24 +158,21 @@ def from_mcnp(source: str) -> tuple[Header, str]:
157158

158159
columns = types.String.from_mcnp(tokens[10])
159160

160-
return (
161-
Header(
162-
code,
163-
version,
164-
ld,
165-
probid,
166-
title,
167-
histories,
168-
number,
169-
particle,
170-
bins_x,
171-
bins_y,
172-
bins_z,
173-
bins_energy,
174-
bins_time,
175-
columns,
176-
),
177-
tokens[11],
161+
return Header(
162+
code,
163+
version,
164+
ld,
165+
probid,
166+
title,
167+
histories,
168+
number,
169+
particle,
170+
bins_x,
171+
bins_y,
172+
bins_z,
173+
bins_energy,
174+
bins_time,
175+
columns,
178176
)
179177

180178
def to_mcnp(self):
@@ -185,4 +183,19 @@ def to_mcnp(self):
185183
INP for ``Header``.
186184
"""
187185

188-
assert False, "I'm working on it!"
186+
bins = ''
187+
bins += f" X direction:{''.join(map(str, self.bins_x))}\n" if self.bins_x else ''
188+
bins += f" Y direction:{''.join(map(str, self.bins_y))}\n" if self.bins_y else ''
189+
bins += f" Z direction:{''.join(map(str, self.bins_z))}\n" if self.bins_z else ''
190+
bins += (
191+
f" Energy bin boundaries:{''.join(map(str, self.bins_energy))}\n"
192+
if self.bins_energy
193+
else ''
194+
)
195+
bins += (
196+
f" Time bin boundaries:{''.join(map(str, self.bins_time))}\n"
197+
if self.bins_time
198+
else ''
199+
)
200+
201+
return f'{self.code:>7}version {self.version:>6}ld={self.ld:>10}probid ={self.probid:>20}\n {self.title:>80}\n Number of histories used for normalizing tallies ={self.histories:>17.2F}\n\n Mesh Tally Number{self.number:>10}\n {self.particle:>8} mesh tally.\n This mesh tally is modified by a dose response function.\n\n Tally bin boundaries:\n{bins}\n{self.columns}\n'

src/pymcnp/meshtal/Tally.py

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Tally(_line.Line):
2121
error: Tally relative error.
2222
"""
2323

24-
_REGEX = re.compile(r'\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\s*(\S*)\n([\s\S]+)')
24+
_REGEX = re.compile(r'\s(.+)(\n|\Z)')
2525

2626
def __init__(
2727
self,
@@ -52,13 +52,13 @@ def __init__(
5252
if error is None:
5353
raise errors.MeshtalError(errors.MeshtalCode.SEMANTICS_LINE, error)
5454

55+
self.result: typing.Final[types.Real] = result
56+
self.error: typing.Final[types.Real] = error
5557
self.x: typing.Final[types.Real] = x
5658
self.y: typing.Final[types.Real] = y
5759
self.z: typing.Final[types.Real] = z
5860
self.energy: typing.Final[types.Real] = energy
5961
self.time: typing.Final[types.Real] = time
60-
self.result: typing.Final[types.Real] = result
61-
self.error: typing.Final[types.Real] = error
6262

6363
@staticmethod
6464
def from_mcnp(source: str, header: Header):
@@ -71,9 +71,9 @@ def from_mcnp(source: str, header: Header):
7171
"""
7272

7373
tokens = Tally._REGEX.match(source)
74-
headings = re.split(r'\s+', header.columns)
74+
headings = re.split(r'\s+', header.columns.strip())
7575

76-
if not tokens or (len(tokens.groups()) != len(headings) + 2):
76+
if not tokens:
7777
raise errors.MeshtalError(errors.MeshtalCode.SYNTAX_LINE, source)
7878

7979
x = None
@@ -84,27 +84,53 @@ def from_mcnp(source: str, header: Header):
8484
result = None
8585
error = None
8686

87+
tokens = re.split(r'\s+', tokens[1].strip())
88+
8789
for i, heading in enumerate(headings):
88-
if heading == 'x':
89-
x = tokens[i + 1]
90-
elif heading == 'y':
91-
y = tokens[i + 1]
92-
elif heading == 'z':
93-
z = tokens[i + 1]
94-
elif heading == 'energy':
95-
energy = tokens[i + 1]
96-
elif heading == 'time':
97-
time = tokens[i + 1]
98-
elif heading == 'result':
99-
result = tokens[i + 1]
100-
elif heading == 'rel':
101-
error = tokens[i + 1]
102-
elif heading == 'error':
90+
if heading == 'X':
91+
x = tokens[i]
92+
elif heading == 'Y':
93+
y = tokens[i]
94+
elif heading == 'Z':
95+
z = tokens[i]
96+
elif heading == 'Energy':
97+
energy = tokens[i]
98+
elif heading == 'Time':
99+
time = tokens[i]
100+
elif heading == 'Result':
101+
result = tokens[i]
102+
elif heading == 'Rel':
103+
error = tokens[i]
104+
elif heading == 'Error':
103105
pass
104106
else:
105107
raise errors.MeshtalError(errors.MeshtalCode.SYNTAX_LINE, source)
106108

107-
return (
108-
Tally(x, y, z, energy, time, result, error),
109-
tokens[8],
109+
return Tally(
110+
result,
111+
error,
112+
x=x,
113+
y=y,
114+
z=z,
115+
energy=energy,
116+
time=time,
110117
)
118+
119+
def to_mcnp(self):
120+
"""
121+
Generates MESTHAL from ``Tally``.
122+
123+
Returns:
124+
INP for ``Tally``.
125+
"""
126+
127+
line = ''
128+
line += f'{self.energy:>11}' if self.energy else ''
129+
line += f'{self.time:>11}' if self.time else ''
130+
line += f'{self.x:>10}' if self.x else ''
131+
line += f'{self.y:>10}' if self.y else ''
132+
line += f'{self.z:>10}' if self.z else ''
133+
line += f'{self.result:>12}' if self.result else ''
134+
line += f'{self.error:>12}' if self.error else ''
135+
136+
return line

0 commit comments

Comments
 (0)