Skip to content
Merged
52 changes: 48 additions & 4 deletions src/openmc_mcnp_adapter/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from copy import deepcopy
from math import pi
import re
from pathlib import Path

import numpy as np

Expand All @@ -24,6 +25,16 @@
""", re.VERBOSE
)

_READ_RE = re.compile(r"""
^ # Beginning of line
\s*read # Keyword
\s.*?file # Everything up to filename
\s*=\s* # = sign (required) with optional spaces
(\S+) # The file name is anything without whitespace
.* # Anything else until end-of-line
""", re.IGNORECASE | re.VERBOSE | re.MULTILINE
)

_CELL1_RE = re.compile(r'\s*(\d+)\s+(\d+)([ \t0-9:#().dDeE\+-]+)\s*(.*)')
_CELL2_RE = re.compile(r'\s*(\d+)\s+like\s+(\d+)\s+but\s*(.*)')
_CELL_FILL_RE = re.compile(r'\s*(\d+)\s*(?:\((.*)\))?')
Expand Down Expand Up @@ -252,22 +263,52 @@ def parse_data(section):
return data


def split_mcnp(filename):
"""Split MCNP file into three strings, one for each block
def expand_read_cards(filename) -> str:
"""Recursively read the MCNP input file and files referenced by READ cards

READ card keywords other than FILE are ignored.

Parameters
----------
filename : str
Path to MCNP file

Returns
-------
str
Text of the MCNP input file

"""
path = Path(filename).resolve()
text = path.read_text()
for match in _READ_RE.finditer(text):
card = match[0].strip()
# If the requested path is absolute, use it directly
requested = Path(match[1])
target = requested if requested.is_absolute() else path.parent / requested
if not target.is_file():
errstr = f"In card {repr(card)}, failed to find: {target}"
raise FileNotFoundError(errstr)
subtext = expand_read_cards(target)
text = text.replace(card, subtext)
return text


def split_mcnp(text):
"""Split MCNP file into three strings, one for each block

Parameters
----------
text : str
Text of the MCNP input file

Returns
-------
list of str
List containing one string for each block

"""
# Find beginning of cell section
text = open(filename, 'r').read()
m = re.search(r'^[ \t]*(\d+)[ \t]+', text, flags=re.MULTILINE)
text = text[m.start():]
return re.split('\n[ \t]*\n', text)
Expand Down Expand Up @@ -331,8 +372,11 @@ def parse(filename):
Dictionary containing data-block information, including materials

"""
# Read the text of the file and any referenced files into memory
text = expand_read_cards(filename)

# Split file into main three sections (cells, surfaces, data)
sections = split_mcnp(filename)
sections = split_mcnp(text)

# Sanitize lines (remove comments, continuation lines, etc.)
cell_section = sanitize(sections[0])
Expand Down
7 changes: 7 additions & 0 deletions tests/inputs/testRead.imcnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Testing read card
C this is a comment
read echo file=testReadTarget.imcnp encode

1 so 0.5

mode n
1 change: 1 addition & 0 deletions tests/inputs/testReadRecursive.imcnp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2 0 +1 $ lowest level
9 changes: 9 additions & 0 deletions tests/inputs/testReadReference.imcnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Testing read card
C this is a comment
1 0 -1
2 0 +1 $ lowest level
c

1 so 0.5

mode n
3 changes: 3 additions & 0 deletions tests/inputs/testReadTarget.imcnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 0 -1
read file=testReadRecursive.imcnp
c
29 changes: 29 additions & 0 deletions tests/test_read_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pathlib import Path
import textwrap
import pytest

from openmc_mcnp_adapter import mcnp_str_to_model, mcnp_to_model
from openmc_mcnp_adapter.parse import expand_read_cards


INPUT_DIR = Path(__file__).with_name("inputs")


def test_read_not_found():
deck = textwrap.dedent("""
title
c The next line points to an invalid file
read file=/badfile.path
""")
with pytest.raises(FileNotFoundError):
mcnp_str_to_model(deck)


def test_read_recursive():
reference = expand_read_cards(INPUT_DIR / "testReadReference.imcnp")
trial = expand_read_cards(INPUT_DIR / "testRead.imcnp")
assert trial == reference


def test_recursive_mcnp_to_model():
mcnp_to_model(INPUT_DIR / "testRead.imcnp")