Skip to content

Commit

Permalink
Merge pull request #472 from idaholab/material_update
Browse files Browse the repository at this point in the history
Fix material to update compositions on export
  • Loading branch information
MicahGale authored Aug 13, 2024
2 parents 55d0c4d + d73487e commit a6a884f
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 58 deletions.
1 change: 1 addition & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ MontePy Changelog

**Bug fixes**

* Fixed bug with material compositions not being updated when written to file (:issue:`470`).
* Fixed bug with appending and renumbering numbered objects from other MCNP problems (:issue:`466`).
* Fixed bug with dynamic typing and the parsers that only appear in edge cases (:issue:`461`).

Expand Down
6 changes: 4 additions & 2 deletions montepy/data_inputs/isotope.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
from montepy.data_inputs.element import Element
from montepy.errors import *
from montepy.input_parser.syntax_node import ValueNode
from montepy.input_parser.syntax_node import PaddingNode, ValueNode


class Isotope:
Expand Down Expand Up @@ -36,6 +36,8 @@ def __init__(self, ZAID="", node=None):
self._library = parts[1]
else:
self._library = ""
if node is None:
self._tree = ValueNode(self.mcnp_str(), str, PaddingNode(" "))

def __parse_zaid(self):
"""
Expand Down Expand Up @@ -178,7 +180,7 @@ def mcnp_str(self):
:returns: a string that can be used in MCNP
:rtype: str
"""
return f"{self.ZAID}.{self.library}"
return f"{self.ZAID}.{self.library}" if self.library else self.ZAID

def get_base_zaid(self):
"""
Expand Down
20 changes: 11 additions & 9 deletions montepy/data_inputs/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,7 @@ def __init__(self, input=None):
self._number = num
set_atom_frac = False
isotope_fractions = self._tree["data"]
if isinstance(isotope_fractions, syntax_node.ListNode):
# in python 3.12 this can be replaced with itertools.batched
def batch_gen():
it = iter(isotope_fractions)
while batch := tuple(itertools.islice(it, 2)):
yield batch

iterator = batch_gen()
elif isinstance(isotope_fractions, syntax_node.IsotopesNode):
if isinstance(isotope_fractions, syntax_node.IsotopesNode):
iterator = iter(isotope_fractions)
else: # pragma: no cover
# this is a fall through error, that should never be raised,
Expand Down Expand Up @@ -112,6 +104,9 @@ def material_components(self):
"""
The internal dictionary containing all the components of this material.
The keys are :class:`~montepy.data_inputs.isotope.Isotope` instances, and the values are
:class:`~montepy.data_inputs.material_component.MaterialComponent` instances.
:rtype: dict
"""
return self._material_components
Expand Down Expand Up @@ -152,6 +147,13 @@ def format_for_mcnp_input(self, mcnp_version):
lines += self.thermal_scattering.format_for_mcnp_input(mcnp_version)
return lines

def _update_values(self):
new_list = syntax_node.IsotopesNode("new isotope list")
for isotope, component in self.material_components.items():
isotope._tree.value = isotope.mcnp_str()
new_list.append(("_", isotope._tree, component._tree))
self._tree.nodes["data"] = new_list

def add_thermal_scattering(self, law):
"""
Adds thermal scattering law to the material
Expand Down
24 changes: 21 additions & 3 deletions montepy/input_parser/material_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
import itertools

from montepy.input_parser.data_parser import DataParser
from montepy.input_parser import syntax_node

Expand All @@ -21,11 +23,27 @@ def material(self, p):

@_("isotope_fractions", "number_sequence", "isotope_hybrid_fractions")
def isotopes(self, p):
if hasattr(p, "number_sequence"):
return self._convert_to_isotope(p.number_sequence)
return p[0]

@_("number_sequence isotope_fraction", "isotope_hybrid_fractions isotope_fraction")
def isotope_hybrid_fractions(self, p):
ret = p[0]
for node in p.isotope_fraction[1:]:
ret.append(node)
if hasattr(p, "number_sequence"):
ret = self._convert_to_isotope(p.number_sequence)
else:
ret = p[0]
ret.append(p.isotope_fraction)
return ret

def _convert_to_isotope(self, old):
new_list = syntax_node.IsotopesNode("converted isotopes")

def batch_gen():
it = iter(old)
while batch := tuple(itertools.islice(it, 2)):
yield batch

for group in batch_gen():
new_list.append(("foo", *group))
return new_list
110 changes: 66 additions & 44 deletions tests/test_material.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved.
from unittest import TestCase
import pytest

import montepy

