From 53637c8d6c015371450e5c03b60c53a068573a9e Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Thu, 15 Aug 2024 15:17:06 -0500 Subject: [PATCH 01/12] Made method to auto insert new materials to data_inputs. --- montepy/materials.py | 4 ++-- montepy/mcnp_problem.py | 2 +- montepy/numbered_object_collection.py | 32 +++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/montepy/materials.py b/montepy/materials.py index d3a9dcb8..70d59103 100644 --- a/montepy/materials.py +++ b/montepy/materials.py @@ -1,11 +1,11 @@ # Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved. import montepy -from montepy.numbered_object_collection import NumberedObjectCollection +from montepy.numbered_object_collection import NumberedDataObjectCollection Material = montepy.data_inputs.material.Material -class Materials(NumberedObjectCollection): +class Materials(NumberedDataObjectCollection): """ A container of multiple :class:`~montepy.data_inputs.material.Material` instances. diff --git a/montepy/mcnp_problem.py b/montepy/mcnp_problem.py index 403e501b..06db2402 100644 --- a/montepy/mcnp_problem.py +++ b/montepy/mcnp_problem.py @@ -285,7 +285,7 @@ def parse_input(self, check_input=False, replace=True): else: raise e if isinstance(obj, Material): - self._materials.append(obj) + self._materials.append(obj, False) if isinstance(obj, transform.Transform): self._transforms.append(obj) if trailing_comment is not None and last_obj is not None: diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index 9656b2d9..d9f25570 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -364,10 +364,8 @@ def __iadd__(self, other): ) else: self.__num_cache[obj.number] = obj - self._objects += other_list - if self._problem: - for obj in other_list: - obj.link_to_problem(self._problem) + for obj in other_list: + self.append(obj) return self def __contains__(self, other): @@ -424,3 +422,29 @@ def items( """ for o in self._objects: yield o.number, o + + +class NumberedDataObjectCollection(NumberedObjectCollection): + def __init__(self, obj_class, objects=None, problem=None): + self._last_index = None + if problem and objects: + self._last_index = problem.data_inputs.index(objects[-1]) + super().__init__(obj_class, objects, problem) + + def append(self, obj, insert_in_data=True): + """Appends the given object to the end of this collection. + + :param obj: the object to add. + :type obj: Numbered_MCNP_Object + :raises NumberConflictError: if this object has a number that is already in use. + """ + super().append(obj) + if self._problem and insert_in_data: + if self._last_index: + index = self._last_index + elif len(self) > 0: + index = self._problem.data_inputs.index(self._objects[-1]) + else: + index = len(self._problem.data_inputs) + self._problem.data_inputs.insert(index + 1, obj) + self._last_index = index + 1 From ca465feef8728f697d2617a5375093ba71d82340 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Thu, 15 Aug 2024 15:33:46 -0500 Subject: [PATCH 02/12] Updated docstrings. --- montepy/numbered_object_collection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index d9f25570..adec71cd 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -436,6 +436,8 @@ def append(self, obj, insert_in_data=True): :param obj: the object to add. :type obj: Numbered_MCNP_Object + :param insert_in_data: Whether or not to add the object to the linked problem's data_inputs. + :type insert_in_data: bool :raises NumberConflictError: if this object has a number that is already in use. """ super().append(obj) From b1c889bb928655651cece945d55982633ed85a47 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Thu, 15 Aug 2024 15:33:59 -0500 Subject: [PATCH 03/12] Made append method more robust. --- montepy/numbered_object_collection.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index adec71cd..62124bfb 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -440,12 +440,16 @@ def append(self, obj, insert_in_data=True): :type insert_in_data: bool :raises NumberConflictError: if this object has a number that is already in use. """ + # TODO delete from data_inputs? super().append(obj) if self._problem and insert_in_data: if self._last_index: index = self._last_index elif len(self) > 0: - index = self._problem.data_inputs.index(self._objects[-1]) + try: + index = self._problem.data_inputs.index(self._objects[-1]) + except ValueError: + index = len(self._problem.data_inputs) else: index = len(self._problem.data_inputs) self._problem.data_inputs.insert(index + 1, obj) From 524acf0ab7173e73320fdbe919227604732fd718 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Thu, 15 Aug 2024 16:32:48 -0500 Subject: [PATCH 04/12] Updated changelog with materials append feature. --- doc/source/changelog.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index 14d8a0bf..06306947 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -4,6 +4,10 @@ MontePy Changelog #Next Version# -------------- +**Features Added** + +* When adding a material to problem.materials it will also be added to problem.data_inputs, ensuring it is printed to the file (:pull:`488`). + **Bug Fixes** * Fixed bug that didn't show metastable states for pretty printing and isotope. Also handled the case that Am-241 metstable states break convention (:issue:`486`). From d0397bff287738375ed8e739bbbce61dfe988498 Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 10:54:34 -0500 Subject: [PATCH 05/12] Added auto deleter. --- montepy/numbered_object_collection.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index 62124bfb..bf341f55 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -331,8 +331,7 @@ def __delitem__(self, idx): raise TypeError("index must be an int") obj = self[idx] self.__num_cache.pop(obj.number, None) - idx = self._objects.index(obj) - del self._objects[idx] + self._objects.remove(obj) def __setitem__(self, key, newvalue): if not isinstance(key, int): @@ -440,7 +439,6 @@ def append(self, obj, insert_in_data=True): :type insert_in_data: bool :raises NumberConflictError: if this object has a number that is already in use. """ - # TODO delete from data_inputs? super().append(obj) if self._problem and insert_in_data: if self._last_index: @@ -454,3 +452,21 @@ def append(self, obj, insert_in_data=True): index = len(self._problem.data_inputs) self._problem.data_inputs.insert(index + 1, obj) self._last_index = index + 1 + + def __delitem__(self, idx): + if not isinstance(idx, int): + raise TypeError("index must be an int") + super().__delitem__(idx) + if self._problem: + self._problem.data_inputs.remove(obj) + + def remove(self, delete): + """ + Removes the given object from the collection. + + :param delete: the object to delete + :type delete: Numbered_MCNP_Object + """ + super().remove(delete) + if self._problem: + self._problem.data_inputs.remove(delete) From 1c3d5b05bc5a2680d5d0fcf6651386951568a2d3 Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 11:31:45 -0500 Subject: [PATCH 06/12] Added all deleter methods to data numbered. --- montepy/numbered_object_collection.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index bf341f55..06694a41 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -470,3 +470,29 @@ def remove(self, delete): super().remove(delete) if self._problem: self._problem.data_inputs.remove(delete) + + def pop(self, pos=-1): + """ + Pop the final items off of the collection + + :param pos: The index of the element to pop from the internal list. + :type pos: int + :return: the final elements + :rtype: Numbered_MCNP_Object + """ + if not isinstance(pos, int): + raise TypeError("The index for popping must be an int") + obj = self._objects.pop(pos) + super().pop(pos) + if self._problem: + self._problem.data_inputs.remove(obj) + return obj + + def clear(self): + """ + Removes all objects from this collection. + """ + if self._problem: + for obj in self._objects: + self._problem.data_inputs.remove(obj) + super().clear() From 38013e2567146ec998f01be3e6112492261ac8cb Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 11:32:01 -0500 Subject: [PATCH 07/12] Started test fixtures for data numbered objects. --- tests/test_numbered_collection.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_numbered_collection.py b/tests/test_numbered_collection.py index 4b494ef7..f1c1ed21 100644 --- a/tests/test_numbered_collection.py +++ b/tests/test_numbered_collection.py @@ -4,6 +4,8 @@ import montepy.cells from montepy.errors import NumberConflictError import unittest +import pytest +import os class TestNumberedObjectCollection(unittest.TestCase): @@ -288,3 +290,18 @@ def test_str(self): ] for phrase in key_phrases: self.assertIn(phrase, repr(cells)) + + +# test data numbered object +@pytest.fixture(scope="module") +def read_simple_problem(): + return montepy.read_input(os.path.join("tests", "inputs", "test.imcnp")) + + +@pytest.fixture +def cp_simple_problem(read_simple_problem): + return copy.deepcopy(read_simple_problem) + + +def test_data_append(cp_simple_problem): + pass From 9fa1a96859ccf879e6d524b1062a9b14497988ce Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 14:32:45 -0500 Subject: [PATCH 08/12] Tested everything for thing. --- tests/test_numbered_collection.py | 69 ++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/test_numbered_collection.py b/tests/test_numbered_collection.py index f1c1ed21..f88cf831 100644 --- a/tests/test_numbered_collection.py +++ b/tests/test_numbered_collection.py @@ -303,5 +303,72 @@ def cp_simple_problem(read_simple_problem): return copy.deepcopy(read_simple_problem) +def test_data_init(cp_simple_problem): + new_mats = montepy.materials.Materials( + list(cp_simple_problem.materials), problem=cp_simple_problem + ) + assert list(new_mats) == list(cp_simple_problem.materials) + + def test_data_append(cp_simple_problem): - pass + prob = cp_simple_problem + new_mat = copy.deepcopy(next(iter(prob.materials))) + new_mat.number = prob.materials.request_number() + prob.materials.append(new_mat) + assert new_mat in prob.materials + assert new_mat in prob.data_inputs + # trigger getting data_inputs end + prob.materials.clear() + prob.materials.append(new_mat) + assert new_mat in prob.materials + assert new_mat in prob.data_inputs + prob.data_inputs.clear() + prob.materials._last_index = None + new_mat = copy.deepcopy(next(iter(prob.materials))) + new_mat.number = prob.materials.request_number() + prob.materials.append(new_mat) + assert new_mat in prob.materials + assert new_mat in prob.data_inputs + # trigger getting index of last material + prob.materials._last_index = None + new_mat = copy.deepcopy(next(iter(prob.materials))) + new_mat.number = prob.materials.request_number() + prob.materials.append(new_mat) + assert new_mat in prob.materials + assert new_mat in prob.data_inputs + + +def test_data_remove(cp_simple_problem): + prob = cp_simple_problem + old_mat = next(iter(prob.materials)) + prob.materials.remove(old_mat) + assert old_mat not in prob.materials + assert old_mat not in prob.data_inputs + + +def test_data_delete(cp_simple_problem): + prob = cp_simple_problem + old_mat = next(iter(prob.materials)) + del prob.materials[old_mat.number] + assert old_mat not in prob.materials + assert old_mat not in prob.data_inputs + with pytest.raises(TypeError): + del prob.materials["foo"] + + +def test_data_clear(cp_simple_problem): + data_len = len(cp_simple_problem.data_inputs) + mat_len = len(cp_simple_problem.materials) + cp_simple_problem.materials.clear() + assert len(cp_simple_problem.materials) == 0 + assert len(cp_simple_problem.data_inputs) == data_len - mat_len + + +def test_data_pop(cp_simple_problem): + old_mat = next(reversed(list(cp_simple_problem.materials))) + popper = cp_simple_problem.materials.pop() + assert popper is old_mat + assert old_mat not in cp_simple_problem.materials + assert old_mat not in cp_simple_problem.data_inputs + with pytest.raises(TypeError): + cp_simple_problem.materials.pop("foo") From 4315fed18defa64f8f6d502cacc32859ee434d1a Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 14:33:04 -0500 Subject: [PATCH 09/12] Fixed new collection bugs that were found. --- montepy/numbered_object_collection.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index 06694a41..faea5e56 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -439,8 +439,8 @@ def append(self, obj, insert_in_data=True): :type insert_in_data: bool :raises NumberConflictError: if this object has a number that is already in use. """ - super().append(obj) - if self._problem and insert_in_data: + if self._problem: + print(self._last_index, len(self)) if self._last_index: index = self._last_index elif len(self) > 0: @@ -450,12 +450,15 @@ def append(self, obj, insert_in_data=True): index = len(self._problem.data_inputs) else: index = len(self._problem.data_inputs) - self._problem.data_inputs.insert(index + 1, obj) + if insert_in_data: + self._problem.data_inputs.insert(index + 1, obj) self._last_index = index + 1 + super().append(obj) def __delitem__(self, idx): if not isinstance(idx, int): raise TypeError("index must be an int") + obj = self[idx] super().__delitem__(idx) if self._problem: self._problem.data_inputs.remove(obj) @@ -495,4 +498,5 @@ def clear(self): if self._problem: for obj in self._objects: self._problem.data_inputs.remove(obj) + self._last_index = None super().clear() From 2da5a82c8ad551c88ff3f32ddef43cd43f846b0f Mon Sep 17 00:00:00 2001 From: Micah Gale Date: Sat, 17 Aug 2024 14:42:09 -0500 Subject: [PATCH 10/12] Updated docs about materials. --- doc/source/developing.rst | 6 ++++++ montepy/materials.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/doc/source/developing.rst b/doc/source/developing.rst index 217a032c..c644a1f8 100644 --- a/doc/source/developing.rst +++ b/doc/source/developing.rst @@ -397,6 +397,12 @@ For example the init function for ``Cells`` def __init__(self, cells=None): super().__init__(montepy.Cell, cells) +Collection: :class:`~montepy.numbered_object_collection.NumberedDataObjectCollection` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This is a subclass of :class:`~montepy.numbered_object_collection.NumberedObjectCollection`, +which is designed for :class:`~montepy.data_inputs.data_input.DataInputAbstract` instances. +It is a wrapper that will ensure that all of its items are also in :func:`~montepy.mcnp_problem.MCNP_Problem.data_inputs`. + Numbered Object :class:`~montepy.numbered_mcnp_object.Numbered_MCNP_Object` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/montepy/materials.py b/montepy/materials.py index 70d59103..66273773 100644 --- a/montepy/materials.py +++ b/montepy/materials.py @@ -9,6 +9,10 @@ class Materials(NumberedDataObjectCollection): """ A container of multiple :class:`~montepy.data_inputs.material.Material` instances. + .. note:: + When items are added to this (and this object is linked to a problem), + they will also be added to :func:`montepy.mcnp_problem.MCNP_Problem.data_inputs`. + :param objects: the list of materials to start with if needed :type objects: list """ From 45cb024822a46ceff8d55291367272d390586833 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 19 Aug 2024 13:22:42 -0500 Subject: [PATCH 11/12] Git merge wasn't smart enough. --- doc/source/changelog.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst index e7e30667..d3291f87 100644 --- a/doc/source/changelog.rst +++ b/doc/source/changelog.rst @@ -6,9 +6,6 @@ MontePy Changelog **Features Added** * Write problems to either file paths or streams (file handles) with MCNP_Problem.write_problem() (:issue:`492`). - -**Features Added** - * When adding a material to problem.materials it will also be added to problem.data_inputs, ensuring it is printed to the file (:pull:`488`). **Bug Fixes** From 9fea07486e78e8deba6b8f68fe923165a4989d61 Mon Sep 17 00:00:00 2001 From: "Micah D. Gale" Date: Mon, 19 Aug 2024 13:24:43 -0500 Subject: [PATCH 12/12] Thanks for the suggestion Word. --- montepy/numbered_object_collection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/montepy/numbered_object_collection.py b/montepy/numbered_object_collection.py index faea5e56..1de1a520 100644 --- a/montepy/numbered_object_collection.py +++ b/montepy/numbered_object_collection.py @@ -435,7 +435,7 @@ def append(self, obj, insert_in_data=True): :param obj: the object to add. :type obj: Numbered_MCNP_Object - :param insert_in_data: Whether or not to add the object to the linked problem's data_inputs. + :param insert_in_data: Whether to add the object to the linked problem's data_inputs. :type insert_in_data: bool :raises NumberConflictError: if this object has a number that is already in use. """