From 4c17ea7954f99aaa4fb32e509b779a4b717df4e2 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Tue, 6 Feb 2024 16:58:01 -0600 Subject: [PATCH 01/10] Added test for implicit library that causes SyntaxError. --- tests/test_material.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_material.py b/tests/test_material.py index 56adfed9..83466f86 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -32,6 +32,16 @@ def test_material_init(self): for component in material.material_components: self.assertEqual(material.material_components[component].fraction, 0.5) + # test implicit library + in_str = "M20 1001 0.5 2001 0.3 8016.710nc 0.5" + input_card = Input([in_str], BlockType.DATA) + material = Material(input_card) + self.assertEqual(material.number, 20) + self.assertEqual(material.old_number, 20) + self.assertTrue(material.is_atom_fraction) + for component in material.material_components: + self.assertEqual(material.material_components[component].fraction, 0.5) + in_str = "M20 1001.80c -0.5 8016.80c -0.5" input_card = Input([in_str], BlockType.DATA) material = Material(input_card) From 89e2766e0015e81b4547df4aa6d01433e914edc2 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Tue, 6 Feb 2024 17:01:55 -0600 Subject: [PATCH 02/10] Added test that replicated the exact error type in #365 --- tests/test_material.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_material.py b/tests/test_material.py index 83466f86..83ec5930 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -32,6 +32,12 @@ def test_material_init(self): for component in material.material_components: self.assertEqual(material.material_components[component].fraction, 0.5) + # test implicit library with syntax tree errors + in_str = """m1 1001 0.33 + 8016 0.666667""" + input_card = Input(in_str.split("\n"), BlockType.DATA) + material = Material(input_card) + # test implicit library in_str = "M20 1001 0.5 2001 0.3 8016.710nc 0.5" input_card = Input([in_str], BlockType.DATA) @@ -42,6 +48,7 @@ def test_material_init(self): for component in material.material_components: self.assertEqual(material.material_components[component].fraction, 0.5) + # test weight fraction in_str = "M20 1001.80c -0.5 8016.80c -0.5" input_card = Input([in_str], BlockType.DATA) material = Material(input_card) From d68c1cff3cb093507c1a7f6ea0b0b293918c01f6 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 12 Feb 2024 09:22:08 -0600 Subject: [PATCH 03/10] Removed test for actually valid material. --- tests/test_material.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_material.py b/tests/test_material.py index 83ec5930..bb2049cb 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -181,8 +181,6 @@ def test_isotope_init(self): Isotope("1001.80c.5") with self.assertRaises(ValueError): Isotope("hi.80c") - with self.assertRaises(ValueError): - Isotope("1001") def test_isotope_metastable_init(self): isotope = Isotope("13426.02c") From 938e25447debe9e471e3cb2e06a82ac1420f8bfa Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 12 Feb 2024 09:24:30 -0600 Subject: [PATCH 04/10] Made material tolerant of IsotopesNode and ListNode. --- montepy/data_inputs/material.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/montepy/data_inputs/material.py b/montepy/data_inputs/material.py index 022c20bc..9a5338de 100644 --- a/montepy/data_inputs/material.py +++ b/montepy/data_inputs/material.py @@ -3,6 +3,7 @@ from montepy.data_inputs import data_input, thermal_scattering from montepy.data_inputs.isotope import Isotope from montepy.data_inputs.material_component import MaterialComponent +from montepy.input_parser import syntax_node from montepy import mcnp_object from montepy.numbered_mcnp_object import Numbered_MCNP_Object from montepy.errors import * @@ -37,7 +38,23 @@ def __init__(self, input=None): self._number = num set_atom_frac = False isotope_fractions = self._tree["data"] - for isotope_node, fraction in isotope_fractions: + 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)): + print(batch) + yield batch + + iterator = batch_gen() + elif isinstance(isotope_fractions, syntax_node.IsotopesNode): + iterator = iter(isotope_fractions) + else: + raise MalformedInputError( + input, + f"Material definitions for material: {self.number} is not valid.", + ) + for isotope_node, fraction in iterator: isotope = Isotope(node=isotope_node) fraction.is_negatable_float = True if not set_atom_frac: From e60138b40320e2119fa69b5d0fb26b38895befab Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 12 Feb 2024 09:24:50 -0600 Subject: [PATCH 05/10] Allowed isotope to have no library. --- montepy/data_inputs/isotope.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/montepy/data_inputs/isotope.py b/montepy/data_inputs/isotope.py index 5a5272a5..4303198d 100644 --- a/montepy/data_inputs/isotope.py +++ b/montepy/data_inputs/isotope.py @@ -20,20 +20,22 @@ class Isotope: def __init__(self, ZAID="", node=None): if node is not None and isinstance(node, ValueNode): + if node.type == float: + node = ValueNode(node.token, str, node.padding) self._tree = node ZAID = node.value - if "." in ZAID: - parts = ZAID.split(".") - try: - assert len(parts) == 2 - int(parts[0]) - except (AssertionError, ValueError) as e: - raise ValueError(f"ZAID: {ZAID} could not be parsed as a valid isotope") - self._ZAID = parts[0] - self.__parse_zaid() + parts = ZAID.split(".") + try: + assert len(parts) <= 2 + int(parts[0]) + except (AssertionError, ValueError) as e: + raise ValueError(f"ZAID: {ZAID} could not be parsed as a valid isotope") + self._ZAID = parts[0] + self.__parse_zaid() + if len(parts) == 2: self._library = parts[1] else: - raise ValueError(f"ZAID: {ZAID} could not be parsed as a valid isotope") + self._library = "" def __parse_zaid(self): """ From e463a08c76a7b2a28e700406d84fcf57587c849c Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 12 Feb 2024 10:39:46 -0600 Subject: [PATCH 06/10] Updated init test to actually work. --- tests/test_material.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_material.py b/tests/test_material.py index bb2049cb..aec22336 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -37,9 +37,8 @@ def test_material_init(self): 8016 0.666667""" input_card = Input(in_str.split("\n"), BlockType.DATA) material = Material(input_card) - # test implicit library - in_str = "M20 1001 0.5 2001 0.3 8016.710nc 0.5" + in_str = "M20 1001 0.5 2001 0.5 8016.710nc 0.5" input_card = Input([in_str], BlockType.DATA) material = Material(input_card) self.assertEqual(material.number, 20) From 8b5448881e3da8315bf51e08b233eb617ed124ca Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 12 Feb 2024 10:40:13 -0600 Subject: [PATCH 07/10] Updated material parser rules to work with libraryless ZAID. --- montepy/data_inputs/material.py | 4 +++- montepy/input_parser/__init__.py | 1 + montepy/input_parser/material_parser.py | 31 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 montepy/input_parser/material_parser.py diff --git a/montepy/data_inputs/material.py b/montepy/data_inputs/material.py index 9a5338de..7a837bad 100644 --- a/montepy/data_inputs/material.py +++ b/montepy/data_inputs/material.py @@ -4,6 +4,7 @@ from montepy.data_inputs.isotope import Isotope from montepy.data_inputs.material_component import MaterialComponent from montepy.input_parser import syntax_node +from montepy.input_parser.material_parser import MaterialParser from montepy import mcnp_object from montepy.numbered_mcnp_object import Numbered_MCNP_Object from montepy.errors import * @@ -27,6 +28,8 @@ class Material(data_input.DataInputAbstract, Numbered_MCNP_Object): :type input: Input """ + _parser = MaterialParser() + def __init__(self, input=None): self._material_components = {} self._thermal_scattering = None @@ -43,7 +46,6 @@ def __init__(self, input=None): def batch_gen(): it = iter(isotope_fractions) while batch := tuple(itertools.islice(it, 2)): - print(batch) yield batch iterator = batch_gen() diff --git a/montepy/input_parser/__init__.py b/montepy/input_parser/__init__.py index 487d3fcb..a80864bb 100644 --- a/montepy/input_parser/__init__.py +++ b/montepy/input_parser/__init__.py @@ -4,6 +4,7 @@ from . import cell_parser from . import data_parser from . import input_reader +from . import material_parser from . import mcnp_input from . import parser_base from . import read_parser diff --git a/montepy/input_parser/material_parser.py b/montepy/input_parser/material_parser.py new file mode 100644 index 00000000..f53e1b4b --- /dev/null +++ b/montepy/input_parser/material_parser.py @@ -0,0 +1,31 @@ +# Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved. +from montepy.input_parser.data_parser import DataParser +from montepy.input_parser import syntax_node + + +class MaterialParser(DataParser): + debugfile = None + + @_( + "introduction isotopes", + "introduction isotopes parameters", + ) + def material(self, p): + ret = {} + for key, node in p.introduction.nodes.items(): + ret[key] = node + ret["data"] = p.isotopes + if hasattr(p, "parameters"): + ret["parameters"] = p.parameters + return syntax_node.SyntaxNode("data", ret) + + @_("isotope_fractions", "number_sequence", "isotope_hybrid_fractions") + def isotopes(self, p): + 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) + return ret From 3ae4a6627916bd948309f128ef44b39e086ca057 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Tue, 5 Mar 2024 10:04:07 -0600 Subject: [PATCH 08/10] Marked dead end code to not be covered. --- montepy/data_inputs/material.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/montepy/data_inputs/material.py b/montepy/data_inputs/material.py index 7a837bad..2f9e1000 100644 --- a/montepy/data_inputs/material.py +++ b/montepy/data_inputs/material.py @@ -51,7 +51,9 @@ def batch_gen(): iterator = batch_gen() elif isinstance(isotope_fractions, syntax_node.IsotopesNode): iterator = iter(isotope_fractions) - else: + else: # pragma: no cover + # this is a fall through error, that should never be raised, + # but is here just in case raise MalformedInputError( input, f"Material definitions for material: {self.number} is not valid.", From c675ff1239cf48cad47381950e2662e181c663ee Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Fri, 12 Apr 2024 08:44:33 -0500 Subject: [PATCH 09/10] Updated integration tests to replicate #395. --- tests/test_integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_integration.py b/tests/test_integration.py index 7da34589..c4ba5afb 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -138,6 +138,10 @@ def test_write_to_file(self): for i, data in enumerate(self.simple_problem.data_inputs): if isinstance(data, material.Material): self.assertEqual(data.number, test_problem.data_inputs[i].number) + if data.thermal_scattering is not None: + assert ( + test_problem.data_inputs[i].thermal_scattering is not None + ) elif isinstance(data, volume.Volume): self.assertEqual(str(data), str(test_problem.data_inputs[i])) else: From 57f8933593dbc7da1845aeecd3ee2501bb858e94 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Fri, 12 Apr 2024 08:58:54 -0500 Subject: [PATCH 10/10] Added method to ensure thermal scattering is written to file. --- montepy/data_inputs/material.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/montepy/data_inputs/material.py b/montepy/data_inputs/material.py index 022c20bc..44516538 100644 --- a/montepy/data_inputs/material.py +++ b/montepy/data_inputs/material.py @@ -116,6 +116,21 @@ def cells(self): if cell.material == self: yield cell + def format_for_mcnp_input(self, mcnp_version): + """ + Creates a string representation of this MCNP_Object that can be + written to file. + + :param mcnp_version: The tuple for the MCNP version that must be exported to. + :type mcnp_version: tuple + :return: a list of strings for the lines that this input will occupy. + :rtype: list + """ + lines = super().format_for_mcnp_input(mcnp_version) + if self.thermal_scattering is not None: + lines += self.thermal_scattering.format_for_mcnp_input(mcnp_version) + return lines + def add_thermal_scattering(self, law): """ Adds thermal scattering law to the material