Expand Down Expand Up @@ -121,51 +122,72 @@ def test_material_sort(self):
for i, mat in enumerate(sort_list):
self.assertEqual(mat, answers[i])

def test_material_format_mcnp(self):
in_strs = ["M20 1001.80c 0.5", " 8016.80c 0.5"]
input_card = Input(in_strs, BlockType.DATA)
material = Material(input_card)
material.number = 25
answers = ["M25 1001.80c 0.5", " 8016.80c 0.5"]
output = material.format_for_mcnp_input((6, 2, 0))
self.assertEqual(len(answers), len(output))
for i, line in enumerate(output):
self.assertEqual(line, answers[i])

def test_material_comp_init(self):
# test fraction test
with self.assertRaises(ValueError):
MaterialComponent(Isotope("1001.80c"), -0.1)

# test bad fraction
with self.assertRaises(TypeError):
MaterialComponent(Isotope("1001.80c"), "hi")

# test bad isotope
with self.assertRaises(TypeError):
MaterialComponent("hi", 1.0)

def test_material_comp_fraction_setter(self):
comp = MaterialComponent(Isotope("1001.80c"), 0.1)
comp.fraction = 5.0
self.assertEqual(comp.fraction, 5.0)
with self.assertRaises(ValueError):
comp.fraction = -1.0
with self.assertRaises(TypeError):
comp.fraction = "hi"

def test_material_comp_fraction_str(self):
comp = MaterialComponent(Isotope("1001.80c"), 0.1)
str(comp)
repr(comp)

def test_material_card_pass_through(self):
in_str = "M20 1001.80c 0.5 8016.80c 0.5"
input_card = Input([in_str], BlockType.DATA)
material = Material(input_card)
self.assertEqual(material.format_for_mcnp_input((6, 2, 0)), [in_str])
material.number = 5
self.assertNotIn("8016", material.format_for_mcnp_input((6, 2, 0)))
def test_material_format_mcnp():
in_strs = ["M20 1001.80c 0.5", " 8016.80c 0.5"]
input_card = Input(in_strs, BlockType.DATA)
material = Material(input_card)
material.number = 25
answers = ["M25 1001.80c 0.5", " 8016.80c 0.5"]
output = material.format_for_mcnp_input((6, 2, 0))
assert output == answers


@pytest.mark.parametrize(
"isotope, conc, error",
[
("1001.80c", -0.1, ValueError),
("1001.80c", "hi", TypeError),
("hi", 1.0, ValueError),
],
)
def test_material_comp_init(isotope, conc, error):
with pytest.raises(error):
MaterialComponent(Isotope(isotope), conc)


def test_material_comp_fraction_setter():
comp = MaterialComponent(Isotope("1001.80c"), 0.1)
comp.fraction = 5.0
assert comp.fraction == pytest.approx(5.0)
with pytest.raises(ValueError):
comp.fraction = -1.0
with pytest.raises(TypeError):
comp.fraction = "hi"


def test_material_comp_fraction_str():
comp = MaterialComponent(Isotope("1001.80c"), 0.1)
str(comp)
repr(comp)


def test_material_update_format():
in_str = "M20 1001.80c 0.5 8016.80c 0.5"
input_card = Input([in_str], BlockType.DATA)
material = Material(input_card)
assert material.format_for_mcnp_input((6, 2, 0)) == [in_str]
material.number = 5
print(material.format_for_mcnp_input((6, 2, 0)))
assert "8016" in material.format_for_mcnp_input((6, 2, 0))[0]
# addition
isotope = Isotope("2004.80c")
material.material_components[isotope] = MaterialComponent(isotope, 0.1)
print(material.format_for_mcnp_input((6, 2, 0)))
assert "2004" in material.format_for_mcnp_input((6, 2, 0))[0]
# update
isotope = list(material.material_components.keys())[-1]
print(material.material_components.keys())
material.material_components[isotope].fraction = 0.7
print(material.format_for_mcnp_input((6, 2, 0)))
assert "0.7" in material.format_for_mcnp_input((6, 2, 0))[0]
material.material_components[isotope] = MaterialComponent(isotope, 0.6)
print(material.format_for_mcnp_input((6, 2, 0)))
assert "0.6" in material.format_for_mcnp_input((6, 2, 0))[0]
# delete
del material.material_components[isotope]
print(material.format_for_mcnp_input((6, 2, 0)))
assert "8016" in material.format_for_mcnp_input((6, 2, 0))[0]


class TestIsotope(TestCase):
Expand Down

0 comments on commit a6a884f

Please sign in to comment.